mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-10-18 14:09:42 +00:00
LibWeb/HTML: Enforce constraints on range inputs
This extends the sanitization algorithm for range inputs in order to prevent overflow, underflow and step mismatch.
This commit is contained in:
parent
57376f8701
commit
aea8650d8b
Notes:
github-actions[bot]
2025-08-14 15:07:02 +00:00
Author: https://github.com/skyz1
Commit: aea8650d8b
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/5812
Reviewed-by: https://github.com/gmta
Reviewed-by: https://github.com/trflynn89
4 changed files with 99 additions and 34 deletions
|
@ -1418,6 +1418,11 @@ void HTMLInputElement::form_associated_element_attribute_changed(FlyString const
|
|||
m_value = value_sanitization_algorithm(m_value);
|
||||
}
|
||||
update_shadow_tree();
|
||||
} else if (name == HTML::AttributeNames::min || name == HTML::AttributeNames::max || name == HTML::AttributeNames::step) {
|
||||
if (type_state() == TypeAttributeState::Range) {
|
||||
m_value = value_sanitization_algorithm(m_value);
|
||||
update_shadow_tree();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1716,19 +1721,59 @@ Utf16String HTMLInputElement::value_sanitization_algorithm(Utf16String const& va
|
|||
}
|
||||
// https://html.spec.whatwg.org/multipage/input.html#range-state-(type=range):value-sanitization-algorithm
|
||||
else if (type_state() == HTMLInputElement::TypeAttributeState::Range) {
|
||||
// If the value of the element is not a valid floating-point number, then set it to the best representation, as
|
||||
// a floating-point number, of the default value.
|
||||
if (auto maybe_value = parse_floating_point_number(value); !is_valid_floating_point_number(value) ||
|
||||
// AD-HOC: The spec doesn't require these checks - but other engines do them.
|
||||
!maybe_value.has_value() || !isfinite(maybe_value.value())) {
|
||||
// The default value is the minimum plus half the difference between the minimum and the maximum, unless the
|
||||
// maximum is less than the minimum, in which case the default value is the minimum.
|
||||
auto minimum = *min();
|
||||
auto maximum = *max();
|
||||
if (maximum < minimum)
|
||||
return JS::number_to_utf16_string(minimum);
|
||||
return JS::number_to_utf16_string(minimum + ((maximum - minimum) / 2.0));
|
||||
auto minimum = *min();
|
||||
auto maximum = *max();
|
||||
auto number_value = [&value, &minimum, &maximum, this] {
|
||||
// If the value of the element is not a valid floating-point number, then set it to the best representation,
|
||||
// as a floating-point number, of the default value.
|
||||
auto maybe_value = parse_floating_point_number(value);
|
||||
if (!is_valid_floating_point_number(value) ||
|
||||
// AD-HOC: The spec doesn't require these checks - but other engines do them.
|
||||
!maybe_value.has_value() || !isfinite(maybe_value.value())) {
|
||||
// The default value is the minimum plus half the difference between the minimum and the maximum, unless
|
||||
// the maximum is less than the minimum, in which case the default value is the minimum.
|
||||
if (maximum < minimum)
|
||||
return minimum;
|
||||
return minimum + ((maximum - minimum) / 2.0);
|
||||
}
|
||||
return maybe_value.value();
|
||||
}();
|
||||
|
||||
// When the element is suffering from an overflow, if the maximum is not less than the minimum, the user agent
|
||||
// must set the element's value to a valid floating-point number that represents the maximum.
|
||||
if (is_number_overflowing(number_value)) {
|
||||
number_value = max().value();
|
||||
}
|
||||
|
||||
// When the element is suffering from an underflow, the user agent must set the element's value to the best
|
||||
// representation, as a floating-point number, of the minimum.
|
||||
if (is_number_underflowing(number_value)) {
|
||||
number_value = min().value();
|
||||
}
|
||||
|
||||
// When the element is suffering from a step mismatch, the user agent must round the element's value to the
|
||||
// nearest number for which the element would not suffer from a step mismatch, and which is greater than or
|
||||
// equal to the minimum, and, if the maximum is not less than the minimum, which is less than or equal to the
|
||||
// maximum, if there is a number that matches these constraints. If two numbers match these constraints, then
|
||||
// user agents must use the one nearest to positive infinity.
|
||||
if (is_number_mismatching_step(number_value)) {
|
||||
auto allowed_step = allowed_value_step().value();
|
||||
auto minimum = min().value();
|
||||
auto maximum = max().value();
|
||||
auto step_mismatch = ((number_value - step_base()) / allowed_step) - floor((number_value - step_base()) / allowed_step);
|
||||
if (step_mismatch >= 0.5) {
|
||||
auto nearest_number = step_base() + (allowed_step * ceil((number_value - step_base()) / allowed_step));
|
||||
if (nearest_number >= minimum && (maximum <= minimum || nearest_number <= maximum))
|
||||
return JS::number_to_utf16_string(nearest_number);
|
||||
}
|
||||
if (step_mismatch <= 0.5) {
|
||||
auto nearest_number = step_base() + (allowed_step * floor((number_value - step_base()) / allowed_step));
|
||||
if (nearest_number >= minimum && (maximum <= minimum || nearest_number <= maximum))
|
||||
return JS::number_to_utf16_string(nearest_number);
|
||||
}
|
||||
}
|
||||
|
||||
return JS::number_to_utf16_string(number_value);
|
||||
}
|
||||
// https://html.spec.whatwg.org/multipage/input.html#color-state-(type=color):value-sanitization-algorithm
|
||||
else if (type_state() == HTMLInputElement::TypeAttributeState::Color) {
|
||||
|
@ -2760,8 +2805,10 @@ WebIDL::ExceptionOr<void> HTMLInputElement::step_up_or_down(bool is_down, WebIDL
|
|||
if (maybe_minimum.has_value() && maybe_maximum.has_value() && *maybe_minimum > *maybe_maximum)
|
||||
return {};
|
||||
|
||||
// FIXME: 4. If the element has a minimum and a maximum and there is no value greater than or equal to the element's minimum and less than
|
||||
// 4. If the element has a minimum and a maximum and there is no value greater than or equal to the element's minimum and less than
|
||||
// or equal to the element's maximum that, when subtracted from the step base, is an integral multiple of the allowed value step, then return.
|
||||
if (maybe_minimum.has_value() && maybe_maximum.has_value() && ceil((*maybe_minimum - step_base()) / allowed_value_step) > floor((*maybe_maximum - step_base()) / allowed_value_step))
|
||||
return {};
|
||||
|
||||
// 5. If applying the algorithm to convert a string to a number to the string given by the element's value does not result in an error,
|
||||
// then let value be the result of that algorithm. Otherwise, let value be zero.
|
||||
|
@ -2796,14 +2843,14 @@ WebIDL::ExceptionOr<void> HTMLInputElement::step_up_or_down(bool is_down, WebIDL
|
|||
// when subtracted from the step base, is an integral multiple of the allowed value step, and that is more than
|
||||
// or equal to that minimum.
|
||||
if (maybe_minimum.has_value() && value < *maybe_minimum) {
|
||||
value = AK::max(value, *maybe_minimum);
|
||||
value = step_base() + ceil((*maybe_minimum - step_base()) / allowed_value_step) * allowed_value_step;
|
||||
}
|
||||
|
||||
// 9. If the element has a maximum, and value is greater than that maximum, then set value to the largest value that,
|
||||
// when subtracted from the step base, is an integral multiple of the allowed value step, and that is less than
|
||||
// or equal to that maximum.
|
||||
if (maybe_maximum.has_value() && value > *maybe_maximum) {
|
||||
value = AK::min(value, *maybe_maximum);
|
||||
value = step_base() + floor((*maybe_maximum - step_base()) / allowed_value_step) * allowed_value_step;
|
||||
}
|
||||
|
||||
// 10. If either the method invoked was the stepDown() method and value is greater than valueBeforeStepping,
|
||||
|
@ -3408,6 +3455,11 @@ bool HTMLInputElement::suffering_from_an_underflow() const
|
|||
if (!result.has_value())
|
||||
return false;
|
||||
auto number = result.value();
|
||||
return is_number_underflowing(number);
|
||||
}
|
||||
|
||||
bool HTMLInputElement::is_number_underflowing(double number) const
|
||||
{
|
||||
// https://html.spec.whatwg.org/multipage/input.html#the-min-and-max-attributes%3Asuffering-from-an-underflow-2
|
||||
// When the element has a minimum and does not have a reversed range,
|
||||
auto minimum = min();
|
||||
|
@ -3436,6 +3488,11 @@ bool HTMLInputElement::suffering_from_an_overflow() const
|
|||
if (!result.has_value())
|
||||
return false;
|
||||
auto number = result.value();
|
||||
return is_number_overflowing(number);
|
||||
}
|
||||
|
||||
bool HTMLInputElement::is_number_overflowing(double number) const
|
||||
{
|
||||
auto maximum = max();
|
||||
// https://html.spec.whatwg.org/multipage/input.html#the-min-and-max-attributes%3Asuffering-from-an-overflow-2
|
||||
// When the element has a maximum and does not have a reversed range,
|
||||
|
@ -3458,17 +3515,22 @@ bool HTMLInputElement::suffering_from_an_overflow() const
|
|||
// https://html.spec.whatwg.org/multipage/input.html#the-step-attribute%3Asuffering-from-a-step-mismatch
|
||||
bool HTMLInputElement::suffering_from_a_step_mismatch() const
|
||||
{
|
||||
// When the element has an allowed value step,
|
||||
auto maybe_allowed_value_step = allowed_value_step();
|
||||
if (!maybe_allowed_value_step.has_value())
|
||||
return false;
|
||||
double allowed_value_step = *maybe_allowed_value_step;
|
||||
// and the result of applying the algorithm to convert a string to a number to the string given by the element's
|
||||
// value is a number,
|
||||
auto maybe_number = convert_string_to_number(value());
|
||||
if (!maybe_number.has_value())
|
||||
return false;
|
||||
double number = maybe_number.value();
|
||||
return is_number_mismatching_step(number);
|
||||
}
|
||||
|
||||
bool HTMLInputElement::is_number_mismatching_step(double number) const
|
||||
{
|
||||
// When the element has an allowed value step,
|
||||
auto maybe_allowed_value_step = allowed_value_step();
|
||||
if (!maybe_allowed_value_step.has_value())
|
||||
return false;
|
||||
double allowed_value_step = *maybe_allowed_value_step;
|
||||
// and that number subtracted from the step base is not an integral multiple of the allowed value step, the element
|
||||
// is suffering from a step mismatch.
|
||||
return fmod(step_base() - number, allowed_value_step) != 0;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue