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