mirror of
https://github.com/servo/servo.git
synced 2025-06-06 16:45:39 +00: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
|
@ -20,7 +20,10 @@ use compositing_traits::{
|
|||
CompositionPipeline, CompositorMsg, CompositorReceiver, ConstellationMsg, SendableFrameTree,
|
||||
};
|
||||
use crossbeam_channel::Sender;
|
||||
use embedder_traits::{Cursor, MouseButton, MouseEventType, TouchEventType, TouchId, WheelDelta};
|
||||
use embedder_traits::{
|
||||
Cursor, InputEvent, MouseButton, MouseButtonAction, MouseButtonEvent, MouseMoveEvent,
|
||||
TouchEvent, TouchEventAction, TouchId,
|
||||
};
|
||||
use euclid::{Point2D, Rect, Scale, Transform3D, Vector2D};
|
||||
use fnv::{FnvHashMap, FnvHashSet};
|
||||
use image::{DynamicImage, ImageFormat};
|
||||
|
@ -30,7 +33,6 @@ use log::{debug, error, info, trace, warn};
|
|||
use pixels::{CorsStatus, Image, PixelFormat};
|
||||
use profile_traits::time::{self as profile_time, ProfilerCategory};
|
||||
use profile_traits::time_profile;
|
||||
use script_traits::CompositorEvent::{MouseButtonEvent, MouseMoveEvent, TouchEvent, WheelEvent};
|
||||
use script_traits::{
|
||||
AnimationState, AnimationTickType, ScriptThreadMessage, ScrollState, WindowSizeData,
|
||||
WindowSizeType,
|
||||
|
@ -58,9 +60,7 @@ use webrender_traits::{
|
|||
use crate::gl::RenderTargetInfo;
|
||||
use crate::touch::{TouchAction, TouchHandler};
|
||||
use crate::webview::{UnknownWebView, WebView, WebViewAlreadyExists, WebViewManager};
|
||||
use crate::windowing::{
|
||||
self, EmbedderCoordinates, MouseWindowEvent, WebRenderDebugOption, WindowMethods,
|
||||
};
|
||||
use crate::windowing::{self, EmbedderCoordinates, WebRenderDebugOption, WindowMethods};
|
||||
use crate::{gl, InitialCompositorState};
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
|
@ -430,7 +430,7 @@ impl IOCompositor {
|
|||
self.webviews_waiting_on_present.iter()
|
||||
}
|
||||
|
||||
fn update_cursor(&mut self, result: CompositorHitTestResult) {
|
||||
fn update_cursor(&mut self, result: &CompositorHitTestResult) {
|
||||
let cursor = match result.cursor {
|
||||
Some(cursor) if cursor != self.cursor => cursor,
|
||||
_ => return,
|
||||
|
@ -566,7 +566,7 @@ impl IOCompositor {
|
|||
|
||||
if recomposite_needed {
|
||||
if let Some(result) = self.hit_test_at_point(self.cursor_pos) {
|
||||
self.update_cursor(result);
|
||||
self.update_cursor(&result);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -584,20 +584,20 @@ impl IOCompositor {
|
|||
}
|
||||
},
|
||||
|
||||
CompositorMsg::WebDriverMouseButtonEvent(mouse_event_type, mouse_button, x, y) => {
|
||||
CompositorMsg::WebDriverMouseButtonEvent(action, button, x, y) => {
|
||||
let dppx = self.device_pixels_per_page_pixel();
|
||||
let point = dppx.transform_point(Point2D::new(x, y));
|
||||
self.on_mouse_window_event_class(match mouse_event_type {
|
||||
MouseEventType::Click => MouseWindowEvent::Click(mouse_button, point),
|
||||
MouseEventType::MouseDown => MouseWindowEvent::MouseDown(mouse_button, point),
|
||||
MouseEventType::MouseUp => MouseWindowEvent::MouseUp(mouse_button, point),
|
||||
});
|
||||
self.dispatch_input_event(InputEvent::MouseButton(MouseButtonEvent {
|
||||
point,
|
||||
action,
|
||||
button,
|
||||
}));
|
||||
},
|
||||
|
||||
CompositorMsg::WebDriverMouseMoveEvent(x, y) => {
|
||||
let dppx = self.device_pixels_per_page_pixel();
|
||||
let point = dppx.transform_point(Point2D::new(x, y));
|
||||
self.on_mouse_window_move_event_class(DevicePoint::new(point.x, point.y));
|
||||
self.dispatch_input_event(InputEvent::MouseMove(MouseMoveEvent { point }));
|
||||
},
|
||||
|
||||
CompositorMsg::PendingPaintMetric(pipeline_id, epoch) => {
|
||||
|
@ -1326,55 +1326,52 @@ impl IOCompositor {
|
|||
true
|
||||
}
|
||||
|
||||
pub fn on_mouse_window_event_class(&mut self, mouse_window_event: MouseWindowEvent) {
|
||||
fn dispatch_input_event(&mut self, event: InputEvent) {
|
||||
// Events that do not need to do hit testing are sent directly to the
|
||||
// constellation to filter down.
|
||||
let Some(point) = event.point() else {
|
||||
return;
|
||||
};
|
||||
|
||||
// If we can't find a pipeline to send this event to, we cannot continue.
|
||||
let Some(result) = self.hit_test_at_point(point) else {
|
||||
return;
|
||||
};
|
||||
|
||||
self.update_cursor(&result);
|
||||
|
||||
if let Err(error) = self
|
||||
.constellation_chan
|
||||
.send(ConstellationMsg::ForwardInputEvent(event, Some(result)))
|
||||
{
|
||||
warn!("Sending event to constellation failed ({error:?}).");
|
||||
}
|
||||
}
|
||||
|
||||
pub fn on_input_event(&mut self, event: InputEvent) {
|
||||
if self.shutdown_state != ShutdownState::NotShuttingDown {
|
||||
return;
|
||||
}
|
||||
|
||||
if self.convert_mouse_to_touch {
|
||||
match mouse_window_event {
|
||||
MouseWindowEvent::Click(_, _) => {},
|
||||
MouseWindowEvent::MouseDown(_, p) => self.on_touch_down(TouchId(0), p),
|
||||
MouseWindowEvent::MouseUp(_, p) => self.on_touch_up(TouchId(0), p),
|
||||
match event {
|
||||
InputEvent::MouseButton(event) => {
|
||||
match event.action {
|
||||
MouseButtonAction::Click => {},
|
||||
MouseButtonAction::Down => self.on_touch_down(TouchId(0), event.point),
|
||||
MouseButtonAction::Up => self.on_touch_up(TouchId(0), event.point),
|
||||
}
|
||||
return;
|
||||
},
|
||||
InputEvent::MouseMove(event) => {
|
||||
self.on_touch_move(TouchId(0), event.point);
|
||||
return;
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
self.dispatch_mouse_window_event_class(mouse_window_event);
|
||||
}
|
||||
|
||||
fn dispatch_mouse_window_event_class(&mut self, mouse_window_event: MouseWindowEvent) {
|
||||
let point = match mouse_window_event {
|
||||
MouseWindowEvent::Click(_, p) => p,
|
||||
MouseWindowEvent::MouseDown(_, p) => p,
|
||||
MouseWindowEvent::MouseUp(_, p) => p,
|
||||
};
|
||||
|
||||
let Some(result) = self.hit_test_at_point(point) else {
|
||||
// TODO: Notify embedder that the event failed to hit test to any webview.
|
||||
// TODO: Also notify embedder if an event hits a webview but isn’t consumed?
|
||||
return;
|
||||
};
|
||||
|
||||
let (button, event_type) = match mouse_window_event {
|
||||
MouseWindowEvent::Click(button, _) => (button, MouseEventType::Click),
|
||||
MouseWindowEvent::MouseDown(button, _) => (button, MouseEventType::MouseDown),
|
||||
MouseWindowEvent::MouseUp(button, _) => (button, MouseEventType::MouseUp),
|
||||
};
|
||||
|
||||
let event_to_send = MouseButtonEvent(
|
||||
event_type,
|
||||
button,
|
||||
result.point_in_viewport.to_untyped(),
|
||||
Some(result.node.into()),
|
||||
Some(result.point_relative_to_item),
|
||||
button as u16,
|
||||
);
|
||||
|
||||
let msg = ConstellationMsg::ForwardEvent(result.pipeline_id, event_to_send);
|
||||
if let Err(e) = self.constellation_chan.send(msg) {
|
||||
warn!("Sending event to constellation failed ({:?}).", e);
|
||||
}
|
||||
self.dispatch_input_event(event);
|
||||
}
|
||||
|
||||
fn hit_test_at_point(&self, point: DevicePoint) -> Option<CompositorHitTestResult> {
|
||||
|
@ -1426,89 +1423,44 @@ impl IOCompositor {
|
|||
.collect()
|
||||
}
|
||||
|
||||
pub fn on_mouse_window_move_event_class(&mut self, cursor: DevicePoint) {
|
||||
if self.shutdown_state != ShutdownState::NotShuttingDown {
|
||||
fn send_touch_event(&self, event: TouchEvent) {
|
||||
let Some(result) = self.hit_test_at_point(event.point) else {
|
||||
return;
|
||||
}
|
||||
|
||||
if self.convert_mouse_to_touch {
|
||||
self.on_touch_move(TouchId(0), cursor);
|
||||
return;
|
||||
}
|
||||
|
||||
self.dispatch_mouse_window_move_event_class(cursor);
|
||||
}
|
||||
|
||||
fn dispatch_mouse_window_move_event_class(&mut self, cursor: DevicePoint) {
|
||||
let result = match self.hit_test_at_point(cursor) {
|
||||
Some(result) => result,
|
||||
None => return,
|
||||
};
|
||||
|
||||
self.cursor_pos = cursor;
|
||||
let event = MouseMoveEvent(result.point_in_viewport, Some(result.node.into()), 0);
|
||||
let msg = ConstellationMsg::ForwardEvent(result.pipeline_id, event);
|
||||
if let Err(e) = self.constellation_chan.send(msg) {
|
||||
let event = InputEvent::Touch(event);
|
||||
if let Err(e) = self
|
||||
.constellation_chan
|
||||
.send(ConstellationMsg::ForwardInputEvent(event, Some(result)))
|
||||
{
|
||||
warn!("Sending event to constellation failed ({:?}).", e);
|
||||
}
|
||||
self.update_cursor(result);
|
||||
}
|
||||
|
||||
fn send_touch_event(
|
||||
&self,
|
||||
event_type: TouchEventType,
|
||||
identifier: TouchId,
|
||||
point: DevicePoint,
|
||||
) {
|
||||
if let Some(result) = self.hit_test_at_point(point) {
|
||||
let event = TouchEvent(
|
||||
event_type,
|
||||
identifier,
|
||||
result.point_in_viewport,
|
||||
Some(result.node.into()),
|
||||
);
|
||||
let msg = ConstellationMsg::ForwardEvent(result.pipeline_id, event);
|
||||
if let Err(e) = self.constellation_chan.send(msg) {
|
||||
warn!("Sending event to constellation failed ({:?}).", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn send_wheel_event(&mut self, delta: WheelDelta, point: DevicePoint) {
|
||||
if let Some(result) = self.hit_test_at_point(point) {
|
||||
let event = WheelEvent(delta, result.point_in_viewport, Some(result.node.into()));
|
||||
let msg = ConstellationMsg::ForwardEvent(result.pipeline_id, event);
|
||||
if let Err(e) = self.constellation_chan.send(msg) {
|
||||
warn!("Sending event to constellation failed ({:?}).", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn on_touch_event(
|
||||
&mut self,
|
||||
event_type: TouchEventType,
|
||||
identifier: TouchId,
|
||||
location: DevicePoint,
|
||||
) {
|
||||
pub fn on_touch_event(&mut self, event: TouchEvent) {
|
||||
if self.shutdown_state != ShutdownState::NotShuttingDown {
|
||||
return;
|
||||
}
|
||||
|
||||
match event_type {
|
||||
TouchEventType::Down => self.on_touch_down(identifier, location),
|
||||
TouchEventType::Move => self.on_touch_move(identifier, location),
|
||||
TouchEventType::Up => self.on_touch_up(identifier, location),
|
||||
TouchEventType::Cancel => self.on_touch_cancel(identifier, location),
|
||||
match event.action {
|
||||
TouchEventAction::Down => self.on_touch_down(event.id, event.point),
|
||||
TouchEventAction::Move => self.on_touch_move(event.id, event.point),
|
||||
TouchEventAction::Up => self.on_touch_up(event.id, event.point),
|
||||
TouchEventAction::Cancel => self.on_touch_cancel(event.id, event.point),
|
||||
}
|
||||
}
|
||||
|
||||
fn on_touch_down(&mut self, identifier: TouchId, point: DevicePoint) {
|
||||
self.touch_handler.on_touch_down(identifier, point);
|
||||
self.send_touch_event(TouchEventType::Down, identifier, point);
|
||||
fn on_touch_down(&mut self, id: TouchId, point: DevicePoint) {
|
||||
self.touch_handler.on_touch_down(id, point);
|
||||
self.send_touch_event(TouchEvent {
|
||||
action: TouchEventAction::Down,
|
||||
id,
|
||||
point,
|
||||
})
|
||||
}
|
||||
|
||||
fn on_touch_move(&mut self, identifier: TouchId, point: DevicePoint) {
|
||||
match self.touch_handler.on_touch_move(identifier, point) {
|
||||
fn on_touch_move(&mut self, id: TouchId, point: DevicePoint) {
|
||||
match self.touch_handler.on_touch_move(id, point) {
|
||||
TouchAction::Scroll(delta) => self.on_scroll_window_event(
|
||||
ScrollLocation::Delta(LayoutVector2D::from_untyped(delta.to_untyped())),
|
||||
point.cast(),
|
||||
|
@ -1530,60 +1482,74 @@ impl IOCompositor {
|
|||
event_count: 1,
|
||||
}));
|
||||
},
|
||||
TouchAction::DispatchEvent => {
|
||||
self.send_touch_event(TouchEventType::Move, identifier, point);
|
||||
},
|
||||
TouchAction::DispatchEvent => self.send_touch_event(TouchEvent {
|
||||
action: TouchEventAction::Move,
|
||||
id,
|
||||
point,
|
||||
}),
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
|
||||
fn on_touch_up(&mut self, identifier: TouchId, point: DevicePoint) {
|
||||
self.send_touch_event(TouchEventType::Up, identifier, point);
|
||||
fn on_touch_up(&mut self, id: TouchId, point: DevicePoint) {
|
||||
self.send_touch_event(TouchEvent {
|
||||
action: TouchEventAction::Up,
|
||||
id,
|
||||
point,
|
||||
});
|
||||
|
||||
if let TouchAction::Click = self.touch_handler.on_touch_up(identifier, point) {
|
||||
if let TouchAction::Click = self.touch_handler.on_touch_up(id, point) {
|
||||
self.simulate_mouse_click(point);
|
||||
}
|
||||
}
|
||||
|
||||
fn on_touch_cancel(&mut self, identifier: TouchId, point: DevicePoint) {
|
||||
fn on_touch_cancel(&mut self, id: TouchId, point: DevicePoint) {
|
||||
// Send the event to script.
|
||||
self.touch_handler.on_touch_cancel(identifier, point);
|
||||
self.send_touch_event(TouchEventType::Cancel, identifier, point);
|
||||
self.touch_handler.on_touch_cancel(id, point);
|
||||
self.send_touch_event(TouchEvent {
|
||||
action: TouchEventAction::Cancel,
|
||||
id,
|
||||
point,
|
||||
})
|
||||
}
|
||||
|
||||
/// <http://w3c.github.io/touch-events/#mouse-events>
|
||||
fn simulate_mouse_click(&mut self, p: DevicePoint) {
|
||||
fn simulate_mouse_click(&mut self, point: DevicePoint) {
|
||||
let button = MouseButton::Left;
|
||||
self.dispatch_mouse_window_move_event_class(p);
|
||||
self.dispatch_mouse_window_event_class(MouseWindowEvent::MouseDown(button, p));
|
||||
self.dispatch_mouse_window_event_class(MouseWindowEvent::MouseUp(button, p));
|
||||
self.dispatch_mouse_window_event_class(MouseWindowEvent::Click(button, p));
|
||||
}
|
||||
|
||||
pub fn on_wheel_event(&mut self, delta: WheelDelta, p: DevicePoint) {
|
||||
if self.shutdown_state != ShutdownState::NotShuttingDown {
|
||||
return;
|
||||
}
|
||||
|
||||
self.send_wheel_event(delta, p);
|
||||
self.dispatch_input_event(InputEvent::MouseMove(MouseMoveEvent { point }));
|
||||
self.dispatch_input_event(InputEvent::MouseButton(MouseButtonEvent {
|
||||
button,
|
||||
action: MouseButtonAction::Down,
|
||||
point,
|
||||
}));
|
||||
self.dispatch_input_event(InputEvent::MouseButton(MouseButtonEvent {
|
||||
button,
|
||||
action: MouseButtonAction::Up,
|
||||
point,
|
||||
}));
|
||||
self.dispatch_input_event(InputEvent::MouseButton(MouseButtonEvent {
|
||||
button,
|
||||
action: MouseButtonAction::Click,
|
||||
point,
|
||||
}));
|
||||
}
|
||||
|
||||
pub fn on_scroll_event(
|
||||
&mut self,
|
||||
scroll_location: ScrollLocation,
|
||||
cursor: DeviceIntPoint,
|
||||
phase: TouchEventType,
|
||||
action: TouchEventAction,
|
||||
) {
|
||||
if self.shutdown_state != ShutdownState::NotShuttingDown {
|
||||
return;
|
||||
}
|
||||
|
||||
match phase {
|
||||
TouchEventType::Move => self.on_scroll_window_event(scroll_location, cursor),
|
||||
TouchEventType::Up | TouchEventType::Cancel => {
|
||||
match action {
|
||||
TouchEventAction::Move => self.on_scroll_window_event(scroll_location, cursor),
|
||||
TouchEventAction::Up | TouchEventAction::Cancel => {
|
||||
self.on_scroll_window_event(scroll_location, cursor);
|
||||
},
|
||||
TouchEventType::Down => {
|
||||
TouchEventAction::Down => {
|
||||
self.on_scroll_window_event(scroll_location, cursor);
|
||||
},
|
||||
}
|
||||
|
|
|
@ -115,10 +115,12 @@ use devtools_traits::{
|
|||
ChromeToDevtoolsControlMsg, DevtoolsControlMsg, DevtoolsPageInfo, NavigationState,
|
||||
ScriptToDevtoolsControlMsg,
|
||||
};
|
||||
use embedder_traits::input_events::MouseButtonAction;
|
||||
use embedder_traits::resources::{self, Resource};
|
||||
use embedder_traits::{
|
||||
ClipboardEventType, Cursor, EmbedderMsg, EmbedderProxy, GamepadEvent, MediaSessionActionType,
|
||||
MediaSessionEvent, MediaSessionPlaybackState, MouseEventType, Theme, TraversalDirection,
|
||||
Cursor, EmbedderMsg, EmbedderProxy, ImeEvent, InputEvent, MediaSessionActionType,
|
||||
MediaSessionEvent, MediaSessionPlaybackState, MouseButton, MouseButtonEvent, Theme,
|
||||
TraversalDirection,
|
||||
};
|
||||
use euclid::default::Size2D as UntypedSize2D;
|
||||
use euclid::Size2D;
|
||||
|
@ -127,7 +129,6 @@ use ipc_channel::ipc::{self, IpcReceiver, IpcSender};
|
|||
use ipc_channel::router::ROUTER;
|
||||
use ipc_channel::Error as IpcError;
|
||||
use keyboard_types::webdriver::Event as WebDriverInputEvent;
|
||||
use keyboard_types::{CompositionEvent, KeyboardEvent};
|
||||
use log::{debug, error, info, trace, warn};
|
||||
use media::{GLPlayerThreads, WindowGLContext};
|
||||
use net_traits::pub_domains::reg_host;
|
||||
|
@ -136,10 +137,9 @@ use net_traits::storage_thread::{StorageThreadMsg, StorageType};
|
|||
use net_traits::{self, IpcSend, ReferrerPolicy, ResourceThreads};
|
||||
use profile_traits::{mem, time};
|
||||
use script_layout_interface::{LayoutFactory, ScriptThreadFactory};
|
||||
use script_traits::CompositorEvent::{MouseButtonEvent, MouseMoveEvent};
|
||||
use script_traits::{
|
||||
webdriver_msg, AnimationState, AnimationTickType, AuxiliaryBrowsingContextLoadInfo,
|
||||
BroadcastMsg, CompositorEvent, DiscardBrowsingContext, DocumentActivity, DocumentState,
|
||||
BroadcastMsg, ConstellationInputEvent, DiscardBrowsingContext, DocumentActivity, DocumentState,
|
||||
IFrameLoadInfo, IFrameLoadInfoWithData, IFrameSandboxState, IFrameSizeMsg, Job,
|
||||
LayoutMsg as FromLayoutMsg, LoadData, LoadOrigin, LogEntry, MessagePortMsg,
|
||||
NavigationHistoryBehavior, PortMessageTask, SWManagerMsg, SWManagerSenders,
|
||||
|
@ -160,7 +160,7 @@ use webgpu::{self, WebGPU, WebGPURequest, WebGPUResponse};
|
|||
use webrender::RenderApi;
|
||||
use webrender::RenderApiSender;
|
||||
use webrender_api::DocumentId;
|
||||
use webrender_traits::WebrenderExternalImageRegistry;
|
||||
use webrender_traits::{CompositorHitTestResult, WebrenderExternalImageRegistry};
|
||||
|
||||
use crate::browsingcontext::{
|
||||
AllBrowsingContextsIterator, BrowsingContext, FullyActiveBrowsingContextsIterator,
|
||||
|
@ -1271,15 +1271,6 @@ where
|
|||
FromCompositorMsg::GetFocusTopLevelBrowsingContext(resp_chan) => {
|
||||
let _ = resp_chan.send(self.webviews.focused_webview().map(|(id, _)| id));
|
||||
},
|
||||
FromCompositorMsg::Keyboard(webview_id, key_event) => {
|
||||
self.handle_key_msg(webview_id, key_event);
|
||||
},
|
||||
FromCompositorMsg::IMECompositionEvent(ime_event) => {
|
||||
self.handle_ime_msg(ime_event);
|
||||
},
|
||||
FromCompositorMsg::IMEDismissed => {
|
||||
self.handle_ime_dismissed();
|
||||
},
|
||||
// Perform a navigation previously requested by script, if approved by the embedder.
|
||||
// If there is already a pending page (self.pending_changes), it will not be overridden;
|
||||
// However, if the id is not encompassed by another change, it will be.
|
||||
|
@ -1435,8 +1426,8 @@ where
|
|||
FromCompositorMsg::LogEntry(top_level_browsing_context_id, thread_name, entry) => {
|
||||
self.handle_log_entry(top_level_browsing_context_id, thread_name, entry);
|
||||
},
|
||||
FromCompositorMsg::ForwardEvent(destination_pipeline_id, event) => {
|
||||
self.forward_event(destination_pipeline_id, event);
|
||||
FromCompositorMsg::ForwardInputEvent(event, hit_test) => {
|
||||
self.forward_input_event(event, hit_test);
|
||||
},
|
||||
FromCompositorMsg::SetCursor(webview_id, cursor) => {
|
||||
self.handle_set_cursor_msg(webview_id, cursor)
|
||||
|
@ -1459,12 +1450,6 @@ where
|
|||
FromCompositorMsg::SetWebViewThrottled(webview_id, throttled) => {
|
||||
self.set_webview_throttled(webview_id, throttled);
|
||||
},
|
||||
FromCompositorMsg::Gamepad(gamepad_event) => {
|
||||
self.handle_gamepad_msg(gamepad_event);
|
||||
},
|
||||
FromCompositorMsg::Clipboard(clipboard_event) => {
|
||||
self.handle_clipboard_msg(clipboard_event);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2893,57 +2878,91 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
fn forward_event(&mut self, destination_pipeline_id: PipelineId, event: CompositorEvent) {
|
||||
if let MouseButtonEvent(event_type, button, ..) = &event {
|
||||
match event_type {
|
||||
MouseEventType::MouseDown | MouseEventType::Click => {
|
||||
self.pressed_mouse_buttons |= *button as u16;
|
||||
},
|
||||
MouseEventType::MouseUp => {
|
||||
self.pressed_mouse_buttons &= !(*button as u16);
|
||||
},
|
||||
fn update_pressed_mouse_buttons(&mut self, event: &MouseButtonEvent) {
|
||||
// This value is ultimately used for a DOM mouse event, and the specification says that
|
||||
// the pressed buttons should be represented as a bitmask with values defined at
|
||||
// <https://w3c.github.io/uievents/#dom-mouseevent-buttons>.
|
||||
let button_as_bitmask = match event.button {
|
||||
MouseButton::Left => 1,
|
||||
MouseButton::Right => 2,
|
||||
MouseButton::Middle => 4,
|
||||
MouseButton::Back => 8,
|
||||
MouseButton::Forward => 16,
|
||||
MouseButton::Other(_) => return,
|
||||
};
|
||||
|
||||
match event.action {
|
||||
MouseButtonAction::Click | MouseButtonAction::Down => {
|
||||
self.pressed_mouse_buttons |= button_as_bitmask;
|
||||
},
|
||||
MouseButtonAction::Up => {
|
||||
self.pressed_mouse_buttons &= !(button_as_bitmask);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn forward_input_event(
|
||||
&mut self,
|
||||
event: InputEvent,
|
||||
hit_test_result: Option<CompositorHitTestResult>,
|
||||
) {
|
||||
if let InputEvent::MouseButton(event) = &event {
|
||||
self.update_pressed_mouse_buttons(event);
|
||||
}
|
||||
|
||||
// The constellation tracks the state of pressed mouse buttons and updates the event
|
||||
// here to reflect the current state.
|
||||
let pressed_mouse_buttons = self.pressed_mouse_buttons;
|
||||
|
||||
// TODO: Click should be handled internally in the `Document`.
|
||||
if let InputEvent::MouseButton(event) = &event {
|
||||
if event.action == MouseButtonAction::Click {
|
||||
self.pressed_mouse_buttons = 0;
|
||||
}
|
||||
}
|
||||
|
||||
let event = match event {
|
||||
MouseButtonEvent(event_type, button, point, node_address, point_in_node, _) => {
|
||||
MouseButtonEvent(
|
||||
event_type,
|
||||
button,
|
||||
point,
|
||||
node_address,
|
||||
point_in_node,
|
||||
self.pressed_mouse_buttons,
|
||||
)
|
||||
},
|
||||
MouseMoveEvent(point, node_address, _) => {
|
||||
MouseMoveEvent(point, node_address, self.pressed_mouse_buttons)
|
||||
},
|
||||
_ => event,
|
||||
};
|
||||
|
||||
if let MouseButtonEvent(MouseEventType::Click, ..) = event {
|
||||
self.pressed_mouse_buttons = 0;
|
||||
}
|
||||
|
||||
let pipeline = match self.pipelines.get(&destination_pipeline_id) {
|
||||
let pipeline_id = match &hit_test_result {
|
||||
Some(hit_test) => hit_test.pipeline_id,
|
||||
None => {
|
||||
debug!("{}: Got event after closure", destination_pipeline_id);
|
||||
return;
|
||||
// If there's no hit test, send to the currently focused WebView.
|
||||
let Some(browsing_context_id) = self
|
||||
.webviews
|
||||
.focused_webview()
|
||||
.map(|(_, webview)| webview.focused_browsing_context_id)
|
||||
else {
|
||||
warn!("Handling InputEvent with no focused WebView");
|
||||
return;
|
||||
};
|
||||
|
||||
let Some(pipeline_id) = self
|
||||
.browsing_contexts
|
||||
.get(&browsing_context_id)
|
||||
.map(|context| context.pipeline_id)
|
||||
else {
|
||||
warn!("{browsing_context_id}: Got InputEvent for nonexistent browsing context");
|
||||
return;
|
||||
};
|
||||
|
||||
pipeline_id
|
||||
},
|
||||
Some(pipeline) => pipeline,
|
||||
};
|
||||
|
||||
self.embedder_proxy.send(EmbedderMsg::EventDelivered(
|
||||
pipeline.top_level_browsing_context_id,
|
||||
(&event).into(),
|
||||
));
|
||||
let Some(pipeline) = self.pipelines.get(&pipeline_id) else {
|
||||
debug!("Got event for pipeline ({pipeline_id}) after closure");
|
||||
return;
|
||||
};
|
||||
|
||||
if let Err(e) = pipeline.event_loop.send(ScriptThreadMessage::SendEvent(
|
||||
destination_pipeline_id,
|
||||
let event = ConstellationInputEvent {
|
||||
hit_test_result,
|
||||
pressed_mouse_buttons,
|
||||
event,
|
||||
)) {
|
||||
self.handle_send_error(destination_pipeline_id, e);
|
||||
};
|
||||
|
||||
if let Err(error) = pipeline
|
||||
.event_loop
|
||||
.send(ScriptThreadMessage::SendInputEvent(pipeline_id, event))
|
||||
{
|
||||
self.handle_send_error(pipeline_id, error);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4124,144 +4143,6 @@ where
|
|||
session_history.replace_history_state(pipeline_id, history_state_id, url);
|
||||
}
|
||||
|
||||
#[cfg_attr(
|
||||
feature = "tracing",
|
||||
tracing::instrument(skip_all, fields(servo_profiling = true), level = "trace")
|
||||
)]
|
||||
fn handle_ime_dismissed(&mut self) {
|
||||
// Send to the focused browsing contexts' current pipeline.
|
||||
let focused_browsing_context_id = self
|
||||
.webviews
|
||||
.focused_webview()
|
||||
.map(|(_, webview)| webview.focused_browsing_context_id);
|
||||
if let Some(browsing_context_id) = focused_browsing_context_id {
|
||||
let pipeline_id = match self.browsing_contexts.get(&browsing_context_id) {
|
||||
Some(ctx) => ctx.pipeline_id,
|
||||
None => {
|
||||
return warn!(
|
||||
"{}: Got IME dismissed event for nonexistent browsing context",
|
||||
browsing_context_id,
|
||||
);
|
||||
},
|
||||
};
|
||||
let msg =
|
||||
ScriptThreadMessage::SendEvent(pipeline_id, CompositorEvent::IMEDismissedEvent);
|
||||
let result = match self.pipelines.get(&pipeline_id) {
|
||||
Some(pipeline) => pipeline.event_loop.send(msg),
|
||||
None => {
|
||||
return debug!("{}: Got IME dismissed event after closure", pipeline_id);
|
||||
},
|
||||
};
|
||||
if let Err(e) = result {
|
||||
self.handle_send_error(pipeline_id, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(
|
||||
feature = "tracing",
|
||||
tracing::instrument(skip_all, fields(servo_profiling = true), level = "trace")
|
||||
)]
|
||||
fn handle_ime_msg(&mut self, event: CompositionEvent) {
|
||||
// Send to the focused browsing contexts' current pipeline.
|
||||
let Some(focused_browsing_context_id) = self
|
||||
.webviews
|
||||
.focused_webview()
|
||||
.map(|(_, webview)| webview.focused_browsing_context_id)
|
||||
else {
|
||||
warn!("No focused browsing context! Dropping IME event {event:?}");
|
||||
return;
|
||||
};
|
||||
let event = CompositorEvent::CompositionEvent(event);
|
||||
let pipeline_id = match self.browsing_contexts.get(&focused_browsing_context_id) {
|
||||
Some(ctx) => ctx.pipeline_id,
|
||||
None => {
|
||||
return warn!(
|
||||
"{}: Got composition event for nonexistent browsing context",
|
||||
focused_browsing_context_id,
|
||||
);
|
||||
},
|
||||
};
|
||||
let msg = ScriptThreadMessage::SendEvent(pipeline_id, event);
|
||||
let result = match self.pipelines.get(&pipeline_id) {
|
||||
Some(pipeline) => pipeline.event_loop.send(msg),
|
||||
None => {
|
||||
return debug!("{}: Got composition event after closure", pipeline_id);
|
||||
},
|
||||
};
|
||||
if let Err(e) = result {
|
||||
self.handle_send_error(pipeline_id, e);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(
|
||||
feature = "tracing",
|
||||
tracing::instrument(skip_all, fields(servo_profiling = true))
|
||||
)]
|
||||
fn handle_key_msg(&mut self, webview_id: WebViewId, event: KeyboardEvent) {
|
||||
// Send to the focused browsing contexts' current pipeline. If it
|
||||
// doesn't exist, fall back to sending to the compositor.
|
||||
let Some(webview) = self.webviews.get(webview_id) else {
|
||||
warn!("Handling keyboard event for unknown webview: {webview_id}");
|
||||
return;
|
||||
};
|
||||
let browsing_context_id = webview.focused_browsing_context_id;
|
||||
let event = CompositorEvent::KeyboardEvent(event);
|
||||
let pipeline_id = match self.browsing_contexts.get(&browsing_context_id) {
|
||||
Some(ctx) => ctx.pipeline_id,
|
||||
None => {
|
||||
return warn!(
|
||||
"{}: Got key event for nonexistent browsing context",
|
||||
browsing_context_id,
|
||||
);
|
||||
},
|
||||
};
|
||||
let msg = ScriptThreadMessage::SendEvent(pipeline_id, event);
|
||||
let result = match self.pipelines.get(&pipeline_id) {
|
||||
Some(pipeline) => pipeline.event_loop.send(msg),
|
||||
None => {
|
||||
return debug!("{}: Got key event after closure", pipeline_id);
|
||||
},
|
||||
};
|
||||
if let Err(e) = result {
|
||||
self.handle_send_error(pipeline_id, e);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(
|
||||
feature = "tracing",
|
||||
tracing::instrument(skip_all, fields(servo_profiling = true))
|
||||
)]
|
||||
fn handle_clipboard_msg(&mut self, event: ClipboardEventType) {
|
||||
let focused_browsing_context_id = self
|
||||
.webviews
|
||||
.focused_webview()
|
||||
.map(|(_, webview)| webview.focused_browsing_context_id);
|
||||
|
||||
if let Some(browsing_context_id) = focused_browsing_context_id {
|
||||
let event = CompositorEvent::ClipboardEvent(event);
|
||||
let pipeline_id = match self.browsing_contexts.get(&browsing_context_id) {
|
||||
Some(ctx) => ctx.pipeline_id,
|
||||
None => {
|
||||
return warn!(
|
||||
"{}: Got clipboard event for nonexistent browsing context",
|
||||
browsing_context_id,
|
||||
);
|
||||
},
|
||||
};
|
||||
let msg = ScriptThreadMessage::SendEvent(pipeline_id, event);
|
||||
let result = match self.pipelines.get(&pipeline_id) {
|
||||
Some(pipeline) => pipeline.event_loop.send(msg),
|
||||
None => {
|
||||
return debug!("{}: Got clipboard event after closure", pipeline_id);
|
||||
},
|
||||
};
|
||||
if let Err(e) = result {
|
||||
self.handle_send_error(pipeline_id, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(
|
||||
feature = "tracing",
|
||||
tracing::instrument(skip_all, fields(servo_profiling = true), level = "trace")
|
||||
|
@ -4624,14 +4505,18 @@ where
|
|||
};
|
||||
for event in cmd {
|
||||
let event = match event {
|
||||
WebDriverInputEvent::Keyboard(event) => {
|
||||
CompositorEvent::KeyboardEvent(event)
|
||||
WebDriverInputEvent::Keyboard(event) => ConstellationInputEvent {
|
||||
pressed_mouse_buttons: self.pressed_mouse_buttons,
|
||||
hit_test_result: None,
|
||||
event: InputEvent::Keyboard(event),
|
||||
},
|
||||
WebDriverInputEvent::Composition(event) => {
|
||||
CompositorEvent::CompositionEvent(event)
|
||||
WebDriverInputEvent::Composition(event) => ConstellationInputEvent {
|
||||
pressed_mouse_buttons: self.pressed_mouse_buttons,
|
||||
hit_test_result: None,
|
||||
event: InputEvent::Ime(ImeEvent::Composition(event)),
|
||||
},
|
||||
};
|
||||
let control_msg = ScriptThreadMessage::SendEvent(pipeline_id, event);
|
||||
let control_msg = ScriptThreadMessage::SendInputEvent(pipeline_id, event);
|
||||
if let Err(e) = event_loop.send(control_msg) {
|
||||
return self.handle_send_error(pipeline_id, e);
|
||||
}
|
||||
|
@ -4648,9 +4533,13 @@ where
|
|||
Some(pipeline) => pipeline.event_loop.clone(),
|
||||
None => return warn!("{}: KeyboardAction after closure", pipeline_id),
|
||||
};
|
||||
let control_msg = ScriptThreadMessage::SendEvent(
|
||||
let control_msg = ScriptThreadMessage::SendInputEvent(
|
||||
pipeline_id,
|
||||
CompositorEvent::KeyboardEvent(event),
|
||||
ConstellationInputEvent {
|
||||
pressed_mouse_buttons: self.pressed_mouse_buttons,
|
||||
hit_test_result: None,
|
||||
event: InputEvent::Keyboard(event),
|
||||
},
|
||||
);
|
||||
if let Err(e) = event_loop.send(control_msg) {
|
||||
self.handle_send_error(pipeline_id, e)
|
||||
|
@ -5749,44 +5638,4 @@ where
|
|||
error!("Got a media session action but no active media session is registered");
|
||||
}
|
||||
}
|
||||
|
||||
/// Handle GamepadEvents from the embedder and forward them to the script thread
|
||||
#[cfg_attr(
|
||||
feature = "tracing",
|
||||
tracing::instrument(skip_all, fields(servo_profiling = true), level = "trace")
|
||||
)]
|
||||
fn handle_gamepad_msg(&mut self, event: GamepadEvent) {
|
||||
// Send to the focused browsing contexts' current pipeline.
|
||||
let focused_browsing_context_id = self
|
||||
.webviews
|
||||
.focused_webview()
|
||||
.map(|(_, webview)| webview.focused_browsing_context_id);
|
||||
match focused_browsing_context_id {
|
||||
Some(browsing_context_id) => {
|
||||
let event = CompositorEvent::GamepadEvent(event);
|
||||
let pipeline_id = match self.browsing_contexts.get(&browsing_context_id) {
|
||||
Some(ctx) => ctx.pipeline_id,
|
||||
None => {
|
||||
return warn!(
|
||||
"{}: Got gamepad event for nonexistent browsing context",
|
||||
browsing_context_id,
|
||||
);
|
||||
},
|
||||
};
|
||||
let msg = ScriptThreadMessage::SendEvent(pipeline_id, event);
|
||||
let result = match self.pipelines.get(&pipeline_id) {
|
||||
Some(pipeline) => pipeline.event_loop.send(msg),
|
||||
None => {
|
||||
return debug!("{}: Got gamepad event after closure", pipeline_id);
|
||||
},
|
||||
};
|
||||
if let Err(e) = result {
|
||||
self.handle_send_error(pipeline_id, e);
|
||||
}
|
||||
},
|
||||
None => {
|
||||
warn!("No focused webview to handle gamepad event");
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -47,6 +47,8 @@ pub(crate) trait LogTarget {
|
|||
}
|
||||
|
||||
mod from_compositor {
|
||||
use embedder_traits::InputEvent;
|
||||
|
||||
use super::LogTarget;
|
||||
|
||||
macro_rules! target {
|
||||
|
@ -65,8 +67,6 @@ mod from_compositor {
|
|||
target!("GetFocusTopLevelBrowsingContext")
|
||||
},
|
||||
Self::IsReadyToSaveImage(..) => target!("IsReadyToSaveImage"),
|
||||
Self::Keyboard(..) => target!("Keyboard"),
|
||||
Self::IMECompositionEvent(..) => target!("IMECompositionEvent"),
|
||||
Self::AllowNavigationResponse(..) => target!("AllowNavigationResponse"),
|
||||
Self::LoadUrl(..) => target!("LoadUrl"),
|
||||
Self::ClearCache => target!("ClearCache"),
|
||||
|
@ -83,37 +83,32 @@ mod from_compositor {
|
|||
Self::SendError(..) => target!("SendError"),
|
||||
Self::FocusWebView(..) => target!("FocusWebView"),
|
||||
Self::BlurWebView => target!("BlurWebView"),
|
||||
Self::ForwardEvent(_, event) => event.log_target(),
|
||||
Self::ForwardInputEvent(event, ..) => event.log_target(),
|
||||
Self::SetCursor(..) => target!("SetCursor"),
|
||||
Self::ToggleProfiler(..) => target!("EnableProfiler"),
|
||||
Self::ExitFullScreen(_) => target!("ExitFullScreen"),
|
||||
Self::MediaSessionAction(_) => target!("MediaSessionAction"),
|
||||
Self::SetWebViewThrottled(_, _) => target!("SetWebViewThrottled"),
|
||||
Self::IMEDismissed => target!("IMEDismissed"),
|
||||
Self::Gamepad(..) => target!("Gamepad"),
|
||||
Self::Clipboard(..) => target!("Clipboard"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl LogTarget for script_traits::CompositorEvent {
|
||||
impl LogTarget for InputEvent {
|
||||
fn log_target(&self) -> &'static str {
|
||||
macro_rules! target_variant {
|
||||
($name:literal) => {
|
||||
target!("ForwardEvent(" $name ")")
|
||||
target!("ForwardInputEvent(" $name ")")
|
||||
};
|
||||
}
|
||||
match self {
|
||||
Self::ResizeEvent(..) => target_variant!("ResizeEvent"),
|
||||
Self::MouseButtonEvent(..) => target_variant!("MouseButtonEvent"),
|
||||
Self::MouseMoveEvent(..) => target_variant!("MouseMoveEvent"),
|
||||
Self::TouchEvent(..) => target_variant!("TouchEvent"),
|
||||
Self::WheelEvent(..) => target_variant!("WheelEvent"),
|
||||
Self::KeyboardEvent(..) => target_variant!("KeyboardEvent"),
|
||||
Self::CompositionEvent(..) => target_variant!("CompositionEvent"),
|
||||
Self::IMEDismissedEvent => target_variant!("IMEDismissedEvent"),
|
||||
Self::GamepadEvent(..) => target_variant!("GamepadEvent"),
|
||||
Self::ClipboardEvent(..) => target_variant!("ClipboardEvent"),
|
||||
InputEvent::EditingAction(..) => target_variant!("EditingAction"),
|
||||
InputEvent::Gamepad(..) => target_variant!("Gamepad"),
|
||||
InputEvent::Ime(..) => target_variant!("Ime"),
|
||||
InputEvent::Keyboard(..) => target_variant!("Keyboard"),
|
||||
InputEvent::MouseButton(..) => target_variant!("MouseButton"),
|
||||
InputEvent::MouseMove(..) => target_variant!("MouseMove"),
|
||||
InputEvent::Touch(..) => target_variant!("Touch"),
|
||||
InputEvent::Wheel(..) => target_variant!("Wheel"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -247,7 +242,6 @@ mod from_script {
|
|||
Self::MediaSessionEvent(..) => target_variant!("MediaSessionEvent"),
|
||||
Self::OnDevtoolsStarted(..) => target_variant!("OnDevtoolsStarted"),
|
||||
Self::RequestDevtoolsConnection(..) => target_variant!("RequestDevtoolsConnection"),
|
||||
Self::EventDelivered(..) => target_variant!("EventDelivered"),
|
||||
Self::PlayGamepadHapticEffect(..) => target_variant!("PlayGamepadHapticEffect"),
|
||||
Self::StopGamepadHapticEffect(..) => target_variant!("StopGamepadHapticEffect"),
|
||||
}
|
||||
|
|
|
@ -102,7 +102,6 @@ impl Formattable for ProfilerCategory {
|
|||
ProfilerCategory::ScriptConstellationMsg => "Script Constellation Msg",
|
||||
ProfilerCategory::ScriptDevtoolsMsg => "Script Devtools Msg",
|
||||
ProfilerCategory::ScriptDocumentEvent => "Script Document Event",
|
||||
ProfilerCategory::ScriptDomEvent => "Script Dom Event",
|
||||
ProfilerCategory::ScriptEvaluate => "Script JS Evaluate",
|
||||
ProfilerCategory::ScriptFileRead => "Script File Read",
|
||||
ProfilerCategory::ScriptHistoryEvent => "Script History Event",
|
||||
|
|
|
@ -18,6 +18,28 @@ use crate::dom::event::{Event, EventBubbles, EventCancelable};
|
|||
use crate::dom::window::Window;
|
||||
use crate::script_runtime::CanGc;
|
||||
|
||||
/// The types of clipboard events in the Clipboard APIs specification:
|
||||
/// <https://www.w3.org/TR/clipboard-apis/#clipboard-actions>.
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) enum ClipboardEventType {
|
||||
Change,
|
||||
Copy,
|
||||
Cut,
|
||||
Paste,
|
||||
}
|
||||
|
||||
impl ClipboardEventType {
|
||||
/// Convert this [`ClipboardEventType`] to a `&str` for use in creating DOM events.
|
||||
pub(crate) fn as_str(&self) -> &str {
|
||||
match *self {
|
||||
ClipboardEventType::Change => "clipboardchange",
|
||||
ClipboardEventType::Copy => "copy",
|
||||
ClipboardEventType::Cut => "cut",
|
||||
ClipboardEventType::Paste => "paste",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[dom_struct]
|
||||
pub struct ClipboardEvent {
|
||||
event: Event,
|
||||
|
|
|
@ -25,8 +25,9 @@ use cssparser::match_ignore_ascii_case;
|
|||
use devtools_traits::ScriptToDevtoolsControlMsg;
|
||||
use dom_struct::dom_struct;
|
||||
use embedder_traits::{
|
||||
AllowOrDeny, ClipboardEventType, ContextMenuResult, EmbedderMsg, LoadStatus, MouseButton,
|
||||
MouseEventType, TouchEventType, TouchId, WheelDelta,
|
||||
AllowOrDeny, ContextMenuResult, EditingActionEvent, EmbedderMsg, ImeEvent, InputEvent,
|
||||
LoadStatus, MouseButton, MouseButtonAction, MouseButtonEvent, TouchEvent, TouchEventAction,
|
||||
TouchId, WheelEvent,
|
||||
};
|
||||
use encoding_rs::{Encoding, UTF_8};
|
||||
use euclid::default::{Point2D, Rect, Size2D};
|
||||
|
@ -53,8 +54,7 @@ use profile_traits::ipc as profile_ipc;
|
|||
use profile_traits::time::{TimerMetadata, TimerMetadataFrameType, TimerMetadataReflowType};
|
||||
use script_layout_interface::{PendingRestyle, TrustedNodeAddress};
|
||||
use script_traits::{
|
||||
AnimationState, AnimationTickType, CompositorEvent, DocumentActivity, ScriptMsg,
|
||||
UntrustedNodeAddress,
|
||||
AnimationState, AnimationTickType, ConstellationInputEvent, DocumentActivity, ScriptMsg,
|
||||
};
|
||||
use servo_arc::Arc;
|
||||
use servo_atoms::Atom;
|
||||
|
@ -74,8 +74,10 @@ use uuid::Uuid;
|
|||
#[cfg(feature = "webgpu")]
|
||||
use webgpu::swapchain::WebGPUContextId;
|
||||
use webrender_api::units::DeviceIntRect;
|
||||
use webrender_traits::CompositorHitTestResult;
|
||||
|
||||
use super::bindings::codegen::Bindings::XPathEvaluatorBinding::XPathEvaluatorMethods;
|
||||
use super::clipboardevent::ClipboardEventType;
|
||||
use crate::animation_timeline::AnimationTimeline;
|
||||
use crate::animations::Animations;
|
||||
use crate::document_loader::{DocumentLoader, LoadType};
|
||||
|
@ -179,7 +181,7 @@ use crate::dom::storageevent::StorageEvent;
|
|||
use crate::dom::stylesheetlist::{StyleSheetList, StyleSheetListOwner};
|
||||
use crate::dom::text::Text;
|
||||
use crate::dom::touch::Touch;
|
||||
use crate::dom::touchevent::TouchEvent;
|
||||
use crate::dom::touchevent::TouchEvent as DomTouchEvent;
|
||||
use crate::dom::touchlist::TouchList;
|
||||
use crate::dom::treewalker::TreeWalker;
|
||||
use crate::dom::types::VisibilityStateEntry;
|
||||
|
@ -188,7 +190,7 @@ use crate::dom::virtualmethods::vtable_for;
|
|||
use crate::dom::webglrenderingcontext::WebGLRenderingContext;
|
||||
#[cfg(feature = "webgpu")]
|
||||
use crate::dom::webgpu::gpucanvascontext::GPUCanvasContext;
|
||||
use crate::dom::wheelevent::WheelEvent;
|
||||
use crate::dom::wheelevent::WheelEvent as DomWheelEvent;
|
||||
use crate::dom::window::Window;
|
||||
use crate::dom::windowproxy::WindowProxy;
|
||||
use crate::dom::xpathevaluator::XPathEvaluator;
|
||||
|
@ -482,10 +484,10 @@ pub(crate) struct Document {
|
|||
dirty_root: MutNullableDom<Element>,
|
||||
/// <https://html.spec.whatwg.org/multipage/#will-declaratively-refresh>
|
||||
declarative_refresh: DomRefCell<Option<DeclarativeRefresh>>,
|
||||
/// Pending composition events, to be handled at the next rendering opportunity.
|
||||
/// Pending input events, to be handled at the next rendering opportunity.
|
||||
#[no_trace]
|
||||
#[ignore_malloc_size_of = "CompositorEvent contains data from outside crates"]
|
||||
pending_compositor_events: DomRefCell<Vec<CompositorEvent>>,
|
||||
pending_input_events: DomRefCell<Vec<ConstellationInputEvent>>,
|
||||
/// The index of the last mouse move event in the pending compositor events queue.
|
||||
mouse_move_event_index: DomRefCell<Option<usize>>,
|
||||
/// Pending animation ticks, to be handled at the next rendering opportunity.
|
||||
|
@ -1267,39 +1269,41 @@ impl Document {
|
|||
}
|
||||
|
||||
#[allow(unsafe_code)]
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub(crate) unsafe fn handle_mouse_button_event(
|
||||
pub(crate) fn handle_mouse_button_event(
|
||||
&self,
|
||||
button: MouseButton,
|
||||
client_point: Point2D<f32>,
|
||||
mouse_event_type: MouseEventType,
|
||||
node_address: Option<UntrustedNodeAddress>,
|
||||
point_in_node: Option<Point2D<f32>>,
|
||||
event: MouseButtonEvent,
|
||||
hit_test_result: Option<CompositorHitTestResult>,
|
||||
pressed_mouse_buttons: u16,
|
||||
can_gc: CanGc,
|
||||
) {
|
||||
let mouse_event_type_string = match mouse_event_type {
|
||||
MouseEventType::Click => "click".to_owned(),
|
||||
MouseEventType::MouseUp => "mouseup".to_owned(),
|
||||
MouseEventType::MouseDown => "mousedown".to_owned(),
|
||||
// Ignore all incoming events without a hit test.
|
||||
let Some(hit_test_result) = hit_test_result else {
|
||||
return;
|
||||
};
|
||||
debug!("{}: at {:?}", mouse_event_type_string, client_point);
|
||||
|
||||
let el = node_address.and_then(|address| {
|
||||
let node = node::from_untrusted_node_address(address);
|
||||
node.inclusive_ancestors(ShadowIncluding::No)
|
||||
.filter_map(DomRoot::downcast::<Element>)
|
||||
.next()
|
||||
});
|
||||
let el = match el {
|
||||
Some(el) => el,
|
||||
None => return,
|
||||
let mouse_event_type_string = match event.action {
|
||||
MouseButtonAction::Click => "click".to_owned(),
|
||||
MouseButtonAction::Up => "mouseup".to_owned(),
|
||||
MouseButtonAction::Down => "mousedown".to_owned(),
|
||||
};
|
||||
debug!(
|
||||
"{}: at {:?}",
|
||||
mouse_event_type_string, hit_test_result.point_in_viewport
|
||||
);
|
||||
|
||||
let node = unsafe { node::from_untrusted_compositor_node_address(hit_test_result.node) };
|
||||
let Some(el) = node
|
||||
.inclusive_ancestors(ShadowIncluding::No)
|
||||
.filter_map(DomRoot::downcast::<Element>)
|
||||
.next()
|
||||
else {
|
||||
return;
|
||||
};
|
||||
|
||||
let node = el.upcast::<Node>();
|
||||
debug!("{} on {:?}", mouse_event_type_string, node.debug_str());
|
||||
// Prevent click event if form control element is disabled.
|
||||
if let MouseEventType::Click = mouse_event_type {
|
||||
if let MouseButtonAction::Click = event.action {
|
||||
// The click event is filtered by the disabled state.
|
||||
if el.is_actually_disabled() {
|
||||
return;
|
||||
|
@ -1310,10 +1314,10 @@ impl Document {
|
|||
}
|
||||
|
||||
// https://w3c.github.io/uievents/#event-type-click
|
||||
let client_x = client_point.x as i32;
|
||||
let client_y = client_point.y as i32;
|
||||
let client_x = hit_test_result.point_in_viewport.x as i32;
|
||||
let client_y = hit_test_result.point_in_viewport.y as i32;
|
||||
let click_count = 1;
|
||||
let event = MouseEvent::new(
|
||||
let dom_event = MouseEvent::new(
|
||||
&self.window,
|
||||
DOMString::from(mouse_event_type_string),
|
||||
EventBubbles::Bubbles,
|
||||
|
@ -1328,58 +1332,59 @@ impl Document {
|
|||
false,
|
||||
false,
|
||||
false,
|
||||
match &button {
|
||||
MouseButton::Left => 0i16,
|
||||
MouseButton::Middle => 1i16,
|
||||
MouseButton::Right => 2i16,
|
||||
},
|
||||
event.button.into(),
|
||||
pressed_mouse_buttons,
|
||||
None,
|
||||
point_in_node,
|
||||
Some(hit_test_result.point_relative_to_item),
|
||||
can_gc,
|
||||
);
|
||||
let event = event.upcast::<Event>();
|
||||
let dom_event = dom_event.upcast::<Event>();
|
||||
|
||||
// https://w3c.github.io/uievents/#trusted-events
|
||||
event.set_trusted(true);
|
||||
dom_event.set_trusted(true);
|
||||
// https://html.spec.whatwg.org/multipage/#run-authentic-click-activation-steps
|
||||
let activatable = el.as_maybe_activatable();
|
||||
match mouse_event_type {
|
||||
MouseEventType::Click => {
|
||||
match event.action {
|
||||
MouseButtonAction::Click => {
|
||||
el.set_click_in_progress(true);
|
||||
event.fire(node.upcast(), can_gc);
|
||||
dom_event.fire(node.upcast(), can_gc);
|
||||
el.set_click_in_progress(false);
|
||||
},
|
||||
MouseEventType::MouseDown => {
|
||||
MouseButtonAction::Down => {
|
||||
if let Some(a) = activatable {
|
||||
a.enter_formal_activation_state();
|
||||
}
|
||||
|
||||
let target = node.upcast();
|
||||
event.fire(target, can_gc);
|
||||
dom_event.fire(target, can_gc);
|
||||
},
|
||||
MouseEventType::MouseUp => {
|
||||
MouseButtonAction::Up => {
|
||||
if let Some(a) = activatable {
|
||||
a.exit_formal_activation_state();
|
||||
}
|
||||
|
||||
let target = node.upcast();
|
||||
event.fire(target, can_gc);
|
||||
dom_event.fire(target, can_gc);
|
||||
},
|
||||
}
|
||||
|
||||
if let MouseEventType::Click = mouse_event_type {
|
||||
if let MouseButtonAction::Click = event.action {
|
||||
self.commit_focus_transaction(FocusType::Element, can_gc);
|
||||
self.maybe_fire_dblclick(client_point, node, pressed_mouse_buttons, can_gc);
|
||||
self.maybe_fire_dblclick(
|
||||
hit_test_result.point_in_viewport,
|
||||
node,
|
||||
pressed_mouse_buttons,
|
||||
can_gc,
|
||||
);
|
||||
}
|
||||
|
||||
// When the contextmenu event is triggered by right mouse button
|
||||
// the contextmenu event MUST be dispatched after the mousedown event.
|
||||
if let (MouseEventType::MouseDown, MouseButton::Right) = (mouse_event_type, button) {
|
||||
if let (MouseButtonAction::Down, MouseButton::Right) = (event.action, event.button) {
|
||||
self.maybe_show_context_menu(
|
||||
node.upcast(),
|
||||
pressed_mouse_buttons,
|
||||
client_point,
|
||||
hit_test_result.point_in_viewport,
|
||||
can_gc,
|
||||
);
|
||||
}
|
||||
|
@ -1528,7 +1533,7 @@ impl Document {
|
|||
let client_x = client_point.x.to_i32().unwrap_or(0);
|
||||
let client_y = client_point.y.to_i32().unwrap_or(0);
|
||||
|
||||
let mouse_event = MouseEvent::new(
|
||||
MouseEvent::new(
|
||||
&self.window,
|
||||
DOMString::from(event_name.as_str()),
|
||||
can_bubble,
|
||||
|
@ -1548,17 +1553,22 @@ impl Document {
|
|||
None,
|
||||
None,
|
||||
can_gc,
|
||||
);
|
||||
let event = mouse_event.upcast::<Event>();
|
||||
event.fire(target, can_gc);
|
||||
)
|
||||
.upcast::<Event>()
|
||||
.fire(target, can_gc);
|
||||
}
|
||||
|
||||
pub(crate) fn handle_editing_action(&self, action: EditingActionEvent, can_gc: CanGc) -> bool {
|
||||
let clipboard_event = match action {
|
||||
EditingActionEvent::Copy => ClipboardEventType::Copy,
|
||||
EditingActionEvent::Cut => ClipboardEventType::Cut,
|
||||
EditingActionEvent::Paste => ClipboardEventType::Paste,
|
||||
};
|
||||
self.handle_clipboard_action(clipboard_event, can_gc)
|
||||
}
|
||||
|
||||
/// <https://www.w3.org/TR/clipboard-apis/#clipboard-actions>
|
||||
pub(crate) fn handle_clipboard_action(
|
||||
&self,
|
||||
action: ClipboardEventType,
|
||||
can_gc: CanGc,
|
||||
) -> bool {
|
||||
fn handle_clipboard_action(&self, action: ClipboardEventType, can_gc: CanGc) -> bool {
|
||||
// The script_triggered flag is set if the action runs because of a script, e.g. document.execCommand()
|
||||
let script_triggered = false;
|
||||
|
||||
|
@ -1762,28 +1772,29 @@ impl Document {
|
|||
#[allow(unsafe_code)]
|
||||
pub(crate) unsafe fn handle_mouse_move_event(
|
||||
&self,
|
||||
client_point: Point2D<f32>,
|
||||
prev_mouse_over_target: &MutNullableDom<Element>,
|
||||
node_address: Option<UntrustedNodeAddress>,
|
||||
hit_test_result: Option<CompositorHitTestResult>,
|
||||
pressed_mouse_buttons: u16,
|
||||
prev_mouse_over_target: &MutNullableDom<Element>,
|
||||
can_gc: CanGc,
|
||||
) {
|
||||
let maybe_new_target = node_address.and_then(|address| {
|
||||
let node = node::from_untrusted_node_address(address);
|
||||
node.inclusive_ancestors(ShadowIncluding::No)
|
||||
.filter_map(DomRoot::downcast::<Element>)
|
||||
.next()
|
||||
});
|
||||
// Ignore all incoming events without a hit test.
|
||||
let Some(hit_test_result) = hit_test_result else {
|
||||
return;
|
||||
};
|
||||
|
||||
let new_target = match maybe_new_target {
|
||||
Some(ref target) => target,
|
||||
None => return,
|
||||
let node = unsafe { node::from_untrusted_compositor_node_address(hit_test_result.node) };
|
||||
let Some(new_target) = node
|
||||
.inclusive_ancestors(ShadowIncluding::No)
|
||||
.filter_map(DomRoot::downcast::<Element>)
|
||||
.next()
|
||||
else {
|
||||
return;
|
||||
};
|
||||
|
||||
let target_has_changed = prev_mouse_over_target
|
||||
.get()
|
||||
.as_ref()
|
||||
.map_or(true, |old_target| old_target != new_target);
|
||||
.map_or(true, |old_target| old_target != &new_target);
|
||||
|
||||
// Here we know the target has changed, so we must update the state,
|
||||
// dispatch mouseout to the previous one, mouseover to the new one.
|
||||
|
@ -1808,7 +1819,7 @@ impl Document {
|
|||
}
|
||||
|
||||
self.fire_mouse_event(
|
||||
client_point,
|
||||
hit_test_result.point_in_viewport,
|
||||
old_target.upcast(),
|
||||
FireMouseEventType::Out,
|
||||
EventBubbles::Bubbles,
|
||||
|
@ -1821,7 +1832,7 @@ impl Document {
|
|||
let event_target = DomRoot::from_ref(old_target.upcast::<Node>());
|
||||
let moving_into = Some(DomRoot::from_ref(new_target.upcast::<Node>()));
|
||||
self.handle_mouse_enter_leave_event(
|
||||
client_point,
|
||||
hit_test_result.point_in_viewport,
|
||||
FireMouseEventType::Leave,
|
||||
moving_into,
|
||||
event_target,
|
||||
|
@ -1844,7 +1855,7 @@ impl Document {
|
|||
}
|
||||
|
||||
self.fire_mouse_event(
|
||||
client_point,
|
||||
hit_test_result.point_in_viewport,
|
||||
new_target.upcast(),
|
||||
FireMouseEventType::Over,
|
||||
EventBubbles::Bubbles,
|
||||
|
@ -1858,7 +1869,7 @@ impl Document {
|
|||
.map(|old_target| DomRoot::from_ref(old_target.upcast::<Node>()));
|
||||
let event_target = DomRoot::from_ref(new_target.upcast::<Node>());
|
||||
self.handle_mouse_enter_leave_event(
|
||||
client_point,
|
||||
hit_test_result.point_in_viewport,
|
||||
FireMouseEventType::Enter,
|
||||
moving_from,
|
||||
event_target,
|
||||
|
@ -1870,7 +1881,7 @@ impl Document {
|
|||
// Send mousemove event to topmost target, unless it's an iframe, in which case the
|
||||
// compositor should have also sent an event to the inner document.
|
||||
self.fire_mouse_event(
|
||||
client_point,
|
||||
hit_test_result.point_in_viewport,
|
||||
new_target.upcast(),
|
||||
FireMouseEventType::Move,
|
||||
EventBubbles::Bubbles,
|
||||
|
@ -1881,7 +1892,7 @@ impl Document {
|
|||
|
||||
// If the target has changed then store the current mouse over target for next frame.
|
||||
if target_has_changed {
|
||||
prev_mouse_over_target.set(maybe_new_target.as_deref());
|
||||
prev_mouse_over_target.set(Some(&new_target));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1938,89 +1949,93 @@ impl Document {
|
|||
}
|
||||
|
||||
#[allow(unsafe_code)]
|
||||
pub(crate) unsafe fn handle_wheel_event(
|
||||
pub(crate) fn handle_wheel_event(
|
||||
&self,
|
||||
delta: WheelDelta,
|
||||
client_point: Point2D<f32>,
|
||||
node_address: Option<UntrustedNodeAddress>,
|
||||
event: WheelEvent,
|
||||
hit_test_result: Option<CompositorHitTestResult>,
|
||||
can_gc: CanGc,
|
||||
) {
|
||||
let wheel_event_type_string = "wheel".to_owned();
|
||||
debug!("{}: at {:?}", wheel_event_type_string, client_point);
|
||||
// Ignore all incoming events without a hit test.
|
||||
let Some(hit_test_result) = hit_test_result else {
|
||||
return;
|
||||
};
|
||||
|
||||
let el = node_address.and_then(|address| {
|
||||
let node = node::from_untrusted_node_address(address);
|
||||
node.inclusive_ancestors(ShadowIncluding::No)
|
||||
.filter_map(DomRoot::downcast::<Element>)
|
||||
.next()
|
||||
});
|
||||
|
||||
let el = match el {
|
||||
Some(el) => el,
|
||||
None => return,
|
||||
let node = unsafe { node::from_untrusted_compositor_node_address(hit_test_result.node) };
|
||||
let Some(el) = node
|
||||
.inclusive_ancestors(ShadowIncluding::No)
|
||||
.filter_map(DomRoot::downcast::<Element>)
|
||||
.next()
|
||||
else {
|
||||
return;
|
||||
};
|
||||
|
||||
let node = el.upcast::<Node>();
|
||||
debug!("{}: on {:?}", wheel_event_type_string, node.debug_str());
|
||||
let wheel_event_type_string = "wheel".to_owned();
|
||||
debug!(
|
||||
"{}: on {:?} at {:?}",
|
||||
wheel_event_type_string,
|
||||
node.debug_str(),
|
||||
hit_test_result.point_in_viewport
|
||||
);
|
||||
|
||||
// https://w3c.github.io/uievents/#event-wheelevents
|
||||
let event = WheelEvent::new(
|
||||
let dom_event = DomWheelEvent::new(
|
||||
&self.window,
|
||||
DOMString::from(wheel_event_type_string),
|
||||
EventBubbles::Bubbles,
|
||||
EventCancelable::Cancelable,
|
||||
Some(&self.window),
|
||||
0i32,
|
||||
Finite::wrap(delta.x),
|
||||
Finite::wrap(delta.y),
|
||||
Finite::wrap(delta.z),
|
||||
delta.mode as u32,
|
||||
Finite::wrap(event.delta.x),
|
||||
Finite::wrap(event.delta.y),
|
||||
Finite::wrap(event.delta.z),
|
||||
event.delta.mode as u32,
|
||||
can_gc,
|
||||
);
|
||||
|
||||
let event = event.upcast::<Event>();
|
||||
event.set_trusted(true);
|
||||
let dom_event = dom_event.upcast::<Event>();
|
||||
dom_event.set_trusted(true);
|
||||
|
||||
let target = node.upcast();
|
||||
event.fire(target, can_gc);
|
||||
dom_event.fire(target, can_gc);
|
||||
}
|
||||
|
||||
#[allow(unsafe_code)]
|
||||
pub(crate) unsafe fn handle_touch_event(
|
||||
pub(crate) fn handle_touch_event(
|
||||
&self,
|
||||
event_type: TouchEventType,
|
||||
touch_id: TouchId,
|
||||
point: Point2D<f32>,
|
||||
node_address: Option<UntrustedNodeAddress>,
|
||||
event: TouchEvent,
|
||||
hit_test_result: Option<CompositorHitTestResult>,
|
||||
can_gc: CanGc,
|
||||
) -> TouchEventResult {
|
||||
let TouchId(identifier) = touch_id;
|
||||
|
||||
let event_name = match event_type {
|
||||
TouchEventType::Down => "touchstart",
|
||||
TouchEventType::Move => "touchmove",
|
||||
TouchEventType::Up => "touchend",
|
||||
TouchEventType::Cancel => "touchcancel",
|
||||
// Ignore all incoming events without a hit test.
|
||||
let Some(hit_test_result) = hit_test_result else {
|
||||
return TouchEventResult::Forwarded;
|
||||
};
|
||||
|
||||
let el = node_address.and_then(|address| {
|
||||
let node = node::from_untrusted_node_address(address);
|
||||
node.inclusive_ancestors(ShadowIncluding::No)
|
||||
.filter_map(DomRoot::downcast::<Element>)
|
||||
.next()
|
||||
});
|
||||
let el = match el {
|
||||
Some(el) => el,
|
||||
None => return TouchEventResult::Forwarded,
|
||||
let TouchId(identifier) = event.id;
|
||||
let event_name = match event.action {
|
||||
TouchEventAction::Down => "touchstart",
|
||||
TouchEventAction::Move => "touchmove",
|
||||
TouchEventAction::Up => "touchend",
|
||||
TouchEventAction::Cancel => "touchcancel",
|
||||
};
|
||||
|
||||
let node = unsafe { node::from_untrusted_compositor_node_address(hit_test_result.node) };
|
||||
let Some(el) = node
|
||||
.inclusive_ancestors(ShadowIncluding::No)
|
||||
.filter_map(DomRoot::downcast::<Element>)
|
||||
.next()
|
||||
else {
|
||||
return TouchEventResult::Forwarded;
|
||||
};
|
||||
|
||||
let target = DomRoot::upcast::<EventTarget>(el);
|
||||
let window = &*self.window;
|
||||
|
||||
let client_x = Finite::wrap(point.x as f64);
|
||||
let client_y = Finite::wrap(point.y as f64);
|
||||
let page_x = Finite::wrap(point.x as f64 + window.PageXOffset() as f64);
|
||||
let page_y = Finite::wrap(point.y as f64 + window.PageYOffset() as f64);
|
||||
let client_x = Finite::wrap(event.point.x as f64);
|
||||
let client_y = Finite::wrap(event.point.y as f64);
|
||||
let page_x = Finite::wrap(event.point.x as f64 + window.PageXOffset() as f64);
|
||||
let page_y = Finite::wrap(event.point.y as f64 + window.PageYOffset() as f64);
|
||||
|
||||
let touch = Touch::new(
|
||||
window, identifier, &target, client_x,
|
||||
|
@ -2028,14 +2043,14 @@ impl Document {
|
|||
client_x, client_y, page_x, page_y,
|
||||
);
|
||||
|
||||
match event_type {
|
||||
TouchEventType::Down => {
|
||||
match event.action {
|
||||
TouchEventAction::Down => {
|
||||
// Add a new touch point
|
||||
self.active_touch_points
|
||||
.borrow_mut()
|
||||
.push(Dom::from_ref(&*touch));
|
||||
},
|
||||
TouchEventType::Move => {
|
||||
TouchEventAction::Move => {
|
||||
// Replace an existing touch point
|
||||
let mut active_touch_points = self.active_touch_points.borrow_mut();
|
||||
match active_touch_points
|
||||
|
@ -2046,7 +2061,7 @@ impl Document {
|
|||
None => warn!("Got a touchmove event for a non-active touch point"),
|
||||
}
|
||||
},
|
||||
TouchEventType::Up | TouchEventType::Cancel => {
|
||||
TouchEventAction::Up | TouchEventAction::Cancel => {
|
||||
// Remove an existing touch point
|
||||
let mut active_touch_points = self.active_touch_points.borrow_mut();
|
||||
match active_touch_points
|
||||
|
@ -2068,7 +2083,7 @@ impl Document {
|
|||
TouchList::new(window, touches.r())
|
||||
};
|
||||
|
||||
let event = TouchEvent::new(
|
||||
let event = DomTouchEvent::new(
|
||||
window,
|
||||
DOMString::from(event_name),
|
||||
EventBubbles::Bubbles,
|
||||
|
@ -2178,19 +2193,19 @@ impl Document {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn ime_dismissed(&self, can_gc: CanGc) {
|
||||
self.request_focus(
|
||||
self.GetBody().as_ref().map(|e| e.upcast()),
|
||||
FocusType::Element,
|
||||
can_gc,
|
||||
)
|
||||
}
|
||||
pub(crate) fn dispatch_ime_event(&self, event: ImeEvent, can_gc: CanGc) {
|
||||
let composition_event = match event {
|
||||
ImeEvent::Dismissed => {
|
||||
self.request_focus(
|
||||
self.GetBody().as_ref().map(|e| e.upcast()),
|
||||
FocusType::Element,
|
||||
can_gc,
|
||||
);
|
||||
return;
|
||||
},
|
||||
ImeEvent::Composition(composition_event) => composition_event,
|
||||
};
|
||||
|
||||
pub(crate) fn dispatch_composition_event(
|
||||
&self,
|
||||
composition_event: ::keyboard_types::CompositionEvent,
|
||||
can_gc: CanGc,
|
||||
) {
|
||||
// spec: https://w3c.github.io/uievents/#compositionstart
|
||||
// spec: https://w3c.github.io/uievents/#compositionupdate
|
||||
// spec: https://w3c.github.io/uievents/#compositionend
|
||||
|
@ -3721,7 +3736,7 @@ impl Document {
|
|||
dirty_root: Default::default(),
|
||||
declarative_refresh: Default::default(),
|
||||
pending_animation_ticks: Default::default(),
|
||||
pending_compositor_events: Default::default(),
|
||||
pending_input_events: Default::default(),
|
||||
mouse_move_event_index: Default::default(),
|
||||
resize_observers: Default::default(),
|
||||
fonts: Default::default(),
|
||||
|
@ -3750,9 +3765,9 @@ impl Document {
|
|||
}
|
||||
|
||||
/// Note a pending compositor event, to be processed at the next `update_the_rendering` task.
|
||||
pub(crate) fn note_pending_compositor_event(&self, event: CompositorEvent) {
|
||||
let mut pending_compositor_events = self.pending_compositor_events.borrow_mut();
|
||||
if matches!(event, CompositorEvent::MouseMoveEvent { .. }) {
|
||||
pub(crate) fn note_pending_input_event(&self, event: ConstellationInputEvent) {
|
||||
let mut pending_compositor_events = self.pending_input_events.borrow_mut();
|
||||
if matches!(event.event, InputEvent::MouseMove(..)) {
|
||||
// First try to replace any existing mouse move event.
|
||||
if let Some(mouse_move_event) = self
|
||||
.mouse_move_event_index
|
||||
|
@ -3770,10 +3785,10 @@ impl Document {
|
|||
}
|
||||
|
||||
/// Get pending compositor events, for processing within an `update_the_rendering` task.
|
||||
pub(crate) fn take_pending_compositor_events(&self) -> Vec<CompositorEvent> {
|
||||
pub(crate) fn take_pending_input_events(&self) -> Vec<ConstellationInputEvent> {
|
||||
// Reset the mouse event index.
|
||||
*self.mouse_move_event_index.borrow_mut() = None;
|
||||
mem::take(&mut *self.pending_compositor_events.borrow_mut())
|
||||
mem::take(&mut *self.pending_input_events.borrow_mut())
|
||||
}
|
||||
|
||||
pub(crate) fn set_csp_list(&self, csp_list: Option<CspList>) {
|
||||
|
@ -5067,7 +5082,7 @@ impl DocumentMethods<crate::DomTypeHolder> for Document {
|
|||
"".into(),
|
||||
can_gc,
|
||||
))),
|
||||
"touchevent" => Ok(DomRoot::upcast(TouchEvent::new_uninitialized(
|
||||
"touchevent" => Ok(DomRoot::upcast(DomTouchEvent::new_uninitialized(
|
||||
&self.window,
|
||||
&TouchList::new(&self.window, &[]),
|
||||
&TouchList::new(&self.window, &[]),
|
||||
|
|
|
@ -43,6 +43,7 @@ use style::properties::ComputedValues;
|
|||
use style::selector_parser::{SelectorImpl, SelectorParser};
|
||||
use style::stylesheets::{Stylesheet, UrlExtraData};
|
||||
use uuid::Uuid;
|
||||
use webrender_traits::UntrustedNodeAddress as CompositorUntrustedNodeAddress;
|
||||
use xml5ever::serialize as xml_serialize;
|
||||
|
||||
use super::globalscope::GlobalScope;
|
||||
|
@ -1420,6 +1421,15 @@ pub(crate) unsafe fn from_untrusted_node_address(candidate: UntrustedNodeAddress
|
|||
DomRoot::from_ref(Node::from_untrusted_node_address(candidate))
|
||||
}
|
||||
|
||||
/// If the given untrusted node address represents a valid DOM node in the given runtime,
|
||||
/// returns it.
|
||||
#[allow(unsafe_code)]
|
||||
pub(crate) unsafe fn from_untrusted_compositor_node_address(
|
||||
candidate: CompositorUntrustedNodeAddress,
|
||||
) -> DomRoot<Node> {
|
||||
DomRoot::from_ref(Node::from_untrusted_compositor_node_address(candidate))
|
||||
}
|
||||
|
||||
#[allow(unsafe_code)]
|
||||
pub(crate) trait LayoutNodeHelpers<'dom> {
|
||||
fn type_id_for_layout(self) -> NodeTypeId;
|
||||
|
@ -2747,6 +2757,26 @@ impl Node {
|
|||
&*(conversions::private_from_object(object) as *const Self)
|
||||
}
|
||||
|
||||
/// If the given untrusted node address represents a valid DOM node in the given runtime,
|
||||
/// returns it.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Callers should ensure they pass a [`CompositorUntrustedNodeAddress`] that points
|
||||
/// to a valid [`JSObject`] in memory that represents a [`Node`].
|
||||
#[allow(unsafe_code)]
|
||||
pub(crate) unsafe fn from_untrusted_compositor_node_address(
|
||||
candidate: CompositorUntrustedNodeAddress,
|
||||
) -> &'static Self {
|
||||
// https://github.com/servo/servo/issues/6383
|
||||
let candidate = candidate.0 as usize;
|
||||
let object = candidate as *mut JSObject;
|
||||
if object.is_null() {
|
||||
panic!("Attempted to create a `Node` from an invalid pointer!")
|
||||
}
|
||||
&*(conversions::private_from_object(object) as *const Self)
|
||||
}
|
||||
|
||||
pub(crate) fn html_serialize(
|
||||
&self,
|
||||
traversal_scope: html_serialize::TraversalScope,
|
||||
|
|
|
@ -58,7 +58,7 @@ impl MixedMessage {
|
|||
ScriptThreadMessage::UnloadDocument(id) => Some(id),
|
||||
ScriptThreadMessage::ExitPipeline(id, ..) => Some(id),
|
||||
ScriptThreadMessage::ExitScriptThread => None,
|
||||
ScriptThreadMessage::SendEvent(id, ..) => Some(id),
|
||||
ScriptThreadMessage::SendInputEvent(id, ..) => Some(id),
|
||||
ScriptThreadMessage::Viewport(id, ..) => Some(id),
|
||||
ScriptThreadMessage::GetTitle(id) => Some(id),
|
||||
ScriptThreadMessage::SetDocumentActivity(id, ..) => Some(id),
|
||||
|
|
|
@ -103,7 +103,6 @@ pub(crate) enum ScriptThreadEventCategory {
|
|||
ConstellationMsg,
|
||||
DevtoolsMsg,
|
||||
DocumentEvent,
|
||||
DomEvent,
|
||||
FileRead,
|
||||
FormPlannedNavigation,
|
||||
HistoryEvent,
|
||||
|
@ -137,7 +136,6 @@ impl From<ScriptThreadEventCategory> for ProfilerCategory {
|
|||
ScriptThreadEventCategory::ConstellationMsg => ProfilerCategory::ScriptConstellationMsg,
|
||||
ScriptThreadEventCategory::DevtoolsMsg => ProfilerCategory::ScriptDevtoolsMsg,
|
||||
ScriptThreadEventCategory::DocumentEvent => ProfilerCategory::ScriptDocumentEvent,
|
||||
ScriptThreadEventCategory::DomEvent => ProfilerCategory::ScriptDomEvent,
|
||||
ScriptThreadEventCategory::EnterFullscreen => ProfilerCategory::ScriptEnterFullscreen,
|
||||
ScriptThreadEventCategory::ExitFullscreen => ProfilerCategory::ScriptExitFullscreen,
|
||||
ScriptThreadEventCategory::FileRead => ProfilerCategory::ScriptFileRead,
|
||||
|
@ -181,14 +179,13 @@ impl From<ScriptThreadEventCategory> for ScriptHangAnnotation {
|
|||
ScriptThreadEventCategory::ConstellationMsg => ScriptHangAnnotation::ConstellationMsg,
|
||||
ScriptThreadEventCategory::DevtoolsMsg => ScriptHangAnnotation::DevtoolsMsg,
|
||||
ScriptThreadEventCategory::DocumentEvent => ScriptHangAnnotation::DocumentEvent,
|
||||
ScriptThreadEventCategory::DomEvent => ScriptHangAnnotation::DomEvent,
|
||||
ScriptThreadEventCategory::InputEvent => ScriptHangAnnotation::InputEvent,
|
||||
ScriptThreadEventCategory::FileRead => ScriptHangAnnotation::FileRead,
|
||||
ScriptThreadEventCategory::FormPlannedNavigation => {
|
||||
ScriptHangAnnotation::FormPlannedNavigation
|
||||
},
|
||||
ScriptThreadEventCategory::HistoryEvent => ScriptHangAnnotation::HistoryEvent,
|
||||
ScriptThreadEventCategory::ImageCacheMsg => ScriptHangAnnotation::ImageCacheMsg,
|
||||
ScriptThreadEventCategory::InputEvent => ScriptHangAnnotation::InputEvent,
|
||||
ScriptThreadEventCategory::NetworkEvent => ScriptHangAnnotation::NetworkEvent,
|
||||
ScriptThreadEventCategory::Rendering => ScriptHangAnnotation::Rendering,
|
||||
ScriptThreadEventCategory::Resize => ScriptHangAnnotation::Resize,
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -999,11 +999,6 @@ impl Servo {
|
|||
},
|
||||
);
|
||||
},
|
||||
EmbedderMsg::EventDelivered(webview_id, event) => {
|
||||
if let Some(webview) = self.get_webview_handle(webview_id) {
|
||||
webview.delegate().notify_event_delivered(webview, event);
|
||||
}
|
||||
},
|
||||
EmbedderMsg::PlayGamepadHapticEffect(
|
||||
webview_id,
|
||||
gamepad_index,
|
||||
|
|
|
@ -8,16 +8,15 @@ use std::rc::{Rc, Weak};
|
|||
use std::time::Duration;
|
||||
|
||||
use base::id::WebViewId;
|
||||
use compositing::windowing::{MouseWindowEvent, WebRenderDebugOption};
|
||||
use compositing::windowing::WebRenderDebugOption;
|
||||
use compositing::IOCompositor;
|
||||
use compositing_traits::ConstellationMsg;
|
||||
use embedder_traits::{
|
||||
ClipboardEventType, Cursor, GamepadEvent, LoadStatus, MediaSessionActionType, Theme,
|
||||
TouchEventType, TouchId, TraversalDirection, WheelDelta,
|
||||
Cursor, InputEvent, LoadStatus, MediaSessionActionType, Theme, TouchEventAction,
|
||||
TraversalDirection,
|
||||
};
|
||||
use keyboard_types::{CompositionEvent, KeyboardEvent};
|
||||
use url::Url;
|
||||
use webrender_api::units::{DeviceIntPoint, DevicePoint, DeviceRect};
|
||||
use webrender_api::units::{DeviceIntPoint, DeviceRect};
|
||||
use webrender_api::ScrollLocation;
|
||||
|
||||
use crate::clipboard_delegate::{ClipboardDelegate, DefaultClipboardDelegate};
|
||||
|
@ -310,68 +309,30 @@ impl WebView {
|
|||
))
|
||||
}
|
||||
|
||||
pub fn notify_pointer_button_event(&self, event: MouseWindowEvent) {
|
||||
self.inner()
|
||||
.compositor
|
||||
.borrow_mut()
|
||||
.on_mouse_window_event_class(event);
|
||||
}
|
||||
|
||||
pub fn notify_pointer_move_event(&self, event: DevicePoint) {
|
||||
self.inner()
|
||||
.compositor
|
||||
.borrow_mut()
|
||||
.on_mouse_window_move_event_class(event);
|
||||
}
|
||||
|
||||
pub fn notify_touch_event(&self, event_type: TouchEventType, id: TouchId, point: DevicePoint) {
|
||||
self.inner()
|
||||
.compositor
|
||||
.borrow_mut()
|
||||
.on_touch_event(event_type, id, point);
|
||||
}
|
||||
|
||||
pub fn notify_wheel_event(&self, delta: WheelDelta, point: DevicePoint) {
|
||||
self.inner()
|
||||
.compositor
|
||||
.borrow_mut()
|
||||
.on_wheel_event(delta, point);
|
||||
}
|
||||
|
||||
pub fn notify_scroll_event(
|
||||
&self,
|
||||
location: ScrollLocation,
|
||||
point: DeviceIntPoint,
|
||||
touch_event_type: TouchEventType,
|
||||
touch_event_action: TouchEventAction,
|
||||
) {
|
||||
self.inner()
|
||||
.compositor
|
||||
.borrow_mut()
|
||||
.on_scroll_event(location, point, touch_event_type);
|
||||
.on_scroll_event(location, point, touch_event_action);
|
||||
}
|
||||
|
||||
pub fn notify_keyboard_event(&self, event: KeyboardEvent) {
|
||||
self.inner()
|
||||
.constellation_proxy
|
||||
.send(ConstellationMsg::Keyboard(self.id(), event))
|
||||
}
|
||||
pub fn notify_input_event(&self, event: InputEvent) {
|
||||
// Events with a `point` first go to the compositor for hit testing.
|
||||
if event.point().is_some() {
|
||||
self.inner().compositor.borrow_mut().on_input_event(event);
|
||||
return;
|
||||
}
|
||||
|
||||
pub fn notify_ime_event(&self, event: CompositionEvent) {
|
||||
self.inner()
|
||||
.constellation_proxy
|
||||
.send(ConstellationMsg::IMECompositionEvent(event))
|
||||
}
|
||||
|
||||
pub fn notify_ime_dismissed_event(&self) {
|
||||
self.inner()
|
||||
.constellation_proxy
|
||||
.send(ConstellationMsg::IMEDismissed);
|
||||
}
|
||||
|
||||
pub fn notify_gamepad_event(&self, event: GamepadEvent) {
|
||||
self.inner()
|
||||
.constellation_proxy
|
||||
.send(ConstellationMsg::Gamepad(event));
|
||||
.send(ConstellationMsg::ForwardInputEvent(
|
||||
event, None, /* hit_test */
|
||||
))
|
||||
}
|
||||
|
||||
pub fn notify_media_session_action_event(&self, event: MediaSessionActionType) {
|
||||
|
@ -380,12 +341,6 @@ impl WebView {
|
|||
.send(ConstellationMsg::MediaSessionAction(event));
|
||||
}
|
||||
|
||||
pub fn notify_clipboard_event(&self, event: ClipboardEventType) {
|
||||
self.inner()
|
||||
.constellation_proxy
|
||||
.send(ConstellationMsg::Clipboard(event));
|
||||
}
|
||||
|
||||
pub fn notify_vsync(&self) {
|
||||
self.inner().compositor.borrow_mut().on_vsync();
|
||||
}
|
||||
|
|
|
@ -7,9 +7,9 @@ use std::path::PathBuf;
|
|||
use base::id::PipelineId;
|
||||
use compositing_traits::ConstellationMsg;
|
||||
use embedder_traits::{
|
||||
AllowOrDeny, AuthenticationResponse, CompositorEventVariant, ContextMenuResult, Cursor,
|
||||
FilterPattern, GamepadHapticEffectType, InputMethodType, LoadStatus, MediaSessionEvent,
|
||||
PermissionFeature, PromptDefinition, PromptOrigin, WebResourceRequest, WebResourceResponseMsg,
|
||||
AllowOrDeny, AuthenticationResponse, ContextMenuResult, Cursor, FilterPattern,
|
||||
GamepadHapticEffectType, InputMethodType, LoadStatus, MediaSessionEvent, PermissionFeature,
|
||||
PromptDefinition, PromptOrigin, WebResourceRequest, WebResourceResponseMsg,
|
||||
};
|
||||
use ipc_channel::ipc::IpcSender;
|
||||
use keyboard_types::KeyboardEvent;
|
||||
|
@ -170,8 +170,6 @@ pub trait WebViewDelegate {
|
|||
fn notify_ready_to_show(&self, _webview: WebView) {}
|
||||
/// Notify the embedder that it needs to present a new frame.
|
||||
fn notify_new_frame_ready(&self, _webview: WebView) {}
|
||||
/// The given event was delivered to a pipeline in the given webview.
|
||||
fn notify_event_delivered(&self, _webview: WebView, _event: CompositorEventVariant) {}
|
||||
/// The history state has changed.
|
||||
// changed pattern; maybe wasteful if embedder doesn’t care?
|
||||
fn notify_history_changed(&self, _webview: WebView, _: Vec<Url>, _: usize) {}
|
||||
|
|
|
@ -20,7 +20,6 @@ pub enum ScriptHangAnnotation {
|
|||
ConstellationMsg,
|
||||
DevtoolsMsg,
|
||||
DocumentEvent,
|
||||
DomEvent,
|
||||
FileRead,
|
||||
FormPlannedNavigation,
|
||||
ImageCacheMsg,
|
||||
|
|
|
@ -8,16 +8,13 @@ use std::time::Duration;
|
|||
|
||||
use base::id::{BrowsingContextId, PipelineId, TopLevelBrowsingContextId, WebViewId};
|
||||
use base::Epoch;
|
||||
use embedder_traits::{
|
||||
ClipboardEventType, Cursor, GamepadEvent, MediaSessionActionType, Theme, TraversalDirection,
|
||||
};
|
||||
use embedder_traits::{Cursor, InputEvent, MediaSessionActionType, Theme, TraversalDirection};
|
||||
use ipc_channel::ipc::IpcSender;
|
||||
use keyboard_types::{CompositionEvent, KeyboardEvent};
|
||||
use script_traits::{
|
||||
AnimationTickType, CompositorEvent, LogEntry, WebDriverCommandMsg, WindowSizeData,
|
||||
WindowSizeType,
|
||||
AnimationTickType, LogEntry, WebDriverCommandMsg, WindowSizeData, WindowSizeType,
|
||||
};
|
||||
use servo_url::ServoUrl;
|
||||
use webrender_traits::CompositorHitTestResult;
|
||||
|
||||
/// Messages to the constellation.
|
||||
pub enum ConstellationMsg {
|
||||
|
@ -34,10 +31,6 @@ pub enum ConstellationMsg {
|
|||
GetFocusTopLevelBrowsingContext(IpcSender<Option<TopLevelBrowsingContextId>>),
|
||||
/// Query the constellation to see if the current compositor output is stable
|
||||
IsReadyToSaveImage(HashMap<PipelineId, Epoch>),
|
||||
/// Inform the constellation of a key event.
|
||||
Keyboard(WebViewId, KeyboardEvent),
|
||||
/// Inform the constellation of a composition event (IME).
|
||||
IMECompositionEvent(CompositionEvent),
|
||||
/// Whether to allow script to navigate.
|
||||
AllowNavigationResponse(PipelineId, bool),
|
||||
/// Request to load a page.
|
||||
|
@ -70,8 +63,8 @@ pub enum ConstellationMsg {
|
|||
FocusWebView(TopLevelBrowsingContextId),
|
||||
/// Make none of the webviews focused.
|
||||
BlurWebView,
|
||||
/// Forward an event to the script task of the given pipeline.
|
||||
ForwardEvent(PipelineId, CompositorEvent),
|
||||
/// Forward an input event to an appropriate ScriptTask.
|
||||
ForwardInputEvent(InputEvent, Option<CompositorHitTestResult>),
|
||||
/// Requesting a change to the onscreen cursor.
|
||||
SetCursor(WebViewId, Cursor),
|
||||
/// Enable the sampling profiler, with a given sampling rate and max total sampling duration.
|
||||
|
@ -82,12 +75,6 @@ pub enum ConstellationMsg {
|
|||
MediaSessionAction(MediaSessionActionType),
|
||||
/// Set whether to use less resources, by stopping animations and running timers at a heavily limited rate.
|
||||
SetWebViewThrottled(TopLevelBrowsingContextId, bool),
|
||||
/// Virtual keyboard was dismissed
|
||||
IMEDismissed,
|
||||
/// Gamepad state has changed
|
||||
Gamepad(GamepadEvent),
|
||||
/// Inform the constellation of a clipboard event.
|
||||
Clipboard(ClipboardEventType),
|
||||
}
|
||||
|
||||
impl fmt::Debug for ConstellationMsg {
|
||||
|
@ -106,8 +93,6 @@ impl ConstellationMsg {
|
|||
GetPipeline(..) => "GetPipeline",
|
||||
GetFocusTopLevelBrowsingContext(..) => "GetFocusTopLevelBrowsingContext",
|
||||
IsReadyToSaveImage(..) => "IsReadyToSaveImage",
|
||||
Keyboard(..) => "Keyboard",
|
||||
IMECompositionEvent(..) => "IMECompositionEvent",
|
||||
AllowNavigationResponse(..) => "AllowNavigationResponse",
|
||||
LoadUrl(..) => "LoadUrl",
|
||||
TraverseHistory(..) => "TraverseHistory",
|
||||
|
@ -123,16 +108,13 @@ impl ConstellationMsg {
|
|||
FocusWebView(..) => "FocusWebView",
|
||||
BlurWebView => "BlurWebView",
|
||||
SendError(..) => "SendError",
|
||||
ForwardEvent(..) => "ForwardEvent",
|
||||
ForwardInputEvent(..) => "ForwardEvent",
|
||||
SetCursor(..) => "SetCursor",
|
||||
ToggleProfiler(..) => "EnableProfiler",
|
||||
ExitFullScreen(..) => "ExitFullScreen",
|
||||
MediaSessionAction(..) => "MediaSessionAction",
|
||||
SetWebViewThrottled(..) => "SetWebViewThrottled",
|
||||
IMEDismissed => "IMEDismissed",
|
||||
ClearCache => "ClearCache",
|
||||
Gamepad(..) => "Gamepad",
|
||||
Clipboard(..) => "Clipboard",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ use base::id::{PipelineId, TopLevelBrowsingContextId};
|
|||
use base::Epoch;
|
||||
pub use constellation_msg::ConstellationMsg;
|
||||
use crossbeam_channel::{Receiver, Sender};
|
||||
use embedder_traits::{EventLoopWaker, MouseButton, MouseEventType};
|
||||
use embedder_traits::{EventLoopWaker, MouseButton, MouseButtonAction};
|
||||
use euclid::Rect;
|
||||
use ipc_channel::ipc::IpcSender;
|
||||
use log::warn;
|
||||
|
@ -93,7 +93,7 @@ pub enum CompositorMsg {
|
|||
/// The load of a page has completed
|
||||
LoadComplete(TopLevelBrowsingContextId),
|
||||
/// WebDriver mouse button event
|
||||
WebDriverMouseButtonEvent(MouseEventType, MouseButton, f32, f32),
|
||||
WebDriverMouseButtonEvent(MouseButtonAction, MouseButton, f32, f32),
|
||||
/// WebDriver mouse move event
|
||||
WebDriverMouseMoveEvent(f32, f32),
|
||||
|
||||
|
|
219
components/shared/embedder/input_events.rs
Normal file
219
components/shared/embedder/input_events.rs
Normal file
|
@ -0,0 +1,219 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use keyboard_types::{CompositionEvent, KeyboardEvent};
|
||||
use malloc_size_of_derive::MallocSizeOf;
|
||||
use serde::{Deserialize, Serialize};
|
||||
pub use webrender_api::units::DevicePoint;
|
||||
|
||||
/// An input event that is sent from the embedder to Servo.
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub enum InputEvent {
|
||||
EditingAction(EditingActionEvent),
|
||||
Gamepad(GamepadEvent),
|
||||
Ime(ImeEvent),
|
||||
Keyboard(KeyboardEvent),
|
||||
MouseButton(MouseButtonEvent),
|
||||
MouseMove(MouseMoveEvent),
|
||||
Touch(TouchEvent),
|
||||
Wheel(WheelEvent),
|
||||
}
|
||||
|
||||
/// An editing action that should be performed on a `WebView`.
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub enum EditingActionEvent {
|
||||
Copy,
|
||||
Cut,
|
||||
Paste,
|
||||
}
|
||||
|
||||
impl InputEvent {
|
||||
pub fn point(&self) -> Option<DevicePoint> {
|
||||
match self {
|
||||
InputEvent::EditingAction(..) => None,
|
||||
InputEvent::Gamepad(..) => None,
|
||||
InputEvent::Ime(..) => None,
|
||||
InputEvent::Keyboard(..) => None,
|
||||
InputEvent::MouseButton(event) => Some(event.point),
|
||||
InputEvent::MouseMove(event) => Some(event.point),
|
||||
InputEvent::Touch(event) => Some(event.point),
|
||||
InputEvent::Wheel(event) => Some(event.point),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
|
||||
pub struct MouseButtonEvent {
|
||||
pub action: MouseButtonAction,
|
||||
pub button: MouseButton,
|
||||
pub point: DevicePoint,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
|
||||
pub enum MouseButton {
|
||||
Left,
|
||||
Middle,
|
||||
Right,
|
||||
Back,
|
||||
Forward,
|
||||
Other(u16),
|
||||
}
|
||||
|
||||
impl From<u16> for MouseButton {
|
||||
fn from(value: u16) -> Self {
|
||||
match value {
|
||||
0 => MouseButton::Left,
|
||||
1 => MouseButton::Middle,
|
||||
2 => MouseButton::Right,
|
||||
3 => MouseButton::Back,
|
||||
4 => MouseButton::Forward,
|
||||
_ => MouseButton::Other(value),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<MouseButton> for i16 {
|
||||
fn from(value: MouseButton) -> Self {
|
||||
match value {
|
||||
MouseButton::Left => 0,
|
||||
MouseButton::Middle => 1,
|
||||
MouseButton::Right => 2,
|
||||
MouseButton::Back => 3,
|
||||
MouseButton::Forward => 4,
|
||||
MouseButton::Other(value) => value as i16,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The types of mouse events
|
||||
#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
|
||||
pub enum MouseButtonAction {
|
||||
/// Mouse button clicked
|
||||
Click,
|
||||
/// Mouse button down
|
||||
Down,
|
||||
/// Mouse button up
|
||||
Up,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
|
||||
pub struct MouseMoveEvent {
|
||||
pub point: DevicePoint,
|
||||
}
|
||||
|
||||
/// The type of input represented by a multi-touch event.
|
||||
#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
|
||||
pub enum TouchEventAction {
|
||||
/// A new touch point came in contact with the screen.
|
||||
Down,
|
||||
/// An existing touch point changed location.
|
||||
Move,
|
||||
/// A touch point was removed from the screen.
|
||||
Up,
|
||||
/// The system stopped tracking a touch point.
|
||||
Cancel,
|
||||
}
|
||||
|
||||
/// An opaque identifier for a touch point.
|
||||
///
|
||||
/// <http://w3c.github.io/touch-events/#widl-Touch-identifier>
|
||||
#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
|
||||
pub struct TouchId(pub i32);
|
||||
|
||||
#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
|
||||
pub struct TouchEvent {
|
||||
pub action: TouchEventAction,
|
||||
pub id: TouchId,
|
||||
pub point: DevicePoint,
|
||||
}
|
||||
|
||||
/// Mode to measure WheelDelta floats in
|
||||
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
|
||||
pub enum WheelMode {
|
||||
/// Delta values are specified in pixels
|
||||
DeltaPixel = 0x00,
|
||||
/// Delta values are specified in lines
|
||||
DeltaLine = 0x01,
|
||||
/// Delta values are specified in pages
|
||||
DeltaPage = 0x02,
|
||||
}
|
||||
|
||||
/// The Wheel event deltas in every direction
|
||||
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
|
||||
pub struct WheelDelta {
|
||||
/// Delta in the left/right direction
|
||||
pub x: f64,
|
||||
/// Delta in the up/down direction
|
||||
pub y: f64,
|
||||
/// Delta in the direction going into/out of the screen
|
||||
pub z: f64,
|
||||
/// Mode to measure the floats in
|
||||
pub mode: WheelMode,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
|
||||
pub struct WheelEvent {
|
||||
pub delta: WheelDelta,
|
||||
pub point: DevicePoint,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub enum ImeEvent {
|
||||
Composition(CompositionEvent),
|
||||
Dismissed,
|
||||
}
|
||||
|
||||
#[derive(
|
||||
Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, Ord, PartialEq, PartialOrd, Serialize,
|
||||
)]
|
||||
/// Index of gamepad in list of system's connected gamepads
|
||||
pub struct GamepadIndex(pub usize);
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
/// The minimum and maximum values that can be reported for axis or button input from this gamepad
|
||||
pub struct GamepadInputBounds {
|
||||
/// Minimum and maximum axis values
|
||||
pub axis_bounds: (f64, f64),
|
||||
/// Minimum and maximum button values
|
||||
pub button_bounds: (f64, f64),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
/// The haptic effects supported by this gamepad
|
||||
pub struct GamepadSupportedHapticEffects {
|
||||
/// Gamepad support for dual rumble effects
|
||||
pub supports_dual_rumble: bool,
|
||||
/// Gamepad support for trigger rumble effects
|
||||
pub supports_trigger_rumble: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
/// The type of Gamepad event
|
||||
pub enum GamepadEvent {
|
||||
/// A new gamepad has been connected
|
||||
/// <https://www.w3.org/TR/gamepad/#event-gamepadconnected>
|
||||
Connected(
|
||||
GamepadIndex,
|
||||
String,
|
||||
GamepadInputBounds,
|
||||
GamepadSupportedHapticEffects,
|
||||
),
|
||||
/// An existing gamepad has been disconnected
|
||||
/// <https://www.w3.org/TR/gamepad/#event-gamepaddisconnected>
|
||||
Disconnected(GamepadIndex),
|
||||
/// An existing gamepad has been updated
|
||||
/// <https://www.w3.org/TR/gamepad/#receiving-inputs>
|
||||
Updated(GamepadIndex, GamepadUpdateType),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
/// The type of Gamepad input being updated
|
||||
pub enum GamepadUpdateType {
|
||||
/// Axis index and input value
|
||||
/// <https://www.w3.org/TR/gamepad/#dfn-represents-a-standard-gamepad-axis>
|
||||
Axis(usize, f64),
|
||||
/// Button index and input value
|
||||
/// <https://www.w3.org/TR/gamepad/#dfn-represents-a-standard-gamepad-button>
|
||||
Button(usize, f64),
|
||||
}
|
|
@ -2,6 +2,7 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
pub mod input_events;
|
||||
pub mod resources;
|
||||
|
||||
use std::fmt::{Debug, Error, Formatter};
|
||||
|
@ -11,7 +12,7 @@ use base::id::{PipelineId, WebViewId};
|
|||
use crossbeam_channel::Sender;
|
||||
use http::{HeaderMap, Method, StatusCode};
|
||||
use ipc_channel::ipc::IpcSender;
|
||||
use keyboard_types::KeyboardEvent;
|
||||
pub use keyboard_types::{KeyboardEvent, Modifiers};
|
||||
use log::warn;
|
||||
use malloc_size_of_derive::MallocSizeOf;
|
||||
use num_derive::FromPrimitive;
|
||||
|
@ -19,6 +20,8 @@ use serde::{Deserialize, Serialize};
|
|||
use servo_url::ServoUrl;
|
||||
use webrender_api::units::{DeviceIntPoint, DeviceIntRect, DeviceIntSize};
|
||||
|
||||
pub use crate::input_events::*;
|
||||
|
||||
/// A cursor for the window. This is different from a CSS cursor (see
|
||||
/// `CursorKind`) in that it has no `Auto` value.
|
||||
#[repr(u8)]
|
||||
|
@ -250,29 +253,12 @@ pub enum EmbedderMsg {
|
|||
OnDevtoolsStarted(Result<u16, ()>, String),
|
||||
/// Ask the user to allow a devtools client to connect.
|
||||
RequestDevtoolsConnection(IpcSender<AllowOrDeny>),
|
||||
/// The given event was delivered to a pipeline in the given browser.
|
||||
EventDelivered(WebViewId, CompositorEventVariant),
|
||||
/// Request to play a haptic effect on a connected gamepad.
|
||||
PlayGamepadHapticEffect(WebViewId, usize, GamepadHapticEffectType, IpcSender<bool>),
|
||||
/// Request to stop a haptic effect on a connected gamepad.
|
||||
StopGamepadHapticEffect(WebViewId, usize, IpcSender<bool>),
|
||||
}
|
||||
|
||||
/// The variant of CompositorEvent that was delivered to a pipeline.
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub enum CompositorEventVariant {
|
||||
ResizeEvent,
|
||||
MouseButtonEvent,
|
||||
MouseMoveEvent,
|
||||
TouchEvent,
|
||||
WheelEvent,
|
||||
KeyboardEvent,
|
||||
CompositionEvent,
|
||||
IMEDismissedEvent,
|
||||
GamepadEvent,
|
||||
ClipboardEvent,
|
||||
}
|
||||
|
||||
impl Debug for EmbedderMsg {
|
||||
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
|
||||
match *self {
|
||||
|
@ -312,7 +298,6 @@ impl Debug for EmbedderMsg {
|
|||
EmbedderMsg::OnDevtoolsStarted(..) => write!(f, "OnDevtoolsStarted"),
|
||||
EmbedderMsg::RequestDevtoolsConnection(..) => write!(f, "RequestDevtoolsConnection"),
|
||||
EmbedderMsg::ShowContextMenu(..) => write!(f, "ShowContextMenu"),
|
||||
EmbedderMsg::EventDelivered(..) => write!(f, "HitTestedEvent"),
|
||||
EmbedderMsg::PlayGamepadHapticEffect(..) => write!(f, "PlayGamepadHapticEffect"),
|
||||
EmbedderMsg::StopGamepadHapticEffect(..) => write!(f, "StopGamepadHapticEffect"),
|
||||
}
|
||||
|
@ -535,150 +520,6 @@ impl WebResourceResponse {
|
|||
}
|
||||
}
|
||||
|
||||
/// The type of input represented by a multi-touch event.
|
||||
#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
|
||||
pub enum TouchEventType {
|
||||
/// A new touch point came in contact with the screen.
|
||||
Down,
|
||||
/// An existing touch point changed location.
|
||||
Move,
|
||||
/// A touch point was removed from the screen.
|
||||
Up,
|
||||
/// The system stopped tracking a touch point.
|
||||
Cancel,
|
||||
}
|
||||
|
||||
/// An opaque identifier for a touch point.
|
||||
///
|
||||
/// <http://w3c.github.io/touch-events/#widl-Touch-identifier>
|
||||
#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
|
||||
pub struct TouchId(pub i32);
|
||||
|
||||
#[derive(
|
||||
Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, Ord, PartialEq, PartialOrd, Serialize,
|
||||
)]
|
||||
/// Index of gamepad in list of system's connected gamepads
|
||||
pub struct GamepadIndex(pub usize);
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
/// The minimum and maximum values that can be reported for axis or button input from this gamepad
|
||||
pub struct GamepadInputBounds {
|
||||
/// Minimum and maximum axis values
|
||||
pub axis_bounds: (f64, f64),
|
||||
/// Minimum and maximum button values
|
||||
pub button_bounds: (f64, f64),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
/// The haptic effects supported by this gamepad
|
||||
pub struct GamepadSupportedHapticEffects {
|
||||
/// Gamepad support for dual rumble effects
|
||||
pub supports_dual_rumble: bool,
|
||||
/// Gamepad support for trigger rumble effects
|
||||
pub supports_trigger_rumble: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
/// The type of Gamepad event
|
||||
pub enum GamepadEvent {
|
||||
/// A new gamepad has been connected
|
||||
/// <https://www.w3.org/TR/gamepad/#event-gamepadconnected>
|
||||
Connected(
|
||||
GamepadIndex,
|
||||
String,
|
||||
GamepadInputBounds,
|
||||
GamepadSupportedHapticEffects,
|
||||
),
|
||||
/// An existing gamepad has been disconnected
|
||||
/// <https://www.w3.org/TR/gamepad/#event-gamepaddisconnected>
|
||||
Disconnected(GamepadIndex),
|
||||
/// An existing gamepad has been updated
|
||||
/// <https://www.w3.org/TR/gamepad/#receiving-inputs>
|
||||
Updated(GamepadIndex, GamepadUpdateType),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
/// The type of Gamepad input being updated
|
||||
pub enum GamepadUpdateType {
|
||||
/// Axis index and input value
|
||||
/// <https://www.w3.org/TR/gamepad/#dfn-represents-a-standard-gamepad-axis>
|
||||
Axis(usize, f64),
|
||||
/// Button index and input value
|
||||
/// <https://www.w3.org/TR/gamepad/#dfn-represents-a-standard-gamepad-button>
|
||||
Button(usize, f64),
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
|
||||
pub enum MouseButton {
|
||||
/// The left mouse button.
|
||||
Left = 1,
|
||||
/// The right mouse button.
|
||||
Right = 2,
|
||||
/// The middle mouse button.
|
||||
Middle = 4,
|
||||
}
|
||||
|
||||
/// The types of mouse events
|
||||
#[derive(Debug, Deserialize, MallocSizeOf, Serialize)]
|
||||
pub enum MouseEventType {
|
||||
/// Mouse button clicked
|
||||
Click,
|
||||
/// Mouse button down
|
||||
MouseDown,
|
||||
/// Mouse button up
|
||||
MouseUp,
|
||||
}
|
||||
|
||||
/// Mode to measure WheelDelta floats in
|
||||
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
|
||||
pub enum WheelMode {
|
||||
/// Delta values are specified in pixels
|
||||
DeltaPixel = 0x00,
|
||||
/// Delta values are specified in lines
|
||||
DeltaLine = 0x01,
|
||||
/// Delta values are specified in pages
|
||||
DeltaPage = 0x02,
|
||||
}
|
||||
|
||||
/// The Wheel event deltas in every direction
|
||||
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
|
||||
pub struct WheelDelta {
|
||||
/// Delta in the left/right direction
|
||||
pub x: f64,
|
||||
/// Delta in the up/down direction
|
||||
pub y: f64,
|
||||
/// Delta in the direction going into/out of the screen
|
||||
pub z: f64,
|
||||
/// Mode to measure the floats in
|
||||
pub mode: WheelMode,
|
||||
}
|
||||
|
||||
/// The mouse button involved in the event.
|
||||
/// The types of clipboard events
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub enum ClipboardEventType {
|
||||
/// Contents of the system clipboard are changed
|
||||
Change,
|
||||
/// Copy
|
||||
Copy,
|
||||
/// Cut
|
||||
Cut,
|
||||
/// Paste
|
||||
Paste,
|
||||
}
|
||||
|
||||
impl ClipboardEventType {
|
||||
/// Convert to event name
|
||||
pub fn as_str(&self) -> &str {
|
||||
match *self {
|
||||
ClipboardEventType::Change => "clipboardchange",
|
||||
ClipboardEventType::Copy => "copy",
|
||||
ClipboardEventType::Cut => "cut",
|
||||
ClipboardEventType::Paste => "paste",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The direction of a history traversal
|
||||
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
|
||||
pub enum TraversalDirection {
|
||||
|
|
|
@ -83,7 +83,6 @@ pub enum ProfilerCategory {
|
|||
ScriptConstellationMsg = 0x61,
|
||||
ScriptDevtoolsMsg = 0x62,
|
||||
ScriptDocumentEvent = 0x63,
|
||||
ScriptDomEvent = 0x64,
|
||||
|
||||
/// Rust tracing only: the script thread is executing a script.
|
||||
/// This may include time doing layout or parse work initiated by the script.
|
||||
|
@ -149,7 +148,6 @@ impl ProfilerCategory {
|
|||
ProfilerCategory::ScriptConstellationMsg => "ScriptConstellationMsg",
|
||||
ProfilerCategory::ScriptDevtoolsMsg => "ScriptDevtoolsMsg",
|
||||
ProfilerCategory::ScriptDocumentEvent => "ScriptDocumentEvent",
|
||||
ProfilerCategory::ScriptDomEvent => "ScriptDomEvent",
|
||||
ProfilerCategory::ScriptEvaluate => "ScriptEvaluate",
|
||||
ProfilerCategory::ScriptEvent => "ScriptEvent",
|
||||
ProfilerCategory::ScriptFileRead => "ScriptFileRead",
|
||||
|
|
|
@ -31,17 +31,14 @@ use bluetooth_traits::BluetoothRequest;
|
|||
use canvas_traits::webgl::WebGLPipeline;
|
||||
use crossbeam_channel::{RecvTimeoutError, Sender};
|
||||
use devtools_traits::{DevtoolScriptControlMsg, ScriptToDevtoolsControlMsg, WorkerId};
|
||||
use embedder_traits::{
|
||||
ClipboardEventType, CompositorEventVariant, GamepadEvent, MediaSessionActionType, MouseButton,
|
||||
MouseEventType, Theme, TouchEventType, TouchId, WheelDelta,
|
||||
};
|
||||
use euclid::default::Point2D;
|
||||
use embedder_traits::input_events::InputEvent;
|
||||
use embedder_traits::{MediaSessionActionType, MouseButton, MouseButtonAction, Theme};
|
||||
use euclid::{Rect, Scale, Size2D, UnknownUnit, Vector2D};
|
||||
use http::{HeaderMap, Method};
|
||||
use ipc_channel::ipc::{IpcReceiver, IpcSender};
|
||||
use ipc_channel::Error as IpcError;
|
||||
use keyboard_types::webdriver::Event as WebDriverInputEvent;
|
||||
use keyboard_types::{CompositionEvent, KeyboardEvent};
|
||||
use keyboard_types::KeyboardEvent;
|
||||
use libc::c_void;
|
||||
use log::warn;
|
||||
use malloc_size_of::malloc_size_of_is_0;
|
||||
|
@ -62,7 +59,8 @@ use webgpu::WebGPUMsg;
|
|||
use webrender_api::units::{DeviceIntSize, DevicePixel, LayoutPixel};
|
||||
use webrender_api::{DocumentId, ExternalScrollId, ImageKey};
|
||||
use webrender_traits::{
|
||||
CrossProcessCompositorApi, UntrustedNodeAddress as WebRenderUntrustedNodeAddress,
|
||||
CompositorHitTestResult, CrossProcessCompositorApi,
|
||||
UntrustedNodeAddress as WebRenderUntrustedNodeAddress,
|
||||
};
|
||||
|
||||
pub use crate::script_msg::{
|
||||
|
@ -315,7 +313,7 @@ pub enum ScriptThreadMessage {
|
|||
/// Notifies the script that the whole thread should be closed.
|
||||
ExitScriptThread,
|
||||
/// Sends a DOM event.
|
||||
SendEvent(PipelineId, CompositorEvent),
|
||||
SendInputEvent(PipelineId, ConstellationInputEvent),
|
||||
/// Notifies script of the viewport.
|
||||
Viewport(PipelineId, Rect<f32, UnknownUnit>),
|
||||
/// Requests that the script thread immediately send the constellation the title of a pipeline.
|
||||
|
@ -422,7 +420,7 @@ impl fmt::Debug for ScriptThreadMessage {
|
|||
UnloadDocument(..) => "UnloadDocument",
|
||||
ExitPipeline(..) => "ExitPipeline",
|
||||
ExitScriptThread => "ExitScriptThread",
|
||||
SendEvent(..) => "SendEvent",
|
||||
SendInputEvent(..) => "SendInputEvent",
|
||||
Viewport(..) => "Viewport",
|
||||
GetTitle(..) => "GetTitle",
|
||||
SetDocumentActivity(..) => "SetDocumentActivity",
|
||||
|
@ -476,64 +474,16 @@ pub enum AnimationState {
|
|||
NoAnimationCallbacksPresent,
|
||||
}
|
||||
|
||||
/// Events from the compositor that the script thread needs to know about
|
||||
/// Input events from the embedder that are sent via the `Constellation`` to the `ScriptThread`.
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub enum CompositorEvent {
|
||||
/// The window was resized.
|
||||
ResizeEvent(WindowSizeData, WindowSizeType),
|
||||
/// A mouse button state changed.
|
||||
MouseButtonEvent(
|
||||
MouseEventType,
|
||||
MouseButton,
|
||||
Point2D<f32>,
|
||||
Option<UntrustedNodeAddress>,
|
||||
Option<Point2D<f32>>,
|
||||
// Bitmask of MouseButton values representing the currently pressed buttons
|
||||
u16,
|
||||
),
|
||||
/// The mouse was moved over a point (or was moved out of the recognizable region).
|
||||
MouseMoveEvent(
|
||||
Point2D<f32>,
|
||||
Option<UntrustedNodeAddress>,
|
||||
// Bitmask of MouseButton values representing the currently pressed buttons
|
||||
u16,
|
||||
),
|
||||
/// A touch event was generated with a touch ID and location.
|
||||
TouchEvent(
|
||||
TouchEventType,
|
||||
TouchId,
|
||||
Point2D<f32>,
|
||||
Option<UntrustedNodeAddress>,
|
||||
),
|
||||
/// A wheel event was generated with a delta in the X, Y, and/or Z directions
|
||||
WheelEvent(WheelDelta, Point2D<f32>, Option<UntrustedNodeAddress>),
|
||||
/// A key was pressed.
|
||||
KeyboardEvent(KeyboardEvent),
|
||||
/// An event from the IME is dispatched.
|
||||
CompositionEvent(CompositionEvent),
|
||||
/// Virtual keyboard was dismissed
|
||||
IMEDismissedEvent,
|
||||
/// Connected gamepad state updated
|
||||
GamepadEvent(GamepadEvent),
|
||||
/// A clipboard action was requested
|
||||
ClipboardEvent(ClipboardEventType),
|
||||
}
|
||||
|
||||
impl From<&CompositorEvent> for CompositorEventVariant {
|
||||
fn from(value: &CompositorEvent) -> Self {
|
||||
match value {
|
||||
CompositorEvent::ResizeEvent(..) => CompositorEventVariant::ResizeEvent,
|
||||
CompositorEvent::MouseButtonEvent(..) => CompositorEventVariant::MouseButtonEvent,
|
||||
CompositorEvent::MouseMoveEvent(..) => CompositorEventVariant::MouseMoveEvent,
|
||||
CompositorEvent::TouchEvent(..) => CompositorEventVariant::TouchEvent,
|
||||
CompositorEvent::WheelEvent(..) => CompositorEventVariant::WheelEvent,
|
||||
CompositorEvent::KeyboardEvent(..) => CompositorEventVariant::KeyboardEvent,
|
||||
CompositorEvent::CompositionEvent(..) => CompositorEventVariant::CompositionEvent,
|
||||
CompositorEvent::IMEDismissedEvent => CompositorEventVariant::IMEDismissedEvent,
|
||||
CompositorEvent::GamepadEvent(..) => CompositorEventVariant::GamepadEvent,
|
||||
CompositorEvent::ClipboardEvent(..) => CompositorEventVariant::ClipboardEvent,
|
||||
}
|
||||
}
|
||||
pub struct ConstellationInputEvent {
|
||||
/// The hit test result of this input event, if any.
|
||||
pub hit_test_result: Option<CompositorHitTestResult>,
|
||||
/// The pressed mouse button state of the constellation when this input
|
||||
/// event was triggered.
|
||||
pub pressed_mouse_buttons: u16,
|
||||
/// The [`InputEvent`] itself.
|
||||
pub event: InputEvent,
|
||||
}
|
||||
|
||||
/// Data needed to construct a script thread.
|
||||
|
@ -717,7 +667,7 @@ pub enum WebDriverCommandMsg {
|
|||
/// Act as if keys were pressed or release in the browsing context with the given ID.
|
||||
KeyboardAction(BrowsingContextId, KeyboardEvent),
|
||||
/// Act as if the mouse was clicked in the browsing context with the given ID.
|
||||
MouseButtonAction(MouseEventType, MouseButton, f32, f32),
|
||||
MouseButtonAction(MouseButtonAction, MouseButton, f32, f32),
|
||||
/// Act as if the mouse was moved in the browsing context with the given ID.
|
||||
MouseMoveAction(f32, f32),
|
||||
/// Set the window size.
|
||||
|
|
|
@ -7,7 +7,7 @@ use std::time::{Duration, Instant};
|
|||
use std::{cmp, thread};
|
||||
|
||||
use compositing_traits::ConstellationMsg;
|
||||
use embedder_traits::{MouseButton, MouseEventType};
|
||||
use embedder_traits::MouseButtonAction;
|
||||
use ipc_channel::ipc;
|
||||
use keyboard_types::webdriver::KeyInputState;
|
||||
use script_traits::webdriver_msg::WebDriverScriptCommand;
|
||||
|
@ -83,18 +83,6 @@ fn compute_tick_duration(tick_actions: &ActionSequence) -> u64 {
|
|||
duration
|
||||
}
|
||||
|
||||
fn u64_to_mouse_button(button: u64) -> Option<MouseButton> {
|
||||
if embedder_traits::MouseButton::Left as u64 == button {
|
||||
Some(MouseButton::Left)
|
||||
} else if MouseButton::Middle as u64 == button {
|
||||
Some(MouseButton::Middle)
|
||||
} else if MouseButton::Right as u64 == button {
|
||||
Some(MouseButton::Right)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl Handler {
|
||||
// https://w3c.github.io/webdriver/#dfn-dispatch-actions
|
||||
pub(crate) fn dispatch_actions(
|
||||
|
@ -291,17 +279,16 @@ impl Handler {
|
|||
},
|
||||
});
|
||||
|
||||
if let Some(button) = u64_to_mouse_button(action.button) {
|
||||
let cmd_msg = WebDriverCommandMsg::MouseButtonAction(
|
||||
MouseEventType::MouseDown,
|
||||
button,
|
||||
pointer_input_state.x as f32,
|
||||
pointer_input_state.y as f32,
|
||||
);
|
||||
self.constellation_chan
|
||||
.send(ConstellationMsg::WebDriverCommand(cmd_msg))
|
||||
.unwrap();
|
||||
}
|
||||
let button = (action.button as u16).into();
|
||||
let cmd_msg = WebDriverCommandMsg::MouseButtonAction(
|
||||
MouseButtonAction::Down,
|
||||
button,
|
||||
pointer_input_state.x as f32,
|
||||
pointer_input_state.y as f32,
|
||||
);
|
||||
self.constellation_chan
|
||||
.send(ConstellationMsg::WebDriverCommand(cmd_msg))
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
// https://w3c.github.io/webdriver/#dfn-dispatch-a-pointerup-action
|
||||
|
@ -338,17 +325,16 @@ impl Handler {
|
|||
},
|
||||
});
|
||||
|
||||
if let Some(button) = u64_to_mouse_button(action.button) {
|
||||
let cmd_msg = WebDriverCommandMsg::MouseButtonAction(
|
||||
MouseEventType::MouseUp,
|
||||
button,
|
||||
pointer_input_state.x as f32,
|
||||
pointer_input_state.y as f32,
|
||||
);
|
||||
self.constellation_chan
|
||||
.send(ConstellationMsg::WebDriverCommand(cmd_msg))
|
||||
.unwrap();
|
||||
}
|
||||
let button = (action.button as u16).into();
|
||||
let cmd_msg = WebDriverCommandMsg::MouseButtonAction(
|
||||
MouseButtonAction::Up,
|
||||
button,
|
||||
pointer_input_state.x as f32,
|
||||
pointer_input_state.y as f32,
|
||||
);
|
||||
self.constellation_chan
|
||||
.send(ConstellationMsg::WebDriverCommand(cmd_msg))
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
// https://w3c.github.io/webdriver/#dfn-dispatch-a-pointermove-action
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue