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