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