Bug 769359 - Adds step attribute and stepUp/stepDown method support for <input type='date'>. r=mounir

This commit is contained in:
Raphael Catolino 2012-12-22 15:46:19 +01:00
Родитель 1b362d7906
Коммит e9a23b4695
4 изменённых файлов: 472 добавлений и 116 удалений

Просмотреть файл

@ -88,6 +88,7 @@
#include "mozilla/LookAndFeel.h"
#include "mozilla/Util.h" // DebugOnly
#include "mozilla/Preferences.h"
#include "mozilla/MathAlgorithms.h"
#include "nsIIDNService.h"
@ -176,6 +177,8 @@ static const nsAttrValue::EnumTable kInputInputmodeTable[] = {
// Default inputmode value is "auto".
static const nsAttrValue::EnumTable* kInputDefaultInputmode = &kInputInputmodeTable[0];
const double nsHTMLInputElement::kStepScaleFactorDate = 86400000;
const double nsHTMLInputElement::kStepScaleFactorNumber = 1;
const double nsHTMLInputElement::kDefaultStepBase = 0;
const double nsHTMLInputElement::kStepAny = 0;
@ -1201,43 +1204,54 @@ void
nsHTMLInputElement::SetValue(double aValue)
{
nsAutoString value;
ConvertNumberToString(aValue, value);
SetValue(value);
}
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}'");
aResultString.Truncate();
switch (mType) {
case NS_FORM_INPUT_NUMBER:
value.AppendFloat(aValue);
break;
aResultString.AppendFloat(aValue);
return true;
case NS_FORM_INPUT_DATE:
{
value.Truncate();
JSContext* ctx = nsContentUtils::GetContextFromDocument(OwnerDoc());
if (!ctx) {
break;
}
{
JSContext* ctx = nsContentUtils::GetContextFromDocument(OwnerDoc());
if (!ctx) {
return false;
}
JSObject* date = JS_NewDateObjectMsec(ctx, aValue);
if (!date) {
break;
}
// The specs require |aValue| to be truncated.
aValue = floor(aValue);
jsval year, month, day;
if(!JS::Call(ctx, date, "getUTCFullYear", 0, nullptr, &year)) {
break;
}
JSObject* date = JS_NewDateObjectMsec(ctx, aValue);
if (!date) {
return false;
}
if(!JS::Call(ctx, date, "getUTCMonth", 0, nullptr, &month)) {
break;
}
jsval year, month, day;
if (!JS::Call(ctx, date, "getUTCFullYear", 0, nullptr, &year) ||
!JS::Call(ctx, date, "getUTCMonth", 0, nullptr, &month) ||
!JS::Call(ctx, date, "getUTCDate", 0, nullptr, &day)) {
return false;
}
if(!JS::Call(ctx, date, "getUTCDate", 0, nullptr, &day)) {
break;
}
aResultString.AppendPrintf("%04.0f-%02.0f-%02.0f", year.toNumber(),
month.toNumber() + 1, day.toNumber());
value.AppendPrintf("%04.0f-%02.0f-%02.0f", year.toNumber(),
month.toNumber() + 1, day.toNumber());
}
break;
return true;
}
default:
MOZ_NOT_REACHED();
return false;
}
SetValue(value);
}
NS_IMETHODIMP
@ -1415,6 +1429,21 @@ nsHTMLInputElement::ApplyStep(int32_t aStep)
value += aStep * step;
// For date inputs, the value can hold a string that is not a day. We do not
// want to round it, as it might result in a step mismatch. Instead we want to
// clamp to the next valid value.
if (mType == NS_FORM_INPUT_DATE &&
NS_floorModulo(value - GetStepBase(), GetStepScaleFactor()) != 0) {
double validStep = EuclidLCM<uint64_t>(static_cast<uint64_t>(step),
static_cast<uint64_t>(GetStepScaleFactor()));
if (aStep > 0) {
value -= NS_floorModulo(value - GetStepBase(), validStep);
value += validStep;
} else if (aStep < 0) {
value -= NS_floorModulo(value - GetStepBase(), validStep);
}
}
// When stepUp() is called and the value is below min, we should clamp on
// min unless stepUp() moves us higher than min.
if (GetValidityState(VALIDITY_STATE_RANGE_UNDERFLOW) && aStep > 0 &&
@ -4255,11 +4284,10 @@ nsHTMLInputElement::DoesMinMaxApply() const
double
nsHTMLInputElement::GetStep() const
{
NS_ASSERTION(mType == NS_FORM_INPUT_NUMBER,
"We can't be there if type!=number!");
MOZ_ASSERT(mType == NS_FORM_INPUT_NUMBER || mType == NS_FORM_INPUT_DATE,
"We can't be there if type!=number or date!");
// NOTE: should be defaultStep * defaultStepScaleFactor,
// which is 1 for type=number.
// NOTE: should be defaultStep, which is 1 for type=number and date.
double step = 1;
if (HasAttr(kNameSpaceID_None, nsGkAtoms::step)) {
@ -4272,17 +4300,16 @@ nsHTMLInputElement::GetStep() const
}
nsresult ec;
// NOTE: should be multiplied by defaultStepScaleFactor,
// which is 1 for type=number.
step = stepStr.ToDouble(&ec);
if (NS_FAILED(ec) || step <= 0) {
// NOTE: we should use defaultStep * defaultStepScaleFactor,
// which is 1 for type=number.
// NOTE: we should use defaultStep, which is 1 for type=number and date.
step = 1;
}
}
return step;
// TODO: This multiplication can lead to inexact results, we should use a
// type that supports a better precision than double. Bug 783607.
return step * GetStepScaleFactor();
}
// nsIConstraintValidation
@ -4473,6 +4500,15 @@ nsHTMLInputElement::HasStepMismatch() const
return false;
}
if (mType == NS_FORM_INPUT_DATE) {
// The multiplication by the stepScaleFactor for date can easily lead
// to precision loss, since in most use cases this value should be
// an integer (millisecond precision), we can get rid of the precision
// loss by rounding step. This will however lead to erroneous results
// when step was intented to have a precision superior to a millisecond.
step = NS_round(step);
}
// Value has to be an integral multiple of step.
return NS_floorModulo(value - GetStepBase(), step) != 0;
}
@ -4748,6 +4784,16 @@ nsHTMLInputElement::GetValidationMessage(nsAString& aValidationMessage,
double step = GetStep();
MOZ_ASSERT(step != kStepAny);
// In case this is a date and the step is not an integer, we don't want to
// display the dates corresponding to the truncated timestamps of valueLow
// and valueHigh because they might suffer from a step mismatch as well.
// Instead we want the timestamps to correspond to a rounded day. That is,
// we want a multiple of the step scale factor (1 day) as well as of step.
if (mType == NS_FORM_INPUT_DATE) {
step = EuclidLCM<uint64_t>(static_cast<uint64_t>(step),
static_cast<uint64_t>(GetStepScaleFactor()));
}
double stepBase = GetStepBase();
double valueLow = value - NS_floorModulo(value - stepBase, step);
@ -4757,8 +4803,8 @@ nsHTMLInputElement::GetValidationMessage(nsAString& aValidationMessage,
if (MOZ_DOUBLE_IS_NaN(max) || valueHigh <= max) {
nsAutoString valueLowStr, valueHighStr;
valueLowStr.AppendFloat(valueLow);
valueHighStr.AppendFloat(valueHigh);
ConvertNumberToString(valueLow, valueLowStr);
ConvertNumberToString(valueHigh, valueHighStr);
const PRUnichar* params[] = { valueLowStr.get(), valueHighStr.get() };
rv = nsContentUtils::FormatLocalizedString(nsContentUtils::eDOM_PROPERTIES,
@ -4766,7 +4812,7 @@ nsHTMLInputElement::GetValidationMessage(nsAString& aValidationMessage,
params, message);
} else {
nsAutoString valueLowStr;
valueLowStr.AppendFloat(valueLow);
ConvertNumberToString(valueLow, valueLowStr);
const PRUnichar* params[] = { valueLowStr.get() };
rv = nsContentUtils::FormatLocalizedString(nsContentUtils::eDOM_PROPERTIES,
@ -5183,6 +5229,22 @@ nsHTMLInputElement::GetFilterFromAccept()
return filter;
}
double
nsHTMLInputElement::GetStepScaleFactor() const
{
MOZ_ASSERT(DoesStepApply());
switch (mType) {
case NS_FORM_INPUT_DATE:
return kStepScaleFactorDate;
case NS_FORM_INPUT_NUMBER:
return kStepScaleFactorNumber;
default:
MOZ_NOT_REACHED();
return MOZ_DOUBLE_NaN();
}
}
void
nsHTMLInputElement::UpdateValidityUIBits(bool aIsFocused)
{

Просмотреть файл

@ -571,6 +571,19 @@ protected:
*/
bool ConvertStringToNumber(nsAString& aValue, double& aResultValue) const;
/**
* Convert a double to a string in a type specific way, ie convert a timestamp
* to a date string if type=date or append the number string representing the
* value if type=number.
*
* @param aValue the double to be converted
* @param aResultString [out] the string representing the double
* @return whether the function succeded, it will fail if the current input's
* type is not supported or the number can't be converted to a string
* as expected by the type.
*/
bool ConvertNumberToString(double aValue, nsAString& aResultString) const;
/**
* Parse a date string of the form yyyy-mm-dd
* @param the string to be parsed.
@ -619,6 +632,13 @@ protected:
*/
double GetMaxAsDouble() const;
/**
* Get the step scale value for the current type.
* See:
* http://www.whatwg.org/specs/web-apps/current-work/multipage/common-input-element-attributes.html#concept-input-step-scale
*/
double GetStepScaleFactor() const;
/**
* Returns the current step value.
* Returns kStepAny if the current step is "any" string.
@ -688,6 +708,10 @@ protected:
*/
nsString mFocusedValue;
// Step scale factor values, for input types that have one.
static const double kStepScaleFactorDate;
static const double kStepScaleFactorNumber;
// Default step base value when a type do not have specific one.
static const double kDefaultStepBase;
// Float alue returned by GetStep() when the step attribute is set to 'any'.

Просмотреть файл

@ -28,7 +28,7 @@ var types = [
[ 'email', false ],
[ 'password', false ],
[ 'datetime', true, true ],
[ 'date', true, true ],
[ 'date', true ],
[ 'month', true, true ],
[ 'week', true, true ],
[ 'time', true, true ],
@ -115,6 +115,132 @@ for (var data of types) {
checkValidity(input, true, apply);
file.remove(false);
} else if (input.type == 'date') {
// For date, the step is calulated on the timestamp since 1970-01-01
// which mean that for all dates prior to the epoch, this timestamp is < 0
// and the behavior might differ, therefore we have to test for these cases.
// When step is 1 every date is valid
input.value = '2012-07-05';
checkValidity(input, true, apply);
input.step = 'foo';
input.value = '1970-01-01';
checkValidity(input, true, apply);
input.step = '-1';
input.value = '1969-12-12';
checkValidity(input, true, apply);
input.removeAttribute('step');
input.value = '1500-01-01';
checkValidity(input, true, apply);
input.step = 'any';
checkValidity(input, true, apply);
input.step = 'aNy';
checkValidity(input, true, apply);
input.step = 'AnY';
checkValidity(input, true, apply);
input.step = 'ANY';
checkValidity(input, true, apply);
// When min is set to a valid date, there is a step base.
input.min = '2008-02-28';
input.step = '2';
input.value = '2008-03-01';
checkValidity(input, true, apply);
input.value = '2008-02-29';
checkValidity(input, false, apply, { low: "2008-02-28", high: "2008-03-01" });
input.min = '2008-02-27';
input.value = '2008-02-28';
checkValidity(input, false, apply, { low: "2008-02-27", high: "2008-02-29" });
input.min = '2009-02-27';
input.value = '2009-02-28';
checkValidity(input, false, apply, { low: "2009-02-27", high: "2009-03-01" });
input.min = '2009-02-01';
input.step = '1.1';
input.value = '2009-02-02';
checkValidity(input, false, apply, { low: "2009-02-01", high: "2009-02-12" });
// Without any step attribute the date is valid
input.removeAttribute('step');
checkValidity(input, true, apply);
input.min = '1950-01-01';
input.step = '366';
input.value = '1951-01-01';
checkValidity(input, false, apply, { low: "1950-01-01", high: "1951-01-02" });
input.min = '1951-01-01';
input.step = '365';
input.value = '1952-01-01';
checkValidity(input, true, apply);
input.step = '0.9';
input.value = '1951-01-02';
checkValidity(input, false, apply, { low: "1951-01-01", high: "1951-01-10" });
input.value = '1951-01-10'
checkValidity(input, true, apply);
input.step = '0.5';
input.value = '1951-01-02';
checkValidity(input, true, apply);
input.step = '1.5';
input.value = '1951-01-03';
checkValidity(input, false, apply, { low: "1951-01-01", high: "1951-01-04" });
input.value = '1951-01-08';
checkValidity(input, false, apply, { low: "1951-01-07", high: "1951-01-10" });
input.step = '3000';
input.min= '1968-01-01';
input.value = '1968-05-12';
checkValidity(input, false, apply, { low: "1968-01-01", high: "1976-03-19" });
input.value = '1971-01-01';
checkValidity(input, false, apply, { low: "1968-01-01", high: "1976-03-19" });
input.value = '1991-01-01';
checkValidity(input, false, apply, { low: "1984-06-05", high: "1992-08-22" });
input.value = '1984-06-05';
checkValidity(input, true, apply);
input.value = '1992-08-22';
checkValidity(input, true, apply);
input.step = '1.1';
input.min = '1991-01-01';
input.value = '1991-01-01';
checkValidity(input, true, apply);
input.value = '1991-01-02';
checkValidity(input, false, apply, { low: "1991-01-01", high: "1991-01-12" });
input.value = '1991-01-12';
checkValidity(input, true, apply);
input.step = '1.1';
input.min = '1969-12-20';
input.value = '1969-12-20';
checkValidity(input, true, apply);
input.value = '1969-12-21';
checkValidity(input, false, apply, { low: "1969-12-20", high: "1969-12-31" });
input.value = '1969-12-31';
checkValidity(input, true, apply);
} else {
// When step=0, the allowed step is 1.
input.value = '1.2';

Просмотреть файл

@ -47,6 +47,7 @@ function checkAvailability()
["reset", false],
["button", false],
["number", true],
["date", true],
// The next types have not been implemented but will fallback to "text"
// which has the same value.
["color", false],
@ -55,7 +56,6 @@ function checkAvailability()
var todoList =
[
["datetime", true],
["date", true],
["month", true],
["week", true],
["time", true],
@ -107,12 +107,13 @@ function checkAvailability()
}
}
function checkStepDownForNumber()
function checkStepDown()
{
// This testData is very similar to the one in checkStepUpForNumber
// with some changes relative to stepDown.
// This testData is very similar to the one in checkStepUp with some changes
// relative to stepDown.
var testData = [
/* Initial value | step | min | max | stepDown arg | final value | exception */
{ type: 'number', data: [
// Regular case.
[ '1', null, null, null, null, '0', false ],
// Argument testing.
@ -178,54 +179,124 @@ function checkStepDownForNumber()
[ '0', 'aNy', null, null, 1, null, true ],
// With @value = step base.
[ '1', '2', null, null, null, '-1', false ],
]},
{ type: 'date', data: [
// Regular case.
[ '2012-07-09', null, null, null, null, '2012-07-08', false ],
// Argument testing.
[ '2012-07-09', null, null, null, 1, '2012-07-08', false ],
[ '2012-07-09', null, null, null, 5, '2012-07-04', false ],
[ '2012-07-09', null, null, null, -1, '2012-07-10', false ],
[ '2012-07-09', null, null, null, 0, '2012-07-09', false ],
// Month/Year wrapping.
[ '2012-08-01', null, null, null, 1, '2012-07-31', false ],
[ '1969-01-02', null, null, null, 4, '1968-12-29', false ],
[ '1969-01-01', null, null, null, -365, '1970-01-01', false ],
[ '2012-02-29', null, null, null, -1, '2012-03-01', false ],
// Float values are rounded to integer (1.1 -> 1).
[ '2012-01-02', null, null, null, 1.1, '2012-01-01', false ],
[ '2012-01-02', null, null, null, 1.9, '2012-01-01', false ],
// With step values.
[ '2012-01-03', '0.5', null, null, null, '2012-01-02', false ],
[ '2012-01-02', '0.5', null, null, null, '2012-01-01', false ],
[ '2012-01-01', '2', null, null, null, '2011-12-30', false ],
[ '2012-01-02', '0.25',null, null, 4, '2012-01-01', false ],
[ '2012-01-15', '1.1', '2012-01-01', null, 1, '2012-01-12', false ],
[ '2012-01-12', '1.1', '2012-01-01', null, 2, '2012-01-01', false ],
[ '2012-01-23', '1.1', '2012-01-01', null, 10, '2012-01-12', false ],
[ '2012-01-23', '1.1', '2012-01-01', null, 11, '2012-01-01', false ],
[ '1968-01-12', '1.1', '1968-01-01', null, 8, '1968-01-01', false ],
// step = 0 isn't allowed (-> step = 1).
[ '2012-01-02', '0', null, null, null, '2012-01-01', false ],
// step < 0 isn't allowed (-> step = 1).
[ '2012-01-02', '-1', null, null, null, '2012-01-01', false ],
// step = NaN isn't allowed (-> step = 1).
[ '2012-01-02', 'foo', null, null, null, '2012-01-01', false ],
// Min values testing.
[ '2012-01-03', '1', 'foo', null, 2, '2012-01-01', false ],
[ '2012-01-02', '1', '2012-01-01', null, null, '2012-01-01', false ],
[ '2012-01-01', '1', '2012-01-01', null, null, '2012-01-01', false ],
[ '2012-01-01', '1', '2012-01-10', null, 1, '2012-01-01', false ],
[ '2012-01-05', '3', '2012-01-01', null, null, '2012-01-04', false ],
[ '1969-01-01', '5', '1969-01-01', '1969-01-02', null, '1969-01-01', false ],
// Max values testing.
[ '2012-01-02', '1', null, 'foo', null, '2012-01-01', false ],
[ '2012-01-02', null, null, '2012-01-05', null, '2012-01-01', false ],
[ '2012-01-03', null, null, '2012-01-03', null, '2012-01-02', false ],
[ '2012-01-07', null, null, '2012-01-04', 4, '2012-01-03', false ],
[ '2012-01-07', '2', null, '2012-01-04', 3, '2012-01-01', false ],
// Step mismatch.
[ '2012-01-04', '2', '2012-01-01', null, null, '2012-01-03', false ],
[ '2012-01-06', '2', '2012-01-01', null, 2, '2012-01-03', false ],
[ '2012-01-05', '2', '2012-01-04', '2012-01-08', null, '2012-01-04', false ],
[ '1970-01-04', '2', null, null, null, '1970-01-03', false ],
[ '1970-01-09', '3', null, null, null, '1970-01-07', false ],
// Clamping.
[ '2012-05-01', null, null, '2012-01-05', null, '2012-01-05', false ],
[ '1970-01-05', '2', '1970-01-02', '1970-01-05', null, '1970-01-04', false ],
[ '1970-01-01', '5', '1970-01-02', '1970-01-09', 10, '1970-01-01', false ],
[ '1970-01-07', '5', '1969-12-27', '1970-01-06', 2, '1970-01-01', false ],
[ '1970-03-08', '3', '1970-02-01', '1970-02-07', 15, '1970-02-01', false ],
[ '1970-01-10', '3', '1970-01-01', '1970-01-06', 2, '1970-01-04', false ],
// value = "" (NaN).
[ '', null, null, null, null, '', false ],
// With step = 'any'.
[ '2012-01-01', 'any', null, null, 1, null, true ],
[ '2012-01-01', 'ANY', null, null, 1, null, true ],
[ '2012-01-01', 'AnY', null, null, 1, null, true ],
[ '2012-01-01', 'aNy', null, null, 1, null, true ],
]},
];
for (var data of testData) {
var element = document.createElement("input");
element.type = 'number';
for (var test of testData) {
for (var data of test.data) {
var element = document.createElement("input");
element.type = test.type;
if (data[0] != null) {
element.setAttribute('value', data[0]);
}
if (data[1] != null) {
element.step = data[1];
}
if (data[2] != null) {
element.min = data[2];
}
if (data[3] != null) {
element.max = data[3];
}
var exceptionCaught = false;
try {
if (data[4] != null) {
element.stepDown(data[4]);
} else {
element.stepDown();
if (data[0] != null) {
element.setAttribute('value', data[0]);
}
is(element.value, data[5], "The value should be " + data[5]);
} catch (e) {
exceptionCaught = true;
is(element.value, data[0], e.name + "The value should not have changed");
is(e.name, 'InvalidStateError',
"It should be a InvalidStateError exception.");
} finally {
is(exceptionCaught, data[6], "exception status should be " + data[6]);
if (data[1] != null) {
element.step = data[1];
}
if (data[2] != null) {
element.min = data[2];
}
if (data[3] != null) {
element.max = data[3];
}
var exceptionCaught = false;
try {
if (data[4] != null) {
element.stepDown(data[4]);
} else {
element.stepDown();
}
is(element.value, data[5], "The value should be " + data[5]);
} catch (e) {
exceptionCaught = true;
is(element.value, data[0], e.name + "The value should not have changed");
is(e.name, 'InvalidStateError',
"It should be a InvalidStateError exception.");
} finally {
is(exceptionCaught, data[6], "exception status should be " + data[6]);
}
}
}
}
function checkStepUpForNumber()
function checkStepUp()
{
// This testData is very similar to the one in checkStepDownForNumber
// with some changes relative to stepUp.
// This testData is very similar to the one in checkStepDown with some changes
// relative to stepUp.
var testData = [
/* Initial value | step | min | max | stepUp arg | final value | exception */
{ type: 'number', data: [
// Regular case.
[ '1', null, null, null, null, '2', false ],
// Argument testing.
@ -288,44 +359,116 @@ function checkStepUpForNumber()
[ '0', 'aNy', null, null, 1, null, true ],
// With @value = step base.
[ '1', '2', null, null, null, '3', false ],
]},
{ type: 'date', data: [
// Regular case.
[ '2012-07-09', null, null, null, null, '2012-07-10', false ],
// Argument testing.
[ '2012-07-09', null, null, null, 1, '2012-07-10', false ],
[ '2012-07-09', null, null, null, 9, '2012-07-18', false ],
[ '2012-07-09', null, null, null, -1, '2012-07-08', false ],
[ '2012-07-09', null, null, null, 0, '2012-07-09', false ],
// Month/Year wrapping.
[ '2012-07-31', null, null, null, 1, '2012-08-01', false ],
[ '1968-12-29', null, null, null, 4, '1969-01-02', false ],
[ '1970-01-01', null, null, null, -365, '1969-01-01', false ],
[ '2012-03-01', null, null, null, -1, '2012-02-29', false ],
// Float values are rounded to integer (1.1 -> 1).
[ '2012-01-01', null, null, null, 1.1, '2012-01-02', false ],
[ '2012-01-01', null, null, null, 1.9, '2012-01-02', false ],
// With step values.
[ '2012-01-01', '0.5', null, null, null, '2012-01-02', false ],
[ '2012-01-01', '0.5', null, null, null, '2012-01-02', false ],
[ '2012-01-01', '2', null, null, null, '2012-01-03', false ],
[ '2012-01-01', '0.25', null, null, 4, '2012-01-02', false ],
[ '2012-01-01', '1.1', '2012-01-01', null, 1, '2012-01-12', false ],
[ '2012-01-01', '1.1', '2012-01-01', null, 2, '2012-01-12', false ],
[ '2012-01-01', '1.1', '2012-01-01', null, 10, '2012-01-12', false ],
[ '2012-01-01', '1.1', '2012-01-01', null, 11, '2012-01-23', false ],
[ '1968-01-01', '1.1', '1968-01-01', null, 8, '1968-01-12', false ],
// step = 0 isn't allowed (-> step = 1).
[ '2012-01-01', '0', null, null, null, '2012-01-02', false ],
// step < 0 isn't allowed (-> step = 1).
[ '2012-01-01', '-1', null, null, null, '2012-01-02', false ],
// step = NaN isn't allowed (-> step = 1).
[ '2012-01-01', 'foo', null, null, null, '2012-01-02', false ],
// Min values testing.
[ '2012-01-01', '1', 'foo', null, null, '2012-01-02', false ],
[ '2012-01-01', null, '2011-12-01', null, null, '2012-01-02', false ],
[ '2012-01-01', null, '2012-01-02', null, null, '2012-01-02', false ],
[ '2012-01-01', null, '2012-01-01', null, null, '2012-01-02', false ],
[ '2012-01-01', null, '2012-01-04', null, 4, '2012-01-05', false ],
[ '2012-01-01', '2', '2012-01-04', null, 3, '2012-01-06', false ],
// Max values testing.
[ '2012-01-01', '1', null, 'foo', 2, '2012-01-03', false ],
[ '2012-01-01', '1', null, '2012-01-10', 1, '2012-01-02', false ],
[ '2012-01-02', null, null, '2012-01-01', null, '2012-01-02', false ],
[ '2012-01-02', null, null, '2012-01-02', null, '2012-01-02', false ],
[ '2012-01-02', null, null, '2012-01-02', null, '2012-01-02', false ],
[ '1969-01-02', '5', '1969-01-01', '1969-01-02', null, '1969-01-02', false ],
// Step mismatch.
[ '2012-01-02', '2', '2012-01-01', null, null, '2012-01-03', false ],
[ '2012-01-02', '2', '2012-01-01', null, 2, '2012-01-05', false ],
[ '2012-01-05', '2', '2012-01-01', '2012-01-06', null, '2012-01-05', false ],
[ '1970-01-02', '2', null, null, null, '1970-01-03', false ],
[ '1970-01-05', '3', null, null, null, '1970-01-07', false ],
[ '1970-01-03', '3', null, null, null, '1970-01-04', false ],
[ '1970-01-03', '3', '1970-01-02', null, null, '1970-01-05', false ],
// Clamping.
[ '2012-01-01', null, '2012-01-31', null, null, '2012-01-31', false ],
[ '1970-01-02', '2', '1970-01-01', '1970-01-04', null, '1970-01-03', false ],
[ '1970-01-01', '5', '1970-01-02', '1970-01-09', 10, '1970-01-07', false ],
[ '1969-12-28', '5', '1969-12-29', '1970-01-06', 3, '1970-01-03', false ],
[ '1970-01-01', '3', '1970-02-01', '1970-02-07', 15, '1970-02-07', false ],
[ '1970-01-01', '3', '1970-01-01', '1970-01-06', 2, '1970-01-04', false ],
// value = "" (NaN).
[ '', null, null, null, null, '', false ],
// With step = 'any'.
[ '2012-01-01', 'any', null, null, 1, null, true ],
[ '2012-01-01', 'ANY', null, null, 1, null, true ],
[ '2012-01-01', 'AnY', null, null, 1, null, true ],
[ '2012-01-01', 'aNy', null, null, 1, null, true ],
]},
];
for (var data of testData) {
var element = document.createElement("input");
element.type = 'number';
for (var test of testData) {
for (var data of test.data) {
var element = document.createElement("input");
element.type = test.type;
if (data[0] != null) {
element.setAttribute('value', data[0]);
}
if (data[1] != null) {
element.step = data[1];
}
if (data[2] != null) {
element.min = data[2];
}
if (data[3] != null) {
element.max = data[3];
}
var exceptionCaught = false;
try {
if (data[4] != null) {
element.stepUp(data[4]);
} else {
element.stepUp();
if (data[0] != null) {
element.setAttribute('value', data[0]);
}
is(element.value, data[5], "The value should be " + data[5]);
} catch (e) {
exceptionCaught = true;
is(element.value, data[0], e.name + "The value should not have changed");
is(e.name, 'InvalidStateError',
"It should be a InvalidStateError exception.");
} finally {
is(exceptionCaught, data[6], "exception status should be " + data[6]);
if (data[1] != null) {
element.step = data[1];
}
if (data[2] != null) {
element.min = data[2];
}
if (data[3] != null) {
element.max = data[3];
}
var exceptionCaught = false;
try {
if (data[4] != null) {
element.stepUp(data[4]);
} else {
element.stepUp();
}
is(element.value, data[5], "The value should be " + data[5]);
} catch (e) {
exceptionCaught = true;
is(element.value, data[0], e.name + "The value should not have changed");
is(e.name, 'InvalidStateError',
"It should be a InvalidStateError exception.");
} finally {
is(exceptionCaught, data[6], "exception status should be " + data[6]);
}
}
}
}
@ -335,8 +478,9 @@ SpecialPowers.pushPrefEnv({'set': [["dom.experimental_forms", true]]}, function(
checkPresence();
checkAvailability();
checkStepDownForNumber();
checkStepUpForNumber();
checkStepDown();
checkStepUp();
SimpleTest.finish();
});