LibWeb: Deduplicate the firing of input events in HTMLInputElement

Input elements without a defined user-interaction behavior need to fire
an input event when the user changes the element's value in some way.
This change moves the code to do this into its own function and adds
some spec text to explain what is being done.
This commit is contained in:
Tim Ledbetter 2024-05-18 05:45:13 +01:00 committed by Andreas Kling
parent bd73dd316d
commit a3d12e569c
Notes: sideshowbarker 2024-07-17 03:05:16 +09:00
2 changed files with 29 additions and 36 deletions

View File

@ -408,22 +408,11 @@ void HTMLInputElement::did_edit_text_node(Badge<Navigable>)
update_placeholder_visibility();
// NOTE: This is a bit ad-hoc, but basically implements part of "4.10.5.5 Common event behaviors"
// https://html.spec.whatwg.org/multipage/input.html#common-input-element-events
queue_an_element_task(HTML::Task::Source::UserInteraction, [this] {
auto input_event = DOM::Event::create(realm(), HTML::EventNames::input);
input_event->set_bubbles(true);
input_event->set_composed(true);
dispatch_event(*input_event);
});
user_interaction_did_change_input_value();
}
void HTMLInputElement::did_pick_color(Optional<Color> picked_color, ColorPickerUpdateState state)
{
// https://html.spec.whatwg.org/multipage/input.html#common-input-element-events
// For input elements without a defined input activation behavior, but to which these events apply
// and for which the user interface involves both interactive manipulation and an explicit commit action
if (type_state() == TypeAttributeState::Color && picked_color.has_value()) {
// then when the user changes the element's value
m_value = value_sanitization_algorithm(picked_color.value().to_string_without_alpha());
@ -432,15 +421,10 @@ void HTMLInputElement::did_pick_color(Optional<Color> picked_color, ColorPickerU
update_color_well_element();
// the user agent must queue an element task on the user interaction task source
queue_an_element_task(HTML::Task::Source::UserInteraction, [this] {
// given the input element to fire an event named input at the input element, with the bubbles and composed attributes initialized to true
auto input_event = DOM::Event::create(realm(), HTML::EventNames::input);
input_event->set_bubbles(true);
input_event->set_composed(true);
dispatch_event(*input_event);
});
user_interaction_did_change_input_value();
// and any time the user commits the change, the user agent must queue an element task on the user interaction task source
// https://html.spec.whatwg.org/multipage/input.html#common-input-element-events
// [...] any time the user commits the change, the user agent must queue an element task on the user interaction task source
if (state == ColorPickerUpdateState::Closed) {
queue_an_element_task(HTML::Task::Source::UserInteraction, [this] {
// given the input element
@ -980,18 +964,8 @@ void HTMLInputElement::create_range_input_shadow_tree()
MUST(slider_runnable_track->append_child(*m_slider_thumb));
update_slider_thumb_element();
//  User event listeners
auto dispatch_input_event = [this]() {
queue_an_element_task(HTML::Task::Source::UserInteraction, [&] {
auto input_event = DOM::Event::create(realm(), HTML::EventNames::input);
input_event->set_bubbles(true);
input_event->set_composed(true);
dispatch_event(*input_event);
});
};
auto keydown_callback_function = JS::NativeFunction::create(
realm(), [this, dispatch_input_event](JS::VM& vm) {
realm(), [this](JS::VM& vm) {
auto key = MUST(vm.argument(0).get(vm, "key")).as_string().utf8_string();
if (key == "ArrowLeft" || key == "ArrowDown")
@ -1004,7 +978,7 @@ void HTMLInputElement::create_range_input_shadow_tree()
if (key == "PageUp")
MUST(step_up(10));
dispatch_input_event();
user_interaction_did_change_input_value();
return JS::js_undefined();
},
0, "", &realm());
@ -1012,28 +986,28 @@ void HTMLInputElement::create_range_input_shadow_tree()
add_event_listener_without_options(UIEvents::EventNames::keydown, DOM::IDLEventListener::create(realm(), keydown_callback));
auto wheel_callback_function = JS::NativeFunction::create(
realm(), [this, dispatch_input_event](JS::VM& vm) {
realm(), [this](JS::VM& vm) {
auto deltaY = MUST(vm.argument(0).get(vm, "deltaY")).as_i32();
if (deltaY > 0) {
MUST(step_down());
} else {
MUST(step_up());
}
dispatch_input_event();
user_interaction_did_change_input_value();
return JS::js_undefined();
},
0, "", &realm());
auto wheel_callback = realm().heap().allocate_without_realm<WebIDL::CallbackType>(*wheel_callback_function, Bindings::host_defined_environment_settings_object(realm()));
add_event_listener_without_options(UIEvents::EventNames::wheel, DOM::IDLEventListener::create(realm(), wheel_callback));
auto update_slider_by_mouse = [this, dispatch_input_event](JS::VM& vm) {
auto update_slider_by_mouse = [this](JS::VM& vm) {
auto client_x = MUST(vm.argument(0).get(vm, "clientX")).as_double();
auto rect = get_bounding_client_rect();
double minimum = *min();
double maximum = *max();
// FIXME: Snap new value to input steps
MUST(set_value_as_number(clamp(round(((client_x - rect->left()) / rect->width()) * (maximum - minimum) + minimum), minimum, maximum)));
dispatch_input_event();
user_interaction_did_change_input_value();
};
auto mousedown_callback_function = JS::NativeFunction::create(
@ -1070,6 +1044,23 @@ void HTMLInputElement::create_range_input_shadow_tree()
add_event_listener_without_options(UIEvents::EventNames::mousedown, DOM::IDLEventListener::create(realm(), mousedown_callback));
}
void HTMLInputElement::user_interaction_did_change_input_value()
{
// https://html.spec.whatwg.org/multipage/input.html#common-input-element-events
// For input elements without a defined input activation behavior, but to which these events apply,
// and for which the user interface involves both interactive manipulation and an explicit commit action,
// then when the user changes the element's value, the user agent must queue an element task on the user interaction task source
// given the input element to fire an event named input at the input element, with the bubbles and composed attributes initialized to true,
// and any time the user commits the change, the user agent must queue an element task on the user interaction task source given the input
// element to set its user validity to true and fire an event named change at the input element, with the bubbles attribute initialized to true.
queue_an_element_task(HTML::Task::Source::UserInteraction, [this] {
auto input_event = DOM::Event::create(realm(), HTML::EventNames::input);
input_event->set_bubbles(true);
input_event->set_composed(true);
dispatch_event(*input_event);
});
}
void HTMLInputElement::update_slider_thumb_element()
{
if (!m_slider_thumb)

View File

@ -255,6 +255,8 @@ private:
void handle_readonly_attribute(Optional<String> const& value);
WebIDL::ExceptionOr<void> handle_src_attribute(String const& value);
void user_interaction_did_change_input_value();
// https://html.spec.whatwg.org/multipage/input.html#value-sanitization-algorithm
String value_sanitization_algorithm(String const&) const;