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