LibWeb: Handle “default step”/“step scale factor” for more input types

This change adds “default step” and “step scale factor” handling for all
remaining HTMLInputElement input types for which the spec defines such
and that we didn’t yet have handling for.
This commit is contained in:
sideshowbarker 2025-03-09 04:01:06 +09:00 committed by Tim Ledbetter
commit cfc1fd7305
Notes: github-actions[bot] 2025-04-14 08:44:14 +00:00
13 changed files with 435 additions and 4 deletions

View file

@ -2475,8 +2475,23 @@ double HTMLInputElement::default_step() const
if (type_state() == TypeAttributeState::Time) if (type_state() == TypeAttributeState::Time)
return 60; return 60;
dbgln("HTMLInputElement::default_step() not implemented for input type {}", type()); // https://html.spec.whatwg.org/multipage/input.html#date-state-(type=date):concept-input-step-default
return 0; if (type_state() == TypeAttributeState::Date)
return 1;
// https://html.spec.whatwg.org/multipage/input.html#month-state-(type=month):concept-input-step-default
if (type_state() == TypeAttributeState::Month)
return 1;
// https://html.spec.whatwg.org/multipage/input.html#week-state-(type=week):concept-input-step-default
if (type_state() == TypeAttributeState::Week)
return 1;
// https://html.spec.whatwg.org/multipage/input.html#local-date-and-time-state-(type=datetime-local):concept-input-step-default
if (type_state() == TypeAttributeState::LocalDateAndTime)
return 60;
VERIFY_NOT_REACHED();
} }
// https://html.spec.whatwg.org/multipage/input.html#concept-input-step-scale // https://html.spec.whatwg.org/multipage/input.html#concept-input-step-scale
@ -2494,8 +2509,23 @@ double HTMLInputElement::step_scale_factor() const
if (type_state() == TypeAttributeState::Time) if (type_state() == TypeAttributeState::Time)
return 1000; return 1000;
dbgln("HTMLInputElement::step_scale_factor() not implemented for input type {}", type()); // https://html.spec.whatwg.org/multipage/input.html#date-state-(type=date):concept-input-step-scale
return 0; if (type_state() == TypeAttributeState::Date)
return 86400000;
// https://html.spec.whatwg.org/multipage/input.html#month-state-(type=month):concept-input-step-scale
if (type_state() == TypeAttributeState::Month)
return 1;
// https://html.spec.whatwg.org/multipage/input.html#week-state-(type=week):concept-input-step-scale
if (type_state() == TypeAttributeState::Week)
return 604800000;
// https://html.spec.whatwg.org/multipage/input.html#local-date-and-time-state-(type=datetime-local):concept-input-step-scale
if (type_state() == TypeAttributeState::LocalDateAndTime)
return 1000;
VERIFY_NOT_REACHED();
} }
// https://html.spec.whatwg.org/multipage/input.html#concept-input-step // https://html.spec.whatwg.org/multipage/input.html#concept-input-step

View file

@ -0,0 +1,34 @@
Harness status: OK
Found 28 tests
24 Pass
4 Fail
Pass [INPUT in DATE status] The step attribute is not set
Pass [INPUT in DATE status] The value attibute is empty string
Pass [INPUT in DATE status] The value must match the step
Pass [INPUT in DATE status] The value must mismatch the step
Pass [INPUT in MONTH status] The step attribute is not set
Pass [INPUT in MONTH status] The value attibute is empty string
Pass [INPUT in MONTH status] The value must match the step
Pass [INPUT in MONTH status] The value must mismatch the step
Pass [INPUT in WEEK status] The step attribute is not set
Pass [INPUT in WEEK status] The value attibute is empty string
Fail [INPUT in WEEK status] The value must match the step
Fail [INPUT in WEEK status] The value must mismatch the step
Pass [INPUT in TIME status] The step attribute is not set
Pass [INPUT in TIME status] The value attibute is empty string
Pass [INPUT in TIME status] The value must match the step
Pass [INPUT in TIME status] The value must mismatch the step
Pass [INPUT in DATETIME-LOCAL status] The step attribute is not set
Pass [INPUT in DATETIME-LOCAL status] The value attibute is empty string
Pass [INPUT in DATETIME-LOCAL status] The value must match the step
Pass [INPUT in DATETIME-LOCAL status] The value must mismatch the step
Pass [INPUT in NUMBER status] The step attribute is not set
Pass [INPUT in NUMBER status] The step attribute is not set and the value attribute is a floating number
Pass [INPUT in NUMBER status] The value attribute is empty string
Pass [INPUT in NUMBER status] The value must match the step
Pass [INPUT in NUMBER status] The value must mismatch the step
Fail [INPUT in NUMBER status] No step mismatch when step is a floating number and value is its integral multiple
Fail [INPUT in NUMBER status] No step mismatch when step is a floating number in exponent format and value is its integral multiple
Pass [INPUT in NUMBER status] Step mismatch when step is a very small floating number and value is not its integral multiple

