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:
Martin Robinson 2025-02-12 18:07:15 +01:00 committed by GitHub
parent b7b8619e87
commit 0908a47780
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
25 changed files with 921 additions and 1200 deletions

View file

@ -17,9 +17,9 @@ use servo::ipc_channel::ipc::IpcSender;
use servo::webrender_api::units::{DeviceIntPoint, DeviceIntSize};
use servo::webrender_api::ScrollLocation;
use servo::{
AllowOrDenyRequest, AuthenticationRequest, CompositorEventVariant, FilterPattern,
GamepadHapticEffectType, LoadStatus, PermissionRequest, PromptDefinition, PromptOrigin,
PromptResult, Servo, ServoDelegate, ServoError, TouchEventType, WebView, WebViewDelegate,
AllowOrDenyRequest, AuthenticationRequest, FilterPattern, GamepadHapticEffectType, LoadStatus,
PermissionRequest, PromptDefinition, PromptOrigin, PromptResult, Servo, ServoDelegate,
ServoError, TouchEventAction, WebView, WebViewDelegate,
};
use tinyfiledialogs::{self, MessageBoxIcon};
use url::Url;
@ -280,36 +280,36 @@ impl RunningAppState {
0.0,
-self.inner().window.page_height() + 2.0 * LINE_HEIGHT,
));
webview.notify_scroll_event(scroll_location, origin, TouchEventType::Move);
webview.notify_scroll_event(scroll_location, origin, TouchEventAction::Move);
})
.shortcut(Modifiers::empty(), Key::PageUp, || {
let scroll_location = ScrollLocation::Delta(Vector2D::new(
0.0,
self.inner().window.page_height() - 2.0 * LINE_HEIGHT,
));
webview.notify_scroll_event(scroll_location, origin, TouchEventType::Move);
webview.notify_scroll_event(scroll_location, origin, TouchEventAction::Move);
})
.shortcut(Modifiers::empty(), Key::Home, || {
webview.notify_scroll_event(ScrollLocation::Start, origin, TouchEventType::Move);
webview.notify_scroll_event(ScrollLocation::Start, origin, TouchEventAction::Move);
})
.shortcut(Modifiers::empty(), Key::End, || {
webview.notify_scroll_event(ScrollLocation::End, origin, TouchEventType::Move);
webview.notify_scroll_event(ScrollLocation::End, origin, TouchEventAction::Move);
})
.shortcut(Modifiers::empty(), Key::ArrowUp, || {
let location = ScrollLocation::Delta(Vector2D::new(0.0, 3.0 * LINE_HEIGHT));
webview.notify_scroll_event(location, origin, TouchEventType::Move);
webview.notify_scroll_event(location, origin, TouchEventAction::Move);
})
.shortcut(Modifiers::empty(), Key::ArrowDown, || {
let location = ScrollLocation::Delta(Vector2D::new(0.0, -3.0 * LINE_HEIGHT));
webview.notify_scroll_event(location, origin, TouchEventType::Move);
webview.notify_scroll_event(location, origin, TouchEventAction::Move);
})
.shortcut(Modifiers::empty(), Key::ArrowLeft, || {
let location = ScrollLocation::Delta(Vector2D::new(LINE_HEIGHT, 0.0));
webview.notify_scroll_event(location, origin, TouchEventType::Move);
webview.notify_scroll_event(location, origin, TouchEventAction::Move);
})
.shortcut(Modifiers::empty(), Key::ArrowRight, || {
let location = ScrollLocation::Delta(Vector2D::new(-LINE_HEIGHT, 0.0));
webview.notify_scroll_event(location, origin, TouchEventType::Move);
webview.notify_scroll_event(location, origin, TouchEventAction::Move);
});
}
}
@ -512,13 +512,6 @@ impl WebViewDelegate for RunningAppState {
self.inner_mut().need_present = true;
}
fn notify_event_delivered(&self, webview: servo::WebView, event: CompositorEventVariant) {
if let CompositorEventVariant::MouseButtonEvent = event {
webview.raise_to_top(true);
webview.focus();
}
}
fn play_gamepad_haptic_effect(
&self,
_webview: servo::WebView,

View file

@ -128,7 +128,7 @@ impl GamepadSupport {
}
if let Some(event) = gamepad_event {
active_webview.notify_gamepad_event(event);
active_webview.notify_input_event(servo::InputEvent::Gamepad(event));
}
}
}

