1use std::array::from_ref;
6use std::cell::{Cell, RefCell};
7use std::f64::consts::PI;
8use std::mem;
9use std::rc::Rc;
10use std::time::{Duration, Instant};
11
12use base::generic_channel;
13use constellation_traits::ScriptToConstellationMessage;
14use embedder_traits::{
15 Cursor, EditingActionEvent, EmbedderMsg, GamepadEvent as EmbedderGamepadEvent,
16 GamepadSupportedHapticEffects, GamepadUpdateType, ImeEvent, InputEvent,
17 KeyboardEvent as EmbedderKeyboardEvent, MouseButton, MouseButtonAction, MouseButtonEvent,
18 MouseLeftViewportEvent, ScrollEvent, TouchEvent as EmbedderTouchEvent, TouchEventType, TouchId,
19 UntrustedNodeAddress, WheelEvent as EmbedderWheelEvent,
20};
21use euclid::Point2D;
22use ipc_channel::ipc;
23use keyboard_types::{Code, Key, KeyState, Modifiers, NamedKey};
24use layout_api::node_id_from_scroll_id;
25use script_bindings::codegen::GenericBindings::DocumentBinding::DocumentMethods;
26use script_bindings::codegen::GenericBindings::EventBinding::EventMethods;
27use script_bindings::codegen::GenericBindings::NavigatorBinding::NavigatorMethods;
28use script_bindings::codegen::GenericBindings::NodeBinding::NodeMethods;
29use script_bindings::codegen::GenericBindings::PerformanceBinding::PerformanceMethods;
30use script_bindings::codegen::GenericBindings::TouchBinding::TouchMethods;
31use script_bindings::codegen::GenericBindings::WindowBinding::WindowMethods;
32use script_bindings::inheritance::Castable;
33use script_bindings::num::Finite;
34use script_bindings::root::{Dom, DomRoot, DomSlice};
35use script_bindings::script_runtime::CanGc;
36use script_bindings::str::DOMString;
37use script_traits::ConstellationInputEvent;
38use servo_config::pref;
39use style_traits::CSSPixel;
40use xml5ever::{local_name, ns};
41
42use crate::dom::bindings::cell::DomRefCell;
43use crate::dom::bindings::refcounted::Trusted;
44use crate::dom::bindings::root::MutNullableDom;
45use crate::dom::clipboardevent::ClipboardEventType;
46use crate::dom::document::{FireMouseEventType, FocusInitiator, TouchEventResult};
47use crate::dom::event::{EventBubbles, EventCancelable, EventDefault};
48use crate::dom::gamepad::gamepad::{Gamepad, contains_user_gesture};
49use crate::dom::gamepad::gamepadevent::GamepadEventType;
50use crate::dom::inputevent::HitTestResult;
51use crate::dom::node::{self, Node, ShadowIncluding};
52use crate::dom::pointerevent::PointerId;
53use crate::dom::types::{
54 ClipboardEvent, CompositionEvent, DataTransfer, Element, Event, EventTarget, GlobalScope,
55 HTMLAnchorElement, KeyboardEvent, MouseEvent, PointerEvent, Touch, TouchEvent, TouchList,
56 WheelEvent, Window,
57};
58use crate::drag_data_store::{DragDataStore, Kind, Mode};
59use crate::realms::enter_realm;
60
61#[derive(JSTraceable, MallocSizeOf)]
65#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
66pub(crate) struct DocumentEventHandler {
67 window: Dom<Window>,
69 #[no_trace]
71 #[ignore_malloc_size_of = "CompositorEvent contains data from outside crates"]
72 pending_input_events: DomRefCell<Vec<ConstellationInputEvent>>,
73 mouse_move_event_index: DomRefCell<Option<usize>>,
75 #[ignore_malloc_size_of = "Defined in std"]
77 #[no_trace]
78 last_click_info: DomRefCell<Option<(Instant, Point2D<f32, CSSPixel>)>>,
79 current_hover_target: MutNullableDom<Element>,
81 #[no_trace]
83 most_recent_mousemove_point: Cell<Option<Point2D<f32, CSSPixel>>>,
84 #[no_trace]
87 current_cursor: Cell<Option<Cursor>>,
88 active_touch_points: DomRefCell<Vec<Dom<Touch>>>,
90 #[no_trace]
92 active_keyboard_modifiers: Cell<Modifiers>,
93}
94
95impl DocumentEventHandler {
96 pub(crate) fn new(window: &Window) -> Self {
97 Self {
98 window: Dom::from_ref(window),
99 pending_input_events: Default::default(),
100 mouse_move_event_index: Default::default(),
101 last_click_info: Default::default(),
102 current_hover_target: Default::default(),
103 most_recent_mousemove_point: Default::default(),
104 current_cursor: Default::default(),
105 active_touch_points: Default::default(),
106 active_keyboard_modifiers: Default::default(),
107 }
108 }
109
110 pub(crate) fn note_pending_input_event(&self, event: ConstellationInputEvent) {
112 let mut pending_compositor_events = self.pending_input_events.borrow_mut();
113 if matches!(event.event, InputEvent::MouseMove(..)) {
114 if let Some(mouse_move_event) = self
116 .mouse_move_event_index
117 .borrow()
118 .and_then(|index| pending_compositor_events.get_mut(index))
119 {
120 *mouse_move_event = event;
121 return;
122 }
123
124 *self.mouse_move_event_index.borrow_mut() = Some(pending_compositor_events.len());
125 }
126
127 pending_compositor_events.push(event);
128 }
129
130 pub(crate) fn has_pending_input_events(&self) -> bool {
133 !self.pending_input_events.borrow().is_empty()
134 }
135
136 pub(crate) fn alternate_action_keyboard_modifier_active(&self) -> bool {
137 #[cfg(target_os = "macos")]
138 {
139 self.active_keyboard_modifiers
140 .get()
141 .contains(Modifiers::META)
142 }
143 #[cfg(not(target_os = "macos"))]
144 {
145 self.active_keyboard_modifiers
146 .get()
147 .contains(Modifiers::CONTROL)
148 }
149 }
150
151 pub(crate) fn handle_pending_input_events(&self, can_gc: CanGc) {
152 let _realm = enter_realm(&*self.window);
153
154 *self.mouse_move_event_index.borrow_mut() = None;
156 let pending_input_events = mem::take(&mut *self.pending_input_events.borrow_mut());
157
158 for event in pending_input_events {
159 self.active_keyboard_modifiers
160 .set(event.active_keyboard_modifiers);
161
162 match event.event.clone() {
163 InputEvent::MouseButton(mouse_button_event) => {
164 self.handle_native_mouse_button_event(mouse_button_event, &event, can_gc);
165 },
166 InputEvent::MouseMove(_) => {
167 self.handle_native_mouse_move_event(&event, can_gc);
168 },
169 InputEvent::MouseLeftViewport(mouse_leave_event) => {
170 self.handle_mouse_left_viewport_event(&event, &mouse_leave_event, can_gc);
171 },
172 InputEvent::Touch(touch_event) => {
173 self.handle_touch_event(touch_event, &event, can_gc);
174 },
175 InputEvent::Wheel(wheel_event) => {
176 self.handle_wheel_event(wheel_event, &event, can_gc);
177 },
178 InputEvent::Keyboard(keyboard_event) => {
179 self.handle_keyboard_event(keyboard_event, can_gc);
180 },
181 InputEvent::Ime(ime_event) => {
182 self.handle_ime_event(ime_event, can_gc);
183 },
184 InputEvent::Gamepad(gamepad_event) => {
185 self.handle_gamepad_event(gamepad_event);
186 },
187 InputEvent::EditingAction(editing_action_event) => {
188 self.handle_editing_action(editing_action_event, can_gc);
189 },
190 InputEvent::Scroll(scroll_event) => {
191 self.handle_embedder_scroll_event(scroll_event);
192 },
193 }
194
195 self.notify_webdriver_input_event_completed(event.event);
196 }
197 }
198
199 fn notify_webdriver_input_event_completed(&self, event: InputEvent) {
200 let Some(id) = event.webdriver_message_id() else {
201 return;
202 };
203
204 let trusted_window = Trusted::new(&*self.window);
206 self.window
207 .as_global_scope()
208 .task_manager()
209 .dom_manipulation_task_source()
210 .queue(task!(notify_webdriver_input_event_completed: move || {
211 let window = trusted_window.root();
212 window.send_to_constellation(ScriptToConstellationMessage::WebDriverInputComplete(id));
213 }));
214 }
215
216 pub(crate) fn set_cursor(&self, cursor: Option<Cursor>) {
217 if cursor == self.current_cursor.get() {
218 return;
219 }
220 self.current_cursor.set(cursor);
221 self.window.send_to_embedder(EmbedderMsg::SetCursor(
222 self.window.webview_id(),
223 cursor.unwrap_or_default(),
224 ));
225 }
226
227 fn handle_mouse_left_viewport_event(
228 &self,
229 input_event: &ConstellationInputEvent,
230 mouse_leave_event: &MouseLeftViewportEvent,
231 can_gc: CanGc,
232 ) {
233 if let Some(current_hover_target) = self.current_hover_target.get() {
234 let current_hover_target = current_hover_target.upcast::<Node>();
235 for element in current_hover_target
236 .inclusive_ancestors(ShadowIncluding::No)
237 .filter_map(DomRoot::downcast::<Element>)
238 {
239 element.set_hover_state(false);
240 element.set_active_state(false);
241 }
242
243 if let Some(hit_test_result) = self
244 .most_recent_mousemove_point
245 .get()
246 .and_then(|point| self.window.hit_test_from_point_in_viewport(point))
247 {
248 MouseEvent::new_simple(
249 &self.window,
250 FireMouseEventType::Out,
251 EventBubbles::Bubbles,
252 EventCancelable::Cancelable,
253 &hit_test_result,
254 input_event,
255 can_gc,
256 )
257 .upcast::<Event>()
258 .fire(current_hover_target.upcast(), can_gc);
259 self.handle_mouse_enter_leave_event(
260 DomRoot::from_ref(current_hover_target),
261 None,
262 FireMouseEventType::Leave,
263 &hit_test_result,
264 input_event,
265 can_gc,
266 );
267 }
268 }
269
270 if !mouse_leave_event.focus_moving_to_another_iframe {
275 self.window
279 .send_to_embedder(EmbedderMsg::Status(self.window.webview_id(), None));
280 self.set_cursor(None);
281 } else {
282 self.current_cursor.set(None);
283 }
284
285 self.current_hover_target.set(None);
286 self.most_recent_mousemove_point.set(None);
287 }
288
289 fn handle_mouse_enter_leave_event(
290 &self,
291 event_target: DomRoot<Node>,
292 related_target: Option<DomRoot<Node>>,
293 event_type: FireMouseEventType,
294 hit_test_result: &HitTestResult,
295 input_event: &ConstellationInputEvent,
296 can_gc: CanGc,
297 ) {
298 assert!(matches!(
299 event_type,
300 FireMouseEventType::Enter | FireMouseEventType::Leave
301 ));
302
303 let common_ancestor = match related_target.as_ref() {
304 Some(related_target) => event_target
305 .common_ancestor(related_target, ShadowIncluding::No)
306 .unwrap_or_else(|| DomRoot::from_ref(&*event_target)),
307 None => DomRoot::from_ref(&*event_target),
308 };
309
310 let mut targets = vec![];
313 let mut current = Some(event_target);
314 while let Some(node) = current {
315 if node == common_ancestor {
316 break;
317 }
318 current = node.GetParentNode();
319 targets.push(node);
320 }
321
322 if event_type == FireMouseEventType::Enter {
325 targets = targets.into_iter().rev().collect();
326 }
327
328 for target in targets {
329 MouseEvent::new_simple(
330 &self.window,
331 event_type,
332 EventBubbles::DoesNotBubble,
333 EventCancelable::NotCancelable,
334 hit_test_result,
335 input_event,
336 can_gc,
337 )
338 .upcast::<Event>()
339 .fire(target.upcast(), can_gc);
340 }
341 }
342
343 fn handle_native_mouse_move_event(&self, input_event: &ConstellationInputEvent, can_gc: CanGc) {
345 let Some(hit_test_result) = self.window.hit_test_from_input_event(input_event) else {
347 return;
348 };
349
350 self.set_cursor(Some(hit_test_result.cursor));
352
353 let Some(new_target) = hit_test_result
354 .node
355 .inclusive_ancestors(ShadowIncluding::No)
356 .filter_map(DomRoot::downcast::<Element>)
357 .next()
358 else {
359 return;
360 };
361
362 let target_has_changed = self
363 .current_hover_target
364 .get()
365 .is_none_or(|old_target| old_target != new_target);
366
367 if target_has_changed {
370 if let Some(old_target) = self.current_hover_target.get() {
372 let old_target_is_ancestor_of_new_target = old_target
373 .upcast::<Node>()
374 .is_ancestor_of(new_target.upcast::<Node>());
375
376 if !old_target_is_ancestor_of_new_target {
379 for element in old_target
380 .upcast::<Node>()
381 .inclusive_ancestors(ShadowIncluding::No)
382 .filter_map(DomRoot::downcast::<Element>)
383 {
384 element.set_hover_state(false);
385 element.set_active_state(false);
386 }
387 }
388
389 MouseEvent::new_simple(
390 &self.window,
391 FireMouseEventType::Out,
392 EventBubbles::Bubbles,
393 EventCancelable::Cancelable,
394 &hit_test_result,
395 input_event,
396 can_gc,
397 )
398 .upcast::<Event>()
399 .fire(old_target.upcast(), can_gc);
400
401 if !old_target_is_ancestor_of_new_target {
402 let event_target = DomRoot::from_ref(old_target.upcast::<Node>());
403 let moving_into = Some(DomRoot::from_ref(new_target.upcast::<Node>()));
404 self.handle_mouse_enter_leave_event(
405 event_target,
406 moving_into,
407 FireMouseEventType::Leave,
408 &hit_test_result,
409 input_event,
410 can_gc,
411 );
412 }
413 }
414
415 for element in new_target
417 .upcast::<Node>()
418 .inclusive_ancestors(ShadowIncluding::No)
419 .filter_map(DomRoot::downcast::<Element>)
420 {
421 element.set_hover_state(true);
422 }
423
424 MouseEvent::new_simple(
425 &self.window,
426 FireMouseEventType::Over,
427 EventBubbles::Bubbles,
428 EventCancelable::Cancelable,
429 &hit_test_result,
430 input_event,
431 can_gc,
432 )
433 .upcast::<Event>()
434 .fire(new_target.upcast(), can_gc);
435
436 let moving_from = self
437 .current_hover_target
438 .get()
439 .map(|old_target| DomRoot::from_ref(old_target.upcast::<Node>()));
440 let event_target = DomRoot::from_ref(new_target.upcast::<Node>());
441 self.handle_mouse_enter_leave_event(
442 event_target,
443 moving_from,
444 FireMouseEventType::Enter,
445 &hit_test_result,
446 input_event,
447 can_gc,
448 );
449 }
450
451 MouseEvent::new_simple(
454 &self.window,
455 FireMouseEventType::Move,
456 EventBubbles::Bubbles,
457 EventCancelable::Cancelable,
458 &hit_test_result,
459 input_event,
460 can_gc,
461 )
462 .upcast::<Event>()
463 .fire(new_target.upcast(), can_gc);
464
465 self.update_current_hover_target_and_status(Some(new_target));
466 self.most_recent_mousemove_point
467 .set(Some(hit_test_result.point_in_frame));
468 }
469
470 fn update_current_hover_target_and_status(&self, new_hover_target: Option<DomRoot<Element>>) {
471 let current_hover_target = self.current_hover_target.get();
472 if current_hover_target == new_hover_target {
473 return;
474 }
475
476 let previous_hover_target = self.current_hover_target.get();
477 self.current_hover_target.set(new_hover_target.as_deref());
478
479 if let Some(target) = self.current_hover_target.get() {
482 if let Some(anchor) = target
483 .upcast::<Node>()
484 .inclusive_ancestors(ShadowIncluding::No)
485 .filter_map(DomRoot::downcast::<HTMLAnchorElement>)
486 .next()
487 {
488 let status = anchor
489 .upcast::<Element>()
490 .get_attribute(&ns!(), &local_name!("href"))
491 .and_then(|href| {
492 let value = href.value();
493 let url = self.window.get_url();
494 url.join(&value).map(|url| url.to_string()).ok()
495 });
496 self.window
497 .send_to_embedder(EmbedderMsg::Status(self.window.webview_id(), status));
498 return;
499 }
500 }
501
502 if previous_hover_target.is_none_or(|previous_hover_target| {
507 previous_hover_target
508 .upcast::<Node>()
509 .inclusive_ancestors(ShadowIncluding::No)
510 .filter_map(DomRoot::downcast::<HTMLAnchorElement>)
511 .next()
512 .is_some()
513 }) {
514 self.window
515 .send_to_embedder(EmbedderMsg::Status(self.window.webview_id(), None));
516 }
517 }
518
519 pub(crate) fn handle_refresh_cursor(&self) {
520 let Some(most_recent_mousemove_point) = self.most_recent_mousemove_point.get() else {
521 return;
522 };
523
524 let Some(hit_test_result) = self
525 .window
526 .hit_test_from_point_in_viewport(most_recent_mousemove_point)
527 else {
528 return;
529 };
530
531 self.set_cursor(Some(hit_test_result.cursor));
532 }
533
534 fn handle_native_mouse_button_event(
537 &self,
538 event: MouseButtonEvent,
539 input_event: &ConstellationInputEvent,
540 can_gc: CanGc,
541 ) {
542 let Some(hit_test_result) = self.window.hit_test_from_input_event(input_event) else {
544 return;
545 };
546
547 debug!(
548 "{:?}: at {:?}",
549 event.action, hit_test_result.point_in_frame
550 );
551
552 let Some(el) = hit_test_result
553 .node
554 .inclusive_ancestors(ShadowIncluding::Yes)
555 .filter_map(DomRoot::downcast::<Element>)
556 .next()
557 else {
558 return;
559 };
560
561 let node = el.upcast::<Node>();
562 debug!("{:?} on {:?}", event.action, node.debug_str());
563
564 if el.is_actually_disabled() {
568 return;
569 }
570
571 let dom_event = DomRoot::upcast::<Event>(MouseEvent::for_platform_mouse_event(
572 event,
573 input_event.pressed_mouse_buttons,
574 &self.window,
575 &hit_test_result,
576 input_event.active_keyboard_modifiers,
577 can_gc,
578 ));
579
580 let activatable = el.as_maybe_activatable();
581 match event.action {
582 MouseButtonAction::Click => {
584 el.set_click_in_progress(true);
585 dom_event.dispatch(node.upcast(), false, can_gc);
586 el.set_click_in_progress(false);
587
588 self.maybe_fire_dblclick(node, &hit_test_result, input_event, can_gc);
589 },
590 MouseButtonAction::Down => {
592 if let Some(a) = activatable {
593 a.enter_formal_activation_state();
594 }
595
596 let target_el = el.find_focusable_shadow_host_if_necessary();
603
604 let document = self.window.Document();
605 document.begin_focus_transaction();
606
607 document.request_focus(None, FocusInitiator::Local, can_gc);
609 document.request_focus(target_el.as_deref(), FocusInitiator::Local, can_gc);
610
611 let result = dom_event.dispatch(node.upcast(), false, can_gc);
613
614 if result && document.has_focus_transaction() {
617 document.commit_focus_transaction(FocusInitiator::Local, can_gc);
618 }
619
620 if let MouseButton::Right = event.button {
623 self.maybe_show_context_menu(
624 node.upcast(),
625 &hit_test_result,
626 input_event,
627 can_gc,
628 );
629 }
630 },
631 MouseButtonAction::Up => {
633 if let Some(a) = activatable {
634 a.exit_formal_activation_state();
635 }
636
637 dom_event.dispatch(node.upcast(), false, can_gc);
641 },
642 }
643 }
644
645 fn maybe_show_context_menu(
647 &self,
648 target: &EventTarget,
649 hit_test_result: &HitTestResult,
650 input_event: &ConstellationInputEvent,
651 can_gc: CanGc,
652 ) {
653 let menu_event = PointerEvent::new(
655 &self.window, DOMString::from("contextmenu"), EventBubbles::Bubbles, EventCancelable::Cancelable, Some(&self.window), 0, hit_test_result.point_in_frame.to_i32(),
662 hit_test_result.point_in_frame.to_i32(),
663 hit_test_result
664 .point_relative_to_initial_containing_block
665 .to_i32(),
666 input_event.active_keyboard_modifiers,
667 2i16, input_event.pressed_mouse_buttons,
669 None, None, PointerId::Mouse as i32, 1, 1, 0.5, 0.0, 0, 0, 0, PI / 2.0, 0.0, DOMString::from("mouse"), true, vec![], vec![], can_gc,
686 );
687
688 let result = menu_event.upcast::<Event>().fire(target, can_gc);
690
691 if result {
693 let (sender, receiver) =
694 generic_channel::channel().expect("Failed to create IPC channel.");
695 self.window.send_to_embedder(EmbedderMsg::ShowContextMenu(
696 self.window.webview_id(),
697 sender,
698 None,
699 vec![],
700 ));
701 let _ = receiver.recv().unwrap();
702 };
703 }
704
705 fn maybe_fire_dblclick(
706 &self,
707 target: &Node,
708 hit_test_result: &HitTestResult,
709 input_event: &ConstellationInputEvent,
710 can_gc: CanGc,
711 ) {
712 let now = Instant::now();
714 let point_in_frame = hit_test_result.point_in_frame;
715 let opt = self.last_click_info.borrow_mut().take();
716
717 if let Some((last_time, last_pos)) = opt {
718 let double_click_timeout =
719 Duration::from_millis(pref!(dom_document_dblclick_timeout) as u64);
720 let double_click_distance_threshold = pref!(dom_document_dblclick_dist) as u64;
721
722 let line = point_in_frame - last_pos;
724 let dist = (line.dot(line) as f64).sqrt();
725
726 if now.duration_since(last_time) < double_click_timeout &&
727 dist < double_click_distance_threshold as f64
728 {
729 let click_count = 2;
731
732 let event = MouseEvent::new(
733 &self.window,
734 DOMString::from("dblclick"),
735 EventBubbles::Bubbles,
736 EventCancelable::Cancelable,
737 Some(&self.window),
738 click_count,
739 point_in_frame.to_i32(),
740 point_in_frame.to_i32(),
741 hit_test_result
742 .point_relative_to_initial_containing_block
743 .to_i32(),
744 input_event.active_keyboard_modifiers,
745 0i16,
746 input_event.pressed_mouse_buttons,
747 None,
748 None,
749 can_gc,
750 );
751 event.upcast::<Event>().fire(target.upcast(), can_gc);
752
753 return;
756 }
757 }
758
759 *self.last_click_info.borrow_mut() = Some((now, point_in_frame));
761 }
762
763 fn handle_touch_event(
764 &self,
765 event: EmbedderTouchEvent,
766 input_event: &ConstellationInputEvent,
767 can_gc: CanGc,
768 ) {
769 let result = self.handle_touch_event_inner(event, input_event, can_gc);
770 if let (TouchEventResult::Processed(handled), true) = (result, event.is_cancelable()) {
771 let sequence_id = event.expect_sequence_id();
772 let result = if handled {
773 embedder_traits::TouchEventResult::DefaultAllowed(sequence_id, event.event_type)
774 } else {
775 embedder_traits::TouchEventResult::DefaultPrevented(sequence_id, event.event_type)
776 };
777 self.window
778 .send_to_constellation(ScriptToConstellationMessage::TouchEventProcessed(result));
779 }
780 }
781
782 fn handle_touch_event_inner(
783 &self,
784 event: EmbedderTouchEvent,
785 input_event: &ConstellationInputEvent,
786 can_gc: CanGc,
787 ) -> TouchEventResult {
788 let Some(hit_test_result) = self.window.hit_test_from_input_event(input_event) else {
790 self.update_active_touch_points_when_early_return(event);
791 return TouchEventResult::Forwarded;
792 };
793
794 let TouchId(identifier) = event.id;
795 let event_name = match event.event_type {
796 TouchEventType::Down => "touchstart",
797 TouchEventType::Move => "touchmove",
798 TouchEventType::Up => "touchend",
799 TouchEventType::Cancel => "touchcancel",
800 };
801
802 let Some(el) = hit_test_result
803 .node
804 .inclusive_ancestors(ShadowIncluding::No)
805 .filter_map(DomRoot::downcast::<Element>)
806 .next()
807 else {
808 self.update_active_touch_points_when_early_return(event);
809 return TouchEventResult::Forwarded;
810 };
811
812 let target = DomRoot::upcast::<EventTarget>(el);
813 let window = &*self.window;
814
815 let client_x = Finite::wrap(hit_test_result.point_in_frame.x as f64);
816 let client_y = Finite::wrap(hit_test_result.point_in_frame.y as f64);
817 let page_x =
818 Finite::wrap(hit_test_result.point_in_frame.x as f64 + window.PageXOffset() as f64);
819 let page_y =
820 Finite::wrap(hit_test_result.point_in_frame.y as f64 + window.PageYOffset() as f64);
821
822 let touch = Touch::new(
823 window, identifier, &target, client_x,
824 client_y, client_x, client_y, page_x, page_y, can_gc,
826 );
827
828 match event.event_type {
829 TouchEventType::Down => {
830 self.active_touch_points
832 .borrow_mut()
833 .push(Dom::from_ref(&*touch));
834 },
835 TouchEventType::Move => {
836 let mut active_touch_points = self.active_touch_points.borrow_mut();
838 match active_touch_points
839 .iter_mut()
840 .find(|t| t.Identifier() == identifier)
841 {
842 Some(t) => *t = Dom::from_ref(&*touch),
843 None => warn!("Got a touchmove event for a non-active touch point"),
844 }
845 },
846 TouchEventType::Up | TouchEventType::Cancel => {
847 let mut active_touch_points = self.active_touch_points.borrow_mut();
849 match active_touch_points
850 .iter()
851 .position(|t| t.Identifier() == identifier)
852 {
853 Some(i) => {
854 active_touch_points.swap_remove(i);
855 },
856 None => warn!("Got a touchend event for a non-active touch point"),
857 }
858 },
859 }
860
861 rooted_vec!(let mut target_touches);
862 let touches = {
863 let touches = self.active_touch_points.borrow();
864 target_touches.extend(touches.iter().filter(|t| t.Target() == target).cloned());
865 TouchList::new(window, touches.r(), can_gc)
866 };
867
868 let event = TouchEvent::new(
869 window,
870 DOMString::from(event_name),
871 EventBubbles::Bubbles,
872 EventCancelable::from(event.is_cancelable()),
873 Some(window),
874 0i32,
875 &touches,
876 &TouchList::new(window, from_ref(&&*touch), can_gc),
877 &TouchList::new(window, target_touches.r(), can_gc),
878 false,
880 false,
881 false,
882 false,
883 can_gc,
884 );
885
886 TouchEventResult::Processed(event.upcast::<Event>().fire(&target, can_gc))
887 }
888
889 fn update_active_touch_points_when_early_return(&self, event: EmbedderTouchEvent) {
891 match event.event_type {
892 TouchEventType::Down => {
893 },
897 TouchEventType::Move => {
898 },
901 TouchEventType::Up | TouchEventType::Cancel => {
902 let mut active_touch_points = self.active_touch_points.borrow_mut();
904 match active_touch_points
905 .iter()
906 .position(|t| t.Identifier() == event.id.0)
907 {
908 Some(i) => {
909 active_touch_points.swap_remove(i);
910 },
911 None => warn!("Got a touchend event for a non-active touch point"),
912 }
913 },
914 }
915 }
916
917 fn handle_keyboard_event(&self, keyboard_event: EmbedderKeyboardEvent, can_gc: CanGc) {
919 let document = self.window.Document();
920 let focused = document.get_focused_element();
921 let body = document.GetBody();
922
923 let target = match (&focused, &body) {
924 (Some(focused), _) => focused.upcast(),
925 (&None, Some(body)) => body.upcast(),
926 (&None, &None) => self.window.upcast(),
927 };
928
929 let keyevent = KeyboardEvent::new(
930 &self.window,
931 DOMString::from(keyboard_event.event.state.event_type()),
932 true,
933 true,
934 Some(&self.window),
935 0,
936 keyboard_event.event.key.clone(),
937 DOMString::from(keyboard_event.event.code.to_string()),
938 keyboard_event.event.location as u32,
939 keyboard_event.event.repeat,
940 keyboard_event.event.is_composing,
941 keyboard_event.event.modifiers,
942 0,
943 keyboard_event.event.key.legacy_keycode(),
944 can_gc,
945 );
946 let event = keyevent.upcast::<Event>();
947 event.fire(target, can_gc);
948 let mut cancel_state = event.get_cancel_state();
949
950 let is_character_value_key = matches!(
956 keyboard_event.event.key,
957 Key::Character(_) | Key::Named(NamedKey::Enter)
958 );
959 if keyboard_event.event.state == KeyState::Down &&
960 is_character_value_key &&
961 !keyboard_event.event.is_composing &&
962 cancel_state != EventDefault::Prevented
963 {
964 let event = KeyboardEvent::new(
966 &self.window,
967 DOMString::from("keypress"),
968 true,
969 true,
970 Some(&self.window),
971 0,
972 keyboard_event.event.key.clone(),
973 DOMString::from(keyboard_event.event.code.to_string()),
974 keyboard_event.event.location as u32,
975 keyboard_event.event.repeat,
976 keyboard_event.event.is_composing,
977 keyboard_event.event.modifiers,
978 keyboard_event.event.key.legacy_charcode(),
979 0,
980 can_gc,
981 );
982 let ev = event.upcast::<Event>();
983 ev.fire(target, can_gc);
984 cancel_state = ev.get_cancel_state();
985 }
986
987 if cancel_state == EventDefault::Allowed {
988 self.window.send_to_embedder(EmbedderMsg::Keyboard(
989 self.window.webview_id(),
990 keyboard_event.clone(),
991 ));
992
993 if (keyboard_event.event.key == Key::Named(NamedKey::Enter) ||
999 keyboard_event.event.code == Code::Space) &&
1000 keyboard_event.event.state == KeyState::Up
1001 {
1002 if let Some(elem) = target.downcast::<Element>() {
1003 elem.upcast::<Node>()
1004 .fire_synthetic_pointer_event_not_trusted(DOMString::from("click"), can_gc);
1005 }
1006 }
1007 }
1008 }
1009
1010 fn handle_ime_event(&self, event: ImeEvent, can_gc: CanGc) {
1011 let document = self.window.Document();
1012 let composition_event = match event {
1013 ImeEvent::Dismissed => {
1014 document.request_focus(
1015 document.GetBody().as_ref().map(|e| e.upcast()),
1016 FocusInitiator::Local,
1017 can_gc,
1018 );
1019 return;
1020 },
1021 ImeEvent::Composition(composition_event) => composition_event,
1022 };
1023
1024 let focused = document.get_focused_element();
1029 let target = if let Some(elem) = &focused {
1030 elem.upcast()
1031 } else {
1032 return;
1034 };
1035
1036 let cancelable = composition_event.state == keyboard_types::CompositionState::Start;
1037 CompositionEvent::new(
1038 &self.window,
1039 DOMString::from(composition_event.state.event_type()),
1040 true,
1041 cancelable,
1042 Some(&self.window),
1043 0,
1044 DOMString::from(composition_event.data),
1045 can_gc,
1046 )
1047 .upcast::<Event>()
1048 .fire(target, can_gc);
1049 }
1050
1051 fn handle_wheel_event(
1052 &self,
1053 event: EmbedderWheelEvent,
1054 input_event: &ConstellationInputEvent,
1055 can_gc: CanGc,
1056 ) {
1057 let Some(hit_test_result) = self.window.hit_test_from_input_event(input_event) else {
1059 return;
1060 };
1061
1062 let Some(el) = hit_test_result
1063 .node
1064 .inclusive_ancestors(ShadowIncluding::No)
1065 .filter_map(DomRoot::downcast::<Element>)
1066 .next()
1067 else {
1068 return;
1069 };
1070
1071 let node = el.upcast::<Node>();
1072 let wheel_event_type_string = "wheel".to_owned();
1073 debug!(
1074 "{}: on {:?} at {:?}",
1075 wheel_event_type_string,
1076 node.debug_str(),
1077 hit_test_result.point_in_frame
1078 );
1079
1080 let dom_event = WheelEvent::new(
1082 &self.window,
1083 DOMString::from(wheel_event_type_string),
1084 EventBubbles::Bubbles,
1085 EventCancelable::Cancelable,
1086 Some(&self.window),
1087 0i32,
1088 hit_test_result.point_in_frame.to_i32(),
1089 hit_test_result.point_in_frame.to_i32(),
1090 hit_test_result
1091 .point_relative_to_initial_containing_block
1092 .to_i32(),
1093 input_event.active_keyboard_modifiers,
1094 0i16,
1095 input_event.pressed_mouse_buttons,
1096 None,
1097 None,
1098 Finite::wrap(-event.delta.x),
1103 Finite::wrap(-event.delta.y),
1104 Finite::wrap(-event.delta.z),
1105 event.delta.mode as u32,
1106 can_gc,
1107 );
1108
1109 let dom_event = dom_event.upcast::<Event>();
1110 dom_event.set_trusted(true);
1111
1112 let target = node.upcast();
1113 dom_event.fire(target, can_gc);
1114 }
1115
1116 fn handle_gamepad_event(&self, gamepad_event: EmbedderGamepadEvent) {
1117 match gamepad_event {
1118 EmbedderGamepadEvent::Connected(index, name, bounds, supported_haptic_effects) => {
1119 self.handle_gamepad_connect(
1120 index.0,
1121 name,
1122 bounds.axis_bounds,
1123 bounds.button_bounds,
1124 supported_haptic_effects,
1125 );
1126 },
1127 EmbedderGamepadEvent::Disconnected(index) => {
1128 self.handle_gamepad_disconnect(index.0);
1129 },
1130 EmbedderGamepadEvent::Updated(index, update_type) => {
1131 self.receive_new_gamepad_button_or_axis(index.0, update_type);
1132 },
1133 };
1134 }
1135
1136 fn handle_gamepad_connect(
1138 &self,
1139 _index: usize,
1143 name: String,
1144 axis_bounds: (f64, f64),
1145 button_bounds: (f64, f64),
1146 supported_haptic_effects: GamepadSupportedHapticEffects,
1147 ) {
1148 let trusted_window = Trusted::new(&*self.window);
1151 self.window
1152 .upcast::<GlobalScope>()
1153 .task_manager()
1154 .gamepad_task_source()
1155 .queue(task!(gamepad_connected: move || {
1156 let window = trusted_window.root();
1157
1158 let navigator = window.Navigator();
1159 let selected_index = navigator.select_gamepad_index();
1160 let gamepad = Gamepad::new(
1161 &window,
1162 selected_index,
1163 name,
1164 "standard".into(),
1165 axis_bounds,
1166 button_bounds,
1167 supported_haptic_effects,
1168 false,
1169 CanGc::note(),
1170 );
1171 navigator.set_gamepad(selected_index as usize, &gamepad, CanGc::note());
1172 }));
1173 }
1174
1175 fn handle_gamepad_disconnect(&self, index: usize) {
1177 let trusted_window = Trusted::new(&*self.window);
1178 self.window
1179 .upcast::<GlobalScope>()
1180 .task_manager()
1181 .gamepad_task_source()
1182 .queue(task!(gamepad_disconnected: move || {
1183 let window = trusted_window.root();
1184 let navigator = window.Navigator();
1185 if let Some(gamepad) = navigator.get_gamepad(index) {
1186 if window.Document().is_fully_active() {
1187 gamepad.update_connected(false, gamepad.exposed(), CanGc::note());
1188 navigator.remove_gamepad(index);
1189 }
1190 }
1191 }));
1192 }
1193
1194 fn receive_new_gamepad_button_or_axis(&self, index: usize, update_type: GamepadUpdateType) {
1196 let trusted_window = Trusted::new(&*self.window);
1197
1198 self.window.upcast::<GlobalScope>().task_manager().gamepad_task_source().queue(
1200 task!(update_gamepad_state: move || {
1201 let window = trusted_window.root();
1202 let navigator = window.Navigator();
1203 if let Some(gamepad) = navigator.get_gamepad(index) {
1204 let current_time = window.Performance().Now();
1205 gamepad.update_timestamp(*current_time);
1206 match update_type {
1207 GamepadUpdateType::Axis(index, value) => {
1208 gamepad.map_and_normalize_axes(index, value);
1209 },
1210 GamepadUpdateType::Button(index, value) => {
1211 gamepad.map_and_normalize_buttons(index, value);
1212 }
1213 };
1214 if !navigator.has_gamepad_gesture() && contains_user_gesture(update_type) {
1215 navigator.set_has_gamepad_gesture(true);
1216 navigator.GetGamepads()
1217 .iter()
1218 .filter_map(|g| g.as_ref())
1219 .for_each(|gamepad| {
1220 gamepad.set_exposed(true);
1221 gamepad.update_timestamp(*current_time);
1222 let new_gamepad = Trusted::new(&**gamepad);
1223 if window.Document().is_fully_active() {
1224 window.upcast::<GlobalScope>().task_manager().gamepad_task_source().queue(
1225 task!(update_gamepad_connect: move || {
1226 let gamepad = new_gamepad.root();
1227 gamepad.notify_event(GamepadEventType::Connected, CanGc::note());
1228 })
1229 );
1230 }
1231 });
1232 }
1233 }
1234 })
1235 );
1236 }
1237
1238 fn handle_editing_action(&self, action: EditingActionEvent, can_gc: CanGc) -> bool {
1240 let clipboard_event_type = match action {
1241 EditingActionEvent::Copy => ClipboardEventType::Copy,
1242 EditingActionEvent::Cut => ClipboardEventType::Cut,
1243 EditingActionEvent::Paste => ClipboardEventType::Paste,
1244 };
1245
1246 let script_triggered = false;
1248
1249 let script_may_access_clipboard = false;
1253
1254 if script_triggered && !script_may_access_clipboard {
1256 return false;
1257 }
1258
1259 let event = ClipboardEvent::new(
1261 &self.window,
1262 None,
1263 DOMString::from(clipboard_event_type.as_str()),
1264 EventBubbles::Bubbles,
1265 EventCancelable::Cancelable,
1266 None,
1267 can_gc,
1268 );
1269 self.fire_clipboard_event(&event, clipboard_event_type, can_gc);
1270
1271 let e = event.upcast::<Event>();
1275
1276 if !e.IsTrusted() {
1277 return false;
1278 }
1279
1280 if e.DefaultPrevented() {
1282 match e.Type().str() {
1283 "copy" => {
1284 if let Some(clipboard_data) = event.get_clipboard_data() {
1287 let drag_data_store =
1288 clipboard_data.data_store().expect("This shouldn't fail");
1289 self.write_content_to_the_clipboard(&drag_data_store);
1290 }
1291 },
1292 "cut" => {
1293 if let Some(clipboard_data) = event.get_clipboard_data() {
1296 let drag_data_store =
1297 clipboard_data.data_store().expect("This shouldn't fail");
1298 self.write_content_to_the_clipboard(&drag_data_store);
1299 }
1300
1301 self.fire_clipboardchange_event(can_gc);
1303 },
1304 "paste" => return false,
1305 _ => (),
1306 }
1307 }
1308 true
1310 }
1311
1312 fn fire_clipboard_event(
1314 &self,
1315 event: &ClipboardEvent,
1316 action: ClipboardEventType,
1317 can_gc: CanGc,
1318 ) {
1319 let mut drag_data_store = DragDataStore::new();
1322
1323 let trusted = true;
1327
1328 let document = self.window.Document();
1330 let focused = document.get_focused_element();
1331 let body = document.GetBody();
1332
1333 let target = match (&focused, &body) {
1334 (Some(focused), _) => focused.upcast(),
1335 (&None, Some(body)) => body.upcast(),
1336 (&None, &None) => self.window.upcast(),
1337 };
1338 match action {
1342 ClipboardEventType::Copy | ClipboardEventType::Cut => {
1343 drag_data_store.set_mode(Mode::ReadWrite);
1345 },
1346 ClipboardEventType::Paste => {
1347 let (sender, receiver) = ipc::channel().unwrap();
1348 self.window.send_to_embedder(EmbedderMsg::GetClipboardText(
1349 self.window.webview_id(),
1350 sender,
1351 ));
1352 let text_contents = receiver
1353 .recv()
1354 .map(Result::unwrap_or_default)
1355 .unwrap_or_default();
1356
1357 drag_data_store.set_mode(Mode::ReadOnly);
1359 if trusted {
1361 let data = DOMString::from(text_contents.to_string());
1365 let type_ = DOMString::from("text/plain");
1366 let _ = drag_data_store.add(Kind::Text { data, type_ });
1367
1368 }
1374 },
1375 ClipboardEventType::Change => (),
1376 }
1377
1378 let clipboard_event_data = DataTransfer::new(
1380 &self.window,
1381 Rc::new(RefCell::new(Some(drag_data_store))),
1382 can_gc,
1383 );
1384
1385 event.set_clipboard_data(Some(&clipboard_event_data));
1387 let event = event.upcast::<Event>();
1388 event.set_trusted(trusted);
1390 event.set_composed(true);
1392 event.dispatch(target, false, can_gc);
1394 }
1395
1396 pub(crate) fn fire_clipboardchange_event(&self, can_gc: CanGc) {
1397 let clipboardchange_event = ClipboardEvent::new(
1398 &self.window,
1399 None,
1400 DOMString::from("clipboardchange"),
1401 EventBubbles::Bubbles,
1402 EventCancelable::Cancelable,
1403 None,
1404 can_gc,
1405 );
1406 self.fire_clipboard_event(&clipboardchange_event, ClipboardEventType::Change, can_gc);
1407 }
1408
1409 fn write_content_to_the_clipboard(&self, drag_data_store: &DragDataStore) {
1411 if drag_data_store.list_len() > 0 {
1413 self.window
1415 .send_to_embedder(EmbedderMsg::ClearClipboard(self.window.webview_id()));
1416 for item in drag_data_store.iter_item_list() {
1418 match item {
1419 Kind::Text { data, .. } => {
1420 self.window.send_to_embedder(EmbedderMsg::SetClipboardText(
1424 self.window.webview_id(),
1425 data.to_string(),
1426 ));
1427 },
1428 Kind::File { .. } => {
1429 },
1433 }
1434 }
1435 } else {
1436 if drag_data_store.clear_was_called {
1438 self.window
1440 .send_to_embedder(EmbedderMsg::ClearClipboard(self.window.webview_id()));
1441 }
1444 }
1445 }
1446
1447 #[allow(unsafe_code)]
1450 fn handle_embedder_scroll_event(&self, event: ScrollEvent) {
1451 let document = self.window.Document();
1453 if event.external_id.is_root() {
1454 document.handle_viewport_scroll_event();
1455 } else {
1456 let Some(node_id) = node_id_from_scroll_id(event.external_id.0 as usize) else {
1458 return;
1459 };
1460 let node = unsafe {
1461 node::from_untrusted_node_address(UntrustedNodeAddress::from_id(node_id))
1462 };
1463 let Some(element) = node
1464 .inclusive_ancestors(ShadowIncluding::No)
1465 .filter_map(DomRoot::downcast::<Element>)
1466 .next()
1467 else {
1468 return;
1469 };
1470
1471 document.handle_element_scroll_event(&element);
1472 }
1473 }
1474}