mirror of
https://github.com/LadybirdBrowser/ladybird
synced 2026-04-26 09:45:06 +02:00
LibWeb: Harden UA event handlers on range and number inputs
These handlers crashed on several kinds of JS-dispatched input: zero-width range (divide by zero in the slider mouse handler), step="any" (MUST(step_up) throws InvalidStateError), plain Event without clientX/deltaY/key (unchecked as_foo() asserts on undefined), min > max (trips clamp()'s VERIFY), and input.type changes leaving the range listeners attached to dereference empty Optionals from the range-only min()/max() accessors. Gate each handler on its expected type_state() and on allowed_value_step() having a value, validate event property types before converting, and bail out on zero-width rects or min > max. Six crash tests cover the new paths. Hit on a Cloudflare challenge page.
This commit is contained in:
committed by
Andreas Kling
parent
6222f2f59d
commit
5da72570b8
Notes:
github-actions[bot]
2026-04-24 05:59:31 +00:00
Author: https://github.com/awesomekling Commit: https://github.com/LadybirdBrowser/ladybird/commit/5da72570b81 Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/9063
@@ -1209,7 +1209,7 @@ void HTMLInputElement::create_text_input_shadow_tree()
|
||||
|
||||
auto up_callback_function = JS::NativeFunction::create(
|
||||
realm(), [this](JS::VM&) {
|
||||
if (is_mutable()) {
|
||||
if (type_state() == TypeAttributeState::Number && is_mutable() && allowed_value_step().has_value()) {
|
||||
MUST(step_up());
|
||||
user_interaction_did_change_input_value();
|
||||
}
|
||||
@@ -1238,7 +1238,7 @@ void HTMLInputElement::create_text_input_shadow_tree()
|
||||
|
||||
auto down_callback_function = JS::NativeFunction::create(
|
||||
realm(), [this](JS::VM&) {
|
||||
if (is_mutable()) {
|
||||
if (type_state() == TypeAttributeState::Number && is_mutable() && allowed_value_step().has_value()) {
|
||||
MUST(step_down());
|
||||
user_interaction_did_change_input_value();
|
||||
}
|
||||
@@ -1361,7 +1361,14 @@ void HTMLInputElement::create_range_input_shadow_tree()
|
||||
|
||||
auto keydown_callback_function = JS::NativeFunction::create(
|
||||
realm(), [this](JS::VM& vm) {
|
||||
auto key = MUST(vm.argument(0).get(vm, "key"_utf16_fly_string)).as_string().utf8_string();
|
||||
if (type_state() != TypeAttributeState::Range)
|
||||
return JS::js_undefined();
|
||||
if (!allowed_value_step().has_value())
|
||||
return JS::js_undefined();
|
||||
auto key_value = MUST(vm.argument(0).get(vm, "key"_utf16_fly_string));
|
||||
if (!key_value.is_string())
|
||||
return JS::js_undefined();
|
||||
auto key = key_value.as_string().utf8_string();
|
||||
|
||||
if (key == "ArrowLeft" || key == "ArrowDown")
|
||||
MUST(step_down());
|
||||
@@ -1382,8 +1389,14 @@ void HTMLInputElement::create_range_input_shadow_tree()
|
||||
|
||||
auto wheel_callback_function = JS::NativeFunction::create(
|
||||
realm(), [this](JS::VM& vm) {
|
||||
auto delta_y = MUST(vm.argument(0).get(vm, "deltaY"_utf16_fly_string)).as_i32();
|
||||
if (delta_y > 0) {
|
||||
if (type_state() != TypeAttributeState::Range)
|
||||
return JS::js_undefined();
|
||||
if (!allowed_value_step().has_value())
|
||||
return JS::js_undefined();
|
||||
auto delta_y_value = MUST(vm.argument(0).get(vm, "deltaY"_utf16_fly_string));
|
||||
if (!delta_y_value.is_finite_number())
|
||||
return JS::js_undefined();
|
||||
if (delta_y_value.as_double() > 0) {
|
||||
MUST(step_down());
|
||||
} else {
|
||||
MUST(step_up());
|
||||
@@ -1396,10 +1409,19 @@ void HTMLInputElement::create_range_input_shadow_tree()
|
||||
add_event_listener_without_options(UIEvents::EventNames::wheel, DOM::IDLEventListener::create(realm(), wheel_callback));
|
||||
|
||||
auto update_slider_by_mouse = [this](JS::VM& vm) {
|
||||
auto client_x = MUST(vm.argument(0).get(vm, "clientX"_utf16_fly_string)).as_double();
|
||||
if (type_state() != TypeAttributeState::Range)
|
||||
return;
|
||||
auto client_x_value = MUST(vm.argument(0).get(vm, "clientX"_utf16_fly_string));
|
||||
if (!client_x_value.is_finite_number())
|
||||
return;
|
||||
auto client_x = client_x_value.as_double();
|
||||
auto rect = get_bounding_client_rect();
|
||||
if (rect.width().to_double() <= 0)
|
||||
return;
|
||||
double minimum = *min();
|
||||
double maximum = *max();
|
||||
if (minimum > maximum)
|
||||
return;
|
||||
// FIXME: Snap new value to input steps
|
||||
MUST(set_value_as_number(clamp(round(((client_x - rect.left().to_double()) / rect.width().to_double()) * (maximum - minimum) + minimum), minimum, maximum)));
|
||||
user_interaction_did_change_input_value();
|
||||
|
||||
Reference in New Issue
Block a user