use std::cell::Cell;
use dom_struct::dom_struct;
use euclid::Point2D;
use js::context::JSContext;
use js::rust::HandleObject;
use keyboard_types::Modifiers;
use script_bindings::cell::DomRefCell;
use script_bindings::reflector::reflect_dom_object_with_proto;
use style::Atom;
use style_traits::CSSPixel;
use crate::dom::bindings::codegen::Bindings::MouseEventBinding::MouseEventMethods;
use crate::dom::bindings::codegen::Bindings::PointerEventBinding::{
PointerEventInit, PointerEventMethods,
};
use crate::dom::bindings::num::Finite;
use crate::dom::bindings::root::{Dom, DomRoot};
use crate::dom::bindings::str::DOMString;
use crate::dom::event::{EventBubbles, EventCancelable};
use crate::dom::eventtarget::EventTarget;
use crate::dom::mouseevent::MouseEvent;
use crate::dom::window::Window;
#[derive(Clone, Copy, MallocSizeOf, PartialEq)]
pub(crate) enum PointerId {
NonPointerDevice = -1,
Mouse,
}
#[dom_struct]
pub(crate) struct PointerEvent {
mouseevent: MouseEvent,
pointer_id: Cell<i32>,
width: Cell<i32>,
height: Cell<i32>,
pressure: Cell<f32>,
tangential_pressure: Cell<f32>,
tilt_x: Cell<i32>,
tilt_y: Cell<i32>,
twist: Cell<i32>,
altitude_angle: Cell<f64>,
azimuth_angle: Cell<f64>,
pointer_type: DomRefCell<DOMString>,
is_primary: Cell<bool>,
coalesced_events: DomRefCell<Vec<Dom<PointerEvent>>>,
predicted_events: DomRefCell<Vec<Dom<PointerEvent>>>,
}
impl PointerEvent {
fn new_inherited() -> PointerEvent {
PointerEvent {
mouseevent: MouseEvent::new_inherited(),
pointer_id: Cell::new(0),
width: Cell::new(0),
height: Cell::new(0),
pressure: Cell::new(0.),
tangential_pressure: Cell::new(0.),
tilt_x: Cell::new(0),
tilt_y: Cell::new(0),
twist: Cell::new(0),
altitude_angle: Cell::new(0.),
azimuth_angle: Cell::new(0.),
pointer_type: DomRefCell::new(DOMString::new()),
is_primary: Cell::new(false),
coalesced_events: DomRefCell::new(Vec::new()),
predicted_events: DomRefCell::new(Vec::new()),
}
}
fn new_uninitialized_with_proto(
cx: &mut JSContext,
window: &Window,
proto: Option<HandleObject>,
) -> DomRoot<PointerEvent> {
reflect_dom_object_with_proto(cx, Box::new(PointerEvent::new_inherited()), window, proto)
}
#[expect(clippy::too_many_arguments)]
pub(crate) fn new(
cx: &mut JSContext,
window: &Window,
event_type: Atom,
can_bubble: EventBubbles,
cancelable: EventCancelable,
view: Option<&Window>,
detail: i32,
screen_point: Point2D<i32, CSSPixel>,
client_point: Point2D<i32, CSSPixel>,
page_point: Point2D<i32, CSSPixel>,
modifiers: Modifiers,
button: i16,
buttons: u16,
related_target: Option<&EventTarget>,
point_in_target: Option<Point2D<f32, CSSPixel>>,
pointer_id: i32,
width: i32,
height: i32,
pressure: f32,
tangential_pressure: f32,
tilt_x: i32,
tilt_y: i32,
twist: i32,
altitude_angle: f64,
azimuth_angle: f64,
pointer_type: DOMString,
is_primary: bool,
coalesced_events: Vec<DomRoot<PointerEvent>>,
predicted_events: Vec<DomRoot<PointerEvent>>,
) -> DomRoot<PointerEvent> {
Self::new_with_proto(
cx,
window,
None,
event_type,
can_bubble,
cancelable,
view,
detail,
screen_point,
client_point,
page_point,
modifiers,
button,
buttons,
related_target,
point_in_target,
pointer_id,
width,
height,
pressure,
tangential_pressure,
tilt_x,
tilt_y,
twist,
altitude_angle,
azimuth_angle,
pointer_type,
is_primary,
coalesced_events,
predicted_events,
)
}
#[expect(clippy::too_many_arguments)]
fn new_with_proto(
cx: &mut JSContext,
window: &Window,
proto: Option<HandleObject>,
event_type: Atom,
can_bubble: EventBubbles,
cancelable: EventCancelable,
view: Option<&Window>,
detail: i32,
screen_point: Point2D<i32, CSSPixel>,
client_point: Point2D<i32, CSSPixel>,
page_point: Point2D<i32, CSSPixel>,
modifiers: Modifiers,
button: i16,
buttons: u16,
related_target: Option<&EventTarget>,
point_in_target: Option<Point2D<f32, CSSPixel>>,
pointer_id: i32,
width: i32,
height: i32,
pressure: f32,
tangential_pressure: f32,
tilt_x: i32,
tilt_y: i32,
twist: i32,
altitude_angle: f64,
azimuth_angle: f64,
pointer_type: DOMString,
is_primary: bool,
coalesced_events: Vec<DomRoot<PointerEvent>>,
predicted_events: Vec<DomRoot<PointerEvent>>,
) -> DomRoot<PointerEvent> {
let ev = PointerEvent::new_uninitialized_with_proto(cx, window, proto);
ev.mouseevent.initialize_mouse_event(
event_type,
can_bubble,
cancelable,
view,
detail,
screen_point,
client_point,
page_point,
modifiers,
button,
buttons,
related_target,
point_in_target,
);
ev.pointer_id.set(pointer_id);
ev.width.set(width);
ev.height.set(height);
ev.pressure.set(pressure);
ev.tangential_pressure.set(tangential_pressure);
ev.tilt_x.set(tilt_x);
ev.tilt_y.set(tilt_y);
ev.twist.set(twist);
ev.altitude_angle.set(altitude_angle);
ev.azimuth_angle.set(azimuth_angle);
*ev.pointer_type.borrow_mut() = pointer_type;
ev.is_primary.set(is_primary);
*ev.coalesced_events.borrow_mut() = coalesced_events
.into_iter()
.map(|event| event.as_traced())
.collect();
*ev.predicted_events.borrow_mut() = predicted_events
.into_iter()
.map(|event| event.as_traced())
.collect();
ev
}
}
impl PointerEventMethods<crate::DomTypeHolder> for PointerEvent {
fn Constructor(
cx: &mut JSContext,
window: &Window,
proto: Option<HandleObject>,
event_type: DOMString,
init: &PointerEventInit,
) -> DomRoot<PointerEvent> {
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();
let page_point = Point2D::new(
scroll_offset.x as i32 + init.parent.clientX,
scroll_offset.y as i32 + init.parent.clientY,
);
PointerEvent::new_with_proto(
cx,
window,
proto,
event_type.into(),
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),
page_point,
init.parent.parent.modifiers(),
init.parent.button,
init.parent.buttons,
init.parent.relatedTarget.as_deref(),
None,
init.pointerId,
init.width,
init.height,
*init.pressure,
*init.tangentialPressure,
init.tiltX.unwrap_or_default(),
init.tiltY.unwrap_or_default(),
init.twist,
*init.altitudeAngle.unwrap_or_default(),
*init.azimuthAngle.unwrap_or_default(),
init.pointerType.clone(),
init.isPrimary,
init.coalescedEvents.clone(),
init.predictedEvents.clone(),
)
}
fn PointerId(&self) -> i32 {
self.pointer_id.get()
}
fn Width(&self) -> i32 {
self.width.get()
}
fn Height(&self) -> i32 {
self.height.get()
}
fn Pressure(&self) -> Finite<f32> {
Finite::wrap(self.pressure.get())
}
fn TangentialPressure(&self) -> Finite<f32> {
Finite::wrap(self.tangential_pressure.get())
}
fn TiltX(&self) -> i32 {
self.tilt_x.get()
}
fn TiltY(&self) -> i32 {
self.tilt_y.get()
}
fn Twist(&self) -> i32 {
self.twist.get()
}
fn AltitudeAngle(&self) -> Finite<f64> {
Finite::wrap(self.altitude_angle.get())
}
fn AzimuthAngle(&self) -> Finite<f64> {
Finite::wrap(self.azimuth_angle.get())
}
fn PointerType(&self) -> DOMString {
self.pointer_type.borrow().clone()
}
fn IsPrimary(&self) -> bool {
self.is_primary.get()
}
fn GetCoalescedEvents(&self) -> Vec<DomRoot<PointerEvent>> {
self.coalesced_events
.borrow()
.iter()
.map(|event| event.as_rooted())
.collect()
}
fn GetPredictedEvents(&self) -> Vec<DomRoot<PointerEvent>> {
self.predicted_events
.borrow()
.iter()
.map(|event| event.as_rooted())
.collect()
}
fn IsTrusted(&self) -> bool {
self.mouseevent.IsTrusted()
}
}