Skip to main content

script/dom/event/
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::context::JSContext;
10use js::rust::HandleObject;
11use keyboard_types::Modifiers;
12use script_bindings::cell::DomRefCell;
13use script_bindings::reflector::reflect_dom_object_with_proto;
14use style::Atom;
15use style_traits::CSSPixel;
16
17use crate::dom::bindings::codegen::Bindings::MouseEventBinding::MouseEventMethods;
18use crate::dom::bindings::codegen::Bindings::PointerEventBinding::{
19    PointerEventInit, PointerEventMethods,
20};
21use crate::dom::bindings::num::Finite;
22use crate::dom::bindings::root::{Dom, DomRoot};
23use crate::dom::bindings::str::DOMString;
24use crate::dom::event::{EventBubbles, EventCancelable};
25use crate::dom::eventtarget::EventTarget;
26use crate::dom::mouseevent::MouseEvent;
27use crate::dom::window::Window;
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<Dom<PointerEvent>>>,
52    predicted_events: DomRefCell<Vec<Dom<PointerEvent>>>,
53}
54
55impl PointerEvent {
56    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    fn new_uninitialized_with_proto(
77        cx: &mut JSContext,
78        window: &Window,
79        proto: Option<HandleObject>,
80    ) -> DomRoot<PointerEvent> {
81        reflect_dom_object_with_proto(cx, Box::new(PointerEvent::new_inherited()), window, proto)
82    }
83
84    #[expect(clippy::too_many_arguments)]
85    pub(crate) fn new(
86        cx: &mut JSContext,
87        window: &Window,
88        event_type: Atom,
89        can_bubble: EventBubbles,
90        cancelable: EventCancelable,
91        view: Option<&Window>,
92        detail: i32,
93        screen_point: Point2D<i32, CSSPixel>,
94        client_point: Point2D<i32, CSSPixel>,
95        page_point: Point2D<i32, CSSPixel>,
96        modifiers: Modifiers,
97        button: i16,
98        buttons: u16,
99        related_target: Option<&EventTarget>,
100        point_in_target: Option<Point2D<f32, CSSPixel>>,
101        pointer_id: i32,
102        width: i32,
103        height: i32,
104        pressure: f32,
105        tangential_pressure: f32,
106        tilt_x: i32,
107        tilt_y: i32,
108        twist: i32,
109        altitude_angle: f64,
110        azimuth_angle: f64,
111        pointer_type: DOMString,
112        is_primary: bool,
113        coalesced_events: Vec<DomRoot<PointerEvent>>,
114        predicted_events: Vec<DomRoot<PointerEvent>>,
115    ) -> DomRoot<PointerEvent> {
116        Self::new_with_proto(
117            cx,
118            window,
119            None,
120            event_type,
121            can_bubble,
122            cancelable,
123            view,
124            detail,
125            screen_point,
126            client_point,
127            page_point,
128            modifiers,
129            button,
130            buttons,
131            related_target,
132            point_in_target,
133            pointer_id,
134            width,
135            height,
136            pressure,
137            tangential_pressure,
138            tilt_x,
139            tilt_y,
140            twist,
141            altitude_angle,
142            azimuth_angle,
143            pointer_type,
144            is_primary,
145            coalesced_events,
146            predicted_events,
147        )
148    }
149
150    #[expect(clippy::too_many_arguments)]
151    fn new_with_proto(
152        cx: &mut JSContext,
153        window: &Window,
154        proto: Option<HandleObject>,
155        event_type: Atom,
156        can_bubble: EventBubbles,
157        cancelable: EventCancelable,
158        view: Option<&Window>,
159        detail: i32,
160        screen_point: Point2D<i32, CSSPixel>,
161        client_point: Point2D<i32, CSSPixel>,
162        page_point: Point2D<i32, CSSPixel>,
163        modifiers: Modifiers,
164        button: i16,
165        buttons: u16,
166        related_target: Option<&EventTarget>,
167        point_in_target: Option<Point2D<f32, CSSPixel>>,
168        pointer_id: i32,
169        width: i32,
170        height: i32,
171        pressure: f32,
172        tangential_pressure: f32,
173        tilt_x: i32,
174        tilt_y: i32,
175        twist: i32,
176        altitude_angle: f64,
177        azimuth_angle: f64,
178        pointer_type: DOMString,
179        is_primary: bool,
180        coalesced_events: Vec<DomRoot<PointerEvent>>,
181        predicted_events: Vec<DomRoot<PointerEvent>>,
182    ) -> DomRoot<PointerEvent> {
183        let ev = PointerEvent::new_uninitialized_with_proto(cx, window, proto);
184        ev.mouseevent.initialize_mouse_event(
185            event_type,
186            can_bubble,
187            cancelable,
188            view,
189            detail,
190            screen_point,
191            client_point,
192            page_point,
193            modifiers,
194            button,
195            buttons,
196            related_target,
197            point_in_target,
198            None,
199        );
200        ev.pointer_id.set(pointer_id);
201        ev.width.set(width);
202        ev.height.set(height);
203        ev.pressure.set(pressure);
204        ev.tangential_pressure.set(tangential_pressure);
205        ev.tilt_x.set(tilt_x);
206        ev.tilt_y.set(tilt_y);
207        ev.twist.set(twist);
208        ev.altitude_angle.set(altitude_angle);
209        ev.azimuth_angle.set(azimuth_angle);
210        *ev.pointer_type.borrow_mut() = pointer_type;
211        ev.is_primary.set(is_primary);
212        *ev.coalesced_events.borrow_mut() = coalesced_events
213            .into_iter()
214            .map(|event| event.as_traced())
215            .collect();
216        *ev.predicted_events.borrow_mut() = predicted_events
217            .into_iter()
218            .map(|event| event.as_traced())
219            .collect();
220        ev
221    }
222}
223
224impl PointerEventMethods<crate::DomTypeHolder> for PointerEvent {
225    /// <https://w3c.github.io/pointerevents/#dom-pointerevent-constructor>
226    fn Constructor(
227        cx: &mut JSContext,
228        window: &Window,
229        proto: Option<HandleObject>,
230        event_type: DOMString,
231        init: &PointerEventInit,
232    ) -> DomRoot<PointerEvent> {
233        let bubbles = EventBubbles::from(init.parent.parent.parent.parent.bubbles);
234        let cancelable = EventCancelable::from(init.parent.parent.parent.parent.cancelable);
235        let scroll_offset = window.scroll_offset();
236        let page_point = Point2D::new(
237            scroll_offset.x as i32 + init.parent.clientX,
238            scroll_offset.y as i32 + init.parent.clientY,
239        );
240        PointerEvent::new_with_proto(
241            cx,
242            window,
243            proto,
244            event_type.into(),
245            bubbles,
246            cancelable,
247            init.parent.parent.parent.view.as_deref(),
248            init.parent.parent.parent.detail,
249            Point2D::new(init.parent.screenX, init.parent.screenY),
250            Point2D::new(init.parent.clientX, init.parent.clientY),
251            page_point,
252            init.parent.parent.modifiers(),
253            init.parent.button,
254            init.parent.buttons,
255            init.parent.relatedTarget.as_deref(),
256            None,
257            init.pointerId,
258            init.width,
259            init.height,
260            *init.pressure,
261            *init.tangentialPressure,
262            init.tiltX.unwrap_or_default(),
263            init.tiltY.unwrap_or_default(),
264            init.twist,
265            *init.altitudeAngle.unwrap_or_default(),
266            *init.azimuthAngle.unwrap_or_default(),
267            init.pointerType.clone(),
268            init.isPrimary,
269            init.coalescedEvents.clone(),
270            init.predictedEvents.clone(),
271        )
272    }
273
274    /// <https://w3c.github.io/pointerevents/#dom-pointerevent-pointerid>
275    fn PointerId(&self) -> i32 {
276        self.pointer_id.get()
277    }
278
279    /// <https://w3c.github.io/pointerevents/#dom-pointerevent-width>
280    fn Width(&self) -> i32 {
281        self.width.get()
282    }
283
284    /// <https://w3c.github.io/pointerevents/#dom-pointerevent-height>
285    fn Height(&self) -> i32 {
286        self.height.get()
287    }
288
289    /// <https://w3c.github.io/pointerevents/#dom-pointerevent-pressure>
290    fn Pressure(&self) -> Finite<f32> {
291        Finite::wrap(self.pressure.get())
292    }
293
294    /// <https://w3c.github.io/pointerevents/#dom-pointerevent-tangentialpressure>
295    fn TangentialPressure(&self) -> Finite<f32> {
296        Finite::wrap(self.tangential_pressure.get())
297    }
298
299    /// <https://w3c.github.io/pointerevents/#dom-pointerevent-tiltx>
300    fn TiltX(&self) -> i32 {
301        self.tilt_x.get()
302    }
303
304    /// <https://w3c.github.io/pointerevents/#dom-pointerevent-tilty>
305    fn TiltY(&self) -> i32 {
306        self.tilt_y.get()
307    }
308
309    /// <https://w3c.github.io/pointerevents/#dom-pointerevent-twist>
310    fn Twist(&self) -> i32 {
311        self.twist.get()
312    }
313
314    /// <https://w3c.github.io/pointerevents/#dom-pointerevent-altitudeangle>
315    fn AltitudeAngle(&self) -> Finite<f64> {
316        Finite::wrap(self.altitude_angle.get())
317    }
318
319    /// <https://w3c.github.io/pointerevents/#dom-pointerevent-azimuthangle>
320    fn AzimuthAngle(&self) -> Finite<f64> {
321        Finite::wrap(self.azimuth_angle.get())
322    }
323
324    /// <https://w3c.github.io/pointerevents/#dom-pointerevent-pointertype>
325    fn PointerType(&self) -> DOMString {
326        self.pointer_type.borrow().clone()
327    }
328
329    /// <https://w3c.github.io/pointerevents/#dom-pointerevent-isprimary>
330    fn IsPrimary(&self) -> bool {
331        self.is_primary.get()
332    }
333
334    /// <https://w3c.github.io/pointerevents/#dom-pointerevent-getcoalescedevents>
335    fn GetCoalescedEvents(&self) -> Vec<DomRoot<PointerEvent>> {
336        self.coalesced_events
337            .borrow()
338            .iter()
339            .map(|event| event.as_rooted())
340            .collect()
341    }
342
343    /// <https://w3c.github.io/pointerevents/#dom-pointerevent-getpredictedevents>
344    fn GetPredictedEvents(&self) -> Vec<DomRoot<PointerEvent>> {
345        self.predicted_events
346            .borrow()
347            .iter()
348            .map(|event| event.as_rooted())
349            .collect()
350    }
351
352    /// <https://dom.spec.whatwg.org/#dom-event-istrusted>
353    fn IsTrusted(&self) -> bool {
354        self.mouseevent.IsTrusted()
355    }
356}