View file

@ -0,0 +1,7 @@
Harness status: OK
Found 2 tests
2 Pass
Pass Calling stepDown() on input - month - where value < min should not modify value.
Pass Calling stepDown() on input - week - where value < min should not modify value.

View file

@ -0,0 +1,10 @@
Harness status: OK
Found 5 tests
5 Pass
Pass Forms
Pass Calling stepDown() on input - number - where value < min should not modify value.
Pass Calling stepDown() on input - date - where value < min should not modify value.
Pass Calling stepDown() on input - datetime-local - where value < min should not modify value.
Pass Calling stepDown() on input - time - where value < min should not modify value.

View file

@ -0,0 +1,7 @@
Harness status: OK
Found 2 tests
2 Pass
Pass Calling stepUp() on input -month- where value > max should not modify value.
Pass Calling stepUp() on input -week- where value > max should not modify value.

View file

@ -0,0 +1,10 @@
Harness status: OK
Found 5 tests
5 Pass
Pass Forms
Pass Calling stepUp() on input -number- where value > max should not modify value.
Pass Calling stepUp() on input -date- where value > max should not modify value.
Pass Calling stepUp() on input -datetime-local- where value > max should not modify value.
Pass Calling stepUp() on input -time- where value > max should not modify value.

View file

@ -0,0 +1,21 @@
Harness status: OK
Found 15 tests
14 Pass
1 Fail
Pass year can be more than four digits
Pass valid value test
Pass year can contain prefixes of zero, as long as there are at least four digits
Pass month type support on input element
Pass User agents must not allow the user to set the value to a non-empty string that is not a valid month string.
Pass Month value can be empty string.
Pass When value attribute has two digits year value, the value,which is invalid, must return empty string.
Pass When value is set with invalid value, the value must return empty string.
Pass When step attribute is given invalid value, it must ignore the invalid value and use defaul value instead.
Pass Month should be <= 13: If the value of the element is not a valid month string, then set it to the empty string instead.
Pass Month should be > 0: If the value of the element is not a valid month string, then set it to the empty string instead.>
Fail Year should be > 0: If the value of the element is not a valid year string, then set it to the empty string instead.>
Pass Month should be two digits: If the value of the element is not a valid month string, then set it to the empty string instead.>
Pass Month should be two digits not characters: If the value of the element is not a valid month string, then set it to the empty string instead.>
Pass Value should be two parts: If the value of the element is not a valid month string, then set it to the empty string instead.>

View file

