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::GenericCallback;
13use constellation_traits::{KeyboardScroll, ScriptToConstellationMessage};
14use embedder_traits::{
15 Cursor, EditingActionEvent, EmbedderMsg, ImeEvent, InputEvent, InputEventAndId,
16 InputEventResult, KeyboardEvent as EmbedderKeyboardEvent, MouseButton, MouseButtonAction,
17 MouseButtonEvent, MouseLeftViewportEvent, TouchEvent as EmbedderTouchEvent, TouchEventType,
18 TouchId, UntrustedNodeAddress, WheelEvent as EmbedderWheelEvent,
19};
20#[cfg(feature = "gamepad")]
21use embedder_traits::{
22 GamepadEvent as EmbedderGamepadEvent, GamepadSupportedHapticEffects, GamepadUpdateType,
23};
24use euclid::{Point2D, Vector2D};
25use js::jsapi::JSAutoRealm;
26use keyboard_types::{Code, Key, KeyState, Modifiers, NamedKey};
27use layout_api::{ScrollContainerQueryFlags, node_id_from_scroll_id};
28use rustc_hash::FxHashMap;
29use script_bindings::codegen::GenericBindings::DocumentBinding::DocumentMethods;
30use script_bindings::codegen::GenericBindings::EventBinding::EventMethods;
31use script_bindings::codegen::GenericBindings::NavigatorBinding::NavigatorMethods;
32use script_bindings::codegen::GenericBindings::NodeBinding::NodeMethods;
33use script_bindings::codegen::GenericBindings::PerformanceBinding::PerformanceMethods;
34use script_bindings::codegen::GenericBindings::TouchBinding::TouchMethods;
35use script_bindings::codegen::GenericBindings::WindowBinding::{ScrollBehavior, WindowMethods};
36use script_bindings::inheritance::Castable;
37use script_bindings::match_domstring_ascii;
38use script_bindings::num::Finite;
39use script_bindings::reflector::DomObject;
40use script_bindings::root::{Dom, DomRoot, DomSlice};
41use script_bindings::script_runtime::CanGc;
42use script_bindings::str::DOMString;
43use script_traits::ConstellationInputEvent;
44use servo_config::pref;
45use style_traits::CSSPixel;
46use webrender_api::ExternalScrollId;
47
48use crate::dom::bindings::cell::DomRefCell;
49use crate::dom::bindings::refcounted::Trusted;
50use crate::dom::bindings::root::MutNullableDom;
51use crate::dom::clipboardevent::ClipboardEventType;
52use crate::dom::document::{FireMouseEventType, FocusInitiator};
53use crate::dom::event::{EventBubbles, EventCancelable, EventComposed, EventFlags};
54#[cfg(feature = "gamepad")]
55use crate::dom::gamepad::gamepad::{Gamepad, contains_user_gesture};
56#[cfg(feature = "gamepad")]
57use crate::dom::gamepad::gamepadevent::GamepadEventType;
58use crate::dom::inputevent::HitTestResult;
59use crate::dom::node::{self, Node, NodeTraits, ShadowIncluding};
60use crate::dom::pointerevent::{PointerEvent, PointerId};
61use crate::dom::scrolling_box::ScrollingBoxAxis;
62use crate::dom::types::{
63 ClipboardEvent, CompositionEvent, DataTransfer, Element, Event, EventTarget, GlobalScope,
64 HTMLAnchorElement, KeyboardEvent, MouseEvent, Touch, TouchEvent, TouchList, WheelEvent, Window,
65};
66use crate::drag_data_store::{DragDataStore, Kind, Mode};
67use crate::realms::enter_realm;
68
69#[derive(Default, JSTraceable, MallocSizeOf)]
79struct ClickCountingInfo {
80 time: Option<Instant>,
81 #[no_trace]
82 point: Option<Point2D<f32, CSSPixel>>,
83 #[no_trace]
84 button: Option<MouseButton>,
85 count: usize,
86}
87
88impl ClickCountingInfo {
89 fn reset_click_count_if_necessary(
90 &mut self,
91 button: MouseButton,
92 point_in_frame: Point2D<f32, CSSPixel>,
93 ) {
94 let (Some(previous_button), Some(previous_point), Some(previous_time)) =
95 (self.button, self.point, self.time)
96 else {
97 assert_eq!(self.count, 0);
98 return;
99 };
100
101 let double_click_timeout =
102 Duration::from_millis(pref!(dom_document_dblclick_timeout) as u64);
103 let double_click_distance_threshold = pref!(dom_document_dblclick_dist) as u64;
104
105 let line = point_in_frame - previous_point;
107 let distance = (line.dot(line) as f64).sqrt();
108 if previous_button != button ||
109 Instant::now().duration_since(previous_time) > double_click_timeout ||
110 distance > double_click_distance_threshold as f64
111 {
112 self.count = 0;
113 self.time = None;
114 self.point = None;
115 }
116 }
117
118 fn increment_click_count(
119 &mut self,
120 button: MouseButton,
121 point: Point2D<f32, CSSPixel>,
122 ) -> usize {
123 self.time = Some(Instant::now());
124 self.point = Some(point);
125 self.button = Some(button);
126 self.count += 1;
127 self.count
128 }
129}
130
131#[derive(JSTraceable, MallocSizeOf)]
135#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
136pub(crate) struct DocumentEventHandler {
137 window: Dom<Window>,
139 #[no_trace]
141 #[ignore_malloc_size_of = "InputEvent contains data from outside crates"]
142 pending_input_events: DomRefCell<Vec<ConstellationInputEvent>>,
143 mouse_move_event_index: DomRefCell<Option<usize>>,
145 click_counting_info: DomRefCell<ClickCountingInfo>,
147 #[no_trace]
148 last_mouse_button_down_point: Cell<Option<Point2D<f32, CSSPixel>>>,
149 down_button_count: Cell<u32>,
152 current_hover_target: MutNullableDom<Element>,
154 most_recently_clicked_element: MutNullableDom<Element>,
156 #[no_trace]
158 most_recent_mousemove_point: Cell<Option<Point2D<f32, CSSPixel>>>,
159 #[no_trace]
162 current_cursor: Cell<Option<Cursor>>,
163 active_touch_points: DomRefCell<Vec<Dom<Touch>>>,
165 #[no_trace]
167 active_keyboard_modifiers: Cell<Modifiers>,
168 active_pointer_ids: DomRefCell<FxHashMap<i32, i32>>,
170 next_touch_pointer_id: Cell<i32>,
172}
173
174impl DocumentEventHandler {
175 pub(crate) fn new(window: &Window) -> Self {
176 Self {
177 window: Dom::from_ref(window),
178 pending_input_events: Default::default(),
179 mouse_move_event_index: Default::default(),
180 click_counting_info: Default::default(),
181 last_mouse_button_down_point: Default::default(),
182 down_button_count: Cell::new(0),
183 current_hover_target: Default::default(),
184 most_recently_clicked_element: Default::default(),
185 most_recent_mousemove_point: Default::default(),
186 current_cursor: Default::default(),
187 active_touch_points: Default::default(),
188 active_keyboard_modifiers: Default::default(),
189 active_pointer_ids: Default::default(),
190 next_touch_pointer_id: Cell::new(1),
191 }
192 }
193
194 pub(crate) fn note_pending_input_event(&self, event: ConstellationInputEvent) {
196 let mut pending_input_events = self.pending_input_events.borrow_mut();
197 if matches!(event.event.event, InputEvent::MouseMove(..)) {
198 if let Some(mouse_move_event) = self
200 .mouse_move_event_index
201 .borrow()
202 .and_then(|index| pending_input_events.get_mut(index))
203 {
204 *mouse_move_event = event;
205 return;
206 }
207
208 *self.mouse_move_event_index.borrow_mut() = Some(pending_input_events.len());
209 }
210
211 pending_input_events.push(event);
212 }
213
214 pub(crate) fn has_pending_input_events(&self) -> bool {
217 !self.pending_input_events.borrow().is_empty()
218 }
219
220 pub(crate) fn alternate_action_keyboard_modifier_active(&self) -> bool {
221 #[cfg(target_os = "macos")]
222 {
223 self.active_keyboard_modifiers
224 .get()
225 .contains(Modifiers::META)
226 }
227 #[cfg(not(target_os = "macos"))]
228 {
229 self.active_keyboard_modifiers
230 .get()
231 .contains(Modifiers::CONTROL)
232 }
233 }
234
235 pub(crate) fn handle_pending_input_events(&self, can_gc: CanGc) {
236 let _realm = enter_realm(&*self.window);
237
238 *self.mouse_move_event_index.borrow_mut() = None;
240 let pending_input_events = mem::take(&mut *self.pending_input_events.borrow_mut());
241
242 for event in pending_input_events {
243 self.active_keyboard_modifiers
244 .set(event.active_keyboard_modifiers);
245
246 let result = match event.event.event.clone() {
252 InputEvent::MouseButton(mouse_button_event) => {
253 self.handle_native_mouse_button_event(mouse_button_event, &event, can_gc);
254 InputEventResult::default()
255 },
256 InputEvent::MouseMove(_) => {
257 self.handle_native_mouse_move_event(&event, can_gc);
258 InputEventResult::default()
259 },
260 InputEvent::MouseLeftViewport(mouse_leave_event) => {
261 self.handle_mouse_left_viewport_event(&event, &mouse_leave_event, can_gc);
262 InputEventResult::default()
263 },
264 InputEvent::Touch(touch_event) => {
265 self.handle_touch_event(touch_event, &event, can_gc)
266 },
267 InputEvent::Wheel(wheel_event) => {
268 self.handle_wheel_event(wheel_event, &event, can_gc)
269 },
270 InputEvent::Keyboard(keyboard_event) => {
271 self.handle_keyboard_event(keyboard_event, can_gc)
272 },
273 InputEvent::Ime(ime_event) => self.handle_ime_event(ime_event, can_gc),
274 #[cfg(feature = "gamepad")]
275 InputEvent::Gamepad(gamepad_event) => {
276 self.handle_gamepad_event(gamepad_event);
277 InputEventResult::default()
278 },
279 InputEvent::EditingAction(editing_action_event) => {
280 self.handle_editing_action(None, editing_action_event, can_gc)
281 },
282 };
283
284 self.notify_embedder_that_event_was_handled(event.event, result);
285 }
286 }
287
288 fn notify_embedder_that_event_was_handled(
289 &self,
290 event: InputEventAndId,
291 result: InputEventResult,
292 ) {
293 let id = event.id;
296 let trusted_window = Trusted::new(&*self.window);
297 self.window
298 .as_global_scope()
299 .task_manager()
300 .dom_manipulation_task_source()
301 .queue(task!(notify_webdriver_input_event_completed: move || {
302 let window = trusted_window.root();
303 window.send_to_embedder(
304 EmbedderMsg::InputEventHandled(window.webview_id(), id, result));
305 }));
306 }
307
308 pub(crate) fn set_cursor(&self, cursor: Option<Cursor>) {
309 if cursor == self.current_cursor.get() {
310 return;
311 }
312 self.current_cursor.set(cursor);
313 self.window.send_to_embedder(EmbedderMsg::SetCursor(
314 self.window.webview_id(),
315 cursor.unwrap_or_default(),
316 ));
317 }
318
319 fn handle_mouse_left_viewport_event(
320 &self,
321 input_event: &ConstellationInputEvent,
322 mouse_leave_event: &MouseLeftViewportEvent,
323 can_gc: CanGc,
324 ) {
325 if let Some(current_hover_target) = self.current_hover_target.get() {
326 let current_hover_target = current_hover_target.upcast::<Node>();
327 for element in current_hover_target
328 .inclusive_ancestors(ShadowIncluding::Yes)
329 .filter_map(DomRoot::downcast::<Element>)
330 {
331 element.set_hover_state(false);
332 element.set_active_state(false);
333 }
334
335 if let Some(hit_test_result) = self
336 .most_recent_mousemove_point
337 .get()
338 .and_then(|point| self.window.hit_test_from_point_in_viewport(point))
339 {
340 MouseEvent::new_for_platform_motion_event(
341 &self.window,
342 FireMouseEventType::Out,
343 &hit_test_result,
344 input_event,
345 can_gc,
346 )
347 .upcast::<Event>()
348 .fire(current_hover_target.upcast(), can_gc);
349 self.handle_mouse_enter_leave_event(
350 DomRoot::from_ref(current_hover_target),
351 None,
352 FireMouseEventType::Leave,
353 &hit_test_result,
354 input_event,
355 can_gc,
356 );
357 }
358 }
359
360 if !mouse_leave_event.focus_moving_to_another_iframe {
365 self.window
369 .send_to_embedder(EmbedderMsg::Status(self.window.webview_id(), None));
370 self.set_cursor(None);
371 } else {
372 self.current_cursor.set(None);
373 }
374
375 self.current_hover_target.set(None);
376 self.most_recent_mousemove_point.set(None);
377 }
378
379 fn handle_mouse_enter_leave_event(
380 &self,
381 event_target: DomRoot<Node>,
382 related_target: Option<DomRoot<Node>>,
383 event_type: FireMouseEventType,
384 hit_test_result: &HitTestResult,
385 input_event: &ConstellationInputEvent,
386 can_gc: CanGc,
387 ) {
388 assert!(matches!(
389 event_type,
390 FireMouseEventType::Enter | FireMouseEventType::Leave
391 ));
392
393 let common_ancestor = match related_target.as_ref() {
394 Some(related_target) => event_target
395 .common_ancestor(related_target, ShadowIncluding::Yes)
396 .unwrap_or_else(|| DomRoot::from_ref(&*event_target)),
397 None => DomRoot::from_ref(&*event_target),
398 };
399
400 let mut targets = vec![];
403 let mut current = Some(event_target);
404 while let Some(node) = current {
405 if node == common_ancestor {
406 break;
407 }
408 current = node.GetParentNode();
409 targets.push(node);
410 }
411
412 if event_type == FireMouseEventType::Enter {
415 targets = targets.into_iter().rev().collect();
416 }
417
418 for target in targets {
419 MouseEvent::new_for_platform_motion_event(
420 &self.window,
421 event_type,
422 hit_test_result,
423 input_event,
424 can_gc,
425 )
426 .upcast::<Event>()
427 .fire(target.upcast(), can_gc);
428 }
429 }
430
431 fn handle_native_mouse_move_event(&self, input_event: &ConstellationInputEvent, can_gc: CanGc) {
433 let Some(hit_test_result) = self.window.hit_test_from_input_event(input_event) else {
435 return;
436 };
437
438 let old_mouse_move_point = self
439 .most_recent_mousemove_point
440 .replace(Some(hit_test_result.point_in_frame));
441 if old_mouse_move_point == Some(hit_test_result.point_in_frame) {
442 return;
443 }
444
445 self.set_cursor(Some(hit_test_result.cursor));
447
448 let Some(new_target) = hit_test_result
449 .node
450 .inclusive_ancestors(ShadowIncluding::Yes)
451 .find_map(DomRoot::downcast::<Element>)
452 else {
453 return;
454 };
455
456 let target_has_changed = self
457 .current_hover_target
458 .get()
459 .is_none_or(|old_target| old_target != new_target);
460
461 if target_has_changed {
464 if let Some(old_target) = self.current_hover_target.get() {
466 let old_target_is_ancestor_of_new_target = old_target
467 .upcast::<Node>()
468 .is_ancestor_of(new_target.upcast::<Node>());
469
470 if !old_target_is_ancestor_of_new_target {
473 for element in old_target
474 .upcast::<Node>()
475 .inclusive_ancestors(ShadowIncluding::No)
476 .filter_map(DomRoot::downcast::<Element>)
477 {
478 element.set_hover_state(false);
479 element.set_active_state(false);
480 }
481 }
482
483 MouseEvent::new_for_platform_motion_event(
484 &self.window,
485 FireMouseEventType::Out,
486 &hit_test_result,
487 input_event,
488 can_gc,
489 )
490 .upcast::<Event>()
491 .fire(old_target.upcast(), can_gc);
492
493 if !old_target_is_ancestor_of_new_target {
494 let event_target = DomRoot::from_ref(old_target.upcast::<Node>());
495 let moving_into = Some(DomRoot::from_ref(new_target.upcast::<Node>()));
496 self.handle_mouse_enter_leave_event(
497 event_target,
498 moving_into,
499 FireMouseEventType::Leave,
500 &hit_test_result,
501 input_event,
502 can_gc,
503 );
504 }
505 }
506
507 for element in new_target
509 .upcast::<Node>()
510 .inclusive_ancestors(ShadowIncluding::Yes)
511 .filter_map(DomRoot::downcast::<Element>)
512 {
513 element.set_hover_state(true);
514 }
515
516 MouseEvent::new_for_platform_motion_event(
517 &self.window,
518 FireMouseEventType::Over,
519 &hit_test_result,
520 input_event,
521 can_gc,
522 )
523 .upcast::<Event>()
524 .dispatch(new_target.upcast(), false, can_gc);
525
526 let moving_from = self
527 .current_hover_target
528 .get()
529 .map(|old_target| DomRoot::from_ref(old_target.upcast::<Node>()));
530 let event_target = DomRoot::from_ref(new_target.upcast::<Node>());
531 self.handle_mouse_enter_leave_event(
532 event_target,
533 moving_from,
534 FireMouseEventType::Enter,
535 &hit_test_result,
536 input_event,
537 can_gc,
538 );
539 }
540
541 let mouse_event = MouseEvent::new_for_platform_motion_event(
544 &self.window,
545 FireMouseEventType::Move,
546 &hit_test_result,
547 input_event,
548 can_gc,
549 );
550
551 let pointer_event = mouse_event.to_pointer_event("pointermove", can_gc);
553 pointer_event
554 .upcast::<Event>()
555 .fire(new_target.upcast(), can_gc);
556
557 mouse_event
560 .upcast::<Event>()
561 .fire(new_target.upcast(), can_gc);
562
563 self.update_current_hover_target_and_status(Some(new_target));
564 }
565
566 fn update_current_hover_target_and_status(&self, new_hover_target: Option<DomRoot<Element>>) {
567 let current_hover_target = self.current_hover_target.get();
568 if current_hover_target == new_hover_target {
569 return;
570 }
571
572 let previous_hover_target = self.current_hover_target.get();
573 self.current_hover_target.set(new_hover_target.as_deref());
574
575 if let Some(target) = self.current_hover_target.get() {
578 if let Some(anchor) = target
579 .upcast::<Node>()
580 .inclusive_ancestors(ShadowIncluding::Yes)
581 .find_map(DomRoot::downcast::<HTMLAnchorElement>)
582 {
583 let status = anchor
584 .full_href_url_for_user_interface()
585 .map(|url| url.to_string());
586 self.window
587 .send_to_embedder(EmbedderMsg::Status(self.window.webview_id(), status));
588 return;
589 }
590 }
591
592 if previous_hover_target.is_none_or(|previous_hover_target| {
597 previous_hover_target
598 .upcast::<Node>()
599 .inclusive_ancestors(ShadowIncluding::Yes)
600 .any(|node| node.is::<HTMLAnchorElement>())
601 }) {
602 self.window
603 .send_to_embedder(EmbedderMsg::Status(self.window.webview_id(), None));
604 }
605 }
606
607 pub(crate) fn handle_refresh_cursor(&self) {
608 let Some(most_recent_mousemove_point) = self.most_recent_mousemove_point.get() else {
609 return;
610 };
611
612 let Some(hit_test_result) = self
613 .window
614 .hit_test_from_point_in_viewport(most_recent_mousemove_point)
615 else {
616 return;
617 };
618
619 self.set_cursor(Some(hit_test_result.cursor));
620 }
621
622 fn handle_native_mouse_button_event(
625 &self,
626 event: MouseButtonEvent,
627 input_event: &ConstellationInputEvent,
628 can_gc: CanGc,
629 ) {
630 let Some(hit_test_result) = self.window.hit_test_from_input_event(input_event) else {
632 return;
633 };
634
635 debug!(
636 "{:?}: at {:?}",
637 event.action, hit_test_result.point_in_frame
638 );
639
640 let Some(element) = hit_test_result
641 .node
642 .inclusive_ancestors(ShadowIncluding::Yes)
643 .find_map(DomRoot::downcast::<Element>)
644 else {
645 return;
646 };
647
648 let node = element.upcast::<Node>();
649 debug!("{:?} on {:?}", event.action, node.debug_str());
650
651 if element.is_actually_disabled() {
655 return;
656 }
657
658 let mouse_event_type_string = match event.action {
659 embedder_traits::MouseButtonAction::Up => "mouseup",
660 embedder_traits::MouseButtonAction::Down => "mousedown",
661 };
662
663 if event.action == MouseButtonAction::Down {
670 self.click_counting_info
671 .borrow_mut()
672 .reset_click_count_if_necessary(event.button, hit_test_result.point_in_frame);
673 }
674
675 let dom_event = DomRoot::upcast::<Event>(MouseEvent::for_platform_button_event(
676 mouse_event_type_string,
677 event,
678 input_event.pressed_mouse_buttons,
679 &self.window,
680 &hit_test_result,
681 input_event.active_keyboard_modifiers,
682 self.click_counting_info.borrow().count + 1,
683 can_gc,
684 ));
685
686 let activatable = element.as_maybe_activatable();
687 match event.action {
688 MouseButtonAction::Down => {
689 self.last_mouse_button_down_point
690 .set(Some(hit_test_result.point_in_frame));
691
692 if let Some(a) = activatable {
693 a.enter_formal_activation_state();
694 }
695
696 let down_button_count = self.down_button_count.get();
698
699 let event_type = if down_button_count == 0 {
700 "pointerdown"
701 } else {
702 "pointermove"
703 };
704 let pointer_event = dom_event
705 .downcast::<MouseEvent>()
706 .unwrap()
707 .to_pointer_event(event_type, can_gc);
708
709 pointer_event.upcast::<Event>().fire(node.upcast(), can_gc);
710
711 self.down_button_count.set(down_button_count + 1);
712
713 let target_el = element.find_focusable_shadow_host_if_necessary();
718
719 let document = self.window.Document();
720 document.begin_focus_transaction();
721
722 document.request_focus(None, FocusInitiator::Local, can_gc);
724 document.request_focus(target_el.as_deref(), FocusInitiator::Local, can_gc);
725
726 let result = dom_event.dispatch(node.upcast(), false, can_gc);
728
729 if result && document.has_focus_transaction() {
732 document.commit_focus_transaction(FocusInitiator::Local, can_gc);
733 }
734
735 if let MouseButton::Right = event.button {
738 self.maybe_show_context_menu(
739 node.upcast(),
740 &hit_test_result,
741 input_event,
742 can_gc,
743 );
744 }
745 },
746 MouseButtonAction::Up => {
748 if let Some(a) = activatable {
749 a.exit_formal_activation_state();
750 }
751
752 let down_button_count = self.down_button_count.get();
754
755 if down_button_count > 0 {
756 self.down_button_count.set(down_button_count - 1);
757 }
758
759 let event_type = if down_button_count == 0 {
760 "pointerup"
761 } else {
762 "pointermove"
763 };
764 let pointer_event = dom_event
765 .downcast::<MouseEvent>()
766 .unwrap()
767 .to_pointer_event(event_type, can_gc);
768
769 pointer_event.upcast::<Event>().fire(node.upcast(), can_gc);
770
771 dom_event.dispatch(node.upcast(), false, can_gc);
773
774 self.click_counting_info
778 .borrow_mut()
779 .increment_click_count(event.button, hit_test_result.point_in_frame);
780
781 self.maybe_trigger_click_for_mouse_button_down_event(
782 event,
783 input_event,
784 &hit_test_result,
785 &element,
786 can_gc,
787 );
788 },
789 }
790 }
791
792 fn maybe_trigger_click_for_mouse_button_down_event(
795 &self,
796 event: MouseButtonEvent,
797 input_event: &ConstellationInputEvent,
798 hit_test_result: &HitTestResult,
799 element: &Element,
800 can_gc: CanGc,
801 ) {
802 if event.button != MouseButton::Left {
803 return;
804 }
805
806 let Some(last_mouse_button_down_point) = self.last_mouse_button_down_point.take() else {
807 return;
808 };
809
810 let distance = last_mouse_button_down_point.distance_to(hit_test_result.point_in_frame);
811 let maximum_click_distance = 10.0 * self.window.device_pixel_ratio().get();
812 if distance > maximum_click_distance {
813 return;
814 }
815
816 let delegated = element.find_focusable_shadow_host_if_necessary();
821 let element = delegated.as_deref().unwrap_or(element);
822 self.most_recently_clicked_element.set(Some(element));
823
824 let click_count = self.click_counting_info.borrow().count;
825 element.set_click_in_progress(true);
826 MouseEvent::for_platform_button_event(
827 "click",
828 event,
829 input_event.pressed_mouse_buttons,
830 &self.window,
831 hit_test_result,
832 input_event.active_keyboard_modifiers,
833 click_count,
834 can_gc,
835 )
836 .upcast::<Event>()
837 .dispatch(element.upcast(), false, can_gc);
838 element.set_click_in_progress(false);
839
840 if click_count % 2 == 0 {
849 MouseEvent::for_platform_button_event(
850 "dblclick",
851 event,
852 input_event.pressed_mouse_buttons,
853 &self.window,
854 hit_test_result,
855 input_event.active_keyboard_modifiers,
856 2,
857 can_gc,
858 )
859 .upcast::<Event>()
860 .dispatch(element.upcast(), false, can_gc);
861 }
862 }
863
864 fn maybe_show_context_menu(
866 &self,
867 target: &EventTarget,
868 hit_test_result: &HitTestResult,
869 input_event: &ConstellationInputEvent,
870 can_gc: CanGc,
871 ) {
872 let menu_event = PointerEvent::new(
874 &self.window, DOMString::from("contextmenu"), EventBubbles::Bubbles, EventCancelable::Cancelable, Some(&self.window), 0, hit_test_result.point_in_frame.to_i32(),
881 hit_test_result.point_in_frame.to_i32(),
882 hit_test_result
883 .point_relative_to_initial_containing_block
884 .to_i32(),
885 input_event.active_keyboard_modifiers,
886 2i16, input_event.pressed_mouse_buttons,
888 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,
905 );
906
907 let result = menu_event.upcast::<Event>().fire(target, can_gc);
909
910 if result {
912 self.window
913 .Document()
914 .embedder_controls()
915 .show_context_menu(hit_test_result);
916 };
917 }
918
919 fn handle_touch_event(
920 &self,
921 event: EmbedderTouchEvent,
922 input_event: &ConstellationInputEvent,
923 can_gc: CanGc,
924 ) -> InputEventResult {
925 let Some(hit_test_result) = self.window.hit_test_from_input_event(input_event) else {
927 self.update_active_touch_points_when_early_return(event);
928 return Default::default();
929 };
930
931 let TouchId(identifier) = event.touch_id;
932
933 let Some(element) = hit_test_result
934 .node
935 .inclusive_ancestors(ShadowIncluding::Yes)
936 .find_map(DomRoot::downcast::<Element>)
937 else {
938 self.update_active_touch_points_when_early_return(event);
939 return Default::default();
940 };
941
942 let current_target = DomRoot::upcast::<EventTarget>(element);
943 let window = &*self.window;
944
945 let client_x = Finite::wrap(hit_test_result.point_in_frame.x as f64);
946 let client_y = Finite::wrap(hit_test_result.point_in_frame.y as f64);
947 let page_x =
948 Finite::wrap(hit_test_result.point_in_frame.x as f64 + window.PageXOffset() as f64);
949 let page_y =
950 Finite::wrap(hit_test_result.point_in_frame.y as f64 + window.PageYOffset() as f64);
951
952 let pointer_touch = Touch::new(
954 window,
955 identifier,
956 ¤t_target,
957 client_x,
958 client_y, client_x,
960 client_y,
961 page_x,
962 page_y,
963 can_gc,
964 );
965
966 let pointer_event_type = match event.event_type {
968 TouchEventType::Down => "pointerdown",
969 TouchEventType::Move => "pointermove",
970 TouchEventType::Up => "pointerup",
971 TouchEventType::Cancel => "pointercancel",
972 };
973
974 let pointer_id = self.get_or_create_pointer_id_for_touch(identifier);
976 let is_primary = self.is_primary_pointer(pointer_id);
977
978 let pointer_event = pointer_touch.to_pointer_event(
979 window,
980 pointer_event_type,
981 pointer_id,
982 is_primary,
983 input_event.active_keyboard_modifiers,
984 event.is_cancelable(),
985 Some(hit_test_result.point_in_node),
986 can_gc,
987 );
988 pointer_event
989 .upcast::<Event>()
990 .fire(¤t_target, can_gc);
991
992 let (touch_dispatch_target, changed_touch) = match event.event_type {
993 TouchEventType::Down => {
994 self.active_touch_points
996 .borrow_mut()
997 .push(Dom::from_ref(&*pointer_touch));
998 (current_target, pointer_touch)
999 },
1000 _ => {
1001 let mut active_touch_points = self.active_touch_points.borrow_mut();
1007 let Some(index) = active_touch_points
1008 .iter()
1009 .position(|point| point.Identifier() == identifier)
1010 else {
1011 warn!("No active touch point for {:?}", event.event_type);
1012 return Default::default();
1013 };
1014 let original_target = active_touch_points[index].Target();
1016
1017 let touch_with_touchstart_target = Touch::new(
1018 window,
1019 identifier,
1020 &original_target,
1021 client_x,
1022 client_y,
1023 client_x,
1024 client_y,
1025 page_x,
1026 page_y,
1027 can_gc,
1028 );
1029
1030 match event.event_type {
1032 TouchEventType::Move => {
1033 active_touch_points[index] = Dom::from_ref(&*touch_with_touchstart_target);
1034 },
1035 TouchEventType::Up | TouchEventType::Cancel => {
1036 active_touch_points.swap_remove(index);
1037 self.remove_pointer_id_for_touch(identifier);
1038 },
1039 TouchEventType::Down => unreachable!("Should have been handled above"),
1040 }
1041 (original_target, touch_with_touchstart_target)
1042 },
1043 };
1044
1045 rooted_vec!(let mut target_touches);
1046 target_touches.extend(
1047 self.active_touch_points
1048 .borrow()
1049 .iter()
1050 .filter(|touch| touch.Target() == touch_dispatch_target)
1051 .cloned(),
1052 );
1053
1054 let event_name = match event.event_type {
1055 TouchEventType::Down => "touchstart",
1056 TouchEventType::Move => "touchmove",
1057 TouchEventType::Up => "touchend",
1058 TouchEventType::Cancel => "touchcancel",
1059 };
1060
1061 let touch_event = TouchEvent::new(
1062 window,
1063 DOMString::from(event_name),
1064 EventBubbles::Bubbles,
1065 EventCancelable::from(event.is_cancelable()),
1066 EventComposed::Composed,
1067 Some(window),
1068 0i32,
1069 &TouchList::new(window, self.active_touch_points.borrow().r(), can_gc),
1070 &TouchList::new(window, from_ref(&&*changed_touch), can_gc),
1071 &TouchList::new(window, target_touches.r(), can_gc),
1072 false,
1074 false,
1075 false,
1076 false,
1077 can_gc,
1078 );
1079 let event = touch_event.upcast::<Event>();
1080 event.fire(&touch_dispatch_target, can_gc);
1081 event.flags().into()
1082 }
1083
1084 fn update_active_touch_points_when_early_return(&self, event: EmbedderTouchEvent) {
1093 match event.event_type {
1094 TouchEventType::Down | TouchEventType::Move => {},
1095 TouchEventType::Up | TouchEventType::Cancel => {
1096 let mut active_touch_points = self.active_touch_points.borrow_mut();
1097 if let Some(index) = active_touch_points
1098 .iter()
1099 .position(|t| t.Identifier() == event.touch_id.0)
1100 {
1101 active_touch_points.swap_remove(index);
1102 self.remove_pointer_id_for_touch(event.touch_id.0);
1103 } else {
1104 warn!(
1105 "Received {:?} for a non-active touch point {}",
1106 event.event_type, event.touch_id.0
1107 );
1108 }
1109 },
1110 }
1111 }
1112
1113 fn handle_keyboard_event(
1115 &self,
1116 keyboard_event: EmbedderKeyboardEvent,
1117 can_gc: CanGc,
1118 ) -> InputEventResult {
1119 let document = self.window.Document();
1120 let focused = document.get_focused_element();
1121 let body = document.GetBody();
1122
1123 let target = match (&focused, &body) {
1124 (Some(focused), _) => focused.upcast(),
1125 (&None, Some(body)) => body.upcast(),
1126 (&None, &None) => self.window.upcast(),
1127 };
1128
1129 let keyevent = KeyboardEvent::new(
1130 &self.window,
1131 DOMString::from(keyboard_event.event.state.event_type()),
1132 true,
1133 true,
1134 Some(&self.window),
1135 0,
1136 keyboard_event.event.key.clone(),
1137 DOMString::from(keyboard_event.event.code.to_string()),
1138 keyboard_event.event.location as u32,
1139 keyboard_event.event.repeat,
1140 keyboard_event.event.is_composing,
1141 keyboard_event.event.modifiers,
1142 0,
1143 keyboard_event.event.key.legacy_keycode(),
1144 can_gc,
1145 );
1146
1147 let event = keyevent.upcast::<Event>();
1148 event.fire(target, can_gc);
1149
1150 let mut flags = event.flags();
1151 if flags.contains(EventFlags::Canceled) {
1152 return flags.into();
1153 }
1154
1155 let is_character_value_key = matches!(
1161 keyboard_event.event.key,
1162 Key::Character(_) | Key::Named(NamedKey::Enter)
1163 );
1164 if keyboard_event.event.state == KeyState::Down &&
1165 is_character_value_key &&
1166 !keyboard_event.event.is_composing
1167 {
1168 let keypress_event = KeyboardEvent::new(
1170 &self.window,
1171 DOMString::from("keypress"),
1172 true,
1173 true,
1174 Some(&self.window),
1175 0,
1176 keyboard_event.event.key.clone(),
1177 DOMString::from(keyboard_event.event.code.to_string()),
1178 keyboard_event.event.location as u32,
1179 keyboard_event.event.repeat,
1180 keyboard_event.event.is_composing,
1181 keyboard_event.event.modifiers,
1182 keyboard_event.event.key.legacy_charcode(),
1183 0,
1184 can_gc,
1185 );
1186 let event = keypress_event.upcast::<Event>();
1187 event.fire(target, can_gc);
1188 flags = event.flags();
1189 }
1190
1191 if flags.contains(EventFlags::Canceled) {
1192 return flags.into();
1193 }
1194
1195 if (keyboard_event.event.key == Key::Named(NamedKey::Enter) ||
1201 keyboard_event.event.code == Code::Space) &&
1202 keyboard_event.event.state == KeyState::Up
1203 {
1204 if let Some(elem) = target.downcast::<Element>() {
1205 elem.upcast::<Node>()
1206 .fire_synthetic_pointer_event_not_trusted(DOMString::from("click"), can_gc);
1207 }
1208 }
1209
1210 flags.into()
1211 }
1212
1213 fn handle_ime_event(&self, event: ImeEvent, can_gc: CanGc) -> InputEventResult {
1214 let document = self.window.Document();
1215 let composition_event = match event {
1216 ImeEvent::Dismissed => {
1217 document.request_focus(
1218 document.GetBody().as_ref().map(|e| e.upcast()),
1219 FocusInitiator::Local,
1220 can_gc,
1221 );
1222 return Default::default();
1223 },
1224 ImeEvent::Composition(composition_event) => composition_event,
1225 };
1226
1227 let focused = document.get_focused_element();
1232 let target = if let Some(elem) = &focused {
1233 elem.upcast()
1234 } else {
1235 return Default::default();
1237 };
1238
1239 let cancelable = composition_event.state == keyboard_types::CompositionState::Start;
1240 let event = CompositionEvent::new(
1241 &self.window,
1242 DOMString::from(composition_event.state.event_type()),
1243 true,
1244 cancelable,
1245 Some(&self.window),
1246 0,
1247 DOMString::from(composition_event.data),
1248 can_gc,
1249 );
1250
1251 let event = event.upcast::<Event>();
1252 event.fire(target, can_gc);
1253 event.flags().into()
1254 }
1255
1256 fn handle_wheel_event(
1257 &self,
1258 event: EmbedderWheelEvent,
1259 input_event: &ConstellationInputEvent,
1260 can_gc: CanGc,
1261 ) -> InputEventResult {
1262 let Some(hit_test_result) = self.window.hit_test_from_input_event(input_event) else {
1264 return Default::default();
1265 };
1266
1267 let Some(el) = hit_test_result
1268 .node
1269 .inclusive_ancestors(ShadowIncluding::Yes)
1270 .find_map(DomRoot::downcast::<Element>)
1271 else {
1272 return Default::default();
1273 };
1274
1275 let node = el.upcast::<Node>();
1276 let wheel_event_type_string = "wheel".to_owned();
1277 debug!(
1278 "{}: on {:?} at {:?}",
1279 wheel_event_type_string,
1280 node.debug_str(),
1281 hit_test_result.point_in_frame
1282 );
1283
1284 let dom_event = WheelEvent::new(
1286 &self.window,
1287 DOMString::from(wheel_event_type_string),
1288 EventBubbles::Bubbles,
1289 EventCancelable::Cancelable,
1290 Some(&self.window),
1291 0i32,
1292 hit_test_result.point_in_frame.to_i32(),
1293 hit_test_result.point_in_frame.to_i32(),
1294 hit_test_result
1295 .point_relative_to_initial_containing_block
1296 .to_i32(),
1297 input_event.active_keyboard_modifiers,
1298 0i16,
1299 input_event.pressed_mouse_buttons,
1300 None,
1301 None,
1302 Finite::wrap(-event.delta.x),
1307 Finite::wrap(-event.delta.y),
1308 Finite::wrap(-event.delta.z),
1309 event.delta.mode as u32,
1310 can_gc,
1311 );
1312
1313 let dom_event = dom_event.upcast::<Event>();
1314 dom_event.set_trusted(true);
1315 dom_event.fire(node.upcast(), can_gc);
1316
1317 dom_event.flags().into()
1318 }
1319
1320 #[cfg(feature = "gamepad")]
1321 fn handle_gamepad_event(&self, gamepad_event: EmbedderGamepadEvent) {
1322 match gamepad_event {
1323 EmbedderGamepadEvent::Connected(index, name, bounds, supported_haptic_effects) => {
1324 self.handle_gamepad_connect(
1325 index.0,
1326 name,
1327 bounds.axis_bounds,
1328 bounds.button_bounds,
1329 supported_haptic_effects,
1330 );
1331 },
1332 EmbedderGamepadEvent::Disconnected(index) => {
1333 self.handle_gamepad_disconnect(index.0);
1334 },
1335 EmbedderGamepadEvent::Updated(index, update_type) => {
1336 self.receive_new_gamepad_button_or_axis(index.0, update_type);
1337 },
1338 };
1339 }
1340
1341 #[cfg(feature = "gamepad")]
1343 fn handle_gamepad_connect(
1344 &self,
1345 _index: usize,
1349 name: String,
1350 axis_bounds: (f64, f64),
1351 button_bounds: (f64, f64),
1352 supported_haptic_effects: GamepadSupportedHapticEffects,
1353 ) {
1354 let trusted_window = Trusted::new(&*self.window);
1357 self.window
1358 .upcast::<GlobalScope>()
1359 .task_manager()
1360 .gamepad_task_source()
1361 .queue(task!(gamepad_connected: move || {
1362 let window = trusted_window.root();
1363
1364 let navigator = window.Navigator();
1365 let selected_index = navigator.select_gamepad_index();
1366 let gamepad = Gamepad::new(
1367 &window,
1368 selected_index,
1369 name,
1370 "standard".into(),
1371 axis_bounds,
1372 button_bounds,
1373 supported_haptic_effects,
1374 false,
1375 CanGc::note(),
1376 );
1377 navigator.set_gamepad(selected_index as usize, &gamepad, CanGc::note());
1378 }));
1379 }
1380
1381 #[cfg(feature = "gamepad")]
1383 fn handle_gamepad_disconnect(&self, index: usize) {
1384 let trusted_window = Trusted::new(&*self.window);
1385 self.window
1386 .upcast::<GlobalScope>()
1387 .task_manager()
1388 .gamepad_task_source()
1389 .queue(task!(gamepad_disconnected: move || {
1390 let window = trusted_window.root();
1391 let navigator = window.Navigator();
1392 if let Some(gamepad) = navigator.get_gamepad(index) {
1393 if window.Document().is_fully_active() {
1394 gamepad.update_connected(false, gamepad.exposed(), CanGc::note());
1395 navigator.remove_gamepad(index);
1396 }
1397 }
1398 }));
1399 }
1400
1401 #[cfg(feature = "gamepad")]
1403 fn receive_new_gamepad_button_or_axis(&self, index: usize, update_type: GamepadUpdateType) {
1404 let trusted_window = Trusted::new(&*self.window);
1405
1406 self.window.upcast::<GlobalScope>().task_manager().gamepad_task_source().queue(
1408 task!(update_gamepad_state: move || {
1409 let window = trusted_window.root();
1410 let navigator = window.Navigator();
1411 if let Some(gamepad) = navigator.get_gamepad(index) {
1412 let current_time = window.Performance().Now();
1413 gamepad.update_timestamp(*current_time);
1414 match update_type {
1415 GamepadUpdateType::Axis(index, value) => {
1416 gamepad.map_and_normalize_axes(index, value);
1417 },
1418 GamepadUpdateType::Button(index, value) => {
1419 gamepad.map_and_normalize_buttons(index, value);
1420 }
1421 };
1422 if !navigator.has_gamepad_gesture() && contains_user_gesture(update_type) {
1423 navigator.set_has_gamepad_gesture(true);
1424 navigator.GetGamepads()
1425 .iter()
1426 .filter_map(|g| g.as_ref())
1427 .for_each(|gamepad| {
1428 gamepad.set_exposed(true);
1429 gamepad.update_timestamp(*current_time);
1430 let new_gamepad = Trusted::new(&**gamepad);
1431 if window.Document().is_fully_active() {
1432 window.upcast::<GlobalScope>().task_manager().gamepad_task_source().queue(
1433 task!(update_gamepad_connect: move || {
1434 let gamepad = new_gamepad.root();
1435 gamepad.notify_event(GamepadEventType::Connected, CanGc::note());
1436 })
1437 );
1438 }
1439 });
1440 }
1441 }
1442 })
1443 );
1444 }
1445
1446 pub(crate) fn handle_editing_action(
1448 &self,
1449 element: Option<DomRoot<Element>>,
1450 action: EditingActionEvent,
1451 can_gc: CanGc,
1452 ) -> InputEventResult {
1453 let clipboard_event_type = match action {
1454 EditingActionEvent::Copy => ClipboardEventType::Copy,
1455 EditingActionEvent::Cut => ClipboardEventType::Cut,
1456 EditingActionEvent::Paste => ClipboardEventType::Paste,
1457 };
1458
1459 let script_triggered = false;
1461
1462 let script_may_access_clipboard = false;
1466
1467 if script_triggered && !script_may_access_clipboard {
1469 return InputEventResult::empty();
1470 }
1471
1472 let clipboard_event =
1474 self.fire_clipboard_event(element.clone(), clipboard_event_type, can_gc);
1475
1476 let event = clipboard_event.upcast::<Event>();
1479 if !event.IsTrusted() {
1480 return event.flags().into();
1481 }
1482
1483 if event.DefaultPrevented() {
1485 let event_type = event.Type();
1486 match_domstring_ascii!(event_type,
1487
1488 "copy" => {
1489 if let Some(clipboard_data) = clipboard_event.get_clipboard_data() {
1492 let drag_data_store =
1493 clipboard_data.data_store().expect("This shouldn't fail");
1494 self.write_content_to_the_clipboard(&drag_data_store);
1495 }
1496 },
1497 "cut" => {
1498 if let Some(clipboard_data) = clipboard_event.get_clipboard_data() {
1501 let drag_data_store =
1502 clipboard_data.data_store().expect("This shouldn't fail");
1503 self.write_content_to_the_clipboard(&drag_data_store);
1504 }
1505
1506 self.fire_clipboard_event(element, ClipboardEventType::Change, can_gc);
1508 },
1509 "paste" => (),
1513 _ => (),
1514 )
1515 }
1516
1517 event.flags().into()
1520 }
1521
1522 pub(crate) fn fire_clipboard_event(
1524 &self,
1525 target: Option<DomRoot<Element>>,
1526 clipboard_event_type: ClipboardEventType,
1527 can_gc: CanGc,
1528 ) -> DomRoot<ClipboardEvent> {
1529 let clipboard_event = ClipboardEvent::new(
1530 &self.window,
1531 None,
1532 DOMString::from(clipboard_event_type.as_str()),
1533 EventBubbles::Bubbles,
1534 EventCancelable::Cancelable,
1535 None,
1536 can_gc,
1537 );
1538
1539 let mut drag_data_store = DragDataStore::new();
1542
1543 let trusted = true;
1547
1548 let document = self.window.Document();
1550 let target = target.or(document.get_focused_element());
1551 let target = target
1552 .map(|target| DomRoot::from_ref(target.upcast()))
1553 .or_else(|| {
1554 document
1555 .GetBody()
1556 .map(|body| DomRoot::from_ref(body.upcast()))
1557 })
1558 .unwrap_or_else(|| DomRoot::from_ref(self.window.upcast()));
1559
1560 match clipboard_event_type {
1563 ClipboardEventType::Copy | ClipboardEventType::Cut => {
1564 drag_data_store.set_mode(Mode::ReadWrite);
1566 },
1567 ClipboardEventType::Paste => {
1568 let (callback, receiver) =
1569 GenericCallback::new_blocking().expect("Could not create callback");
1570 self.window.send_to_embedder(EmbedderMsg::GetClipboardText(
1571 self.window.webview_id(),
1572 callback,
1573 ));
1574 let text_contents = receiver
1575 .recv()
1576 .map(Result::unwrap_or_default)
1577 .unwrap_or_default();
1578
1579 drag_data_store.set_mode(Mode::ReadOnly);
1581 if trusted {
1583 let data = DOMString::from(text_contents.to_string());
1587 let type_ = DOMString::from("text/plain");
1588 let _ = drag_data_store.add(Kind::Text { data, type_ });
1589
1590 }
1596 },
1597 ClipboardEventType::Change => (),
1598 }
1599
1600 let clipboard_event_data = DataTransfer::new(
1602 &self.window,
1603 Rc::new(RefCell::new(Some(drag_data_store))),
1604 can_gc,
1605 );
1606
1607 clipboard_event.set_clipboard_data(Some(&clipboard_event_data));
1609
1610 let event = clipboard_event.upcast::<Event>();
1612 event.set_trusted(trusted);
1613
1614 event.set_composed(true);
1616
1617 event.dispatch(&target, false, can_gc);
1619
1620 DomRoot::from(clipboard_event)
1621 }
1622
1623 fn write_content_to_the_clipboard(&self, drag_data_store: &DragDataStore) {
1625 if drag_data_store.list_len() > 0 {
1627 self.window
1629 .send_to_embedder(EmbedderMsg::ClearClipboard(self.window.webview_id()));
1630 for item in drag_data_store.iter_item_list() {
1632 match item {
1633 Kind::Text { data, .. } => {
1634 self.window.send_to_embedder(EmbedderMsg::SetClipboardText(
1638 self.window.webview_id(),
1639 data.to_string(),
1640 ));
1641 },
1642 Kind::File { .. } => {
1643 },
1647 }
1648 }
1649 } else {
1650 if drag_data_store.clear_was_called {
1652 self.window
1654 .send_to_embedder(EmbedderMsg::ClearClipboard(self.window.webview_id()));
1655 }
1658 }
1659 }
1660
1661 #[expect(unsafe_code)]
1664 pub(crate) fn handle_embedder_scroll_event(&self, scrolled_node: ExternalScrollId) {
1665 let document = self.window.Document();
1667 if scrolled_node.is_root() {
1668 document.handle_viewport_scroll_event();
1669 } else {
1670 let Some(node_id) = node_id_from_scroll_id(scrolled_node.0 as usize) else {
1672 return;
1673 };
1674 let node = unsafe {
1675 node::from_untrusted_node_address(UntrustedNodeAddress::from_id(node_id))
1676 };
1677 let Some(element) = node
1678 .inclusive_ancestors(ShadowIncluding::Yes)
1679 .find_map(DomRoot::downcast::<Element>)
1680 else {
1681 return;
1682 };
1683
1684 document.handle_element_scroll_event(&element);
1685 }
1686 }
1687
1688 pub(crate) fn run_default_keyboard_event_handler(&self, event: &KeyboardEvent) {
1689 if event.upcast::<Event>().type_() != atom!("keydown") {
1690 return;
1691 }
1692 if !event.modifiers().is_empty() {
1693 return;
1694 }
1695 let scroll = match event.key() {
1696 Key::Named(NamedKey::ArrowDown) => KeyboardScroll::Down,
1697 Key::Named(NamedKey::ArrowLeft) => KeyboardScroll::Left,
1698 Key::Named(NamedKey::ArrowRight) => KeyboardScroll::Right,
1699 Key::Named(NamedKey::ArrowUp) => KeyboardScroll::Up,
1700 Key::Named(NamedKey::End) => KeyboardScroll::End,
1701 Key::Named(NamedKey::Home) => KeyboardScroll::Home,
1702 Key::Named(NamedKey::PageDown) => KeyboardScroll::PageDown,
1703 Key::Named(NamedKey::PageUp) => KeyboardScroll::PageUp,
1704 _ => return,
1705 };
1706 self.do_keyboard_scroll(scroll);
1707 }
1708
1709 pub(crate) fn do_keyboard_scroll(&self, scroll: KeyboardScroll) {
1710 let scroll_axis = match scroll {
1711 KeyboardScroll::Left | KeyboardScroll::Right => ScrollingBoxAxis::X,
1712 _ => ScrollingBoxAxis::Y,
1713 };
1714
1715 let document = self.window.Document();
1716 let mut scrolling_box = document
1717 .get_focused_element()
1718 .or(self.most_recently_clicked_element.get())
1719 .and_then(|element| element.scrolling_box(ScrollContainerQueryFlags::Inclusive))
1720 .unwrap_or_else(|| {
1721 document.viewport_scrolling_box(ScrollContainerQueryFlags::Inclusive)
1722 });
1723
1724 while !scrolling_box.can_keyboard_scroll_in_axis(scroll_axis) {
1725 if scrolling_box.is_viewport() {
1727 break;
1728 }
1729 let parent = scrolling_box.parent().unwrap_or_else(|| {
1730 document.viewport_scrolling_box(ScrollContainerQueryFlags::Inclusive)
1731 });
1732 scrolling_box = parent;
1733 }
1734
1735 let calculate_current_scroll_offset_and_delta = || {
1736 const LINE_HEIGHT: f32 = 76.0;
1737 const LINE_WIDTH: f32 = 76.0;
1738
1739 let current_scroll_offset = scrolling_box.scroll_position();
1740 (
1741 current_scroll_offset,
1742 match scroll {
1743 KeyboardScroll::Home => Vector2D::new(0.0, -current_scroll_offset.y),
1744 KeyboardScroll::End => Vector2D::new(
1745 0.0,
1746 -current_scroll_offset.y + scrolling_box.content_size().height -
1747 scrolling_box.size().height,
1748 ),
1749 KeyboardScroll::PageDown => {
1750 Vector2D::new(0.0, scrolling_box.size().height - 2.0 * LINE_HEIGHT)
1751 },
1752 KeyboardScroll::PageUp => {
1753 Vector2D::new(0.0, 2.0 * LINE_HEIGHT - scrolling_box.size().height)
1754 },
1755 KeyboardScroll::Up => Vector2D::new(0.0, -LINE_HEIGHT),
1756 KeyboardScroll::Down => Vector2D::new(0.0, LINE_HEIGHT),
1757 KeyboardScroll::Left => Vector2D::new(-LINE_WIDTH, 0.0),
1758 KeyboardScroll::Right => Vector2D::new(LINE_WIDTH, 0.0),
1759 },
1760 )
1761 };
1762
1763 let parent_pipeline = self.window.parent_info();
1767 if scrolling_box.is_viewport() && parent_pipeline.is_none() {
1768 let (_, delta) = calculate_current_scroll_offset_and_delta();
1769 self.window
1770 .paint_api()
1771 .scroll_viewport_by_delta(self.window.webview_id(), delta);
1772 }
1773
1774 if !scrolling_box.can_keyboard_scroll_in_axis(scroll_axis) {
1777 assert!(scrolling_box.is_viewport());
1778
1779 let window_proxy = document.window().window_proxy();
1780 if let Some(iframe) = window_proxy.frame_element() {
1781 let cx = GlobalScope::get_cx();
1784 let iframe_window = iframe.owner_window();
1785 let _ac = JSAutoRealm::new(*cx, iframe_window.reflector().get_jsobject().get());
1786 iframe_window
1787 .Document()
1788 .event_handler()
1789 .do_keyboard_scroll(scroll);
1790 } else if let Some(parent_pipeline) = parent_pipeline {
1791 document.window().send_to_constellation(
1795 ScriptToConstellationMessage::ForwardKeyboardScroll(parent_pipeline, scroll),
1796 );
1797 };
1798 return;
1799 }
1800
1801 let (current_scroll_offset, delta) = calculate_current_scroll_offset_and_delta();
1802 scrolling_box.scroll_to(delta + current_scroll_offset, ScrollBehavior::Auto);
1803 }
1804
1805 fn get_or_create_pointer_id_for_touch(&self, touch_id: i32) -> i32 {
1808 let mut active_pointer_ids = self.active_pointer_ids.borrow_mut();
1809
1810 if let Some(&pointer_id) = active_pointer_ids.get(&touch_id) {
1811 return pointer_id;
1812 }
1813
1814 let pointer_id = self.next_touch_pointer_id.get();
1815 active_pointer_ids.insert(touch_id, pointer_id);
1816 self.next_touch_pointer_id.set(pointer_id + 1);
1817 pointer_id
1818 }
1819
1820 fn remove_pointer_id_for_touch(&self, touch_id: i32) {
1822 self.active_pointer_ids.borrow_mut().remove(&touch_id);
1823 }
1824
1825 fn is_primary_pointer(&self, pointer_id: i32) -> bool {
1828 self.active_pointer_ids
1831 .borrow()
1832 .values()
1833 .min()
1834 .is_some_and(|primary_pointer| *primary_pointer == pointer_id)
1835 }
1836}