From c76c44d0fbed564f4f54c83cc8f50b280cc334d9 Mon Sep 17 00:00:00 2001 From: Abdelrahman Hossam <43494928+abdelrahman1234567@users.noreply.github.com> Date: Thu, 17 Jul 2025 13:32:03 +0800 Subject: [PATCH] script: Ensure that keyboard modifiers, screen point, and client point are set in WheelEvents (#37947) - Updating the WheelEvent initialization to correctly handle keyboard modifiers when the wheel event is triggered. The changes ensure that the modifiers (Ctrl, Alt, Shift, Meta) are properly set based on the current state of the keyboard when the wheel event is created. This is particularly important for scenarios where the wheel event is influenced by key presses, such as scrolling with the Ctrl key pressed to zoom in or out. - Updating the `screen_point` and `client_point` as it was always 0,0 before. Now, it shows the correct position of the mouse pointer while triggering the wheel event. Test: Manual Test case and existing WPT tests (classic/perform_actions/wheel.py[test_scroll_with_key_pressed]) Fixes: #37827 Signed-off-by: abdelrahman1234567 --- components/script/dom/document.rs | 14 +- components/script/dom/wheelevent.rs | 124 ++++++++++++++++-- components/script/script_thread.rs | 2 +- .../Event-subclasses-constructors.html.ini | 3 - .../classic/perform_actions/wheel.py.ini | 3 - 5 files changed, 129 insertions(+), 17 deletions(-) diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs index 30a67590507..9eef30aa0a4 100644 --- a/components/script/dom/document.rs +++ b/components/script/dom/document.rs @@ -2186,11 +2186,11 @@ impl Document { pub(crate) fn handle_wheel_event( &self, event: WheelEvent, - hit_test_result: Option, + input_event: &ConstellationInputEvent, can_gc: CanGc, ) { // Ignore all incoming events without a hit test. - let Some(hit_test_result) = hit_test_result else { + let Some(hit_test_result) = &input_event.hit_test_result else { return; }; @@ -2220,6 +2220,16 @@ impl Document { EventCancelable::Cancelable, Some(&self.window), 0i32, + hit_test_result.point_in_viewport.to_i32(), + hit_test_result.point_in_viewport.to_i32(), + hit_test_result + .point_relative_to_initial_containing_block + .to_i32(), + input_event.active_keyboard_modifiers, + 0i16, + input_event.pressed_mouse_buttons, + None, + None, // winit defines positive wheel delta values as revealing more content left/up. // https://docs.rs/winit-gtk/latest/winit/event/enum.MouseScrollDelta.html // This is the opposite of wheel delta in uievents diff --git a/components/script/dom/wheelevent.rs b/components/script/dom/wheelevent.rs index 2923e49d5a2..05d8cd2c03a 100644 --- a/components/script/dom/wheelevent.rs +++ b/components/script/dom/wheelevent.rs @@ -5,7 +5,10 @@ use std::cell::Cell; use dom_struct::dom_struct; +use euclid::Point2D; use js::rust::HandleObject; +use keyboard_types::Modifiers; +use style_traits::CSSPixel; use crate::dom::bindings::codegen::Bindings::MouseEventBinding::MouseEventMethods; use crate::dom::bindings::codegen::Bindings::WheelEventBinding; @@ -17,6 +20,7 @@ use crate::dom::bindings::reflector::reflect_dom_object_with_proto; use crate::dom::bindings::root::DomRoot; use crate::dom::bindings::str::DOMString; use crate::dom::event::{Event, EventBubbles, EventCancelable}; +use crate::dom::eventtarget::EventTarget; use crate::dom::mouseevent::MouseEvent; use crate::dom::window::Window; use crate::script_runtime::CanGc; @@ -57,6 +61,14 @@ impl WheelEvent { cancelable: EventCancelable, view: Option<&Window>, detail: i32, + screen_point: Point2D, + client_point: Point2D, + page_point: Point2D, + modifiers: Modifiers, + button: i16, + buttons: u16, + related_target: Option<&EventTarget>, + point_in_target: Option>, delta_x: Finite, delta_y: Finite, delta_z: Finite, @@ -64,8 +76,26 @@ impl WheelEvent { can_gc: CanGc, ) -> DomRoot { Self::new_with_proto( - window, None, type_, can_bubble, cancelable, view, detail, delta_x, delta_y, delta_z, - delta_mode, can_gc, + window, + None, + type_, + can_bubble, + cancelable, + view, + detail, + screen_point, + client_point, + page_point, + modifiers, + button, + buttons, + related_target, + point_in_target, + delta_x, + delta_y, + delta_z, + delta_mode, + can_gc, ) } @@ -78,6 +108,14 @@ impl WheelEvent { cancelable: EventCancelable, view: Option<&Window>, detail: i32, + screen_point: Point2D, + client_point: Point2D, + page_point: Point2D, + modifiers: Modifiers, + button: i16, + buttons: u16, + related_target: Option<&EventTarget>, + point_in_target: Option>, delta_x: Finite, delta_y: Finite, delta_z: Finite, @@ -85,21 +123,74 @@ impl WheelEvent { can_gc: CanGc, ) -> DomRoot { let ev = WheelEvent::new_unintialized(window, proto, can_gc); - ev.InitWheelEvent( + ev.intitialize_wheel_event( type_, - bool::from(can_bubble), - bool::from(cancelable), + can_bubble, + cancelable, view, detail, + screen_point, + client_point, + page_point, + modifiers, + button, + buttons, + related_target, + point_in_target, delta_x, delta_y, delta_z, delta_mode, - can_gc, ); ev } + + #[allow(clippy::too_many_arguments)] + pub(crate) fn intitialize_wheel_event( + &self, + type_: DOMString, + can_bubble: EventBubbles, + cancelable: EventCancelable, + view: Option<&Window>, + detail: i32, + screen_point: Point2D, + client_point: Point2D, + page_point: Point2D, + modifiers: Modifiers, + button: i16, + buttons: u16, + related_target: Option<&EventTarget>, + point_in_target: Option>, + delta_x: Finite, + delta_y: Finite, + delta_z: Finite, + delta_mode: u32, + ) { + if self.upcast::().dispatching() { + return; + } + + self.upcast::().initialize_mouse_event( + type_, + can_bubble, + cancelable, + view, + detail, + screen_point, + client_point, + page_point, + modifiers, + button, + buttons, + related_target, + point_in_target, + ); + self.delta_x.set(delta_x); + self.delta_y.set(delta_y); + self.delta_z.set(delta_z); + self.delta_mode.set(delta_mode); + } } impl WheelEventMethods for WheelEvent { @@ -111,14 +202,31 @@ impl WheelEventMethods for WheelEvent { type_: DOMString, init: &WheelEventBinding::WheelEventInit, ) -> Fallible> { + let bubbles = EventBubbles::from(init.parent.parent.parent.parent.bubbles); + let cancelable = EventCancelable::from(init.parent.parent.parent.parent.cancelable); + let scroll_offset = window.scroll_offset(can_gc); + + let page_point = Point2D::::new( + scroll_offset.x as i32 + init.parent.clientX, + scroll_offset.y as i32 + init.parent.clientY, + ); + let event = WheelEvent::new_with_proto( window, proto, type_, - EventBubbles::from(init.parent.parent.parent.parent.bubbles), - EventCancelable::from(init.parent.parent.parent.parent.cancelable), + bubbles, + cancelable, init.parent.parent.parent.view.as_deref(), init.parent.parent.parent.detail, + Point2D::new(init.parent.screenX, init.parent.screenY), + Point2D::new(init.parent.clientX, init.parent.clientY), + Point2D::new(page_point.x, page_point.y), + init.parent.parent.modifiers(), + init.parent.button, + init.parent.buttons, + init.parent.relatedTarget.as_deref(), + None, init.deltaX, init.deltaY, init.deltaZ, diff --git a/components/script/script_thread.rs b/components/script/script_thread.rs index 18bf170f1f3..f25966a215f 100644 --- a/components/script/script_thread.rs +++ b/components/script/script_thread.rs @@ -1133,7 +1133,7 @@ impl ScriptThread { } }, InputEvent::Wheel(wheel_event) => { - document.handle_wheel_event(wheel_event, event.hit_test_result, can_gc); + document.handle_wheel_event(wheel_event, &event, can_gc); }, InputEvent::Keyboard(keyboard_event) => { document.dispatch_key_event(keyboard_event, can_gc); diff --git a/tests/wpt/meta/dom/events/Event-subclasses-constructors.html.ini b/tests/wpt/meta/dom/events/Event-subclasses-constructors.html.ini index 266d22518a2..42a81c01e0c 100644 --- a/tests/wpt/meta/dom/events/Event-subclasses-constructors.html.ini +++ b/tests/wpt/meta/dom/events/Event-subclasses-constructors.html.ini @@ -1,7 +1,4 @@ [Event-subclasses-constructors.html] - [WheelEvent constructor (argument with non-default values)] - expected: FAIL - [KeyboardEvent constructor (argument with non-default values)] expected: FAIL diff --git a/tests/wpt/meta/webdriver/tests/classic/perform_actions/wheel.py.ini b/tests/wpt/meta/webdriver/tests/classic/perform_actions/wheel.py.ini index a170a1026cd..d886b85b230 100644 --- a/tests/wpt/meta/webdriver/tests/classic/perform_actions/wheel.py.ini +++ b/tests/wpt/meta/webdriver/tests/classic/perform_actions/wheel.py.ini @@ -5,9 +5,6 @@ [test_scroll_shadow_tree[inner-closed\]] expected: FAIL - [test_scroll_with_key_pressed] - expected: FAIL - [test_scroll_shadow_tree[outer-open\]] expected: FAIL