@ -0,0 +1,81 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>The constraint validation API Test: element.validity.stepMismatch</title>
<link rel="author" title="Intel" href="http://www.intel.com/">
<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-validitystate-stepmismatch">
<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-constraint-validation-api">
<script src="../../../../resources/testharness.js"></script>
<script src="../../../../resources/testharnessreport.js"></script>
<script src="support/validator.js"></script>
<div id="log"></div>
<script>
//set step = 2 * default step * factor
var testElements = [
{
tag: "input",
types: ["date"],
testData: [
{conditions: {step: "", value: "2000-01-01"}, expected: false, name: "[target] The step attribute is not set"},
{conditions: {step: 2, value: ""}, expected: false, name: "[target] The value attibute is empty string"},
{conditions: {step: 2, value: "1970-01-03"}, expected: false, name: "[target] The value must match the step"},
{conditions: {step: 2, value: "1970-01-02"}, expected: true, name: "[target] The value must mismatch the step"}
]
},
{
tag: "input",
types: ["month"],
testData: [
{conditions: {step: "", value: "2000-01"}, expected: false, name: "[target] The step attribute is not set"},
{conditions: {step: 2, value: ""}, expected: false, name: "[target] The value attibute is empty string"},
{conditions: {step: 2, value: "1970-03"}, expected: false, name: "[target] The value must match the step"},
{conditions: {step: 2, value: "1970-04"}, expected: true, name: "[target] The value must mismatch the step"}
]
},
{
tag: "input",
types: ["week"],
testData: [
{conditions: {step: "", value: "1970-W01"}, expected: false, name: "[target] The step attribute is not set"},
{conditions: {step: 2, value: ""}, expected: false, name: "[target] The value attibute is empty string"},
{conditions: {step: 2, value: "1970-W03"}, expected: false, name: "[target] The value must match the step"},
{conditions: {step: 2, value: "1970-W04"}, expected: true, name: "[target] The value must mismatch the step"}
]
},
{
tag: "input",
types: ["time"],
testData: [
{conditions: {step: "", value: "12:00:00"}, expected: false, name: "[target] The step attribute is not set"},
{conditions: {step: 2 * 60, value: ""}, expected: false, name: "[target] The value attibute is empty string"},
{conditions: {step: 2 * 60, value: "12:02:00"}, expected: false, name: "[target] The value must match the step"},
{conditions: {step: 2 * 60, value: "12:03:00"}, expected: true, name: "[target] The value must mismatch the step"}
]
},
{
tag: "input",
types: ["datetime-local"],
testData: [
{conditions: {step: "", value: "2000-01-01T12:00:00"}, expected: false, name: "[target] The step attribute is not set"},
{conditions: {step: 2 * 60, value: ""}, expected: false, name: "[target] The value attibute is empty string"},
{conditions: {step: 2 * 60, value: "1970-01-01T12:02:00"}, expected: false, name: "[target] The value must match the step"},
{conditions: {step: 2 * 60, value: "1970-01-01T12:03:00"}, expected: true, name: "[target] The value must mismatch the step"}
]
},
{
tag: "input",
types: ["number"],
testData: [
{conditions: {step: "", value: "1"}, expected: false, name: "[target] The step attribute is not set"},
{conditions: {step: "", value: "-.8"}, expected: true, name: "[target] The step attribute is not set and the value attribute is a floating number"},
{conditions: {step: 2 * 1 * 1, value: ""}, expected: false, name: "[target] The value attribute is empty string"},
{conditions: {step: 2 * 1 * 1, value: "2"}, expected: false, name: "[target] The value must match the step"},
{conditions: {step: 2 * 1 * 1, value: "3"}, expected: true, name: "[target] The value must mismatch the step"},
{conditions: {step: 0.003, value: "3.6"}, expected: false, name: "[target] No step mismatch when step is a floating number and value is its integral multiple"},
{conditions: {step: 1e-12, value: "-12345678.9"}, expected: false, name: "[target] No step mismatch when step is a floating number in exponent format and value is its integral multiple"},
{conditions: {step: 3e-15, value: "17"}, expected: true, name: "[target] Step mismatch when step is a very small floating number and value is not its integral multiple"},
]
}
];
validator.run_test(testElements, "stepMismatch");
</script>

View file

@ -0,0 +1,22 @@
<!DOCTYPE HTML>
<html>
<title>Forms</title>
<script src="../../../../resources/testharness.js"></script>
<script src="../../../../resources/testharnessreport.js"></script>
<h3>input_stepDown</h3>
<input type="month" id="month_input" min="2011-02" step="1" value="2010-02">
<input type="week" id="week_input" min="2011-W02" step="1" value="2010-W02">
<script>
function testStepDownOverflow(id, value, type) {
test(function() {
var input = document.getElementById(id);
input.stepDown();
assert_equals(input.value, value, "value shouldn't change.");
}, "Calling stepDown() on input - " + type + " - where value < min should not modify value.");
}
testStepDownOverflow("month_input", "2010-02", "month");
testStepDownOverflow("week_input", "2010-W02", "week");
</script>
</html>

View file

