From 1d5d1d991e03d8134bbe776b2fac46929fb5a1de Mon Sep 17 00:00:00 2001 From: Mounir Lamouri Date: Wed, 6 Feb 2013 22:56:49 +0000 Subject: [PATCH] Bug 781573 - Implement the step attribute for . r=smaug --HG-- extra : rebase_source : 802145c921a679d041f83212e872cc5583ed4cc9 --- .../html/content/src/nsHTMLInputElement.cpp | 70 +- content/html/content/src/nsHTMLInputElement.h | 16 +- .../test/forms/test_max_attribute.html | 3 + .../test/forms/test_min_attribute.html | 3 + .../test/forms/test_step_attribute.html | 761 ++++++++++-------- .../test/forms/test_stepup_stepdown.html | 130 ++- dom/locales/en-US/chrome/dom/dom.properties | 10 +- 7 files changed, 645 insertions(+), 348 deletions(-) diff --git a/content/html/content/src/nsHTMLInputElement.cpp b/content/html/content/src/nsHTMLInputElement.cpp index aa3609dbd4f2..7ac35fb5cc32 100644 --- a/content/html/content/src/nsHTMLInputElement.cpp +++ b/content/html/content/src/nsHTMLInputElement.cpp @@ -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(); diff --git a/content/html/content/src/nsHTMLInputElement.h b/content/html/content/src/nsHTMLInputElement.h index 6c05359c826f..1ef427db1df9 100644 --- a/content/html/content/src/nsHTMLInputElement.h +++ b/content/html/content/src/nsHTMLInputElement.h @@ -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; /** diff --git a/content/html/content/test/forms/test_max_attribute.html b/content/html/content/test/forms/test_max_attribute.html index 4cb7591966c9..25cefba7825d 100644 --- a/content/html/content/test/forms/test_max_attribute.html +++ b/content/html/content/test/forms/test_max_attribute.html @@ -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); diff --git a/content/html/content/test/forms/test_min_attribute.html b/content/html/content/test/forms/test_min_attribute.html index 6dace7f7a055..6284e31588c6 100644 --- a/content/html/content/test/forms/test_min_attribute.html +++ b/content/html/content/test/forms/test_min_attribute.html @@ -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); diff --git a/content/html/content/test/forms/test_step_attribute.html b/content/html/content/test/forms/test_step_attribute.html index 096ff7af2610..34ee26e6bc8c 100644 --- a/content/html/content/test/forms/test_step_attribute.html +++ b/content/html/content/test/forms/test_step_attribute.html @@ -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