Bug 781573 - Implement the step attribute for <input type=time>. r=smaug

--HG--
extra : rebase_source : 802145c921a679d041f83212e872cc5583ed4cc9
This commit is contained in:
Mounir Lamouri 2013-02-06 22:56:49 +00:00
Родитель 4a0ad76a83
Коммит 1d5d1d991e
7 изменённых файлов: 645 добавлений и 348 удалений

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

@ -180,7 +180,10 @@ static const nsAttrValue::EnumTable* kInputDefaultInputmode = &kInputInputmodeTa
const double nsHTMLInputElement::kStepScaleFactorDate = 86400000;
const double nsHTMLInputElement::kStepScaleFactorNumber = 1;
const double nsHTMLInputElement::kStepScaleFactorTime = 1000;
const double nsHTMLInputElement::kDefaultStepBase = 0;
const double nsHTMLInputElement::kDefaultStep = 1;
const double nsHTMLInputElement::kDefaultStepTime = 60;
const double nsHTMLInputElement::kStepAny = 0;
#define NS_INPUT_ELEMENT_STATE_IID \
@ -1486,7 +1489,8 @@ double
nsHTMLInputElement::GetStepBase() const
{
MOZ_ASSERT(mType == NS_FORM_INPUT_NUMBER ||
mType == NS_FORM_INPUT_DATE,
mType == NS_FORM_INPUT_DATE ||
mType == NS_FORM_INPUT_TIME,
"Check that kDefaultStepBase is correct for this new type");
double stepBase;
@ -4489,24 +4493,22 @@ nsHTMLInputElement::GetStep() const
{
MOZ_ASSERT(DoesStepApply(), "GetStep() can only be called if @step applies");
// NOTE: should be defaultStep, which is 1 for type=number and date.
double step = 1;
if (!HasAttr(kNameSpaceID_None, nsGkAtoms::step)) {
return GetDefaultStep() * GetStepScaleFactor();
}
if (HasAttr(kNameSpaceID_None, nsGkAtoms::step)) {
nsAutoString stepStr;
GetAttr(kNameSpaceID_None, nsGkAtoms::step, stepStr);
nsAutoString stepStr;
GetAttr(kNameSpaceID_None, nsGkAtoms::step, stepStr);
if (stepStr.LowerCaseEqualsLiteral("any")) {
// The element can't suffer from step mismatch if there is no step.
return kStepAny;
}
if (stepStr.LowerCaseEqualsLiteral("any")) {
// The element can't suffer from step mismatch if there is no step.
return kStepAny;
}
nsresult ec;
step = stepStr.ToDouble(&ec);
if (NS_FAILED(ec) || step <= 0) {
// NOTE: we should use defaultStep, which is 1 for type=number and date.
step = 1;
}
nsresult ec;
double step = stepStr.ToDouble(&ec);
if (NS_FAILED(ec) || step <= 0) {
step = GetDefaultStep();
}
// TODO: This multiplication can lead to inexact results, we should use a
@ -5008,17 +5010,24 @@ nsHTMLInputElement::GetValidationMessage(nsAString& aValidationMessage,
ConvertNumberToString(valueLow, valueLowStr);
ConvertNumberToString(valueHigh, valueHighStr);
const PRUnichar* params[] = { valueLowStr.get(), valueHighStr.get() };
rv = nsContentUtils::FormatLocalizedString(nsContentUtils::eDOM_PROPERTIES,
"FormValidationStepMismatch",
params, message);
if (valueLowStr.Equals(valueHighStr)) {
const PRUnichar* params[] = { valueLowStr.get() };
rv = nsContentUtils::FormatLocalizedString(nsContentUtils::eDOM_PROPERTIES,
"FormValidationStepMismatchOneValue",
params, message);
} else {
const PRUnichar* params[] = { valueLowStr.get(), valueHighStr.get() };
rv = nsContentUtils::FormatLocalizedString(nsContentUtils::eDOM_PROPERTIES,
"FormValidationStepMismatch",
params, message);
}
} else {
nsAutoString valueLowStr;
ConvertNumberToString(valueLow, valueLowStr);
const PRUnichar* params[] = { valueLowStr.get() };
rv = nsContentUtils::FormatLocalizedString(nsContentUtils::eDOM_PROPERTIES,
"FormValidationStepMismatchWithoutMax",
"FormValidationStepMismatchOneValue",
params, message);
}
@ -5441,6 +5450,25 @@ nsHTMLInputElement::GetStepScaleFactor() const
return kStepScaleFactorDate;
case NS_FORM_INPUT_NUMBER:
return kStepScaleFactorNumber;
case NS_FORM_INPUT_TIME:
return kStepScaleFactorTime;
default:
MOZ_NOT_REACHED();
return MOZ_DOUBLE_NaN();
}
}
double
nsHTMLInputElement::GetDefaultStep() const
{
MOZ_ASSERT(DoesStepApply());
switch (mType) {
case NS_FORM_INPUT_DATE:
case NS_FORM_INPUT_NUMBER:
return kDefaultStep;
case NS_FORM_INPUT_TIME:
return kDefaultStepTime;
default:
MOZ_NOT_REACHED();
return MOZ_DOUBLE_NaN();

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

@ -486,7 +486,7 @@ protected:
/**
* Returns if the step attribute apply for the current type.
*/
bool DoesStepApply() const { return DoesMinMaxApply() && mType != NS_FORM_INPUT_TIME; }
bool DoesStepApply() const { return DoesMinMaxApply(); }
/**
* Returns if stepDown and stepUp methods apply for the current type.
@ -703,6 +703,12 @@ protected:
*/
double GetStepBase() const;
/**
* Returns the default step for the current type.
* @return the default step for the current type.
*/
double GetDefaultStep() const;
/**
* Apply a step change from stepUp or stepDown by multiplying aStep by the
* current step value.
@ -768,10 +774,16 @@ protected:
// Step scale factor values, for input types that have one.
static const double kStepScaleFactorDate;
static const double kStepScaleFactorNumber;
static const double kStepScaleFactorTime;
// 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'.
// Default step used when there is no specified step.
static const double kDefaultStep;
static const double kDefaultStepTime;
// Float value returned by GetStep() when the step attribute is set to 'any'.
static const double kStepAny;
/**

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

@ -239,6 +239,9 @@ for (var test of data) {
break;
case 'time':
// Don't worry about that.
input.step = 'any';
input.max = '10:10';
input.value = '10:09';
checkValidity(input, true, apply, apply);

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

@ -237,6 +237,9 @@ for (var test of data) {
"validation message");
break;
case 'time':
// Don't worry about that.
input.step = 'any';
input.min = '20:20';
input.value = '20:20:01';
checkValidity(input, true, apply, apply);

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

@ -19,30 +19,30 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=635553
/** Test for Bug 635553 **/
var types = [
[ 'hidden', false ],
[ 'text', false ],
[ 'search', false ],
[ 'tel', false ],
[ 'url', false ],
[ 'email', false ],
[ 'password', false ],
[ 'datetime', true, true ],
[ 'date', true ],
[ 'month', true, true ],
[ 'week', true, true ],
[ 'time', true ],
[ 'datetime-local', true, true ],
[ 'number', true ],
[ 'range', true, true ],
[ 'color', false, true ],
[ 'checkbox', false ],
[ 'radio', false ],
[ 'file', false ],
[ 'submit', false ],
[ 'image', false ],
[ 'reset', false ],
[ 'button', false ],
var data = [
{ type: 'hidden', apply: false },
{ type: 'text', apply: false },
{ type: 'search', apply: false },
{ type: 'tel', apply: false },
{ type: 'url', apply: false },
{ type: 'email', apply: false },
{ type: 'password', apply: false },
{ type: 'datetime', apply: true, todo: true },
{ type: 'date', apply: true },
{ type: 'month', apply: true, todo: true },
{ type: 'week', apply: true, todo: true },
{ type: 'time', apply: true },
{ type: 'datetime-local', apply: true, todo: true },
{ type: 'number', apply: true },
{ type: 'range', apply: true, todo: true },
{ type: 'color', apply: false, todo: true },
{ type: 'checkbox', apply: false },
{ type: 'radio', apply: false },
{ type: 'file', apply: false },
{ type: 'submit', apply: false },
{ type: 'image', apply: false },
{ type: 'reset', apply: false },
{ type: 'button', apply: false },
];
function getFreshElement(type) {
@ -63,9 +63,18 @@ function checkValidity(aElement, aValidity, aApply, aData)
if (aValidity) {
is(aElement.validationMessage, "", "There should be no validation message.");
} else {
is(aElement.validationMessage, "Please select a valid value. " +
"The two nearest valid values are " + aData.low + " and " + aData.high + ".",
"There should be a validation message.");
if (aElement.validity.rangeUnderflow) {
is(aElement.validationMessage, "Please select a value that is higher than " +
aElement.min + ".", "There should be a validation message.");
} else if (aData.low == aData.high) {
is(aElement.validationMessage, "Please select a valid value. " +
"The nearest valid value is " + aData.low + ".",
"There should be a validation message.");
} else {
is(aElement.validationMessage, "Please select a valid value. " +
"The two nearest valid values are " + aData.low + " and " + aData.high + ".",
"There should be a validation message.");
}
}
is(aElement.mozMatchesSelector(":valid"), aElement.willValidate && aValidity,
@ -76,327 +85,441 @@ function checkValidity(aElement, aValidity, aApply, aData)
SimpleTest.waitForExplicitFinish();
SpecialPowers.pushPrefEnv({'set': [["dom.experimental_forms", true]]}, function() {
for (var data of types) {
var input = getFreshElement(data[0]);
var apply = data[1];
for (var test of data) {
var input = getFreshElement(test.type);
var apply = test.apply;
if (data[2]) {
todo_is(input.type, data[0], data[0] + " isn't implemented yet");
continue;
}
if (input.type == 'time') {
if (test.todo) {
todo_is(input.type, test.type, test.type + " isn't implemented yet");
continue;
}
// The element should be valid, there should be no step mismatch.
checkValidity(input, true, apply);
input.step = '0';
checkValidity(input, true, apply);
if (input.type == 'url') {
input.value = 'http://mozilla.org';
checkValidity(input, true, apply);
} else if (input.type == 'email') {
input.value = 'foo@bar.com';
checkValidity(input, true, apply);
} else if (input.type == 'file') {
// Need privileges to set a filename with .value.
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
var dirSvc = Components.classes["@mozilla.org/file/directory_service;1"]
.getService(Components.interfaces.nsIProperties);
var file = dirSvc.get("ProfD", Components.interfaces.nsIFile);
file.append('635499_file');
var outStream = Components.classes["@mozilla.org/network/file-output-stream;1"].
createInstance(Components.interfaces.nsIFileOutputStream);
outStream.init(file, 0x02 | 0x08 | 0x20, // write, create, truncate
0666, 0);
outStream.write("foo", 3);
outStream.close();
input.value = file.path;
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" });
// Checks to do for all types that support step:
// - check for @step=0,
// - check for @step behind removed,
// - check for @step being 'any' with different case variations.
switch (input.type) {
case 'text':
case 'hidden':
case 'search':
case 'password':
case 'tel':
case 'radio':
case 'checkbox':
case 'reset':
case 'button':
case 'submit':
case 'image':
input.value = '0';
checkValidity(input, true, apply);
break;
case 'url':
input.value = 'http://mozilla.org';
checkValidity(input, true, apply);
break;
case 'email':
input.value = 'foo@bar.com';
checkValidity(input, true, apply);
break;
case 'file':
// Need privileges to set a filename with .value.
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
var dirSvc = Components.classes["@mozilla.org/file/directory_service;1"]
.getService(Components.interfaces.nsIProperties);
var file = dirSvc.get("ProfD", Components.interfaces.nsIFile);
file.append('635499_file');
var outStream = Components.classes["@mozilla.org/network/file-output-stream;1"].
createInstance(Components.interfaces.nsIFileOutputStream);
outStream.init(file, 0x02 | 0x08 | 0x20, // write, create, truncate
0666, 0);
outStream.write("foo", 3);
outStream.close();
input.value = file.path;
checkValidity(input, true, apply);
file.remove(false);
break;
case '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 invalid, every date is valid
input.step = 0;
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';
input.value = '1966-12-12';
checkValidity(input, true, apply);
input.step = 'ANY';
input.value = '2013-02-03';
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 = '1951-01-08';
checkValidity(input, false, apply, { low: "1951-01-07", high: "1951-01-10" });
input.value = '1991-01-01';
checkValidity(input, false, apply, { low: "1984-06-05", high: "1992-08-22" });
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 = '1984-06-05';
checkValidity(input, true, apply);
input.value = '1971-01-01';
checkValidity(input, false, apply, { low: "1968-01-01", high: "1976-03-19" });
input.value = '1992-08-22';
checkValidity(input, true, apply);
input.value = '1991-01-01';
checkValidity(input, false, apply, { low: "1984-06-05", high: "1992-08-22" });
input.step = '1.1';
input.min = '1991-01-01';
input.value = '1991-01-01';
checkValidity(input, true, apply);
input.value = '1984-06-05';
checkValidity(input, true, apply);
input.value = '1991-01-02';
checkValidity(input, false, apply, { low: "1991-01-01", high: "1991-01-12" });
input.value = '1992-08-22';
checkValidity(input, true, apply);
input.value = '1991-01-12';
checkValidity(input, true, apply);
input.step = '1.1';
input.min = '1991-01-01';
input.value = '1991-01-01';
checkValidity(input, true, apply);
input.step = '1.1';
input.min = '1969-12-20';
input.value = '1969-12-20';
checkValidity(input, true, apply);
input.value = '1991-01-02';
checkValidity(input, false, apply, { low: "1991-01-01", high: "1991-01-12" });
input.value = '1969-12-21';
checkValidity(input, false, apply, { low: "1969-12-20", high: "1969-12-31" });
input.value = '1991-01-12';
checkValidity(input, true, apply);
input.value = '1969-12-31';
checkValidity(input, true, apply);
input.step = '1.1';
input.min = '1969-12-20';
input.value = '1969-12-20';
checkValidity(input, true, apply);
break;
case 'number':
// When step=0, the allowed step is 1.
input.step = '0';
input.value = '1.2';
checkValidity(input, false, apply, { low: 1, high: 2 });
input.value = '1969-12-21';
checkValidity(input, false, apply, { low: "1969-12-20", high: "1969-12-31" });
input.value = '1';
checkValidity(input, true, apply);
input.value = '1969-12-31';
checkValidity(input, true, apply);
input.value = '0';
checkValidity(input, true, apply);
} else {
// When step=0, the allowed step is 1.
input.value = '1.2';
checkValidity(input, false, apply, { low: 1, high: 2 });
// When step is NaN, the allowed step value is 1.
input.step = 'foo';
input.value = '1';
checkValidity(input, true, apply);
input.value = '1';
checkValidity(input, true, apply);
input.value = '1.5';
checkValidity(input, false, apply, { low: 1, high: 2 });
input.value = '0';
checkValidity(input, true, apply);
// When step is negative, the allowed step value is 1.
input.step = '-0.1';
checkValidity(input, false, apply, { low: 1, high: 2 });
// When step is NaN, the allowed step value is 1.
input.step = 'foo';
input.value = '1';
checkValidity(input, true, apply);
input.value = '1';
checkValidity(input, true, apply);
input.value = '1.5';
checkValidity(input, false, apply, { low: 1, high: 2 });
// When step is missing, the allowed step value is 1.
input.removeAttribute('step');
input.value = '1.5';
checkValidity(input, false, apply, { low: 1, high: 2 });
// When step is negative, the allowed step value is 1.
input.step = '-0.1';
checkValidity(input, false, apply, { low: 1, high: 2 });
input.value = '1';
checkValidity(input, true, apply);
input.value = '1';
checkValidity(input, true, apply);
// When step is 'any', all values are fine wrt to step.
input.step = 'any';
checkValidity(input, true, apply);
// When step is missing, the allowed step value is 1.
input.removeAttribute('step');
input.value = '1.5';
checkValidity(input, false, apply, { low: 1, high: 2 });
input.step = 'aNy';
input.value = '1337';
checkValidity(input, true, apply);
input.value = '1';
checkValidity(input, true, apply);
input.step = 'AnY';
input.value = '0.1';
checkValidity(input, true, apply);
// When step is 'any', all values are fine wrt to step.
input.step = 'any';
checkValidity(input, true, apply);
input.step = 'ANY';
input.value = '-13.37';
checkValidity(input, true, apply);
input.step = 'aNy';
input.value = '1337';
checkValidity(input, true, apply);
input.step = 'AnY';
input.value = '0.1';
checkValidity(input, true, apply);
input.step = 'ANY';
input.value = '-13.37';
checkValidity(input, true, apply);
// When min is set to a valid float, there is a step base.
input.min = '1';
input.step = '2';
input.value = '3';
checkValidity(input, true, apply);
input.value = '2';
checkValidity(input, false, apply, { low: 1, high: 3 });
input.removeAttribute('step'); // step = 1
input.min = '0.5';
input.value = '5.5';
checkValidity(input, true, apply);
input.value = '1';
checkValidity(input, false, apply, { low: 0.5, high: 1.5 });
input.min = '-0.1';
input.step = '1';
input.value = '0.9';
checkValidity(input, true, apply);
input.value = '0.1';
checkValidity(input, false, apply, { low: -0.1, high: 0.9 });
// When min is set to NaN, there is no step base (step base=0 actually).
input.min = 'foo';
input.step = '1';
input.value = '1';
checkValidity(input, true, apply);
input.value = '0.5';
checkValidity(input, false, apply, { low: 0, high: 1 });
input.min = '';
input.value = '1';
checkValidity(input, true, apply);
input.value = '0.5';
checkValidity(input, false, apply, { low: 0, high: 1 });
input.removeAttribute('min');
// If value isn't a number, the element isn't invalid.
input.value = '';
checkValidity(input, true, apply);
// Regular situations.
input.step = '2';
input.value = '1.5';
checkValidity(input, false, apply, { low: 0, high: 2 });
input.value = '42.0';
checkValidity(input, true, apply);
input.step = '0.1';
input.value = '-0.1';
checkValidity(input, true, apply);
input.step = '2';
input.removeAttribute('min');
input.max = '10';
input.value = '-9';
checkValidity(input, false, apply, {low: -10, high: -8});
// If there is a value defined but no min, the step base is the value.
input = getFreshElement(data[0]);
input.setAttribute('value', '1');
input.step = 2;
checkValidity(input, true, apply);
input.value = 3;
checkValidity(input, true, apply);
input.value = 2;
checkValidity(input, false, apply, {low: 1, high: 3});
// Should also work with defaultValue.
input = getFreshElement(data[0]);
input.defaultValue = 1;
input.step = 2;
checkValidity(input, true, apply);
input.value = 3;
checkValidity(input, true, apply);
input.value = 2;
checkValidity(input, false, apply, {low: 1, high: 3});
}
if (input.type == 'number') {
// Check that when the higher value is higher than max, we don't show it.
input = getFreshElement(data[0]);
input.step = '2';
input.min = '1';
input.max = '10.9';
input.value = '10';
is(input.validationMessage, "Please select a valid value. " +
"The nearest valid value is 9.",
"The validation message should not include the higher value.");
// When min is set to a valid float, there is a step base.
input.min = '1';
input.step = '2';
input.value = '3';
checkValidity(input, true, apply);
input.value = '2';
checkValidity(input, false, apply, { low: 1, high: 3 });
input.removeAttribute('step'); // step = 1
input.min = '0.5';
input.value = '5.5';
checkValidity(input, true, apply);
input.value = '1';
checkValidity(input, false, apply, { low: 0.5, high: 1.5 });
input.min = '-0.1';
input.step = '1';
input.value = '0.9';
checkValidity(input, true, apply);
input.value = '0.1';
checkValidity(input, false, apply, { low: -0.1, high: 0.9 });
// When min is set to NaN, there is no step base (step base=0 actually).
input.min = 'foo';
input.step = '1';
input.value = '1';
checkValidity(input, true, apply);
input.value = '0.5';
checkValidity(input, false, apply, { low: 0, high: 1 });
input.min = '';
input.value = '1';
checkValidity(input, true, apply);
input.value = '0.5';
checkValidity(input, false, apply, { low: 0, high: 1 });
input.removeAttribute('min');
// If value isn't a number, the element isn't invalid.
input.value = '';
checkValidity(input, true, apply);
// Regular situations.
input.step = '2';
input.value = '1.5';
checkValidity(input, false, apply, { low: 0, high: 2 });
input.value = '42.0';
checkValidity(input, true, apply);
input.step = '0.1';
input.value = '-0.1';
checkValidity(input, true, apply);
input.step = '2';
input.removeAttribute('min');
input.max = '10';
input.value = '-9';
checkValidity(input, false, apply, {low: -10, high: -8});
// If there is a value defined but no min, the step base is the value.
input = getFreshElement(test.type);
input.setAttribute('value', '1');
input.step = 2;
checkValidity(input, true, apply);
input.value = 3;
checkValidity(input, true, apply);
input.value = 2;
checkValidity(input, false, apply, {low: 1, high: 3});
// Should also work with defaultValue.
input = getFreshElement(test.type);
input.defaultValue = 1;
input.step = 2;
checkValidity(input, true, apply);
input.value = 3;
checkValidity(input, true, apply);
input.value = 2;
checkValidity(input, false, apply, {low: 1, high: 3});
// Check that when the higher value is higher than max, we don't show it.
input = getFreshElement(test.type);
input.step = '2';
input.min = '1';
input.max = '10.9';
input.value = '10';
is(input.validationMessage, "Please select a valid value. " +
"The nearest valid value is 9.",
"The validation message should not include the higher value.");
break;
case 'time':
// Tests invalid step values. That defaults to step = 1 minute (60).
var values = [ '0', '-1', 'foo', 'any', 'ANY', 'aNy' ];
for (var value of values) {
input.step = value;
input.value = '19:06:00';
checkValidity(input, true, apply);
input.value = '19:06:51';
if (value.toLowerCase() != 'any') {
checkValidity(input, false, apply, {low: '19:06', high: '19:07'});
} else {
checkValidity(input, true, apply);
}
}
// No step means that we use the default step value.
input.removeAttribute('step');
input.value = '19:06:00';
checkValidity(input, true, apply);
input.value = '19:06:51';
checkValidity(input, false, apply, {low: '19:06', high: '19:07'});
var tests = [
// With step=1, we allow values by the second.
{ step: '1', value: '19:11:01', min: '00:00', result: true },
{ step: '1', value: '19:11:01.001', min: '00:00', result: false,
low: '19:11:01', high: '19:11:02' },
{ step: '1', value: '19:11:01.1', min: '00:00', result: false,
low: '19:11:01', high: '19:11:02' },
// When step >= 86400000, only the minimum value is valid.
// This is actually @value if there is no @min.
{ step: '86400000', value: '00:00', result: true },
{ step: '86400000', value: '00:01', result: true },
{ step: '86400000', value: '00:00', min: '00:01', result: false },
{ step: '86400000', value: '00:01', min: '00:00', result: false,
low: '00:00', high: '00:00' },
// When step < 1, it should just work.
{ step: '0.1', value: '15:05:05.1', min: '00:00', result: true },
{ step: '0.1', value: '15:05:05.101', min: '00:00', result: false,
low: '15:05:05.100', high: '15:05:05.200' },
{ step: '0.2', value: '15:05:05.2', min: '00:00', result: true },
{ step: '0.2', value: '15:05:05.1', min: '00:00', result: false,
low: '15:05:05', high: '15:05:05.200' },
{ step: '0.01', value: '15:05:05.01', min: '00:00', result: true },
{ step: '0.01', value: '15:05:05.011', min: '00:00', result: false,
low: '15:05:05.010', high: '15:05:05.020' },
{ step: '0.02', value: '15:05:05.02', min: '00:00', result: true },
{ step: '0.02', value: '15:05:05.01', min: '00:00', result: false,
low: '15:05:05', high: '15:05:05.020' },
{ step: '0.002', value: '15:05:05.002', min: '00:00', result: true },
{ step: '0.002', value: '15:05:05.001', min: '00:00', result: false,
low: '15:05:05', high: '15:05:05.002' },
// When step<=0.001, any value is allowed.
{ step: '0.001', value: '15:05:05.001', min: '00:00', result: true },
{ step: '0.001', value: '15:05:05', min: '00:00', result: true },
{ step: '0.000001', value: '15:05:05', min: '00:00', result: true },
// This value has conversion to double issues.
{ step: '0.0000001', value: '15:05:05', min: '00:00', result: true,
todo: true },
// Some random values.
{ step: '100', value: '15:06:40', min: '00:00', result: true },
{ step: '100', value: '15:05:05.010', min: '00:00', result: false,
low: '15:05', high: '15:06:40' },
{ step: '3600', value: '15:00', min: '00:00', result: true },
{ step: '3600', value: '15:14', min: '00:00', result: false,
low: '15:00', high: '16:00' },
{ step: '7200', value: '14:00', min: '00:00', result: true },
{ step: '7200', value: '15:14', min: '00:00', result: false,
low: '14:00', high: '16:00' },
{ step: '7260', value: '14:07', min: '00:00', result: true },
{ step: '7260', value: '15:14', min: '00:00', result: false,
low: '14:07', high: '16:08' },
];
var type = test.type;
for (var test of tests) {
var input = getFreshElement(type);
input.step = test.step;
input.setAttribute('value', test.value);
if (test.min !== undefined) {
input.min = test.min;
}
if (test.todo) {
todo(input.validity.valid, test.result,
"This test should fail for the moment because of precission issues");
continue;
}
if (test.result) {
checkValidity(input, true, apply);
} else {
checkValidity(input, false, apply,
{ low: test.low, high: test.high });
}
}
break;
default:
ok(false, "Implement the tests for <input type='" + test.type + " >");
break;
}
}

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

@ -48,6 +48,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],
@ -58,7 +59,6 @@ function checkAvailability()
["datetime", true],
["month", true],
["week", true],
["time", true],
["datetime-local", true],
["range", true],
];
@ -246,6 +246,70 @@ function checkStepDown()
[ '2012-01-01', 'AnY', null, null, 1, null, true ],
[ '2012-01-01', 'aNy', null, null, 1, null, true ],
]},
{ type: 'time', data: [
// Regular case.
[ '16:39', null, null, null, null, '16:38', false ],
// Argument testing.
[ '16:40', null, null, null, 1, '16:39', false ],
[ '16:40', null, null, null, 5, '16:35', false ],
[ '16:40', null, null, null, -1, '16:41', false ],
[ '16:40', null, null, null, 0, '16:40', false ],
// hour/minutes/seconds wrapping.
[ '05:00', null, null, null, null, '04:59', false ],
[ '05:00:00', 1, null, null, null, '04:59:59', false ],
[ '05:00:00', 0.1, null, null, null, '04:59:59.900', false ],
[ '05:00:00', 0.01, null, null, null, '04:59:59.990', false ],
[ '05:00:00', 0.001, null, null, null, '04:59:59.999', false ],
// stepDown() on '00:00' gives '23:59'.
[ '00:00', null, null, null, 1, '23:59', false ],
[ '00:00', null, null, null, 3, '23:57', false ],
// Some random step values..
[ '16:56', '0.5', null, null, null, '16:55:59.500', false ],
[ '16:56', '2', null, null, null, '16:55:58', false ],
[ '16:56', '0.25',null, null, 4, '16:55:59', false ],
[ '16:57', '1.1', '16:00', null, 1, '16:56:59.900', false ],
[ '16:57', '1.1', '16:00', null, 2, '16:56:58.800', false ],
[ '16:57', '1.1', '16:00', null, 10, '16:56:50', false ],
[ '16:57', '1.1', '16:00', null, 11, '16:56:48.900', false ],
[ '16:57', '1.1', '16:00', null, 8, '16:56:52.200', false ],
// Invalid @step, means that we use the default value.
[ '17:01', '0', null, null, null, '17:00', false ],
[ '17:01', '-1', null, null, null, '17:00', false ],
[ '17:01', 'foo', null, null, null, '17:00', false ],
// Min values testing.
[ '17:02', '60', 'foo', null, 2, '17:00', false ],
[ '17:10', '60', '17:09', null, null, '17:09', false ],
[ '17:10', '60', '17:10', null, null, '17:10', false ],
[ '17:10', '60', '17:30', null, 1, '17:10', false ],
[ '17:10', '180', '17:05', null, null, '17:08', false ],
[ '17:10', '300', '17:10', '17:11', null, '17:10', false ],
// Max values testing.
[ '17:15', '60', null, 'foo', null, '17:14', false ],
[ '17:15', null, null, '17:20', null, '17:14', false ],
[ '17:15', null, null, '17:15', null, '17:14', false ],
[ '17:15', null, null, '17:13', 4, '17:11', false ],
[ '17:15', '120', null, '17:13', 3, '17:09', false ],
// Step mismatch.
[ '17:19', '120', '17:10', null, null, '17:18', false ],
[ '17:19', '120', '17:10', null, 2, '17:16', false ],
[ '17:19', '120', '17:18', '17:25', null, '17:18', false ],
[ '17:19', '120', null, null, null, '17:17', false ],
[ '17:19', '180', null, null, null, '17:16', false ],
// Clamping.
[ '17:22', null, null, '17:11', null, '17:11', false ],
[ '17:22', '120', '17:20', '17:22', null, '17:20', false ],
[ '17:22', '300', '17:12', '17:20', 10, '17:12', false ],
[ '17:22', '300', '17:18', '17:20', 2, '17:18', false ],
[ '17:22', '180', '17:00', '17:20', 15, '17:00', false ],
[ '17:22', '180', '17:10', '17:20', 2, '17:16', false ],
// value = "" (NaN).
[ '', null, null, null, null, '', false ],
// With step = 'any'.
[ '17:26', 'any', null, null, 1, null, true ],
[ '17:26', 'ANY', null, null, 1, null, true ],
[ '17:26', 'AnY', null, null, 1, null, true ],
[ '17:26', 'aNy', null, null, 1, null, true ],
]},
];
for (var test of testData) {
@ -429,6 +493,70 @@ function checkStepUp()
[ '2012-01-01', 'AnY', null, null, 1, null, true ],
[ '2012-01-01', 'aNy', null, null, 1, null, true ],
]},
{ type: 'time', data: [
// Regular case.
[ '16:39', null, null, null, null, '16:40', false ],
// Argument testing.
[ '16:40', null, null, null, 1, '16:41', false ],
[ '16:40', null, null, null, 5, '16:45', false ],
[ '16:40', null, null, null, -1, '16:39', false ],
[ '16:40', null, null, null, 0, '16:40', false ],
// hour/minutes/seconds wrapping.
[ '04:59', null, null, null, null, '05:00', false ],
[ '04:59:59', 1, null, null, null, '05:00', false ],
[ '04:59:59.900', 0.1, null, null, null, '05:00', false ],
[ '04:59:59.990', 0.01, null, null, null, '05:00', false ],
[ '04:59:59.999', 0.001, null, null, null, '05:00', false ],
// stepUp() on '23:59' gives '00:00'.
[ '23:59', null, null, null, 1, '00:00', false ],
[ '23:59', null, null, null, 3, '00:02', false ],
// Some random step values..
[ '16:56', '0.5', null, null, null, '16:56:00.500', false ],
[ '16:56', '2', null, null, null, '16:56:02', false ],
[ '16:56', '0.25',null, null, 4, '16:56:01', false ],
[ '16:57', '1.1', '16:00', null, 1, '16:57:01', false ],
[ '16:57', '1.1', '16:00', null, 2, '16:57:02.100', false ],
[ '16:57', '1.1', '16:00', null, 10, '16:57:10.900', false ],
[ '16:57', '1.1', '16:00', null, 11, '16:57:12', false ],
[ '16:57', '1.1', '16:00', null, 8, '16:57:08.700', false ],
// Invalid @step, means that we use the default value.
[ '17:01', '0', null, null, null, '17:02', false ],
[ '17:01', '-1', null, null, null, '17:02', false ],
[ '17:01', 'foo', null, null, null, '17:02', false ],
// Min values testing.
[ '17:02', '60', 'foo', null, 2, '17:04', false ],
[ '17:10', '60', '17:09', null, null, '17:11', false ],
[ '17:10', '60', '17:10', null, null, '17:11', false ],
[ '17:10', '60', '17:30', null, 1, '17:30', false ],
[ '17:10', '180', '17:05', null, null, '17:11', false ],
[ '17:10', '300', '17:10', '17:11', null,'17:10', false ],
// Max values testing.
[ '17:15', '60', null, 'foo', null, '17:16', false ],
[ '17:15', null, null, '17:20', null, '17:16', false ],
[ '17:15', null, null, '17:15', null, '17:15', false ],
[ '17:15', null, null, '17:13', 4, '17:15', false ],
[ '17:15', '120', null, '17:13', 3, '17:15', false ],
// Step mismatch.
[ '17:19', '120', '17:10', null, null, '17:20', false ],
[ '17:19', '120', '17:10', null, 2, '17:22', false ],
[ '17:19', '120', '17:18', '17:25', null, '17:20', false ],
[ '17:19', '120', null, null, null, '17:21', false ],
[ '17:19', '180', null, null, null, '17:22', false ],
// Clamping.
[ '17:22', null, null, '17:11', null, '17:22', false ],
[ '17:22', '120', '17:20', '17:22', null, '17:22', false ],
[ '17:22', '300', '17:12', '17:20', 10, '17:22', false ],
[ '17:22', '300', '17:18', '17:20', 2, '17:22', false ],
[ '17:22', '180', '17:00', '17:20', 15, '17:22', false ],
[ '17:22', '180', '17:10', '17:20', 2, '17:22', false ],
// value = "" (NaN).
[ '', null, null, null, null, '', false ],
// With step = 'any'.
[ '17:26', 'any', null, null, 1, null, true ],
[ '17:26', 'ANY', null, null, 1, null, true ],
[ '17:26', 'AnY', null, null, 1, null, true ],
[ '17:26', 'aNy', null, null, 1, null, true ],
]},
];
for (var test of testData) {

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

@ -40,14 +40,14 @@ FormValidationInvalidURL=Please enter a URL.
FormValidationPatternMismatch=Please match the requested format.
# LOCALIZATION NOTE (FormValidationPatternMismatchWithTitle): %S is the (possibly truncated) title attribute value.
FormValidationPatternMismatchWithTitle=Please match the requested format: %S.
# LOCALIZATION NOTE (FormValidationRangeOverflow): %S can be a number or a date.
# LOCALIZATION NOTE (FormValidationRangeOverflow): %S can be a number, a date or a time.
FormValidationRangeOverflow=Please select a value that is lower than %S.
# LOCALIZATION NOTE (FormValidationRangeUnderflow): %S can be a number or a date.
# LOCALIZATION NOTE (FormValidationRangeUnderflow): %S can be a number, a date or a time.
FormValidationRangeUnderflow=Please select a value that is higher than %S.
# LOCALIZATION NOTE (FormValidationStepMismatch): both %S can be a number or a date.
# LOCALIZATION NOTE (FormValidationStepMismatch): both %S can be a number, a date or a time.
FormValidationStepMismatch=Please select a valid value. The two nearest valid values are %S and %S.
# LOCALIZATION NOTE (FormValidationStepMismatchWitoutMax): %S can be a number or a date. This is called instead of FormValidationStepMismatch when the second value isn't valid because it's higher than the maximum allowed value.
FormValidationStepMismatchWithoutMax=Please select a valid value. The nearest valid value is %S.
# LOCALIZATION NOTE (FormValidationStepMismatchOneValue): %S can be a number, a date or a time. This is called instead of FormValidationStepMismatch when the second value is the same as the first.
FormValidationStepMismatchOneValue=Please select a valid value. The nearest valid value is %S.
GetAttributeNodeWarning=Use of getAttributeNode() is deprecated. Use getAttribute() instead.
SetAttributeNodeWarning=Use of setAttributeNode() is deprecated. Use setAttribute() instead.
GetAttributeNodeNSWarning=Use of getAttributeNodeNS() is deprecated. Use getAttributeNS() instead.