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_traits::ConstellationInputEvent;
14use servo_config::pref;
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, MutNullableDom};
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 related_target: MutNullableDom<EventTarget>,
79 #[no_trace]
80 point_in_target: Cell<Option<Point2D<f32, CSSPixel>>>,
81}
82
83impl MouseEvent {
84 pub(crate) fn new_inherited() -> MouseEvent {
85 MouseEvent {
86 uievent: UIEvent::new_inherited(),
87 screen_point: Cell::new(Default::default()),
88 client_point: Cell::new(Default::default()),
89 page_point: Cell::new(Default::default()),
90 modifiers: Cell::new(Modifiers::empty()),
91 button: Cell::new(0),
92 buttons: Cell::new(0),
93 related_target: Default::default(),
94 point_in_target: Cell::new(None),
95 }
96 }
97
98 pub(crate) fn new_uninitialized(window: &Window, can_gc: CanGc) -> DomRoot<MouseEvent> {
99 Self::new_uninitialized_with_proto(window, None, can_gc)
100 }
101
102 fn new_uninitialized_with_proto(
103 window: &Window,
104 proto: Option<HandleObject>,
105 can_gc: CanGc,
106 ) -> DomRoot<MouseEvent> {
107 reflect_dom_object_with_proto(Box::new(MouseEvent::new_inherited()), window, proto, can_gc)
108 }
109
110 #[allow(clippy::too_many_arguments)]
111 pub(crate) fn new(
112 window: &Window,
113 type_: DOMString,
114 can_bubble: EventBubbles,
115 cancelable: EventCancelable,
116 view: Option<&Window>,
117 detail: i32,
118 screen_point: Point2D<i32, CSSPixel>,
119 client_point: Point2D<i32, CSSPixel>,
120 page_point: Point2D<i32, CSSPixel>,
121 modifiers: Modifiers,
122 button: i16,
123 buttons: u16,
124 related_target: Option<&EventTarget>,
125 point_in_target: Option<Point2D<f32, CSSPixel>>,
126 can_gc: CanGc,
127 ) -> DomRoot<MouseEvent> {
128 Self::new_with_proto(
129 window,
130 None,
131 type_,
132 can_bubble,
133 cancelable,
134 view,
135 detail,
136 screen_point,
137 client_point,
138 page_point,
139 modifiers,
140 button,
141 buttons,
142 related_target,
143 point_in_target,
144 can_gc,
145 )
146 }
147
148 #[allow(clippy::too_many_arguments)]
149 fn new_with_proto(
150 window: &Window,
151 proto: Option<HandleObject>,
152 type_: DOMString,
153 can_bubble: EventBubbles,
154 cancelable: EventCancelable,
155 view: Option<&Window>,
156 detail: i32,
157 screen_point: Point2D<i32, CSSPixel>,
158 client_point: Point2D<i32, CSSPixel>,
159 page_point: Point2D<i32, CSSPixel>,
160 modifiers: Modifiers,
161 button: i16,
162 buttons: u16,
163 related_target: Option<&EventTarget>,
164 point_in_target: Option<Point2D<f32, CSSPixel>>,
165 can_gc: CanGc,
166 ) -> DomRoot<MouseEvent> {
167 let ev = MouseEvent::new_uninitialized_with_proto(window, proto, can_gc);
168 ev.initialize_mouse_event(
169 type_,
170 can_bubble,
171 cancelable,
172 view,
173 detail,
174 screen_point,
175 client_point,
176 page_point,
177 modifiers,
178 button,
179 buttons,
180 related_target,
181 point_in_target,
182 );
183 ev
184 }
185
186 pub(crate) fn new_simple(
187 window: &Window,
188 event_name: FireMouseEventType,
189 can_bubble: EventBubbles,
190 cancelable: EventCancelable,
191 hit_test_result: &HitTestResult,
192 input_event: &ConstellationInputEvent,
193 can_gc: CanGc,
194 ) -> DomRoot<Self> {
195 Self::new(
196 window,
197 DOMString::from(event_name.as_str()),
198 can_bubble,
199 cancelable,
200 Some(window),
201 0i32,
202 hit_test_result.point_in_frame.to_i32(),
203 hit_test_result.point_in_frame.to_i32(),
204 hit_test_result
205 .point_relative_to_initial_containing_block
206 .to_i32(),
207 input_event.active_keyboard_modifiers,
208 0i16,
209 input_event.pressed_mouse_buttons,
210 None,
211 None,
212 can_gc,
213 )
214 }
215
216 #[allow(clippy::too_many_arguments)]
218 pub(crate) fn initialize_mouse_event(
219 &self,
220 type_: DOMString,
221 can_bubble: EventBubbles,
222 cancelable: EventCancelable,
223 view: Option<&Window>,
224 detail: i32,
225 screen_point: Point2D<i32, CSSPixel>,
226 client_point: Point2D<i32, CSSPixel>,
227 page_point: Point2D<i32, CSSPixel>,
228 modifiers: Modifiers,
229 button: i16,
230 buttons: u16,
231 related_target: Option<&EventTarget>,
232 point_in_target: Option<Point2D<f32, CSSPixel>>,
233 ) {
234 self.uievent.initialize_ui_event(
235 type_,
236 view.map(|window| window.upcast::<EventTarget>()),
237 can_bubble,
238 cancelable,
239 );
240
241 self.uievent.set_detail(detail);
242 self.screen_point.set(screen_point);
243 self.client_point.set(client_point);
244 self.page_point.set(page_point);
245 self.modifiers.set(modifiers);
246 self.button.set(button);
247 self.buttons.set(buttons);
248 self.related_target.set(related_target);
249 self.point_in_target.set(point_in_target);
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.related_target.get()
477 }
478
479 fn Which(&self) -> i32 {
485 if pref!(dom_mouse_event_which_enabled) {
486 (self.button.get() + 1) as i32
487 } else {
488 0
489 }
490 }
491
492 fn InitMouseEvent(
494 &self,
495 type_arg: DOMString,
496 can_bubble_arg: bool,
497 cancelable_arg: bool,
498 view_arg: Option<&Window>,
499 detail_arg: i32,
500 screen_x_arg: i32,
501 screen_y_arg: i32,
502 client_x_arg: i32,
503 client_y_arg: i32,
504 ctrl_key_arg: bool,
505 alt_key_arg: bool,
506 shift_key_arg: bool,
507 meta_key_arg: bool,
508 button_arg: i16,
509 related_target_arg: Option<&EventTarget>,
510 ) {
511 if self.upcast::<Event>().dispatching() {
512 return;
513 }
514
515 self.upcast::<UIEvent>().InitUIEvent(
516 type_arg,
517 can_bubble_arg,
518 cancelable_arg,
519 view_arg,
520 detail_arg,
521 );
522 self.screen_point
523 .set(Point2D::new(screen_x_arg, screen_y_arg));
524 self.client_point
525 .set(Point2D::new(client_x_arg, client_y_arg));
526
527 let global = self.global();
528 let scroll_offset = global.as_window().scroll_offset();
529 self.page_point.set(Point2D::new(
530 scroll_offset.x as i32 + client_x_arg,
531 scroll_offset.y as i32 + client_y_arg,
532 ));
533
534 let mut modifiers = Modifiers::empty();
535 if ctrl_key_arg {
536 modifiers.insert(Modifiers::CONTROL);
537 }
538 if alt_key_arg {
539 modifiers.insert(Modifiers::ALT);
540 }
541 if shift_key_arg {
542 modifiers.insert(Modifiers::SHIFT);
543 }
544 if meta_key_arg {
545 modifiers.insert(Modifiers::META);
546 }
547 self.modifiers.set(modifiers);
548
549 self.button.set(button_arg);
550 self.related_target.set(related_target_arg);
551 }
552
553 fn IsTrusted(&self) -> bool {
555 self.uievent.IsTrusted()
556 }
557
558 fn GetModifierState(&self, key_arg: DOMString) -> bool {
560 self.modifiers.get().contains(match &*key_arg.str() {
561 "Alt" => Modifiers::ALT,
562 "AltGraph" => Modifiers::ALT_GRAPH,
563 "CapsLock" => Modifiers::CAPS_LOCK,
564 "Control" => Modifiers::CONTROL,
565 "Fn" => Modifiers::FN,
566 "FnLock" => Modifiers::FN_LOCK,
567 "Meta" => Modifiers::META,
568 "NumLock" => Modifiers::NUM_LOCK,
569 "ScrollLock" => Modifiers::SCROLL_LOCK,
570 "Shift" => Modifiers::SHIFT,
571 "Symbol" => Modifiers::SYMBOL,
572 "SymbolLock" => Modifiers::SYMBOL_LOCK,
573 _ => return false,
574 })
575 }
576}