View file

@ -15,7 +15,7 @@ use keyboard_types::{Modifiers, ShortcutMatcher};
use log::{debug, info};
use raw_window_handle::{HasDisplayHandle, HasWindowHandle};
use servo::compositing::windowing::{
AnimationState, EmbedderCoordinates, MouseWindowEvent, WebRenderDebugOption, WindowMethods,
AnimationState, EmbedderCoordinates, WebRenderDebugOption, WindowMethods,
};
use servo::config::opts::Opts;
use servo::servo_config::pref;
@ -24,8 +24,9 @@ use servo::webrender_api::units::{DeviceIntPoint, DeviceIntRect, DeviceIntSize,
use servo::webrender_api::ScrollLocation;
use servo::webrender_traits::SurfmanRenderingContext;
use servo::{
ClipboardEventType, Cursor, Key, KeyState, KeyboardEvent, MouseButton as ServoMouseButton,
Theme, TouchEventType, TouchId, WebView, WheelDelta, WheelMode,
Cursor, InputEvent, Key, KeyState, KeyboardEvent, MouseButton as ServoMouseButton,
MouseButtonAction, MouseButtonEvent, MouseMoveEvent, Theme, TouchEvent, TouchEventAction,
TouchId, WebView, WheelDelta, WheelEvent, WheelMode,
};
use surfman::{Context, Device, SurfaceType};
use url::Url;
@ -192,7 +193,7 @@ impl Window {
for xr_window_pose in &*xr_poses {
xr_window_pose.handle_xr_translation(&event);
}
webview.notify_keyboard_event(event);
webview.notify_input_event(InputEvent::Keyboard(event));
}
fn handle_keyboard_input(&self, state: Rc<RunningAppState>, winit_event: KeyEvent) {
@ -233,7 +234,7 @@ impl Window {
for xr_window_pose in &*xr_poses {
xr_window_pose.handle_xr_rotation(&winit_event, self.modifiers_state.get());
}
webview.notify_keyboard_event(keyboard_event);
webview.notify_input_event(InputEvent::Keyboard(keyboard_event));
}
// servoshell also has key bindings that are visible to, and overridable by, the page.
@ -253,35 +254,46 @@ impl Window {
MouseButton::Left => ServoMouseButton::Left,
MouseButton::Right => ServoMouseButton::Right,
MouseButton::Middle => ServoMouseButton::Middle,
_ => ServoMouseButton::Left,
MouseButton::Back => ServoMouseButton::Back,
MouseButton::Forward => ServoMouseButton::Forward,
MouseButton::Other(value) => ServoMouseButton::Other(*value),
};
let event = match action {
let action = match action {
ElementState::Pressed => {
self.mouse_down_point.set(coords);
self.mouse_down_button.set(Some(button));
MouseWindowEvent::MouseDown(mouse_button, coords.to_f32())
},
ElementState::Released => {
let mouse_up_event = MouseWindowEvent::MouseUp(mouse_button, coords.to_f32());
match self.mouse_down_button.get() {
None => mouse_up_event,
Some(but) if button == but => {
let pixel_dist = self.mouse_down_point.get() - coords;
let pixel_dist =
((pixel_dist.x * pixel_dist.x + pixel_dist.y * pixel_dist.y) as f32)
.sqrt();
if pixel_dist < max_pixel_dist {
webview.notify_pointer_button_event(mouse_up_event);
MouseWindowEvent::Click(mouse_button, coords.to_f32())
} else {
mouse_up_event
}
},
Some(_) => mouse_up_event,
}
MouseButtonAction::Down
},
ElementState::Released => MouseButtonAction::Up,
};
webview.notify_pointer_button_event(event);
webview.notify_input_event(InputEvent::MouseButton(MouseButtonEvent {
action,
button: mouse_button,
point: coords.to_f32(),
}));
// Also send a 'click' event if this is release and the press was recorded
// to be within a 10 pixels.
//
// TODO: This should be happening within the ScriptThread.
if action != MouseButtonAction::Up {
return;
}
if let Some(mouse_down_button) = self.mouse_down_button.get() {
let pixel_dist = self.mouse_down_point.get() - coords;
let pixel_dist =
((pixel_dist.x * pixel_dist.x + pixel_dist.y * pixel_dist.y) as f32).sqrt();
if mouse_down_button == button && pixel_dist < max_pixel_dist {
webview.notify_input_event(InputEvent::MouseButton(MouseButtonEvent {
action: MouseButtonAction::Click,
button: mouse_button,
point: coords.to_f32(),
}));
}
}
}
/// Handle key events before sending them to Servo.
@ -315,13 +327,16 @@ impl Window {
);
})
.shortcut(CMD_OR_CONTROL, 'X', || {
focused_webview.notify_clipboard_event(ClipboardEventType::Cut);
focused_webview
.notify_input_event(InputEvent::EditingAction(servo::EditingActionEvent::Cut))
})
.shortcut(CMD_OR_CONTROL, 'C', || {
focused_webview.notify_clipboard_event(ClipboardEventType::Copy);
focused_webview
.notify_input_event(InputEvent::EditingAction(servo::EditingActionEvent::Copy))
})
.shortcut(CMD_OR_CONTROL, 'V', || {
focused_webview.notify_clipboard_event(ClipboardEventType::Paste);
focused_webview
.notify_input_event(InputEvent::EditingAction(servo::EditingActionEvent::Paste))
})
.shortcut(Modifiers::CONTROL, Key::F9, || {
focused_webview.capture_webrender();
@ -538,7 +553,9 @@ impl WindowPortsMethods for Window {
winit::event::WindowEvent::CursorMoved { position, .. } => {
let position = winit_position_to_euclid_point(position);
self.mouse_pos.set(position.to_i32());
webview.notify_pointer_move_event(position.to_f32());
webview.notify_input_event(InputEvent::MouseMove(MouseMoveEvent {
point: position.to_f32(),
}));
},
winit::event::WindowEvent::MouseWheel { delta, phase, .. } => {
let (mut dx, mut dy, mode) = match delta {
@ -553,14 +570,14 @@ impl WindowPortsMethods for Window {
};
// Create wheel event before snapping to the major axis of movement
let wheel_delta = WheelDelta {
let delta = WheelDelta {
x: dx,
y: dy,
z: 0.0,
mode,
};
let pos = self.mouse_pos.get();
let position = Point2D::new(pos.x as f32, pos.y as f32);
let point = Point2D::new(pos.x as f32, pos.y as f32);
// Scroll events snap to the major axis of movement, with vertical
// preferred over horizontal.
@ -571,18 +588,18 @@ impl WindowPortsMethods for Window {
}
let scroll_location = ScrollLocation::Delta(Vector2D::new(dx as f32, dy as f32));
let phase = winit_phase_to_touch_event_type(phase);
let phase = winit_phase_to_touch_event_action(phase);
// Send events
webview.notify_wheel_event(wheel_delta, position);
webview.notify_input_event(InputEvent::Wheel(WheelEvent { delta, point }));
webview.notify_scroll_event(scroll_location, self.mouse_pos.get(), phase);
},
winit::event::WindowEvent::Touch(touch) => {
let touch_event_type = winit_phase_to_touch_event_type(touch.phase);
let id = TouchId(touch.id as i32);
let position = touch.location;
let point = Point2D::new(position.x as f32, position.y as f32);
webview.notify_touch_event(touch_event_type, id, point);
webview.notify_input_event(InputEvent::Touch(TouchEvent {
action: winit_phase_to_touch_event_action(touch.phase),
id: TouchId(touch.id as i32),
point: Point2D::new(touch.location.x as f32, touch.location.y as f32),
}));
},
winit::event::WindowEvent::PinchGesture { delta, .. } => {
webview.set_pinch_zoom(delta as f32 + 1.0);
@ -673,12 +690,12 @@ impl WindowMethods for Window {
}
}
fn winit_phase_to_touch_event_type(phase: TouchPhase) -> TouchEventType {
fn winit_phase_to_touch_event_action(phase: TouchPhase) -> TouchEventAction {
match phase {
TouchPhase::Started => TouchEventType::Down,
TouchPhase::Moved => TouchEventType::Move,
TouchPhase::Ended => TouchEventType::Up,
TouchPhase::Cancelled => TouchEventType::Cancel,
TouchPhase::Started => TouchEventAction::Down,
TouchPhase::Moved => TouchEventAction::Move,
TouchPhase::Ended => TouchEventAction::Up,
TouchPhase::Cancelled => TouchEventAction::Cancel,
}
}

View file

@ -11,7 +11,7 @@ use keyboard_types::{CompositionEvent, CompositionState};
use log::{debug, error, info, warn};
use servo::base::id::WebViewId;
use servo::compositing::windowing::{
AnimationState, EmbedderCoordinates, EmbedderMethods, MouseWindowEvent, WindowMethods,
AnimationState, EmbedderCoordinates, EmbedderMethods, WindowMethods,
};
use servo::euclid::{Box2D, Point2D, Rect, Scale, Size2D, Vector2D};
use servo::servo_geometry::DeviceIndependentPixel;
@ -19,10 +19,11 @@ use servo::webrender_api::units::{DeviceIntRect, DeviceIntSize, DevicePixel, Dev
use servo::webrender_api::ScrollLocation;
use servo::webrender_traits::SurfmanRenderingContext;
use servo::{
AllowOrDenyRequest, ContextMenuResult, EmbedderProxy, EventLoopWaker, InputMethodType, Key,
KeyState, KeyboardEvent, LoadStatus, MediaSessionActionType, MediaSessionEvent, MouseButton,
AllowOrDenyRequest, ContextMenuResult, EmbedderProxy, EventLoopWaker, ImeEvent, InputEvent,
InputMethodType, Key, KeyState, KeyboardEvent, LoadStatus, MediaSessionActionType,
MediaSessionEvent, MouseButton, MouseButtonAction, MouseButtonEvent, MouseMoveEvent,
NavigationRequest, PermissionRequest, PromptDefinition, PromptOrigin, PromptResult, Servo,
ServoDelegate, ServoError, TouchEventType, TouchId, WebView, WebViewDelegate,
ServoDelegate, ServoError, TouchEvent, TouchEventAction, TouchId, WebView, WebViewDelegate,
};
use url::Url;
@ -445,7 +446,7 @@ impl RunningAppState {
self.active_webview().notify_scroll_event(
scroll_location,
Point2D::new(x, y),
TouchEventType::Down,
TouchEventAction::Down,
);
self.perform_updates();
}
@ -459,7 +460,7 @@ impl RunningAppState {
self.active_webview().notify_scroll_event(
scroll_location,
Point2D::new(x, y),
TouchEventType::Move,
TouchEventAction::Move,
);
self.perform_updates();
}
@ -474,69 +475,83 @@ impl RunningAppState {
self.active_webview().notify_scroll_event(
scroll_location,
Point2D::new(x, y),
TouchEventType::Up,
TouchEventAction::Up,
);
self.perform_updates();
}
/// Touch event: press down
pub fn touch_down(&self, x: f32, y: f32, pointer_id: i32) {
self.active_webview().notify_touch_event(
TouchEventType::Down,
TouchId(pointer_id),
Point2D::new(x, y),
);
self.active_webview()
.notify_input_event(InputEvent::Touch(TouchEvent {
action: TouchEventAction::Down,
id: TouchId(pointer_id),
point: Point2D::new(x, y),
}));
self.perform_updates();
}
/// Touch event: move touching finger
pub fn touch_move(&self, x: f32, y: f32, pointer_id: i32) {
self.active_webview().notify_touch_event(
TouchEventType::Move,
TouchId(pointer_id),
Point2D::new(x, y),
);
self.active_webview()
.notify_input_event(InputEvent::Touch(TouchEvent {
action: TouchEventAction::Move,
id: TouchId(pointer_id),
point: Point2D::new(x, y),
}));
self.perform_updates();
}
/// Touch event: Lift touching finger
pub fn touch_up(&self, x: f32, y: f32, pointer_id: i32) {
self.active_webview().notify_touch_event(
TouchEventType::Up,
TouchId(pointer_id),
Point2D::new(x, y),
);
self.active_webview()
.notify_input_event(InputEvent::Touch(TouchEvent {
action: TouchEventAction::Up,
id: TouchId(pointer_id),
point: Point2D::new(x, y),
}));
self.perform_updates();
}
/// Cancel touch event
pub fn touch_cancel(&self, x: f32, y: f32, pointer_id: i32) {
self.active_webview().notify_touch_event(
TouchEventType::Cancel,
TouchId(pointer_id),
Point2D::new(x, y),
);
self.active_webview()
.notify_input_event(InputEvent::Touch(TouchEvent {
action: TouchEventAction::Cancel,
id: TouchId(pointer_id),
point: Point2D::new(x, y),
}));
self.perform_updates();
}
/// Register a mouse movement.
pub fn mouse_move(&self, x: f32, y: f32) {
self.active_webview()
.notify_pointer_move_event(Point2D::new(x, y));
.notify_input_event(InputEvent::MouseMove(MouseMoveEvent {
point: Point2D::new(x, y),
}));
self.perform_updates();
}
/// Register a mouse button press.
pub fn mouse_down(&self, x: f32, y: f32, button: MouseButton) {
self.active_webview()
.notify_pointer_button_event(MouseWindowEvent::MouseDown(button, Point2D::new(x, y)));
.notify_input_event(InputEvent::MouseButton(MouseButtonEvent {
action: MouseButtonAction::Down,
button,
point: Point2D::new(x, y),
}));
self.perform_updates();
}
/// Register a mouse button release.
pub fn mouse_up(&self, x: f32, y: f32, button: MouseButton) {
self.active_webview()
.notify_pointer_button_event(MouseWindowEvent::MouseUp(button, Point2D::new(x, y)));
.notify_input_event(InputEvent::MouseButton(MouseButtonEvent {
action: MouseButtonAction::Up,
button,
point: Point2D::new(x, y),
}));
self.perform_updates();
}
@ -564,10 +579,11 @@ impl RunningAppState {
/// Perform a click.
pub fn click(&self, x: f32, y: f32) {
self.active_webview()
.notify_pointer_button_event(MouseWindowEvent::Click(
MouseButton::Left,
Point2D::new(x, y),
));
.notify_input_event(InputEvent::MouseButton(MouseButtonEvent {
action: MouseButtonAction::Click,
button: MouseButton::Left,
point: Point2D::new(x, y),
}));
self.perform_updates();
}
@ -577,7 +593,8 @@ impl RunningAppState {
key,
..KeyboardEvent::default()
};
self.active_webview().notify_keyboard_event(key_event);
self.active_webview()
.notify_input_event(InputEvent::Keyboard(key_event));
self.perform_updates();
}
@ -587,15 +604,17 @@ impl RunningAppState {
key,
..KeyboardEvent::default()
};
self.active_webview().notify_keyboard_event(key_event);
self.active_webview()
.notify_input_event(InputEvent::Keyboard(key_event));
self.perform_updates();
}
pub fn ime_insert_text(&self, text: String) {
self.active_webview().notify_ime_event(CompositionEvent {
state: CompositionState::End,
data: text,
});
self.active_webview()
.notify_input_event(InputEvent::Ime(ImeEvent::Composition(CompositionEvent {
state: CompositionState::End,
data: text,
})));
self.perform_updates();
}
@ -644,7 +663,8 @@ impl RunningAppState {
pub fn ime_dismissed(&self) {
info!("ime_dismissed");
self.active_webview().notify_ime_dismissed_event();
self.active_webview()
.notify_input_event(InputEvent::Ime(ImeEvent::Dismissed));
self.perform_updates();
}