diff --git a/content/html/content/src/nsHTMLInputElement.cpp b/content/html/content/src/nsHTMLInputElement.cpp
index 60568edc1709..b077762bddd0 100644
--- a/content/html/content/src/nsHTMLInputElement.cpp
+++ b/content/html/content/src/nsHTMLInputElement.cpp
@@ -1103,7 +1103,7 @@ nsHTMLInputElement::ConvertStringToNumber(nsAString& aValue,
jsval rval;
jsval fullYear[3];
fullYear[0].setInt32(year);
- fullYear[1].setInt32(month-1);
+ fullYear[1].setInt32(month - 1);
fullYear[2].setInt32(day);
if (!JS::Call(ctx, date, "setUTCFullYear", 3, fullYear, &rval)) {
JS_ClearPendingException(ctx);
@@ -1323,45 +1323,73 @@ nsHTMLInputElement::ConvertNumberToString(double aValue,
NS_IMETHODIMP
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();
return NS_OK;
}
- uint32_t year, month, day;
- nsAutoString value;
- GetValueInternal(value);
- if (!GetValueAsDate(value, &year, &month, &day)) {
- aDate->setNull();
- return NS_OK;
+ switch (mType) {
+ case NS_FORM_INPUT_DATE:
+ {
+ uint32_t year, month, day;
+ nsAutoString value;
+ 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);
- 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;
+ MOZ_NOT_REACHED();
+ return NS_ERROR_UNEXPECTED;
}
NS_IMETHODIMP
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;
}
diff --git a/content/html/content/test/forms/test_valueasdate_attribute.html b/content/html/content/test/forms/test_valueasdate_attribute.html
index 7fc8d09ffa33..0a0736a074c5 100644
--- a/content/html/content/test/forms/test_valueasdate_attribute.html
+++ b/content/html/content/test/forms/test_valueasdate_attribute.html
@@ -23,42 +23,43 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=769370
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()
{
- 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 =
- [
- ["datetime", true],
- ["month", true],
- ["week", true],
- ["time", true],
- ["datetime-local", true],
- ];
-
- for (data of testData) {
+ for (data of validTypes) {
var exceptionCatched = false;
element.type = data[0];
try {
@@ -79,7 +80,7 @@ function checkAvailability()
" availability is not correct");
}
- for (data of todoList) {
+ for (data of todoTypes) {
var exceptionCatched = false;
element.type = data[0];
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 =
[
@@ -152,7 +192,7 @@ function checkGet()
}
-function checkSet()
+function checkDateSet()
{
var testData =
[
@@ -176,10 +216,6 @@ function checkSet()
// the corresponding date string is the empty string
[ -62135596800001, "" ],
// 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";
@@ -188,71 +224,169 @@ function checkSet()
is(element.value, data[1], "valueAsDate should set the value to "
+ data[1]);
}
+}
- 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");
-
- var illegalValues = [
- "foobar", 42, {}, function() { return 42; }, function() { return Date(); }
+function checkTimeGet()
+{
+ var tests = [
+ // Some invalid values to begin.
+ { value: "", result: null },
+ { value: "foobar", result: null },
+ { value: "00:", result: null },
+ { value: "24:00", result: null },
+ { value: "00:99", result: null },
+ { 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) {
- try {
- var caught = false;
- element.valueAsDate = value;
- } catch(e) {
- caught = true;
+ var element = document.createElement('input');
+ element.type = 'time';
+
+ for (test of tests) {
+ element.value = test.value;
+ 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()
{
- var element = document.createElement('input');
- element.type = 'date';
+ for (type of validTypes) {
+ if (!type[1]) {
+ continue;
+ }
- var witnessDate = new Date();
+ type = type[0];
- 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) { };
+ var element = document.createElement('input');
+ element.type = type;
- element.valueAsDate = new Date();
+ var witnessDate = 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.
+ var backupPrototype = {};
+ backupPrototype.getUTCFullYear = Date.prototype.getUTCFullYear;
+ 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; };
- 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();
- 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");
- // TODO: check the Date object value (UTCFullYear, UTCMonth and UTCDate)
- // when .valueAsDate will stop returning null.
+ // Same test as above but using NaN instead of {}.
+
+ 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();
SpecialPowers.pushPrefEnv({'set': [["dom.experimental_forms", true]]}, function() {
+
checkAvailability();
-checkGet();
-checkSet();
+checkGarbageValues();
checkWithBustedPrototype();
+// Test .
+checkDateGet();
+checkDateSet();
+
+// Test .
+checkTimeGet();
+checkTimeSet();
+
SimpleTest.finish();
});