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 constellation_traits::{KeyboardScroll, ScriptToConstellationMessage};
13use embedder_traits::{
14 Cursor, EditingActionEvent, EmbedderMsg, GamepadEvent as EmbedderGamepadEvent,
15 GamepadSupportedHapticEffects, GamepadUpdateType, ImeEvent, InputEvent, InputEventAndId,
16 InputEventResult, KeyboardEvent as EmbedderKeyboardEvent, MouseButton, MouseButtonAction,
17 MouseButtonEvent, MouseLeftViewportEvent, ScrollEvent, TouchEvent as EmbedderTouchEvent,
18 TouchEventType, TouchId, UntrustedNodeAddress, WheelEvent as EmbedderWheelEvent,
19};
20use euclid::{Point2D, Vector2D};
21use ipc_channel::ipc;
22use js::jsapi::JSAutoRealm;
23use keyboard_types::{Code, Key, KeyState, Modifiers, NamedKey};
24use layout_api::{ScrollContainerQueryFlags, 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::{ScrollBehavior, WindowMethods};
32use script_bindings::inheritance::Castable;
33use script_bindings::match_domstring_ascii;
34use script_bindings::num::Finite;
35use script_bindings::reflector::DomObject;
36use script_bindings::root::{Dom, DomRoot, DomSlice};
37use script_bindings::script_runtime::CanGc;
38use script_bindings::str::DOMString;
39use script_traits::ConstellationInputEvent;
40use servo_config::pref;
41use style_traits::CSSPixel;
42
43use crate::dom::bindings::cell::DomRefCell;
44use crate::dom::bindings::refcounted::Trusted;
45use crate::dom::bindings::root::MutNullableDom;
46use crate::dom::clipboardevent::ClipboardEventType;
47use crate::dom::document::{FireMouseEventType, FocusInitiator};
48use crate::dom::event::{EventBubbles, EventCancelable, EventComposed, EventFlags};
49use crate::dom::gamepad::gamepad::{Gamepad, contains_user_gesture};
50use crate::dom::gamepad::gamepadevent::GamepadEventType;
51use crate::dom::inputevent::HitTestResult;
52use crate::dom::node::{self, Node, NodeTraits, ShadowIncluding};
53use crate::dom::pointerevent::PointerId;
54use crate::dom::scrolling_box::ScrollingBoxAxis;
55use crate::dom::types::{
56 ClipboardEvent, CompositionEvent, DataTransfer, Element, Event, EventTarget, GlobalScope,
57 HTMLAnchorElement, KeyboardEvent, MouseEvent, PointerEvent, Touch, TouchEvent, TouchList,
58 WheelEvent, Window,
59};
60use crate::drag_data_store::{DragDataStore, Kind, Mode};
61use crate::realms::enter_realm;
62
63#[derive(JSTraceable, MallocSizeOf)]
67#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
68pub(crate) struct DocumentEventHandler {
69 window: Dom<Window>,
71 #[no_trace]
73 #[ignore_malloc_size_of = "CompositorEvent contains data from outside crates"]
74 pending_input_events: DomRefCell<Vec<ConstellationInputEvent>>,
75 mouse_move_event_index: DomRefCell<Option<usize>>,
77 #[ignore_malloc_size_of = "Defined in std"]
79 #[no_trace]
80 last_click_info: DomRefCell<Option<(Instant, Point2D<f32, CSSPixel>)>>,
81 #[no_trace]
82 last_mouse_button_down_point: Cell<Option<Point2D<f32, CSSPixel>>>,
83 current_hover_target: MutNullableDom<Element>,
85 most_recently_clicked_element: MutNullableDom<Element>,
87 #[no_trace]
89 most_recent_mousemove_point: Cell<Option<Point2D<f32, CSSPixel>>>,
90 #[no_trace]
93 current_cursor: Cell<Option<Cursor>>,
94 active_touch_points: DomRefCell<Vec<Dom<Touch>>>,
96 #[no_trace]
98 active_keyboard_modifiers: Cell<Modifiers>,
99}
100
101impl DocumentEventHandler {
102 pub(crate) fn new(window: &Window) -> Self {
103 Self {
104 window: Dom::from_ref(window),
105 pending_input_events: Default::default(),
106 mouse_move_event_index: Default::default(),
107 last_click_info: Default::default(),
108 last_mouse_button_down_point: Default::default(),
109 current_hover_target: Default::default(),
110 most_recently_clicked_element: Default::default(),
111 most_recent_mousemove_point: Default::default(),
112 current_cursor: Default::default(),
113 active_touch_points: Default::default(),
114 active_keyboard_modifiers: Default::default(),
115 }
116 }
117
118 pub(crate) fn note_pending_input_event(&self, event: ConstellationInputEvent) {
120 let mut pending_compositor_events = self.pending_input_events.borrow_mut();
121 if matches!(event.event.event, InputEvent::MouseMove(..)) {
122 if let Some(mouse_move_event) = self
124 .mouse_move_event_index
125 .borrow()
126 .and_then(|index| pending_compositor_events.get_mut(index))
127 {
128 *mouse_move_event = event;
129 return;
130 }
131
132 *self.mouse_move_event_index.borrow_mut() = Some(pending_compositor_events.len());
133 }
134
135 pending_compositor_events.push(event);
136 }
137
138 pub(crate) fn has_pending_input_events(&self) -> bool {
141 !self.pending_input_events.borrow().is_empty()
142 }
143
144 pub(crate) fn alternate_action_keyboard_modifier_active(&self) -> bool {
145 #[cfg(target_os = "macos")]
146 {
147 self.active_keyboard_modifiers
148 .get()
149 .contains(Modifiers::META)
150 }
151 #[cfg(not(target_os = "macos"))]
152 {
153 self.active_keyboard_modifiers
154 .get()
155 .contains(Modifiers::CONTROL)
156 }
157 }
158
159 pub(crate) fn handle_pending_input_events(&self, can_gc: CanGc) {
160 let _realm = enter_realm(&*self.window);
161
162 *self.mouse_move_event_index.borrow_mut() = None;
164 let pending_input_events = mem::take(&mut *self.pending_input_events.borrow_mut());
165
166 for event in pending_input_events {
167 self.active_keyboard_modifiers
168 .set(event.active_keyboard_modifiers);
169
170 let result = match event.event.event.clone() {
176 InputEvent::MouseButton(mouse_button_event) => {
177 self.handle_native_mouse_button_event(mouse_button_event, &event, can_gc);
178 InputEventResult::default()
179 },
180 InputEvent::MouseMove(_) => {
181 self.handle_native_mouse_move_event(&event, can_gc);
182 InputEventResult::default()
183 },
184 InputEvent::MouseLeftViewport(mouse_leave_event) => {
185 self.handle_mouse_left_viewport_event(&event, &mouse_leave_event, can_gc);
186 InputEventResult::default()
187 },
188 InputEvent::Touch(touch_event) => {
189 self.handle_touch_event(touch_event, &event, can_gc)
190 },
191 InputEvent::Wheel(wheel_event) => {
192 self.handle_wheel_event(wheel_event, &event, can_gc)
193 },
194 InputEvent::Keyboard(keyboard_event) => {
195 self.handle_keyboard_event(keyboard_event, can_gc)
196 },
197 InputEvent::Ime(ime_event) => self.handle_ime_event(ime_event, can_gc),
198 InputEvent::Gamepad(gamepad_event) => {
199 self.handle_gamepad_event(gamepad_event);
200 InputEventResult::default()
201 },
202 InputEvent::EditingAction(editing_action_event) => {
203 self.handle_editing_action(None, editing_action_event, can_gc)
204 },
205 InputEvent::Scroll(scroll_event) => {
206 self.handle_embedder_scroll_event(scroll_event);
207 InputEventResult::default()
208 },
209 };
210
211 self.notify_embedder_that_event_was_handled(event.event, result);
212 }
213 }
214
215 fn notify_embedder_that_event_was_handled(
216 &self,
217 event: InputEventAndId,
218 result: InputEventResult,
219 ) {
220 let id = event.id;
223 let trusted_window = Trusted::new(&*self.window);
224 self.window
225 .as_global_scope()
226 .task_manager()
227 .dom_manipulation_task_source()
228 .queue(task!(notify_webdriver_input_event_completed: move || {
229 let window = trusted_window.root();
230 window.send_to_embedder(
231 EmbedderMsg::InputEventHandled(window.webview_id(), id, result));
232 }));
233 }
234
235 pub(crate) fn set_cursor(&self, cursor: Option<Cursor>) {
236 if cursor == self.current_cursor.get() {
237 return;
238 }
239 self.current_cursor.set(cursor);
240 self.window.send_to_embedder(EmbedderMsg::SetCursor(
241 self.window.webview_id(),
242 cursor.unwrap_or_default(),
243 ));
244 }
245
246 fn handle_mouse_left_viewport_event(
247 &self,
248 input_event: &ConstellationInputEvent,
249 mouse_leave_event: &MouseLeftViewportEvent,
250 can_gc: CanGc,
251 ) {
252 if let Some(current_hover_target) = self.current_hover_target.get() {
253 let current_hover_target = current_hover_target.upcast::<Node>();
254 for element in current_hover_target
255 .inclusive_ancestors(ShadowIncluding::No)
256 .filter_map(DomRoot::downcast::<Element>)
257 {
258 element.set_hover_state(false);
259 element.set_active_state(false);
260 }
261
262 if let Some(hit_test_result) = self
263 .most_recent_mousemove_point
264 .get()
265 .and_then(|point| self.window.hit_test_from_point_in_viewport(point))
266 {
267 MouseEvent::new_simple(
268 &self.window,
269 FireMouseEventType::Out,
270 EventBubbles::Bubbles,
271 EventCancelable::Cancelable,
272 &hit_test_result,
273 input_event,
274 can_gc,
275 )
276 .upcast::<Event>()
277 .fire(current_hover_target.upcast(), can_gc);
278 self.handle_mouse_enter_leave_event(
279 DomRoot::from_ref(current_hover_target),
280 None,
281 FireMouseEventType::Leave,
282 &hit_test_result,
283 input_event,
284 can_gc,
285 );
286 }
287 }
288
289 if !mouse_leave_event.focus_moving_to_another_iframe {
294 self.window
298 .send_to_embedder(EmbedderMsg::Status(self.window.webview_id(), None));
299 self.set_cursor(None);
300 } else {
301 self.current_cursor.set(None);
302 }
303
304 self.current_hover_target.set(None);
305 self.most_recent_mousemove_point.set(None);
306 }
307
308 fn handle_mouse_enter_leave_event(
309 &self,
310 event_target: DomRoot<Node>,
311 related_target: Option<DomRoot<Node>>,
312 event_type: FireMouseEventType,
313 hit_test_result: &HitTestResult,
314 input_event: &ConstellationInputEvent,
315 can_gc: CanGc,
316 ) {
317 assert!(matches!(
318 event_type,
319 FireMouseEventType::Enter | FireMouseEventType::Leave
320 ));
321
322 let common_ancestor = match related_target.as_ref() {
323 Some(related_target) => event_target
324 .common_ancestor(related_target, ShadowIncluding::No)
325 .unwrap_or_else(|| DomRoot::from_ref(&*event_target)),
326 None => DomRoot::from_ref(&*event_target),
327 };
328
329 let mut targets = vec![];
332 let mut current = Some(event_target);
333 while let Some(node) = current {
334 if node == common_ancestor {
335 break;
336 }
337 current = node.GetParentNode();
338 targets.push(node);
339 }
340
341 if event_type == FireMouseEventType::Enter {
344 targets = targets.into_iter().rev().collect();
345 }
346
347 for target in targets {
348 MouseEvent::new_simple(
349 &self.window,
350 event_type,
351 EventBubbles::DoesNotBubble,
352 EventCancelable::NotCancelable,
353 hit_test_result,
354 input_event,
355 can_gc,
356 )
357 .upcast::<Event>()
358 .fire(target.upcast(), can_gc);
359 }
360 }
361
362 fn handle_native_mouse_move_event(&self, input_event: &ConstellationInputEvent, can_gc: CanGc) {
364 let Some(hit_test_result) = self.window.hit_test_from_input_event(input_event) else {
366 return;
367 };
368
369 self.set_cursor(Some(hit_test_result.cursor));
371
372 let Some(new_target) = hit_test_result
373 .node
374 .inclusive_ancestors(ShadowIncluding::No)
375 .find_map(DomRoot::downcast::<Element>)
376 else {
377 return;
378 };
379
380 let target_has_changed = self
381 .current_hover_target
382 .get()
383 .is_none_or(|old_target| old_target != new_target);
384
385 if target_has_changed {
388 if let Some(old_target) = self.current_hover_target.get() {
390 let old_target_is_ancestor_of_new_target = old_target
391 .upcast::<Node>()
392 .is_ancestor_of(new_target.upcast::<Node>());
393
394 if !old_target_is_ancestor_of_new_target {
397 for element in old_target
398 .upcast::<Node>()
399 .inclusive_ancestors(ShadowIncluding::No)
400 .filter_map(DomRoot::downcast::<Element>)
401 {
402 element.set_hover_state(false);
403 element.set_active_state(false);
404 }
405 }
406
407 MouseEvent::new_simple(
408 &self.window,
409 FireMouseEventType::Out,
410 EventBubbles::Bubbles,
411 EventCancelable::Cancelable,
412 &hit_test_result,
413 input_event,
414 can_gc,
415 )
416 .upcast::<Event>()
417 .fire(old_target.upcast(), can_gc);
418
419 if !old_target_is_ancestor_of_new_target {
420 let event_target = DomRoot::from_ref(old_target.upcast::<Node>());
421 let moving_into = Some(DomRoot::from_ref(new_target.upcast::<Node>()));
422 self.handle_mouse_enter_leave_event(
423 event_target,
424 moving_into,
425 FireMouseEventType::Leave,
426 &hit_test_result,
427 input_event,
428 can_gc,
429 );
430 }
431 }
432
433 for element in new_target
435 .upcast::<Node>()
436 .inclusive_ancestors(ShadowIncluding::No)
437 .filter_map(DomRoot::downcast::<Element>)
438 {
439 element.set_hover_state(true);
440 }
441
442 MouseEvent::new_simple(
443 &self.window,
444 FireMouseEventType::Over,
445 EventBubbles::Bubbles,
446 EventCancelable::Cancelable,
447 &hit_test_result,
448 input_event,
449 can_gc,
450 )
451 .upcast::<Event>()
452 .fire(new_target.upcast(), can_gc);
453
454 let moving_from = self
455 .current_hover_target
456 .get()
457 .map(|old_target| DomRoot::from_ref(old_target.upcast::<Node>()));
458 let event_target = DomRoot::from_ref(new_target.upcast::<Node>());
459 self.handle_mouse_enter_leave_event(
460 event_target,
461 moving_from,
462 FireMouseEventType::Enter,
463 &hit_test_result,
464 input_event,
465 can_gc,
466 );
467 }
468
469 MouseEvent::new_simple(
472 &self.window,
473 FireMouseEventType::Move,
474 EventBubbles::Bubbles,
475 EventCancelable::Cancelable,
476 &hit_test_result,
477 input_event,
478 can_gc,
479 )
480 .upcast::<Event>()
481 .fire(new_target.upcast(), can_gc);
482
483 self.update_current_hover_target_and_status(Some(new_target));
484 self.most_recent_mousemove_point
485 .set(Some(hit_test_result.point_in_frame));
486 }
487
488 fn update_current_hover_target_and_status(&self, new_hover_target: Option<DomRoot<Element>>) {
489 let current_hover_target = self.current_hover_target.get();
490 if current_hover_target == new_hover_target {
491 return;
492 }
493
494 let previous_hover_target = self.current_hover_target.get();
495 self.current_hover_target.set(new_hover_target.as_deref());
496
497 if let Some(target) = self.current_hover_target.get() {
500 if let Some(anchor) = target
501 .upcast::<Node>()
502 .inclusive_ancestors(ShadowIncluding::No)
503 .find_map(DomRoot::downcast::<HTMLAnchorElement>)
504 {
505 let status = anchor
506 .full_href_url_for_user_interface()
507 .map(|url| url.to_string());
508 self.window
509 .send_to_embedder(EmbedderMsg::Status(self.window.webview_id(), status));
510 return;
511 }
512 }
513
514 if previous_hover_target.is_none_or(|previous_hover_target| {
519 previous_hover_target
520 .upcast::<Node>()
521 .inclusive_ancestors(ShadowIncluding::No)
522 .any(|node| node.is::<HTMLAnchorElement>())
523 }) {
524 self.window
525 .send_to_embedder(EmbedderMsg::Status(self.window.webview_id(), None));
526 }
527 }
528
529 pub(crate) fn handle_refresh_cursor(&self) {
530 let Some(most_recent_mousemove_point) = self.most_recent_mousemove_point.get() else {
531 return;
532 };
533
534 let Some(hit_test_result) = self
535 .window
536 .hit_test_from_point_in_viewport(most_recent_mousemove_point)
537 else {
538 return;
539 };
540
541 self.set_cursor(Some(hit_test_result.cursor));
542 }
543
544 fn handle_native_mouse_button_event(
547 &self,
548 event: MouseButtonEvent,
549 input_event: &ConstellationInputEvent,
550 can_gc: CanGc,
551 ) {
552 let Some(hit_test_result) = self.window.hit_test_from_input_event(input_event) else {
554 return;
555 };
556
557 debug!(
558 "{:?}: at {:?}",
559 event.action, hit_test_result.point_in_frame
560 );
561
562 let Some(element) = hit_test_result
563 .node
564 .inclusive_ancestors(ShadowIncluding::Yes)
565 .find_map(DomRoot::downcast::<Element>)
566 else {
567 return;
568 };
569
570 let node = element.upcast::<Node>();
571 debug!("{:?} on {:?}", event.action, node.debug_str());
572
573 if element.is_actually_disabled() {
577 return;
578 }
579
580 let mouse_event_type_string = match event.action {
581 embedder_traits::MouseButtonAction::Up => "mouseup",
582 embedder_traits::MouseButtonAction::Down => "mousedown",
583 };
584 let dom_event = DomRoot::upcast::<Event>(MouseEvent::for_platform_mouse_event(
585 mouse_event_type_string,
586 event,
587 input_event.pressed_mouse_buttons,
588 &self.window,
589 &hit_test_result,
590 input_event.active_keyboard_modifiers,
591 can_gc,
592 ));
593
594 let activatable = element.as_maybe_activatable();
595 match event.action {
596 MouseButtonAction::Down => {
597 self.last_mouse_button_down_point
598 .set(Some(hit_test_result.point_in_frame));
599
600 if let Some(a) = activatable {
601 a.enter_formal_activation_state();
602 }
603
604 let target_el = element.find_focusable_shadow_host_if_necessary();
611
612 let document = self.window.Document();
613 document.begin_focus_transaction();
614
615 document.request_focus(None, FocusInitiator::Local, can_gc);
617 document.request_focus(target_el.as_deref(), FocusInitiator::Local, can_gc);
618
619 let result = dom_event.dispatch(node.upcast(), false, can_gc);
621
622 if result && document.has_focus_transaction() {
625 document.commit_focus_transaction(FocusInitiator::Local, can_gc);
626 }
627
628 if let MouseButton::Right = event.button {
631 self.maybe_show_context_menu(
632 node.upcast(),
633 &hit_test_result,
634 input_event,
635 can_gc,
636 );
637 }
638 },
639 MouseButtonAction::Up => {
641 if let Some(a) = activatable {
642 a.exit_formal_activation_state();
643 }
644
645 dom_event.dispatch(node.upcast(), false, can_gc);
649
650 self.maybe_trigger_click_for_mouse_button_down_event(
651 event,
652 input_event,
653 &hit_test_result,
654 &element,
655 can_gc,
656 );
657 },
658 }
659 }
660
661 fn maybe_trigger_click_for_mouse_button_down_event(
663 &self,
664 event: MouseButtonEvent,
665 input_event: &ConstellationInputEvent,
666 hit_test_result: &HitTestResult,
667 element: &Element,
668 can_gc: CanGc,
669 ) {
670 if event.button != MouseButton::Left {
671 return;
672 }
673 let Some(last_mouse_button_down_point) = self.last_mouse_button_down_point.take() else {
674 return;
675 };
676
677 let distance = last_mouse_button_down_point.distance_to(hit_test_result.point_in_frame);
678 let maximum_click_distance = 10.0 * self.window.device_pixel_ratio().get();
679 if distance > maximum_click_distance {
680 return;
681 }
682
683 self.most_recently_clicked_element.set(Some(element));
688
689 element.set_click_in_progress(true);
690 let dom_event = DomRoot::upcast::<Event>(MouseEvent::for_platform_mouse_event(
691 "click",
692 event,
693 input_event.pressed_mouse_buttons,
694 &self.window,
695 hit_test_result,
696 input_event.active_keyboard_modifiers,
697 can_gc,
698 ));
699 let node = element.upcast::<Node>();
700 dom_event.dispatch(node.upcast(), false, can_gc);
701 element.set_click_in_progress(false);
702
703 self.maybe_fire_dblclick(node, hit_test_result, input_event, can_gc);
704 }
705
706 fn maybe_show_context_menu(
708 &self,
709 target: &EventTarget,
710 hit_test_result: &HitTestResult,
711 input_event: &ConstellationInputEvent,
712 can_gc: CanGc,
713 ) {
714 let menu_event = PointerEvent::new(
716 &self.window, DOMString::from("contextmenu"), EventBubbles::Bubbles, EventCancelable::Cancelable, Some(&self.window), 0, hit_test_result.point_in_frame.to_i32(),
723 hit_test_result.point_in_frame.to_i32(),
724 hit_test_result
725 .point_relative_to_initial_containing_block
726 .to_i32(),
727 input_event.active_keyboard_modifiers,
728 2i16, input_event.pressed_mouse_buttons,
730 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,
747 );
748
749 let result = menu_event.upcast::<Event>().fire(target, can_gc);
751
752 if result {
754 self.window
755 .Document()
756 .embedder_controls()
757 .show_context_menu(hit_test_result);
758 };
759 }
760
761 fn maybe_fire_dblclick(
762 &self,
763 target: &Node,
764 hit_test_result: &HitTestResult,
765 input_event: &ConstellationInputEvent,
766 can_gc: CanGc,
767 ) {
768 let now = Instant::now();
770 let point_in_frame = hit_test_result.point_in_frame;
771 let opt = self.last_click_info.borrow_mut().take();
772
773 if let Some((last_time, last_pos)) = opt {
774 let double_click_timeout =
775 Duration::from_millis(pref!(dom_document_dblclick_timeout) as u64);
776 let double_click_distance_threshold = pref!(dom_document_dblclick_dist) as u64;
777
778 let line = point_in_frame - last_pos;
780 let dist = (line.dot(line) as f64).sqrt();
781
782 if now.duration_since(last_time) < double_click_timeout &&
783 dist < double_click_distance_threshold as f64
784 {
785 let click_count = 2;
787
788 let event = MouseEvent::new(
789 &self.window,
790 DOMString::from("dblclick"),
791 EventBubbles::Bubbles,
792 EventCancelable::Cancelable,
793 Some(&self.window),
794 click_count,
795 point_in_frame.to_i32(),
796 point_in_frame.to_i32(),
797 hit_test_result
798 .point_relative_to_initial_containing_block
799 .to_i32(),
800 input_event.active_keyboard_modifiers,
801 0i16,
802 input_event.pressed_mouse_buttons,
803 None,
804 None,
805 can_gc,
806 );
807 event.upcast::<Event>().fire(target.upcast(), can_gc);
808
809 return;
812 }
813 }
814
815 *self.last_click_info.borrow_mut() = Some((now, point_in_frame));
817 }
818
819 fn handle_touch_event(
820 &self,
821 event: EmbedderTouchEvent,
822 input_event: &ConstellationInputEvent,
823 can_gc: CanGc,
824 ) -> InputEventResult {
825 let Some(hit_test_result) = self.window.hit_test_from_input_event(input_event) else {
827 self.update_active_touch_points_when_early_return(event);
828 return Default::default();
829 };
830
831 let TouchId(identifier) = event.id;
832 let event_name = match event.event_type {
833 TouchEventType::Down => "touchstart",
834 TouchEventType::Move => "touchmove",
835 TouchEventType::Up => "touchend",
836 TouchEventType::Cancel => "touchcancel",
837 };
838
839 let Some(el) = hit_test_result
840 .node
841 .inclusive_ancestors(ShadowIncluding::No)
842 .find_map(DomRoot::downcast::<Element>)
843 else {
844 self.update_active_touch_points_when_early_return(event);
845 return Default::default();
846 };
847
848 let target = DomRoot::upcast::<EventTarget>(el);
849 let window = &*self.window;
850
851 let client_x = Finite::wrap(hit_test_result.point_in_frame.x as f64);
852 let client_y = Finite::wrap(hit_test_result.point_in_frame.y as f64);
853 let page_x =
854 Finite::wrap(hit_test_result.point_in_frame.x as f64 + window.PageXOffset() as f64);
855 let page_y =
856 Finite::wrap(hit_test_result.point_in_frame.y as f64 + window.PageYOffset() as f64);
857
858 let touch = Touch::new(
859 window, identifier, &target, client_x,
860 client_y, client_x, client_y, page_x, page_y, can_gc,
862 );
863
864 match event.event_type {
865 TouchEventType::Down => {
866 self.active_touch_points
868 .borrow_mut()
869 .push(Dom::from_ref(&*touch));
870 },
871 TouchEventType::Move => {
872 let mut active_touch_points = self.active_touch_points.borrow_mut();
874 match active_touch_points
875 .iter_mut()
876 .find(|t| t.Identifier() == identifier)
877 {
878 Some(t) => *t = Dom::from_ref(&*touch),
879 None => warn!("Got a touchmove event for a non-active touch point"),
880 }
881 },
882 TouchEventType::Up | TouchEventType::Cancel => {
883 let mut active_touch_points = self.active_touch_points.borrow_mut();
885 match active_touch_points
886 .iter()
887 .position(|t| t.Identifier() == identifier)
888 {
889 Some(i) => {
890 active_touch_points.swap_remove(i);
891 },
892 None => warn!("Got a touchend event for a non-active touch point"),
893 }
894 },
895 }
896
897 rooted_vec!(let mut target_touches);
898 let touches = {
899 let touches = self.active_touch_points.borrow();
900 target_touches.extend(touches.iter().filter(|t| t.Target() == target).cloned());
901 TouchList::new(window, touches.r(), can_gc)
902 };
903
904 let touch_event = TouchEvent::new(
905 window,
906 DOMString::from(event_name),
907 EventBubbles::Bubbles,
908 EventCancelable::from(event.is_cancelable()),
909 EventComposed::Composed,
910 Some(window),
911 0i32,
912 &touches,
913 &TouchList::new(window, from_ref(&&*touch), can_gc),
914 &TouchList::new(window, target_touches.r(), can_gc),
915 false,
917 false,
918 false,
919 false,
920 can_gc,
921 );
922
923 let event = touch_event.upcast::<Event>();
924 event.fire(&target, can_gc);
925 event.flags().into()
926 }
927
928 fn update_active_touch_points_when_early_return(&self, event: EmbedderTouchEvent) {
930 match event.event_type {
931 TouchEventType::Down => {
932 },
936 TouchEventType::Move => {
937 },
940 TouchEventType::Up | TouchEventType::Cancel => {
941 let mut active_touch_points = self.active_touch_points.borrow_mut();
943 match active_touch_points
944 .iter()
945 .position(|t| t.Identifier() == event.id.0)
946 {
947 Some(i) => {
948 active_touch_points.swap_remove(i);
949 },
950 None => warn!("Got a touchend event for a non-active touch point"),
951 }
952 },
953 }
954 }
955
956 fn handle_keyboard_event(
958 &self,
959 keyboard_event: EmbedderKeyboardEvent,
960 can_gc: CanGc,
961 ) -> InputEventResult {
962 let document = self.window.Document();
963 let focused = document.get_focused_element();
964 let body = document.GetBody();
965
966 let target = match (&focused, &body) {
967 (Some(focused), _) => focused.upcast(),
968 (&None, Some(body)) => body.upcast(),
969 (&None, &None) => self.window.upcast(),
970 };
971
972 let keyevent = KeyboardEvent::new(
973 &self.window,
974 DOMString::from(keyboard_event.event.state.event_type()),
975 true,
976 true,
977 Some(&self.window),
978 0,
979 keyboard_event.event.key.clone(),
980 DOMString::from(keyboard_event.event.code.to_string()),
981 keyboard_event.event.location as u32,
982 keyboard_event.event.repeat,
983 keyboard_event.event.is_composing,
984 keyboard_event.event.modifiers,
985 0,
986 keyboard_event.event.key.legacy_keycode(),
987 can_gc,
988 );
989
990 let event = keyevent.upcast::<Event>();
991 event.fire(target, can_gc);
992
993 let mut flags = event.flags();
994 if flags.contains(EventFlags::Canceled) {
995 return flags.into();
996 }
997
998 let is_character_value_key = matches!(
1004 keyboard_event.event.key,
1005 Key::Character(_) | Key::Named(NamedKey::Enter)
1006 );
1007 if keyboard_event.event.state == KeyState::Down &&
1008 is_character_value_key &&
1009 !keyboard_event.event.is_composing
1010 {
1011 let keypress_event = KeyboardEvent::new(
1013 &self.window,
1014 DOMString::from("keypress"),
1015 true,
1016 true,
1017 Some(&self.window),
1018 0,
1019 keyboard_event.event.key.clone(),
1020 DOMString::from(keyboard_event.event.code.to_string()),
1021 keyboard_event.event.location as u32,
1022 keyboard_event.event.repeat,
1023 keyboard_event.event.is_composing,
1024 keyboard_event.event.modifiers,
1025 keyboard_event.event.key.legacy_charcode(),
1026 0,
1027 can_gc,
1028 );
1029 let event = keypress_event.upcast::<Event>();
1030 event.fire(target, can_gc);
1031 flags = event.flags();
1032 }
1033
1034 if flags.contains(EventFlags::Canceled) {
1035 return flags.into();
1036 }
1037
1038 if (keyboard_event.event.key == Key::Named(NamedKey::Enter) ||
1044 keyboard_event.event.code == Code::Space) &&
1045 keyboard_event.event.state == KeyState::Up
1046 {
1047 if let Some(elem) = target.downcast::<Element>() {
1048 elem.upcast::<Node>()
1049 .fire_synthetic_pointer_event_not_trusted(DOMString::from("click"), can_gc);
1050 }
1051 }
1052
1053 flags.into()
1054 }
1055
1056 fn handle_ime_event(&self, event: ImeEvent, can_gc: CanGc) -> InputEventResult {
1057 let document = self.window.Document();
1058 let composition_event = match event {
1059 ImeEvent::Dismissed => {
1060 document.request_focus(
1061 document.GetBody().as_ref().map(|e| e.upcast()),
1062 FocusInitiator::Local,
1063 can_gc,
1064 );
1065 return Default::default();
1066 },
1067 ImeEvent::Composition(composition_event) => composition_event,
1068 };
1069
1070 let focused = document.get_focused_element();
1075 let target = if let Some(elem) = &focused {
1076 elem.upcast()
1077 } else {
1078 return Default::default();
1080 };
1081
1082 let cancelable = composition_event.state == keyboard_types::CompositionState::Start;
1083 let event = CompositionEvent::new(
1084 &self.window,
1085 DOMString::from(composition_event.state.event_type()),
1086 true,
1087 cancelable,
1088 Some(&self.window),
1089 0,
1090 DOMString::from(composition_event.data),
1091 can_gc,
1092 );
1093
1094 let event = event.upcast::<Event>();
1095 event.fire(target, can_gc);
1096 event.flags().into()
1097 }
1098
1099 fn handle_wheel_event(
1100 &self,
1101 event: EmbedderWheelEvent,
1102 input_event: &ConstellationInputEvent,
1103 can_gc: CanGc,
1104 ) -> InputEventResult {
1105 let Some(hit_test_result) = self.window.hit_test_from_input_event(input_event) else {
1107 return Default::default();
1108 };
1109
1110 let Some(el) = hit_test_result
1111 .node
1112 .inclusive_ancestors(ShadowIncluding::No)
1113 .find_map(DomRoot::downcast::<Element>)
1114 else {
1115 return Default::default();
1116 };
1117
1118 let node = el.upcast::<Node>();
1119 let wheel_event_type_string = "wheel".to_owned();
1120 debug!(
1121 "{}: on {:?} at {:?}",
1122 wheel_event_type_string,
1123 node.debug_str(),
1124 hit_test_result.point_in_frame
1125 );
1126
1127 let dom_event = WheelEvent::new(
1129 &self.window,
1130 DOMString::from(wheel_event_type_string),
1131 EventBubbles::Bubbles,
1132 EventCancelable::Cancelable,
1133 Some(&self.window),
1134 0i32,
1135 hit_test_result.point_in_frame.to_i32(),
1136 hit_test_result.point_in_frame.to_i32(),
1137 hit_test_result
1138 .point_relative_to_initial_containing_block
1139 .to_i32(),
1140 input_event.active_keyboard_modifiers,
1141 0i16,
1142 input_event.pressed_mouse_buttons,
1143 None,
1144 None,
1145 Finite::wrap(-event.delta.x),
1150 Finite::wrap(-event.delta.y),
1151 Finite::wrap(-event.delta.z),
1152 event.delta.mode as u32,
1153 can_gc,
1154 );
1155
1156 let dom_event = dom_event.upcast::<Event>();
1157 dom_event.set_trusted(true);
1158 dom_event.fire(node.upcast(), can_gc);
1159
1160 dom_event.flags().into()
1161 }
1162
1163 fn handle_gamepad_event(&self, gamepad_event: EmbedderGamepadEvent) {
1164 match gamepad_event {
1165 EmbedderGamepadEvent::Connected(index, name, bounds, supported_haptic_effects) => {
1166 self.handle_gamepad_connect(
1167 index.0,
1168 name,
1169 bounds.axis_bounds,
1170 bounds.button_bounds,
1171 supported_haptic_effects,
1172 );
1173 },
1174 EmbedderGamepadEvent::Disconnected(index) => {
1175 self.handle_gamepad_disconnect(index.0);
1176 },
1177 EmbedderGamepadEvent::Updated(index, update_type) => {
1178 self.receive_new_gamepad_button_or_axis(index.0, update_type);
1179 },
1180 };
1181 }
1182
1183 fn handle_gamepad_connect(
1185 &self,
1186 _index: usize,
1190 name: String,
1191 axis_bounds: (f64, f64),
1192 button_bounds: (f64, f64),
1193 supported_haptic_effects: GamepadSupportedHapticEffects,
1194 ) {
1195 let trusted_window = Trusted::new(&*self.window);
1198 self.window
1199 .upcast::<GlobalScope>()
1200 .task_manager()
1201 .gamepad_task_source()
1202 .queue(task!(gamepad_connected: move || {
1203 let window = trusted_window.root();
1204
1205 let navigator = window.Navigator();
1206 let selected_index = navigator.select_gamepad_index();
1207 let gamepad = Gamepad::new(
1208 &window,
1209 selected_index,
1210 name,
1211 "standard".into(),
1212 axis_bounds,
1213 button_bounds,
1214 supported_haptic_effects,
1215 false,
1216 CanGc::note(),
1217 );
1218 navigator.set_gamepad(selected_index as usize, &gamepad, CanGc::note());
1219 }));
1220 }
1221
1222 fn handle_gamepad_disconnect(&self, index: usize) {
1224 let trusted_window = Trusted::new(&*self.window);
1225 self.window
1226 .upcast::<GlobalScope>()
1227 .task_manager()
1228 .gamepad_task_source()
1229 .queue(task!(gamepad_disconnected: move || {
1230 let window = trusted_window.root();
1231 let navigator = window.Navigator();
1232 if let Some(gamepad) = navigator.get_gamepad(index) {
1233 if window.Document().is_fully_active() {
1234 gamepad.update_connected(false, gamepad.exposed(), CanGc::note());
1235 navigator.remove_gamepad(index);
1236 }
1237 }
1238 }));
1239 }
1240
1241 fn receive_new_gamepad_button_or_axis(&self, index: usize, update_type: GamepadUpdateType) {
1243 let trusted_window = Trusted::new(&*self.window);
1244
1245 self.window.upcast::<GlobalScope>().task_manager().gamepad_task_source().queue(
1247 task!(update_gamepad_state: move || {
1248 let window = trusted_window.root();
1249 let navigator = window.Navigator();
1250 if let Some(gamepad) = navigator.get_gamepad(index) {
1251 let current_time = window.Performance().Now();
1252 gamepad.update_timestamp(*current_time);
1253 match update_type {
1254 GamepadUpdateType::Axis(index, value) => {
1255 gamepad.map_and_normalize_axes(index, value);
1256 },
1257 GamepadUpdateType::Button(index, value) => {
1258 gamepad.map_and_normalize_buttons(index, value);
1259 }
1260 };
1261 if !navigator.has_gamepad_gesture() && contains_user_gesture(update_type) {
1262 navigator.set_has_gamepad_gesture(true);
1263 navigator.GetGamepads()
1264 .iter()
1265 .filter_map(|g| g.as_ref())
1266 .for_each(|gamepad| {
1267 gamepad.set_exposed(true);
1268 gamepad.update_timestamp(*current_time);
1269 let new_gamepad = Trusted::new(&**gamepad);
1270 if window.Document().is_fully_active() {
1271 window.upcast::<GlobalScope>().task_manager().gamepad_task_source().queue(
1272 task!(update_gamepad_connect: move || {
1273 let gamepad = new_gamepad.root();
1274 gamepad.notify_event(GamepadEventType::Connected, CanGc::note());
1275 })
1276 );
1277 }
1278 });
1279 }
1280 }
1281 })
1282 );
1283 }
1284
1285 pub(crate) fn handle_editing_action(
1287 &self,
1288 element: Option<DomRoot<Element>>,
1289 action: EditingActionEvent,
1290 can_gc: CanGc,
1291 ) -> InputEventResult {
1292 let clipboard_event_type = match action {
1293 EditingActionEvent::Copy => ClipboardEventType::Copy,
1294 EditingActionEvent::Cut => ClipboardEventType::Cut,
1295 EditingActionEvent::Paste => ClipboardEventType::Paste,
1296 };
1297
1298 let script_triggered = false;
1300
1301 let script_may_access_clipboard = false;
1305
1306 if script_triggered && !script_may_access_clipboard {
1308 return InputEventResult::empty();
1309 }
1310
1311 let clipboard_event =
1313 self.fire_clipboard_event(element.clone(), clipboard_event_type, can_gc);
1314
1315 let event = clipboard_event.upcast::<Event>();
1318 if !event.IsTrusted() {
1319 return event.flags().into();
1320 }
1321
1322 if event.DefaultPrevented() {
1324 let event_type = event.Type();
1325 match_domstring_ascii!(event_type,
1326
1327 "copy" => {
1328 if let Some(clipboard_data) = clipboard_event.get_clipboard_data() {
1331 let drag_data_store =
1332 clipboard_data.data_store().expect("This shouldn't fail");
1333 self.write_content_to_the_clipboard(&drag_data_store);
1334 }
1335 },
1336 "cut" => {
1337 if let Some(clipboard_data) = clipboard_event.get_clipboard_data() {
1340 let drag_data_store =
1341 clipboard_data.data_store().expect("This shouldn't fail");
1342 self.write_content_to_the_clipboard(&drag_data_store);
1343 }
1344
1345 self.fire_clipboard_event(element, ClipboardEventType::Change, can_gc);
1347 },
1348 "paste" => (),
1352 _ => (),
1353 )
1354 }
1355
1356 event.flags().into()
1359 }
1360
1361 pub(crate) fn fire_clipboard_event(
1363 &self,
1364 target: Option<DomRoot<Element>>,
1365 clipboard_event_type: ClipboardEventType,
1366 can_gc: CanGc,
1367 ) -> DomRoot<ClipboardEvent> {
1368 let clipboard_event = ClipboardEvent::new(
1369 &self.window,
1370 None,
1371 DOMString::from(clipboard_event_type.as_str()),
1372 EventBubbles::Bubbles,
1373 EventCancelable::Cancelable,
1374 None,
1375 can_gc,
1376 );
1377
1378 let mut drag_data_store = DragDataStore::new();
1381
1382 let trusted = true;
1386
1387 let document = self.window.Document();
1389 let target = target.or(document.get_focused_element());
1390 let target = target
1391 .map(|target| DomRoot::from_ref(target.upcast()))
1392 .or_else(|| {
1393 document
1394 .GetBody()
1395 .map(|body| DomRoot::from_ref(body.upcast()))
1396 })
1397 .unwrap_or_else(|| DomRoot::from_ref(self.window.upcast()));
1398
1399 match clipboard_event_type {
1402 ClipboardEventType::Copy | ClipboardEventType::Cut => {
1403 drag_data_store.set_mode(Mode::ReadWrite);
1405 },
1406 ClipboardEventType::Paste => {
1407 let (sender, receiver) = ipc::channel().unwrap();
1408 self.window.send_to_embedder(EmbedderMsg::GetClipboardText(
1409 self.window.webview_id(),
1410 sender,
1411 ));
1412 let text_contents = receiver
1413 .recv()
1414 .map(Result::unwrap_or_default)
1415 .unwrap_or_default();
1416
1417 drag_data_store.set_mode(Mode::ReadOnly);
1419 if trusted {
1421 let data = DOMString::from(text_contents.to_string());
1425 let type_ = DOMString::from("text/plain");
1426 let _ = drag_data_store.add(Kind::Text { data, type_ });
1427
1428 }
1434 },
1435 ClipboardEventType::Change => (),
1436 }
1437
1438 let clipboard_event_data = DataTransfer::new(
1440 &self.window,
1441 Rc::new(RefCell::new(Some(drag_data_store))),
1442 can_gc,
1443 );
1444
1445 clipboard_event.set_clipboard_data(Some(&clipboard_event_data));
1447
1448 let event = clipboard_event.upcast::<Event>();
1450 event.set_trusted(trusted);
1451
1452 event.set_composed(true);
1454
1455 event.dispatch(&target, false, can_gc);
1457
1458 DomRoot::from(clipboard_event)
1459 }
1460
1461 fn write_content_to_the_clipboard(&self, drag_data_store: &DragDataStore) {
1463 if drag_data_store.list_len() > 0 {
1465 self.window
1467 .send_to_embedder(EmbedderMsg::ClearClipboard(self.window.webview_id()));
1468 for item in drag_data_store.iter_item_list() {
1470 match item {
1471 Kind::Text { data, .. } => {
1472 self.window.send_to_embedder(EmbedderMsg::SetClipboardText(
1476 self.window.webview_id(),
1477 data.to_string(),
1478 ));
1479 },
1480 Kind::File { .. } => {
1481 },
1485 }
1486 }
1487 } else {
1488 if drag_data_store.clear_was_called {
1490 self.window
1492 .send_to_embedder(EmbedderMsg::ClearClipboard(self.window.webview_id()));
1493 }
1496 }
1497 }
1498
1499 #[expect(unsafe_code)]
1502 fn handle_embedder_scroll_event(&self, event: ScrollEvent) {
1503 let document = self.window.Document();
1505 if event.external_id.is_root() {
1506 document.handle_viewport_scroll_event();
1507 } else {
1508 let Some(node_id) = node_id_from_scroll_id(event.external_id.0 as usize) else {
1510 return;
1511 };
1512 let node = unsafe {
1513 node::from_untrusted_node_address(UntrustedNodeAddress::from_id(node_id))
1514 };
1515 let Some(element) = node
1516 .inclusive_ancestors(ShadowIncluding::No)
1517 .find_map(DomRoot::downcast::<Element>)
1518 else {
1519 return;
1520 };
1521
1522 document.handle_element_scroll_event(&element);
1523 }
1524 }
1525
1526 pub(crate) fn run_default_keyboard_event_handler(&self, event: &KeyboardEvent) {
1527 if event.upcast::<Event>().type_() != atom!("keydown") {
1528 return;
1529 }
1530 if !event.modifiers().is_empty() {
1531 return;
1532 }
1533 let scroll = match event.key() {
1534 Key::Named(NamedKey::ArrowDown) => KeyboardScroll::Down,
1535 Key::Named(NamedKey::ArrowLeft) => KeyboardScroll::Left,
1536 Key::Named(NamedKey::ArrowRight) => KeyboardScroll::Right,
1537 Key::Named(NamedKey::ArrowUp) => KeyboardScroll::Up,
1538 Key::Named(NamedKey::End) => KeyboardScroll::End,
1539 Key::Named(NamedKey::Home) => KeyboardScroll::Home,
1540 Key::Named(NamedKey::PageDown) => KeyboardScroll::PageDown,
1541 Key::Named(NamedKey::PageUp) => KeyboardScroll::PageUp,
1542 _ => return,
1543 };
1544 self.do_keyboard_scroll(scroll);
1545 }
1546
1547 pub(crate) fn do_keyboard_scroll(&self, scroll: KeyboardScroll) {
1548 let scroll_axis = match scroll {
1549 KeyboardScroll::Left | KeyboardScroll::Right => ScrollingBoxAxis::X,
1550 _ => ScrollingBoxAxis::Y,
1551 };
1552
1553 let document = self.window.Document();
1554 let mut scrolling_box = document
1555 .get_focused_element()
1556 .or(self.most_recently_clicked_element.get())
1557 .and_then(|element| element.scrolling_box(ScrollContainerQueryFlags::Inclusive))
1558 .unwrap_or_else(|| {
1559 document.viewport_scrolling_box(ScrollContainerQueryFlags::Inclusive)
1560 });
1561
1562 while !scrolling_box.can_keyboard_scroll_in_axis(scroll_axis) {
1563 if scrolling_box.is_viewport() {
1565 break;
1566 }
1567 let parent = scrolling_box.parent().unwrap_or_else(|| {
1568 document.viewport_scrolling_box(ScrollContainerQueryFlags::Inclusive)
1569 });
1570 scrolling_box = parent;
1571 }
1572
1573 let calculate_current_scroll_offset_and_delta = || {
1574 const LINE_HEIGHT: f32 = 76.0;
1575 const LINE_WIDTH: f32 = 76.0;
1576
1577 let current_scroll_offset = scrolling_box.scroll_position();
1578 (
1579 current_scroll_offset,
1580 match scroll {
1581 KeyboardScroll::Home => Vector2D::new(0.0, -current_scroll_offset.y),
1582 KeyboardScroll::End => Vector2D::new(
1583 0.0,
1584 -current_scroll_offset.y + scrolling_box.content_size().height -
1585 scrolling_box.size().height,
1586 ),
1587 KeyboardScroll::PageDown => {
1588 Vector2D::new(0.0, scrolling_box.size().height - 2.0 * LINE_HEIGHT)
1589 },
1590 KeyboardScroll::PageUp => {
1591 Vector2D::new(0.0, 2.0 * LINE_HEIGHT - scrolling_box.size().height)
1592 },
1593 KeyboardScroll::Up => Vector2D::new(0.0, -LINE_HEIGHT),
1594 KeyboardScroll::Down => Vector2D::new(0.0, LINE_HEIGHT),
1595 KeyboardScroll::Left => Vector2D::new(-LINE_WIDTH, 0.0),
1596 KeyboardScroll::Right => Vector2D::new(LINE_WIDTH, 0.0),
1597 },
1598 )
1599 };
1600
1601 let parent_pipeline = self.window.parent_info();
1605 if scrolling_box.is_viewport() && parent_pipeline.is_none() {
1606 let (_, delta) = calculate_current_scroll_offset_and_delta();
1607 self.window
1608 .compositor_api()
1609 .scroll_viewport_by_delta(self.window.webview_id(), delta);
1610 }
1611
1612 if !scrolling_box.can_keyboard_scroll_in_axis(scroll_axis) {
1615 assert!(scrolling_box.is_viewport());
1616
1617 let window_proxy = document.window().window_proxy();
1618 if let Some(iframe) = window_proxy.frame_element() {
1619 let cx = GlobalScope::get_cx();
1622 let iframe_window = iframe.owner_window();
1623 let _ac = JSAutoRealm::new(*cx, iframe_window.reflector().get_jsobject().get());
1624 iframe_window
1625 .Document()
1626 .event_handler()
1627 .do_keyboard_scroll(scroll);
1628 } else if let Some(parent_pipeline) = parent_pipeline {
1629 document.window().send_to_constellation(
1633 ScriptToConstellationMessage::ForwardKeyboardScroll(parent_pipeline, scroll),
1634 );
1635 };
1636 return;
1637 }
1638
1639 let (current_scroll_offset, delta) = calculate_current_scroll_offset_and_delta();
1640 scrolling_box.scroll_to(delta + current_scroll_offset, ScrollBehavior::Auto);
1641 }
1642}