script/dom/
pointerevent.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use std::cell::Cell;
6
7use dom_struct::dom_struct;
8use euclid::Point2D;
9use js::rust::HandleObject;
10use keyboard_types::Modifiers;
11use script_bindings::inheritance::Castable;
12use style_traits::CSSPixel;
13
14use super::bindings::codegen::Bindings::MouseEventBinding::MouseEventMethods;
15use crate::dom::bindings::cell::DomRefCell;
16use crate::dom::bindings::codegen::Bindings::PointerEventBinding::{
17    PointerEventInit, PointerEventMethods,
18};
19use crate::dom::bindings::num::Finite;
20use crate::dom::bindings::reflector::reflect_dom_object_with_proto;
21use crate::dom::bindings::root::DomRoot;
22use crate::dom::bindings::str::DOMString;
23use crate::dom::event::{Event, EventBubbles, EventCancelable};
24use crate::dom::eventtarget::EventTarget;
25use crate::dom::mouseevent::MouseEvent;
26use crate::dom::window::Window;
27use crate::script_runtime::CanGc;
28
29/// <https://w3c.github.io/pointerevents/#dom-pointerevent-pointerid>
30#[derive(Clone, Copy, MallocSizeOf, PartialEq)]
31pub(crate) enum PointerId {
32    NonPointerDevice = -1,
33    Mouse,
34}
35
36#[dom_struct]
37pub(crate) struct PointerEvent {
38    mouseevent: MouseEvent,
39    pointer_id: Cell<i32>,
40    width: Cell<i32>,
41    height: Cell<i32>,
42    pressure: Cell<f32>,
43    tangential_pressure: Cell<f32>,
44    tilt_x: Cell<i32>,
45    tilt_y: Cell<i32>,
46    twist: Cell<i32>,
47    altitude_angle: Cell<f64>,
48    azimuth_angle: Cell<f64>,
49    pointer_type: DomRefCell<DOMString>,
50    is_primary: Cell<bool>,
51    coalesced_events: DomRefCell<Vec<DomRoot<PointerEvent>>>,
52    predicted_events: DomRefCell<Vec<DomRoot<PointerEvent>>>,
53}
54
55impl PointerEvent {
56    pub(crate) fn new_inherited() -> PointerEvent {
57        PointerEvent {
58            mouseevent: MouseEvent::new_inherited(),
59            pointer_id: Cell::new(0),
60            width: Cell::new(0),
61            height: Cell::new(0),
62            pressure: Cell::new(0.),
63            tangential_pressure: Cell::new(0.),
64            tilt_x: Cell::new(0),
65            tilt_y: Cell::new(0),
66            twist: Cell::new(0),
67            altitude_angle: Cell::new(0.),
68            azimuth_angle: Cell::new(0.),
69            pointer_type: DomRefCell::new(DOMString::new()),
70            is_primary: Cell::new(false),
71            coalesced_events: DomRefCell::new(Vec::new()),
72            predicted_events: DomRefCell::new(Vec::new()),
73        }
74    }
75
76    pub(crate) fn new_uninitialized(window: &Window, can_gc: CanGc) -> DomRoot<PointerEvent> {
77        Self::new_uninitialized_with_proto(window, None, can_gc)
78    }
79
80    fn new_uninitialized_with_proto(
81        window: &Window,
82        proto: Option<HandleObject>,
83        can_gc: CanGc,
84    ) -> DomRoot<PointerEvent> {
85        reflect_dom_object_with_proto(
86            Box::new(PointerEvent::new_inherited()),
87            window,
88            proto,
89            can_gc,
90        )
91    }
92
93    #[expect(clippy::too_many_arguments)]
94    pub(crate) fn new(
95        window: &Window,
96        type_: DOMString,
97        can_bubble: EventBubbles,
98        cancelable: EventCancelable,
99        view: Option<&Window>,
100        detail: i32,
101        screen_point: Point2D<i32, CSSPixel>,
102        client_point: Point2D<i32, CSSPixel>,
103        page_point: Point2D<i32, CSSPixel>,
104        modifiers: Modifiers,
105        button: i16,
106        buttons: u16,
107        related_target: Option<&EventTarget>,
108        point_in_target: Option<Point2D<f32, CSSPixel>>,
109        pointer_id: i32,
110        width: i32,
111        height: i32,
112        pressure: f32,
113        tangential_pressure: f32,
114        tilt_x: i32,
115        tilt_y: i32,
116        twist: i32,
117        altitude_angle: f64,
118        azimuth_angle: f64,
119        pointer_type: DOMString,
120        is_primary: bool,
121        coalesced_events: Vec<DomRoot<PointerEvent>>,
122        predicted_events: Vec<DomRoot<PointerEvent>>,
123        can_gc: CanGc,
124    ) -> DomRoot<PointerEvent> {
125        Self::new_with_proto(
126            window,
127            None,
128            type_,
129            can_bubble,
130            cancelable,
131            view,
132            detail,
133            screen_point,
134            client_point,
135            page_point,
136            modifiers,
137            button,
138            buttons,
139            related_target,
140            point_in_target,
141            pointer_id,
142            width,
143            height,
144            pressure,
145            tangential_pressure,
146            tilt_x,
147            tilt_y,
148            twist,
149            altitude_angle,
150            azimuth_angle,
151            pointer_type,
152            is_primary,
153            coalesced_events,
154            predicted_events,
155            can_gc,
156        )
157    }
158
159    #[expect(clippy::too_many_arguments)]
160    fn new_with_proto(
161        window: &Window,
162        proto: Option<HandleObject>,
163        type_: DOMString,
164        can_bubble: EventBubbles,
165        cancelable: EventCancelable,
166        view: Option<&Window>,
167        detail: i32,
168        screen_point: Point2D<i32, CSSPixel>,
169        client_point: Point2D<i32, CSSPixel>,
170        page_point: Point2D<i32, CSSPixel>,
171        modifiers: Modifiers,
172        button: i16,
173        buttons: u16,
174        related_target: Option<&EventTarget>,
175        point_in_target: Option<Point2D<f32, CSSPixel>>,
176        pointer_id: i32,
177        width: i32,
178        height: i32,
179        pressure: f32,
180        tangential_pressure: f32,
181        tilt_x: i32,
182        tilt_y: i32,
183        twist: i32,
184        altitude_angle: f64,
185        azimuth_angle: f64,
186        pointer_type: DOMString,
187        is_primary: bool,
188        coalesced_events: Vec<DomRoot<PointerEvent>>,
189        predicted_events: Vec<DomRoot<PointerEvent>>,
190        can_gc: CanGc,
191    ) -> DomRoot<PointerEvent> {
192        let ev = PointerEvent::new_uninitialized_with_proto(window, proto, can_gc);
193        // See <https://w3c.github.io/pointerevents/#attributes-and-default-actions>
194        let composed = type_ != "pointerenter" && type_ != "pointerleave";
195        ev.mouseevent.initialize_mouse_event(
196            type_,
197            can_bubble,
198            cancelable,
199            view,
200            detail,
201            screen_point,
202            client_point,
203            page_point,
204            modifiers,
205            button,
206            buttons,
207            related_target,
208            point_in_target,
209        );
210        ev.mouseevent.upcast::<Event>().set_composed(composed);
211        ev.pointer_id.set(pointer_id);
212        ev.width.set(width);
213        ev.height.set(height);
214        ev.pressure.set(pressure);
215        ev.tangential_pressure.set(tangential_pressure);
216        ev.tilt_x.set(tilt_x);
217        ev.tilt_y.set(tilt_y);
218        ev.twist.set(twist);
219        ev.altitude_angle.set(altitude_angle);
220        ev.azimuth_angle.set(azimuth_angle);
221        *ev.pointer_type.borrow_mut() = pointer_type;
222        ev.is_primary.set(is_primary);
223        *ev.coalesced_events.borrow_mut() = coalesced_events;
224        *ev.predicted_events.borrow_mut() = predicted_events;
225        ev
226    }
227}
228
229impl PointerEventMethods<crate::DomTypeHolder> for PointerEvent {
230    /// <https://w3c.github.io/pointerevents/#dom-pointerevent-constructor>
231    fn Constructor(
232        window: &Window,
233        proto: Option<HandleObject>,
234        can_gc: CanGc,
235        type_: DOMString,
236        init: &PointerEventInit,
237    ) -> DomRoot<PointerEvent> {
238        let bubbles = EventBubbles::from(init.parent.parent.parent.parent.bubbles);
239        let cancelable = EventCancelable::from(init.parent.parent.parent.parent.cancelable);
240        let scroll_offset = window.scroll_offset();
241        let page_point = Point2D::new(
242            scroll_offset.x as i32 + init.parent.clientX,
243            scroll_offset.y as i32 + init.parent.clientY,
244        );
245        PointerEvent::new_with_proto(
246            window,
247            proto,
248            type_,
249            bubbles,
250            cancelable,
251            init.parent.parent.parent.view.as_deref(),
252            init.parent.parent.parent.detail,
253            Point2D::new(init.parent.screenX, init.parent.screenY),
254            Point2D::new(init.parent.clientX, init.parent.clientY),
255            page_point,
256            init.parent.parent.modifiers(),
257            init.parent.button,
258            init.parent.buttons,
259            init.parent.relatedTarget.as_deref(),
260            None,
261            init.pointerId,
262            init.width,
263            init.height,
264            *init.pressure,
265            *init.tangentialPressure,
266            init.tiltX.unwrap_or_default(),
267            init.tiltY.unwrap_or_default(),
268            init.twist,
269            *init.altitudeAngle.unwrap_or_default(),
270            *init.azimuthAngle.unwrap_or_default(),
271            init.pointerType.clone(),
272            init.isPrimary,
273            init.coalescedEvents.clone(),
274            init.predictedEvents.clone(),
275            can_gc,
276        )
277    }
278
279    /// <https://w3c.github.io/pointerevents/#dom-pointerevent-pointerid>
280    fn PointerId(&self) -> i32 {
281        self.pointer_id.get()
282    }
283
284    /// <https://w3c.github.io/pointerevents/#dom-pointerevent-width>
285    fn Width(&self) -> i32 {
286        self.width.get()
287    }
288
289    /// <https://w3c.github.io/pointerevents/#dom-pointerevent-height>
290    fn Height(&self) -> i32 {
291        self.height.get()
292    }
293
294    /// <https://w3c.github.io/pointerevents/#dom-pointerevent-pressure>
295    fn Pressure(&self) -> Finite<f32> {
296        Finite::wrap(self.pressure.get())
297    }
298
299    /// <https://w3c.github.io/pointerevents/#dom-pointerevent-tangentialpressure>
300    fn TangentialPressure(&self) -> Finite<f32> {
301        Finite::wrap(self.tangential_pressure.get())
302    }
303
304    /// <https://w3c.github.io/pointerevents/#dom-pointerevent-tiltx>
305    fn TiltX(&self) -> i32 {
306        self.tilt_x.get()
307    }
308
309    /// <https://w3c.github.io/pointerevents/#dom-pointerevent-tilty>
310    fn TiltY(&self) -> i32 {
311        self.tilt_y.get()
312    }
313
314    /// <https://w3c.github.io/pointerevents/#dom-pointerevent-twist>
315    fn Twist(&self) -> i32 {
316        self.twist.get()
317    }
318
319    /// <https://w3c.github.io/pointerevents/#dom-pointerevent-altitudeangle>
320    fn AltitudeAngle(&self) -> Finite<f64> {
321        Finite::wrap(self.altitude_angle.get())
322    }
323
324    /// <https://w3c.github.io/pointerevents/#dom-pointerevent-azimuthangle>
325    fn AzimuthAngle(&self) -> Finite<f64> {
326        Finite::wrap(self.azimuth_angle.get())
327    }
328
329    /// <https://w3c.github.io/pointerevents/#dom-pointerevent-pointertype>
330    fn PointerType(&self) -> DOMString {
331        self.pointer_type.borrow().clone()
332    }
333
334    /// <https://w3c.github.io/pointerevents/#dom-pointerevent-isprimary>
335    fn IsPrimary(&self) -> bool {
336        self.is_primary.get()
337    }
338
339    /// <https://w3c.github.io/pointerevents/#dom-pointerevent-getcoalescedevents>
340    fn GetCoalescedEvents(&self) -> Vec<DomRoot<PointerEvent>> {
341        self.coalesced_events.borrow().clone()
342    }
343
344    /// <https://w3c.github.io/pointerevents/#dom-pointerevent-getpredictedevents>
345    fn GetPredictedEvents(&self) -> Vec<DomRoot<PointerEvent>> {
346        self.predicted_events.borrow().clone()
347    }
348
349    /// <https://dom.spec.whatwg.org/#dom-event-istrusted>
350    fn IsTrusted(&self) -> bool {
351        self.mouseevent.IsTrusted()
352    }
353}