author | Raphael Catolino <rcatolino@mozilla.com> |
Sat, 22 Dec 2012 15:46:19 +0100 | |
changeset 117118 | 3ff974766a3e324a7a10227613239c58ebecf7c2 |
parent 117117 | 5705bb322879b3f0ca5b047ce84bb0f63e4732bb |
child 117119 | 0bb4773db082c0ecf241e9dac3d8ca3402efbb13 |
push id | 24086 |
push user | mlamouri@mozilla.com |
push date | Fri, 28 Dec 2012 12:55:44 +0000 |
treeherder | mozilla-central@0bb4773db082 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | mounir |
bugs | 769359 |
milestone | 20.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/content/html/content/src/nsHTMLInputElement.cpp +++ b/content/html/content/src/nsHTMLInputElement.cpp @@ -83,16 +83,17 @@ #include "nsContentCreatorFunctions.h" #include "nsContentUtils.h" #include "mozilla/dom/DirectionalityUtils.h" #include "nsRadioVisitor.h" #include "mozilla/LookAndFeel.h" #include "mozilla/Util.h" // DebugOnly #include "mozilla/Preferences.h" +#include "mozilla/MathAlgorithms.h" #include "nsIIDNService.h" #include <limits> // input type=date #include "jsapi.h" @@ -171,16 +172,18 @@ static const nsAttrValue::EnumTable kInp { "titlecase", NS_INPUT_INPUTMODE_TITLECASE }, { "autocapitalized", NS_INPUT_INPUTMODE_AUTOCAPITALIZED }, { 0 } }; // 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; #define NS_INPUT_ELEMENT_STATE_IID \ { /* dc3b3d14-23e2-4479-b513-7b369343e3a0 */ \ 0xdc3b3d14, \ 0x23e2, \ 0x4479, \ @@ -1196,53 +1199,64 @@ nsHTMLInputElement::GetList(nsIDOMHTMLEl CallQueryInterface(element, aValue); return NS_OK; } 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; - } - - JSObject* date = JS_NewDateObjectMsec(ctx, aValue); - if (!date) { - break; + { + JSContext* ctx = nsContentUtils::GetContextFromDocument(OwnerDoc()); + if (!ctx) { + return false; + } + + // The specs require |aValue| to be truncated. + aValue = floor(aValue); + + JSObject* date = JS_NewDateObjectMsec(ctx, aValue); + if (!date) { + return false; + } + + 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; + } + + aResultString.AppendPrintf("%04.0f-%02.0f-%02.0f", year.toNumber(), + month.toNumber() + 1, day.toNumber()); + + return true; } - - jsval year, month, day; - if(!JS::Call(ctx, date, "getUTCFullYear", 0, nullptr, &year)) { - break; - } - - if(!JS::Call(ctx, date, "getUTCMonth", 0, nullptr, &month)) { - break; - } - - if(!JS::Call(ctx, date, "getUTCDate", 0, nullptr, &day)) { - break; - } - - value.AppendPrintf("%04.0f-%02.0f-%02.0f", year.toNumber(), - month.toNumber() + 1, day.toNumber()); - } - break; - } - - SetValue(value); + default: + MOZ_NOT_REACHED(); + return false; + } } NS_IMETHODIMP nsHTMLInputElement::GetValueAsDate(JSContext* aCtx, jsval* aDate) { if (mType != NS_FORM_INPUT_DATE) { aDate->setNull(); return NS_OK; @@ -1410,16 +1424,31 @@ nsHTMLInputElement::ApplyStep(int32_t aS } else if (aStep < 0) { value -= NS_floorModulo(value - GetStepBase(), step); value += step; } } 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 && value <= min) { MOZ_ASSERT(!MOZ_DOUBLE_IS_NaN(min)); // min can't be NaN if we are here! value = min; // Same goes for stepDown() and max. } else if (GetValidityState(VALIDITY_STATE_RANGE_OVERFLOW) && aStep < 0 && @@ -4250,44 +4279,42 @@ nsHTMLInputElement::DoesMinMaxApply() co return false; #endif // DEBUG } } double nsHTMLInputElement::GetStep() const { - NS_ASSERTION(mType == NS_FORM_INPUT_NUMBER, - "We can't be there if type!=number!"); - - // NOTE: should be defaultStep * defaultStepScaleFactor, - // which is 1 for 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, which is 1 for type=number and date. double step = 1; if (HasAttr(kNameSpaceID_None, nsGkAtoms::step)) { 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; } 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 NS_IMETHODIMP nsHTMLInputElement::SetCustomValidity(const nsAString& aError) { nsIConstraintValidation::SetCustomValidity(aError); @@ -4468,16 +4495,25 @@ nsHTMLInputElement::HasStepMismatch() co return false; } double step = GetStep(); if (step == kStepAny) { 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; } void nsHTMLInputElement::UpdateTooLongValidityState() { // TODO: this code will be re-enabled with bug 613016 and bug 613019. @@ -4743,35 +4779,45 @@ nsHTMLInputElement::GetValidationMessage nsXPIDLString message; double value = GetValueAsDouble(); MOZ_ASSERT(!MOZ_DOUBLE_IS_NaN(value)); 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); double valueHigh = value + step - NS_floorModulo(value - stepBase, step); double max = GetMaxAsDouble(); 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, "FormValidationStepMismatch", params, message); } else { nsAutoString valueLowStr; - valueLowStr.AppendFloat(valueLow); + ConvertNumberToString(valueLow, valueLowStr); const PRUnichar* params[] = { valueLowStr.get() }; rv = nsContentUtils::FormatLocalizedString(nsContentUtils::eDOM_PROPERTIES, "FormValidationStepMismatchWithoutMax", params, message); } aValidationMessage = message; @@ -5178,16 +5224,32 @@ nsHTMLInputElement::GetFilterFromAccept( } filter = tokenFilter; } } 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) { if (aIsFocused) { // If the invalid UI is shown, we should show it while focusing (and // update). Otherwise, we should not. mCanShowInvalidUI = !IsValid() && ShouldShowValidityUI();
--- a/content/html/content/src/nsHTMLInputElement.h +++ b/content/html/content/src/nsHTMLInputElement.h @@ -567,16 +567,29 @@ protected: * or parse a number string to its value if type=number. * @param aValue the string to be parsed. * @param aResultValue the timestamp as a double. * @result whether the parsing was successful. */ 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. * @return whether the string is a valid date. * Note : this function does not consider the empty string as valid. */ bool IsValidDate(nsAString& aValue) const; /** @@ -614,16 +627,23 @@ protected: double GetMinAsDouble() const; /** * Returns the max attribute as a double. * Returns NaN if the max attribute isn't a valid floating point number. */ 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. * * @return the current step value. */ double GetStep() const; @@ -683,16 +703,20 @@ protected: * The value of the input element when first initialized and it is updated * when the element is either changed through a script, focused or dispatches * a change event. This is to ensure correct future change event firing. * NB: This is ONLY applicable where the element is a text control. ie, * where type= "text", "email", "search", "tel", "url" or "password". */ 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'. static const double kStepAny; /** * The type of this input (<input type=...>) as an integer. * @see nsIFormControl.h (specifically NS_FORM_INPUT_*)
--- a/content/html/content/test/forms/test_step_attribute.html +++ b/content/html/content/test/forms/test_step_attribute.html @@ -23,17 +23,17 @@ var types = [ [ 'hidden', false ], [ 'text', false ], [ 'search', false ], [ 'tel', false ], [ 'url', false ], [ 'email', false ], [ 'password', false ], [ 'datetime', true, true ], - [ 'date', true, true ], + [ 'date', true ], [ 'month', true, true ], [ 'week', true, true ], [ 'time', true, true ], [ 'datetime-local', true, true ], [ 'number', true ], [ 'range', true, true ], [ 'color', false, true ], [ 'checkbox', false ], @@ -110,16 +110,142 @@ for (var data of types) { 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" }); + + 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'; checkValidity(input, false, apply, { low: 1, high: 2 }); input.value = '1'; checkValidity(input, true, apply);
--- a/content/html/content/test/forms/test_stepup_stepdown.html +++ b/content/html/content/test/forms/test_stepup_stepdown.html @@ -42,25 +42,25 @@ function checkAvailability() ["checkbox", false], ["radio", false], ["file", false], ["submit", false], ["image", false], ["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], ]; var todoList = [ ["datetime", true], - ["date", true], ["month", true], ["week", true], ["time", true], ["datetime-local", true], ["range", true], ]; var element = document.createElement("input"); @@ -102,22 +102,23 @@ function checkAvailability() } catch (e) { exceptionCaught = true; } todo_is(exceptionCaught, !data[1], "stepUp() availability is not correct"); } } -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. [ '1', null, null, null, 1, '0', false ], [ '9', null, null, null, 9, '0', false ], [ '1', null, null, null, -1, '2', false ], [ '1', null, null, null, 0, '1', false ], // Float values are rounded to integer (1.1 -> 1). @@ -173,64 +174,134 @@ function checkStepDownForNumber() [ '', null, null, null, null, '', false ], // With step = 'any'. [ '0', 'any', null, null, 1, null, true ], [ '0', 'ANY', null, null, 1, null, true ], [ '0', 'AnY', null, null, 1, null, true ], [ '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'; - - if (data[0] != null) { - element.setAttribute('value', data[0]); - } - - if (data[1] != null) { - element.step = data[1]; - } + for (var test of testData) { + for (var data of test.data) { + var element = document.createElement("input"); + element.type = test.type; - if (data[2] != null) { - element.min = data[2]; - } + if (data[0] != null) { + element.setAttribute('value', data[0]); + } - if (data[3] != null) { - element.max = data[3]; - } + if (data[1] != null) { + element.step = data[1]; + } - var exceptionCaught = false; - try { - if (data[4] != null) { - element.stepDown(data[4]); - } else { - element.stepDown(); + if (data[2] != null) { + element.min = data[2]; } - 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[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. [ '1', null, null, null, 1, '2', false ], [ '9', null, null, null, 9, '18', false ], [ '1', null, null, null, -1, '0', false ], [ '1', null, null, null, 0, '1', false ], // Float values are rounded to integer (1.1 -> 1). @@ -283,65 +354,138 @@ function checkStepUpForNumber() [ '', null, null, null, null, '', false ], // With step = 'any'. [ '0', 'any', null, null, 1, null, true ], [ '0', 'ANY', null, null, 1, null, true ], [ '0', 'AnY', null, null, 1, null, true ], [ '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'; - - if (data[0] != null) { - element.setAttribute('value', data[0]); - } - - if (data[1] != null) { - element.step = data[1]; - } + for (var test of testData) { + for (var data of test.data) { + var element = document.createElement("input"); + element.type = test.type; - if (data[2] != null) { - element.min = data[2]; - } + if (data[0] != null) { + element.setAttribute('value', data[0]); + } - if (data[3] != null) { - element.max = data[3]; - } + if (data[1] != null) { + element.step = data[1]; + } - var exceptionCaught = false; - try { - if (data[4] != null) { - element.stepUp(data[4]); - } else { - element.stepUp(); + if (data[2] != null) { + element.min = data[2]; } - 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[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]); + } } } } SimpleTest.waitForExplicitFinish(); SpecialPowers.pushPrefEnv({'set': [["dom.experimental_forms", true]]}, function() { checkPresence(); checkAvailability(); -checkStepDownForNumber(); -checkStepUpForNumber(); + +checkStepDown(); +checkStepUp(); SimpleTest.finish(); }); </script> </pre> </body> </html>