@ -0,0 +1,43 @@
<!DOCTYPE HTML>
<html>
<title>Forms</title>
<script src="../../../../resources/testharness.js"></script>
<script src="../../../../resources/testharnessreport.js"></script>
<h3>input_stepDown</h3>
<input type='number' id='input_number'>
<input type="number" id="number_input" min="300" step="1" value="200">
<input type="date" id="date_input" min="2011-02-10" step="1" value="2010-02-10">
<input type="datetime-local" id="dtl_input" min="2011-02-10T20:13" step="1" value="2010-02-10T20:13">
<input type="time" id="time_input" min="21:13" step="60" value="20:13">
<script>
var input_number = document.getElementById("input_number");
input_number.max = "30";
input_number.step = "3";
input_number.value = "30";
input_number.stepDown(5);
if (typeof(input_number.stepDown) == "function") {
test(function() {
assert_equals(input_number.value, "15", "call of stepDown method is failed.");
});
} else {
test(function() {
assert_unreached("stepDown attribute is not exist.");
});
}
function testStepDownOverflow(id, value, type) {
test(function() {
var input = document.getElementById(id);
input.stepDown();
assert_equals(input.value, value, "value shouldn't change.");
}, "Calling stepDown() on input - " + type + " - where value < min should not modify value.");
}
testStepDownOverflow("number_input", "200", "number");
testStepDownOverflow("date_input", "2010-02-10", "date");
testStepDownOverflow("dtl_input", "2010-02-10T20:13", "datetime-local");
testStepDownOverflow("time_input", "20:13", "time");
</script>
</html>

View file

@ -0,0 +1,22 @@
<!DOCTYPE HTML>
<html>
<title>Forms</title>
<script src="../../../../resources/testharness.js"></script>
<script src="../../../../resources/testharnessreport.js"></script>
<h3>input_stepUp</h3>
<input type="month" id="month_input" max="2009-02" step="1" value="2010-02">
<input type="week" id="week_input" max="2009-W02" step="1" value="2010-W02">
<script>
function testStepUpOverflow(id, value, type) {
test(function() {
var input = document.getElementById(id);
input.stepUp();
assert_equals(input.value, value, "value shouldn't change.");
}, "Calling stepUp() on input -" + type + "- where value > max should not modify value.");
}
testStepUpOverflow("month_input", "2010-02", "month");
testStepUpOverflow("week_input", "2010-W02", "week");
</script>
</html>

View file

@ -0,0 +1,44 @@
<!DOCTYPE HTML>
<html>
<title>Forms</title>
<script src="../../../../resources/testharness.js"></script>
<script src="../../../../resources/testharnessreport.js"></script>
<h3>input_stepUp</h3>
<input type='number' id='input_number'> <br/>
<input type="number" id="number_input" max="100" step="1" value="200">
<input type="date" id="date_input" max="2009-02-10" step="1" value="2010-02-10">
<input type="datetime-local" id="dtl_input" max="2009-02-10T20:13" step="1" value="2010-02-10T20:13">
<input type="time" id="time_input" max="19:13" step="60" value="20:13">
<script>
var input_number = document.getElementById("input_number");
input_number.max = "30";
input_number.step = "3";
input_number.value = "0";
input_number.stepUp(5);
if (typeof(input_number.stepUp) == "function") {
test(function() {
assert_equals(input_number.value, "15", "call of stepUp method is failed.");
});
} else {
test(function() {
assert_unreached("stepUp attribute is not exist.");
});
}
function testStepUpOverflow(id, value, type) {
test(function() {
var input = document.getElementById(id);
input.stepUp();
assert_equals(input.value, value, "value shouldn't change.");
}, "Calling stepUp() on input -" + type + "- where value > max should not modify value.");
}
testStepUpOverflow("number_input", "200", "number");
testStepUpOverflow("date_input", "2010-02-10", "date");
testStepUpOverflow("dtl_input", "2010-02-10T20:13", "datetime-local");
testStepUpOverflow("time_input", "20:13", "time");
</script>
</html>

View file

