webdriver: Move NewWebView, FocusWebView, GetWindowSize, and SetWindowSize to servoshell (#37555)

Follow up to: https://github.com/servo/servo/pull/36714
Moving webdriver [context
commands](https://www.w3.org/TR/webdriver2/#contexts) to be handled in
embedder:

- [x] New Window command - `WebdriverCommandMsg::NewWebView`
- [x]  Switch To Window command - `WebdriverCommandMsg::FocusWebView`
- [x] Resizing commands

cc: @xiaochengh

---------

Signed-off-by: batu_hoang <longvatrong111@gmail.com>
This commit is contained in:
batu_hoang 2025-06-25 18:29:34 +08:00 committed by GitHub
parent 5ea003752a
commit d970584332
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 148 additions and 97 deletions

View file

@ -182,7 +182,7 @@ impl App {
self.servoshell_preferences.clone(),
webdriver_receiver,
));
running_state.new_toplevel_webview(self.initial_url.clone().into_url());
running_state.create_and_focus_toplevel_webview(self.initial_url.clone().into_url());
if let Some(ref mut minibrowser) = self.minibrowser {
minibrowser.update(window.winit_window().unwrap(), &running_state, "init");
@ -310,7 +310,7 @@ impl App {
},
MinibrowserEvent::NewWebView => {
minibrowser.update_location_dirty(false);
state.new_toplevel_webview(Url::parse("servo:newtab").unwrap());
state.create_and_focus_toplevel_webview(Url::parse("servo:newtab").unwrap());
},
MinibrowserEvent::CloseWebView(id) => {
minibrowser.update_location_dirty(false);
@ -333,16 +333,69 @@ impl App {
match msg {
WebDriverCommandMsg::IsWebViewOpen(webview_id, sender) => {
let context = running_state.webview_by_id(webview_id);
sender.send(context.is_some()).unwrap();
if let Err(error) = sender.send(context.is_some()) {
warn!("Failed to send response of IsWebViewOpein: {error}");
}
},
webdriver_msg @ WebDriverCommandMsg::IsBrowsingContextOpen(..) => {
running_state.forward_webdriver_command(webdriver_msg);
},
WebDriverCommandMsg::NewWebView(response_sender, load_status_sender) => {
let new_webview =
running_state.create_toplevel_webview(Url::parse("auto:blank").unwrap());
if let Err(error) = response_sender.send(new_webview.id()) {
warn!("Failed to send response of NewWebview: {error}");
}
running_state.set_load_status_sender(new_webview.id(), load_status_sender);
},
WebDriverCommandMsg::CloseWebView(webview_id) => {
running_state.close_webview(webview_id);
},
WebDriverCommandMsg::GetWindowSize(..) |
WebDriverCommandMsg::FocusWebView(..) |
WebDriverCommandMsg::FocusWebView(webview_id) => {
if let Some(webview) = running_state.webview_by_id(webview_id) {
webview.focus();
}
// TODO: send a response to the WebDriver
// so it knows when the focus has finished.
},
WebDriverCommandMsg::GetWindowSize(_webview_id, response_sender) => {
let window = self
.windows
.values()
.next()
.expect("Should have at least one window in servoshell");
if let Err(error) = response_sender.send(window.screen_geometry().size) {
warn!("Failed to send response of GetWindowSize: {error}");
}
},
WebDriverCommandMsg::SetWindowSize(webview_id, size, size_sender) => {
let Some(webview) = running_state.webview_by_id(webview_id) else {
continue;
};
let window = self
.windows
.values()
.next()
.expect("Should have at least one window in servoshell");
let size = window.request_resize(&webview, size);
if let Err(error) = size_sender.send(size.unwrap_or_default()) {
warn!("Failed to send window size: {error}");
}
},
WebDriverCommandMsg::GetFocusedWebView(sender) => {
let focused_webview = running_state.focused_webview();
if let Err(error) = sender.send(focused_webview.map(|w| w.id())) {
warn!("Failed to send response of GetFocusedWebView: {error}");
};
},
WebDriverCommandMsg::GetViewportSize(..) |
WebDriverCommandMsg::LoadUrl(..) |
WebDriverCommandMsg::ScriptCommand(..) |
WebDriverCommandMsg::SendKeys(..) |
@ -350,9 +403,7 @@ impl App {
WebDriverCommandMsg::MouseButtonAction(..) |
WebDriverCommandMsg::MouseMoveAction(..) |
WebDriverCommandMsg::WheelScrollAction(..) |
WebDriverCommandMsg::SetWindowSize(..) |
WebDriverCommandMsg::TakeScreenshot(..) |
WebDriverCommandMsg::NewWebView(..) |
WebDriverCommandMsg::Refresh(..) => {
warn!(
"WebDriverCommand {:?} is still not moved from constellation to embedder",

View file

@ -19,7 +19,7 @@ use servo::webrender_api::units::{DeviceIntPoint, DeviceIntSize};
use servo::{
AllowOrDenyRequest, AuthenticationRequest, FilterPattern, FormControl, GamepadHapticEffectType,
KeyboardEvent, LoadStatus, PermissionRequest, Servo, ServoDelegate, ServoError, SimpleDialog,
WebDriverCommandMsg, WebView, WebViewBuilder, WebViewDelegate,
WebDriverCommandMsg, WebDriverLoadStatus, WebView, WebViewBuilder, WebViewDelegate,
};
use url::Url;
@ -37,6 +37,13 @@ pub(crate) enum AppState {
ShuttingDown,
}
/// A collection of [`IpcSender`]s that are used to asynchronously communicate
/// to a WebDriver server with information about application state.
#[derive(Clone, Default)]
struct WebDriverSenders {
pub load_status_senders: HashMap<WebViewId, IpcSender<WebDriverLoadStatus>>,
}
pub(crate) struct RunningAppState {
/// A handle to the Servo instance of the [`RunningAppState`]. This is not stored inside
/// `inner` so that we can keep a reference to Servo in order to spin the event loop,
@ -48,6 +55,7 @@ pub(crate) struct RunningAppState {
/// A [`Receiver`] for receiving commands from a running WebDriver server, if WebDriver
/// was enabled.
webdriver_receiver: Option<Receiver<WebDriverCommandMsg>>,
webdriver_senders: RefCell<WebDriverSenders>,
inner: RefCell<RunningAppStateInner>,
}
@ -99,6 +107,7 @@ impl RunningAppState {
servo,
servoshell_preferences,
webdriver_receiver,
webdriver_senders: RefCell::default(),
inner: RefCell::new(RunningAppStateInner {
webviews: HashMap::default(),
creation_order: Default::default(),
@ -112,7 +121,13 @@ impl RunningAppState {
}
}
pub(crate) fn new_toplevel_webview(self: &Rc<Self>, url: Url) {
pub(crate) fn create_and_focus_toplevel_webview(self: &Rc<Self>, url: Url) {
let webview = self.create_toplevel_webview(url);
webview.focus();
webview.raise_to_top(true);
}
pub(crate) fn create_toplevel_webview(self: &Rc<Self>, url: Url) -> WebView {
let webview = WebViewBuilder::new(self.servo())
.url(url)
.hidpi_scale_factor(self.inner().window.hidpi_scale_factor())
@ -120,10 +135,8 @@ impl RunningAppState {
.build();
webview.notify_theme_change(self.inner().window.theme());
webview.focus();
webview.raise_to_top(true);
self.add(webview);
self.add(webview.clone());
webview
}
pub(crate) fn inner(&self) -> Ref<RunningAppStateInner> {
@ -382,6 +395,17 @@ impl RunningAppState {
webview.notify_scroll_event(location, origin);
});
}
pub(crate) fn set_load_status_sender(
&self,
webview_id: WebViewId,
sender: IpcSender<WebDriverLoadStatus>,
) {
self.webdriver_senders
.borrow_mut()
.load_status_senders
.insert(webview_id, sender);
}
}
struct ServoShellServoDelegate;
@ -507,8 +531,19 @@ impl WebViewDelegate for RunningAppState {
self.inner().window.set_cursor(cursor);
}
fn notify_load_status_changed(&self, _webview: servo::WebView, _status: LoadStatus) {
fn notify_load_status_changed(&self, webview: servo::WebView, status: LoadStatus) {
self.inner_mut().need_update = true;
if status == LoadStatus::Complete {
if let Some(sender) = self
.webdriver_senders
.borrow_mut()
.load_status_senders
.remove(&webview.id())
{
let _ = sender.send(WebDriverLoadStatus::Complete);
}
}
}
fn notify_fullscreen_state_changed(&self, _webview: servo::WebView, fullscreen_state: bool) {

View file

@ -394,7 +394,7 @@ impl Window {
}
})
.shortcut(CMD_OR_CONTROL, 'T', || {
state.new_toplevel_webview(Url::parse("servo:newtab").unwrap());
state.create_and_focus_toplevel_webview(Url::parse("servo:newtab").unwrap());
})
.shortcut(CMD_OR_CONTROL, 'Q', || state.servo().start_shutting_down())
.otherwise(|| handled = false);

View file

@ -336,11 +336,11 @@ impl RunningAppState {
}),
});
app_state.new_toplevel_webview(initial_url);
app_state.create_and_focus_toplevel_webview(initial_url);
app_state
}
pub(crate) fn new_toplevel_webview(self: &Rc<Self>, url: Url) {
pub(crate) fn create_and_focus_toplevel_webview(self: &Rc<Self>, url: Url) {
let webview = WebViewBuilder::new(&self.servo)
.url(url)
.hidpi_scale_factor(self.inner().hidpi_scale_factor)

View file

@ -228,7 +228,7 @@ impl ServoAction {
},
NewWebview(xcomponent, window) => {
servo.pause_compositor();
servo.new_toplevel_webview("about:blank".parse().unwrap());
servo.create_and_focus_toplevel_webview("about:blank".parse().unwrap());
let (window_handle, _, coordinates) =
simpleservo::get_raw_window_handle(xcomponent.0, window.0);