Skip to main content

script/dom/event/
mouseevent.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;
6use std::default::Default;
7use std::f64::consts::PI;
8
9use dom_struct::dom_struct;
10use embedder_traits::MouseButton;
11use euclid::Point2D;
12use js::context::JSContext;
13use js::rust::HandleObject;
14use keyboard_types::Modifiers;
15use script_bindings::codegen::GenericBindings::WindowBinding::WindowMethods;
16use script_bindings::match_domstring_ascii;
17use script_bindings::reflector::reflect_dom_object_with_proto;
18use script_traits::{ConstellationInputEvent, MouseButtons};
19use style::Atom;
20use style_traits::CSSPixel;
21
22use crate::dom::bindings::codegen::Bindings::EventBinding::Event_Binding::EventMethods;
23use crate::dom::bindings::codegen::Bindings::MouseEventBinding;
24use crate::dom::bindings::codegen::Bindings::MouseEventBinding::MouseEventMethods;
25use crate::dom::bindings::codegen::Bindings::UIEventBinding::UIEventMethods;
26use crate::dom::bindings::error::Fallible;
27use crate::dom::bindings::inheritance::Castable;
28use crate::dom::bindings::reflector::DomGlobal;
29use crate::dom::bindings::root::DomRoot;
30use crate::dom::bindings::str::DOMString;
31use crate::dom::document::FireMouseEventType;
32use crate::dom::event::{Event, EventBubbles, EventCancelable};
33use crate::dom::eventtarget::EventTarget;
34use crate::dom::inputevent::HitTestResult;
35use crate::dom::node::Node;
36use crate::dom::pointerevent::{PointerEvent, PointerId};
37use crate::dom::uievent::UIEvent;
38use crate::dom::window::Window;
39
40/// <https://w3c.github.io/uievents/#interface-mouseevent>
41#[dom_struct]
42pub(crate) struct MouseEvent {
43    uievent: UIEvent,
44
45    /// The point on the screen of where this [`MouseEvent`] was originally triggered,
46    /// to use during the dispatch phase.
47    ///
48    /// See:
49    /// <https://w3c.github.io/uievents/#dom-mouseevent-screenx>
50    /// <https://w3c.github.io/uievents/#dom-mouseevent-screeny>
51    #[no_trace]
52    screen_point: Cell<Point2D<i32, CSSPixel>>,
53
54    /// The point in the viewport of where this [`MouseEvent`] was originally triggered,
55    /// to use during the dispatch phase.
56    ///
57    /// See:
58    /// <https://w3c.github.io/uievents/#dom-mouseevent-clientx>
59    /// <https://w3c.github.io/uievents/#dom-mouseevent-clienty>
60    #[no_trace]
61    client_point: Cell<Point2D<i32, CSSPixel>>,
62
63    /// The point in the initial containing block of where this [`MouseEvent`] was
64    /// originally triggered to use during the dispatch phase.
65    ///
66    /// See:
67    /// <https://w3c.github.io/uievents/#dom-mouseevent-pagex>
68    /// <https://w3c.github.io/uievents/#dom-mouseevent-pagey>
69    #[no_trace]
70    page_point: Cell<Point2D<i32, CSSPixel>>,
71
72    /// The keyboard modifiers that were active when this mouse event was triggered.
73    #[no_trace]
74    modifiers: Cell<Modifiers>,
75
76    /// <https://w3c.github.io/uievents/#dom-mouseevent-button>
77    #[no_trace]
78    button: Cell<MouseButton>,
79
80    /// <https://w3c.github.io/uievents/#dom-mouseevent-buttons>
81    #[no_trace]
82    buttons: Cell<MouseButtons>,
83
84    #[no_trace]
85    point_in_target: Cell<Option<Point2D<f32, CSSPixel>>>,
86}
87
88impl MouseEvent {
89    pub(crate) fn new_inherited() -> MouseEvent {
90        MouseEvent {
91            uievent: UIEvent::new_inherited(),
92            screen_point: Cell::new(Default::default()),
93            client_point: Cell::new(Default::default()),
94            page_point: Cell::new(Default::default()),
95            modifiers: Cell::new(Modifiers::empty()),
96            button: Cell::new(MouseButton::Primary),
97            buttons: Cell::new(MouseButtons::empty()),
98            point_in_target: Cell::new(None),
99        }
100    }
101
102    pub(crate) fn new_uninitialized(cx: &mut JSContext, window: &Window) -> DomRoot<MouseEvent> {
103        Self::new_uninitialized_with_proto(cx, window, None)
104    }
105
106    fn new_uninitialized_with_proto(
107        cx: &mut JSContext,
108        window: &Window,
109        proto: Option<HandleObject>,
110    ) -> DomRoot<MouseEvent> {
111        reflect_dom_object_with_proto(cx, Box::new(MouseEvent::new_inherited()), window, proto)
112    }
113
114    #[allow(clippy::too_many_arguments)]
115    pub(crate) fn new(
116        cx: &mut JSContext,
117        window: &Window,
118        event_type: Atom,
119        can_bubble: EventBubbles,
120        cancelable: EventCancelable,
121        view: Option<&Window>,
122        detail: i32,
123        screen_point: Point2D<i32, CSSPixel>,
124        client_point: Point2D<i32, CSSPixel>,
125        page_point: Point2D<i32, CSSPixel>,
126        modifiers: Modifiers,
127        button: MouseButton,
128        buttons: MouseButtons,
129        related_target: Option<&EventTarget>,
130        point_in_target: Option<Point2D<f32, CSSPixel>>,
131    ) -> DomRoot<MouseEvent> {
132        Self::new_with_proto(
133            cx,
134            window,
135            None,
136            event_type,
137            can_bubble,
138            cancelable,
139            view,
140            detail,
141            screen_point,
142            client_point,
143            page_point,
144            modifiers,
145            button,
146            buttons,
147            related_target,
148            point_in_target,
149        )
150    }
151
152    #[allow(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    ) -> DomRoot<MouseEvent> {
171        let ev = MouseEvent::new_uninitialized_with_proto(cx, window, proto);
172        ev.initialize_mouse_event(
173            event_type,
174            can_bubble,
175            cancelable,
176            view,
177            detail,
178            screen_point,
179            client_point,
180            page_point,
181            modifiers,
182            button,
183            buttons,
184            related_target,
185            point_in_target,
186        );
187        ev
188    }
189
190    /// <https://w3c.github.io/pointerevents/#initialize-a-mouseevent>
191    #[expect(clippy::too_many_arguments)]
192    pub(crate) fn initialize_mouse_event(
193        &self,
194        event_type: Atom,
195        can_bubble: EventBubbles,
196        cancelable: EventCancelable,
197        view: Option<&Window>,
198        detail: i32,
199        screen_point: Point2D<i32, CSSPixel>,
200        client_point: Point2D<i32, CSSPixel>,
201        page_point: Point2D<i32, CSSPixel>,
202        modifiers: Modifiers,
203        button: MouseButton,
204        buttons: MouseButtons,
205        related_target: Option<&EventTarget>,
206        point_in_target: Option<Point2D<f32, CSSPixel>>,
207    ) {
208        self.uievent.initialize_ui_event(
209            event_type,
210            view.map(|window| window.upcast::<EventTarget>()),
211            can_bubble,
212            cancelable,
213        );
214
215        self.uievent.set_detail(detail);
216        self.screen_point.set(screen_point);
217        self.client_point.set(client_point);
218        self.page_point.set(page_point);
219        self.modifiers.set(modifiers);
220        self.button.set(button);
221        self.buttons.set(buttons);
222        self.upcast::<Event>().set_related_target(related_target);
223        self.point_in_target.set(point_in_target);
224
225        // From <https://w3c.github.io/uievents/#dom-uievent-which>:
226        // > For MouseEvents, this contains a value equal to the value stored in button+1.
227        self.uievent.set_which((i16::from(button) + 1) as u32);
228    }
229
230    pub(crate) fn new_for_platform_motion_event(
231        cx: &mut JSContext,
232        window: &Window,
233        event_name: FireMouseEventType,
234        hit_test_result: &HitTestResult,
235        input_event: &ConstellationInputEvent,
236    ) -> DomRoot<Self> {
237        // These values come from the event tables in
238        // <https://w3c.github.io/pointerevents/#mouse-event-types>.
239        let (bubbles, cancelable, composed) = match event_name {
240            FireMouseEventType::Move | FireMouseEventType::Over | FireMouseEventType::Out => {
241                (EventBubbles::Bubbles, EventCancelable::Cancelable, true)
242            },
243            FireMouseEventType::Enter | FireMouseEventType::Leave => (
244                EventBubbles::DoesNotBubble,
245                EventCancelable::NotCancelable,
246                false,
247            ),
248        };
249
250        let mouse_event = Self::new(
251            cx,
252            window,
253            Atom::from(event_name.as_str()),
254            bubbles,
255            cancelable,
256            Some(window),
257            0i32,
258            hit_test_result.point_in_frame.to_i32(),
259            hit_test_result.point_in_frame.to_i32(),
260            hit_test_result
261                .point_relative_to_initial_containing_block
262                .to_i32(),
263            input_event.active_keyboard_modifiers,
264            MouseButton::Primary,
265            input_event.pressed_mouse_buttons,
266            None,
267            None,
268        );
269
270        let event = mouse_event.upcast::<Event>();
271        event.set_composed(composed);
272        event.set_trusted(true);
273
274        mouse_event
275    }
276
277    /// Create a [MouseEvent] triggered by the embedder.
278    ///
279    /// <https://w3c.github.io/pointerevents/#create-a-cancelable-mouseevent>
280    #[expect(clippy::too_many_arguments)]
281    pub(crate) fn for_platform_button_event(
282        cx: &mut JSContext,
283        event_type: Atom,
284        event: embedder_traits::MouseButtonEvent,
285        pressed_mouse_buttons: MouseButtons,
286        window: &Window,
287        hit_test_result: &HitTestResult,
288        modifiers: Modifiers,
289        click_count: usize,
290    ) -> DomRoot<Self> {
291        let client_point = hit_test_result.point_in_frame.to_i32();
292        let page_point = hit_test_result
293            .point_relative_to_initial_containing_block
294            .to_i32();
295
296        let mouse_event = Self::new(
297            cx,
298            window,
299            event_type,
300            EventBubbles::Bubbles,
301            EventCancelable::Cancelable,
302            Some(window),
303            click_count as i32,
304            client_point, // TODO: Get real screen coordinates?
305            client_point,
306            page_point,
307            modifiers,
308            event.button,
309            pressed_mouse_buttons,
310            None,
311            Some(hit_test_result.point_in_node),
312        );
313
314        mouse_event.upcast::<Event>().set_trusted(true);
315        mouse_event.upcast::<Event>().set_composed(true);
316
317        mouse_event
318    }
319
320    /// Create a PointerEvent from this MouseEvent.
321    /// <https://w3c.github.io/pointerevents/#the-primary-pointer>
322    /// For mouse, the pointer ID is always -1, and is_primary is always true.
323    pub(crate) fn to_pointer_event(
324        &self,
325        cx: &mut JSContext,
326        event_type: Atom,
327    ) -> DomRoot<crate::dom::pointerevent::PointerEvent> {
328        // TODO: This function should almost certainly accept an enumn for the event type.
329        let is_pointer_down = &*event_type == "pointerdown";
330        let is_pointer_move = &*event_type == "pointermove";
331        let is_pointer_up = &*event_type == "pointerup";
332
333        // Pressure is 0.5 when button is down, 0.0 when up
334        let pressure = if is_pointer_down || (is_pointer_move && !self.buttons.get().is_empty()) {
335            0.5
336        } else {
337            0.0
338        };
339
340        let button = if is_pointer_down || is_pointer_up {
341            self.button.get()
342        } else {
343            MouseButton::None
344        };
345
346        // https://w3c.github.io/pointerevents/#dfn-attributes-and-default-actions
347        // For pointerenter and pointerleave events, the composed [DOM] attribute SHOULD be false;
348        // for all other pointer events in the table above, the attribute SHOULD be true.
349        let composed = !matches!(&*event_type, "pointerenter" | "pointerleave");
350
351        let window = self.global();
352        let window = window.as_window();
353
354        let pointer_event = PointerEvent::new(
355            cx,
356            window,
357            event_type,
358            EventBubbles::from(self.upcast::<Event>().Bubbles()),
359            EventCancelable::from(self.upcast::<Event>().Cancelable()),
360            self.uievent.GetView().as_deref(),
361            self.uievent.Detail(),
362            Point2D::new(self.ScreenX(), self.ScreenY()),
363            Point2D::new(self.ClientX(), self.ClientY()),
364            Point2D::new(self.PageX(), self.PageY()),
365            self.modifiers.get(),
366            button,
367            self.buttons.get(),
368            self.GetRelatedTarget().as_deref(),
369            self.point_in_target.get(),
370            PointerId::Mouse as i32, // Mouse pointer ID is always -1
371            1,                       // width
372            1,                       // height
373            pressure,
374            0.0,      // tangential_pressure
375            0,        // tilt_x
376            0,        // tilt_y
377            0,        // twist
378            PI / 2.0, // altitude_angle (perpendicular to surface)
379            0.0,      // azimuth_angle
380            DOMString::from_static("mouse"),
381            true,   // is_primary (mouse is always primary)
382            vec![], // coalesced_events
383            vec![], // predicted_events
384        );
385
386        pointer_event.upcast::<Event>().set_composed(composed);
387
388        pointer_event
389    }
390
391    /// Create a PointerEvent for hover events (pointerover, pointerenter, pointerout, pointerleave).
392    /// <https://w3c.github.io/pointerevents/#the-primary-pointer>
393    /// For mouse, the pointer ID is always -1, and is_primary is always true.
394    pub(crate) fn to_pointer_hover_event(
395        &self,
396        cx: &mut JSContext,
397        event_type: &str,
398    ) -> DomRoot<crate::dom::pointerevent::PointerEvent> {
399        // Determine bubbles and cancelable based on event type
400        // pointerover/pointerout bubble and are cancelable
401        // pointerenter/pointerleave do not bubble and are not cancelable
402        let (bubbles, cancelable) = match event_type {
403            "pointerover" | "pointerout" => (EventBubbles::Bubbles, EventCancelable::Cancelable),
404            "pointerenter" | "pointerleave" => {
405                (EventBubbles::DoesNotBubble, EventCancelable::NotCancelable)
406            },
407            _ => (EventBubbles::Bubbles, EventCancelable::Cancelable),
408        };
409
410        let window = self.global();
411        let window = window.as_window();
412
413        let pointer_event = PointerEvent::new(
414            cx,
415            window,
416            event_type.into(),
417            bubbles,
418            cancelable,
419            self.uievent.GetView().as_deref(),
420            self.uievent.Detail(),
421            Point2D::new(self.ScreenX(), self.ScreenY()),
422            Point2D::new(self.ClientX(), self.ClientY()),
423            Point2D::new(self.PageX(), self.PageY()),
424            self.modifiers.get(),
425            MouseButton::None,
426            self.buttons.get(),
427            self.GetRelatedTarget().as_deref(),
428            self.point_in_target.get(),
429            PointerId::Mouse as i32, // Mouse pointer ID is always -1
430            1,                       // width
431            1,                       // height
432            0.0,                     // pressure: 0.0 for hover events
433            0.0,                     // tangential_pressure
434            0,                       // tilt_x
435            0,                       // tilt_y
436            0,                       // twist
437            PI / 2.0,                // altitude_angle (perpendicular to surface)
438            0.0,                     // azimuth_angle
439            DOMString::from_static("mouse"),
440            true,   // is_primary (mouse is always primary)
441            vec![], // coalesced_events
442            vec![], // predicted_events
443        );
444
445        // https://w3c.github.io/pointerevents/#dfn-attributes-and-default-actions
446        // For pointerenter and pointerleave events, the composed [DOM] attribute SHOULD be false;
447        // for all other pointer events in the table above, the attribute SHOULD be true.
448        let composed = !matches!(event_type, "pointerenter" | "pointerleave");
449        pointer_event.upcast::<Event>().set_composed(composed);
450
451        // Set trusted to match the source mouse event
452        pointer_event
453            .upcast::<Event>()
454            .set_trusted(self.IsTrusted());
455
456        pointer_event
457    }
458
459    pub(crate) fn button(&self) -> MouseButton {
460        self.button.get()
461    }
462
463    pub(crate) fn buttons(&self) -> MouseButtons {
464        self.buttons.get()
465    }
466}
467
468impl MouseEventMethods<crate::DomTypeHolder> for MouseEvent {
469    /// <https://w3c.github.io/pointerevents/#dom-mouseevent-constructor>
470    fn Constructor(
471        cx: &mut JSContext,
472        window: &Window,
473        proto: Option<HandleObject>,
474        event_type: DOMString,
475        init: &MouseEventBinding::MouseEventInit,
476    ) -> Fallible<DomRoot<MouseEvent>> {
477        let bubbles = EventBubbles::from(init.parent.parent.parent.bubbles);
478        let cancelable = EventCancelable::from(init.parent.parent.parent.cancelable);
479        let scroll_offset = window.scroll_offset();
480        let page_point = Point2D::new(
481            scroll_offset.x as i32 + init.clientX,
482            scroll_offset.y as i32 + init.clientY,
483        );
484        let event = MouseEvent::new_with_proto(
485            cx,
486            window,
487            proto,
488            event_type.into(),
489            bubbles,
490            cancelable,
491            init.parent.parent.view.as_deref(),
492            init.parent.parent.detail,
493            Point2D::new(init.screenX, init.screenY),
494            Point2D::new(init.clientX, init.clientY),
495            page_point,
496            init.parent.modifiers(),
497            init.button.into(),
498            MouseButtons::from_bits_retain(init.buttons),
499            init.relatedTarget.as_deref(),
500            None,
501        );
502        event
503            .upcast::<Event>()
504            .set_composed(init.parent.parent.parent.composed);
505        Ok(event)
506    }
507
508    /// <https://w3c.github.io/pointerevents/#dom-mouseevent-screenx>
509    fn ScreenX(&self) -> i32 {
510        self.screen_point.get().x
511    }
512
513    /// <https://w3c.github.io/pointerevents/#dom-mouseevent-screeny>
514    fn ScreenY(&self) -> i32 {
515        self.screen_point.get().y
516    }
517
518    /// <https://w3c.github.io/pointerevents/#dom-mouseevent-clientx>
519    fn ClientX(&self) -> i32 {
520        self.client_point.get().x
521    }
522
523    /// <https://w3c.github.io/pointerevents/#dom-mouseevent-clienty>
524    fn ClientY(&self) -> i32 {
525        self.client_point.get().y
526    }
527
528    /// <https://drafts.csswg.org/cssom-view/#dom-mouseevent-pagex>
529    fn PageX(&self) -> i32 {
530        // The pageX attribute must follow these steps:
531        // > 1. If the event’s dispatch flag is set, return the horizontal coordinate of the
532        // > position where the event occurred relative to the origin of the initial containing
533        // > block and terminate these steps.
534        if self.upcast::<Event>().dispatching() {
535            return self.page_point.get().x;
536        }
537
538        // > 2. Let offset be the value of the scrollX attribute of the event’s associated
539        // > Window object, if there is one, or zero otherwise.
540        // > 3. Return the sum of offset and the value of the event’s clientX attribute.
541        self.global().as_window().ScrollX() + self.ClientX()
542    }
543
544    /// <https://drafts.csswg.org/cssom-view/#dom-mouseevent-pagey>
545    fn PageY(&self) -> i32 {
546        // The pageY attribute must follow these steps:
547        // > 1. If the event’s dispatch flag is set, return the vertical coordinate of the
548        // > position where the event occurred relative to the origin of the initial
549        // > containing block and terminate these steps.
550        if self.upcast::<Event>().dispatching() {
551            return self.page_point.get().y;
552        }
553
554        // > 2. Let offset be the value of the scrollY attribute of the event’s associated
555        // > Window object, if there is one, or zero otherwise.
556        // > 3. Return the sum of offset and the value of the event’s clientY attribute.
557        self.global().as_window().ScrollY() + self.ClientY()
558    }
559
560    /// <https://drafts.csswg.org/cssom-view/#dom-mouseevent-x>
561    fn X(&self) -> i32 {
562        self.ClientX()
563    }
564
565    /// <https://drafts.csswg.org/cssom-view/#dom-mouseevent-y>
566    fn Y(&self) -> i32 {
567        self.ClientY()
568    }
569
570    /// <https://drafts.csswg.org/cssom-view/#dom-mouseevent-offsetx>
571    fn OffsetX(&self) -> i32 {
572        // > The offsetX attribute must follow these steps:
573        // > 1. If the event’s dispatch flag is set, return the x-coordinate of the position
574        // >    where the event occurred relative to the origin of the padding edge of the
575        // >    target node, ignoring the transforms that apply to the element and its
576        // >    ancestors, and terminate these steps.
577        let event = self.upcast::<Event>();
578        if event.dispatching() {
579            let Some(target) = event.GetTarget() else {
580                return 0;
581            };
582            let Some(node) = target.downcast::<Node>() else {
583                return 0;
584            };
585            return self.ClientX() - node.client_rect().origin.x;
586        }
587
588        // > 2. Return the value of the event’s pageX attribute.
589        self.PageX()
590    }
591
592    /// <https://drafts.csswg.org/cssom-view/#dom-mouseevent-offsety>
593    fn OffsetY(&self) -> i32 {
594        // > The offsetY attribute must follow these steps:
595        // > 1. If the event’s dispatch flag is set, return the y-coordinate of the
596        // >    position where the event occurred relative to the origin of the padding edge of
597        // >    the target node, ignoring the transforms that apply to the element and its
598        // >    ancestors, and terminate these steps.
599        let event = self.upcast::<Event>();
600        if event.dispatching() {
601            let Some(target) = event.GetTarget() else {
602                return 0;
603            };
604            let Some(node) = target.downcast::<Node>() else {
605                return 0;
606            };
607            return self.ClientY() - node.client_rect().origin.y;
608        }
609
610        // 2. Return the value of the event’s pageY attribute.
611        self.PageY()
612    }
613
614    /// <https://w3c.github.io/pointerevents/#dom-mouseevent-ctrlkey>
615    fn CtrlKey(&self) -> bool {
616        self.modifiers.get().contains(Modifiers::CONTROL)
617    }
618
619    /// <https://w3c.github.io/pointerevents/#dom-mouseevent-shiftkey>
620    fn ShiftKey(&self) -> bool {
621        self.modifiers.get().contains(Modifiers::SHIFT)
622    }
623
624    /// <https://w3c.github.io/pointerevents/#dom-mouseevent-altkey>
625    fn AltKey(&self) -> bool {
626        self.modifiers.get().contains(Modifiers::ALT)
627    }
628
629    /// <https://w3c.github.io/pointerevents/#dom-mouseevent-metakey>
630    fn MetaKey(&self) -> bool {
631        self.modifiers.get().contains(Modifiers::META)
632    }
633
634    /// <https://w3c.github.io/pointerevents/#dom-mouseevent-button>
635    fn Button(&self) -> i16 {
636        self.button.get().into()
637    }
638
639    /// <https://w3c.github.io/pointerevents/#dom-mouseevent-buttons>
640    fn Buttons(&self) -> u16 {
641        self.buttons.get().bits()
642    }
643
644    /// <https://w3c.github.io/pointerevents/#dom-mouseevent-relatedtarget>
645    fn GetRelatedTarget(&self) -> Option<DomRoot<EventTarget>> {
646        self.upcast::<Event>().related_target()
647    }
648
649    /// <https://w3c.github.io/pointerevents/#dom-mouseevent-initmouseevent>
650    fn InitMouseEvent(
651        &self,
652        type_arg: DOMString,
653        can_bubble_arg: bool,
654        cancelable_arg: bool,
655        view_arg: Option<&Window>,
656        detail_arg: i32,
657        screen_x_arg: i32,
658        screen_y_arg: i32,
659        client_x_arg: i32,
660        client_y_arg: i32,
661        ctrl_key_arg: bool,
662        alt_key_arg: bool,
663        shift_key_arg: bool,
664        meta_key_arg: bool,
665        button_arg: i16,
666        related_target_arg: Option<&EventTarget>,
667    ) {
668        if self.upcast::<Event>().dispatching() {
669            return;
670        }
671
672        self.upcast::<UIEvent>().InitUIEvent(
673            type_arg,
674            can_bubble_arg,
675            cancelable_arg,
676            view_arg,
677            detail_arg,
678        );
679        self.screen_point
680            .set(Point2D::new(screen_x_arg, screen_y_arg));
681        self.client_point
682            .set(Point2D::new(client_x_arg, client_y_arg));
683
684        let global = self.global();
685        let scroll_offset = global.as_window().scroll_offset();
686        self.page_point.set(Point2D::new(
687            scroll_offset.x as i32 + client_x_arg,
688            scroll_offset.y as i32 + client_y_arg,
689        ));
690
691        let mut modifiers = Modifiers::empty();
692        if ctrl_key_arg {
693            modifiers.insert(Modifiers::CONTROL);
694        }
695        if alt_key_arg {
696            modifiers.insert(Modifiers::ALT);
697        }
698        if shift_key_arg {
699            modifiers.insert(Modifiers::SHIFT);
700        }
701        if meta_key_arg {
702            modifiers.insert(Modifiers::META);
703        }
704        self.modifiers.set(modifiers);
705
706        self.button.set(button_arg.into());
707        self.upcast::<Event>()
708            .set_related_target(related_target_arg);
709
710        // Keep UIEvent.which in sync for legacy init path too.
711        let w = if button_arg >= 0 {
712            (button_arg as u32) + 1
713        } else {
714            0
715        };
716        self.uievent.set_which(w);
717    }
718
719    /// <https://dom.spec.whatwg.org/#dom-event-istrusted>
720    fn IsTrusted(&self) -> bool {
721        self.uievent.IsTrusted()
722    }
723
724    /// <https://w3c.github.io/pointerevents/#dfn-getmodifierstate-keyarg>
725    fn GetModifierState(&self, key_arg: DOMString) -> bool {
726        self.modifiers
727            .get()
728            .contains(match_domstring_ascii!(key_arg,
729                "Alt" => Modifiers::ALT,
730                "AltGraph" => Modifiers::ALT_GRAPH,
731                "CapsLock" => Modifiers::CAPS_LOCK,
732                "Control" => Modifiers::CONTROL,
733                "Fn" => Modifiers::FN,
734                "FnLock" => Modifiers::FN_LOCK,
735                "Meta" => Modifiers::META,
736                "NumLock" => Modifiers::NUM_LOCK,
737                "ScrollLock" => Modifiers::SCROLL_LOCK,
738                "Shift" => Modifiers::SHIFT,
739                "Symbol" => Modifiers::SYMBOL,
740                "SymbolLock" => Modifiers::SYMBOL_LOCK,
741                    _ => { return false; },
742            ))
743    }
744}