1use std::cell::Cell;
6use std::default::Default;
7
8use dom_struct::dom_struct;
9use euclid::Point2D;
10use js::rust::HandleObject;
11use keyboard_types::Modifiers;
12use script_bindings::codegen::GenericBindings::WindowBinding::WindowMethods;
13use script_bindings::match_domstring_ascii;
14use script_traits::ConstellationInputEvent;
15use style_traits::CSSPixel;
16
17use crate::dom::bindings::codegen::Bindings::EventBinding::Event_Binding::EventMethods;
18use crate::dom::bindings::codegen::Bindings::MouseEventBinding;
19use crate::dom::bindings::codegen::Bindings::MouseEventBinding::MouseEventMethods;
20use crate::dom::bindings::codegen::Bindings::UIEventBinding::UIEventMethods;
21use crate::dom::bindings::error::Fallible;
22use crate::dom::bindings::inheritance::Castable;
23use crate::dom::bindings::reflector::{DomGlobal, reflect_dom_object_with_proto};
24use crate::dom::bindings::root::DomRoot;
25use crate::dom::bindings::str::DOMString;
26use crate::dom::document::FireMouseEventType;
27use crate::dom::event::{Event, EventBubbles, EventCancelable};
28use crate::dom::eventtarget::EventTarget;
29use crate::dom::inputevent::HitTestResult;
30use crate::dom::node::Node;
31use crate::dom::uievent::UIEvent;
32use crate::dom::window::Window;
33use crate::script_runtime::CanGc;
34
35#[dom_struct]
37pub(crate) struct MouseEvent {
38 uievent: UIEvent,
39
40 #[no_trace]
47 screen_point: Cell<Point2D<i32, CSSPixel>>,
48
49 #[no_trace]
56 client_point: Cell<Point2D<i32, CSSPixel>>,
57
58 #[no_trace]
65 page_point: Cell<Point2D<i32, CSSPixel>>,
66
67 #[no_trace]
69 modifiers: Cell<Modifiers>,
70
71 button: Cell<i16>,
73
74 buttons: Cell<u16>,
76
77 #[no_trace]
78 point_in_target: Cell<Option<Point2D<f32, CSSPixel>>>,
79}
80
81impl MouseEvent {
82 pub(crate) fn new_inherited() -> MouseEvent {
83 MouseEvent {
84 uievent: UIEvent::new_inherited(),
85 screen_point: Cell::new(Default::default()),
86 client_point: Cell::new(Default::default()),
87 page_point: Cell::new(Default::default()),
88 modifiers: Cell::new(Modifiers::empty()),
89 button: Cell::new(0),
90 buttons: Cell::new(0),
91 point_in_target: Cell::new(None),
92 }
93 }
94
95 pub(crate) fn new_uninitialized(window: &Window, can_gc: CanGc) -> DomRoot<MouseEvent> {
96 Self::new_uninitialized_with_proto(window, None, can_gc)
97 }
98
99 fn new_uninitialized_with_proto(
100 window: &Window,
101 proto: Option<HandleObject>,
102 can_gc: CanGc,
103 ) -> DomRoot<MouseEvent> {
104 reflect_dom_object_with_proto(Box::new(MouseEvent::new_inherited()), window, proto, can_gc)
105 }
106
107 #[allow(clippy::too_many_arguments)]
108 pub(crate) fn new(
109 window: &Window,
110 type_: DOMString,
111 can_bubble: EventBubbles,
112 cancelable: EventCancelable,
113 view: Option<&Window>,
114 detail: i32,
115 screen_point: Point2D<i32, CSSPixel>,
116 client_point: Point2D<i32, CSSPixel>,
117 page_point: Point2D<i32, CSSPixel>,
118 modifiers: Modifiers,
119 button: i16,
120 buttons: u16,
121 related_target: Option<&EventTarget>,
122 point_in_target: Option<Point2D<f32, CSSPixel>>,
123 can_gc: CanGc,
124 ) -> DomRoot<MouseEvent> {
125 Self::new_with_proto(
126 window,
127 None,
128 type_,
129 can_bubble,
130 cancelable,
131 view,
132 detail,
133 screen_point,
134 client_point,
135 page_point,
136 modifiers,
137 button,
138 buttons,
139 related_target,
140 point_in_target,
141 can_gc,
142 )
143 }
144
145 #[allow(clippy::too_many_arguments)]
146 fn new_with_proto(
147 window: &Window,
148 proto: Option<HandleObject>,
149 type_: DOMString,
150 can_bubble: EventBubbles,
151 cancelable: EventCancelable,
152 view: Option<&Window>,
153 detail: i32,
154 screen_point: Point2D<i32, CSSPixel>,
155 client_point: Point2D<i32, CSSPixel>,
156 page_point: Point2D<i32, CSSPixel>,
157 modifiers: Modifiers,
158 button: i16,
159 buttons: u16,
160 related_target: Option<&EventTarget>,
161 point_in_target: Option<Point2D<f32, CSSPixel>>,
162 can_gc: CanGc,
163 ) -> DomRoot<MouseEvent> {
164 let ev = MouseEvent::new_uninitialized_with_proto(window, proto, can_gc);
165 ev.initialize_mouse_event(
166 type_,
167 can_bubble,
168 cancelable,
169 view,
170 detail,
171 screen_point,
172 client_point,
173 page_point,
174 modifiers,
175 button,
176 buttons,
177 related_target,
178 point_in_target,
179 );
180 ev
181 }
182
183 #[expect(clippy::too_many_arguments)]
185 pub(crate) fn initialize_mouse_event(
186 &self,
187 type_: DOMString,
188 can_bubble: EventBubbles,
189 cancelable: EventCancelable,
190 view: Option<&Window>,
191 detail: i32,
192 screen_point: Point2D<i32, CSSPixel>,
193 client_point: Point2D<i32, CSSPixel>,
194 page_point: Point2D<i32, CSSPixel>,
195 modifiers: Modifiers,
196 button: i16,
197 buttons: u16,
198 related_target: Option<&EventTarget>,
199 point_in_target: Option<Point2D<f32, CSSPixel>>,
200 ) {
201 self.uievent.initialize_ui_event(
202 type_,
203 view.map(|window| window.upcast::<EventTarget>()),
204 can_bubble,
205 cancelable,
206 );
207
208 self.uievent.set_detail(detail);
209 self.screen_point.set(screen_point);
210 self.client_point.set(client_point);
211 self.page_point.set(page_point);
212 self.modifiers.set(modifiers);
213 self.button.set(button);
214 self.buttons.set(buttons);
215 self.upcast::<Event>().set_related_target(related_target);
216 self.point_in_target.set(point_in_target);
217 let w = if button >= 0 { (button as u32) + 1 } else { 0 };
219 self.uievent.set_which(w);
220 }
221
222 pub(crate) fn new_for_platform_motion_event(
223 window: &Window,
224 event_name: FireMouseEventType,
225 hit_test_result: &HitTestResult,
226 input_event: &ConstellationInputEvent,
227 can_gc: CanGc,
228 ) -> DomRoot<Self> {
229 let (bubbles, cancelable, composed) = match event_name {
232 FireMouseEventType::Move | FireMouseEventType::Over | FireMouseEventType::Out => {
233 (EventBubbles::Bubbles, EventCancelable::Cancelable, true)
234 },
235 FireMouseEventType::Enter | FireMouseEventType::Leave => (
236 EventBubbles::DoesNotBubble,
237 EventCancelable::NotCancelable,
238 false,
239 ),
240 };
241
242 let mouse_event = Self::new(
243 window,
244 DOMString::from(event_name.as_str()),
245 bubbles,
246 cancelable,
247 Some(window),
248 0i32,
249 hit_test_result.point_in_frame.to_i32(),
250 hit_test_result.point_in_frame.to_i32(),
251 hit_test_result
252 .point_relative_to_initial_containing_block
253 .to_i32(),
254 input_event.active_keyboard_modifiers,
255 0i16,
256 input_event.pressed_mouse_buttons,
257 None,
258 None,
259 can_gc,
260 );
261
262 let event = mouse_event.upcast::<Event>();
263 event.set_composed(composed);
264 event.set_trusted(true);
265
266 mouse_event
267 }
268
269 #[expect(clippy::too_many_arguments)]
272 pub(crate) fn for_platform_button_event(
273 event_type_string: &'static str,
274 event: embedder_traits::MouseButtonEvent,
275 pressed_mouse_buttons: u16,
276 window: &Window,
277 hit_test_result: &HitTestResult,
278 modifiers: Modifiers,
279 click_count: usize,
280 can_gc: CanGc,
281 ) -> DomRoot<Self> {
282 let client_point = hit_test_result.point_in_frame.to_i32();
283 let page_point = hit_test_result
284 .point_relative_to_initial_containing_block
285 .to_i32();
286
287 let mouse_event = Self::new(
288 window,
289 event_type_string.into(),
290 EventBubbles::Bubbles,
291 EventCancelable::Cancelable,
292 Some(window),
293 click_count as i32,
294 client_point, client_point,
296 page_point,
297 modifiers,
298 event.button.into(),
299 pressed_mouse_buttons,
300 None,
301 Some(hit_test_result.point_in_node),
302 can_gc,
303 );
304
305 mouse_event.upcast::<Event>().set_trusted(true);
306 mouse_event.upcast::<Event>().set_composed(true);
307
308 mouse_event
309 }
310
311 pub(crate) fn point_in_viewport(&self) -> Option<Point2D<f32, CSSPixel>> {
312 Some(self.client_point.get().to_f32())
313 }
314}
315
316impl MouseEventMethods<crate::DomTypeHolder> for MouseEvent {
317 fn Constructor(
319 window: &Window,
320 proto: Option<HandleObject>,
321 can_gc: CanGc,
322 type_: DOMString,
323 init: &MouseEventBinding::MouseEventInit,
324 ) -> Fallible<DomRoot<MouseEvent>> {
325 let bubbles = EventBubbles::from(init.parent.parent.parent.bubbles);
326 let cancelable = EventCancelable::from(init.parent.parent.parent.cancelable);
327 let scroll_offset = window.scroll_offset();
328 let page_point = Point2D::new(
329 scroll_offset.x as i32 + init.clientX,
330 scroll_offset.y as i32 + init.clientY,
331 );
332 let event = MouseEvent::new_with_proto(
333 window,
334 proto,
335 type_,
336 bubbles,
337 cancelable,
338 init.parent.parent.view.as_deref(),
339 init.parent.parent.detail,
340 Point2D::new(init.screenX, init.screenY),
341 Point2D::new(init.clientX, init.clientY),
342 page_point,
343 init.parent.modifiers(),
344 init.button,
345 init.buttons,
346 init.relatedTarget.as_deref(),
347 None,
348 can_gc,
349 );
350 event
351 .upcast::<Event>()
352 .set_composed(init.parent.parent.parent.composed);
353 Ok(event)
354 }
355
356 fn ScreenX(&self) -> i32 {
358 self.screen_point.get().x
359 }
360
361 fn ScreenY(&self) -> i32 {
363 self.screen_point.get().y
364 }
365
366 fn ClientX(&self) -> i32 {
368 self.client_point.get().x
369 }
370
371 fn ClientY(&self) -> i32 {
373 self.client_point.get().y
374 }
375
376 fn PageX(&self) -> i32 {
378 if self.upcast::<Event>().dispatching() {
383 return self.page_point.get().x;
384 }
385
386 self.global().as_window().ScrollX() + self.ClientX()
390 }
391
392 fn PageY(&self) -> i32 {
394 if self.upcast::<Event>().dispatching() {
399 return self.page_point.get().y;
400 }
401
402 self.global().as_window().ScrollY() + self.ClientY()
406 }
407
408 fn X(&self) -> i32 {
410 self.ClientX()
411 }
412
413 fn Y(&self) -> i32 {
415 self.ClientY()
416 }
417
418 fn OffsetX(&self) -> i32 {
420 let event = self.upcast::<Event>();
426 if event.dispatching() {
427 let Some(target) = event.GetTarget() else {
428 return 0;
429 };
430 let Some(node) = target.downcast::<Node>() else {
431 return 0;
432 };
433 return self.ClientX() - node.client_rect().origin.x;
434 }
435
436 self.PageX()
438 }
439
440 fn OffsetY(&self) -> i32 {
442 let event = self.upcast::<Event>();
448 if event.dispatching() {
449 let Some(target) = event.GetTarget() else {
450 return 0;
451 };
452 let Some(node) = target.downcast::<Node>() else {
453 return 0;
454 };
455 return self.ClientY() - node.client_rect().origin.y;
456 }
457
458 self.PageY()
460 }
461
462 fn CtrlKey(&self) -> bool {
464 self.modifiers.get().contains(Modifiers::CONTROL)
465 }
466
467 fn ShiftKey(&self) -> bool {
469 self.modifiers.get().contains(Modifiers::SHIFT)
470 }
471
472 fn AltKey(&self) -> bool {
474 self.modifiers.get().contains(Modifiers::ALT)
475 }
476
477 fn MetaKey(&self) -> bool {
479 self.modifiers.get().contains(Modifiers::META)
480 }
481
482 fn Button(&self) -> i16 {
484 self.button.get()
485 }
486
487 fn Buttons(&self) -> u16 {
489 self.buttons.get()
490 }
491
492 fn GetRelatedTarget(&self) -> Option<DomRoot<EventTarget>> {
494 self.upcast::<Event>().related_target()
495 }
496
497 fn InitMouseEvent(
499 &self,
500 type_arg: DOMString,
501 can_bubble_arg: bool,
502 cancelable_arg: bool,
503 view_arg: Option<&Window>,
504 detail_arg: i32,
505 screen_x_arg: i32,
506 screen_y_arg: i32,
507 client_x_arg: i32,
508 client_y_arg: i32,
509 ctrl_key_arg: bool,
510 alt_key_arg: bool,
511 shift_key_arg: bool,
512 meta_key_arg: bool,
513 button_arg: i16,
514 related_target_arg: Option<&EventTarget>,
515 ) {
516 if self.upcast::<Event>().dispatching() {
517 return;
518 }
519
520 self.upcast::<UIEvent>().InitUIEvent(
521 type_arg,
522 can_bubble_arg,
523 cancelable_arg,
524 view_arg,
525 detail_arg,
526 );
527 self.screen_point
528 .set(Point2D::new(screen_x_arg, screen_y_arg));
529 self.client_point
530 .set(Point2D::new(client_x_arg, client_y_arg));
531
532 let global = self.global();
533 let scroll_offset = global.as_window().scroll_offset();
534 self.page_point.set(Point2D::new(
535 scroll_offset.x as i32 + client_x_arg,
536 scroll_offset.y as i32 + client_y_arg,
537 ));
538
539 let mut modifiers = Modifiers::empty();
540 if ctrl_key_arg {
541 modifiers.insert(Modifiers::CONTROL);
542 }
543 if alt_key_arg {
544 modifiers.insert(Modifiers::ALT);
545 }
546 if shift_key_arg {
547 modifiers.insert(Modifiers::SHIFT);
548 }
549 if meta_key_arg {
550 modifiers.insert(Modifiers::META);
551 }
552 self.modifiers.set(modifiers);
553
554 self.button.set(button_arg);
555 self.upcast::<Event>()
556 .set_related_target(related_target_arg);
557
558 let w = if button_arg >= 0 {
560 (button_arg as u32) + 1
561 } else {
562 0
563 };
564 self.uievent.set_which(w);
565 }
566
567 fn IsTrusted(&self) -> bool {
569 self.uievent.IsTrusted()
570 }
571
572 fn GetModifierState(&self, key_arg: DOMString) -> bool {
574 self.modifiers
575 .get()
576 .contains(match_domstring_ascii!(key_arg,
577 "Alt" => Modifiers::ALT,
578 "AltGraph" => Modifiers::ALT_GRAPH,
579 "CapsLock" => Modifiers::CAPS_LOCK,
580 "Control" => Modifiers::CONTROL,
581 "Fn" => Modifiers::FN,
582 "FnLock" => Modifiers::FN_LOCK,
583 "Meta" => Modifiers::META,
584 "NumLock" => Modifiers::NUM_LOCK,
585 "ScrollLock" => Modifiers::SCROLL_LOCK,
586 "Shift" => Modifiers::SHIFT,
587 "Symbol" => Modifiers::SYMBOL,
588 "SymbolLock" => Modifiers::SYMBOL_LOCK,
589 _ => { return false; },
590 ))
591 }
592}