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: embedder_traits::MouseButtonEvent,
260 pressed_mouse_buttons: u16,
261 window: &Window,
262 hit_test_result: &HitTestResult,
263 modifiers: Modifiers,
264 can_gc: CanGc,
265 ) -> DomRoot<Self> {
266 let mouse_event_type_string = match event.action {
267 embedder_traits::MouseButtonAction::Click => "click",
268 embedder_traits::MouseButtonAction::Up => "mouseup",
269 embedder_traits::MouseButtonAction::Down => "mousedown",
270 };
271
272 let client_point = hit_test_result.point_in_frame.to_i32();
273 let page_point = hit_test_result
274 .point_relative_to_initial_containing_block
275 .to_i32();
276
277 let click_count = 1;
278 let mouse_event = MouseEvent::new(
279 window,
280 mouse_event_type_string.into(),
281 EventBubbles::Bubbles,
282 EventCancelable::Cancelable,
283 Some(window),
284 click_count,
285 client_point, client_point,
287 page_point,
288 modifiers,
289 event.button.into(),
290 pressed_mouse_buttons,
291 None,
292 Some(hit_test_result.point_in_node),
293 can_gc,
294 );
295
296 mouse_event.upcast::<Event>().set_trusted(true);
297 mouse_event.upcast::<Event>().set_composed(true);
298
299 mouse_event
300 }
301}
302
303impl MouseEventMethods<crate::DomTypeHolder> for MouseEvent {
304 fn Constructor(
306 window: &Window,
307 proto: Option<HandleObject>,
308 can_gc: CanGc,
309 type_: DOMString,
310 init: &MouseEventBinding::MouseEventInit,
311 ) -> Fallible<DomRoot<MouseEvent>> {
312 let bubbles = EventBubbles::from(init.parent.parent.parent.bubbles);
313 let cancelable = EventCancelable::from(init.parent.parent.parent.cancelable);
314 let scroll_offset = window.scroll_offset();
315 let page_point = Point2D::new(
316 scroll_offset.x as i32 + init.clientX,
317 scroll_offset.y as i32 + init.clientY,
318 );
319 let event = MouseEvent::new_with_proto(
320 window,
321 proto,
322 type_,
323 bubbles,
324 cancelable,
325 init.parent.parent.view.as_deref(),
326 init.parent.parent.detail,
327 Point2D::new(init.screenX, init.screenY),
328 Point2D::new(init.clientX, init.clientY),
329 page_point,
330 init.parent.modifiers(),
331 init.button,
332 init.buttons,
333 init.relatedTarget.as_deref(),
334 None,
335 can_gc,
336 );
337 event
338 .upcast::<Event>()
339 .set_composed(init.parent.parent.parent.composed);
340 Ok(event)
341 }
342
343 fn ScreenX(&self) -> i32 {
345 self.screen_point.get().x
346 }
347
348 fn ScreenY(&self) -> i32 {
350 self.screen_point.get().y
351 }
352
353 fn ClientX(&self) -> i32 {
355 self.client_point.get().x
356 }
357
358 fn ClientY(&self) -> i32 {
360 self.client_point.get().y
361 }
362
363 fn PageX(&self) -> i32 {
365 if self.upcast::<Event>().dispatching() {
370 return self.page_point.get().x;
371 }
372
373 self.global().as_window().ScrollX() + self.ClientX()
377 }
378
379 fn PageY(&self) -> i32 {
381 if self.upcast::<Event>().dispatching() {
386 return self.page_point.get().y;
387 }
388
389 self.global().as_window().ScrollY() + self.ClientY()
393 }
394
395 fn X(&self) -> i32 {
397 self.ClientX()
398 }
399
400 fn Y(&self) -> i32 {
402 self.ClientY()
403 }
404
405 fn OffsetX(&self) -> i32 {
407 let event = self.upcast::<Event>();
413 if event.dispatching() {
414 let Some(target) = event.GetTarget() else {
415 return 0;
416 };
417 let Some(node) = target.downcast::<Node>() else {
418 return 0;
419 };
420 return self.ClientX() - node.client_rect().origin.x;
421 }
422
423 self.PageX()
425 }
426
427 fn OffsetY(&self) -> i32 {
429 let event = self.upcast::<Event>();
435 if event.dispatching() {
436 let Some(target) = event.GetTarget() else {
437 return 0;
438 };
439 let Some(node) = target.downcast::<Node>() else {
440 return 0;
441 };
442 return self.ClientY() - node.client_rect().origin.y;
443 }
444
445 self.PageY()
447 }
448
449 fn CtrlKey(&self) -> bool {
451 self.modifiers.get().contains(Modifiers::CONTROL)
452 }
453
454 fn ShiftKey(&self) -> bool {
456 self.modifiers.get().contains(Modifiers::SHIFT)
457 }
458
459 fn AltKey(&self) -> bool {
461 self.modifiers.get().contains(Modifiers::ALT)
462 }
463
464 fn MetaKey(&self) -> bool {
466 self.modifiers.get().contains(Modifiers::META)
467 }
468
469 fn Button(&self) -> i16 {
471 self.button.get()
472 }
473
474 fn Buttons(&self) -> u16 {
476 self.buttons.get()
477 }
478
479 fn GetRelatedTarget(&self) -> Option<DomRoot<EventTarget>> {
481 self.related_target.get()
482 }
483
484 fn Which(&self) -> i32 {
490 if pref!(dom_mouse_event_which_enabled) {
491 (self.button.get() + 1) as i32
492 } else {
493 0
494 }
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.related_target.set(related_target_arg);
556 }
557
558 fn IsTrusted(&self) -> bool {
560 self.uievent.IsTrusted()
561 }
562
563 fn GetModifierState(&self, key_arg: DOMString) -> bool {
565 self.modifiers.get().contains(match &*key_arg {
566 "Alt" => Modifiers::ALT,
567 "AltGraph" => Modifiers::ALT_GRAPH,
568 "CapsLock" => Modifiers::CAPS_LOCK,
569 "Control" => Modifiers::CONTROL,
570 "Fn" => Modifiers::FN,
571 "FnLock" => Modifiers::FN_LOCK,
572 "Meta" => Modifiers::META,
573 "NumLock" => Modifiers::NUM_LOCK,
574 "ScrollLock" => Modifiers::SCROLL_LOCK,
575 "Shift" => Modifiers::SHIFT,
576 "Symbol" => Modifiers::SYMBOL,
577 "SymbolLock" => Modifiers::SYMBOL_LOCK,
578 _ => return false,
579 })
580 }
581}