script: Only trigger one default event handler for form controls (#43247)

There is a `handle_event` virtual method on `Node` that triggers the
default event handler. Typically the "superclass" virtual method is
called and then the specific type runs its default event handler. This
isn't good for form controls, because typically the event handler should
prevent further event handlers from running. This badness can be
observed by pressing down in a text input, which will both move the
cursor as well as scroll the page. 🙃

This change makes it so that the default form control event handler
consistently calls the `Event::mark_as_handled` method and triggers the
"superclass" event handler *after* running its own. In addition, the
scrolling default event handler on `Node` now doesn't run if the `Event`
was already handled.

Finally, a little bit of dead event handling code is removed.

Testing: This change adds a Servo-specific test for this behavior.
Fixes: #40785

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
This commit is contained in:
Martin Robinson
2026-03-13 17:27:22 +01:00
committed by GitHub
parent 9186a7efab
commit 4c6dbd59cb
5 changed files with 81 additions and 22 deletions

View File

@@ -755,23 +755,16 @@ impl VirtualMethods for HTMLTextAreaElement {
// copied and modified from htmlinputelement.rs
fn handle_event(&self, event: &Event, can_gc: CanGc) {
if let Some(s) = self.super_type() {
s.handle_event(event, can_gc);
}
if let Some(mouse_event) = event.downcast::<MouseEvent>() {
self.handle_mouse_event(mouse_event);
event.mark_as_handled();
} else if event.type_() == atom!("keydown") && !event.DefaultPrevented() {
if let Some(kevent) = event.downcast::<KeyboardEvent>() {
if let Some(keyboard_event) = event.downcast::<KeyboardEvent>() {
// This can't be inlined, as holding on to textinput.borrow_mut()
// during self.implicit_submission will cause a panic.
let action = self.textinput.borrow_mut().handle_keydown(kevent);
let action = self.textinput.borrow_mut().handle_keydown(keyboard_event);
self.handle_key_reaction(action, event, can_gc);
}
} else if event.type_() == atom!("keypress") && !event.DefaultPrevented() {
// keypress should be deprecated and replaced by beforeinput.
// keypress was supposed to fire "blur" and "focus" events
// but already done in `document.rs`
} else if event.type_() == atom!("compositionstart") ||
event.type_() == atom!("compositionupdate") ||
event.type_() == atom!("compositionend")
@@ -818,6 +811,7 @@ impl VirtualMethods for HTMLTextAreaElement {
);
}
if !flags.is_empty() {
event.mark_as_handled();
self.handle_text_content_changed(can_gc);
}
} else if let Some(event) = event.downcast::<FocusEvent>() {
@@ -826,6 +820,10 @@ impl VirtualMethods for HTMLTextAreaElement {
self.validity_state(can_gc)
.perform_validation_and_update(ValidationFlags::all(), can_gc);
if let Some(super_type) = self.super_type() {
super_type.handle_event(event, can_gc);
}
}
fn pop(&self) {