зеркало из https://github.com/mozilla/gecko-dev.git
Bug 781570 - Part 2/2 - Implement .valueAsDate for <input type='time'>. r=smaug
This commit is contained in:
Родитель
694154d730
Коммит
43c71a80db
|
@ -1103,7 +1103,7 @@ nsHTMLInputElement::ConvertStringToNumber(nsAString& aValue,
|
||||||
jsval rval;
|
jsval rval;
|
||||||
jsval fullYear[3];
|
jsval fullYear[3];
|
||||||
fullYear[0].setInt32(year);
|
fullYear[0].setInt32(year);
|
||||||
fullYear[1].setInt32(month-1);
|
fullYear[1].setInt32(month - 1);
|
||||||
fullYear[2].setInt32(day);
|
fullYear[2].setInt32(day);
|
||||||
if (!JS::Call(ctx, date, "setUTCFullYear", 3, fullYear, &rval)) {
|
if (!JS::Call(ctx, date, "setUTCFullYear", 3, fullYear, &rval)) {
|
||||||
JS_ClearPendingException(ctx);
|
JS_ClearPendingException(ctx);
|
||||||
|
@ -1323,45 +1323,73 @@ nsHTMLInputElement::ConvertNumberToString(double aValue,
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
nsHTMLInputElement::GetValueAsDate(JSContext* aCtx, jsval* aDate)
|
nsHTMLInputElement::GetValueAsDate(JSContext* aCtx, jsval* aDate)
|
||||||
{
|
{
|
||||||
if (mType != NS_FORM_INPUT_DATE) {
|
if (mType != NS_FORM_INPUT_DATE && mType != NS_FORM_INPUT_TIME) {
|
||||||
aDate->setNull();
|
aDate->setNull();
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t year, month, day;
|
switch (mType) {
|
||||||
nsAutoString value;
|
case NS_FORM_INPUT_DATE:
|
||||||
GetValueInternal(value);
|
{
|
||||||
if (!GetValueAsDate(value, &year, &month, &day)) {
|
uint32_t year, month, day;
|
||||||
aDate->setNull();
|
nsAutoString value;
|
||||||
return NS_OK;
|
GetValueInternal(value);
|
||||||
|
if (!GetValueAsDate(value, &year, &month, &day)) {
|
||||||
|
aDate->setNull();
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
JSObject* date = JS_NewDateObjectMsec(aCtx, 0);
|
||||||
|
if (!date) {
|
||||||
|
JS_ClearPendingException(aCtx);
|
||||||
|
aDate->setNull();
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
jsval rval;
|
||||||
|
jsval fullYear[3];
|
||||||
|
fullYear[0].setInt32(year);
|
||||||
|
fullYear[1].setInt32(month - 1);
|
||||||
|
fullYear[2].setInt32(day);
|
||||||
|
if(!JS::Call(aCtx, date, "setUTCFullYear", 3, fullYear, &rval)) {
|
||||||
|
JS_ClearPendingException(aCtx);
|
||||||
|
aDate->setNull();
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
aDate->setObjectOrNull(date);
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
case NS_FORM_INPUT_TIME:
|
||||||
|
{
|
||||||
|
uint32_t millisecond;
|
||||||
|
nsAutoString value;
|
||||||
|
GetValueInternal(value);
|
||||||
|
if (!ParseTime(value, &millisecond)) {
|
||||||
|
aDate->setNull();
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
JSObject* date = JS_NewDateObjectMsec(aCtx, millisecond);
|
||||||
|
if (!date) {
|
||||||
|
JS_ClearPendingException(aCtx);
|
||||||
|
aDate->setNull();
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
aDate->setObjectOrNull(date);
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
JSObject* date = JS_NewDateObjectMsec(aCtx, 0);
|
MOZ_NOT_REACHED();
|
||||||
if (!date) {
|
return NS_ERROR_UNEXPECTED;
|
||||||
JS_ClearPendingException(aCtx);
|
|
||||||
aDate->setNull();
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
jsval rval;
|
|
||||||
jsval fullYear[3];
|
|
||||||
fullYear[0].setInt32(year);
|
|
||||||
fullYear[1].setInt32(month-1);
|
|
||||||
fullYear[2].setInt32(day);
|
|
||||||
if(!JS::Call(aCtx, date, "setUTCFullYear", 3, fullYear, &rval)) {
|
|
||||||
JS_ClearPendingException(aCtx);
|
|
||||||
aDate->setNull();
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
aDate->setObjectOrNull(date);
|
|
||||||
return NS_OK;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
nsHTMLInputElement::SetValueAsDate(JSContext* aCtx, const jsval& aDate)
|
nsHTMLInputElement::SetValueAsDate(JSContext* aCtx, const jsval& aDate)
|
||||||
{
|
{
|
||||||
if (mType != NS_FORM_INPUT_DATE) {
|
if (mType != NS_FORM_INPUT_DATE && mType != NS_FORM_INPUT_TIME) {
|
||||||
return NS_ERROR_DOM_INVALID_STATE_ERR;
|
return NS_ERROR_DOM_INVALID_STATE_ERR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,42 +23,43 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=769370
|
||||||
|
|
||||||
var element = document.createElement("input");
|
var element = document.createElement("input");
|
||||||
|
|
||||||
|
var validTypes =
|
||||||
|
[
|
||||||
|
["text", false],
|
||||||
|
["password", false],
|
||||||
|
["search", false],
|
||||||
|
["telephone", false],
|
||||||
|
["email", false],
|
||||||
|
["url", false],
|
||||||
|
["hidden", false],
|
||||||
|
["checkbox", false],
|
||||||
|
["radio", false],
|
||||||
|
["file", false],
|
||||||
|
["submit", false],
|
||||||
|
["image", false],
|
||||||
|
["reset", false],
|
||||||
|
["button", false],
|
||||||
|
["number", false],
|
||||||
|
["date", true],
|
||||||
|
["time", true],
|
||||||
|
// The next types have not been implemented but will fallback to "text"
|
||||||
|
// which has the same value.
|
||||||
|
["range", false],
|
||||||
|
["color", false],
|
||||||
|
];
|
||||||
|
|
||||||
|
var todoTypes =
|
||||||
|
[
|
||||||
|
["datetime", true],
|
||||||
|
["month", true],
|
||||||
|
["week", true],
|
||||||
|
["datetime-local", true],
|
||||||
|
];
|
||||||
|
|
||||||
function checkAvailability()
|
function checkAvailability()
|
||||||
{
|
{
|
||||||
var testData =
|
|
||||||
[
|
|
||||||
["text", false],
|
|
||||||
["password", false],
|
|
||||||
["search", false],
|
|
||||||
["telephone", false],
|
|
||||||
["email", false],
|
|
||||||
["url", false],
|
|
||||||
["hidden", false],
|
|
||||||
["checkbox", false],
|
|
||||||
["radio", false],
|
|
||||||
["file", false],
|
|
||||||
["submit", false],
|
|
||||||
["image", false],
|
|
||||||
["reset", false],
|
|
||||||
["button", false],
|
|
||||||
["number", false],
|
|
||||||
["date", true],
|
|
||||||
// The next types have not been implemented but will fallback to "text"
|
|
||||||
// which has the same value.
|
|
||||||
["range", false],
|
|
||||||
["color", false],
|
|
||||||
];
|
|
||||||
|
|
||||||
var todoList =
|
for (data of validTypes) {
|
||||||
[
|
|
||||||
["datetime", true],
|
|
||||||
["month", true],
|
|
||||||
["week", true],
|
|
||||||
["time", true],
|
|
||||||
["datetime-local", true],
|
|
||||||
];
|
|
||||||
|
|
||||||
for (data of testData) {
|
|
||||||
var exceptionCatched = false;
|
var exceptionCatched = false;
|
||||||
element.type = data[0];
|
element.type = data[0];
|
||||||
try {
|
try {
|
||||||
|
@ -79,7 +80,7 @@ function checkAvailability()
|
||||||
" availability is not correct");
|
" availability is not correct");
|
||||||
}
|
}
|
||||||
|
|
||||||
for (data of todoList) {
|
for (data of todoTypes) {
|
||||||
var exceptionCatched = false;
|
var exceptionCatched = false;
|
||||||
element.type = data[0];
|
element.type = data[0];
|
||||||
try {
|
try {
|
||||||
|
@ -101,7 +102,46 @@ function checkAvailability()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkGet()
|
function checkGarbageValues()
|
||||||
|
{
|
||||||
|
for (type of validTypes) {
|
||||||
|
if (!type[1]) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
type = type[0];
|
||||||
|
|
||||||
|
var element = document.createElement('input');
|
||||||
|
element.type = type;
|
||||||
|
|
||||||
|
element.value = "test";
|
||||||
|
element.valueAsDate = null;
|
||||||
|
is(element.value, "", "valueAsDate should set the value to the empty string");
|
||||||
|
|
||||||
|
element.value = "test";
|
||||||
|
element.valueAsDate = undefined;
|
||||||
|
is(element.value, "", "valueAsDate should set the value to the empty string");
|
||||||
|
|
||||||
|
element.value = "test";
|
||||||
|
element.valueAsDate = new Date(NaN);
|
||||||
|
is(element.value, "", "valueAsDate should set the value to the empty string");
|
||||||
|
|
||||||
|
var illegalValues = [
|
||||||
|
"foobar", 42, {}, function() { return 42; }, function() { return Date(); }
|
||||||
|
];
|
||||||
|
|
||||||
|
for (value of illegalValues) {
|
||||||
|
try {
|
||||||
|
var caught = false;
|
||||||
|
element.valueAsDate = value;
|
||||||
|
} catch(e) {
|
||||||
|
caught = true;
|
||||||
|
}
|
||||||
|
ok(caught, "Assigning " + value + " to .valueAsDate should throw");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkDateGet()
|
||||||
{
|
{
|
||||||
var validData =
|
var validData =
|
||||||
[
|
[
|
||||||
|
@ -152,7 +192,7 @@ function checkGet()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkSet()
|
function checkDateSet()
|
||||||
{
|
{
|
||||||
var testData =
|
var testData =
|
||||||
[
|
[
|
||||||
|
@ -176,10 +216,6 @@ function checkSet()
|
||||||
// the corresponding date string is the empty string
|
// the corresponding date string is the empty string
|
||||||
[ -62135596800001, "" ],
|
[ -62135596800001, "" ],
|
||||||
// Invalid dates.
|
// Invalid dates.
|
||||||
// We set the value to something different than the empty string because
|
|
||||||
// NaN should set the value to the empty string.
|
|
||||||
[ 86400000, "1970-01-02" ],
|
|
||||||
[ NaN, "" ],
|
|
||||||
];
|
];
|
||||||
|
|
||||||
element.type = "date";
|
element.type = "date";
|
||||||
|
@ -188,71 +224,169 @@ function checkSet()
|
||||||
is(element.value, data[1], "valueAsDate should set the value to "
|
is(element.value, data[1], "valueAsDate should set the value to "
|
||||||
+ data[1]);
|
+ data[1]);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
element.value = "test";
|
function checkTimeGet()
|
||||||
element.valueAsDate = null;
|
{
|
||||||
is(element.value, "", "valueAsDate should set the value to the empty string");
|
var tests = [
|
||||||
|
// Some invalid values to begin.
|
||||||
element.value = "test";
|
{ value: "", result: null },
|
||||||
element.valueAsDate = undefined;
|
{ value: "foobar", result: null },
|
||||||
is(element.value, "", "valueAsDate should set the value to the empty string");
|
{ value: "00:", result: null },
|
||||||
|
{ value: "24:00", result: null },
|
||||||
var illegalValues = [
|
{ value: "00:99", result: null },
|
||||||
"foobar", 42, {}, function() { return 42; }, function() { return Date(); }
|
{ value: "00:00:", result: null },
|
||||||
|
{ value: "00:00:99", result: null },
|
||||||
|
{ value: "00:00:00:", result: null },
|
||||||
|
{ value: "00:00:00.", result: null },
|
||||||
|
{ value: "00:00:00.0000", result: null },
|
||||||
|
// Some simple valid values.
|
||||||
|
{ value: "00:00", result: { time: 0, hours: 0, minutes: 0, seconds: 0, ms: 0 } },
|
||||||
|
{ value: "00:01", result: { time: 60000, hours: 0, minutes: 1, seconds: 0, ms: 0 } },
|
||||||
|
{ value: "01:00", result: { time: 3600000, hours: 1, minutes: 0, seconds: 0, ms: 0 } },
|
||||||
|
{ value: "01:01", result: { time: 3660000, hours: 1, minutes: 1, seconds: 0, ms: 0 } },
|
||||||
|
{ value: "13:37", result: { time: 49020000, hours: 13, minutes: 37, seconds: 0, ms: 0 } },
|
||||||
|
// Valid values including seconds.
|
||||||
|
{ value: "00:00:01", result: { time: 1000, hours: 0, minutes: 0, seconds: 1, ms: 0 } },
|
||||||
|
{ value: "13:37:42", result: { time: 49062000, hours: 13, minutes: 37, seconds: 42, ms: 0 } },
|
||||||
|
// Valid values including seconds fractions.
|
||||||
|
{ value: "00:00:00.001", result: { time: 1, hours: 0, minutes: 0, seconds: 0, ms: 1 } },
|
||||||
|
{ value: "00:00:00.123", result: { time: 123, hours: 0, minutes: 0, seconds: 0, ms: 123 } },
|
||||||
|
{ value: "00:00:00.100", result: { time: 100, hours: 0, minutes: 0, seconds: 0, ms: 100 } },
|
||||||
|
{ value: "00:00:00.000", result: { time: 0, hours: 0, minutes: 0, seconds: 0, ms: 0 } },
|
||||||
|
{ value: "20:17:31.142", result: { time: 73051142, hours: 20, minutes: 17, seconds: 31, ms: 142 } },
|
||||||
|
// Highest possible value.
|
||||||
|
{ value: "23:59:59.999", result: { time: 86399999, hours: 23, minutes: 59, seconds: 59, ms: 999 } },
|
||||||
|
// Some values with one or two digits for the fraction of seconds.
|
||||||
|
{ value: "00:00:00.1", result: { time: 100, hours: 0, minutes: 0, seconds: 0, ms: 100 } },
|
||||||
|
{ value: "00:00:00.14", result: { time: 140, hours: 0, minutes: 0, seconds: 0, ms: 140 } },
|
||||||
|
{ value: "13:37:42.7", result: { time: 49062700, hours: 13, minutes: 37, seconds: 42, ms: 700 } },
|
||||||
|
{ value: "23:31:12.23", result: { time: 84672230, hours: 23, minutes: 31, seconds: 12, ms: 230 } },
|
||||||
];
|
];
|
||||||
|
|
||||||
for (value of illegalValues) {
|
var element = document.createElement('input');
|
||||||
try {
|
element.type = 'time';
|
||||||
var caught = false;
|
|
||||||
element.valueAsDate = value;
|
for (test of tests) {
|
||||||
} catch(e) {
|
element.value = test.value;
|
||||||
caught = true;
|
if (test.result === null) {
|
||||||
|
is(element.valueAsDate, null, "element.valueAsDate should return null");
|
||||||
|
} else {
|
||||||
|
var date = element.valueAsDate;
|
||||||
|
isnot(date, null, "element.valueAsDate should not be null");
|
||||||
|
|
||||||
|
is(date.getTime(), test.result.time);
|
||||||
|
is(date.getUTCHours(), test.result.hours);
|
||||||
|
is(date.getUTCMinutes(), test.result.minutes);
|
||||||
|
is(date.getUTCSeconds(), test.result.seconds);
|
||||||
|
is(date.getUTCMilliseconds(), test.result.ms);
|
||||||
}
|
}
|
||||||
ok(caught, "Assigning " + value + " to .valueAsDate should throw");
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkTimeSet()
|
||||||
|
{
|
||||||
|
var tests = [
|
||||||
|
// Simple tests.
|
||||||
|
{ value: 0, result: "00:00" },
|
||||||
|
{ value: 1, result: "00:00:00.001" },
|
||||||
|
{ value: 100, result: "00:00:00.100" },
|
||||||
|
{ value: 1000, result: "00:00:01" },
|
||||||
|
{ value: 60000, result: "00:01" },
|
||||||
|
{ value: 3600000, result: "01:00" },
|
||||||
|
{ value: 83622234, result: "23:13:42.234" },
|
||||||
|
// Some edge cases.
|
||||||
|
{ value: 86400000, result: "00:00" },
|
||||||
|
{ value: 86400001, result: "00:00:00.001" },
|
||||||
|
{ value: 170022234, result: "23:13:42.234" },
|
||||||
|
{ value: 432000000, result: "00:00" },
|
||||||
|
{ value: -1, result: "23:59:59.999" },
|
||||||
|
{ value: -86400000, result: "00:00" },
|
||||||
|
{ value: -86400001, result: "23:59:59.999" },
|
||||||
|
{ value: -56789, result: "23:59:03.211" },
|
||||||
|
{ value: 0.9, result: "00:00" },
|
||||||
|
];
|
||||||
|
|
||||||
|
var element = document.createElement('input');
|
||||||
|
element.type = 'time';
|
||||||
|
|
||||||
|
for (test of tests) {
|
||||||
|
element.valueAsDate = new Date(test.value);
|
||||||
|
is(element.value, test.result,
|
||||||
|
"element.value should have been changed by setting valueAsDate");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkWithBustedPrototype()
|
function checkWithBustedPrototype()
|
||||||
{
|
{
|
||||||
var element = document.createElement('input');
|
for (type of validTypes) {
|
||||||
element.type = 'date';
|
if (!type[1]) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
var witnessDate = new Date();
|
type = type[0];
|
||||||
|
|
||||||
Date.prototype.getUTCFullYear = function() { return {}; };
|
var element = document.createElement('input');
|
||||||
Date.prototype.getUTCMonth = function() { return {}; };
|
element.type = type;
|
||||||
Date.prototype.getUTCDate = function() { return {}; };
|
|
||||||
Date.prototype.getTime = function() { return {}; };
|
|
||||||
Date.prototype.setUTCFullYear = function(y,m,d) { };
|
|
||||||
|
|
||||||
element.valueAsDate = new Date();
|
var witnessDate = new Date();
|
||||||
|
|
||||||
todo_isnot(element.valueAsDate, null, ".valueAsDate should not return null");
|
var backupPrototype = {};
|
||||||
// TODO: check the Date object value (UTCFullYear, UTCMonth and UTCDate)
|
backupPrototype.getUTCFullYear = Date.prototype.getUTCFullYear;
|
||||||
// when .valueAsDate will stop returning null.
|
backupPrototype.getUTCMonth = Date.prototype.getUTCMonth;
|
||||||
|
backupPrototype.getUTCDate = Date.prototype.getUTCDate;
|
||||||
|
backupPrototype.getTime = Date.prototype.getTime;
|
||||||
|
backupPrototype.setUTCFullYear = Date.prototype.setUTCFullYear;
|
||||||
|
|
||||||
// Same test as above but using NaN instead of {}.
|
Date.prototype.getUTCFullYear = function() { return {}; };
|
||||||
|
Date.prototype.getUTCMonth = function() { return {}; };
|
||||||
|
Date.prototype.getUTCDate = function() { return {}; };
|
||||||
|
Date.prototype.getTime = function() { return {}; };
|
||||||
|
Date.prototype.setUTCFullYear = function(y,m,d) { };
|
||||||
|
|
||||||
Date.prototype.getUTCFullYear = function() { return NaN; };
|
element.valueAsDate = new Date();
|
||||||
Date.prototype.getUTCMonth = function() { return NaN; };
|
|
||||||
Date.prototype.getUTCDate = function() { return NaN; };
|
|
||||||
Date.prototype.getTime = function() { return NaN; };
|
|
||||||
Date.prototype.setUTCFullYear = function(y,m,d) { };
|
|
||||||
|
|
||||||
element.valueAsDate = new Date();
|
todo_isnot(element.valueAsDate, null, ".valueAsDate should not return null");
|
||||||
|
// TODO: check the Date object value (UTCFullYear, UTCMonth and UTCDate)
|
||||||
|
// when .valueAsDate will stop returning null.
|
||||||
|
|
||||||
todo_isnot(element.valueAsDate, null, ".valueAsDate should not return null");
|
// Same test as above but using NaN instead of {}.
|
||||||
// TODO: check the Date object value (UTCFullYear, UTCMonth and UTCDate)
|
|
||||||
// when .valueAsDate will stop returning null.
|
Date.prototype.getUTCFullYear = function() { return NaN; };
|
||||||
|
Date.prototype.getUTCMonth = function() { return NaN; };
|
||||||
|
Date.prototype.getUTCDate = function() { return NaN; };
|
||||||
|
Date.prototype.getTime = function() { return NaN; };
|
||||||
|
Date.prototype.setUTCFullYear = function(y,m,d) { };
|
||||||
|
|
||||||
|
element.valueAsDate = new Date();
|
||||||
|
|
||||||
|
todo_isnot(element.valueAsDate, null, ".valueAsDate should not return null");
|
||||||
|
// TODO: check the Date object value (UTCFullYear, UTCMonth and UTCDate)
|
||||||
|
// when .valueAsDate will stop returning null.
|
||||||
|
|
||||||
|
Date.prototype.getUTCFullYear = backupPrototype.getUTCFullYear;
|
||||||
|
Date.prototype.getUTCMonth = backupPrototype.getUTCMonth;
|
||||||
|
Date.prototype.getUTCDate = backupPrototype.getUTCDate;
|
||||||
|
Date.prototype.getTime = backupPrototype.getTime;
|
||||||
|
Date.prototype.setUTCFullYear = backupPrototype.setUTCFullYear;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SimpleTest.waitForExplicitFinish();
|
SimpleTest.waitForExplicitFinish();
|
||||||
SpecialPowers.pushPrefEnv({'set': [["dom.experimental_forms", true]]}, function() {
|
SpecialPowers.pushPrefEnv({'set': [["dom.experimental_forms", true]]}, function() {
|
||||||
|
|
||||||
checkAvailability();
|
checkAvailability();
|
||||||
checkGet();
|
checkGarbageValues();
|
||||||
checkSet();
|
|
||||||
checkWithBustedPrototype();
|
checkWithBustedPrototype();
|
||||||
|
|
||||||
|
// Test <input type='date'>.
|
||||||
|
checkDateGet();
|
||||||
|
checkDateSet();
|
||||||
|
|
||||||
|
// Test <input type='time'>.
|
||||||
|
checkTimeGet();
|
||||||
|
checkTimeSet();
|
||||||
|
|
||||||
SimpleTest.finish();
|
SimpleTest.finish();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
Загрузка…
Ссылка в новой задаче