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, ScrollEvent, TouchEvent as EmbedderTouchEvent,
18 TouchEventType, 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 script_bindings::codegen::GenericBindings::DocumentBinding::DocumentMethods;
29use script_bindings::codegen::GenericBindings::EventBinding::EventMethods;
30use script_bindings::codegen::GenericBindings::NavigatorBinding::NavigatorMethods;
31use script_bindings::codegen::GenericBindings::NodeBinding::NodeMethods;
32use script_bindings::codegen::GenericBindings::PerformanceBinding::PerformanceMethods;
33use script_bindings::codegen::GenericBindings::TouchBinding::TouchMethods;
34use script_bindings::codegen::GenericBindings::WindowBinding::{ScrollBehavior, WindowMethods};
35use script_bindings::inheritance::Castable;
36use script_bindings::match_domstring_ascii;
37use script_bindings::num::Finite;
38use script_bindings::reflector::DomObject;
39use script_bindings::root::{Dom, DomRoot, DomSlice};
40use script_bindings::script_runtime::CanGc;
41use script_bindings::str::DOMString;
42use script_traits::ConstellationInputEvent;
43use servo_config::pref;
44use style_traits::CSSPixel;
45
46use crate::dom::bindings::cell::DomRefCell;
47use crate::dom::bindings::refcounted::Trusted;
48use crate::dom::bindings::root::MutNullableDom;
49use crate::dom::clipboardevent::ClipboardEventType;
50use crate::dom::document::{FireMouseEventType, FocusInitiator};
51use crate::dom::event::{EventBubbles, EventCancelable, EventComposed, EventFlags};
52#[cfg(feature = "gamepad")]
53use crate::dom::gamepad::gamepad::{Gamepad, contains_user_gesture};
54#[cfg(feature = "gamepad")]
55use crate::dom::gamepad::gamepadevent::GamepadEventType;
56use crate::dom::inputevent::HitTestResult;
57use crate::dom::node::{self, Node, NodeTraits, ShadowIncluding};
58use crate::dom::pointerevent::PointerId;
59use crate::dom::scrolling_box::ScrollingBoxAxis;
60use crate::dom::types::{
61 ClipboardEvent, CompositionEvent, DataTransfer, Element, Event, EventTarget, GlobalScope,
62 HTMLAnchorElement, KeyboardEvent, MouseEvent, PointerEvent, Touch, TouchEvent, TouchList,
63 WheelEvent, Window,
64};
65use crate::drag_data_store::{DragDataStore, Kind, Mode};
66use crate::realms::enter_realm;
67
68#[derive(Default, JSTraceable, MallocSizeOf)]
78struct ClickCountingInfo {
79 time: Option<Instant>,
80 #[no_trace]
81 point: Option<Point2D<f32, CSSPixel>>,
82 #[no_trace]
83 button: Option<MouseButton>,
84 count: usize,
85}
86
87impl ClickCountingInfo {
88 fn reset_click_count_if_necessary(
89 &mut self,
90 button: MouseButton,
91 point_in_frame: Point2D<f32, CSSPixel>,
92 ) {
93 let (Some(previous_button), Some(previous_point), Some(previous_time)) =
94 (self.button, self.point, self.time)
95 else {
96 assert_eq!(self.count, 0);
97 return;
98 };
99
100 let double_click_timeout =
101 Duration::from_millis(pref!(dom_document_dblclick_timeout) as u64);
102 let double_click_distance_threshold = pref!(dom_document_dblclick_dist) as u64;
103
104 let line = point_in_frame - previous_point;
106 let distance = (line.dot(line) as f64).sqrt();
107 if previous_button != button ||
108 Instant::now().duration_since(previous_time) > double_click_timeout ||
109 distance > double_click_distance_threshold as f64
110 {
111 self.count = 0;
112 self.time = None;
113 self.point = None;
114 }
115 }
116
117 fn increment_click_count(
118 &mut self,
119 button: MouseButton,
120 point: Point2D<f32, CSSPixel>,
121 ) -> usize {
122 self.time = Some(Instant::now());
123 self.point = Some(point);
124 self.button = Some(button);
125 self.count += 1;
126 self.count
127 }
128}
129
130#[derive(JSTraceable, MallocSizeOf)]
134#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
135pub(crate) struct DocumentEventHandler {
136 window: Dom<Window>,
138 #[no_trace]
140 #[ignore_malloc_size_of = "InputEvent contains data from outside crates"]
141 pending_input_events: DomRefCell<Vec<ConstellationInputEvent>>,
142 mouse_move_event_index: DomRefCell<Option<usize>>,
144 click_counting_info: DomRefCell<ClickCountingInfo>,
146 #[no_trace]
147 last_mouse_button_down_point: Cell<Option<Point2D<f32, CSSPixel>>>,
148 current_hover_target: MutNullableDom<Element>,
150 most_recently_clicked_element: MutNullableDom<Element>,
152 #[no_trace]
154 most_recent_mousemove_point: Cell<Option<Point2D<f32, CSSPixel>>>,
155 #[no_trace]
158 current_cursor: Cell<Option<Cursor>>,
159 active_touch_points: DomRefCell<Vec<Dom<Touch>>>,
161 #[no_trace]
163 active_keyboard_modifiers: Cell<Modifiers>,
164}
165
166impl DocumentEventHandler {
167 pub(crate) fn new(window: &Window) -> Self {
168 Self {
169 window: Dom::from_ref(window),
170 pending_input_events: Default::default(),
171 mouse_move_event_index: Default::default(),
172 click_counting_info: Default::default(),
173 last_mouse_button_down_point: Default::default(),
174 current_hover_target: Default::default(),
175 most_recently_clicked_element: Default::default(),
176 most_recent_mousemove_point: Default::default(),
177 current_cursor: Default::default(),
178 active_touch_points: Default::default(),
179 active_keyboard_modifiers: Default::default(),
180 }
181 }
182
183 pub(crate) fn note_pending_input_event(&self, event: ConstellationInputEvent) {
185 let mut pending_input_events = self.pending_input_events.borrow_mut();
186 if matches!(event.event.event, InputEvent::MouseMove(..)) {
187 if let Some(mouse_move_event) = self
189 .mouse_move_event_index
190 .borrow()
191 .and_then(|index| pending_input_events.get_mut(index))
192 {
193 *mouse_move_event = event;
194 return;
195 }
196
197 *self.mouse_move_event_index.borrow_mut() = Some(pending_input_events.len());
198 }
199
200 pending_input_events.push(event);
201 }
202
203 pub(crate) fn has_pending_input_events(&self) -> bool {
206 !self.pending_input_events.borrow().is_empty()
207 }
208
209 pub(crate) fn alternate_action_keyboard_modifier_active(&self) -> bool {
210 #[cfg(target_os = "macos")]
211 {
212 self.active_keyboard_modifiers
213 .get()
214 .contains(Modifiers::META)
215 }
216 #[cfg(not(target_os = "macos"))]
217 {
218 self.active_keyboard_modifiers
219 .get()
220 .contains(Modifiers::CONTROL)
221 }
222 }
223
224 pub(crate) fn handle_pending_input_events(&self, can_gc: CanGc) {
225 let _realm = enter_realm(&*self.window);
226
227 *self.mouse_move_event_index.borrow_mut() = None;
229 let pending_input_events = mem::take(&mut *self.pending_input_events.borrow_mut());
230
231 for event in pending_input_events {
232 self.active_keyboard_modifiers
233 .set(event.active_keyboard_modifiers);
234
235 let result = match event.event.event.clone() {
241 InputEvent::MouseButton(mouse_button_event) => {
242 self.handle_native_mouse_button_event(mouse_button_event, &event, can_gc);
243 InputEventResult::default()
244 },
245 InputEvent::MouseMove(_) => {
246 self.handle_native_mouse_move_event(&event, can_gc);
247 InputEventResult::default()
248 },
249 InputEvent::MouseLeftViewport(mouse_leave_event) => {
250 self.handle_mouse_left_viewport_event(&event, &mouse_leave_event, can_gc);
251 InputEventResult::default()
252 },
253 InputEvent::Touch(touch_event) => {
254 self.handle_touch_event(touch_event, &event, can_gc)
255 },
256 InputEvent::Wheel(wheel_event) => {
257 self.handle_wheel_event(wheel_event, &event, can_gc)
258 },
259 InputEvent::Keyboard(keyboard_event) => {
260 self.handle_keyboard_event(keyboard_event, can_gc)
261 },
262 InputEvent::Ime(ime_event) => self.handle_ime_event(ime_event, can_gc),
263 #[cfg(feature = "gamepad")]
264 InputEvent::Gamepad(gamepad_event) => {
265 self.handle_gamepad_event(gamepad_event);
266 InputEventResult::default()
267 },
268 InputEvent::EditingAction(editing_action_event) => {
269 self.handle_editing_action(None, editing_action_event, can_gc)
270 },
271 InputEvent::Scroll(scroll_event) => {
272 self.handle_embedder_scroll_event(scroll_event);
273 InputEventResult::default()
274 },
275 };
276
277 self.notify_embedder_that_event_was_handled(event.event, result);
278 }
279 }
280
281 fn notify_embedder_that_event_was_handled(
282 &self,
283 event: InputEventAndId,
284 result: InputEventResult,
285 ) {
286 let id = event.id;
289 let trusted_window = Trusted::new(&*self.window);
290 self.window
291 .as_global_scope()
292 .task_manager()
293 .dom_manipulation_task_source()
294 .queue(task!(notify_webdriver_input_event_completed: move || {
295 let window = trusted_window.root();
296 window.send_to_embedder(
297 EmbedderMsg::InputEventHandled(window.webview_id(), id, result));
298 }));
299 }
300
301 pub(crate) fn set_cursor(&self, cursor: Option<Cursor>) {
302 if cursor == self.current_cursor.get() {
303 return;
304 }
305 self.current_cursor.set(cursor);
306 self.window.send_to_embedder(EmbedderMsg::SetCursor(
307 self.window.webview_id(),
308 cursor.unwrap_or_default(),
309 ));
310 }
311
312 fn handle_mouse_left_viewport_event(
313 &self,
314 input_event: &ConstellationInputEvent,
315 mouse_leave_event: &MouseLeftViewportEvent,
316 can_gc: CanGc,
317 ) {
318 if let Some(current_hover_target) = self.current_hover_target.get() {
319 let current_hover_target = current_hover_target.upcast::<Node>();
320 for element in current_hover_target
321 .inclusive_ancestors(ShadowIncluding::Yes)
322 .filter_map(DomRoot::downcast::<Element>)
323 {
324 element.set_hover_state(false);
325 element.set_active_state(false);
326 }
327
328 if let Some(hit_test_result) = self
329 .most_recent_mousemove_point
330 .get()
331 .and_then(|point| self.window.hit_test_from_point_in_viewport(point))
332 {
333 MouseEvent::new_for_platform_motion_event(
334 &self.window,
335 FireMouseEventType::Out,
336 &hit_test_result,
337 input_event,
338 can_gc,
339 )
340 .upcast::<Event>()
341 .fire(current_hover_target.upcast(), can_gc);
342 self.handle_mouse_enter_leave_event(
343 DomRoot::from_ref(current_hover_target),
344 None,
345 FireMouseEventType::Leave,
346 &hit_test_result,
347 input_event,
348 can_gc,
349 );
350 }
351 }
352
353 if !mouse_leave_event.focus_moving_to_another_iframe {
358 self.window
362 .send_to_embedder(EmbedderMsg::Status(self.window.webview_id(), None));
363 self.set_cursor(None);
364 } else {
365 self.current_cursor.set(None);
366 }
367
368 self.current_hover_target.set(None);
369 self.most_recent_mousemove_point.set(None);
370 }
371
372 fn handle_mouse_enter_leave_event(
373 &self,
374 event_target: DomRoot<Node>,
375 related_target: Option<DomRoot<Node>>,
376 event_type: FireMouseEventType,
377 hit_test_result: &HitTestResult,
378 input_event: &ConstellationInputEvent,
379 can_gc: CanGc,
380 ) {
381 assert!(matches!(
382 event_type,
383 FireMouseEventType::Enter | FireMouseEventType::Leave
384 ));
385
386 let common_ancestor = match related_target.as_ref() {
387 Some(related_target) => event_target
388 .common_ancestor(related_target, ShadowIncluding::Yes)
389 .unwrap_or_else(|| DomRoot::from_ref(&*event_target)),
390 None => DomRoot::from_ref(&*event_target),
391 };
392
393 let mut targets = vec![];
396 let mut current = Some(event_target);
397 while let Some(node) = current {
398 if node == common_ancestor {
399 break;
400 }
401 current = node.GetParentNode();
402 targets.push(node);
403 }
404
405 if event_type == FireMouseEventType::Enter {
408 targets = targets.into_iter().rev().collect();
409 }
410
411 for target in targets {
412 MouseEvent::new_for_platform_motion_event(
413 &self.window,
414 event_type,
415 hit_test_result,
416 input_event,
417 can_gc,
418 )
419 .upcast::<Event>()
420 .fire(target.upcast(), can_gc);
421 }
422 }
423
424 fn handle_native_mouse_move_event(&self, input_event: &ConstellationInputEvent, can_gc: CanGc) {
426 let Some(hit_test_result) = self.window.hit_test_from_input_event(input_event) else {
428 return;
429 };
430
431 let old_mouse_move_point = self
432 .most_recent_mousemove_point
433 .replace(Some(hit_test_result.point_in_frame));
434 if old_mouse_move_point == Some(hit_test_result.point_in_frame) {
435 return;
436 }
437
438 self.set_cursor(Some(hit_test_result.cursor));
440
441 let Some(new_target) = hit_test_result
442 .node
443 .inclusive_ancestors(ShadowIncluding::Yes)
444 .find_map(DomRoot::downcast::<Element>)
445 else {
446 return;
447 };
448
449 let target_has_changed = self
450 .current_hover_target
451 .get()
452 .is_none_or(|old_target| old_target != new_target);
453
454 if target_has_changed {
457 if let Some(old_target) = self.current_hover_target.get() {
459 let old_target_is_ancestor_of_new_target = old_target
460 .upcast::<Node>()
461 .is_ancestor_of(new_target.upcast::<Node>());
462
463 if !old_target_is_ancestor_of_new_target {
466 for element in old_target
467 .upcast::<Node>()
468 .inclusive_ancestors(ShadowIncluding::No)
469 .filter_map(DomRoot::downcast::<Element>)
470 {
471 element.set_hover_state(false);
472 element.set_active_state(false);
473 }
474 }
475
476 MouseEvent::new_for_platform_motion_event(
477 &self.window,
478 FireMouseEventType::Out,
479 &hit_test_result,
480 input_event,
481 can_gc,
482 )
483 .upcast::<Event>()
484 .fire(old_target.upcast(), can_gc);
485
486 if !old_target_is_ancestor_of_new_target {
487 let event_target = DomRoot::from_ref(old_target.upcast::<Node>());
488 let moving_into = Some(DomRoot::from_ref(new_target.upcast::<Node>()));
489 self.handle_mouse_enter_leave_event(
490 event_target,
491 moving_into,
492 FireMouseEventType::Leave,
493 &hit_test_result,
494 input_event,
495 can_gc,
496 );
497 }
498 }
499
500 for element in new_target
502 .upcast::<Node>()
503 .inclusive_ancestors(ShadowIncluding::Yes)
504 .filter_map(DomRoot::downcast::<Element>)
505 {
506 element.set_hover_state(true);
507 }
508
509 MouseEvent::new_for_platform_motion_event(
510 &self.window,
511 FireMouseEventType::Over,
512 &hit_test_result,
513 input_event,
514 can_gc,
515 )
516 .upcast::<Event>()
517 .dispatch(new_target.upcast(), false, can_gc);
518
519 let moving_from = self
520 .current_hover_target
521 .get()
522 .map(|old_target| DomRoot::from_ref(old_target.upcast::<Node>()));
523 let event_target = DomRoot::from_ref(new_target.upcast::<Node>());
524 self.handle_mouse_enter_leave_event(
525 event_target,
526 moving_from,
527 FireMouseEventType::Enter,
528 &hit_test_result,
529 input_event,
530 can_gc,
531 );
532 }
533
534 MouseEvent::new_for_platform_motion_event(
537 &self.window,
538 FireMouseEventType::Move,
539 &hit_test_result,
540 input_event,
541 can_gc,
542 )
543 .upcast::<Event>()
544 .fire(new_target.upcast(), can_gc);
545
546 self.update_current_hover_target_and_status(Some(new_target));
547 }
548
549 fn update_current_hover_target_and_status(&self, new_hover_target: Option<DomRoot<Element>>) {
550 let current_hover_target = self.current_hover_target.get();
551 if current_hover_target == new_hover_target {
552 return;
553 }
554
555 let previous_hover_target = self.current_hover_target.get();
556 self.current_hover_target.set(new_hover_target.as_deref());
557
558 if let Some(target) = self.current_hover_target.get() {
561 if let Some(anchor) = target
562 .upcast::<Node>()
563 .inclusive_ancestors(ShadowIncluding::Yes)
564 .find_map(DomRoot::downcast::<HTMLAnchorElement>)
565 {
566 let status = anchor
567 .full_href_url_for_user_interface()
568 .map(|url| url.to_string());
569 self.window
570 .send_to_embedder(EmbedderMsg::Status(self.window.webview_id(), status));
571 return;
572 }
573 }
574
575 if previous_hover_target.is_none_or(|previous_hover_target| {
580 previous_hover_target
581 .upcast::<Node>()
582 .inclusive_ancestors(ShadowIncluding::Yes)
583 .any(|node| node.is::<HTMLAnchorElement>())
584 }) {
585 self.window
586 .send_to_embedder(EmbedderMsg::Status(self.window.webview_id(), None));
587 }
588 }
589
590 pub(crate) fn handle_refresh_cursor(&self) {
591 let Some(most_recent_mousemove_point) = self.most_recent_mousemove_point.get() else {
592 return;
593 };
594
595 let Some(hit_test_result) = self
596 .window
597 .hit_test_from_point_in_viewport(most_recent_mousemove_point)
598 else {
599 return;
600 };
601
602 self.set_cursor(Some(hit_test_result.cursor));
603 }
604
605 fn handle_native_mouse_button_event(
608 &self,
609 event: MouseButtonEvent,
610 input_event: &ConstellationInputEvent,
611 can_gc: CanGc,
612 ) {
613 let Some(hit_test_result) = self.window.hit_test_from_input_event(input_event) else {
615 return;
616 };
617
618 debug!(
619 "{:?}: at {:?}",
620 event.action, hit_test_result.point_in_frame
621 );
622
623 let Some(element) = hit_test_result
624 .node
625 .inclusive_ancestors(ShadowIncluding::Yes)
626 .find_map(DomRoot::downcast::<Element>)
627 else {
628 return;
629 };
630
631 let node = element.upcast::<Node>();
632 debug!("{:?} on {:?}", event.action, node.debug_str());
633
634 if element.is_actually_disabled() {
638 return;
639 }
640
641 let mouse_event_type_string = match event.action {
642 embedder_traits::MouseButtonAction::Up => "mouseup",
643 embedder_traits::MouseButtonAction::Down => "mousedown",
644 };
645
646 if event.action == MouseButtonAction::Down {
653 self.click_counting_info
654 .borrow_mut()
655 .reset_click_count_if_necessary(event.button, hit_test_result.point_in_frame);
656 }
657
658 let dom_event = DomRoot::upcast::<Event>(MouseEvent::for_platform_button_event(
659 mouse_event_type_string,
660 event,
661 input_event.pressed_mouse_buttons,
662 &self.window,
663 &hit_test_result,
664 input_event.active_keyboard_modifiers,
665 self.click_counting_info.borrow().count + 1,
666 can_gc,
667 ));
668
669 let activatable = element.as_maybe_activatable();
670 match event.action {
671 MouseButtonAction::Down => {
672 self.last_mouse_button_down_point
673 .set(Some(hit_test_result.point_in_frame));
674
675 if let Some(a) = activatable {
676 a.enter_formal_activation_state();
677 }
678
679 let target_el = element.find_focusable_shadow_host_if_necessary();
686
687 let document = self.window.Document();
688 document.begin_focus_transaction();
689
690 document.request_focus(None, FocusInitiator::Local, can_gc);
692 document.request_focus(target_el.as_deref(), FocusInitiator::Local, can_gc);
693
694 let result = dom_event.dispatch(node.upcast(), false, can_gc);
696
697 if result && document.has_focus_transaction() {
700 document.commit_focus_transaction(FocusInitiator::Local, can_gc);
701 }
702
703 if let MouseButton::Right = event.button {
706 self.maybe_show_context_menu(
707 node.upcast(),
708 &hit_test_result,
709 input_event,
710 can_gc,
711 );
712 }
713 },
714 MouseButtonAction::Up => {
716 if let Some(a) = activatable {
717 a.exit_formal_activation_state();
718 }
719
720 dom_event.dispatch(node.upcast(), false, can_gc);
724
725 self.click_counting_info
729 .borrow_mut()
730 .increment_click_count(event.button, hit_test_result.point_in_frame);
731
732 self.maybe_trigger_click_for_mouse_button_down_event(
733 event,
734 input_event,
735 &hit_test_result,
736 &element,
737 can_gc,
738 );
739 },
740 }
741 }
742
743 fn maybe_trigger_click_for_mouse_button_down_event(
746 &self,
747 event: MouseButtonEvent,
748 input_event: &ConstellationInputEvent,
749 hit_test_result: &HitTestResult,
750 element: &Element,
751 can_gc: CanGc,
752 ) {
753 if event.button != MouseButton::Left {
754 return;
755 }
756
757 let Some(last_mouse_button_down_point) = self.last_mouse_button_down_point.take() else {
758 return;
759 };
760
761 let distance = last_mouse_button_down_point.distance_to(hit_test_result.point_in_frame);
762 let maximum_click_distance = 10.0 * self.window.device_pixel_ratio().get();
763 if distance > maximum_click_distance {
764 return;
765 }
766
767 let delegated = element.find_focusable_shadow_host_if_necessary();
772 let element = delegated.as_deref().unwrap_or(element);
773 self.most_recently_clicked_element.set(Some(element));
774
775 let click_count = self.click_counting_info.borrow().count;
776 element.set_click_in_progress(true);
777 MouseEvent::for_platform_button_event(
778 "click",
779 event,
780 input_event.pressed_mouse_buttons,
781 &self.window,
782 hit_test_result,
783 input_event.active_keyboard_modifiers,
784 click_count,
785 can_gc,
786 )
787 .upcast::<Event>()
788 .dispatch(element.upcast(), false, can_gc);
789 element.set_click_in_progress(false);
790
791 if click_count % 2 == 0 {
800 MouseEvent::for_platform_button_event(
801 "dblclick",
802 event,
803 input_event.pressed_mouse_buttons,
804 &self.window,
805 hit_test_result,
806 input_event.active_keyboard_modifiers,
807 2,
808 can_gc,
809 )
810 .upcast::<Event>()
811 .dispatch(element.upcast(), false, can_gc);
812 }
813 }
814
815 fn maybe_show_context_menu(
817 &self,
818 target: &EventTarget,
819 hit_test_result: &HitTestResult,
820 input_event: &ConstellationInputEvent,
821 can_gc: CanGc,
822 ) {
823 let menu_event = PointerEvent::new(
825 &self.window, DOMString::from("contextmenu"), EventBubbles::Bubbles, EventCancelable::Cancelable, Some(&self.window), 0, hit_test_result.point_in_frame.to_i32(),
832 hit_test_result.point_in_frame.to_i32(),
833 hit_test_result
834 .point_relative_to_initial_containing_block
835 .to_i32(),
836 input_event.active_keyboard_modifiers,
837 2i16, input_event.pressed_mouse_buttons,
839 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,
856 );
857
858 let result = menu_event.upcast::<Event>().fire(target, can_gc);
860
861 if result {
863 self.window
864 .Document()
865 .embedder_controls()
866 .show_context_menu(hit_test_result);
867 };
868 }
869
870 fn handle_touch_event(
871 &self,
872 event: EmbedderTouchEvent,
873 input_event: &ConstellationInputEvent,
874 can_gc: CanGc,
875 ) -> InputEventResult {
876 let Some(hit_test_result) = self.window.hit_test_from_input_event(input_event) else {
878 self.update_active_touch_points_when_early_return(event);
879 return Default::default();
880 };
881
882 let TouchId(identifier) = event.id;
883 let event_name = match event.event_type {
884 TouchEventType::Down => "touchstart",
885 TouchEventType::Move => "touchmove",
886 TouchEventType::Up => "touchend",
887 TouchEventType::Cancel => "touchcancel",
888 };
889
890 let Some(el) = hit_test_result
891 .node
892 .inclusive_ancestors(ShadowIncluding::Yes)
893 .find_map(DomRoot::downcast::<Element>)
894 else {
895 self.update_active_touch_points_when_early_return(event);
896 return Default::default();
897 };
898
899 let target = DomRoot::upcast::<EventTarget>(el);
900 let window = &*self.window;
901
902 let client_x = Finite::wrap(hit_test_result.point_in_frame.x as f64);
903 let client_y = Finite::wrap(hit_test_result.point_in_frame.y as f64);
904 let page_x =
905 Finite::wrap(hit_test_result.point_in_frame.x as f64 + window.PageXOffset() as f64);
906 let page_y =
907 Finite::wrap(hit_test_result.point_in_frame.y as f64 + window.PageYOffset() as f64);
908
909 let touch = Touch::new(
910 window, identifier, &target, client_x,
911 client_y, client_x, client_y, page_x, page_y, can_gc,
913 );
914
915 match event.event_type {
916 TouchEventType::Down => {
917 self.active_touch_points
919 .borrow_mut()
920 .push(Dom::from_ref(&*touch));
921 },
922 TouchEventType::Move => {
923 let mut active_touch_points = self.active_touch_points.borrow_mut();
925 match active_touch_points
926 .iter_mut()
927 .find(|t| t.Identifier() == identifier)
928 {
929 Some(t) => *t = Dom::from_ref(&*touch),
930 None => warn!("Got a touchmove event for a non-active touch point"),
931 }
932 },
933 TouchEventType::Up | TouchEventType::Cancel => {
934 let mut active_touch_points = self.active_touch_points.borrow_mut();
936 match active_touch_points
937 .iter()
938 .position(|t| t.Identifier() == identifier)
939 {
940 Some(i) => {
941 active_touch_points.swap_remove(i);
942 },
943 None => warn!("Got a touchend event for a non-active touch point"),
944 }
945 },
946 }
947
948 rooted_vec!(let mut target_touches);
949 let touches = {
950 let touches = self.active_touch_points.borrow();
951 target_touches.extend(touches.iter().filter(|t| t.Target() == target).cloned());
952 TouchList::new(window, touches.r(), can_gc)
953 };
954
955 let touch_event = TouchEvent::new(
956 window,
957 DOMString::from(event_name),
958 EventBubbles::Bubbles,
959 EventCancelable::from(event.is_cancelable()),
960 EventComposed::Composed,
961 Some(window),
962 0i32,
963 &touches,
964 &TouchList::new(window, from_ref(&&*touch), can_gc),
965 &TouchList::new(window, target_touches.r(), can_gc),
966 false,
968 false,
969 false,
970 false,
971 can_gc,
972 );
973
974 let event = touch_event.upcast::<Event>();
975 event.fire(&target, can_gc);
976 event.flags().into()
977 }
978
979 fn update_active_touch_points_when_early_return(&self, event: EmbedderTouchEvent) {
981 match event.event_type {
982 TouchEventType::Down => {
983 },
987 TouchEventType::Move => {
988 },
991 TouchEventType::Up | TouchEventType::Cancel => {
992 let mut active_touch_points = self.active_touch_points.borrow_mut();
994 match active_touch_points
995 .iter()
996 .position(|t| t.Identifier() == event.id.0)
997 {
998 Some(i) => {
999 active_touch_points.swap_remove(i);
1000 },
1001 None => warn!("Got a touchend event for a non-active touch point"),
1002 }
1003 },
1004 }
1005 }
1006
1007 fn handle_keyboard_event(
1009 &self,
1010 keyboard_event: EmbedderKeyboardEvent,
1011 can_gc: CanGc,
1012 ) -> InputEventResult {
1013 let document = self.window.Document();
1014 let focused = document.get_focused_element();
1015 let body = document.GetBody();
1016
1017 let target = match (&focused, &body) {
1018 (Some(focused), _) => focused.upcast(),
1019 (&None, Some(body)) => body.upcast(),
1020 (&None, &None) => self.window.upcast(),
1021 };
1022
1023 let keyevent = KeyboardEvent::new(
1024 &self.window,
1025 DOMString::from(keyboard_event.event.state.event_type()),
1026 true,
1027 true,
1028 Some(&self.window),
1029 0,
1030 keyboard_event.event.key.clone(),
1031 DOMString::from(keyboard_event.event.code.to_string()),
1032 keyboard_event.event.location as u32,
1033 keyboard_event.event.repeat,
1034 keyboard_event.event.is_composing,
1035 keyboard_event.event.modifiers,
1036 0,
1037 keyboard_event.event.key.legacy_keycode(),
1038 can_gc,
1039 );
1040
1041 let event = keyevent.upcast::<Event>();
1042 event.fire(target, can_gc);
1043
1044 let mut flags = event.flags();
1045 if flags.contains(EventFlags::Canceled) {
1046 return flags.into();
1047 }
1048
1049 let is_character_value_key = matches!(
1055 keyboard_event.event.key,
1056 Key::Character(_) | Key::Named(NamedKey::Enter)
1057 );
1058 if keyboard_event.event.state == KeyState::Down &&
1059 is_character_value_key &&
1060 !keyboard_event.event.is_composing
1061 {
1062 let keypress_event = KeyboardEvent::new(
1064 &self.window,
1065 DOMString::from("keypress"),
1066 true,
1067 true,
1068 Some(&self.window),
1069 0,
1070 keyboard_event.event.key.clone(),
1071 DOMString::from(keyboard_event.event.code.to_string()),
1072 keyboard_event.event.location as u32,
1073 keyboard_event.event.repeat,
1074 keyboard_event.event.is_composing,
1075 keyboard_event.event.modifiers,
1076 keyboard_event.event.key.legacy_charcode(),
1077 0,
1078 can_gc,
1079 );
1080 let event = keypress_event.upcast::<Event>();
1081 event.fire(target, can_gc);
1082 flags = event.flags();
1083 }
1084
1085 if flags.contains(EventFlags::Canceled) {
1086 return flags.into();
1087 }
1088
1089 if (keyboard_event.event.key == Key::Named(NamedKey::Enter) ||
1095 keyboard_event.event.code == Code::Space) &&
1096 keyboard_event.event.state == KeyState::Up
1097 {
1098 if let Some(elem) = target.downcast::<Element>() {
1099 elem.upcast::<Node>()
1100 .fire_synthetic_pointer_event_not_trusted(DOMString::from("click"), can_gc);
1101 }
1102 }
1103
1104 flags.into()
1105 }
1106
1107 fn handle_ime_event(&self, event: ImeEvent, can_gc: CanGc) -> InputEventResult {
1108 let document = self.window.Document();
1109 let composition_event = match event {
1110 ImeEvent::Dismissed => {
1111 document.request_focus(
1112 document.GetBody().as_ref().map(|e| e.upcast()),
1113 FocusInitiator::Local,
1114 can_gc,
1115 );
1116 return Default::default();
1117 },
1118 ImeEvent::Composition(composition_event) => composition_event,
1119 };
1120
1121 let focused = document.get_focused_element();
1126 let target = if let Some(elem) = &focused {
1127 elem.upcast()
1128 } else {
1129 return Default::default();
1131 };
1132
1133 let cancelable = composition_event.state == keyboard_types::CompositionState::Start;
1134 let event = CompositionEvent::new(
1135 &self.window,
1136 DOMString::from(composition_event.state.event_type()),
1137 true,
1138 cancelable,
1139 Some(&self.window),
1140 0,
1141 DOMString::from(composition_event.data),
1142 can_gc,
1143 );
1144
1145 let event = event.upcast::<Event>();
1146 event.fire(target, can_gc);
1147 event.flags().into()
1148 }
1149
1150 fn handle_wheel_event(
1151 &self,
1152 event: EmbedderWheelEvent,
1153 input_event: &ConstellationInputEvent,
1154 can_gc: CanGc,
1155 ) -> InputEventResult {
1156 let Some(hit_test_result) = self.window.hit_test_from_input_event(input_event) else {
1158 return Default::default();
1159 };
1160
1161 let Some(el) = hit_test_result
1162 .node
1163 .inclusive_ancestors(ShadowIncluding::Yes)
1164 .find_map(DomRoot::downcast::<Element>)
1165 else {
1166 return Default::default();
1167 };
1168
1169 let node = el.upcast::<Node>();
1170 let wheel_event_type_string = "wheel".to_owned();
1171 debug!(
1172 "{}: on {:?} at {:?}",
1173 wheel_event_type_string,
1174 node.debug_str(),
1175 hit_test_result.point_in_frame
1176 );
1177
1178 let dom_event = WheelEvent::new(
1180 &self.window,
1181 DOMString::from(wheel_event_type_string),
1182 EventBubbles::Bubbles,
1183 EventCancelable::Cancelable,
1184 Some(&self.window),
1185 0i32,
1186 hit_test_result.point_in_frame.to_i32(),
1187 hit_test_result.point_in_frame.to_i32(),
1188 hit_test_result
1189 .point_relative_to_initial_containing_block
1190 .to_i32(),
1191 input_event.active_keyboard_modifiers,
1192 0i16,
1193 input_event.pressed_mouse_buttons,
1194 None,
1195 None,
1196 Finite::wrap(-event.delta.x),
1201 Finite::wrap(-event.delta.y),
1202 Finite::wrap(-event.delta.z),
1203 event.delta.mode as u32,
1204 can_gc,
1205 );
1206
1207 let dom_event = dom_event.upcast::<Event>();
1208 dom_event.set_trusted(true);
1209 dom_event.fire(node.upcast(), can_gc);
1210
1211 dom_event.flags().into()
1212 }
1213
1214 #[cfg(feature = "gamepad")]
1215 fn handle_gamepad_event(&self, gamepad_event: EmbedderGamepadEvent) {
1216 match gamepad_event {
1217 EmbedderGamepadEvent::Connected(index, name, bounds, supported_haptic_effects) => {
1218 self.handle_gamepad_connect(
1219 index.0,
1220 name,
1221 bounds.axis_bounds,
1222 bounds.button_bounds,
1223 supported_haptic_effects,
1224 );
1225 },
1226 EmbedderGamepadEvent::Disconnected(index) => {
1227 self.handle_gamepad_disconnect(index.0);
1228 },
1229 EmbedderGamepadEvent::Updated(index, update_type) => {
1230 self.receive_new_gamepad_button_or_axis(index.0, update_type);
1231 },
1232 };
1233 }
1234
1235 #[cfg(feature = "gamepad")]
1237 fn handle_gamepad_connect(
1238 &self,
1239 _index: usize,
1243 name: String,
1244 axis_bounds: (f64, f64),
1245 button_bounds: (f64, f64),
1246 supported_haptic_effects: GamepadSupportedHapticEffects,
1247 ) {
1248 let trusted_window = Trusted::new(&*self.window);
1251 self.window
1252 .upcast::<GlobalScope>()
1253 .task_manager()
1254 .gamepad_task_source()
1255 .queue(task!(gamepad_connected: move || {
1256 let window = trusted_window.root();
1257
1258 let navigator = window.Navigator();
1259 let selected_index = navigator.select_gamepad_index();
1260 let gamepad = Gamepad::new(
1261 &window,
1262 selected_index,
1263 name,
1264 "standard".into(),
1265 axis_bounds,
1266 button_bounds,
1267 supported_haptic_effects,
1268 false,
1269 CanGc::note(),
1270 );
1271 navigator.set_gamepad(selected_index as usize, &gamepad, CanGc::note());
1272 }));
1273 }
1274
1275 #[cfg(feature = "gamepad")]
1277 fn handle_gamepad_disconnect(&self, index: usize) {
1278 let trusted_window = Trusted::new(&*self.window);
1279 self.window
1280 .upcast::<GlobalScope>()
1281 .task_manager()
1282 .gamepad_task_source()
1283 .queue(task!(gamepad_disconnected: move || {
1284 let window = trusted_window.root();
1285 let navigator = window.Navigator();
1286 if let Some(gamepad) = navigator.get_gamepad(index) {
1287 if window.Document().is_fully_active() {
1288 gamepad.update_connected(false, gamepad.exposed(), CanGc::note());
1289 navigator.remove_gamepad(index);
1290 }
1291 }
1292 }));
1293 }
1294
1295 #[cfg(feature = "gamepad")]
1297 fn receive_new_gamepad_button_or_axis(&self, index: usize, update_type: GamepadUpdateType) {
1298 let trusted_window = Trusted::new(&*self.window);
1299
1300 self.window.upcast::<GlobalScope>().task_manager().gamepad_task_source().queue(
1302 task!(update_gamepad_state: move || {
1303 let window = trusted_window.root();
1304 let navigator = window.Navigator();
1305 if let Some(gamepad) = navigator.get_gamepad(index) {
1306 let current_time = window.Performance().Now();
1307 gamepad.update_timestamp(*current_time);
1308 match update_type {
1309 GamepadUpdateType::Axis(index, value) => {
1310 gamepad.map_and_normalize_axes(index, value);
1311 },
1312 GamepadUpdateType::Button(index, value) => {
1313 gamepad.map_and_normalize_buttons(index, value);
1314 }
1315 };
1316 if !navigator.has_gamepad_gesture() && contains_user_gesture(update_type) {
1317 navigator.set_has_gamepad_gesture(true);
1318 navigator.GetGamepads()
1319 .iter()
1320 .filter_map(|g| g.as_ref())
1321 .for_each(|gamepad| {
1322 gamepad.set_exposed(true);
1323 gamepad.update_timestamp(*current_time);
1324 let new_gamepad = Trusted::new(&**gamepad);
1325 if window.Document().is_fully_active() {
1326 window.upcast::<GlobalScope>().task_manager().gamepad_task_source().queue(
1327 task!(update_gamepad_connect: move || {
1328 let gamepad = new_gamepad.root();
1329 gamepad.notify_event(GamepadEventType::Connected, CanGc::note());
1330 })
1331 );
1332 }
1333 });
1334 }
1335 }
1336 })
1337 );
1338 }
1339
1340 pub(crate) fn handle_editing_action(
1342 &self,
1343 element: Option<DomRoot<Element>>,
1344 action: EditingActionEvent,
1345 can_gc: CanGc,
1346 ) -> InputEventResult {
1347 let clipboard_event_type = match action {
1348 EditingActionEvent::Copy => ClipboardEventType::Copy,
1349 EditingActionEvent::Cut => ClipboardEventType::Cut,
1350 EditingActionEvent::Paste => ClipboardEventType::Paste,
1351 };
1352
1353 let script_triggered = false;
1355
1356 let script_may_access_clipboard = false;
1360
1361 if script_triggered && !script_may_access_clipboard {
1363 return InputEventResult::empty();
1364 }
1365
1366 let clipboard_event =
1368 self.fire_clipboard_event(element.clone(), clipboard_event_type, can_gc);
1369
1370 let event = clipboard_event.upcast::<Event>();
1373 if !event.IsTrusted() {
1374 return event.flags().into();
1375 }
1376
1377 if event.DefaultPrevented() {
1379 let event_type = event.Type();
1380 match_domstring_ascii!(event_type,
1381
1382 "copy" => {
1383 if let Some(clipboard_data) = clipboard_event.get_clipboard_data() {
1386 let drag_data_store =
1387 clipboard_data.data_store().expect("This shouldn't fail");
1388 self.write_content_to_the_clipboard(&drag_data_store);
1389 }
1390 },
1391 "cut" => {
1392 if let Some(clipboard_data) = clipboard_event.get_clipboard_data() {
1395 let drag_data_store =
1396 clipboard_data.data_store().expect("This shouldn't fail");
1397 self.write_content_to_the_clipboard(&drag_data_store);
1398 }
1399
1400 self.fire_clipboard_event(element, ClipboardEventType::Change, can_gc);
1402 },
1403 "paste" => (),
1407 _ => (),
1408 )
1409 }
1410
1411 event.flags().into()
1414 }
1415
1416 pub(crate) fn fire_clipboard_event(
1418 &self,
1419 target: Option<DomRoot<Element>>,
1420 clipboard_event_type: ClipboardEventType,
1421 can_gc: CanGc,
1422 ) -> DomRoot<ClipboardEvent> {
1423 let clipboard_event = ClipboardEvent::new(
1424 &self.window,
1425 None,
1426 DOMString::from(clipboard_event_type.as_str()),
1427 EventBubbles::Bubbles,
1428 EventCancelable::Cancelable,
1429 None,
1430 can_gc,
1431 );
1432
1433 let mut drag_data_store = DragDataStore::new();
1436
1437 let trusted = true;
1441
1442 let document = self.window.Document();
1444 let target = target.or(document.get_focused_element());
1445 let target = target
1446 .map(|target| DomRoot::from_ref(target.upcast()))
1447 .or_else(|| {
1448 document
1449 .GetBody()
1450 .map(|body| DomRoot::from_ref(body.upcast()))
1451 })
1452 .unwrap_or_else(|| DomRoot::from_ref(self.window.upcast()));
1453
1454 match clipboard_event_type {
1457 ClipboardEventType::Copy | ClipboardEventType::Cut => {
1458 drag_data_store.set_mode(Mode::ReadWrite);
1460 },
1461 ClipboardEventType::Paste => {
1462 let (callback, receiver) =
1463 GenericCallback::new_blocking().expect("Could not create callback");
1464 self.window.send_to_embedder(EmbedderMsg::GetClipboardText(
1465 self.window.webview_id(),
1466 callback,
1467 ));
1468 let text_contents = receiver
1469 .recv()
1470 .map(Result::unwrap_or_default)
1471 .unwrap_or_default();
1472
1473 drag_data_store.set_mode(Mode::ReadOnly);
1475 if trusted {
1477 let data = DOMString::from(text_contents.to_string());
1481 let type_ = DOMString::from("text/plain");
1482 let _ = drag_data_store.add(Kind::Text { data, type_ });
1483
1484 }
1490 },
1491 ClipboardEventType::Change => (),
1492 }
1493
1494 let clipboard_event_data = DataTransfer::new(
1496 &self.window,
1497 Rc::new(RefCell::new(Some(drag_data_store))),
1498 can_gc,
1499 );
1500
1501 clipboard_event.set_clipboard_data(Some(&clipboard_event_data));
1503
1504 let event = clipboard_event.upcast::<Event>();
1506 event.set_trusted(trusted);
1507
1508 event.set_composed(true);
1510
1511 event.dispatch(&target, false, can_gc);
1513
1514 DomRoot::from(clipboard_event)
1515 }
1516
1517 fn write_content_to_the_clipboard(&self, drag_data_store: &DragDataStore) {
1519 if drag_data_store.list_len() > 0 {
1521 self.window
1523 .send_to_embedder(EmbedderMsg::ClearClipboard(self.window.webview_id()));
1524 for item in drag_data_store.iter_item_list() {
1526 match item {
1527 Kind::Text { data, .. } => {
1528 self.window.send_to_embedder(EmbedderMsg::SetClipboardText(
1532 self.window.webview_id(),
1533 data.to_string(),
1534 ));
1535 },
1536 Kind::File { .. } => {
1537 },
1541 }
1542 }
1543 } else {
1544 if drag_data_store.clear_was_called {
1546 self.window
1548 .send_to_embedder(EmbedderMsg::ClearClipboard(self.window.webview_id()));
1549 }
1552 }
1553 }
1554
1555 #[expect(unsafe_code)]
1558 fn handle_embedder_scroll_event(&self, event: ScrollEvent) {
1559 let document = self.window.Document();
1561 if event.external_id.is_root() {
1562 document.handle_viewport_scroll_event();
1563 } else {
1564 let Some(node_id) = node_id_from_scroll_id(event.external_id.0 as usize) else {
1566 return;
1567 };
1568 let node = unsafe {
1569 node::from_untrusted_node_address(UntrustedNodeAddress::from_id(node_id))
1570 };
1571 let Some(element) = node
1572 .inclusive_ancestors(ShadowIncluding::Yes)
1573 .find_map(DomRoot::downcast::<Element>)
1574 else {
1575 return;
1576 };
1577
1578 document.handle_element_scroll_event(&element);
1579 }
1580 }
1581
1582 pub(crate) fn run_default_keyboard_event_handler(&self, event: &KeyboardEvent) {
1583 if event.upcast::<Event>().type_() != atom!("keydown") {
1584 return;
1585 }
1586 if !event.modifiers().is_empty() {
1587 return;
1588 }
1589 let scroll = match event.key() {
1590 Key::Named(NamedKey::ArrowDown) => KeyboardScroll::Down,
1591 Key::Named(NamedKey::ArrowLeft) => KeyboardScroll::Left,
1592 Key::Named(NamedKey::ArrowRight) => KeyboardScroll::Right,
1593 Key::Named(NamedKey::ArrowUp) => KeyboardScroll::Up,
1594 Key::Named(NamedKey::End) => KeyboardScroll::End,
1595 Key::Named(NamedKey::Home) => KeyboardScroll::Home,
1596 Key::Named(NamedKey::PageDown) => KeyboardScroll::PageDown,
1597 Key::Named(NamedKey::PageUp) => KeyboardScroll::PageUp,
1598 _ => return,
1599 };
1600 self.do_keyboard_scroll(scroll);
1601 }
1602
1603 pub(crate) fn do_keyboard_scroll(&self, scroll: KeyboardScroll) {
1604 let scroll_axis = match scroll {
1605 KeyboardScroll::Left | KeyboardScroll::Right => ScrollingBoxAxis::X,
1606 _ => ScrollingBoxAxis::Y,
1607 };
1608
1609 let document = self.window.Document();
1610 let mut scrolling_box = document
1611 .get_focused_element()
1612 .or(self.most_recently_clicked_element.get())
1613 .and_then(|element| element.scrolling_box(ScrollContainerQueryFlags::Inclusive))
1614 .unwrap_or_else(|| {
1615 document.viewport_scrolling_box(ScrollContainerQueryFlags::Inclusive)
1616 });
1617
1618 while !scrolling_box.can_keyboard_scroll_in_axis(scroll_axis) {
1619 if scrolling_box.is_viewport() {
1621 break;
1622 }
1623 let parent = scrolling_box.parent().unwrap_or_else(|| {
1624 document.viewport_scrolling_box(ScrollContainerQueryFlags::Inclusive)
1625 });
1626 scrolling_box = parent;
1627 }
1628
1629 let calculate_current_scroll_offset_and_delta = || {
1630 const LINE_HEIGHT: f32 = 76.0;
1631 const LINE_WIDTH: f32 = 76.0;
1632
1633 let current_scroll_offset = scrolling_box.scroll_position();
1634 (
1635 current_scroll_offset,
1636 match scroll {
1637 KeyboardScroll::Home => Vector2D::new(0.0, -current_scroll_offset.y),
1638 KeyboardScroll::End => Vector2D::new(
1639 0.0,
1640 -current_scroll_offset.y + scrolling_box.content_size().height -
1641 scrolling_box.size().height,
1642 ),
1643 KeyboardScroll::PageDown => {
1644 Vector2D::new(0.0, scrolling_box.size().height - 2.0 * LINE_HEIGHT)
1645 },
1646 KeyboardScroll::PageUp => {
1647 Vector2D::new(0.0, 2.0 * LINE_HEIGHT - scrolling_box.size().height)
1648 },
1649 KeyboardScroll::Up => Vector2D::new(0.0, -LINE_HEIGHT),
1650 KeyboardScroll::Down => Vector2D::new(0.0, LINE_HEIGHT),
1651 KeyboardScroll::Left => Vector2D::new(-LINE_WIDTH, 0.0),
1652 KeyboardScroll::Right => Vector2D::new(LINE_WIDTH, 0.0),
1653 },
1654 )
1655 };
1656
1657 let parent_pipeline = self.window.parent_info();
1661 if scrolling_box.is_viewport() && parent_pipeline.is_none() {
1662 let (_, delta) = calculate_current_scroll_offset_and_delta();
1663 self.window
1664 .paint_api()
1665 .scroll_viewport_by_delta(self.window.webview_id(), delta);
1666 }
1667
1668 if !scrolling_box.can_keyboard_scroll_in_axis(scroll_axis) {
1671 assert!(scrolling_box.is_viewport());
1672
1673 let window_proxy = document.window().window_proxy();
1674 if let Some(iframe) = window_proxy.frame_element() {
1675 let cx = GlobalScope::get_cx();
1678 let iframe_window = iframe.owner_window();
1679 let _ac = JSAutoRealm::new(*cx, iframe_window.reflector().get_jsobject().get());
1680 iframe_window
1681 .Document()
1682 .event_handler()
1683 .do_keyboard_scroll(scroll);
1684 } else if let Some(parent_pipeline) = parent_pipeline {
1685 document.window().send_to_constellation(
1689 ScriptToConstellationMessage::ForwardKeyboardScroll(parent_pipeline, scroll),
1690 );
1691 };
1692 return;
1693 }
1694
1695 let (current_scroll_offset, delta) = calculate_current_scroll_offset_and_delta();
1696 scrolling_box.scroll_to(delta + current_scroll_offset, ScrollBehavior::Auto);
1697 }
1698}