mirror of
https://github.com/servo/servo.git
synced 2025-07-22 23:03:42 +01:00
libservo: Expose a single InputEvent
type and pass it to script (#35430)
This change exposes a single `InputEvent` type and now there is only a single delegate method for this `WebViewDelegate::notify_input_event`. - Clipboard events are now handled as `EditingAction` inpute events. In the future this can include things like "Select All", etc. In addition, many parts of the dance to pass these events can now be simplified due to this abstraction. - All forwarded events are handled the same way in the `Constellation`, though they may carry an optional hit test (for events that have a `point`) which affects which `Pipeline` they are sent to. - In the `ScriptThread` we now accept these `InputEvents` and use them everywhere. Now all "compositor events" are "input events". - This allows removing several data structures which are no longer necessary. - We no longer inform the embedder when an event was handled by a WebView as that was only important for a MDI feature that will no longer be so important the full-featured `WebView` API. Signed-off-by: Martin Robinson <mrobinson@igalia.com> Co-authored-by: Mukilan Thiyagarajan <mukilan@igalia.com>
This commit is contained in:
parent
b7b8619e87
commit
0908a47780
25 changed files with 921 additions and 1200 deletions
|
@ -45,11 +45,8 @@ use devtools_traits::{
|
|||
CSSError, DevtoolScriptControlMsg, DevtoolsPageInfo, NavigationState,
|
||||
ScriptToDevtoolsControlMsg, WorkerId,
|
||||
};
|
||||
use embedder_traits::{
|
||||
EmbedderMsg, MediaSessionActionType, MouseButton, MouseEventType, Theme, TouchEventType,
|
||||
TouchId, WheelDelta,
|
||||
};
|
||||
use euclid::default::{Point2D, Rect};
|
||||
use embedder_traits::{EmbedderMsg, InputEvent, MediaSessionActionType, Theme, TouchEventAction};
|
||||
use euclid::default::Rect;
|
||||
use fonts::{FontContext, SystemFontServiceProxy};
|
||||
use headers::{HeaderMapExt, LastModified, ReferrerPolicy as ReferrerPolicyHeader};
|
||||
use html5ever::{local_name, namespace_url, ns};
|
||||
|
@ -82,10 +79,10 @@ use script_layout_interface::{
|
|||
};
|
||||
use script_traits::webdriver_msg::WebDriverScriptCommand;
|
||||
use script_traits::{
|
||||
CompositorEvent, DiscardBrowsingContext, DocumentActivity, EventResult, InitialScriptState,
|
||||
JsEvalResult, LoadData, LoadOrigin, NavigationHistoryBehavior, NewLayoutInfo, Painter,
|
||||
ProgressiveWebMetricType, ScriptMsg, ScriptThreadMessage, ScriptToConstellationChan,
|
||||
ScrollState, StructuredSerializedData, UntrustedNodeAddress, UpdatePipelineIdReason,
|
||||
ConstellationInputEvent, DiscardBrowsingContext, DocumentActivity, EventResult,
|
||||
InitialScriptState, JsEvalResult, LoadData, LoadOrigin, NavigationHistoryBehavior,
|
||||
NewLayoutInfo, Painter, ProgressiveWebMetricType, ScriptMsg, ScriptThreadMessage,
|
||||
ScriptToConstellationChan, ScrollState, StructuredSerializedData, UpdatePipelineIdReason,
|
||||
WindowSizeData, WindowSizeType,
|
||||
};
|
||||
use servo_atoms::Atom;
|
||||
|
@ -98,7 +95,7 @@ use url::Position;
|
|||
#[cfg(feature = "webgpu")]
|
||||
use webgpu::{WebGPUDevice, WebGPUMsg};
|
||||
use webrender_api::DocumentId;
|
||||
use webrender_traits::CrossProcessCompositorApi;
|
||||
use webrender_traits::{CompositorHitTestResult, CrossProcessCompositorApi};
|
||||
|
||||
use crate::document_collection::DocumentCollection;
|
||||
use crate::document_loader::DocumentLoader;
|
||||
|
@ -1003,9 +1000,7 @@ impl ScriptThread {
|
|||
fn process_mouse_move_event(
|
||||
&self,
|
||||
document: &Document,
|
||||
window: &Window,
|
||||
point: Point2D<f32>,
|
||||
node_address: Option<UntrustedNodeAddress>,
|
||||
hit_test_result: Option<CompositorHitTestResult>,
|
||||
pressed_mouse_buttons: u16,
|
||||
can_gc: CanGc,
|
||||
) {
|
||||
|
@ -1014,10 +1009,9 @@ impl ScriptThread {
|
|||
|
||||
unsafe {
|
||||
document.handle_mouse_move_event(
|
||||
point,
|
||||
&self.topmost_mouse_over_target,
|
||||
node_address,
|
||||
hit_test_result,
|
||||
pressed_mouse_buttons,
|
||||
&self.topmost_mouse_over_target,
|
||||
can_gc,
|
||||
)
|
||||
}
|
||||
|
@ -1030,6 +1024,7 @@ impl ScriptThread {
|
|||
let mut state_already_changed = false;
|
||||
|
||||
// Notify Constellation about the topmost anchor mouse over target.
|
||||
let window = document.window();
|
||||
if let Some(target) = self.topmost_mouse_over_target.get() {
|
||||
if let Some(anchor) = target
|
||||
.upcast::<Node>()
|
||||
|
@ -1045,7 +1040,7 @@ impl ScriptThread {
|
|||
let url = document.url();
|
||||
url.join(&value).map(|url| url.to_string()).ok()
|
||||
});
|
||||
let event = EmbedderMsg::Status(window.webview_id(), status);
|
||||
let event = EmbedderMsg::Status(document.webview_id(), status);
|
||||
window.send_to_embedder(event);
|
||||
|
||||
state_already_changed = true;
|
||||
|
@ -1070,7 +1065,7 @@ impl ScriptThread {
|
|||
}
|
||||
|
||||
/// Process compositor events as part of a "update the rendering task".
|
||||
fn process_pending_compositor_events(&self, pipeline_id: PipelineId, can_gc: CanGc) {
|
||||
fn proces_pending_input_events(&self, pipeline_id: PipelineId, can_gc: CanGc) {
|
||||
let Some(document) = self.documents.borrow().find_document(pipeline_id) else {
|
||||
warn!("Processing pending compositor events for closed pipeline {pipeline_id}.");
|
||||
return;
|
||||
|
@ -1084,54 +1079,30 @@ impl ScriptThread {
|
|||
|
||||
let window = document.window();
|
||||
let _realm = enter_realm(document.window());
|
||||
for event in document.take_pending_compositor_events().into_iter() {
|
||||
match event {
|
||||
CompositorEvent::ResizeEvent(new_size, size_type) => {
|
||||
window.add_resize_event(new_size, size_type);
|
||||
},
|
||||
|
||||
CompositorEvent::MouseButtonEvent(
|
||||
event_type,
|
||||
button,
|
||||
point,
|
||||
node_address,
|
||||
point_in_node,
|
||||
pressed_mouse_buttons,
|
||||
) => {
|
||||
self.handle_mouse_button_event(
|
||||
pipeline_id,
|
||||
event_type,
|
||||
button,
|
||||
point,
|
||||
node_address,
|
||||
point_in_node,
|
||||
pressed_mouse_buttons,
|
||||
for event in document.take_pending_input_events().into_iter() {
|
||||
match event.event {
|
||||
InputEvent::MouseButton(mouse_button_event) => {
|
||||
document.handle_mouse_button_event(
|
||||
mouse_button_event,
|
||||
event.hit_test_result,
|
||||
event.pressed_mouse_buttons,
|
||||
can_gc,
|
||||
);
|
||||
},
|
||||
|
||||
CompositorEvent::MouseMoveEvent(point, node_address, pressed_mouse_buttons) => {
|
||||
InputEvent::MouseMove(_) => {
|
||||
// The event itself is unecessary here, because the point in the viewport is in the hit test.
|
||||
self.process_mouse_move_event(
|
||||
&document,
|
||||
window,
|
||||
point,
|
||||
node_address,
|
||||
pressed_mouse_buttons,
|
||||
event.hit_test_result,
|
||||
event.pressed_mouse_buttons,
|
||||
can_gc,
|
||||
);
|
||||
},
|
||||
|
||||
CompositorEvent::TouchEvent(event_type, identifier, point, node_address) => {
|
||||
let touch_result = self.handle_touch_event(
|
||||
pipeline_id,
|
||||
event_type,
|
||||
identifier,
|
||||
point,
|
||||
node_address,
|
||||
can_gc,
|
||||
);
|
||||
match (event_type, touch_result) {
|
||||
(TouchEventType::Down, TouchEventResult::Processed(handled)) => {
|
||||
InputEvent::Touch(touch_event) => {
|
||||
let touch_result =
|
||||
document.handle_touch_event(touch_event, event.hit_test_result, can_gc);
|
||||
match (touch_event.action, touch_result) {
|
||||
(TouchEventAction::Down, TouchEventResult::Processed(handled)) => {
|
||||
let result = if handled {
|
||||
// TODO: Wait to see if preventDefault is called on the first touchmove event.
|
||||
EventResult::DefaultAllowed
|
||||
|
@ -1149,29 +1120,20 @@ impl ScriptThread {
|
|||
},
|
||||
}
|
||||
},
|
||||
|
||||
CompositorEvent::WheelEvent(delta, point, node_address) => {
|
||||
self.handle_wheel_event(pipeline_id, delta, point, node_address, can_gc);
|
||||
InputEvent::Wheel(wheel_event) => {
|
||||
document.handle_wheel_event(wheel_event, event.hit_test_result, can_gc);
|
||||
},
|
||||
|
||||
CompositorEvent::KeyboardEvent(key_event) => {
|
||||
document.dispatch_key_event(key_event, can_gc);
|
||||
InputEvent::Keyboard(keyboard_event) => {
|
||||
document.dispatch_key_event(keyboard_event, can_gc);
|
||||
},
|
||||
|
||||
CompositorEvent::IMEDismissedEvent => {
|
||||
document.ime_dismissed(can_gc);
|
||||
InputEvent::Ime(ime_event) => {
|
||||
document.dispatch_ime_event(ime_event, can_gc);
|
||||
},
|
||||
|
||||
CompositorEvent::CompositionEvent(composition_event) => {
|
||||
document.dispatch_composition_event(composition_event, can_gc);
|
||||
},
|
||||
|
||||
CompositorEvent::GamepadEvent(gamepad_event) => {
|
||||
InputEvent::Gamepad(gamepad_event) => {
|
||||
window.as_global_scope().handle_gamepad_event(gamepad_event);
|
||||
},
|
||||
|
||||
CompositorEvent::ClipboardEvent(clipboard_action) => {
|
||||
document.handle_clipboard_action(clipboard_action, can_gc);
|
||||
InputEvent::EditingAction(editing_action_event) => {
|
||||
document.handle_editing_action(editing_action_event, can_gc);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -1244,12 +1206,12 @@ impl ScriptThread {
|
|||
}
|
||||
|
||||
// TODO(#31581): The steps in the "Revealing the document" section need to be implemente
|
||||
// `process_pending_compositor_events` handles the focusing steps as well as other events
|
||||
// `proces_pending_input_events` handles the focusing steps as well as other events
|
||||
// from the compositor.
|
||||
|
||||
// TODO: Should this be broken and to match the specification more closely? For instance see
|
||||
// https://html.spec.whatwg.org/multipage/#flush-autofocus-candidates.
|
||||
self.process_pending_compositor_events(*pipeline_id, can_gc);
|
||||
self.proces_pending_input_events(*pipeline_id, can_gc);
|
||||
|
||||
// TODO(#31665): Implement the "run the scroll steps" from
|
||||
// https://drafts.csswg.org/cssom-view/#document-run-the-scroll-steps.
|
||||
|
@ -1469,8 +1431,8 @@ impl ScriptThread {
|
|||
)
|
||||
}
|
||||
},
|
||||
MixedMessage::FromConstellation(ScriptThreadMessage::SendEvent(id, event)) => {
|
||||
self.handle_event(id, event)
|
||||
MixedMessage::FromConstellation(ScriptThreadMessage::SendInputEvent(id, event)) => {
|
||||
self.handle_input_event(id, event)
|
||||
},
|
||||
MixedMessage::FromScript(MainThreadScriptMsg::Common(CommonScriptMsg::Task(
|
||||
_,
|
||||
|
@ -1594,7 +1556,7 @@ impl ScriptThread {
|
|||
fn categorize_msg(&self, msg: &MixedMessage) -> ScriptThreadEventCategory {
|
||||
match *msg {
|
||||
MixedMessage::FromConstellation(ref inner_msg) => match *inner_msg {
|
||||
ScriptThreadMessage::SendEvent(_, _) => ScriptThreadEventCategory::DomEvent,
|
||||
ScriptThreadMessage::SendInputEvent(_, _) => ScriptThreadEventCategory::InputEvent,
|
||||
_ => ScriptThreadEventCategory::ConstellationMsg,
|
||||
},
|
||||
// TODO https://github.com/servo/servo/issues/18998
|
||||
|
@ -1646,8 +1608,8 @@ impl ScriptThread {
|
|||
profiler_chan,
|
||||
f
|
||||
),
|
||||
ScriptThreadEventCategory::DomEvent => {
|
||||
time_profile!(ProfilerCategory::ScriptDomEvent, None, profiler_chan, f)
|
||||
ScriptThreadEventCategory::InputEvent => {
|
||||
time_profile!(ProfilerCategory::ScriptInputEvent, None, profiler_chan, f)
|
||||
},
|
||||
ScriptThreadEventCategory::FileRead => {
|
||||
time_profile!(ProfilerCategory::ScriptFileRead, None, profiler_chan, f)
|
||||
|
@ -1667,9 +1629,6 @@ impl ScriptThread {
|
|||
profiler_chan,
|
||||
f
|
||||
),
|
||||
ScriptThreadEventCategory::InputEvent => {
|
||||
time_profile!(ProfilerCategory::ScriptInputEvent, None, profiler_chan, f)
|
||||
},
|
||||
ScriptThreadEventCategory::NetworkEvent => {
|
||||
time_profile!(ProfilerCategory::ScriptNetworkEvent, None, profiler_chan, f)
|
||||
},
|
||||
|
@ -1893,7 +1852,7 @@ impl ScriptThread {
|
|||
msg @ ScriptThreadMessage::Viewport(..) |
|
||||
msg @ ScriptThreadMessage::Resize(..) |
|
||||
msg @ ScriptThreadMessage::ExitFullScreen(..) |
|
||||
msg @ ScriptThreadMessage::SendEvent(..) |
|
||||
msg @ ScriptThreadMessage::SendInputEvent(..) |
|
||||
msg @ ScriptThreadMessage::TickAllAnimations(..) |
|
||||
msg @ ScriptThreadMessage::ExitScriptThread => {
|
||||
panic!("should have handled {:?} already", msg)
|
||||
|
@ -3318,75 +3277,12 @@ impl ScriptThread {
|
|||
|
||||
/// Queue compositor events for later dispatching as part of a
|
||||
/// `update_the_rendering` task.
|
||||
fn handle_event(&self, pipeline_id: PipelineId, event: CompositorEvent) {
|
||||
fn handle_input_event(&self, pipeline_id: PipelineId, event: ConstellationInputEvent) {
|
||||
let Some(document) = self.documents.borrow().find_document(pipeline_id) else {
|
||||
warn!("Compositor event sent to closed pipeline {pipeline_id}.");
|
||||
return;
|
||||
};
|
||||
document.note_pending_compositor_event(event);
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn handle_mouse_button_event(
|
||||
&self,
|
||||
pipeline_id: PipelineId,
|
||||
mouse_event_type: MouseEventType,
|
||||
button: MouseButton,
|
||||
point: Point2D<f32>,
|
||||
node_address: Option<UntrustedNodeAddress>,
|
||||
point_in_node: Option<Point2D<f32>>,
|
||||
pressed_mouse_buttons: u16,
|
||||
can_gc: CanGc,
|
||||
) {
|
||||
let Some(document) = self.documents.borrow().find_document(pipeline_id) else {
|
||||
warn!("Message sent to closed pipeline {pipeline_id}.");
|
||||
return;
|
||||
};
|
||||
unsafe {
|
||||
document.handle_mouse_button_event(
|
||||
button,
|
||||
point,
|
||||
mouse_event_type,
|
||||
node_address,
|
||||
point_in_node,
|
||||
pressed_mouse_buttons,
|
||||
can_gc,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_touch_event(
|
||||
&self,
|
||||
pipeline_id: PipelineId,
|
||||
event_type: TouchEventType,
|
||||
identifier: TouchId,
|
||||
point: Point2D<f32>,
|
||||
node_address: Option<UntrustedNodeAddress>,
|
||||
can_gc: CanGc,
|
||||
) -> TouchEventResult {
|
||||
let document = match self.documents.borrow().find_document(pipeline_id) {
|
||||
Some(document) => document,
|
||||
None => {
|
||||
warn!("Message sent to closed pipeline {}.", pipeline_id);
|
||||
return TouchEventResult::Processed(true);
|
||||
},
|
||||
};
|
||||
unsafe { document.handle_touch_event(event_type, identifier, point, node_address, can_gc) }
|
||||
}
|
||||
|
||||
fn handle_wheel_event(
|
||||
&self,
|
||||
pipeline_id: PipelineId,
|
||||
wheel_delta: WheelDelta,
|
||||
point: Point2D<f32>,
|
||||
node_address: Option<UntrustedNodeAddress>,
|
||||
can_gc: CanGc,
|
||||
) {
|
||||
let Some(document) = self.documents.borrow().find_document(pipeline_id) else {
|
||||
warn!("Message sent to closed pipeline {pipeline_id}.");
|
||||
return;
|
||||
};
|
||||
unsafe { document.handle_wheel_event(wheel_delta, point, node_address, can_gc) };
|
||||
document.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