@ -0,0 +1,100 @@
<!DOCTYPE html>
<html>
<head>
<title>Inputs Month</title>
<link rel="author" title="Morishita Hiromitsu" href="mailto:hero@asterisk-works.jp">
<link rel="author" title="kaseijin" href="mailto:pcmkas@gmail.com">
<link rel="author" title="Denis Ah-Kang" href="mailto:denis@w3.org">
<link rel="help" href="https://html.spec.whatwg.org/multipage/#months">
<link rel="help" href="https://html.spec.whatwg.org/multipage/#month-state-(type=month)">
<script src="../../../../resources/testharness.js"></script>
<script src="../../../../resources/testharnessreport.js"></script>
</head>
<body>
<h1>Inputs Month</h1>
<div style="display: none">
<input id="valid_value_1" type="month" value="20133-12" />
<input id="valid_value_2" type="month" value="2013-12" />
<input id="valid_value_3" type="month" value="0003-01" />
<input id="valid" type="month" value="2011-11" min="2011-01" max="2011-12" />
<input id="invalid_value" type="month" value="invalid-month" min="2011-01" max="2011-12"/>
<input id="value_can_be_empty_string" type="month" value="2013-06" />
<input id="invalid_value_with_two_digits_year" type="month" value="13-06" />
<input id="invalid_value_is_set" type="month" />
<input id="step_attribute_is_invalid_value" type="month" value="2013-06" step="invalid_step_value" />
<input id="invalid_month_too_high" type="month" value="2013-13" />
<input id="invalid_month_too_low" type="month" value="2013-00" />
<input id="invalid_year_all_zero" type="month" value="0000-10" />
<input id="invalid_month_with_one_number" type="month" value="2013-1" />
<input id="invalid_month_non_numerical" type="month" value="2013-abc" />
<input id="invalid_date_additional_tuples" type="month" value="2013-11-1-1" />
</div>
<div id="log"></div>
<script>
test(function() {
assert_equals(document.getElementById("valid_value_1").value, "20133-12")
}, "year can be more than four digits");
test(function() {
assert_equals(document.getElementById("valid_value_2").value, "2013-12")
}, "valid value test");
test(function() {
assert_equals(document.getElementById("valid_value_3").value, "0003-01")
}, "year can contain prefixes of zero, as long as there are at least four digits");
test(function() {
assert_equals(document.getElementById("valid").type, "month")
}, "month type support on input element");
test(function() {
assert_equals(document.getElementById("invalid_value").value, "")
}, "User agents must not allow the user to set the value to a non-empty string that is not a valid month string.");
test(function() {
document.getElementById("value_can_be_empty_string").value = "";
assert_equals(document.getElementById("value_can_be_empty_string").value, "")
}, "Month value can be empty string.");
test(function() {
assert_equals(document.getElementById("invalid_value_with_two_digits_year").value, "")
}, "When value attribute has two digits year value, the value,which is invalid, must return empty string.");
test(function() {
document.getElementById("invalid_value_is_set").value = "invalid value";
assert_equals(document.getElementById("invalid_value_is_set").value, "")
}, "When value is set with invalid value, the value must return empty string.");
test(function() {
document.getElementById("step_attribute_is_invalid_value").stepUp();
assert_equals(document.getElementById("step_attribute_is_invalid_value").value, "2013-07")
}, "When step attribute is given invalid value, it must ignore the invalid value and use defaul value instead.");
test(function() {
assert_equals(document.getElementById("invalid_month_too_high").value, "");
}, "Month should be <= 13: If the value of the element is not a valid month string, then set it to the empty string instead.");
test(function() {
assert_equals(document.getElementById("invalid_month_too_low").value, "");
}, "Month should be > 0: If the value of the element is not a valid month string, then set it to the empty string instead.>");
test(function() {
assert_equals(document.getElementById("invalid_year_all_zero").value, "");
}, "Year should be > 0: If the value of the element is not a valid year string, then set it to the empty string instead.>");
test(function() {
assert_equals(document.getElementById("invalid_month_with_one_number").value, "");
}, "Month should be two digits: If the value of the element is not a valid month string, then set it to the empty string instead.>");
test(function() {
assert_equals(document.getElementById("invalid_month_non_numerical").value, "");
}, "Month should be two digits not characters: If the value of the element is not a valid month string, then set it to the empty string instead.>");
test(function() {
assert_equals(document.getElementById("invalid_date_additional_tuples").value, "");
}, "Value should be two parts: If the value of the element is not a valid month string, then set it to the empty string instead.>");
</script>
</body>
</html>