Move webdriver actions commands to servoshell (#37669)

Move webdriver actions commands to servoshell.

Testing: Test with webdriver enable in the CI
[Test result
1](https://github.com/longvatrong111/servo/actions/runs/15875355256)
[Test result
2](https://github.com/longvatrong111/servo/actions/runs/15875356595)
[Test result
3](https://github.com/longvatrong111/servo/actions/runs/15875361886)

Fixes: https://github.com/servo/servo/issues/37370

Signed-off-by: batu_hoang <longvatrong111@gmail.com>
This commit is contained in:
batu_hoang 2025-07-05 00:29:38 +08:00 committed by GitHub
parent 9bd8d4f026
commit 940eff9497
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 126 additions and 208 deletions

View file

@ -27,10 +27,10 @@ use constellation_traits::{EmbedderToConstellationMessage, PaintMetricEvent};
use crossbeam_channel::{Receiver, Sender}; use crossbeam_channel::{Receiver, Sender};
use dpi::PhysicalSize; use dpi::PhysicalSize;
use embedder_traits::{ use embedder_traits::{
CompositorHitTestResult, Cursor, InputEvent, MouseButtonEvent, MouseMoveEvent, ShutdownState, CompositorHitTestResult, Cursor, InputEvent, ShutdownState, UntrustedNodeAddress,
UntrustedNodeAddress, ViewportDetails, WheelDelta, WheelEvent, WheelMode, ViewportDetails,
}; };
use euclid::{Point2D, Rect, Scale, Size2D, Transform3D, Vector2D}; use euclid::{Point2D, Rect, Scale, Size2D, Transform3D};
use ipc_channel::ipc::{self, IpcSharedMemory}; use ipc_channel::ipc::{self, IpcSharedMemory};
use libc::c_void; use libc::c_void;
use log::{debug, info, trace, warn}; use log::{debug, info, trace, warn};
@ -672,64 +672,6 @@ impl IOCompositor {
} }
}, },
CompositorMsg::WebDriverMouseButtonEvent(
webview_id,
action,
button,
x,
y,
message_id,
) => {
let Some(webview_renderer) = self.webview_renderers.get_mut(webview_id) else {
warn!("Handling input event for unknown webview: {webview_id}");
return;
};
let dppx = webview_renderer.device_pixels_per_page_pixel();
let point = dppx.transform_point(Point2D::new(x, y));
webview_renderer.dispatch_point_input_event(
InputEvent::MouseButton(MouseButtonEvent::new(action, button, point))
.with_webdriver_message_id(message_id),
);
},
CompositorMsg::WebDriverMouseMoveEvent(webview_id, x, y, message_id) => {
let Some(webview_renderer) = self.webview_renderers.get_mut(webview_id) else {
warn!("Handling input event for unknown webview: {webview_id}");
return;
};
let dppx = webview_renderer.device_pixels_per_page_pixel();
let point = dppx.transform_point(Point2D::new(x, y));
webview_renderer.dispatch_point_input_event(
InputEvent::MouseMove(MouseMoveEvent::new(point))
.with_webdriver_message_id(message_id),
);
},
CompositorMsg::WebDriverWheelScrollEvent(webview_id, x, y, dx, dy, message_id) => {
let Some(webview_renderer) = self.webview_renderers.get_mut(webview_id) else {
warn!("Handling input event for unknown webview: {webview_id}");
return;
};
// The sign of wheel delta value definition in uievent
// is inverted compared to `winit`s wheel delta. Hence,
// here we invert the sign to mimic wheel scroll
// implementation in `headed_window.rs`.
let delta = WheelDelta {
x: -dx,
y: -dy,
z: 0.0,
mode: WheelMode::DeltaPixel,
};
let dppx = webview_renderer.device_pixels_per_page_pixel();
let point = dppx.transform_point(Point2D::new(x, y));
let scroll_delta = dppx.transform_vector(Vector2D::new(dx as f32, dy as f32));
webview_renderer.dispatch_point_input_event(
InputEvent::Wheel(WheelEvent::new(delta, point))
.with_webdriver_message_id(message_id),
);
webview_renderer.on_webdriver_wheel_action(scroll_delta, point);
},
CompositorMsg::SendInitialTransaction(pipeline) => { CompositorMsg::SendInitialTransaction(pipeline) => {
let mut txn = Transaction::new(); let mut txn = Transaction::new();
txn.set_display_list(WebRenderEpoch(0), (pipeline, Default::default())); txn.set_display_list(WebRenderEpoch(0), (pipeline, Default::default()));

View file

@ -40,9 +40,6 @@ mod from_constellation {
Self::NewWebRenderFrameReady(..) => target!("NewWebRenderFrameReady"), Self::NewWebRenderFrameReady(..) => target!("NewWebRenderFrameReady"),
Self::PipelineExited(..) => target!("PipelineExited"), Self::PipelineExited(..) => target!("PipelineExited"),
Self::LoadComplete(..) => target!("LoadComplete"), Self::LoadComplete(..) => target!("LoadComplete"),
Self::WebDriverMouseButtonEvent(..) => target!("WebDriverMouseButtonEvent"),
Self::WebDriverMouseMoveEvent(..) => target!("WebDriverMouseMoveEvent"),
Self::WebDriverWheelScrollEvent(..) => target!("WebDriverWheelScrollEvent"),
Self::SendInitialTransaction(..) => target!("SendInitialTransaction"), Self::SendInitialTransaction(..) => target!("SendInitialTransaction"),
Self::SendScrollNode(..) => target!("SendScrollNode"), Self::SendScrollNode(..) => target!("SendScrollNode"),
Self::SendDisplayList { .. } => target!("SendDisplayList"), Self::SendDisplayList { .. } => target!("SendDisplayList"),

View file

@ -792,22 +792,6 @@ impl WebViewRenderer {
})); }));
} }
/// Push scroll pending event when receiving wheel action from webdriver
pub(crate) fn on_webdriver_wheel_action(
&mut self,
scroll_delta: Vector2D<f32, DevicePixel>,
point: Point2D<f32, DevicePixel>,
) {
if self.global.borrow().shutdown_state() != ShutdownState::NotShuttingDown {
return;
}
let scroll_location =
ScrollLocation::Delta(LayoutVector2D::from_untyped(scroll_delta.to_untyped()));
let cursor = DeviceIntPoint::new(point.x as i32, point.y as i32);
self.on_scroll_window_event(scroll_location, cursor)
}
/// Process pending scroll events for this [`WebViewRenderer`]. Returns a tuple containing: /// Process pending scroll events for this [`WebViewRenderer`]. Returns a tuple containing:
/// ///
/// - A boolean that is true if a zoom occurred. /// - A boolean that is true if a zoom occurred.

