script: Pass more information to the MouseEvent constructor (#37672)

- Instead of eagerly computing `pageX` and `pageY`, collect the offset
  from the content's initial containing block in the compositor and pass
  that information through to `MouseEvent`. This prevents a layout flush
  that was happening when eagerly trying to fetch `Document` scroll
  offsets.
- Pass keyboard modifiers properly to `MouseEvent`.
- Now all this information is stored and passed as `Point2D` (typed) and
  `Modifiers` which greatly reduces the amount of arguments that need to
  be passed around.

Testing: It is difficult to test input events as they require WebDriver
which
isn't completely working yet. I have manually run Speedometer 2.1 and I
have
verified that this fixes the regression from #37601.
Fixes: #37601.

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
This commit is contained in:
Martin Robinson 2025-06-25 14:29:27 +02:00 committed by GitHub
parent 50cf01cf3d
commit 0346a62214
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 374 additions and 369 deletions

1
Cargo.lock generated
View file

@ -7139,6 +7139,7 @@ dependencies = [
"html5ever", "html5ever",
"indexmap", "indexmap",
"jstraceable_derive", "jstraceable_derive",
"keyboard-types",
"libc", "libc",
"log", "log",
"malloc_size_of_derive", "malloc_size_of_derive",

View file

@ -336,11 +336,23 @@ impl ServoRenderer {
_ => return None, _ => return None,
} }
let offset = details
.scroll_tree
.scroll_offset(pipeline_id.root_scroll_id())
.unwrap_or_default();
let point_in_initial_containing_block =
(item.point_in_viewport + offset).to_untyped();
let info = &details.hit_test_items[item.tag.0 as usize]; let info = &details.hit_test_items[item.tag.0 as usize];
Some(CompositorHitTestResult { Some(CompositorHitTestResult {
pipeline_id, pipeline_id,
point_in_viewport: item.point_in_viewport.to_untyped(), point_in_viewport: Point2D::from_untyped(item.point_in_viewport.to_untyped()),
point_relative_to_item: item.point_relative_to_item.to_untyped(), point_relative_to_initial_containing_block: Point2D::from_untyped(
point_in_initial_containing_block,
),
point_relative_to_item: Point2D::from_untyped(
item.point_relative_to_item.to_untyped(),
),
node: UntrustedNodeAddress(info.node as *const c_void), node: UntrustedNodeAddress(info.node as *const c_void),
cursor: info.cursor, cursor: info.cursor,
scroll_tree_node: info.scroll_tree_node, scroll_tree_node: info.scroll_tree_node,

View file

@ -34,7 +34,8 @@ use embedder_traits::{
UntrustedNodeAddress, WheelEvent, UntrustedNodeAddress, WheelEvent,
}; };
use encoding_rs::{Encoding, UTF_8}; use encoding_rs::{Encoding, UTF_8};
use euclid::default::{Point2D, Rect, Size2D}; use euclid::Point2D;
use euclid::default::{Rect, Size2D};
use fnv::FnvHashMap; use fnv::FnvHashMap;
use html5ever::{LocalName, Namespace, QualName, local_name, ns}; use html5ever::{LocalName, Namespace, QualName, local_name, ns};
use hyper_serde::Serde; use hyper_serde::Serde;
@ -50,7 +51,6 @@ use net_traits::pub_domains::is_pub_domain;
use net_traits::request::{InsecureRequestsPolicy, RequestBuilder}; use net_traits::request::{InsecureRequestsPolicy, RequestBuilder};
use net_traits::response::HttpsState; use net_traits::response::HttpsState;
use net_traits::{FetchResponseListener, IpcSend, ReferrerPolicy}; use net_traits::{FetchResponseListener, IpcSend, ReferrerPolicy};
use num_traits::ToPrimitive;
use percent_encoding::percent_decode; use percent_encoding::percent_decode;
use profile_traits::ipc as profile_ipc; use profile_traits::ipc as profile_ipc;
use profile_traits::time::TimerMetadataFrameType; use profile_traits::time::TimerMetadataFrameType;
@ -69,6 +69,7 @@ use style::shared_lock::SharedRwLock as StyleSharedRwLock;
use style::str::{split_html_space_chars, str_join}; use style::str::{split_html_space_chars, str_join};
use style::stylesheet_set::DocumentStylesheetSet; use style::stylesheet_set::DocumentStylesheetSet;
use style::stylesheets::{Origin, OriginSet, Stylesheet}; use style::stylesheets::{Origin, OriginSet, Stylesheet};
use style_traits::CSSPixel;
use stylo_atoms::Atom; use stylo_atoms::Atom;
use url::Host; use url::Host;
use uuid::Uuid; use uuid::Uuid;
@ -425,7 +426,7 @@ pub(crate) struct Document {
/// <https://w3c.github.io/uievents/#event-type-dblclick> /// <https://w3c.github.io/uievents/#event-type-dblclick>
#[ignore_malloc_size_of = "Defined in std"] #[ignore_malloc_size_of = "Defined in std"]
#[no_trace] #[no_trace]
last_click_info: DomRefCell<Option<(Instant, Point2D<f32>)>>, last_click_info: DomRefCell<Option<(Instant, Point2D<f32, CSSPixel>)>>,
/// <https://html.spec.whatwg.org/multipage/#ignore-destructive-writes-counter> /// <https://html.spec.whatwg.org/multipage/#ignore-destructive-writes-counter>
ignore_destructive_writes_counter: Cell<u32>, ignore_destructive_writes_counter: Cell<u32>,
/// <https://html.spec.whatwg.org/multipage/#ignore-opens-during-unload-counter> /// <https://html.spec.whatwg.org/multipage/#ignore-opens-during-unload-counter>
@ -1515,12 +1516,11 @@ impl Document {
pub(crate) fn handle_mouse_button_event( pub(crate) fn handle_mouse_button_event(
&self, &self,
event: MouseButtonEvent, event: MouseButtonEvent,
hit_test_result: Option<CompositorHitTestResult>, input_event: &ConstellationInputEvent,
pressed_mouse_buttons: u16,
can_gc: CanGc, can_gc: CanGc,
) { ) {
// Ignore all incoming events without a hit test. // 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; return;
}; };
@ -1556,9 +1556,10 @@ impl Document {
let dom_event = DomRoot::upcast::<Event>(MouseEvent::for_platform_mouse_event( let dom_event = DomRoot::upcast::<Event>(MouseEvent::for_platform_mouse_event(
event, event,
pressed_mouse_buttons, input_event.pressed_mouse_buttons,
&self.window, &self.window,
&hit_test_result, hit_test_result,
input_event.active_keyboard_modifiers,
can_gc, can_gc,
)); ));
@ -1592,23 +1593,13 @@ impl Document {
if self.focus_transaction.borrow().is_some() { if self.focus_transaction.borrow().is_some() {
self.commit_focus_transaction(FocusInitiator::Local, can_gc); self.commit_focus_transaction(FocusInitiator::Local, can_gc);
} }
self.maybe_fire_dblclick( self.maybe_fire_dblclick(node, hit_test_result, input_event, can_gc);
hit_test_result.point_in_viewport,
node,
pressed_mouse_buttons,
can_gc,
);
} }
// When the contextmenu event is triggered by right mouse button // When the contextmenu event is triggered by right mouse button
// the contextmenu event MUST be dispatched after the mousedown event. // the contextmenu event MUST be dispatched after the mousedown event.
if let (MouseButtonAction::Down, MouseButton::Right) = (event.action, event.button) { if let (MouseButtonAction::Down, MouseButton::Right) = (event.action, event.button) {
self.maybe_show_context_menu( self.maybe_show_context_menu(node.upcast(), hit_test_result, input_event, can_gc);
node.upcast(),
pressed_mouse_buttons,
hit_test_result.point_in_viewport,
can_gc,
);
} }
} }
@ -1616,13 +1607,10 @@ impl Document {
fn maybe_show_context_menu( fn maybe_show_context_menu(
&self, &self,
target: &EventTarget, target: &EventTarget,
pressed_mouse_buttons: u16, hit_test_result: &CompositorHitTestResult,
client_point: Point2D<f32>, input_event: &ConstellationInputEvent,
can_gc: CanGc, can_gc: CanGc,
) { ) {
let client_x = client_point.x.to_i32().unwrap_or(0);
let client_y = client_point.y.to_i32().unwrap_or(0);
// <https://w3c.github.io/uievents/#contextmenu> // <https://w3c.github.io/uievents/#contextmenu>
let menu_event = PointerEvent::new( let menu_event = PointerEvent::new(
&self.window, // window &self.window, // window
@ -1631,32 +1619,30 @@ impl Document {
EventCancelable::Cancelable, // cancelable EventCancelable::Cancelable, // cancelable
Some(&self.window), // view Some(&self.window), // view
0, // detail 0, // detail
client_x, // screen_x hit_test_result.point_in_viewport.to_i32(),
client_y, // screen_y hit_test_result.point_in_viewport.to_i32(),
client_x, // client_x hit_test_result
client_y, // client_y .point_relative_to_initial_containing_block
false, // ctrl_key .to_i32(),
false, // alt_key input_event.active_keyboard_modifiers,
false, // shift_key 2i16, // button, right mouse button
false, // meta_key input_event.pressed_mouse_buttons,
2i16, // button, right mouse button None, // related_target
pressed_mouse_buttons, // buttons None, // point_in_target
None, // related_target PointerId::Mouse as i32, // pointer_id
None, // point_in_target 1, // width
PointerId::Mouse as i32, // pointer_id 1, // height
1, // width 0.5, // pressure
1, // height 0.0, // tangential_pressure
0.5, // pressure 0, // tilt_x
0.0, // tangential_pressure 0, // tilt_y
0, // tilt_x 0, // twist
0, // tilt_y PI / 2.0, // altitude_angle
0, // twist 0.0, // azimuth_angle
PI / 2.0, // altitude_angle DOMString::from("mouse"), // pointer_type
0.0, // azimuth_angle true, // is_primary
DOMString::from("mouse"), // pointer_type vec![], // coalesced_events
true, // is_primary vec![], // predicted_events
vec![], // coalesced_events
vec![], // predicted_events
can_gc, can_gc,
); );
let event = menu_event.upcast::<Event>(); let event = menu_event.upcast::<Event>();
@ -1678,14 +1664,14 @@ impl Document {
fn maybe_fire_dblclick( fn maybe_fire_dblclick(
&self, &self,
click_pos: Point2D<f32>,
target: &Node, target: &Node,
pressed_mouse_buttons: u16, hit_test_result: &CompositorHitTestResult,
input_event: &ConstellationInputEvent,
can_gc: CanGc, can_gc: CanGc,
) { ) {
// https://w3c.github.io/uievents/#event-type-dblclick // https://w3c.github.io/uievents/#event-type-dblclick
let now = Instant::now(); let now = Instant::now();
let point_in_viewport = hit_test_result.point_in_viewport;
let opt = self.last_click_info.borrow_mut().take(); let opt = self.last_click_info.borrow_mut().take();
if let Some((last_time, last_pos)) = opt { if let Some((last_time, last_pos)) = opt {
@ -1694,7 +1680,7 @@ impl Document {
let DBL_CLICK_DIST_THRESHOLD = pref!(dom_document_dblclick_dist) as u64; let DBL_CLICK_DIST_THRESHOLD = pref!(dom_document_dblclick_dist) as u64;
// Calculate distance between this click and the previous click. // Calculate distance between this click and the previous click.
let line = click_pos - last_pos; let line = point_in_viewport - last_pos;
let dist = (line.dot(line) as f64).sqrt(); let dist = (line.dot(line) as f64).sqrt();
if now.duration_since(last_time) < DBL_CLICK_TIMEOUT && if now.duration_since(last_time) < DBL_CLICK_TIMEOUT &&
@ -1702,8 +1688,6 @@ impl Document {
{ {
// A double click has occurred if this click is within a certain time and dist. of previous click. // A double click has occurred if this click is within a certain time and dist. of previous click.
let click_count = 2; let click_count = 2;
let client_x = click_pos.x as i32;
let client_y = click_pos.y as i32;
let event = MouseEvent::new( let event = MouseEvent::new(
&self.window, &self.window,
@ -1712,16 +1696,14 @@ impl Document {
EventCancelable::Cancelable, EventCancelable::Cancelable,
Some(&self.window), Some(&self.window),
click_count, click_count,
client_x, point_in_viewport.to_i32(),
client_y, point_in_viewport.to_i32(),
client_x, hit_test_result
client_y, .point_relative_to_initial_containing_block
false, .to_i32(),
false, input_event.active_keyboard_modifiers,
false,
false,
0i16, 0i16,
pressed_mouse_buttons, input_event.pressed_mouse_buttons,
None, None,
None, None,
can_gc, can_gc,
@ -1735,23 +1717,20 @@ impl Document {
} }
// Update last_click_info with the time and position of the click. // Update last_click_info with the time and position of the click.
*self.last_click_info.borrow_mut() = Some((now, click_pos)); *self.last_click_info.borrow_mut() = Some((now, point_in_viewport));
} }
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
pub(crate) fn fire_mouse_event( pub(crate) fn fire_mouse_event(
&self, &self,
client_point: Point2D<f32>,
target: &EventTarget, target: &EventTarget,
event_name: FireMouseEventType, event_name: FireMouseEventType,
can_bubble: EventBubbles, can_bubble: EventBubbles,
cancelable: EventCancelable, cancelable: EventCancelable,
pressed_mouse_buttons: u16, hit_test_result: &CompositorHitTestResult,
input_event: &ConstellationInputEvent,
can_gc: CanGc, can_gc: CanGc,
) { ) {
let client_x = client_point.x.to_i32().unwrap_or(0);
let client_y = client_point.y.to_i32().unwrap_or(0);
MouseEvent::new( MouseEvent::new(
&self.window, &self.window,
DOMString::from(event_name.as_str()), DOMString::from(event_name.as_str()),
@ -1759,16 +1738,14 @@ impl Document {
cancelable, cancelable,
Some(&self.window), Some(&self.window),
0i32, 0i32,
client_x, hit_test_result.point_in_viewport.to_i32(),
client_y, hit_test_result.point_in_viewport.to_i32(),
client_x, hit_test_result
client_y, .point_relative_to_initial_containing_block
false, .to_i32(),
false, input_event.active_keyboard_modifiers,
false,
false,
0i16, 0i16,
pressed_mouse_buttons, input_event.pressed_mouse_buttons,
None, None,
None, None,
can_gc, can_gc,
@ -1989,13 +1966,12 @@ impl Document {
#[allow(unsafe_code)] #[allow(unsafe_code)]
pub(crate) unsafe fn handle_mouse_move_event( pub(crate) unsafe fn handle_mouse_move_event(
&self, &self,
hit_test_result: Option<CompositorHitTestResult>, input_event: &ConstellationInputEvent,
pressed_mouse_buttons: u16,
prev_mouse_over_target: &MutNullableDom<Element>, prev_mouse_over_target: &MutNullableDom<Element>,
can_gc: CanGc, can_gc: CanGc,
) { ) {
// Ignore all incoming events without a hit test. // 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; return;
}; };
@ -2036,12 +2012,12 @@ impl Document {
} }
self.fire_mouse_event( self.fire_mouse_event(
hit_test_result.point_in_viewport,
old_target.upcast(), old_target.upcast(),
FireMouseEventType::Out, FireMouseEventType::Out,
EventBubbles::Bubbles, EventBubbles::Bubbles,
EventCancelable::Cancelable, EventCancelable::Cancelable,
pressed_mouse_buttons, hit_test_result,
input_event,
can_gc, can_gc,
); );
@ -2049,11 +2025,11 @@ impl Document {
let event_target = DomRoot::from_ref(old_target.upcast::<Node>()); let event_target = DomRoot::from_ref(old_target.upcast::<Node>());
let moving_into = Some(DomRoot::from_ref(new_target.upcast::<Node>())); let moving_into = Some(DomRoot::from_ref(new_target.upcast::<Node>()));
self.handle_mouse_enter_leave_event( self.handle_mouse_enter_leave_event(
hit_test_result.point_in_viewport,
FireMouseEventType::Leave,
moving_into,
event_target, event_target,
pressed_mouse_buttons, moving_into,
FireMouseEventType::Leave,
hit_test_result,
input_event,
can_gc, can_gc,
); );
} }
@ -2072,12 +2048,12 @@ impl Document {
} }
self.fire_mouse_event( self.fire_mouse_event(
hit_test_result.point_in_viewport,
new_target.upcast(), new_target.upcast(),
FireMouseEventType::Over, FireMouseEventType::Over,
EventBubbles::Bubbles, EventBubbles::Bubbles,
EventCancelable::Cancelable, EventCancelable::Cancelable,
pressed_mouse_buttons, hit_test_result,
input_event,
can_gc, can_gc,
); );
@ -2086,11 +2062,11 @@ impl Document {
.map(|old_target| DomRoot::from_ref(old_target.upcast::<Node>())); .map(|old_target| DomRoot::from_ref(old_target.upcast::<Node>()));
let event_target = DomRoot::from_ref(new_target.upcast::<Node>()); let event_target = DomRoot::from_ref(new_target.upcast::<Node>());
self.handle_mouse_enter_leave_event( self.handle_mouse_enter_leave_event(
hit_test_result.point_in_viewport,
FireMouseEventType::Enter,
moving_from,
event_target, event_target,
pressed_mouse_buttons, moving_from,
FireMouseEventType::Enter,
hit_test_result,
input_event,
can_gc, can_gc,
); );
} }
@ -2098,12 +2074,12 @@ impl Document {
// Send mousemove event to topmost target, unless it's an iframe, in which case the // Send mousemove event to topmost target, unless it's an iframe, in which case the
// compositor should have also sent an event to the inner document. // compositor should have also sent an event to the inner document.
self.fire_mouse_event( self.fire_mouse_event(
hit_test_result.point_in_viewport,
new_target.upcast(), new_target.upcast(),
FireMouseEventType::Move, FireMouseEventType::Move,
EventBubbles::Bubbles, EventBubbles::Bubbles,
EventCancelable::Cancelable, EventCancelable::Cancelable,
pressed_mouse_buttons, hit_test_result,
input_event,
can_gc, can_gc,
); );
@ -2116,12 +2092,11 @@ impl Document {
#[allow(unsafe_code)] #[allow(unsafe_code)]
pub(crate) fn handle_mouse_leave_event( pub(crate) fn handle_mouse_leave_event(
&self, &self,
hit_test_result: Option<CompositorHitTestResult>, input_event: &ConstellationInputEvent,
pressed_mouse_buttons: u16,
can_gc: CanGc, can_gc: CanGc,
) { ) {
// Ignore all incoming events without a hit test. // 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; return;
}; };
@ -2138,31 +2113,31 @@ impl Document {
} }
self.fire_mouse_event( self.fire_mouse_event(
hit_test_result.point_in_viewport,
node.upcast(), node.upcast(),
FireMouseEventType::Out, FireMouseEventType::Out,
EventBubbles::Bubbles, EventBubbles::Bubbles,
EventCancelable::Cancelable, EventCancelable::Cancelable,
pressed_mouse_buttons, hit_test_result,
input_event,
can_gc, can_gc,
); );
self.handle_mouse_enter_leave_event( self.handle_mouse_enter_leave_event(
hit_test_result.point_in_viewport,
FireMouseEventType::Leave,
None,
node, node,
pressed_mouse_buttons, None,
FireMouseEventType::Leave,
hit_test_result,
input_event,
can_gc, can_gc,
); );
} }
fn handle_mouse_enter_leave_event( fn handle_mouse_enter_leave_event(
&self, &self,
client_point: Point2D<f32>,
event_type: FireMouseEventType,
related_target: Option<DomRoot<Node>>,
event_target: DomRoot<Node>, event_target: DomRoot<Node>,
pressed_mouse_buttons: u16, related_target: Option<DomRoot<Node>>,
event_type: FireMouseEventType,
hit_test_result: &CompositorHitTestResult,
input_event: &ConstellationInputEvent,
can_gc: CanGc, can_gc: CanGc,
) { ) {
assert!(matches!( assert!(matches!(
@ -2197,12 +2172,12 @@ impl Document {
for target in targets { for target in targets {
self.fire_mouse_event( self.fire_mouse_event(
client_point,
target.upcast(), target.upcast(),
event_type, event_type,
EventBubbles::DoesNotBubble, EventBubbles::DoesNotBubble,
EventCancelable::NotCancelable, EventCancelable::NotCancelable,
pressed_mouse_buttons, hit_test_result,
input_event,
can_gc, can_gc,
); );
} }

View file

@ -2853,8 +2853,11 @@ impl VirtualMethods for HTMLInputElement {
// now. // now.
if let Some(point_in_target) = mouse_event.point_in_target() { if let Some(point_in_target) = mouse_event.point_in_target() {
let window = self.owner_window(); let window = self.owner_window();
let index = let index = window.text_index_query(
window.text_index_query(self.upcast::<Node>(), point_in_target, can_gc); self.upcast::<Node>(),
point_in_target.to_untyped(),
can_gc,
);
// Position the caret at the click position or at the end of the current // Position the caret at the click position or at the end of the current
// value. // value.
let edit_point_index = match index { let edit_point_index = match index {

View file

@ -7,10 +7,12 @@ use std::default::Default;
use dom_struct::dom_struct; use dom_struct::dom_struct;
use embedder_traits::CompositorHitTestResult; use embedder_traits::CompositorHitTestResult;
use euclid::default::Point2D; use euclid::Point2D;
use js::rust::HandleObject; use js::rust::HandleObject;
use keyboard_types::Modifiers;
use script_bindings::codegen::GenericBindings::WindowBinding::WindowMethods; use script_bindings::codegen::GenericBindings::WindowBinding::WindowMethods;
use servo_config::pref; use servo_config::pref;
use style_traits::CSSPixel;
use crate::dom::bindings::codegen::Bindings::EventBinding::Event_Binding::EventMethods; use crate::dom::bindings::codegen::Bindings::EventBinding::Event_Binding::EventMethods;
use crate::dom::bindings::codegen::Bindings::MouseEventBinding; use crate::dom::bindings::codegen::Bindings::MouseEventBinding;
@ -33,36 +35,36 @@ use crate::script_runtime::CanGc;
pub(crate) struct MouseEvent { pub(crate) struct MouseEvent {
uievent: UIEvent, uievent: UIEvent,
/// The point on the screen of where this [`MouseEvent`] was originally triggered,
/// to use during the dispatch phase.
///
/// See:
/// <https://w3c.github.io/uievents/#dom-mouseevent-screenx> /// <https://w3c.github.io/uievents/#dom-mouseevent-screenx>
screen_x: Cell<i32>,
/// <https://w3c.github.io/uievents/#dom-mouseevent-screeny> /// <https://w3c.github.io/uievents/#dom-mouseevent-screeny>
screen_y: Cell<i32>, #[no_trace]
screen_point: Cell<Point2D<i32, CSSPixel>>,
/// The point in the viewport of where this [`MouseEvent`] was originally triggered,
/// to use during the dispatch phase.
///
/// See:
/// <https://w3c.github.io/uievents/#dom-mouseevent-clientx> /// <https://w3c.github.io/uievents/#dom-mouseevent-clientx>
client_x: Cell<i32>,
/// <https://w3c.github.io/uievents/#dom-mouseevent-clienty> /// <https://w3c.github.io/uievents/#dom-mouseevent-clienty>
client_y: Cell<i32>, #[no_trace]
client_point: Cell<Point2D<i32, CSSPixel>>,
page_x: Cell<i32>, /// The point in the initial containing block of where this [`MouseEvent`] was
page_y: Cell<i32>, /// originally triggered to use during the dispatch phase.
x: Cell<i32>, ///
y: Cell<i32>, /// See:
offset_x: Cell<i32>, /// <https://w3c.github.io/uievents/#dom-mouseevent-pagex>
offset_y: Cell<i32>, /// <https://w3c.github.io/uievents/#dom-mouseevent-pagey>
#[no_trace]
page_point: Cell<Point2D<i32, CSSPixel>>,
/// <https://w3c.github.io/uievents/#dom-mouseevent-ctrlkey> /// The keyboard modifiers that were active when this mouse event was triggered.
ctrl_key: Cell<bool>, #[no_trace]
modifiers: Cell<Modifiers>,
/// <https://w3c.github.io/uievents/#dom-mouseevent-shiftkey>
shift_key: Cell<bool>,
/// <https://w3c.github.io/uievents/#dom-mouseevent-altkey>
alt_key: Cell<bool>,
/// <https://w3c.github.io/uievents/#dom-mouseevent-metakey>
meta_key: Cell<bool>,
/// <https://w3c.github.io/uievents/#dom-mouseevent-button> /// <https://w3c.github.io/uievents/#dom-mouseevent-button>
button: Cell<i16>, button: Cell<i16>,
@ -73,27 +75,17 @@ pub(crate) struct MouseEvent {
/// <https://w3c.github.io/uievents/#dom-mouseevent-relatedtarget> /// <https://w3c.github.io/uievents/#dom-mouseevent-relatedtarget>
related_target: MutNullableDom<EventTarget>, related_target: MutNullableDom<EventTarget>,
#[no_trace] #[no_trace]
point_in_target: Cell<Option<Point2D<f32>>>, point_in_target: Cell<Option<Point2D<f32, CSSPixel>>>,
} }
impl MouseEvent { impl MouseEvent {
pub(crate) fn new_inherited() -> MouseEvent { pub(crate) fn new_inherited() -> MouseEvent {
MouseEvent { MouseEvent {
uievent: UIEvent::new_inherited(), uievent: UIEvent::new_inherited(),
screen_x: Cell::new(0), screen_point: Cell::new(Default::default()),
screen_y: Cell::new(0), client_point: Cell::new(Default::default()),
client_x: Cell::new(0), page_point: Cell::new(Default::default()),
client_y: Cell::new(0), modifiers: Cell::new(Modifiers::empty()),
page_x: Cell::new(0),
page_y: Cell::new(0),
x: Cell::new(0),
y: Cell::new(0),
offset_x: Cell::new(0),
offset_y: Cell::new(0),
ctrl_key: Cell::new(false),
shift_key: Cell::new(false),
alt_key: Cell::new(false),
meta_key: Cell::new(false),
button: Cell::new(0), button: Cell::new(0),
buttons: Cell::new(0), buttons: Cell::new(0),
related_target: Default::default(), related_target: Default::default(),
@ -121,18 +113,14 @@ impl MouseEvent {
cancelable: EventCancelable, cancelable: EventCancelable,
view: Option<&Window>, view: Option<&Window>,
detail: i32, detail: i32,
screen_x: i32, screen_point: Point2D<i32, CSSPixel>,
screen_y: i32, client_point: Point2D<i32, CSSPixel>,
client_x: i32, page_point: Point2D<i32, CSSPixel>,
client_y: i32, modifiers: Modifiers,
ctrl_key: bool,
alt_key: bool,
shift_key: bool,
meta_key: bool,
button: i16, button: i16,
buttons: u16, buttons: u16,
related_target: Option<&EventTarget>, related_target: Option<&EventTarget>,
point_in_target: Option<Point2D<f32>>, point_in_target: Option<Point2D<f32, CSSPixel>>,
can_gc: CanGc, can_gc: CanGc,
) -> DomRoot<MouseEvent> { ) -> DomRoot<MouseEvent> {
Self::new_with_proto( Self::new_with_proto(
@ -143,14 +131,10 @@ impl MouseEvent {
cancelable, cancelable,
view, view,
detail, detail,
screen_x, screen_point,
screen_y, client_point,
client_x, page_point,
client_y, modifiers,
ctrl_key,
alt_key,
shift_key,
meta_key,
button, button,
buttons, buttons,
related_target, related_target,
@ -168,18 +152,14 @@ impl MouseEvent {
cancelable: EventCancelable, cancelable: EventCancelable,
view: Option<&Window>, view: Option<&Window>,
detail: i32, detail: i32,
screen_x: i32, screen_point: Point2D<i32, CSSPixel>,
screen_y: i32, client_point: Point2D<i32, CSSPixel>,
client_x: i32, page_point: Point2D<i32, CSSPixel>,
client_y: i32, modifiers: Modifiers,
ctrl_key: bool,
alt_key: bool,
shift_key: bool,
meta_key: bool,
button: i16, button: i16,
buttons: u16, buttons: u16,
related_target: Option<&EventTarget>, related_target: Option<&EventTarget>,
point_in_target: Option<Point2D<f32>>, point_in_target: Option<Point2D<f32, CSSPixel>>,
can_gc: CanGc, can_gc: CanGc,
) -> DomRoot<MouseEvent> { ) -> DomRoot<MouseEvent> {
let ev = MouseEvent::new_uninitialized_with_proto(window, proto, can_gc); let ev = MouseEvent::new_uninitialized_with_proto(window, proto, can_gc);
@ -189,14 +169,10 @@ impl MouseEvent {
cancelable, cancelable,
view, view,
detail, detail,
screen_x, screen_point,
screen_y, client_point,
client_x, page_point,
client_y, modifiers,
ctrl_key,
alt_key,
shift_key,
meta_key,
button, button,
buttons, buttons,
related_target, related_target,
@ -214,18 +190,14 @@ impl MouseEvent {
cancelable: EventCancelable, cancelable: EventCancelable,
view: Option<&Window>, view: Option<&Window>,
detail: i32, detail: i32,
screen_x: i32, screen_point: Point2D<i32, CSSPixel>,
screen_y: i32, client_point: Point2D<i32, CSSPixel>,
client_x: i32, page_point: Point2D<i32, CSSPixel>,
client_y: i32, modifiers: Modifiers,
ctrl_key: bool,
alt_key: bool,
shift_key: bool,
meta_key: bool,
button: i16, button: i16,
buttons: u16, buttons: u16,
related_target: Option<&EventTarget>, related_target: Option<&EventTarget>,
point_in_target: Option<Point2D<f32>>, point_in_target: Option<Point2D<f32, CSSPixel>>,
) { ) {
self.uievent.initialize_ui_event( self.uievent.initialize_ui_event(
type_, type_,
@ -235,32 +207,17 @@ impl MouseEvent {
); );
self.uievent.set_detail(detail); self.uievent.set_detail(detail);
self.screen_point.set(screen_point);
self.screen_x.set(screen_x); self.client_point.set(client_point);
self.screen_y.set(screen_y); self.page_point.set(page_point);
self.client_x.set(client_x); self.modifiers.set(modifiers);
self.client_y.set(client_y);
self.page_x.set(self.PageX());
self.page_y.set(self.PageY());
// skip setting flags as they are absent
self.shift_key.set(shift_key);
self.ctrl_key.set(ctrl_key);
self.alt_key.set(alt_key);
self.meta_key.set(meta_key);
self.button.set(button); self.button.set(button);
self.buttons.set(buttons); self.buttons.set(buttons);
// skip step 3: Initialize PointerLock attributes for MouseEvent with event,
// as movementX, movementY is absent
self.related_target.set(related_target); self.related_target.set(related_target);
// below is not in the spec
self.point_in_target.set(point_in_target); self.point_in_target.set(point_in_target);
} }
pub(crate) fn point_in_target(&self) -> Option<Point2D<f32>> { pub(crate) fn point_in_target(&self) -> Option<Point2D<f32, CSSPixel>> {
self.point_in_target.get() self.point_in_target.get()
} }
@ -270,6 +227,7 @@ impl MouseEvent {
pressed_mouse_buttons: u16, pressed_mouse_buttons: u16,
window: &Window, window: &Window,
hit_test_result: &CompositorHitTestResult, hit_test_result: &CompositorHitTestResult,
modifiers: Modifiers,
can_gc: CanGc, can_gc: CanGc,
) -> DomRoot<Self> { ) -> DomRoot<Self> {
let mouse_event_type_string = match event.action { let mouse_event_type_string = match event.action {
@ -278,8 +236,11 @@ impl MouseEvent {
embedder_traits::MouseButtonAction::Down => "mousedown", embedder_traits::MouseButtonAction::Down => "mousedown",
}; };
let client_x = hit_test_result.point_in_viewport.x as i32; let client_point = hit_test_result.point_in_viewport.to_i32();
let client_y = hit_test_result.point_in_viewport.y as i32; let page_point = hit_test_result
.point_relative_to_initial_containing_block
.to_i32();
let click_count = 1; let click_count = 1;
let mouse_event = MouseEvent::new( let mouse_event = MouseEvent::new(
window, window,
@ -288,14 +249,10 @@ impl MouseEvent {
EventCancelable::Cancelable, EventCancelable::Cancelable,
Some(window), Some(window),
click_count, click_count,
client_x, client_point, // TODO: Get real screen coordinates?
client_y, client_point,
client_x, page_point,
client_y, // TODO: Get real screen coordinates? modifiers,
false,
false,
false,
false,
event.button.into(), event.button.into(),
pressed_mouse_buttons, pressed_mouse_buttons,
None, None,
@ -321,6 +278,10 @@ impl MouseEventMethods<crate::DomTypeHolder> for MouseEvent {
) -> Fallible<DomRoot<MouseEvent>> { ) -> Fallible<DomRoot<MouseEvent>> {
let bubbles = EventBubbles::from(init.parent.parent.parent.bubbles); let bubbles = EventBubbles::from(init.parent.parent.parent.bubbles);
let cancelable = EventCancelable::from(init.parent.parent.parent.cancelable); let cancelable = EventCancelable::from(init.parent.parent.parent.cancelable);
let page_point = Point2D::new(
window.ScrollX() + init.clientX,
window.ScrollY() + init.clientY,
);
let event = MouseEvent::new_with_proto( let event = MouseEvent::new_with_proto(
window, window,
proto, proto,
@ -329,14 +290,10 @@ impl MouseEventMethods<crate::DomTypeHolder> for MouseEvent {
cancelable, cancelable,
init.parent.parent.view.as_deref(), init.parent.parent.view.as_deref(),
init.parent.parent.detail, init.parent.parent.detail,
init.screenX, Point2D::new(init.screenX, init.screenY),
init.screenY, Point2D::new(init.clientX, init.clientY),
init.clientX, page_point,
init.clientY, init.parent.modifiers(),
init.parent.ctrlKey,
init.parent.altKey,
init.parent.shiftKey,
init.parent.metaKey,
init.button, init.button,
init.buttons, init.buttons,
init.relatedTarget.as_deref(), init.relatedTarget.as_deref(),
@ -351,110 +308,128 @@ impl MouseEventMethods<crate::DomTypeHolder> for MouseEvent {
/// <https://w3c.github.io/uievents/#widl-MouseEvent-screenX> /// <https://w3c.github.io/uievents/#widl-MouseEvent-screenX>
fn ScreenX(&self) -> i32 { fn ScreenX(&self) -> i32 {
self.screen_x.get() self.screen_point.get().x
} }
/// <https://w3c.github.io/uievents/#widl-MouseEvent-screenY> /// <https://w3c.github.io/uievents/#widl-MouseEvent-screenY>
fn ScreenY(&self) -> i32 { fn ScreenY(&self) -> i32 {
self.screen_y.get() self.screen_point.get().y
} }
/// <https://w3c.github.io/uievents/#widl-MouseEvent-clientX> /// <https://w3c.github.io/uievents/#widl-MouseEvent-clientX>
fn ClientX(&self) -> i32 { fn ClientX(&self) -> i32 {
self.client_x.get() self.client_point.get().x
} }
/// <https://w3c.github.io/uievents/#widl-MouseEvent-clientY> /// <https://w3c.github.io/uievents/#widl-MouseEvent-clientY>
fn ClientY(&self) -> i32 { fn ClientY(&self) -> i32 {
self.client_y.get() self.client_point.get().y
} }
/// <https://drafts.csswg.org/cssom-view/#dom-mouseevent-pagex> /// <https://drafts.csswg.org/cssom-view/#dom-mouseevent-pagex>
fn PageX(&self) -> i32 { fn PageX(&self) -> i32 {
// The pageX attribute must follow these steps:
// > 1. If the events dispatch flag is set, return the horizontal coordinate of the
// > position where the event occurred relative to the origin of the initial containing
// > block and terminate these steps.
if self.upcast::<Event>().dispatching() { if self.upcast::<Event>().dispatching() {
self.page_x.get() return self.page_point.get().x;
} else {
self.global().as_window().ScrollX() + self.client_x.get()
} }
// > 2. Let offset be the value of the scrollX attribute of the events associated
// > Window object, if there is one, or zero otherwise.
// > 3. Return the sum of offset and the value of the events clientX attribute.
self.global().as_window().ScrollX() + self.ClientX()
} }
/// <https://drafts.csswg.org/cssom-view/#dom-mouseevent-pagey> /// <https://drafts.csswg.org/cssom-view/#dom-mouseevent-pagey>
fn PageY(&self) -> i32 { fn PageY(&self) -> i32 {
// The pageY attribute must follow these steps:
// > 1. If the events dispatch flag is set, return the vertical coordinate of the
// > position where the event occurred relative to the origin of the initial
// > containing block and terminate these steps.
if self.upcast::<Event>().dispatching() { if self.upcast::<Event>().dispatching() {
self.page_y.get() return self.page_point.get().y;
} else {
self.global().as_window().ScrollY() + self.client_y.get()
} }
// > 2. Let offset be the value of the scrollY attribute of the events associated
// > Window object, if there is one, or zero otherwise.
// > 3. Return the sum of offset and the value of the events clientY attribute.
self.global().as_window().ScrollY() + self.ClientY()
} }
/// <https://drafts.csswg.org/cssom-view/#dom-mouseevent-x> /// <https://drafts.csswg.org/cssom-view/#dom-mouseevent-x>
fn X(&self) -> i32 { fn X(&self) -> i32 {
self.client_x.get() self.ClientX()
} }
/// <https://drafts.csswg.org/cssom-view/#dom-mouseevent-y> /// <https://drafts.csswg.org/cssom-view/#dom-mouseevent-y>
fn Y(&self) -> i32 { fn Y(&self) -> i32 {
self.client_y.get() self.ClientY()
} }
/// <https://drafts.csswg.org/cssom-view/#dom-mouseevent-offsetx> /// <https://drafts.csswg.org/cssom-view/#dom-mouseevent-offsetx>
fn OffsetX(&self, can_gc: CanGc) -> i32 { fn OffsetX(&self, can_gc: CanGc) -> i32 {
// > The offsetX attribute must follow these steps:
// > 1. If the events dispatch flag is set, return the x-coordinate of the position
// > where the event occurred relative to the origin of the padding edge of the
// > target node, ignoring the transforms that apply to the element and its
// > ancestors, and terminate these steps.
let event = self.upcast::<Event>(); let event = self.upcast::<Event>();
if event.dispatching() { if event.dispatching() {
match event.GetTarget() { let Some(target) = event.GetTarget() else {
Some(target) => { return 0;
if let Some(node) = target.downcast::<Node>() { };
let rect = node.client_rect(can_gc); let Some(node) = target.downcast::<Node>() else {
self.client_x.get() - rect.origin.x return 0;
} else { };
self.offset_x.get() return self.ClientX() - node.client_rect(can_gc).origin.x;
}
},
None => self.offset_x.get(),
}
} else {
self.PageX()
} }
// > 2. Return the value of the events pageX attribute.
self.PageX()
} }
/// <https://drafts.csswg.org/cssom-view/#dom-mouseevent-offsety> /// <https://drafts.csswg.org/cssom-view/#dom-mouseevent-offsety>
fn OffsetY(&self, can_gc: CanGc) -> i32 { fn OffsetY(&self, can_gc: CanGc) -> i32 {
// > The offsetY attribute must follow these steps:
// > 1. If the events dispatch flag is set, return the y-coordinate of the
// > position where the event occurred relative to the origin of the padding edge of
// > the target node, ignoring the transforms that apply to the element and its
// > ancestors, and terminate these steps.
let event = self.upcast::<Event>(); let event = self.upcast::<Event>();
if event.dispatching() { if event.dispatching() {
match event.GetTarget() { let Some(target) = event.GetTarget() else {
Some(target) => { return 0;
if let Some(node) = target.downcast::<Node>() { };
let rect = node.client_rect(can_gc); let Some(node) = target.downcast::<Node>() else {
self.client_y.get() - rect.origin.y return 0;
} else { };
self.offset_y.get() return self.ClientY() - node.client_rect(can_gc).origin.y;
}
},
None => self.offset_y.get(),
}
} else {
self.PageY()
} }
// 2. Return the value of the events pageY attribute.
self.PageY()
} }
/// <https://w3c.github.io/uievents/#dom-mouseevent-ctrlkey> /// <https://w3c.github.io/uievents/#dom-mouseevent-ctrlkey>
fn CtrlKey(&self) -> bool { fn CtrlKey(&self) -> bool {
self.ctrl_key.get() self.modifiers.get().contains(Modifiers::CONTROL)
} }
/// <https://w3c.github.io/uievents/#dom-mouseevent-shiftkey> /// <https://w3c.github.io/uievents/#dom-mouseevent-shiftkey>
fn ShiftKey(&self) -> bool { fn ShiftKey(&self) -> bool {
self.shift_key.get() self.modifiers.get().contains(Modifiers::SHIFT)
} }
/// <https://w3c.github.io/uievents/#dom-mouseevent-altkey> /// <https://w3c.github.io/uievents/#dom-mouseevent-altkey>
fn AltKey(&self) -> bool { fn AltKey(&self) -> bool {
self.alt_key.get() self.modifiers.get().contains(Modifiers::ALT)
} }
/// <https://w3c.github.io/uievents/#dom-mouseevent-metakey> /// <https://w3c.github.io/uievents/#dom-mouseevent-metakey>
fn MetaKey(&self) -> bool { fn MetaKey(&self) -> bool {
self.meta_key.get() self.modifiers.get().contains(Modifiers::META)
} }
/// <https://w3c.github.io/uievents/#dom-mouseevent-button> /// <https://w3c.github.io/uievents/#dom-mouseevent-button>
@ -515,14 +490,32 @@ impl MouseEventMethods<crate::DomTypeHolder> for MouseEvent {
view_arg, view_arg,
detail_arg, detail_arg,
); );
self.screen_x.set(screen_x_arg); self.screen_point
self.screen_y.set(screen_y_arg); .set(Point2D::new(screen_x_arg, screen_y_arg));
self.client_x.set(client_x_arg); self.client_point
self.client_y.set(client_y_arg); .set(Point2D::new(client_x_arg, client_y_arg));
self.ctrl_key.set(ctrl_key_arg);
self.alt_key.set(alt_key_arg); let global = self.global();
self.shift_key.set(shift_key_arg); self.page_point.set(Point2D::new(
self.meta_key.set(meta_key_arg); global.as_window().ScrollX() + client_x_arg,
global.as_window().ScrollY() + client_y_arg,
));
let mut modifiers = Modifiers::empty();
if ctrl_key_arg {
modifiers.insert(Modifiers::CONTROL);
}
if alt_key_arg {
modifiers.insert(Modifiers::ALT);
}
if shift_key_arg {
modifiers.insert(Modifiers::SHIFT);
}
if meta_key_arg {
modifiers.insert(Modifiers::META);
}
self.modifiers.set(modifiers);
self.button.set(button_arg); self.button.set(button_arg);
self.related_target.set(related_target_arg); self.related_target.set(related_target_arg);
} }

View file

@ -18,11 +18,13 @@ use bitflags::bitflags;
use devtools_traits::NodeInfo; use devtools_traits::NodeInfo;
use dom_struct::dom_struct; use dom_struct::dom_struct;
use embedder_traits::UntrustedNodeAddress; use embedder_traits::UntrustedNodeAddress;
use euclid::Point2D;
use euclid::default::{Rect, Size2D}; use euclid::default::{Rect, Size2D};
use html5ever::serialize::HtmlSerializer; use html5ever::serialize::HtmlSerializer;
use html5ever::{Namespace, Prefix, QualName, ns, serialize as html_serialize}; use html5ever::{Namespace, Prefix, QualName, ns, serialize as html_serialize};
use js::jsapi::JSObject; use js::jsapi::JSObject;
use js::rust::HandleObject; use js::rust::HandleObject;
use keyboard_types::Modifiers;
use layout_api::{ use layout_api::{
GenericLayoutData, HTMLCanvasData, HTMLMediaData, LayoutElementType, LayoutNodeType, QueryMsg, GenericLayoutData, HTMLCanvasData, HTMLMediaData, LayoutElementType, LayoutNodeType, QueryMsg,
SVGSVGData, StyleData, TrustedNodeAddress, SVGSVGData, StyleData, TrustedNodeAddress,
@ -479,14 +481,10 @@ impl Node {
EventCancelable::Cancelable, // Step 3: cancelable EventCancelable::Cancelable, // Step 3: cancelable
Some(&window), // Step 7: view Some(&window), // Step 7: view
0, // detail uninitialized 0, // detail uninitialized
0, // coordinates uninitialized Point2D::zero(), // coordinates uninitialized
0, // coordinates uninitialized Point2D::zero(), // coordinates uninitialized
0, // coordinates uninitialized Point2D::zero(), // coordinates uninitialized
0, // coordinates uninitialized Modifiers::empty(), // empty modifiers
false, // ctrl_key
false, // alt_key
false, // shift_key
false, // meta_key
0, // button, left mouse button 0, // button, left mouse button
0, // buttons 0, // buttons
None, // related_target None, // related_target

View file

@ -5,8 +5,11 @@
use std::cell::Cell; use std::cell::Cell;
use dom_struct::dom_struct; use dom_struct::dom_struct;
use euclid::default::Point2D; use euclid::Point2D;
use js::rust::HandleObject; use js::rust::HandleObject;
use keyboard_types::Modifiers;
use script_bindings::codegen::GenericBindings::WindowBinding::WindowMethods;
use style_traits::CSSPixel;
use super::bindings::codegen::Bindings::MouseEventBinding::MouseEventMethods; use super::bindings::codegen::Bindings::MouseEventBinding::MouseEventMethods;
use crate::dom::bindings::cell::DomRefCell; use crate::dom::bindings::cell::DomRefCell;
@ -95,18 +98,14 @@ impl PointerEvent {
cancelable: EventCancelable, cancelable: EventCancelable,
view: Option<&Window>, view: Option<&Window>,
detail: i32, detail: i32,
screen_x: i32, screen_point: Point2D<i32, CSSPixel>,
screen_y: i32, client_point: Point2D<i32, CSSPixel>,
client_x: i32, page_point: Point2D<i32, CSSPixel>,
client_y: i32, modifiers: Modifiers,
ctrl_key: bool,
alt_key: bool,
shift_key: bool,
meta_key: bool,
button: i16, button: i16,
buttons: u16, buttons: u16,
related_target: Option<&EventTarget>, related_target: Option<&EventTarget>,
point_in_target: Option<Point2D<f32>>, point_in_target: Option<Point2D<f32, CSSPixel>>,
pointer_id: i32, pointer_id: i32,
width: i32, width: i32,
height: i32, height: i32,
@ -131,14 +130,10 @@ impl PointerEvent {
cancelable, cancelable,
view, view,
detail, detail,
screen_x, screen_point,
screen_y, client_point,
client_x, page_point,
client_y, modifiers,
ctrl_key,
alt_key,
shift_key,
meta_key,
button, button,
buttons, buttons,
related_target, related_target,
@ -170,18 +165,14 @@ impl PointerEvent {
cancelable: EventCancelable, cancelable: EventCancelable,
view: Option<&Window>, view: Option<&Window>,
detail: i32, detail: i32,
screen_x: i32, screen_point: Point2D<i32, CSSPixel>,
screen_y: i32, client_point: Point2D<i32, CSSPixel>,
client_x: i32, page_point: Point2D<i32, CSSPixel>,
client_y: i32, modifiers: Modifiers,
ctrl_key: bool,
alt_key: bool,
shift_key: bool,
meta_key: bool,
button: i16, button: i16,
buttons: u16, buttons: u16,
related_target: Option<&EventTarget>, related_target: Option<&EventTarget>,
point_in_target: Option<Point2D<f32>>, point_in_target: Option<Point2D<f32, CSSPixel>>,
pointer_id: i32, pointer_id: i32,
width: i32, width: i32,
height: i32, height: i32,
@ -205,14 +196,10 @@ impl PointerEvent {
cancelable, cancelable,
view, view,
detail, detail,
screen_x, screen_point,
screen_y, client_point,
client_x, page_point,
client_y, modifiers,
ctrl_key,
alt_key,
shift_key,
meta_key,
button, button,
buttons, buttons,
related_target, related_target,
@ -247,6 +234,10 @@ impl PointerEventMethods<crate::DomTypeHolder> for PointerEvent {
) -> DomRoot<PointerEvent> { ) -> DomRoot<PointerEvent> {
let bubbles = EventBubbles::from(init.parent.parent.parent.parent.bubbles); let bubbles = EventBubbles::from(init.parent.parent.parent.parent.bubbles);
let cancelable = EventCancelable::from(init.parent.parent.parent.parent.cancelable); let cancelable = EventCancelable::from(init.parent.parent.parent.parent.cancelable);
let page_point = Point2D::new(
window.ScrollX() + init.parent.clientX,
window.ScrollY() + init.parent.clientY,
);
PointerEvent::new_with_proto( PointerEvent::new_with_proto(
window, window,
proto, proto,
@ -255,14 +246,10 @@ impl PointerEventMethods<crate::DomTypeHolder> for PointerEvent {
cancelable, cancelable,
init.parent.parent.parent.view.as_deref(), init.parent.parent.parent.view.as_deref(),
init.parent.parent.parent.detail, init.parent.parent.parent.detail,
init.parent.screenX, Point2D::new(init.parent.screenX, init.parent.screenY),
init.parent.screenY, Point2D::new(init.parent.clientX, init.parent.clientY),
init.parent.clientX, page_point,
init.parent.clientY, init.parent.parent.modifiers(),
init.parent.parent.ctrlKey,
init.parent.parent.altKey,
init.parent.parent.shiftKey,
init.parent.parent.metaKey,
init.parent.button, init.parent.button,
init.parent.buttons, init.parent.buttons,
init.parent.relatedTarget.as_deref(), init.parent.relatedTarget.as_deref(),

View file

@ -49,9 +49,9 @@ use devtools_traits::{
}; };
use embedder_traits::user_content_manager::UserContentManager; use embedder_traits::user_content_manager::UserContentManager;
use embedder_traits::{ use embedder_traits::{
CompositorHitTestResult, EmbedderMsg, FocusSequenceNumber, InputEvent, EmbedderMsg, FocusSequenceNumber, InputEvent, JavaScriptEvaluationError,
JavaScriptEvaluationError, JavaScriptEvaluationId, MediaSessionActionType, MouseButton, JavaScriptEvaluationId, MediaSessionActionType, MouseButton, MouseButtonAction,
MouseButtonAction, MouseButtonEvent, Theme, ViewportDetails, WebDriverScriptCommand, MouseButtonEvent, Theme, ViewportDetails, WebDriverScriptCommand,
}; };
use euclid::Point2D; use euclid::Point2D;
use euclid::default::Rect; use euclid::default::Rect;
@ -1021,20 +1021,14 @@ impl ScriptThread {
fn process_mouse_move_event( fn process_mouse_move_event(
&self, &self,
document: &Document, document: &Document,
hit_test_result: Option<CompositorHitTestResult>, input_event: &ConstellationInputEvent,
pressed_mouse_buttons: u16,
can_gc: CanGc, can_gc: CanGc,
) { ) {
// Get the previous target temporarily // Get the previous target temporarily
let prev_mouse_over_target = self.topmost_mouse_over_target.get(); let prev_mouse_over_target = self.topmost_mouse_over_target.get();
unsafe { unsafe {
document.handle_mouse_move_event( document.handle_mouse_move_event(input_event, &self.topmost_mouse_over_target, can_gc)
hit_test_result,
pressed_mouse_buttons,
&self.topmost_mouse_over_target,
can_gc,
)
} }
// Short-circuit if nothing changed // Short-circuit if nothing changed
@ -1109,29 +1103,15 @@ impl ScriptThread {
match event.event { match event.event {
InputEvent::MouseButton(mouse_button_event) => { InputEvent::MouseButton(mouse_button_event) => {
document.handle_mouse_button_event( document.handle_mouse_button_event(mouse_button_event, &event, can_gc);
mouse_button_event,
event.hit_test_result,
event.pressed_mouse_buttons,
can_gc,
);
}, },
InputEvent::MouseMove(_) => { InputEvent::MouseMove(_) => {
// The event itself is unecessary here, because the point in the viewport is in the hit test. // The event itself is unecessary here, because the point in the viewport is in the hit test.
self.process_mouse_move_event( self.process_mouse_move_event(&document, &event, can_gc);
&document,
event.hit_test_result,
event.pressed_mouse_buttons,
can_gc,
);
}, },
InputEvent::MouseLeave(_) => { InputEvent::MouseLeave(_) => {
self.topmost_mouse_over_target.take(); self.topmost_mouse_over_target.take();
document.handle_mouse_leave_event( document.handle_mouse_leave_event(&event, can_gc);
event.hit_test_result,
event.pressed_mouse_buttons,
can_gc,
);
}, },
InputEvent::Touch(touch_event) => { InputEvent::Touch(touch_event) => {
let touch_result = let touch_result =

View file

@ -28,6 +28,7 @@ js = { workspace = true }
jstraceable_derive = { path = "../jstraceable_derive" } jstraceable_derive = { path = "../jstraceable_derive" }
libc = { workspace = true } libc = { workspace = true }
log = { workspace = true } log = { workspace = true }
keyboard-types = { workspace = true }
malloc_size_of = { workspace = true } malloc_size_of = { workspace = true }
malloc_size_of_derive = { workspace = true } malloc_size_of_derive = { workspace = true }
num-traits = { workspace = true } num-traits = { workspace = true }

View file

@ -22,9 +22,11 @@ use js::rust::{
HandleId, HandleValue, MutableHandleValue, ToString, get_object_class, is_dom_class, HandleId, HandleValue, MutableHandleValue, ToString, get_object_class, is_dom_class,
is_dom_object, maybe_wrap_value, is_dom_object, maybe_wrap_value,
}; };
use keyboard_types::Modifiers;
use num_traits::Float; use num_traits::Float;
use crate::JSTraceable; use crate::JSTraceable;
use crate::codegen::GenericBindings::EventModifierInitBinding::EventModifierInit;
use crate::inheritance::Castable; use crate::inheritance::Castable;
use crate::num::Finite; use crate::num::Finite;
use crate::reflector::{DomObject, Reflector}; use crate::reflector::{DomObject, Reflector};
@ -589,3 +591,52 @@ pub(crate) unsafe fn windowproxy_from_handlevalue<D: crate::DomTypes>(
let ptr = value.to_private() as *const D::WindowProxy; let ptr = value.to_private() as *const D::WindowProxy;
Ok(DomRoot::from_ref(&*ptr)) Ok(DomRoot::from_ref(&*ptr))
} }
impl<D: crate::DomTypes> EventModifierInit<D> {
pub fn modifiers(&self) -> Modifiers {
let mut modifiers = Modifiers::empty();
if self.altKey {
modifiers.insert(Modifiers::ALT);
}
if self.ctrlKey {
modifiers.insert(Modifiers::CONTROL);
}
if self.shiftKey {
modifiers.insert(Modifiers::SHIFT);
}
if self.metaKey {
modifiers.insert(Modifiers::META);
}
if self.keyModifierStateAltGraph {
modifiers.insert(Modifiers::ALT_GRAPH);
}
if self.keyModifierStateCapsLock {
modifiers.insert(Modifiers::CAPS_LOCK);
}
if self.keyModifierStateFn {
modifiers.insert(Modifiers::FN);
}
if self.keyModifierStateFnLock {
modifiers.insert(Modifiers::FN_LOCK);
}
if self.keyModifierStateHyper {
modifiers.insert(Modifiers::HYPER);
}
if self.keyModifierStateNumLock {
modifiers.insert(Modifiers::NUM_LOCK);
}
if self.keyModifierStateScrollLock {
modifiers.insert(Modifiers::SCROLL_LOCK);
}
if self.keyModifierStateSuper {
modifiers.insert(Modifiers::SUPER);
}
if self.keyModifierStateSymbol {
modifiers.insert(Modifiers::SYMBOL);
}
if self.keyModifierStateSymbolLock {
modifiers.insert(Modifiers::SYMBOL_LOCK);
}
modifiers
}
}

View file

@ -22,7 +22,7 @@ use std::sync::Arc;
use base::id::{PipelineId, ScrollTreeNodeId, WebViewId}; use base::id::{PipelineId, ScrollTreeNodeId, WebViewId};
use crossbeam_channel::Sender; use crossbeam_channel::Sender;
use euclid::{Scale, Size2D}; use euclid::{Point2D, Scale, Size2D};
use http::{HeaderMap, Method, StatusCode}; use http::{HeaderMap, Method, StatusCode};
use ipc_channel::ipc::IpcSender; use ipc_channel::ipc::IpcSender;
use log::warn; use log::warn;
@ -770,10 +770,14 @@ pub struct CompositorHitTestResult {
pub pipeline_id: PipelineId, pub pipeline_id: PipelineId,
/// The hit test point in the item's viewport. /// The hit test point in the item's viewport.
pub point_in_viewport: euclid::default::Point2D<f32>, pub point_in_viewport: Point2D<f32, CSSPixel>,
/// The hit test point relative to the root scroll node content origin / initial
/// containing block.
pub point_relative_to_initial_containing_block: Point2D<f32, CSSPixel>,
/// The hit test point relative to the item itself. /// The hit test point relative to the item itself.
pub point_relative_to_item: euclid::default::Point2D<f32>, pub point_relative_to_item: Point2D<f32, CSSPixel>,
/// The node address of the hit test result. /// The node address of the hit test result.
pub node: UntrustedNodeAddress, pub node: UntrustedNodeAddress,