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 pub(crate) fn new_simple(
184 window: &Window,
185 event_name: FireMouseEventType,
186 can_bubble: EventBubbles,
187 cancelable: EventCancelable,
188 hit_test_result: &HitTestResult,
189 input_event: &ConstellationInputEvent,
190 can_gc: CanGc,
191 ) -> DomRoot<Self> {
192 Self::new(
193 window,
194 DOMString::from(event_name.as_str()),
195 can_bubble,
196 cancelable,
197 Some(window),
198 0i32,
199 hit_test_result.point_in_frame.to_i32(),
200 hit_test_result.point_in_frame.to_i32(),
201 hit_test_result
202 .point_relative_to_initial_containing_block
203 .to_i32(),
204 input_event.active_keyboard_modifiers,
205 0i16,
206 input_event.pressed_mouse_buttons,
207 None,
208 None,
209 can_gc,
210 )
211 }
212
213 #[allow(clippy::too_many_arguments)]
215 pub(crate) fn initialize_mouse_event(
216 &self,
217 type_: DOMString,
218 can_bubble: EventBubbles,
219 cancelable: EventCancelable,
220 view: Option<&Window>,
221 detail: i32,
222 screen_point: Point2D<i32, CSSPixel>,
223 client_point: Point2D<i32, CSSPixel>,
224 page_point: Point2D<i32, CSSPixel>,
225 modifiers: Modifiers,
226 button: i16,
227 buttons: u16,
228 related_target: Option<&EventTarget>,
229 point_in_target: Option<Point2D<f32, CSSPixel>>,
230 ) {
231 self.uievent.initialize_ui_event(
232 type_,
233 view.map(|window| window.upcast::<EventTarget>()),
234 can_bubble,
235 cancelable,
236 );
237
238 self.uievent.set_detail(detail);
239 self.screen_point.set(screen_point);
240 self.client_point.set(client_point);
241 self.page_point.set(page_point);
242 self.modifiers.set(modifiers);
243 self.button.set(button);
244 self.buttons.set(buttons);
245 self.upcast::<Event>().set_related_target(related_target);
246 self.point_in_target.set(point_in_target);
247 let w = if button >= 0 { (button as u32) + 1 } else { 0 };
249 self.uievent.set_which(w);
250 }
251
252 pub(crate) fn point_in_target(&self) -> Option<Point2D<f32, CSSPixel>> {
253 self.point_in_target.get()
254 }
255
256 pub(crate) fn for_platform_mouse_event(
259 event_type_string: &'static str,
260 event: embedder_traits::MouseButtonEvent,
261 pressed_mouse_buttons: u16,
262 window: &Window,
263 hit_test_result: &HitTestResult,
264 modifiers: Modifiers,
265 can_gc: CanGc,
266 ) -> DomRoot<Self> {
267 let client_point = hit_test_result.point_in_frame.to_i32();
268 let page_point = hit_test_result
269 .point_relative_to_initial_containing_block
270 .to_i32();
271
272 let click_count = 1;
273 let mouse_event = MouseEvent::new(
274 window,
275 event_type_string.into(),
276 EventBubbles::Bubbles,
277 EventCancelable::Cancelable,
278 Some(window),
279 click_count,
280 client_point, client_point,
282 page_point,
283 modifiers,
284 event.button.into(),
285 pressed_mouse_buttons,
286 None,
287 Some(hit_test_result.point_in_node),
288 can_gc,
289 );
290
291 mouse_event.upcast::<Event>().set_trusted(true);
292 mouse_event.upcast::<Event>().set_composed(true);
293
294 mouse_event
295 }
296}
297
298impl MouseEventMethods<crate::DomTypeHolder> for MouseEvent {
299 fn Constructor(
301 window: &Window,
302 proto: Option<HandleObject>,
303 can_gc: CanGc,
304 type_: DOMString,
305 init: &MouseEventBinding::MouseEventInit,
306 ) -> Fallible<DomRoot<MouseEvent>> {
307 let bubbles = EventBubbles::from(init.parent.parent.parent.bubbles);
308 let cancelable = EventCancelable::from(init.parent.parent.parent.cancelable);
309 let scroll_offset = window.scroll_offset();
310 let page_point = Point2D::new(
311 scroll_offset.x as i32 + init.clientX,
312 scroll_offset.y as i32 + init.clientY,
313 );
314 let event = MouseEvent::new_with_proto(
315 window,
316 proto,
317 type_,
318 bubbles,
319 cancelable,
320 init.parent.parent.view.as_deref(),
321 init.parent.parent.detail,
322 Point2D::new(init.screenX, init.screenY),
323 Point2D::new(init.clientX, init.clientY),
324 page_point,
325 init.parent.modifiers(),
326 init.button,
327 init.buttons,
328 init.relatedTarget.as_deref(),
329 None,
330 can_gc,
331 );
332 event
333 .upcast::<Event>()
334 .set_composed(init.parent.parent.parent.composed);
335 Ok(event)
336 }
337
338 fn ScreenX(&self) -> i32 {
340 self.screen_point.get().x
341 }
342
343 fn ScreenY(&self) -> i32 {
345 self.screen_point.get().y
346 }
347
348 fn ClientX(&self) -> i32 {
350 self.client_point.get().x
351 }
352
353 fn ClientY(&self) -> i32 {
355 self.client_point.get().y
356 }
357
358 fn PageX(&self) -> i32 {
360 if self.upcast::<Event>().dispatching() {
365 return self.page_point.get().x;
366 }
367
368 self.global().as_window().ScrollX() + self.ClientX()
372 }
373
374 fn PageY(&self) -> i32 {
376 if self.upcast::<Event>().dispatching() {
381 return self.page_point.get().y;
382 }
383
384 self.global().as_window().ScrollY() + self.ClientY()
388 }
389
390 fn X(&self) -> i32 {
392 self.ClientX()
393 }
394
395 fn Y(&self) -> i32 {
397 self.ClientY()
398 }
399
400 fn OffsetX(&self) -> i32 {
402 let event = self.upcast::<Event>();
408 if event.dispatching() {
409 let Some(target) = event.GetTarget() else {
410 return 0;
411 };
412 let Some(node) = target.downcast::<Node>() else {
413 return 0;
414 };
415 return self.ClientX() - node.client_rect().origin.x;
416 }
417
418 self.PageX()
420 }
421
422 fn OffsetY(&self) -> i32 {
424 let event = self.upcast::<Event>();
430 if event.dispatching() {
431 let Some(target) = event.GetTarget() else {
432 return 0;
433 };
434 let Some(node) = target.downcast::<Node>() else {
435 return 0;
436 };
437 return self.ClientY() - node.client_rect().origin.y;
438 }
439
440 self.PageY()
442 }
443
444 fn CtrlKey(&self) -> bool {
446 self.modifiers.get().contains(Modifiers::CONTROL)
447 }
448
449 fn ShiftKey(&self) -> bool {
451 self.modifiers.get().contains(Modifiers::SHIFT)
452 }
453
454 fn AltKey(&self) -> bool {
456 self.modifiers.get().contains(Modifiers::ALT)
457 }
458
459 fn MetaKey(&self) -> bool {
461 self.modifiers.get().contains(Modifiers::META)
462 }
463
464 fn Button(&self) -> i16 {
466 self.button.get()
467 }
468
469 fn Buttons(&self) -> u16 {
471 self.buttons.get()
472 }
473
474 fn GetRelatedTarget(&self) -> Option<DomRoot<EventTarget>> {
476 self.upcast::<Event>().related_target()
477 }
478
479 fn InitMouseEvent(
481 &self,
482 type_arg: DOMString,
483 can_bubble_arg: bool,
484 cancelable_arg: bool,
485 view_arg: Option<&Window>,
486 detail_arg: i32,
487 screen_x_arg: i32,
488 screen_y_arg: i32,
489 client_x_arg: i32,
490 client_y_arg: i32,
491 ctrl_key_arg: bool,
492 alt_key_arg: bool,
493 shift_key_arg: bool,
494 meta_key_arg: bool,
495 button_arg: i16,
496 related_target_arg: Option<&EventTarget>,
497 ) {
498 if self.upcast::<Event>().dispatching() {
499 return;
500 }
501
502 self.upcast::<UIEvent>().InitUIEvent(
503 type_arg,
504 can_bubble_arg,
505 cancelable_arg,
506 view_arg,
507 detail_arg,
508 );
509 self.screen_point
510 .set(Point2D::new(screen_x_arg, screen_y_arg));
511 self.client_point
512 .set(Point2D::new(client_x_arg, client_y_arg));
513
514 let global = self.global();
515 let scroll_offset = global.as_window().scroll_offset();
516 self.page_point.set(Point2D::new(
517 scroll_offset.x as i32 + client_x_arg,
518 scroll_offset.y as i32 + client_y_arg,
519 ));
520
521 let mut modifiers = Modifiers::empty();
522 if ctrl_key_arg {
523 modifiers.insert(Modifiers::CONTROL);
524 }
525 if alt_key_arg {
526 modifiers.insert(Modifiers::ALT);
527 }
528 if shift_key_arg {
529 modifiers.insert(Modifiers::SHIFT);
530 }
531 if meta_key_arg {
532 modifiers.insert(Modifiers::META);
533 }
534 self.modifiers.set(modifiers);
535
536 self.button.set(button_arg);
537 self.upcast::<Event>()
538 .set_related_target(related_target_arg);
539
540 let w = if button_arg >= 0 {
542 (button_arg as u32) + 1
543 } else {
544 0
545 };
546 self.uievent.set_which(w);
547 }
548
549 fn IsTrusted(&self) -> bool {
551 self.uievent.IsTrusted()
552 }
553
554 fn GetModifierState(&self, key_arg: DOMString) -> bool {
556 self.modifiers
557 .get()
558 .contains(match_domstring_ascii!(key_arg,
559 "Alt" => Modifiers::ALT,
560 "AltGraph" => Modifiers::ALT_GRAPH,
561 "CapsLock" => Modifiers::CAPS_LOCK,
562 "Control" => Modifiers::CONTROL,
563 "Fn" => Modifiers::FN,
564 "FnLock" => Modifiers::FN_LOCK,
565 "Meta" => Modifiers::META,
566 "NumLock" => Modifiers::NUM_LOCK,
567 "ScrollLock" => Modifiers::SCROLL_LOCK,
568 "Shift" => Modifiers::SHIFT,
569 "Symbol" => Modifiers::SYMBOL,
570 "SymbolLock" => Modifiers::SYMBOL_LOCK,
571 _ => { return false; },
572 ))
573 }
574}