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(); });