mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 781570 - Part 1/2 - Implement .valueAsNumber for <input type='time'>. r=smaug
This commit is contained in:
parent
5e64a4a04d
commit
f409f0ad8e
@ -1070,6 +1070,9 @@ bool
|
||||
nsHTMLInputElement::ConvertStringToNumber(nsAString& aValue,
|
||||
double& aResultValue) const
|
||||
{
|
||||
MOZ_ASSERT(DoesValueAsNumberApply(),
|
||||
"ConvertStringToNumber only applies if .valueAsNumber applies");
|
||||
|
||||
switch (mType) {
|
||||
case NS_FORM_INPUT_NUMBER:
|
||||
{
|
||||
@ -1120,6 +1123,14 @@ nsHTMLInputElement::ConvertStringToNumber(nsAString& aValue,
|
||||
aResultValue = timestamp.toNumber();
|
||||
return true;
|
||||
}
|
||||
case NS_FORM_INPUT_TIME:
|
||||
uint32_t milliseconds;
|
||||
if (!ParseTime(aValue, &milliseconds)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
aResultValue = static_cast<double>(milliseconds);
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@ -1228,8 +1239,8 @@ bool
|
||||
nsHTMLInputElement::ConvertNumberToString(double aValue,
|
||||
nsAString& aResultString) const
|
||||
{
|
||||
MOZ_ASSERT(mType == NS_FORM_INPUT_DATE || mType == NS_FORM_INPUT_NUMBER,
|
||||
"ConvertNumberToString is only implemented for type='{number,date}'");
|
||||
MOZ_ASSERT(DoesValueAsNumberApply(),
|
||||
"ConvertNumberToString is only implemented for types implementing .valueAsNumber");
|
||||
MOZ_ASSERT(!MOZ_DOUBLE_IS_NaN(aValue) && !MOZ_DOUBLE_IS_INFINITE(aValue),
|
||||
"aValue must be a valid non-Infinite number.");
|
||||
|
||||
@ -1273,6 +1284,36 @@ nsHTMLInputElement::ConvertNumberToString(double aValue,
|
||||
|
||||
return true;
|
||||
}
|
||||
case NS_FORM_INPUT_TIME:
|
||||
{
|
||||
// Per spec, we need to truncate |aValue| and we should only represent
|
||||
// times inside a day [00:00, 24:00[, which means that we should do a
|
||||
// modulo on |aValue| using the number of milliseconds in a day (86400000).
|
||||
uint32_t value = NS_floorModulo(floor(aValue), 86400000);
|
||||
|
||||
uint16_t milliseconds = value % 1000;
|
||||
value /= 1000;
|
||||
|
||||
uint8_t seconds = value % 60;
|
||||
value /= 60;
|
||||
|
||||
uint8_t minutes = value % 60;
|
||||
value /= 60;
|
||||
|
||||
uint8_t hours = value;
|
||||
|
||||
if (milliseconds != 0) {
|
||||
aResultString.AppendPrintf("%02d:%02d:%02d.%03d",
|
||||
hours, minutes, seconds, milliseconds);
|
||||
} else if (seconds != 0) {
|
||||
aResultString.AppendPrintf("%02d:%02d:%02d",
|
||||
hours, minutes, seconds);
|
||||
} else {
|
||||
aResultString.AppendPrintf("%02d:%02d", hours, minutes);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
MOZ_NOT_REACHED();
|
||||
return false;
|
||||
@ -3106,6 +3147,12 @@ nsHTMLInputElement::DigitSubStringToNumber(const nsAString& aStr,
|
||||
|
||||
bool
|
||||
nsHTMLInputElement::IsValidTime(const nsAString& aValue) const
|
||||
{
|
||||
return ParseTime(aValue, nullptr);
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
nsHTMLInputElement::ParseTime(const nsAString& aValue, uint32_t* aResult)
|
||||
{
|
||||
/* The string must have the following parts:
|
||||
* - HOURS: two digits, value being in [0, 23];
|
||||
@ -3140,6 +3187,9 @@ nsHTMLInputElement::IsValidTime(const nsAString& aValue) const
|
||||
}
|
||||
|
||||
if (aValue.Length() == 5) {
|
||||
if (aResult) {
|
||||
*aResult = ((hours * 60) + minutes) * 60000;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -3154,6 +3204,9 @@ nsHTMLInputElement::IsValidTime(const nsAString& aValue) const
|
||||
}
|
||||
|
||||
if (aValue.Length() == 8) {
|
||||
if (aResult) {
|
||||
*aResult = (((hours * 60) + minutes) * 60 + seconds) * 1000;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -3164,7 +3217,18 @@ nsHTMLInputElement::IsValidTime(const nsAString& aValue) const
|
||||
}
|
||||
|
||||
uint32_t fractionsSeconds;
|
||||
return DigitSubStringToNumber(aValue, 9, aValue.Length() - 9, &fractionsSeconds);
|
||||
if (!DigitSubStringToNumber(aValue, 9, aValue.Length() - 9, &fractionsSeconds)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (aResult) {
|
||||
*aResult = (((hours * 60) + minutes) * 60 + seconds) * 1000 +
|
||||
// NOTE: there is 10.0 instead of 10 and static_cast<int> because
|
||||
// some old [and stupid] compilers can't just do the right thing.
|
||||
fractionsSeconds * pow(10.0, static_cast<int>(3 - (aValue.Length() - 9)));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -496,7 +496,7 @@ protected:
|
||||
/**
|
||||
* Returns if valueAsNumber attribute applies for the current type.
|
||||
*/
|
||||
bool DoesValueAsNumberApply() const { return DoesMinMaxApply(); }
|
||||
bool DoesValueAsNumberApply() const { return DoesMinMaxApply() || mType == NS_FORM_INPUT_TIME; }
|
||||
|
||||
/**
|
||||
* Returns if the maxlength attribute applies for the current type.
|
||||
@ -635,6 +635,19 @@ protected:
|
||||
*/
|
||||
bool IsValidTime(const nsAString& aValue) const;
|
||||
|
||||
/**
|
||||
* Returns the time expressed in milliseconds of |aValue| being parsed as a
|
||||
* time following the HTML specifications:
|
||||
* http://www.whatwg.org/specs/web-apps/current-work/#parse-a-time-string
|
||||
*
|
||||
* Note: |aResult| can be null.
|
||||
*
|
||||
* @param aValue the string to be parsed.
|
||||
* @param aResult the time expressed in milliseconds representing the time [out]
|
||||
* @return Whether the parsing was successful.
|
||||
*/
|
||||
static bool ParseTime(const nsAString& aValue, uint32_t* aResult);
|
||||
|
||||
/**
|
||||
* Sets the value of the element to the string representation of the double.
|
||||
*
|
||||
|
@ -21,9 +21,6 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=636737
|
||||
* This test is checking .valueAsNumber.
|
||||
*/
|
||||
|
||||
// Global variable used by all functions.
|
||||
var element = document.createElement("input");
|
||||
|
||||
function checkAvailability()
|
||||
{
|
||||
var testData =
|
||||
@ -44,6 +41,7 @@ function checkAvailability()
|
||||
["button", false],
|
||||
["number", true],
|
||||
["date", true],
|
||||
["time", true],
|
||||
// The next types have not been implemented but will fallback to "text"
|
||||
// which has the same value.
|
||||
["color", false],
|
||||
@ -54,11 +52,11 @@ function checkAvailability()
|
||||
["datetime", true],
|
||||
["month", true],
|
||||
["week", true],
|
||||
["time", true],
|
||||
["datetime-local", true],
|
||||
["range", true],
|
||||
];
|
||||
|
||||
var element = document.createElement('input');
|
||||
|
||||
for (data of testData) {
|
||||
var exceptionCatched = false;
|
||||
@ -123,6 +121,7 @@ function checkNumberGet()
|
||||
["42,13", null], // comma can't be used as a decimal separator
|
||||
];
|
||||
|
||||
var element = document.createElement('input');
|
||||
element.type = "number";
|
||||
for (data of testData) {
|
||||
element.value = data[0];
|
||||
@ -162,6 +161,7 @@ function checkNumberSet()
|
||||
[NaN, ""],
|
||||
];
|
||||
|
||||
var element = document.createElement('input');
|
||||
element.type = "number";
|
||||
for (data of testData) {
|
||||
var caught = false;
|
||||
@ -213,6 +213,7 @@ function checkDateGet()
|
||||
"1900-02-29",
|
||||
];
|
||||
|
||||
var element = document.createElement('input');
|
||||
element.type = "date";
|
||||
for (data of validData) {
|
||||
element.value = data[0];
|
||||
@ -268,6 +269,7 @@ function checkDateSet()
|
||||
[ -Infinity, "2011-02-28", true ],
|
||||
];
|
||||
|
||||
var element = document.createElement('input');
|
||||
element.type = "date";
|
||||
for (data of testData) {
|
||||
var caught = false;
|
||||
@ -289,6 +291,111 @@ function checkDateSet()
|
||||
|
||||
}
|
||||
|
||||
function checkTimeGet()
|
||||
{
|
||||
var tests = [
|
||||
// Some invalid values to begin.
|
||||
{ value: "", result: NaN },
|
||||
{ value: "foobar", result: NaN },
|
||||
{ value: "00:", result: NaN },
|
||||
{ value: "24:00", result: NaN },
|
||||
{ value: "00:99", result: NaN },
|
||||
{ value: "00:00:", result: NaN },
|
||||
{ value: "00:00:99", result: NaN },
|
||||
{ value: "00:00:00:", result: NaN },
|
||||
{ value: "00:00:00.", result: NaN },
|
||||
{ value: "00:00:00.0000", result: NaN },
|
||||
// Some simple valid values.
|
||||
{ value: "00:00", result: 0 },
|
||||
{ value: "00:01", result: 60000 },
|
||||
{ value: "01:00", result: 3600000 },
|
||||
{ value: "01:01", result: 3660000 },
|
||||
{ value: "13:37", result: 49020000 },
|
||||
// Valid values including seconds.
|
||||
{ value: "00:00:01", result: 1000 },
|
||||
{ value: "13:37:42", result: 49062000 },
|
||||
// Valid values including seconds fractions.
|
||||
{ value: "00:00:00.001", result: 1 },
|
||||
{ value: "00:00:00.123", result: 123 },
|
||||
{ value: "00:00:00.100", result: 100 },
|
||||
{ value: "00:00:00.000", result: 0 },
|
||||
{ value: "20:17:31.142", result: 73051142 },
|
||||
// Highest possible value.
|
||||
{ value: "23:59:59.999", result: 86399999 },
|
||||
// Some values with one or two digits for the fraction of seconds.
|
||||
{ value: "00:00:00.1", result: 100 },
|
||||
{ value: "00:00:00.14", result: 140 },
|
||||
{ value: "13:37:42.7", result: 49062700 },
|
||||
{ value: "23:31:12.23", result: 84672230 },
|
||||
];
|
||||
|
||||
var element = document.createElement('input');
|
||||
element.type = 'time';
|
||||
|
||||
for (test of tests) {
|
||||
element.value = test.value;
|
||||
if (isNaN(test.result)) {
|
||||
ok(isNaN(element.valueAsNumber),
|
||||
"invalid value should have .valueAsNumber return NaN");
|
||||
} else {
|
||||
is(element.valueAsNumber, test.result,
|
||||
".valueAsNumber should return " + test.result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function checkTimeSet()
|
||||
{
|
||||
var tests = [
|
||||
// Some NaN values (should set to empty string).
|
||||
{ value: NaN, result: "" },
|
||||
{ value: "foobar", result: "" },
|
||||
{ value: function() {}, result: "" },
|
||||
// Inifinity (should throw).
|
||||
{ value: Infinity, throw: true },
|
||||
{ value: -Infinity, throw: true },
|
||||
// "" converts to 0... JS is fun :)
|
||||
{ value: "", result: "00:00" },
|
||||
// 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) {
|
||||
try {
|
||||
var caught = false;
|
||||
element.valueAsNumber = test.value;
|
||||
is(element.value, test.result, "value should return " + test.result);
|
||||
} catch(e) {
|
||||
caught = true;
|
||||
}
|
||||
|
||||
if (!test.throw) {
|
||||
test.throw = false;
|
||||
}
|
||||
|
||||
is(caught, test.throw, "the test throwing status should be " + test.throw);
|
||||
}
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
SpecialPowers.pushPrefEnv({'set': [["dom.experimental_forms", true]]}, function() {
|
||||
checkAvailability();
|
||||
@ -301,6 +408,10 @@ checkNumberSet();
|
||||
checkDateGet();
|
||||
checkDateSet();
|
||||
|
||||
// <input type='time'> test
|
||||
checkTimeGet();
|
||||
checkTimeSet();
|
||||
|
||||
SimpleTest.finish();
|
||||
});
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user