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