View file

@ -1468,6 +1468,9 @@ where
) )
} }
}, },
EmbedderToConstellationMessage::SetWebDriverResponseSender(sender) => {
self.webdriver.input_command_response_sender = Some(sender);
},
} }
} }
@ -1850,7 +1853,6 @@ where
.send(WebDriverCommandResponse { id: msg_id }) .send(WebDriverCommandResponse { id: msg_id })
.unwrap_or_else(|_| { .unwrap_or_else(|_| {
warn!("Failed to send WebDriverInputComplete {:?}", msg_id); warn!("Failed to send WebDriverInputComplete {:?}", msg_id);
self.webdriver.input_command_response_sender = None;
}); });
} else { } else {
warn!("No WebDriver input_command_response_sender"); warn!("No WebDriver input_command_response_sender");
@ -4616,14 +4618,7 @@ where
} }
} }
}, },
WebDriverCommandMsg::KeyboardAction( WebDriverCommandMsg::KeyboardAction(browsing_context_id, key_event, msg_id) => {
browsing_context_id,
key_event,
msg_id,
response_sender,
) => {
self.webdriver.input_command_response_sender = Some(response_sender);
let pipeline_id = match self.browsing_contexts.get(&browsing_context_id) { let pipeline_id = match self.browsing_contexts.get(&browsing_context_id) {
Some(browsing_context) => browsing_context.pipeline_id, Some(browsing_context) => browsing_context.pipeline_id,
None => { None => {
@ -4649,50 +4644,14 @@ where
self.handle_send_error(pipeline_id, e) self.handle_send_error(pipeline_id, e)
} }
}, },
WebDriverCommandMsg::MouseButtonAction( WebDriverCommandMsg::MouseButtonAction(..) => {
webview_id, unreachable!("This command should be send directly to the embedder.");
mouse_event_type,
mouse_button,
x,
y,
msg_id,
response_sender,
) => {
self.webdriver.input_command_response_sender = Some(response_sender);
self.compositor_proxy
.send(CompositorMsg::WebDriverMouseButtonEvent(
webview_id,
mouse_event_type,
mouse_button,
x,
y,
msg_id,
));
}, },
WebDriverCommandMsg::MouseMoveAction(webview_id, x, y, msg_id, response_sender) => { WebDriverCommandMsg::MouseMoveAction(..) => {
self.webdriver.input_command_response_sender = Some(response_sender); unreachable!("This command should be send directly to the embedder.");
self.compositor_proxy
.send(CompositorMsg::WebDriverMouseMoveEvent(
webview_id, x, y, msg_id,
));
}, },
WebDriverCommandMsg::WheelScrollAction( WebDriverCommandMsg::WheelScrollAction(..) => {
webview_id, unreachable!("This command should be send directly to the embedder.");
x,
y,
delta_x,
delta_y,
msg_id,
response_sender,
) => {
self.webdriver.input_command_response_sender = Some(response_sender);
self.compositor_proxy
.send(CompositorMsg::WebDriverWheelScrollEvent(
webview_id, x, y, delta_x, delta_y, msg_id,
));
}, },
WebDriverCommandMsg::TakeScreenshot(webview_id, rect, response_sender) => { WebDriverCommandMsg::TakeScreenshot(webview_id, rect, response_sender) => {
self.compositor_proxy.send(CompositorMsg::CreatePng( self.compositor_proxy.send(CompositorMsg::CreatePng(

View file

@ -77,6 +77,7 @@ mod from_compositor {
Self::EvaluateJavaScript(..) => target!("EvaluateJavaScript"), Self::EvaluateJavaScript(..) => target!("EvaluateJavaScript"),
Self::CreateMemoryReport(..) => target!("CreateMemoryReport"), Self::CreateMemoryReport(..) => target!("CreateMemoryReport"),
Self::SendImageKeysForPipeline(..) => target!("SendImageKeysForPipeline"), Self::SendImageKeysForPipeline(..) => target!("SendImageKeysForPipeline"),
Self::SetWebDriverResponseSender(..) => target!("SetWebDriverResponseSender"),
} }
} }
} }

View file

@ -8,10 +8,7 @@ use std::fmt::{Debug, Error, Formatter};
use base::id::{PipelineId, WebViewId}; use base::id::{PipelineId, WebViewId};
use crossbeam_channel::Sender; use crossbeam_channel::Sender;
use embedder_traits::{ use embedder_traits::{AnimationState, EventLoopWaker, TouchEventResult};
AnimationState, EventLoopWaker, MouseButton, MouseButtonAction, TouchEventResult,
WebDriverMessageId,
};
use euclid::Rect; use euclid::Rect;
use ipc_channel::ipc::IpcSender; use ipc_channel::ipc::IpcSender;
use log::warn; use log::warn;
@ -105,20 +102,6 @@ pub enum CompositorMsg {
PipelineExited(WebViewId, PipelineId, PipelineExitSource), PipelineExited(WebViewId, PipelineId, PipelineExitSource),
/// The load of a page has completed /// The load of a page has completed
LoadComplete(WebViewId), LoadComplete(WebViewId),
/// WebDriver mouse button event
WebDriverMouseButtonEvent(
WebViewId,
MouseButtonAction,
MouseButton,
f32,
f32,
Option<WebDriverMessageId>,
),
/// WebDriver mouse move event
WebDriverMouseMoveEvent(WebViewId, f32, f32, Option<WebDriverMessageId>),
// Webdriver wheel scroll event
WebDriverWheelScrollEvent(WebViewId, f32, f32, f64, f64, Option<WebDriverMessageId>),
/// Inform WebRender of the existence of this pipeline. /// Inform WebRender of the existence of this pipeline.
SendInitialTransaction(WebRenderPipelineId), SendInitialTransaction(WebRenderPipelineId),
/// Perform a scroll operation. /// Perform a scroll operation.

View file

@ -20,7 +20,7 @@ use base::cross_process_instant::CrossProcessInstant;
use base::id::{MessagePortId, PipelineId, WebViewId}; use base::id::{MessagePortId, PipelineId, WebViewId};
use embedder_traits::{ use embedder_traits::{
CompositorHitTestResult, Cursor, InputEvent, JavaScriptEvaluationId, MediaSessionActionType, CompositorHitTestResult, Cursor, InputEvent, JavaScriptEvaluationId, MediaSessionActionType,
Theme, ViewportDetails, WebDriverCommandMsg, Theme, ViewportDetails, WebDriverCommandMsg, WebDriverCommandResponse,
}; };
pub use from_script_message::*; pub use from_script_message::*;
use ipc_channel::ipc::IpcSender; use ipc_channel::ipc::IpcSender;
@ -96,6 +96,8 @@ pub enum EmbedderToConstellationMessage {
CreateMemoryReport(IpcSender<MemoryReportResult>), CreateMemoryReport(IpcSender<MemoryReportResult>),
/// Sends the generated image key to the image cache associated with this pipeline. /// Sends the generated image key to the image cache associated with this pipeline.
SendImageKeysForPipeline(PipelineId, Vec<ImageKey>), SendImageKeysForPipeline(PipelineId, Vec<ImageKey>),
/// Set WebDriver input event handled sender.
SetWebDriverResponseSender(IpcSender<WebDriverCommandResponse>),
} }
/// A description of a paint metric that is sent from the Servo renderer to the /// A description of a paint metric that is sent from the Servo renderer to the

View file

@ -30,6 +30,9 @@ pub struct WebDriverMessageId(pub usize);
/// Messages to the constellation originating from the WebDriver server. /// Messages to the constellation originating from the WebDriver server.
#[derive(Debug, Deserialize, Serialize)] #[derive(Debug, Deserialize, Serialize)]
pub enum WebDriverCommandMsg { pub enum WebDriverCommandMsg {
/// Used in the initialization of the WebDriver server to set the sender for sending responses
/// back to the WebDriver client. It is set to constellation for now
SetWebDriverResponseSender(IpcSender<WebDriverCommandResponse>),
/// Get the window size. /// Get the window size.
GetWindowRect(WebViewId, IpcSender<DeviceIntRect>), GetWindowRect(WebViewId, IpcSender<DeviceIntRect>),
/// Get the viewport size. /// Get the viewport size.
@ -53,7 +56,6 @@ pub enum WebDriverCommandMsg {
KeyboardEvent, KeyboardEvent,
// Should never be None. // Should never be None.
Option<WebDriverMessageId>, Option<WebDriverMessageId>,
IpcSender<WebDriverCommandResponse>,
), ),
/// Act as if the mouse was clicked in the browsing context with the given ID. /// Act as if the mouse was clicked in the browsing context with the given ID.
MouseButtonAction( MouseButtonAction(
@ -64,7 +66,6 @@ pub enum WebDriverCommandMsg {
f32, f32,
// Should never be None. // Should never be None.
Option<WebDriverMessageId>, Option<WebDriverMessageId>,
IpcSender<WebDriverCommandResponse>,
), ),
/// Act as if the mouse was moved in the browsing context with the given ID. /// Act as if the mouse was moved in the browsing context with the given ID.
MouseMoveAction( MouseMoveAction(
@ -74,7 +75,6 @@ pub enum WebDriverCommandMsg {
// None if it's not the last `perform_pointer_move` since we only // None if it's not the last `perform_pointer_move` since we only
// expect one response from constellation for each tick actions. // expect one response from constellation for each tick actions.
Option<WebDriverMessageId>, Option<WebDriverMessageId>,
IpcSender<WebDriverCommandResponse>,
), ),
/// Act as if the mouse wheel is scrolled in the browsing context given the given ID. /// Act as if the mouse wheel is scrolled in the browsing context given the given ID.
WheelScrollAction( WheelScrollAction(
@ -86,7 +86,6 @@ pub enum WebDriverCommandMsg {
// None if it's not the last `perform_wheel_scroll` since we only // None if it's not the last `perform_wheel_scroll` since we only
// expect one response from constellation for each tick actions. // expect one response from constellation for each tick actions.
Option<WebDriverMessageId>, Option<WebDriverMessageId>,
IpcSender<WebDriverCommandResponse>,
), ),
/// Set the window size. /// Set the window size.
SetWindowSize( SetWindowSize(

View file

@ -7,7 +7,6 @@ use std::thread;
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
use base::id::BrowsingContextId; use base::id::BrowsingContextId;
use constellation_traits::EmbedderToConstellationMessage;
use embedder_traits::{MouseButtonAction, WebDriverCommandMsg, WebDriverScriptCommand}; use embedder_traits::{MouseButtonAction, WebDriverCommandMsg, WebDriverScriptCommand};
use ipc_channel::ipc; use ipc_channel::ipc;
use keyboard_types::webdriver::KeyInputState; use keyboard_types::webdriver::KeyInputState;
@ -178,7 +177,7 @@ impl Handler {
// //
// Wait for num_pending_actions number of responses // Wait for num_pending_actions number of responses
for _ in 0..self.num_pending_actions.get() { for _ in 0..self.num_pending_actions.get() {
match self.constellation_receiver.recv() { match self.webdriver_response_receiver.recv() {
Ok(response) => { Ok(response) => {
let current_waiting_id = self let current_waiting_id = self
.current_action_id .current_action_id
@ -316,11 +315,8 @@ impl Handler {
self.session().unwrap().browsing_context_id, self.session().unwrap().browsing_context_id,
keyboard_event, keyboard_event,
msg_id, msg_id,
self.constellation_sender.clone(),
); );
self.constellation_chan let _ = self.send_message_to_embedder(cmd_msg);
.send(EmbedderToConstellationMessage::WebDriverCommand(cmd_msg))
.unwrap();
} }
/// <https://w3c.github.io/webdriver/#dfn-dispatch-a-keyup-action> /// <https://w3c.github.io/webdriver/#dfn-dispatch-a-keyup-action>
@ -358,11 +354,8 @@ impl Handler {
self.session().unwrap().browsing_context_id, self.session().unwrap().browsing_context_id,
keyboard_event, keyboard_event,
msg_id, msg_id,
self.constellation_sender.clone(),
); );
self.constellation_chan let _ = self.send_message_to_embedder(cmd_msg);
.send(EmbedderToConstellationMessage::WebDriverCommand(cmd_msg))
.unwrap();
} }
} }
@ -390,11 +383,8 @@ impl Handler {
pointer_input_state.x as f32, pointer_input_state.x as f32,
pointer_input_state.y as f32, pointer_input_state.y as f32,
msg_id, msg_id,
self.constellation_sender.clone(),
); );
self.constellation_chan let _ = self.send_message_to_embedder(cmd_msg);
.send(EmbedderToConstellationMessage::WebDriverCommand(cmd_msg))
.unwrap();
} }
/// <https://w3c.github.io/webdriver/#dfn-dispatch-a-pointerup-action> /// <https://w3c.github.io/webdriver/#dfn-dispatch-a-pointerup-action>
@ -439,11 +429,8 @@ impl Handler {
pointer_input_state.x as f32, pointer_input_state.x as f32,
pointer_input_state.y as f32, pointer_input_state.y as f32,
msg_id, msg_id,
self.constellation_sender.clone(),
); );
self.constellation_chan let _ = self.send_message_to_embedder(cmd_msg);
.send(EmbedderToConstellationMessage::WebDriverCommand(cmd_msg))
.unwrap();
} }
/// <https://w3c.github.io/webdriver/#dfn-dispatch-a-pointermove-action> /// <https://w3c.github.io/webdriver/#dfn-dispatch-a-pointermove-action>
@ -567,11 +554,8 @@ impl Handler {
x as f32, x as f32,
y as f32, y as f32,
msg_id, msg_id,
self.constellation_sender.clone(),
); );
self.constellation_chan let _ = self.send_message_to_embedder(cmd_msg);
.send(EmbedderToConstellationMessage::WebDriverCommand(cmd_msg))
.unwrap();
// Step 7.3 // Step 7.3
pointer_input_state.x = x; pointer_input_state.x = x;
pointer_input_state.y = y; pointer_input_state.y = y;
@ -707,11 +691,8 @@ impl Handler {
delta_x as f64, delta_x as f64,
delta_y as f64, delta_y as f64,
msg_id, msg_id,
self.constellation_sender.clone(),
); );
self.constellation_chan let _ = self.send_message_to_embedder(cmd_msg);
.send(EmbedderToConstellationMessage::WebDriverCommand(cmd_msg))
.unwrap();
curr_delta_x += delta_x; curr_delta_x += delta_x;
curr_delta_y += delta_y; curr_delta_y += delta_y;

View file

@ -127,12 +127,15 @@ pub fn start_server(
constellation_chan_deprecated: Sender<EmbedderToConstellationMessage>, constellation_chan_deprecated: Sender<EmbedderToConstellationMessage>,
embedder_sender: Sender<WebDriverCommandMsg>, embedder_sender: Sender<WebDriverCommandMsg>,
event_loop_waker: Box<dyn EventLoopWaker>, event_loop_waker: Box<dyn EventLoopWaker>,
webdriver_response_receiver: IpcReceiver<WebDriverCommandResponse>,
) { ) {
let handler = Handler::new( let handler = Handler::new(
constellation_chan_deprecated, constellation_chan_deprecated,
embedder_sender, embedder_sender,
event_loop_waker, event_loop_waker,
webdriver_response_receiver,
); );
thread::Builder::new() thread::Builder::new()
.name("WebDriverHttpServer".to_owned()) .name("WebDriverHttpServer".to_owned())
.spawn(move || { .spawn(move || {
@ -242,12 +245,8 @@ struct Handler {
/// TODO: change name to constellation_sender /// TODO: change name to constellation_sender
constellation_chan: Sender<EmbedderToConstellationMessage>, constellation_chan: Sender<EmbedderToConstellationMessage>,
/// The IPC sender which we can clone and pass along to the constellation
/// TODO: change name to webdriver_response_sender
constellation_sender: IpcSender<WebDriverCommandResponse>,
/// Receiver notification from the constellation when a command is completed /// Receiver notification from the constellation when a command is completed
constellation_receiver: IpcReceiver<WebDriverCommandResponse>, webdriver_response_receiver: IpcReceiver<WebDriverCommandResponse>,
id_generator: WebDriverMessageIdGenerator, id_generator: WebDriverMessageIdGenerator,
@ -474,6 +473,7 @@ impl Handler {
constellation_chan: Sender<EmbedderToConstellationMessage>, constellation_chan: Sender<EmbedderToConstellationMessage>,
embedder_sender: Sender<WebDriverCommandMsg>, embedder_sender: Sender<WebDriverCommandMsg>,
event_loop_waker: Box<dyn EventLoopWaker>, event_loop_waker: Box<dyn EventLoopWaker>,
webdriver_response_receiver: IpcReceiver<WebDriverCommandResponse>,
) -> Handler { ) -> Handler {
// Create a pair of both an IPC and a threaded channel, // Create a pair of both an IPC and a threaded channel,
// keep the IPC sender to clone and pass to the constellation for each load, // keep the IPC sender to clone and pass to the constellation for each load,
@ -484,8 +484,6 @@ impl Handler {
let (sender, load_status_receiver) = unbounded(); let (sender, load_status_receiver) = unbounded();
ROUTER.route_ipc_receiver_to_crossbeam_sender(receiver, sender); ROUTER.route_ipc_receiver_to_crossbeam_sender(receiver, sender);
let (constellation_sender, constellation_receiver) = ipc::channel().unwrap();
Handler { Handler {
load_status_sender, load_status_sender,
load_status_receiver, load_status_receiver,
@ -493,8 +491,7 @@ impl Handler {
embedder_sender, embedder_sender,
event_loop_waker, event_loop_waker,
constellation_chan, constellation_chan,
constellation_sender, webdriver_response_receiver,
constellation_receiver,
id_generator: WebDriverMessageIdGenerator::new(), id_generator: WebDriverMessageIdGenerator::new(),
current_action_id: Cell::new(None), current_action_id: Cell::new(None),
num_pending_actions: Cell::new(0), num_pending_actions: Cell::new(0),
@ -1836,9 +1833,7 @@ impl Handler {
sender, sender,
); );
let cmd_msg = WebDriverCommandMsg::ScriptCommand(browsing_context_id, cmd); let cmd_msg = WebDriverCommandMsg::ScriptCommand(browsing_context_id, cmd);
self.constellation_chan self.send_message_to_embedder(cmd_msg)?;
.send(EmbedderToConstellationMessage::WebDriverCommand(cmd_msg))
.unwrap();
// TODO: distinguish the not found and not focusable cases // TODO: distinguish the not found and not focusable cases
// File input and non-typeable form control should have // File input and non-typeable form control should have
@ -1853,9 +1848,7 @@ impl Handler {
// send keys command being two separate messages, // send keys command being two separate messages,
// so the constellation may have changed state between them. // so the constellation may have changed state between them.
let cmd_msg = WebDriverCommandMsg::SendKeys(browsing_context_id, input_events); let cmd_msg = WebDriverCommandMsg::SendKeys(browsing_context_id, input_events);
self.constellation_chan self.send_message_to_embedder(cmd_msg)?;
.send(EmbedderToConstellationMessage::WebDriverCommand(cmd_msg))
.unwrap();
Ok(WebDriverResponse::Void) Ok(WebDriverResponse::Void)
} }

View file

@ -62,6 +62,7 @@ euclid = { workspace = true }
getopts = { workspace = true } getopts = { workspace = true }
hitrace = { workspace = true, optional = true } hitrace = { workspace = true, optional = true }
image = { workspace = true } image = { workspace = true }
ipc-channel = { workspace = true }
keyboard-types = { workspace = true } keyboard-types = { workspace = true }
libc = { workspace = true } libc = { workspace = true }
libservo = { path = "../../components/servo", features = ["background_hang_monitor", "bluetooth", "testbinding"] } libservo = { path = "../../components/servo", features = ["background_hang_monitor", "bluetooth", "testbinding"] }
@ -78,7 +79,6 @@ webdriver_server = { path = "../../components/webdriver_server" }
[target.'cfg(target_os = "android")'.dependencies] [target.'cfg(target_os = "android")'.dependencies]
android_logger = "0.15" android_logger = "0.15"
ipc-channel = { workspace = true }
jni = "0.21.1" jni = "0.21.1"
[target.'cfg(not(target_os = "android"))'.dependencies] [target.'cfg(not(target_os = "android"))'.dependencies]

View file

@ -12,14 +12,21 @@ use std::time::Instant;
use std::{env, fs}; use std::{env, fs};
use ::servo::ServoBuilder; use ::servo::ServoBuilder;
use constellation_traits::EmbedderToConstellationMessage;
use crossbeam_channel::unbounded; use crossbeam_channel::unbounded;
use euclid::{Point2D, Vector2D};
use ipc_channel::ipc;
use log::{info, trace, warn}; use log::{info, trace, warn};
use net::protocols::ProtocolRegistry; use net::protocols::ProtocolRegistry;
use servo::config::opts::Opts; use servo::config::opts::Opts;
use servo::config::prefs::Preferences; use servo::config::prefs::Preferences;
use servo::servo_url::ServoUrl; use servo::servo_url::ServoUrl;
use servo::user_content_manager::{UserContentManager, UserScript}; use servo::user_content_manager::{UserContentManager, UserScript};
use servo::{EventLoopWaker, WebDriverCommandMsg}; use servo::webrender_api::ScrollLocation;
use servo::{
EventLoopWaker, InputEvent, MouseButtonEvent, MouseMoveEvent, WebDriverCommandMsg, WheelDelta,
WheelEvent, WheelMode,
};
use url::Url; use url::Url;
use winit::application::ApplicationHandler; use winit::application::ApplicationHandler;
use winit::event::WindowEvent; use winit::event::WindowEvent;
@ -162,15 +169,25 @@ impl App {
// Initialize WebDriver server here before `servo` is moved. // Initialize WebDriver server here before `servo` is moved.
let webdriver_receiver = self.servoshell_preferences.webdriver_port.map(|port| { let webdriver_receiver = self.servoshell_preferences.webdriver_port.map(|port| {
let (embedder_sender, embedder_receiver) = unbounded(); let (embedder_sender, embedder_receiver) = unbounded();
let (webdriver_response_sender, webdriver_response_receiver) = ipc::channel().unwrap();
// Set the WebDriver response sender to constellation.
// TODO: consider using Servo API to notify embedder about input events completions
servo
.constellation_sender()
.send(EmbedderToConstellationMessage::SetWebDriverResponseSender(
webdriver_response_sender,
))
.unwrap_or_else(|_| {
warn!("Failed to set WebDriver response sender in constellation");
});
// TODO: WebDriver will no longer need this channel once all WebDriver
// commands are executed via the Servo API.
let constellation_sender_deprecated = servo.constellation_sender();
webdriver_server::start_server( webdriver_server::start_server(
port, port,
constellation_sender_deprecated, servo.constellation_sender(),
embedder_sender, embedder_sender,
self.waker.clone(), self.waker.clone(),
webdriver_response_receiver,
); );
embedder_receiver embedder_receiver
@ -331,6 +348,9 @@ impl App {
while let Ok(msg) = webdriver_receiver.try_recv() { while let Ok(msg) = webdriver_receiver.try_recv() {
match msg { match msg {
WebDriverCommandMsg::SetWebDriverResponseSender(..) => {
running_state.forward_webdriver_command(msg);
},
WebDriverCommandMsg::IsWebViewOpen(webview_id, sender) => { WebDriverCommandMsg::IsWebViewOpen(webview_id, sender) => {
let context = running_state.webview_by_id(webview_id); let context = running_state.webview_by_id(webview_id);
@ -338,8 +358,8 @@ impl App {
warn!("Failed to send response of IsWebViewOpein: {error}"); warn!("Failed to send response of IsWebViewOpein: {error}");
} }
}, },
webdriver_msg @ WebDriverCommandMsg::IsBrowsingContextOpen(..) => { WebDriverCommandMsg::IsBrowsingContextOpen(..) => {
running_state.forward_webdriver_command(webdriver_msg); running_state.forward_webdriver_command(msg);
}, },
WebDriverCommandMsg::NewWebView(response_sender, load_status_sender) => { WebDriverCommandMsg::NewWebView(response_sender, load_status_sender) => {
let new_webview = let new_webview =
@ -434,11 +454,68 @@ impl App {
webview.go_forward(1); webview.go_forward(1);
} }
}, },
WebDriverCommandMsg::SendKeys(..) | // Key events don't need hit test so can be forwarded to constellation for now
WebDriverCommandMsg::KeyboardAction(..) | WebDriverCommandMsg::SendKeys(..) => {
WebDriverCommandMsg::MouseButtonAction(..) | running_state.forward_webdriver_command(msg);
WebDriverCommandMsg::MouseMoveAction(..) | },
WebDriverCommandMsg::WheelScrollAction(..) | WebDriverCommandMsg::KeyboardAction(..) => {
running_state.forward_webdriver_command(msg);
},
WebDriverCommandMsg::MouseButtonAction(
webview_id,
mouse_event_type,
mouse_button,
x,
y,
webdriver_message_id,
) => {
if let Some(webview) = running_state.webview_by_id(webview_id) {
webview.notify_input_event(
InputEvent::MouseButton(MouseButtonEvent::new(
mouse_event_type,
mouse_button,
Point2D::new(x, y),
))
.with_webdriver_message_id(webdriver_message_id),
);
}
},
WebDriverCommandMsg::MouseMoveAction(webview_id, x, y, webdriver_message_id) => {
if let Some(webview) = running_state.webview_by_id(webview_id) {
webview.notify_input_event(
InputEvent::MouseMove(MouseMoveEvent::new(Point2D::new(x, y)))
.with_webdriver_message_id(webdriver_message_id),
);
}
},
WebDriverCommandMsg::WheelScrollAction(
webview_id,
x,
y,
dx,
dy,
webdriver_message_id,
) => {
if let Some(webview) = running_state.webview_by_id(webview_id) {
let delta = WheelDelta {
x: -dx,
y: -dy,
z: 0.0,
mode: WheelMode::DeltaPixel,
};
let point = Point2D::new(x, y);
let scroll_location =
ScrollLocation::Delta(Vector2D::new(dx as f32, dy as f32));
webview.notify_input_event(
InputEvent::Wheel(WheelEvent::new(delta, point))
.with_webdriver_message_id(webdriver_message_id),
);
webview.notify_scroll_event(scroll_location, point.to_i32());
}
},
WebDriverCommandMsg::ScriptCommand(..) | WebDriverCommandMsg::ScriptCommand(..) |
WebDriverCommandMsg::TakeScreenshot(..) => { WebDriverCommandMsg::TakeScreenshot(..) => {
warn!( warn!(