servoshell: Consider window decorations when handling resize requests from web content (#38174)

Also fix some docs. This is used by JS `resizeTo`, `resizeBy` and
webdriver [set window
rect](https://w3c.github.io/webdriver/#set-window-rect).

Testing: Can now pass more tests for headed window.
Fixes: Well.. Originally the attempt is to address
https://github.com/servo/servo/issues/38093#issuecomment-3092284104. But
it turns out as a more general problem to be fixed in another PR.

---------

Signed-off-by: Euclid Ye <euclid.ye@huawei.com>
This commit is contained in:
Euclid Ye 2025-07-24 13:42:04 +08:00 committed by GitHub
parent f62aa8edc2
commit 0b8986c8da
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 47 additions and 30 deletions

View file

@ -361,6 +361,13 @@ impl WebView {
.move_resize_webview(self.id(), rect); .move_resize_webview(self.id(), rect);
} }
pub fn resize(&self, new_size: PhysicalSize<u32>) {
self.inner()
.compositor
.borrow_mut()
.resize_rendering_context(new_size);
}
pub fn hidpi_scale_factor(&self) -> Scale<f32, DeviceIndependentPixel, DevicePixel> { pub fn hidpi_scale_factor(&self) -> Scale<f32, DeviceIndependentPixel, DevicePixel> {
self.inner().hidpi_scale_factor self.inner().hidpi_scale_factor
} }
@ -490,13 +497,6 @@ impl WebView {
self.inner().compositor.borrow_mut().on_vsync(self.id()); self.inner().compositor.borrow_mut().on_vsync(self.id());
} }
pub fn resize(&self, new_size: PhysicalSize<u32>) {
self.inner()
.compositor
.borrow_mut()
.resize_rendering_context(new_size);
}
pub fn set_zoom(&self, new_zoom: f32) { pub fn set_zoom(&self, new_zoom: f32) {
self.inner() self.inner()
.compositor .compositor

View file

@ -468,10 +468,10 @@ pub trait WebViewDelegate {
/// Whether or not to allow a [`WebView`] to unload a `Document` in its main frame or one /// Whether or not to allow a [`WebView`] to unload a `Document` in its main frame or one
/// of its nested `<iframe>`s. By default, unloads are allowed. /// of its nested `<iframe>`s. By default, unloads are allowed.
fn request_unload(&self, _webview: WebView, _unload_request: AllowOrDenyRequest) {} fn request_unload(&self, _webview: WebView, _unload_request: AllowOrDenyRequest) {}
/// Move the window to a point /// Move the window to a point.
fn request_move_to(&self, _webview: WebView, _: DeviceIntPoint) {} fn request_move_to(&self, _webview: WebView, _: DeviceIntPoint) {}
/// Resize the window to size /// Try to resize the window that contains this [`WebView`] to the provided outer size.
fn request_resize_to(&self, _webview: WebView, _: DeviceIntSize) {} fn request_resize_to(&self, _webview: WebView, _requested_outer_size: DeviceIntSize) {}
/// Whether or not to allow script to open a new `WebView`. If not handled by the /// Whether or not to allow script to open a new `WebView`. If not handled by the
/// embedder, these requests are automatically denied. /// embedder, these requests are automatically denied.
fn request_open_auxiliary_webview(&self, _parent_webview: WebView) -> Option<WebView> { fn request_open_auxiliary_webview(&self, _parent_webview: WebView) -> Option<WebView> {

View file

@ -553,11 +553,11 @@ impl WebViewDelegate for RunningAppState {
self.inner().window.set_position(new_position); self.inner().window.set_position(new_position);
} }
fn request_resize_to(&self, webview: servo::WebView, new_outer_size: DeviceIntSize) { fn request_resize_to(&self, webview: servo::WebView, requested_outer_size: DeviceIntSize) {
let mut rect = webview.rect(); // We need to update compositor's view later as we not sure about resizing result.
rect.set_size(new_outer_size.to_f32()); self.inner()
webview.move_resize(rect); .window
self.inner().window.request_resize(&webview, new_outer_size); .request_resize(&webview, requested_outer_size);
} }
fn show_simple_dialog(&self, webview: servo::WebView, dialog: SimpleDialog) { fn show_simple_dialog(&self, webview: servo::WebView, dialog: SimpleDialog) {

View file

@ -91,12 +91,12 @@ impl Window {
event_loop: &ActiveEventLoop, event_loop: &ActiveEventLoop,
) -> Window { ) -> Window {
let no_native_titlebar = servoshell_preferences.no_native_titlebar; let no_native_titlebar = servoshell_preferences.no_native_titlebar;
let window_size = servoshell_preferences.initial_window_size; let inner_size = servoshell_preferences.initial_window_size;
let window_attr = winit::window::Window::default_attributes() let window_attr = winit::window::Window::default_attributes()
.with_title("Servo".to_string()) .with_title("Servo".to_string())
.with_decorations(!no_native_titlebar) .with_decorations(!no_native_titlebar)
.with_transparent(no_native_titlebar) .with_transparent(no_native_titlebar)
.with_inner_size(LogicalSize::new(window_size.width, window_size.height)) .with_inner_size(LogicalSize::new(inner_size.width, inner_size.height))
.with_min_inner_size(LogicalSize::new(1, 1)) .with_min_inner_size(LogicalSize::new(1, 1))
// Must be invisible at startup; accesskit_winit setup needs to // Must be invisible at startup; accesskit_winit setup needs to
// happen before the window is shown for the first time. // happen before the window is shown for the first time.
@ -430,6 +430,15 @@ impl Window {
} }
} }
} }
/// Update [`WindowRenderingContext`] after a resize.
fn update_window_rendering_context_after_resize(&self, inner_size: PhysicalSize<u32>) {
self.window_rendering_context.resize(PhysicalSize::new(
inner_size.width,
(inner_size.height as f32 - (self.toolbar_height() * self.hidpi_scale_factor()).0)
as u32,
));
}
} }
impl WindowPortsMethods for Window { impl WindowPortsMethods for Window {
@ -495,11 +504,11 @@ impl WindowPortsMethods for Window {
new_outer_size.width - decoration_width as i32, new_outer_size.width - decoration_width as i32,
new_outer_size.height - decoration_height as i32, new_outer_size.height - decoration_height as i32,
)) ))
.and_then(|size| { .map(|resulting_size| {
Some(DeviceIntSize::new( DeviceIntSize::new(
size.width.try_into().ok()?, (resulting_size.width + decoration_width) as i32,
size.height.try_into().ok()?, (resulting_size.height + decoration_height) as i32,
)) )
}) })
} }
@ -678,10 +687,11 @@ impl WindowPortsMethods for Window {
WindowEvent::CloseRequested => { WindowEvent::CloseRequested => {
state.servo().start_shutting_down(); state.servo().start_shutting_down();
}, },
WindowEvent::Resized(new_size) => { WindowEvent::Resized(new_inner_size) => {
if self.inner_size.get() != new_size { if self.inner_size.get() != new_inner_size {
self.window_rendering_context.resize(new_size); self.inner_size.set(new_inner_size);
self.inner_size.set(new_size); // `WebView::move_resize` was already called in `Minibrowser::update`.
self.update_window_rendering_context_after_resize(new_inner_size);
} }
}, },
WindowEvent::ThemeChanged(theme) => { WindowEvent::ThemeChanged(theme) => {
@ -755,6 +765,9 @@ impl WindowPortsMethods for Window {
} }
fn set_toolbar_height(&self, height: Length<f32, DeviceIndependentPixel>) { fn set_toolbar_height(&self, height: Length<f32, DeviceIndependentPixel>) {
if self.toolbar_height() == height {
return;
}
self.toolbar_height.set(height); self.toolbar_height.set(height);
// Prevent the inner area from being 0 pixels wide or tall // Prevent the inner area from being 0 pixels wide or tall
// this prevents a crash in the compositor due to invalid surface size // this prevents a crash in the compositor due to invalid surface size

View file

@ -77,10 +77,10 @@ impl WindowPortsMethods for Window {
fn request_resize( fn request_resize(
&self, &self,
webview: &::servo::WebView, webview: &::servo::WebView,
size: DeviceIntSize, outer_size: DeviceIntSize,
) -> Option<DeviceIntSize> { ) -> Option<DeviceIntSize> {
// Surfman doesn't support zero-sized surfaces. // Surfman doesn't support zero-sized surfaces.
let new_size = DeviceIntSize::new(size.width.max(1), size.height.max(1)); let new_size = DeviceIntSize::new(outer_size.width.max(1), outer_size.height.max(1));
if self.inner_size.get() == new_size { if self.inner_size.get() == new_size {
return Some(new_size); return Some(new_size);
} }
@ -90,7 +90,11 @@ impl WindowPortsMethods for Window {
// Because we are managing the rendering surface ourselves, there will be no other // Because we are managing the rendering surface ourselves, there will be no other
// notification (such as from the display manager) that it has changed size, so we // notification (such as from the display manager) that it has changed size, so we
// must notify the compositor here. // must notify the compositor here.
webview.resize(PhysicalSize::new(size.width as u32, size.height as u32)); webview.move_resize(outer_size.to_f32().into());
webview.resize(PhysicalSize::new(
outer_size.width as u32,
outer_size.height as u32,
));
Some(new_size) Some(new_size)
} }

View file

@ -44,7 +44,7 @@ pub(crate) struct ServoShellPreferences {
/// Overrides directives specified via `SERVO_TRACING` if set. /// Overrides directives specified via `SERVO_TRACING` if set.
/// See: <https://docs.rs/tracing-subscriber/0.3.19/tracing_subscriber/filter/struct.EnvFilter.html#directives> /// See: <https://docs.rs/tracing-subscriber/0.3.19/tracing_subscriber/filter/struct.EnvFilter.html#directives>
pub tracing_filter: Option<String>, pub tracing_filter: Option<String>,
/// The initial requested size of the window. /// The initial requested inner size of the window.
pub initial_window_size: Size2D<u32, DeviceIndependentPixel>, pub initial_window_size: Size2D<u32, DeviceIndependentPixel>,
/// An override for the screen resolution. This is useful for testing behavior on different screen sizes, /// An override for the screen resolution. This is useful for testing behavior on different screen sizes,
/// such as the screen of a mobile device. /// such as the screen of a mobile device.