mirror of
https://github.com/servo/servo.git
synced 2025-08-15 02:15:33 +01:00
script: Move the majority of the input event handling code to DocumentEventHandler
(#38584)
This moves the majority of the input event handler code to the `DocumentEventHandler` helper structure. It better encapsulates event handling, hiding most of the details from both `ScriptThread` and `Document`. The benefit here is that the majority of the functions can become private and `Document` is over 1000 lines shorter. Testing: This should not change any behavior so is covered by existing WPT tests. Signed-off-by: Martin Robinson <mrobinson@igalia.com>
This commit is contained in:
parent
bd9bb77295
commit
069ad40872
8 changed files with 1519 additions and 1484 deletions
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -3164,7 +3164,9 @@ impl VirtualMethods for HTMLInputElement {
|
|||
.borrow_mut()
|
||||
.handle_clipboard_event(clipboard_event);
|
||||
if reaction.contains(ClipboardEventReaction::FireClipboardChangedEvent) {
|
||||
self.owner_document().fire_clipboardchange_event(can_gc);
|
||||
self.owner_document()
|
||||
.event_handler()
|
||||
.fire_clipboardchange_event(can_gc);
|
||||
}
|
||||
if reaction.contains(ClipboardEventReaction::QueueInputEvent) {
|
||||
self.owner_global()
|
||||
|
|
|
@ -703,7 +703,9 @@ impl VirtualMethods for HTMLTextAreaElement {
|
|||
.borrow_mut()
|
||||
.handle_clipboard_event(clipboard_event);
|
||||
if reaction.contains(ClipboardEventReaction::FireClipboardChangedEvent) {
|
||||
self.owner_document().fire_clipboardchange_event(can_gc);
|
||||
self.owner_document()
|
||||
.event_handler()
|
||||
.fire_clipboardchange_event(can_gc);
|
||||
}
|
||||
if reaction.contains(ClipboardEventReaction::QueueInputEvent) {
|
||||
self.owner_global()
|
||||
|
|
|
@ -10,6 +10,7 @@ use euclid::Point2D;
|
|||
use js::rust::HandleObject;
|
||||
use keyboard_types::Modifiers;
|
||||
use script_bindings::codegen::GenericBindings::WindowBinding::WindowMethods;
|
||||
use script_traits::ConstellationInputEvent;
|
||||
use servo_config::pref;
|
||||
use style_traits::CSSPixel;
|
||||
|
||||
|
@ -22,6 +23,7 @@ use crate::dom::bindings::inheritance::Castable;
|
|||
use crate::dom::bindings::reflector::{DomGlobal, reflect_dom_object_with_proto};
|
||||
use crate::dom::bindings::root::{DomRoot, MutNullableDom};
|
||||
use crate::dom::bindings::str::DOMString;
|
||||
use crate::dom::document::FireMouseEventType;
|
||||
use crate::dom::event::{Event, EventBubbles, EventCancelable};
|
||||
use crate::dom::eventtarget::EventTarget;
|
||||
use crate::dom::inputevent::HitTestResult;
|
||||
|
@ -181,6 +183,36 @@ impl MouseEvent {
|
|||
ev
|
||||
}
|
||||
|
||||
pub(crate) fn new_simple(
|
||||
window: &Window,
|
||||
event_name: FireMouseEventType,
|
||||
can_bubble: EventBubbles,
|
||||
cancelable: EventCancelable,
|
||||
hit_test_result: &HitTestResult,
|
||||
input_event: &ConstellationInputEvent,
|
||||
can_gc: CanGc,
|
||||
) -> DomRoot<Self> {
|
||||
Self::new(
|
||||
window,
|
||||
DOMString::from(event_name.as_str()),
|
||||
can_bubble,
|
||||
cancelable,
|
||||
Some(window),
|
||||
0i32,
|
||||
hit_test_result.point_in_frame.to_i32(),
|
||||
hit_test_result.point_in_frame.to_i32(),
|
||||
hit_test_result
|
||||
.point_relative_to_initial_containing_block
|
||||
.to_i32(),
|
||||
input_event.active_keyboard_modifiers,
|
||||
0i16,
|
||||
input_event.pressed_mouse_buttons,
|
||||
None,
|
||||
None,
|
||||
can_gc,
|
||||
)
|
||||
}
|
||||
|
||||
/// <https://w3c.github.io/uievents/#initialize-a-mouseevent>
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub(crate) fn initialize_mouse_event(
|
||||
|
|
|
@ -33,9 +33,9 @@ use devtools_traits::{ScriptToDevtoolsControlMsg, TimelineMarker, TimelineMarker
|
|||
use dom_struct::dom_struct;
|
||||
use embedder_traits::user_content_manager::{UserContentManager, UserScript};
|
||||
use embedder_traits::{
|
||||
AlertResponse, ConfirmResponse, EmbedderMsg, GamepadEvent, GamepadSupportedHapticEffects,
|
||||
GamepadUpdateType, PromptResponse, SimpleDialog, Theme, UntrustedNodeAddress, ViewportDetails,
|
||||
WebDriverJSError, WebDriverJSResult, WebDriverLoadStatus,
|
||||
AlertResponse, ConfirmResponse, EmbedderMsg, PromptResponse, SimpleDialog, Theme,
|
||||
UntrustedNodeAddress, ViewportDetails, WebDriverJSError, WebDriverJSResult,
|
||||
WebDriverLoadStatus,
|
||||
};
|
||||
use euclid::default::{Point2D as UntypedPoint2D, Rect as UntypedRect, Size2D as UntypedSize2D};
|
||||
use euclid::{Point2D, Scale, Size2D, Vector2D};
|
||||
|
@ -69,8 +69,6 @@ use num_traits::ToPrimitive;
|
|||
use profile_traits::ipc as ProfiledIpc;
|
||||
use profile_traits::mem::ProfilerChan as MemProfilerChan;
|
||||
use profile_traits::time::ProfilerChan as TimeProfilerChan;
|
||||
use script_bindings::codegen::GenericBindings::NavigatorBinding::NavigatorMethods;
|
||||
use script_bindings::codegen::GenericBindings::PerformanceBinding::PerformanceMethods;
|
||||
use script_bindings::conversions::SafeToJSValConvertible;
|
||||
use script_bindings::interfaces::WindowHelpers;
|
||||
use script_bindings::root::Root;
|
||||
|
@ -133,8 +131,6 @@ use crate::dom::document::{AnimationFrameCallback, Document};
|
|||
use crate::dom::element::Element;
|
||||
use crate::dom::event::{Event, EventBubbles, EventCancelable};
|
||||
use crate::dom::eventtarget::EventTarget;
|
||||
use crate::dom::gamepad::{Gamepad, contains_user_gesture};
|
||||
use crate::dom::gamepadevent::GamepadEventType;
|
||||
use crate::dom::globalscope::GlobalScope;
|
||||
use crate::dom::hashchangeevent::HashChangeEvent;
|
||||
use crate::dom::history::History;
|
||||
|
@ -713,126 +709,6 @@ impl Window {
|
|||
pub(crate) fn font_context(&self) -> &Arc<FontContext> {
|
||||
&self.font_context
|
||||
}
|
||||
|
||||
pub(crate) fn handle_gamepad_event(&self, gamepad_event: GamepadEvent) {
|
||||
match gamepad_event {
|
||||
GamepadEvent::Connected(index, name, bounds, supported_haptic_effects) => {
|
||||
self.handle_gamepad_connect(
|
||||
index.0,
|
||||
name,
|
||||
bounds.axis_bounds,
|
||||
bounds.button_bounds,
|
||||
supported_haptic_effects,
|
||||
);
|
||||
},
|
||||
GamepadEvent::Disconnected(index) => {
|
||||
self.handle_gamepad_disconnect(index.0);
|
||||
},
|
||||
GamepadEvent::Updated(index, update_type) => {
|
||||
self.receive_new_gamepad_button_or_axis(index.0, update_type);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/// <https://www.w3.org/TR/gamepad/#dfn-gamepadconnected>
|
||||
fn handle_gamepad_connect(
|
||||
&self,
|
||||
// As the spec actually defines how to set the gamepad index, the GilRs index
|
||||
// is currently unused, though in practice it will almost always be the same.
|
||||
// More infra is currently needed to track gamepads across windows.
|
||||
_index: usize,
|
||||
name: String,
|
||||
axis_bounds: (f64, f64),
|
||||
button_bounds: (f64, f64),
|
||||
supported_haptic_effects: GamepadSupportedHapticEffects,
|
||||
) {
|
||||
// TODO: 2. If document is not null and is not allowed to use the "gamepad" permission,
|
||||
// then abort these steps.
|
||||
let this = Trusted::new(self);
|
||||
self.upcast::<GlobalScope>()
|
||||
.task_manager()
|
||||
.gamepad_task_source()
|
||||
.queue(task!(gamepad_connected: move || {
|
||||
let window = this.root();
|
||||
|
||||
let navigator = window.Navigator();
|
||||
let selected_index = navigator.select_gamepad_index();
|
||||
let gamepad = Gamepad::new(
|
||||
&window,
|
||||
selected_index,
|
||||
name,
|
||||
"standard".into(),
|
||||
axis_bounds,
|
||||
button_bounds,
|
||||
supported_haptic_effects,
|
||||
false,
|
||||
CanGc::note(),
|
||||
);
|
||||
navigator.set_gamepad(selected_index as usize, &gamepad, CanGc::note());
|
||||
}));
|
||||
}
|
||||
|
||||
/// <https://www.w3.org/TR/gamepad/#dfn-gamepaddisconnected>
|
||||
fn handle_gamepad_disconnect(&self, index: usize) {
|
||||
let this = Trusted::new(self);
|
||||
self.upcast::<GlobalScope>()
|
||||
.task_manager()
|
||||
.gamepad_task_source()
|
||||
.queue(task!(gamepad_disconnected: move || {
|
||||
let window = this.root();
|
||||
let navigator = window.Navigator();
|
||||
if let Some(gamepad) = navigator.get_gamepad(index) {
|
||||
if window.Document().is_fully_active() {
|
||||
gamepad.update_connected(false, gamepad.exposed(), CanGc::note());
|
||||
navigator.remove_gamepad(index);
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
/// <https://www.w3.org/TR/gamepad/#receiving-inputs>
|
||||
fn receive_new_gamepad_button_or_axis(&self, index: usize, update_type: GamepadUpdateType) {
|
||||
let this = Trusted::new(self);
|
||||
|
||||
// <https://w3c.github.io/gamepad/#dfn-update-gamepad-state>
|
||||
self.upcast::<GlobalScope>().task_manager().gamepad_task_source().queue(
|
||||
task!(update_gamepad_state: move || {
|
||||
let window = this.root();
|
||||
let navigator = window.Navigator();
|
||||
if let Some(gamepad) = navigator.get_gamepad(index) {
|
||||
let current_time = window.Performance().Now();
|
||||
gamepad.update_timestamp(*current_time);
|
||||
match update_type {
|
||||
GamepadUpdateType::Axis(index, value) => {
|
||||
gamepad.map_and_normalize_axes(index, value);
|
||||
},
|
||||
GamepadUpdateType::Button(index, value) => {
|
||||
gamepad.map_and_normalize_buttons(index, value);
|
||||
}
|
||||
};
|
||||
if !navigator.has_gamepad_gesture() && contains_user_gesture(update_type) {
|
||||
navigator.set_has_gamepad_gesture(true);
|
||||
navigator.GetGamepads()
|
||||
.iter()
|
||||
.filter_map(|g| g.as_ref())
|
||||
.for_each(|gamepad| {
|
||||
gamepad.set_exposed(true);
|
||||
gamepad.update_timestamp(*current_time);
|
||||
let new_gamepad = Trusted::new(&**gamepad);
|
||||
if window.Document().is_fully_active() {
|
||||
window.upcast::<GlobalScope>().task_manager().gamepad_task_source().queue(
|
||||
task!(update_gamepad_connect: move || {
|
||||
let gamepad = new_gamepad.root();
|
||||
gamepad.notify_event(GamepadEventType::Connected, CanGc::note());
|
||||
})
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/#atob
|
||||
|
@ -3128,7 +3004,9 @@ impl Window {
|
|||
return;
|
||||
};
|
||||
|
||||
self.Document().set_cursor(hit_test_result.cursor);
|
||||
self.Document()
|
||||
.event_handler()
|
||||
.set_cursor(hit_test_result.cursor);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -358,7 +358,10 @@ pub(crate) fn follow_hyperlink(
|
|||
let document = subject.owner_document();
|
||||
let target_attribute_value =
|
||||
if subject.is::<HTMLAreaElement>() || subject.is::<HTMLAnchorElement>() {
|
||||
if document.alternate_action_keyboard_modifier_active() {
|
||||
if document
|
||||
.event_handler()
|
||||
.alternate_action_keyboard_modifier_active()
|
||||
{
|
||||
Some("_blank".into())
|
||||
} else {
|
||||
get_element_target(subject)
|
||||
|
|
|
@ -123,7 +123,7 @@ use crate::dom::customelementregistry::{
|
|||
CallbackReaction, CustomElementDefinition, CustomElementReactionStack,
|
||||
};
|
||||
use crate::dom::document::{
|
||||
Document, DocumentSource, FocusInitiator, HasBrowsingContext, IsHTMLDocument, TouchEventResult,
|
||||
Document, DocumentSource, FocusInitiator, HasBrowsingContext, IsHTMLDocument,
|
||||
};
|
||||
use crate::dom::element::Element;
|
||||
use crate::dom::globalscope::GlobalScope;
|
||||
|
@ -1039,16 +1039,6 @@ impl ScriptThread {
|
|||
debug!("Stopped script thread.");
|
||||
}
|
||||
|
||||
/// Process a compositor mouse move event.
|
||||
fn process_mouse_move_event(
|
||||
&self,
|
||||
document: &Document,
|
||||
input_event: &ConstellationInputEvent,
|
||||
can_gc: CanGc,
|
||||
) {
|
||||
unsafe { document.handle_mouse_move_event(input_event, can_gc) }
|
||||
}
|
||||
|
||||
/// Process compositor events as part of a "update the rendering task".
|
||||
fn process_pending_input_events(&self, pipeline_id: PipelineId, can_gc: CanGc) {
|
||||
let Some(document) = self.documents.borrow().find_document(pipeline_id) else {
|
||||
|
@ -1062,97 +1052,10 @@ impl ScriptThread {
|
|||
}
|
||||
ScriptThread::set_user_interacting(true);
|
||||
|
||||
let window = document.window();
|
||||
let _realm = enter_realm(document.window());
|
||||
for event in document.take_pending_input_events().into_iter() {
|
||||
document.update_active_keyboard_modifiers(event.active_keyboard_modifiers);
|
||||
|
||||
match event.event.clone() {
|
||||
InputEvent::MouseButton(mouse_button_event) => {
|
||||
document.handle_mouse_button_event(mouse_button_event, &event, can_gc);
|
||||
},
|
||||
InputEvent::MouseMove(_) => {
|
||||
self.process_mouse_move_event(&document, &event, can_gc);
|
||||
},
|
||||
InputEvent::MouseLeave(mouse_leave_event) => {
|
||||
document.handle_mouse_leave_event(&event, &mouse_leave_event, can_gc);
|
||||
},
|
||||
InputEvent::Touch(touch_event) => {
|
||||
let touch_result = document.handle_touch_event(touch_event, &event, can_gc);
|
||||
if let (TouchEventResult::Processed(handled), true) =
|
||||
(touch_result, touch_event.is_cancelable())
|
||||
{
|
||||
let sequence_id = touch_event.expect_sequence_id();
|
||||
let result = if handled {
|
||||
embedder_traits::TouchEventResult::DefaultAllowed(
|
||||
sequence_id,
|
||||
touch_event.event_type,
|
||||
)
|
||||
} else {
|
||||
embedder_traits::TouchEventResult::DefaultPrevented(
|
||||
sequence_id,
|
||||
touch_event.event_type,
|
||||
)
|
||||
};
|
||||
let message = ScriptToConstellationMessage::TouchEventProcessed(result);
|
||||
self.senders
|
||||
.pipeline_to_constellation_sender
|
||||
.send((pipeline_id, message))
|
||||
.unwrap();
|
||||
}
|
||||
},
|
||||
InputEvent::Wheel(wheel_event) => {
|
||||
document.handle_wheel_event(wheel_event, &event, can_gc);
|
||||
},
|
||||
InputEvent::Keyboard(keyboard_event) => {
|
||||
document.dispatch_key_event(keyboard_event, can_gc);
|
||||
},
|
||||
InputEvent::Ime(ime_event) => {
|
||||
document.dispatch_ime_event(ime_event, can_gc);
|
||||
},
|
||||
InputEvent::Gamepad(gamepad_event) => {
|
||||
window.handle_gamepad_event(gamepad_event);
|
||||
},
|
||||
InputEvent::EditingAction(editing_action_event) => {
|
||||
document.handle_editing_action(editing_action_event, can_gc);
|
||||
},
|
||||
InputEvent::Scroll(scroll_event) => {
|
||||
document.handle_embedder_scroll_event(scroll_event);
|
||||
},
|
||||
}
|
||||
|
||||
self.notify_webdriver_input_event_completed(pipeline_id, event.event, &document);
|
||||
}
|
||||
document.event_handler().handle_pending_input_events(can_gc);
|
||||
ScriptThread::set_user_interacting(false);
|
||||
}
|
||||
|
||||
fn notify_webdriver_input_event_completed(
|
||||
&self,
|
||||
pipeline_id: PipelineId,
|
||||
event: InputEvent,
|
||||
document: &Document,
|
||||
) {
|
||||
let Some(id) = event.webdriver_message_id() else {
|
||||
return;
|
||||
};
|
||||
|
||||
let sender = self.senders.pipeline_to_constellation_sender.clone();
|
||||
|
||||
// Webdriver should be notified once all current dom events have been processed.
|
||||
document
|
||||
.owner_global()
|
||||
.task_manager()
|
||||
.dom_manipulation_task_source()
|
||||
.queue(task!(notify_webdriver_input_event_completed: move || {
|
||||
if let Err(error) = sender.send((
|
||||
pipeline_id,
|
||||
ScriptToConstellationMessage::WebDriverInputComplete(id),
|
||||
)) {
|
||||
warn!("ScriptThread failed to send WebDriverInputComplete {id:?}: {error:?}",);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
fn cancel_scheduled_update_the_rendering(&self) {
|
||||
if let Some(timer_id) = self.scheduled_update_the_rendering.borrow_mut().take() {
|
||||
self.timer_scheduler.borrow_mut().cancel_timer(timer_id);
|
||||
|
@ -3629,23 +3532,27 @@ impl ScriptThread {
|
|||
(pixel_dist.x * pixel_dist.x + pixel_dist.y * pixel_dist.y).sqrt();
|
||||
if pixel_dist < 10.0 * document.window().device_pixel_ratio().get() {
|
||||
// Pass webdriver_id to the newly generated click event
|
||||
document.note_pending_input_event(ConstellationInputEvent {
|
||||
hit_test_result: event.hit_test_result.clone(),
|
||||
pressed_mouse_buttons: event.pressed_mouse_buttons,
|
||||
active_keyboard_modifiers: event.active_keyboard_modifiers,
|
||||
event: event.event.clone().with_webdriver_message_id(None),
|
||||
});
|
||||
document.note_pending_input_event(ConstellationInputEvent {
|
||||
hit_test_result: event.hit_test_result,
|
||||
pressed_mouse_buttons: event.pressed_mouse_buttons,
|
||||
active_keyboard_modifiers: event.active_keyboard_modifiers,
|
||||
event: InputEvent::MouseButton(MouseButtonEvent::new(
|
||||
MouseButtonAction::Click,
|
||||
mouse_button_event.button,
|
||||
mouse_button_event.point,
|
||||
))
|
||||
.with_webdriver_message_id(event.event.webdriver_message_id()),
|
||||
});
|
||||
document.event_handler().note_pending_input_event(
|
||||
ConstellationInputEvent {
|
||||
hit_test_result: event.hit_test_result.clone(),
|
||||
pressed_mouse_buttons: event.pressed_mouse_buttons,
|
||||
active_keyboard_modifiers: event.active_keyboard_modifiers,
|
||||
event: event.event.clone().with_webdriver_message_id(None),
|
||||
},
|
||||
);
|
||||
document.event_handler().note_pending_input_event(
|
||||
ConstellationInputEvent {
|
||||
hit_test_result: event.hit_test_result,
|
||||
pressed_mouse_buttons: event.pressed_mouse_buttons,
|
||||
active_keyboard_modifiers: event.active_keyboard_modifiers,
|
||||
event: InputEvent::MouseButton(MouseButtonEvent::new(
|
||||
MouseButtonAction::Click,
|
||||
mouse_button_event.button,
|
||||
mouse_button_event.point,
|
||||
))
|
||||
.with_webdriver_message_id(event.event.webdriver_message_id()),
|
||||
},
|
||||
);
|
||||
return;
|
||||
}
|
||||
},
|
||||
|
@ -3657,7 +3564,7 @@ impl ScriptThread {
|
|||
}
|
||||
}
|
||||
|
||||
document.note_pending_input_event(event);
|
||||
document.event_handler().note_pending_input_event(event);
|
||||
}
|
||||
|
||||
/// Handle a "navigate an iframe" message from the constellation.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue