libservo: Make zooming and HiDPI scaling work per-WebView (#36419)

libservo: Make zooming and HiDPI scaling work per-`WebView`

This change moves all zooming and HiDPI scaling to work per-`WebView` in
both libservo and Compositor. This means that you can pinch zoom one
`WebView` and it should now work independently of other `WebView`s.
This is accomplished by making each `WebView` in the WebRender scene
have its own scaling reference frame.

All WebViews are now expected to manage their HiDPI scaling factor and
this can be set independently of other WebViews. Perhaps in the future
this will become a Servo-wide setting.

This allows full removal of the `WindowMethods` trait from Servo.

Testing: There are not yet any tests for the WebView API, but I hope
to add those soon.

Co-authored-by: Shubham Gupta <shubham13297@gmail.com>
Signed-off-by: Martin Robinson <mrobinson@igalia.com>

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
Co-authored-by: Shubham Gupta <shubham13297@gmail.com>
This commit is contained in:
Martin Robinson 2025-04-14 14:01:49 +02:00 committed by GitHub
parent f1417c4e75
commit c6dc7c83a8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
17 changed files with 415 additions and 385 deletions

View file

@ -21,16 +21,14 @@ use compositing_traits::rendering_context::RenderingContext;
use compositing_traits::{ use compositing_traits::{
CompositionPipeline, CompositorMsg, ImageUpdate, RendererWebView, SendableFrameTree, CompositionPipeline, CompositorMsg, ImageUpdate, RendererWebView, SendableFrameTree,
}; };
use constellation_traits::{ use constellation_traits::{AnimationTickType, EmbedderToConstellationMessage, PaintMetricEvent};
AnimationTickType, EmbedderToConstellationMessage, PaintMetricEvent, WindowSizeType,
};
use crossbeam_channel::Sender; use crossbeam_channel::Sender;
use dpi::PhysicalSize; use dpi::PhysicalSize;
use embedder_traits::{ use embedder_traits::{
AnimationState, CompositorHitTestResult, Cursor, InputEvent, MouseButtonEvent, MouseMoveEvent, AnimationState, CompositorHitTestResult, Cursor, InputEvent, MouseButtonEvent, MouseMoveEvent,
ScreenGeometry, ShutdownState, TouchEventType, UntrustedNodeAddress, ViewportDetails, ShutdownState, TouchEventType, UntrustedNodeAddress, ViewportDetails,
}; };
use euclid::{Box2D, Point2D, Rect, Scale, Size2D, Transform3D}; use euclid::{Point2D, Rect, Scale, Size2D, Transform3D};
use fnv::FnvHashMap; use fnv::FnvHashMap;
use ipc_channel::ipc::{self, IpcReceiver, IpcSharedMemory}; use ipc_channel::ipc::{self, IpcReceiver, IpcSharedMemory};
use libc::c_void; use libc::c_void;
@ -40,7 +38,7 @@ use profile_traits::time::{self as profile_time, ProfilerCategory};
use profile_traits::time_profile; use profile_traits::time_profile;
use servo_config::opts; use servo_config::opts;
use servo_geometry::DeviceIndependentPixel; use servo_geometry::DeviceIndependentPixel;
use style_traits::{CSSPixel, PinchZoomFactor}; use style_traits::CSSPixel;
use webrender::{CaptureBits, RenderApi, Transaction}; use webrender::{CaptureBits, RenderApi, Transaction};
use webrender_api::units::{ use webrender_api::units::{
DeviceIntPoint, DeviceIntRect, DevicePixel, DevicePoint, DeviceRect, LayoutPoint, LayoutRect, DeviceIntPoint, DeviceIntRect, DevicePixel, DevicePoint, DeviceRect, LayoutPoint, LayoutRect,
@ -57,7 +55,7 @@ use webrender_api::{
use crate::InitialCompositorState; use crate::InitialCompositorState;
use crate::webview::{UnknownWebView, WebView}; use crate::webview::{UnknownWebView, WebView};
use crate::webview_manager::WebViewManager; use crate::webview_manager::WebViewManager;
use crate::windowing::{WebRenderDebugOption, WindowMethods}; use crate::windowing::WebRenderDebugOption;
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
enum UnableToComposite { enum UnableToComposite {
@ -71,10 +69,6 @@ enum NotReadyToPaint {
WaitingOnConstellation, WaitingOnConstellation,
} }
// Default viewport constraints
const MAX_ZOOM: f32 = 8.0;
const MIN_ZOOM: f32 = 0.1;
/// Holds the state when running reftests that determines when it is /// Holds the state when running reftests that determines when it is
/// safe to save the output image. /// safe to save the output image.
#[derive(Clone, Copy, Debug, PartialEq)] #[derive(Clone, Copy, Debug, PartialEq)]
@ -134,19 +128,6 @@ pub struct IOCompositor {
/// Our top-level browsing contexts. /// Our top-level browsing contexts.
webviews: WebViewManager<WebView>, webviews: WebViewManager<WebView>,
/// The application window.
pub window: Rc<dyn WindowMethods>,
/// "Mobile-style" zoom that does not reflow the page.
viewport_zoom: PinchZoomFactor,
/// Viewport zoom constraints provided by @viewport.
min_viewport_zoom: Option<PinchZoomFactor>,
max_viewport_zoom: Option<PinchZoomFactor>,
/// "Desktop-style" zoom that resizes the viewport to fit the window.
page_zoom: Scale<f32, CSSPixel, DeviceIndependentPixel>,
/// Tracks whether or not the view needs to be repainted. /// Tracks whether or not the view needs to be repainted.
needs_repaint: Cell<RepaintReason>, needs_repaint: Cell<RepaintReason>,
@ -160,10 +141,6 @@ pub struct IOCompositor {
/// The surfman instance that webrender targets /// The surfman instance that webrender targets
rendering_context: Rc<dyn RenderingContext>, rendering_context: Rc<dyn RenderingContext>,
/// The HighDPI factor of the native window, its view and the screen.
/// TODO: Eventually this should be a property of the `WebView`.
hidpi_factor: Scale<f32, DeviceIndependentPixel, DevicePixel>,
/// The number of frames pending to receive from WebRender. /// The number of frames pending to receive from WebRender.
pending_frames: usize, pending_frames: usize,
@ -413,11 +390,7 @@ impl ServoRenderer {
} }
impl IOCompositor { impl IOCompositor {
pub fn new( pub fn new(state: InitialCompositorState, convert_mouse_to_touch: bool) -> Self {
window: Rc<dyn WindowMethods>,
state: InitialCompositorState,
convert_mouse_to_touch: bool,
) -> Self {
let compositor = IOCompositor { let compositor = IOCompositor {
global: Rc::new(RefCell::new(ServoRenderer { global: Rc::new(RefCell::new(ServoRenderer {
shutdown_state: state.shutdown_state, shutdown_state: state.shutdown_state,
@ -435,13 +408,7 @@ impl IOCompositor {
cursor_pos: DevicePoint::new(0.0, 0.0), cursor_pos: DevicePoint::new(0.0, 0.0),
})), })),
webviews: WebViewManager::default(), webviews: WebViewManager::default(),
hidpi_factor: window.hidpi_factor(),
window,
needs_repaint: Cell::default(), needs_repaint: Cell::default(),
page_zoom: Scale::new(1.0),
viewport_zoom: PinchZoomFactor::new(1.0),
min_viewport_zoom: Some(PinchZoomFactor::new(1.0)),
max_viewport_zoom: None,
ready_to_save_state: ReadyState::Unknown, ready_to_save_state: ReadyState::Unknown,
webrender: Some(state.webrender), webrender: Some(state.webrender),
rendering_context: state.rendering_context, rendering_context: state.rendering_context,
@ -467,15 +434,8 @@ impl IOCompositor {
} }
} }
pub fn default_webview_viewport_details(&self) -> ViewportDetails { pub fn rendering_context_size(&self) -> Size2D<u32, DevicePixel> {
// The division by 1 represents the page's default zoom of 100%, self.rendering_context.size2d()
// and gives us the appropriate CSSPixel type for the viewport.
let hidpi_scale_factor = self.window.hidpi_factor();
let scaled_viewport_size = self.rendering_context.size2d().to_f32() / hidpi_scale_factor;
ViewportDetails {
size: scaled_viewport_size / Scale::new(1.0),
hidpi_scale_factor: Scale::new(hidpi_scale_factor.0),
}
} }
pub fn webxr_running(&self) -> bool { pub fn webxr_running(&self) -> bool {
@ -577,8 +537,8 @@ impl IOCompositor {
webview.on_touch_event_processed(result); webview.on_touch_event_processed(result);
}, },
CompositorMsg::CreatePng(page_rect, reply) => { CompositorMsg::CreatePng(webview_id, page_rect, reply) => {
let res = self.render_to_shared_memory(page_rect); let res = self.render_to_shared_memory(webview_id, page_rect);
if let Err(ref e) = res { if let Err(ref e) = res {
info!("Error retrieving PNG: {:?}", e); info!("Error retrieving PNG: {:?}", e);
} }
@ -646,12 +606,12 @@ impl IOCompositor {
}, },
CompositorMsg::WebDriverMouseButtonEvent(webview_id, action, button, x, y) => { CompositorMsg::WebDriverMouseButtonEvent(webview_id, action, button, x, y) => {
let dppx = self.device_pixels_per_page_pixel();
let point = dppx.transform_point(Point2D::new(x, y));
let Some(webview) = self.webviews.get_mut(webview_id) else { let Some(webview) = self.webviews.get_mut(webview_id) else {
warn!("Handling input event for unknown webview: {webview_id}"); warn!("Handling input event for unknown webview: {webview_id}");
return; return;
}; };
let dppx = webview.device_pixels_per_page_pixel();
let point = dppx.transform_point(Point2D::new(x, y));
webview.dispatch_input_event(InputEvent::MouseButton(MouseButtonEvent { webview.dispatch_input_event(InputEvent::MouseButton(MouseButtonEvent {
point, point,
action, action,
@ -660,12 +620,12 @@ impl IOCompositor {
}, },
CompositorMsg::WebDriverMouseMoveEvent(webview_id, x, y) => { CompositorMsg::WebDriverMouseMoveEvent(webview_id, x, y) => {
let dppx = self.device_pixels_per_page_pixel();
let point = dppx.transform_point(Point2D::new(x, y));
let Some(webview) = self.webviews.get_mut(webview_id) else { let Some(webview) = self.webviews.get_mut(webview_id) else {
warn!("Handling input event for unknown webview: {webview_id}"); warn!("Handling input event for unknown webview: {webview_id}");
return; return;
}; };
let dppx = webview.device_pixels_per_page_pixel();
let point = dppx.transform_point(Point2D::new(x, y));
webview.dispatch_input_event(InputEvent::MouseMove(MouseMoveEvent { point })); webview.dispatch_input_event(InputEvent::MouseMove(MouseMoveEvent { point }));
}, },
@ -897,45 +857,38 @@ impl IOCompositor {
let _ = result_sender.send((font_keys, font_instance_keys)); let _ = result_sender.send((font_keys, font_instance_keys));
}, },
CompositorMsg::GetClientWindowRect(webview_id, response_sender) => { CompositorMsg::GetClientWindowRect(webview_id, response_sender) => {
let screen_geometry = self.webview_screen_geometry(webview_id); let client_window_rect = self
let rect = DeviceIntRect::from_origin_and_size( .webviews
screen_geometry.offset, .get(webview_id)
self.rendering_context.size2d().to_i32(), .map(|webview| webview.client_window_rect(self.rendering_context.size2d()))
) .unwrap_or_default();
.to_f32() / if let Err(error) = response_sender.send(client_window_rect) {
self.hidpi_factor;
if let Err(error) = response_sender.send(rect.to_i32()) {
warn!("Sending response to get client window failed ({error:?})."); warn!("Sending response to get client window failed ({error:?}).");
} }
}, },
CompositorMsg::GetScreenSize(webview_id, response_sender) => { CompositorMsg::GetScreenSize(webview_id, response_sender) => {
let screen_geometry = self.webview_screen_geometry(webview_id); let screen_size = self
let screen_size = screen_geometry.size.to_f32() / self.hidpi_factor; .webviews
.get(webview_id)
if let Err(error) = response_sender.send(screen_size.to_i32()) { .map(WebView::screen_size)
.unwrap_or_default();
if let Err(error) = response_sender.send(screen_size) {
warn!("Sending response to get screen size failed ({error:?})."); warn!("Sending response to get screen size failed ({error:?}).");
} }
}, },
CompositorMsg::GetAvailableScreenSize(webview_id, response_sender) => { CompositorMsg::GetAvailableScreenSize(webview_id, response_sender) => {
let screen_geometry = self.webview_screen_geometry(webview_id); let available_screen_size = self
let available_screen_size = .webviews
screen_geometry.available_size.to_f32() / self.hidpi_factor; .get(webview_id)
.map(WebView::available_screen_size)
if let Err(error) = response_sender.send(available_screen_size.to_i32()) { .unwrap_or_default();
if let Err(error) = response_sender.send(available_screen_size) {
warn!("Sending response to get screen size failed ({error:?})."); warn!("Sending response to get screen size failed ({error:?}).");
} }
}, },
} }
} }
fn webview_screen_geometry(&self, webview_id: WebViewId) -> ScreenGeometry {
self.webviews
.get(webview_id)
.and_then(|webview| webview.renderer_webview.screen_geometry())
.unwrap_or_default()
}
/// Handle messages sent to the compositor during the shutdown process. In general, /// Handle messages sent to the compositor during the shutdown process. In general,
/// the things the compositor can do in this state are limited. It's very important to /// the things the compositor can do in this state are limited. It's very important to
/// answer any synchronous messages though as other threads might be waiting on the /// answer any synchronous messages though as other threads might be waiting on the
@ -1035,12 +988,31 @@ impl IOCompositor {
let mut builder = webrender_api::DisplayListBuilder::new(root_pipeline); let mut builder = webrender_api::DisplayListBuilder::new(root_pipeline);
builder.begin(); builder.begin();
let zoom_factor = self.device_pixels_per_page_pixel().0; let root_reference_frame = SpatialId::root_reference_frame(root_pipeline);
let zoom_reference_frame = builder.push_reference_frame(
let viewport_size = self.rendering_context.size2d().to_f32().to_untyped();
let viewport_rect = LayoutRect::from_origin_and_size(
LayoutPoint::zero(), LayoutPoint::zero(),
SpatialId::root_reference_frame(root_pipeline), LayoutSize::from_untyped(viewport_size),
);
let root_clip_id = builder.define_clip_rect(root_reference_frame, viewport_rect);
let clip_chain_id = builder.define_clip_chain(None, [root_clip_id]);
for (_, webview) in self.webviews.painting_order() {
let Some(pipeline_id) = webview.root_pipeline_id else {
continue;
};
let device_pixels_per_page_pixel = webview.device_pixels_per_page_pixel().0;
let webview_reference_frame = builder.push_reference_frame(
LayoutPoint::zero(),
root_reference_frame,
TransformStyle::Flat, TransformStyle::Flat,
PropertyBinding::Value(Transform3D::scale(zoom_factor, zoom_factor, 1.)), PropertyBinding::Value(Transform3D::scale(
device_pixels_per_page_pixel,
device_pixels_per_page_pixel,
1.,
)),
ReferenceFrameKind::Transform { ReferenceFrameKind::Transform {
is_2d_scale_translation: true, is_2d_scale_translation: true,
should_snap: true, should_snap: true,
@ -1049,30 +1021,18 @@ impl IOCompositor {
SpatialTreeItemKey::new(0, 0), SpatialTreeItemKey::new(0, 0),
); );
let scaled_viewport_size = let scaled_webview_rect = webview.rect / device_pixels_per_page_pixel;
self.rendering_context.size2d().to_f32().to_untyped() / zoom_factor;
let scaled_viewport_rect = LayoutRect::from_origin_and_size(
LayoutPoint::zero(),
LayoutSize::from_untyped(scaled_viewport_size),
);
let root_clip_id = builder.define_clip_rect(zoom_reference_frame, scaled_viewport_rect);
let clip_chain_id = builder.define_clip_chain(None, [root_clip_id]);
for (_, webview) in self.webviews.painting_order() {
if let Some(pipeline_id) = webview.root_pipeline_id {
let scaled_webview_rect = webview.rect / zoom_factor;
builder.push_iframe( builder.push_iframe(
LayoutRect::from_untyped(&scaled_webview_rect.to_untyped()), LayoutRect::from_untyped(&scaled_webview_rect.to_untyped()),
LayoutRect::from_untyped(&scaled_webview_rect.to_untyped()), LayoutRect::from_untyped(&scaled_webview_rect.to_untyped()),
&SpaceAndClipInfo { &SpaceAndClipInfo {
spatial_id: zoom_reference_frame, spatial_id: webview_reference_frame,
clip_chain_id, clip_chain_id,
}, },
pipeline_id.into(), pipeline_id.into(),
true, true,
); );
} }
}
let built_display_list = builder.end(); let built_display_list = builder.end();
@ -1113,12 +1073,15 @@ impl IOCompositor {
} }
} }
pub fn add_webview(&mut self, webview: Box<dyn RendererWebView>) { pub fn add_webview(
let size = self.rendering_context.size2d().to_f32(); &mut self,
webview: Box<dyn RendererWebView>,
viewport_details: ViewportDetails,
) {
self.webviews.entry(webview.id()).or_insert(WebView::new( self.webviews.entry(webview.id()).or_insert(WebView::new(
webview,
Box2D::from_origin_and_size(Point2D::origin(), size),
self.global.clone(), self.global.clone(),
webview,
viewport_details,
)); ));
} }
@ -1147,31 +1110,6 @@ impl IOCompositor {
self.send_root_pipeline_display_list(); self.send_root_pipeline_display_list();
} }
pub fn move_resize_webview(&mut self, webview_id: WebViewId, rect: DeviceRect) {
debug!("{webview_id}: Moving and/or resizing webview; rect={rect:?}");
let rect_changed;
let size_changed;
match self.webviews.get_mut(webview_id) {
Some(webview) => {
rect_changed = rect != webview.rect;
size_changed = rect.size() != webview.rect.size();
webview.rect = rect;
},
None => {
warn!("{webview_id}: MoveResizeWebView on unknown webview id");
return;
},
};
if rect_changed {
if size_changed {
self.send_window_size_message_for_top_level_browser_context(rect, webview_id);
}
self.send_root_pipeline_display_list();
}
}
pub fn show_webview( pub fn show_webview(
&mut self, &mut self,
webview_id: WebViewId, webview_id: WebViewId,
@ -1228,37 +1166,46 @@ impl IOCompositor {
Ok(()) Ok(())
} }
fn send_window_size_message_for_top_level_browser_context( pub fn move_resize_webview(&mut self, webview_id: WebViewId, rect: DeviceRect) {
&self,
rect: DeviceRect,
webview_id: WebViewId,
) {
// The device pixel ratio used by the style system should include the scale from page pixels
// to device pixels, but not including any pinch zoom.
let hidpi_scale_factor = self.device_pixels_per_page_pixel_not_including_page_zoom();
let size = rect.size().to_f32() / hidpi_scale_factor;
let msg = EmbedderToConstellationMessage::ChangeViewportDetails(
webview_id,
ViewportDetails {
size,
hidpi_scale_factor,
},
WindowSizeType::Resize,
);
if let Err(e) = self.global.borrow().constellation_sender.send(msg) {
warn!("Sending window resize to constellation failed ({:?}).", e);
}
}
pub fn resize_rendering_context(&mut self, new_size: PhysicalSize<u32>) -> bool {
if self.global.borrow().shutdown_state() != ShutdownState::NotShuttingDown { if self.global.borrow().shutdown_state() != ShutdownState::NotShuttingDown {
return false; return;
}
let Some(webview) = self.webviews.get_mut(webview_id) else {
return;
};
if !webview.set_rect(rect) {
return;
} }
let old_hidpi_factor = self.hidpi_factor; self.send_root_pipeline_display_list();
self.hidpi_factor = self.window.hidpi_factor(); self.set_needs_repaint(RepaintReason::Resize);
if self.hidpi_factor == old_hidpi_factor && self.rendering_context.size() == new_size { }
return false;
pub fn set_hidpi_scale_factor(
&mut self,
webview_id: WebViewId,
new_scale_factor: Scale<f32, DeviceIndependentPixel, DevicePixel>,
) {
if self.global.borrow().shutdown_state() != ShutdownState::NotShuttingDown {
return;
}
let Some(webview) = self.webviews.get_mut(webview_id) else {
return;
};
if !webview.set_hidpi_scale_factor(new_scale_factor) {
return;
}
self.send_root_pipeline_display_list();
self.set_needs_repaint(RepaintReason::Resize);
}
pub fn resize_rendering_context(&mut self, new_size: PhysicalSize<u32>) {
if self.global.borrow().shutdown_state() != ShutdownState::NotShuttingDown {
return;
}
if self.rendering_context.size() == new_size {
return;
} }
self.rendering_context.resize(new_size); self.rendering_context.resize(new_size);
@ -1271,9 +1218,8 @@ impl IOCompositor {
transaction.set_document_view(output_region); transaction.set_document_view(output_region);
self.global.borrow_mut().send_transaction(transaction); self.global.borrow_mut().send_transaction(transaction);
self.update_after_zoom_or_hidpi_change(); self.send_root_pipeline_display_list();
self.set_needs_repaint(RepaintReason::Resize); self.set_needs_repaint(RepaintReason::Resize);
true
} }
/// If there are any animations running, dispatches appropriate messages to the constellation. /// If there are any animations running, dispatches appropriate messages to the constellation.
@ -1294,41 +1240,25 @@ impl IOCompositor {
} }
} }
pub(crate) fn device_pixels_per_page_pixel(&self) -> Scale<f32, CSSPixel, DevicePixel> { pub fn on_zoom_reset_window_event(&mut self, webview_id: WebViewId) {
self.device_pixels_per_page_pixel_not_including_page_zoom() * self.pinch_zoom_level()
}
fn device_pixels_per_page_pixel_not_including_page_zoom(
&self,
) -> Scale<f32, CSSPixel, DevicePixel> {
self.page_zoom * self.hidpi_factor
}
pub fn on_zoom_reset_window_event(&mut self) {
if self.global.borrow().shutdown_state() != ShutdownState::NotShuttingDown { if self.global.borrow().shutdown_state() != ShutdownState::NotShuttingDown {
return; return;
} }
self.page_zoom = Scale::new(1.0); if let Some(webview) = self.webviews.get_mut(webview_id) {
self.update_after_zoom_or_hidpi_change(); webview.set_page_zoom(1.0);
}
self.send_root_pipeline_display_list();
} }
pub fn on_zoom_window_event(&mut self, magnification: f32) { pub fn on_zoom_window_event(&mut self, webview_id: WebViewId, magnification: f32) {
if self.global.borrow().shutdown_state() != ShutdownState::NotShuttingDown { if self.global.borrow().shutdown_state() != ShutdownState::NotShuttingDown {
return; return;
} }
self.page_zoom = if let Some(webview) = self.webviews.get_mut(webview_id) {
Scale::new((self.page_zoom.get() * magnification).clamp(MIN_ZOOM, MAX_ZOOM)); webview.set_page_zoom(magnification);
self.update_after_zoom_or_hidpi_change();
} }
fn update_after_zoom_or_hidpi_change(&mut self) {
for (webview_id, webview) in self.webviews.painting_order() {
self.send_window_size_message_for_top_level_browser_context(webview.rect, *webview_id);
}
// Update the root transform in WebRender to reflect the new zoom.
self.send_root_pipeline_display_list(); self.send_root_pipeline_display_list();
} }
@ -1428,13 +1358,19 @@ impl IOCompositor {
/// [`IOCompositor`]. If succesful return the output image in shared memory. /// [`IOCompositor`]. If succesful return the output image in shared memory.
fn render_to_shared_memory( fn render_to_shared_memory(
&mut self, &mut self,
webview_id: WebViewId,
page_rect: Option<Rect<f32, CSSPixel>>, page_rect: Option<Rect<f32, CSSPixel>>,
) -> Result<Option<Image>, UnableToComposite> { ) -> Result<Option<Image>, UnableToComposite> {
self.render_inner()?; self.render_inner()?;
let size = self.rendering_context.size2d().to_i32(); let size = self.rendering_context.size2d().to_i32();
let rect = if let Some(rect) = page_rect { let rect = if let Some(rect) = page_rect {
let rect = self.device_pixels_per_page_pixel().transform_rect(&rect); let scale = self
.webviews
.get(webview_id)
.map(WebView::device_pixels_per_page_pixel)
.unwrap_or_else(|| Scale::new(1.0));
let rect = scale.transform_rect(&rect);
let x = rect.origin.x as i32; let x = rect.origin.x as i32;
// We need to convert to the bottom-left origin coordinate // We need to convert to the bottom-left origin coordinate
@ -1676,22 +1612,6 @@ impl IOCompositor {
self.global.borrow().shutdown_state() != ShutdownState::FinishedShuttingDown self.global.borrow().shutdown_state() != ShutdownState::FinishedShuttingDown
} }
pub fn pinch_zoom_level(&self) -> Scale<f32, DevicePixel, DevicePixel> {
Scale::new(self.viewport_zoom.get())
}
pub(crate) fn set_pinch_zoom_level(&mut self, mut zoom: f32) -> bool {
if let Some(min) = self.min_viewport_zoom {
zoom = f32::max(min.get(), zoom);
}
if let Some(max) = self.max_viewport_zoom {
zoom = f32::min(max.get(), zoom);
}
let old_zoom = std::mem::replace(&mut self.viewport_zoom, PinchZoomFactor::new(zoom));
old_zoom != self.viewport_zoom
}
pub fn toggle_webrender_debug(&mut self, option: WebRenderDebugOption) { pub fn toggle_webrender_debug(&mut self, option: WebRenderDebugOption) {
let Some(webrender) = self.webrender.as_mut() else { let Some(webrender) = self.webrender.as_mut() else {
return; return;

View file

@ -9,17 +9,21 @@ use std::rc::Rc;
use base::id::{PipelineId, WebViewId}; use base::id::{PipelineId, WebViewId};
use compositing_traits::{RendererWebView, SendableFrameTree}; use compositing_traits::{RendererWebView, SendableFrameTree};
use constellation_traits::{EmbedderToConstellationMessage, ScrollState}; use constellation_traits::{EmbedderToConstellationMessage, ScrollState, WindowSizeType};
use embedder_traits::{ use embedder_traits::{
AnimationState, CompositorHitTestResult, InputEvent, MouseButton, MouseButtonAction, AnimationState, CompositorHitTestResult, InputEvent, MouseButton, MouseButtonAction,
MouseButtonEvent, MouseMoveEvent, ShutdownState, TouchEvent, TouchEventResult, TouchEventType, MouseButtonEvent, MouseMoveEvent, ShutdownState, TouchEvent, TouchEventResult, TouchEventType,
TouchId, TouchId, ViewportDetails,
}; };
use euclid::{Point2D, Scale, Vector2D}; use euclid::{Box2D, Point2D, Scale, Size2D, Vector2D};
use fnv::FnvHashSet; use fnv::FnvHashSet;
use log::{debug, warn}; use log::{debug, warn};
use servo_geometry::DeviceIndependentPixel;
use style_traits::{CSSPixel, PinchZoomFactor};
use webrender::Transaction; use webrender::Transaction;
use webrender_api::units::{DeviceIntPoint, DevicePoint, DeviceRect, LayoutVector2D}; use webrender_api::units::{
DeviceIntPoint, DeviceIntRect, DevicePixel, DevicePoint, DeviceRect, LayoutVector2D,
};
use webrender_api::{ use webrender_api::{
ExternalScrollId, HitTestFlags, RenderReasons, SampledScrollOffset, ScrollLocation, ExternalScrollId, HitTestFlags, RenderReasons, SampledScrollOffset, ScrollLocation,
}; };
@ -28,6 +32,10 @@ use crate::IOCompositor;
use crate::compositor::{PipelineDetails, ServoRenderer}; use crate::compositor::{PipelineDetails, ServoRenderer};
use crate::touch::{TouchHandler, TouchMoveAction, TouchMoveAllowed, TouchSequenceState}; use crate::touch::{TouchHandler, TouchMoveAction, TouchMoveAllowed, TouchSequenceState};
// Default viewport constraints
const MAX_ZOOM: f32 = 8.0;
const MIN_ZOOM: f32 = 0.1;
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
struct ScrollEvent { struct ScrollEvent {
/// Scroll by this offset, or to Start or End /// Scroll by this offset, or to Start or End
@ -65,6 +73,16 @@ pub(crate) struct WebView {
pending_scroll_zoom_events: Vec<ScrollZoomEvent>, pending_scroll_zoom_events: Vec<ScrollZoomEvent>,
/// Touch input state machine /// Touch input state machine
touch_handler: TouchHandler, touch_handler: TouchHandler,
/// "Desktop-style" zoom that resizes the viewport to fit the window.
pub page_zoom: Scale<f32, CSSPixel, DeviceIndependentPixel>,
/// "Mobile-style" zoom that does not reflow the page.
viewport_zoom: PinchZoomFactor,
/// Viewport zoom constraints provided by @viewport.
min_viewport_zoom: Option<PinchZoomFactor>,
max_viewport_zoom: Option<PinchZoomFactor>,
/// The HiDPI scale factor for the `WebView` associated with this renderer. This is controlled
/// by the embedding layer.
hidpi_scale_factor: Scale<f32, DeviceIndependentPixel, DevicePixel>,
} }
impl Drop for WebView { impl Drop for WebView {
@ -78,19 +96,26 @@ impl Drop for WebView {
impl WebView { impl WebView {
pub(crate) fn new( pub(crate) fn new(
renderer_webview: Box<dyn RendererWebView>,
rect: DeviceRect,
global: Rc<RefCell<ServoRenderer>>, global: Rc<RefCell<ServoRenderer>>,
renderer_webview: Box<dyn RendererWebView>,
viewport_details: ViewportDetails,
) -> Self { ) -> Self {
let hidpi_scale_factor = viewport_details.hidpi_scale_factor;
let size = viewport_details.size * viewport_details.hidpi_scale_factor;
Self { Self {
id: renderer_webview.id(), id: renderer_webview.id(),
renderer_webview, renderer_webview,
root_pipeline_id: None, root_pipeline_id: None,
rect, rect: DeviceRect::from_origin_and_size(DevicePoint::origin(), size),
pipelines: Default::default(), pipelines: Default::default(),
touch_handler: TouchHandler::new(), touch_handler: TouchHandler::new(),
global, global,
pending_scroll_zoom_events: Default::default(), pending_scroll_zoom_events: Default::default(),
page_zoom: Scale::new(1.0),
viewport_zoom: PinchZoomFactor::new(1.0),
min_viewport_zoom: Some(PinchZoomFactor::new(1.0)),
max_viewport_zoom: None,
hidpi_scale_factor: Scale::new(hidpi_scale_factor.0),
} }
} }
@ -265,7 +290,7 @@ impl WebView {
} }
/// On a Window refresh tick (e.g. vsync) /// On a Window refresh tick (e.g. vsync)
pub fn on_vsync(&mut self) { pub(crate) fn on_vsync(&mut self) {
if let Some(fling_action) = self.touch_handler.on_vsync() { if let Some(fling_action) = self.touch_handler.on_vsync() {
self.on_scroll_window_event( self.on_scroll_window_event(
ScrollLocation::Delta(fling_action.delta), ScrollLocation::Delta(fling_action.delta),
@ -300,7 +325,7 @@ impl WebView {
} }
} }
pub fn notify_input_event(&mut self, event: InputEvent) { pub(crate) fn notify_input_event(&mut self, event: InputEvent) {
if self.global.borrow().shutdown_state() != ShutdownState::NotShuttingDown { if self.global.borrow().shutdown_state() != ShutdownState::NotShuttingDown {
return; return;
} }
@ -365,7 +390,7 @@ impl WebView {
} }
} }
pub fn on_touch_event(&mut self, event: TouchEvent) { pub(crate) fn on_touch_event(&mut self, event: TouchEvent) {
if self.global.borrow().shutdown_state() != ShutdownState::NotShuttingDown { if self.global.borrow().shutdown_state() != ShutdownState::NotShuttingDown {
return; return;
} }
@ -644,7 +669,7 @@ impl WebView {
})); }));
} }
pub fn notify_scroll_event( pub(crate) fn notify_scroll_event(
&mut self, &mut self,
scroll_location: ScrollLocation, scroll_location: ScrollLocation,
cursor: DeviceIntPoint, cursor: DeviceIntPoint,
@ -727,13 +752,12 @@ impl WebView {
} }
} }
let zoom_changed = compositor let zoom_changed =
.set_pinch_zoom_level(compositor.pinch_zoom_level().get() * combined_magnification); self.set_pinch_zoom_level(self.pinch_zoom_level().get() * combined_magnification);
let scroll_result = combined_scroll_event.and_then(|combined_event| { let scroll_result = combined_scroll_event.and_then(|combined_event| {
self.scroll_node_at_device_point( self.scroll_node_at_device_point(
combined_event.cursor.to_f32(), combined_event.cursor.to_f32(),
combined_event.scroll_location, combined_event.scroll_location,
compositor,
) )
}); });
if !zoom_changed && scroll_result.is_none() { if !zoom_changed && scroll_result.is_none() {
@ -769,11 +793,10 @@ impl WebView {
&mut self, &mut self,
cursor: DevicePoint, cursor: DevicePoint,
scroll_location: ScrollLocation, scroll_location: ScrollLocation,
compositor: &mut IOCompositor,
) -> Option<(PipelineId, ExternalScrollId, LayoutVector2D)> { ) -> Option<(PipelineId, ExternalScrollId, LayoutVector2D)> {
let scroll_location = match scroll_location { let scroll_location = match scroll_location {
ScrollLocation::Delta(delta) => { ScrollLocation::Delta(delta) => {
let device_pixels_per_page = compositor.device_pixels_per_page_pixel(); let device_pixels_per_page = self.device_pixels_per_page_pixel();
let scaled_delta = (Vector2D::from_untyped(delta.to_untyped()) / let scaled_delta = (Vector2D::from_untyped(delta.to_untyped()) /
device_pixels_per_page) device_pixels_per_page)
.to_untyped(); .to_untyped();
@ -818,8 +841,39 @@ impl WebView {
None None
} }
pub(crate) fn pinch_zoom_level(&self) -> Scale<f32, DevicePixel, DevicePixel> {
Scale::new(self.viewport_zoom.get())
}
fn set_pinch_zoom_level(&mut self, mut zoom: f32) -> bool {
if let Some(min) = self.min_viewport_zoom {
zoom = f32::max(min.get(), zoom);
}
if let Some(max) = self.max_viewport_zoom {
zoom = f32::min(max.get(), zoom);
}
let old_zoom = std::mem::replace(&mut self.viewport_zoom, PinchZoomFactor::new(zoom));
old_zoom != self.viewport_zoom
}
pub(crate) fn set_page_zoom(&mut self, magnification: f32) {
self.page_zoom =
Scale::new((self.page_zoom.get() * magnification).clamp(MIN_ZOOM, MAX_ZOOM));
}
pub(crate) fn device_pixels_per_page_pixel(&self) -> Scale<f32, CSSPixel, DevicePixel> {
self.page_zoom * self.hidpi_scale_factor * self.pinch_zoom_level()
}
pub(crate) fn device_pixels_per_page_pixel_not_including_pinch_zoom(
&self,
) -> Scale<f32, CSSPixel, DevicePixel> {
self.page_zoom * self.hidpi_scale_factor
}
/// Simulate a pinch zoom /// Simulate a pinch zoom
pub fn set_pinch_zoom(&mut self, magnification: f32) { pub(crate) fn set_pinch_zoom(&mut self, magnification: f32) {
if self.global.borrow().shutdown_state() != ShutdownState::NotShuttingDown { if self.global.borrow().shutdown_state() != ShutdownState::NotShuttingDown {
return; return;
} }
@ -828,6 +882,71 @@ impl WebView {
self.pending_scroll_zoom_events self.pending_scroll_zoom_events
.push(ScrollZoomEvent::PinchZoom(magnification)); .push(ScrollZoomEvent::PinchZoom(magnification));
} }
fn send_window_size_message(&self) {
// The device pixel ratio used by the style system should include the scale from page pixels
// to device pixels, but not including any pinch zoom.
let device_pixel_ratio = self.device_pixels_per_page_pixel_not_including_pinch_zoom();
let initial_viewport = self.rect.size().to_f32() / device_pixel_ratio;
let msg = EmbedderToConstellationMessage::ChangeViewportDetails(
self.id,
ViewportDetails {
hidpi_scale_factor: device_pixel_ratio,
size: initial_viewport,
},
WindowSizeType::Resize,
);
if let Err(e) = self.global.borrow().constellation_sender.send(msg) {
warn!("Sending window resize to constellation failed ({:?}).", e);
}
}
/// Set the `hidpi_scale_factor` for this renderer, returning `true` if the value actually changed.
pub(crate) fn set_hidpi_scale_factor(
&mut self,
new_scale: Scale<f32, DeviceIndependentPixel, DevicePixel>,
) -> bool {
let old_scale_factor = std::mem::replace(&mut self.hidpi_scale_factor, new_scale);
if self.hidpi_scale_factor == old_scale_factor {
return false;
}
self.send_window_size_message();
true
}
/// Set the `rect` for this renderer, returning `true` if the value actually changed.
pub(crate) fn set_rect(&mut self, new_rect: DeviceRect) -> bool {
let old_rect = std::mem::replace(&mut self.rect, new_rect);
if old_rect.size() != self.rect.size() {
self.send_window_size_message();
}
old_rect != self.rect
}
pub(crate) fn client_window_rect(
&self,
rendering_context_size: Size2D<u32, DevicePixel>,
) -> Box2D<i32, DeviceIndependentPixel> {
let screen_geometry = self.renderer_webview.screen_geometry().unwrap_or_default();
let rect = DeviceIntRect::from_origin_and_size(
screen_geometry.offset,
rendering_context_size.to_i32(),
)
.to_f32() /
self.hidpi_scale_factor;
rect.to_i32()
}
pub(crate) fn screen_size(&self) -> Size2D<i32, DeviceIndependentPixel> {
let screen_geometry = self.renderer_webview.screen_geometry().unwrap_or_default();
(screen_geometry.size.to_f32() / self.hidpi_scale_factor).to_i32()
}
pub(crate) fn available_screen_size(&self) -> Size2D<i32, DeviceIndependentPixel> {
let screen_geometry = self.renderer_webview.screen_geometry().unwrap_or_default();
(screen_geometry.available_size.to_f32() / self.hidpi_scale_factor).to_i32()
}
} }
#[derive(Clone, Copy, Debug, PartialEq)] #[derive(Clone, Copy, Debug, PartialEq)]

View file

@ -5,10 +5,8 @@
//! Abstract windowing methods. The concrete implementations of these can be found in `platform/`. //! Abstract windowing methods. The concrete implementations of these can be found in `platform/`.
use embedder_traits::{EventLoopWaker, MouseButton}; use embedder_traits::{EventLoopWaker, MouseButton};
use euclid::Scale;
use net::protocols::ProtocolRegistry; use net::protocols::ProtocolRegistry;
use servo_geometry::DeviceIndependentPixel; use webrender_api::units::DevicePoint;
use webrender_api::units::{DevicePixel, DevicePoint};
#[derive(Clone)] #[derive(Clone)]
pub enum MouseWindowEvent { pub enum MouseWindowEvent {
@ -25,15 +23,6 @@ pub enum WebRenderDebugOption {
RenderTargetDebug, RenderTargetDebug,
} }
// TODO: this trait assumes that the window is responsible
// for creating the GL context, making it current, buffer
// swapping, etc. Really that should all be done by surfman.
pub trait WindowMethods {
/// Get the HighDPI factor of the native window, the screen and the framebuffer.
/// TODO(martin): Move this to `RendererWebView` when possible.
fn hidpi_factor(&self) -> Scale<f32, DeviceIndependentPixel, DevicePixel>;
}
pub trait EmbedderMethods { pub trait EmbedderMethods {
/// Returns a thread-safe object to wake up the window's event loop. /// Returns a thread-safe object to wake up the window's event loop.
fn create_event_loop_waker(&mut self) -> Box<dyn EventLoopWaker>; fn create_event_loop_waker(&mut self) -> Box<dyn EventLoopWaker>;

View file

@ -4588,9 +4588,12 @@ where
self.compositor_proxy self.compositor_proxy
.send(CompositorMsg::WebDriverMouseMoveEvent(webview_id, x, y)); .send(CompositorMsg::WebDriverMouseMoveEvent(webview_id, x, y));
}, },
WebDriverCommandMsg::TakeScreenshot(_, rect, response_sender) => { WebDriverCommandMsg::TakeScreenshot(webview_id, rect, response_sender) => {
self.compositor_proxy self.compositor_proxy.send(CompositorMsg::CreatePng(
.send(CompositorMsg::CreatePng(rect, response_sender)); webview_id,
rect,
response_sender,
));
}, },
} }
} }

View file

@ -5,18 +5,17 @@ use std::cell::RefCell;
use std::error::Error; use std::error::Error;
use std::rc::Rc; use std::rc::Rc;
use compositing::windowing::{EmbedderMethods, WindowMethods}; use compositing::windowing::EmbedderMethods;
use euclid::{Point2D, Scale, Size2D}; use euclid::{Scale, Size2D};
use servo::{ use servo::{
RenderingContext, Servo, TouchEventType, WebView, WebViewBuilder, WindowRenderingContext, RenderingContext, Servo, TouchEventType, WebView, WebViewBuilder, WindowRenderingContext,
}; };
use servo_geometry::DeviceIndependentPixel;
use tracing::warn; use tracing::warn;
use url::Url; use url::Url;
use webrender_api::ScrollLocation; use webrender_api::ScrollLocation;
use webrender_api::units::{DeviceIntPoint, DevicePixel, LayoutVector2D}; use webrender_api::units::{DeviceIntPoint, DevicePixel, LayoutVector2D};
use winit::application::ApplicationHandler; use winit::application::ApplicationHandler;
use winit::dpi::{PhysicalPosition, PhysicalSize}; use winit::dpi::PhysicalSize;
use winit::event::{MouseScrollDelta, WindowEvent}; use winit::event::{MouseScrollDelta, WindowEvent};
use winit::event_loop::EventLoop; use winit::event_loop::EventLoop;
use winit::raw_window_handle::{HasDisplayHandle, HasWindowHandle}; use winit::raw_window_handle::{HasDisplayHandle, HasWindowHandle};
@ -43,7 +42,7 @@ fn main() -> Result<(), Box<dyn Error>> {
} }
struct AppState { struct AppState {
window_delegate: Rc<WindowDelegate>, window: Window,
servo: Servo, servo: Servo,
rendering_context: Rc<WindowRenderingContext>, rendering_context: Rc<WindowRenderingContext>,
webviews: RefCell<Vec<WebView>>, webviews: RefCell<Vec<WebView>>,
@ -51,15 +50,17 @@ struct AppState {
impl ::servo::WebViewDelegate for AppState { impl ::servo::WebViewDelegate for AppState {
fn notify_new_frame_ready(&self, _: WebView) { fn notify_new_frame_ready(&self, _: WebView) {
self.window_delegate.window.request_redraw(); self.window.request_redraw();
} }
fn request_open_auxiliary_webview(&self, parent_webview: WebView) -> Option<WebView> { fn request_open_auxiliary_webview(&self, parent_webview: WebView) -> Option<WebView> {
let webview = WebViewBuilder::new_auxiliary(&self.servo) let webview = WebViewBuilder::new_auxiliary(&self.servo)
.hidpi_scale_factor(Scale::new(self.window.scale_factor() as f32))
.delegate(parent_webview.delegate()) .delegate(parent_webview.delegate())
.build(); .build();
webview.focus(); webview.focus();
webview.raise_to_top(true); webview.raise_to_top(true);
self.webviews.borrow_mut().push(webview.clone()); self.webviews.borrow_mut().push(webview.clone());
Some(webview) Some(webview)
} }
@ -91,7 +92,6 @@ impl ApplicationHandler<WakerEvent> for App {
WindowRenderingContext::new(display_handle, window_handle, window.inner_size()) WindowRenderingContext::new(display_handle, window_handle, window.inner_size())
.expect("Could not create RenderingContext for window."), .expect("Could not create RenderingContext for window."),
); );
let window_delegate = Rc::new(WindowDelegate::new(window));
let _ = rendering_context.make_current(); let _ = rendering_context.make_current();
@ -102,13 +102,12 @@ impl ApplicationHandler<WakerEvent> for App {
Box::new(EmbedderDelegate { Box::new(EmbedderDelegate {
waker: waker.clone(), waker: waker.clone(),
}), }),
window_delegate.clone(),
Default::default(), Default::default(),
); );
servo.setup_logging(); servo.setup_logging();
let app_state = Rc::new(AppState { let app_state = Rc::new(AppState {
window_delegate, window,
servo, servo,
rendering_context, rendering_context,
webviews: Default::default(), webviews: Default::default(),
@ -120,8 +119,10 @@ impl ApplicationHandler<WakerEvent> for App {
let webview = WebViewBuilder::new(&app_state.servo) let webview = WebViewBuilder::new(&app_state.servo)
.url(url) .url(url)
.hidpi_scale_factor(Scale::new(app_state.window.scale_factor() as f32))
.delegate(app_state.clone()) .delegate(app_state.clone())
.build(); .build();
webview.focus(); webview.focus();
webview.raise_to_top(true); webview.raise_to_top(true);
@ -239,26 +240,6 @@ impl embedder_traits::EventLoopWaker for Waker {
} }
} }
struct WindowDelegate {
window: Window,
}
impl WindowDelegate {
fn new(window: Window) -> Self {
Self { window }
}
}
impl WindowMethods for WindowDelegate {
fn hidpi_factor(&self) -> Scale<f32, DeviceIndependentPixel, DevicePixel> {
Scale::new(self.window.scale_factor() as f32)
}
}
pub fn winit_size_to_euclid_size<T>(size: PhysicalSize<T>) -> Size2D<T, DevicePixel> { pub fn winit_size_to_euclid_size<T>(size: PhysicalSize<T>) -> Size2D<T, DevicePixel> {
Size2D::new(size.width, size.height) Size2D::new(size.width, size.height)
} }
pub fn winit_position_to_euclid_point<T>(position: PhysicalPosition<T>) -> Point2D<T, DevicePixel> {
Point2D::new(position.x, position.y)
}

View file

@ -42,7 +42,7 @@ use canvas::WebGLComm;
use canvas::canvas_paint_thread::CanvasPaintThread; use canvas::canvas_paint_thread::CanvasPaintThread;
use canvas_traits::webgl::{GlType, WebGLThreads}; use canvas_traits::webgl::{GlType, WebGLThreads};
use clipboard_delegate::StringRequest; use clipboard_delegate::StringRequest;
use compositing::windowing::{EmbedderMethods, WindowMethods}; use compositing::windowing::EmbedderMethods;
use compositing::{IOCompositor, InitialCompositorState}; use compositing::{IOCompositor, InitialCompositorState};
pub use compositing_traits::rendering_context::{ pub use compositing_traits::rendering_context::{
OffscreenRenderingContext, RenderingContext, SoftwareRenderingContext, WindowRenderingContext, OffscreenRenderingContext, RenderingContext, SoftwareRenderingContext, WindowRenderingContext,
@ -186,11 +186,8 @@ mod media_platform {
/// orchestrating the interaction between JavaScript, CSS layout, /// orchestrating the interaction between JavaScript, CSS layout,
/// rendering, and the client window. /// rendering, and the client window.
/// ///
/// Clients create a `Servo` instance for a given reference-counted type // Clients create an event loop to pump messages between the embedding
/// implementing `WindowMethods`, which is the bridge to whatever // application and various browser components.
/// application Servo is embedded in. Clients then create an event
/// loop to pump messages between the embedding application and
/// various browser components.
pub struct Servo { pub struct Servo {
delegate: RefCell<Rc<dyn ServoDelegate>>, delegate: RefCell<Rc<dyn ServoDelegate>>,
compositor: Rc<RefCell<IOCompositor>>, compositor: Rc<RefCell<IOCompositor>>,
@ -250,7 +247,7 @@ impl Servo {
#[cfg_attr( #[cfg_attr(
feature = "tracing", feature = "tracing",
tracing::instrument( tracing::instrument(
skip(preferences, rendering_context, embedder, window), skip(preferences, rendering_context, embedder),
fields(servo_profiling = true), fields(servo_profiling = true),
level = "trace", level = "trace",
) )
@ -260,7 +257,6 @@ impl Servo {
preferences: Preferences, preferences: Preferences,
rendering_context: Rc<dyn RenderingContext>, rendering_context: Rc<dyn RenderingContext>,
mut embedder: Box<dyn EmbedderMethods>, mut embedder: Box<dyn EmbedderMethods>,
window: Rc<dyn WindowMethods>,
user_content_manager: UserContentManager, user_content_manager: UserContentManager,
) -> Self { ) -> Self {
// Global configuration options, parsed from the command line. // Global configuration options, parsed from the command line.
@ -484,7 +480,6 @@ impl Servo {
// rendered page and display it somewhere. // rendered page and display it somewhere.
let shutdown_state = Rc::new(Cell::new(ShutdownState::NotShuttingDown)); let shutdown_state = Rc::new(Cell::new(ShutdownState::NotShuttingDown));
let compositor = IOCompositor::new( let compositor = IOCompositor::new(
window,
InitialCompositorState { InitialCompositorState {
sender: compositor_proxy, sender: compositor_proxy,
receiver: compositor_receiver, receiver: compositor_receiver,
@ -625,10 +620,6 @@ impl Servo {
.retain(|_webview_id, webview| webview.strong_count() > 0); .retain(|_webview_id, webview| webview.strong_count() > 0);
} }
pub fn pinch_zoom_level(&self) -> f32 {
self.compositor.borrow_mut().pinch_zoom_level().get()
}
pub fn setup_logging(&self) { pub fn setup_logging(&self) {
let constellation_chan = self.constellation_proxy.sender(); let constellation_chan = self.constellation_proxy.sender();
let env = env_logger::Env::default(); let env = env_logger::Env::default();
@ -724,15 +715,7 @@ impl Servo {
let webview_id_and_viewport_details = webview let webview_id_and_viewport_details = webview
.delegate() .delegate()
.request_open_auxiliary_webview(webview) .request_open_auxiliary_webview(webview)
.map(|webview| { .map(|webview| (webview.id(), webview.viewport_details()));
let mut viewport =
self.compositor.borrow().default_webview_viewport_details();
let rect = webview.rect();
if !rect.is_empty() {
viewport.size = rect.size() / viewport.hidpi_scale_factor;
}
(webview.id(), viewport)
});
let _ = response_sender.send(webview_id_and_viewport_details); let _ = response_sender.send(webview_id_and_viewport_details);
} }
}, },

View file

@ -7,14 +7,12 @@ use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::atomic::{AtomicBool, Ordering};
use std::time::Duration; use std::time::Duration;
use compositing::windowing::{EmbedderMethods, WindowMethods}; use compositing::windowing::EmbedderMethods;
use compositing_traits::rendering_context::{RenderingContext, SoftwareRenderingContext}; use compositing_traits::rendering_context::{RenderingContext, SoftwareRenderingContext};
use dpi::PhysicalSize; use dpi::PhysicalSize;
use embedder_traits::EventLoopWaker; use embedder_traits::EventLoopWaker;
use euclid::Scale; use euclid::Scale;
use servo::Servo; use servo::Servo;
use servo_geometry::DeviceIndependentPixel;
use webrender_api::units::DevicePixel;
pub struct ServoTest { pub struct ServoTest {
servo: Servo, servo: Servo,
@ -39,13 +37,6 @@ impl ServoTest {
} }
} }
struct WindowMethodsImpl;
impl WindowMethods for WindowMethodsImpl {
fn hidpi_factor(&self) -> Scale<f32, DeviceIndependentPixel, DevicePixel> {
Scale::new(1.0)
}
}
#[derive(Clone)] #[derive(Clone)]
struct EventLoopWakerImpl(Arc<AtomicBool>); struct EventLoopWakerImpl(Arc<AtomicBool>);
impl EventLoopWaker for EventLoopWakerImpl { impl EventLoopWaker for EventLoopWakerImpl {
@ -64,7 +55,6 @@ impl ServoTest {
Default::default(), Default::default(),
rendering_context.clone(), rendering_context.clone(),
Box::new(EmbedderMethodsImpl(user_event_triggered)), Box::new(EmbedderMethodsImpl(user_event_triggered)),
Rc::new(WindowMethodsImpl),
Default::default(), Default::default(),
); );
Self { servo } Self { servo }

View file

@ -15,10 +15,13 @@ use constellation_traits::{EmbedderToConstellationMessage, TraversalDirection};
use dpi::PhysicalSize; use dpi::PhysicalSize;
use embedder_traits::{ use embedder_traits::{
Cursor, InputEvent, LoadStatus, MediaSessionActionType, ScreenGeometry, Theme, TouchEventType, Cursor, InputEvent, LoadStatus, MediaSessionActionType, ScreenGeometry, Theme, TouchEventType,
ViewportDetails,
}; };
use euclid::{Point2D, Scale, Size2D};
use servo_geometry::DeviceIndependentPixel;
use url::Url; use url::Url;
use webrender_api::ScrollLocation; use webrender_api::ScrollLocation;
use webrender_api::units::{DeviceIntPoint, DeviceRect}; use webrender_api::units::{DeviceIntPoint, DevicePixel, DeviceRect};
use crate::clipboard_delegate::{ClipboardDelegate, DefaultClipboardDelegate}; use crate::clipboard_delegate::{ClipboardDelegate, DefaultClipboardDelegate};
use crate::webview_delegate::{DefaultWebViewDelegate, WebViewDelegate}; use crate::webview_delegate::{DefaultWebViewDelegate, WebViewDelegate};
@ -75,6 +78,7 @@ pub(crate) struct WebViewInner {
pub(crate) clipboard_delegate: Rc<dyn ClipboardDelegate>, pub(crate) clipboard_delegate: Rc<dyn ClipboardDelegate>,
rect: DeviceRect, rect: DeviceRect,
hidpi_scale_factor: Scale<f32, DeviceIndependentPixel, DevicePixel>,
load_status: LoadStatus, load_status: LoadStatus,
url: Option<Url>, url: Option<Url>,
status_text: Option<String>, status_text: Option<String>,
@ -96,6 +100,17 @@ impl WebView {
pub(crate) fn new(builder: WebViewBuilder) -> Self { pub(crate) fn new(builder: WebViewBuilder) -> Self {
let id = WebViewId::new(); let id = WebViewId::new();
let servo = builder.servo; let servo = builder.servo;
let size = builder.size.map_or_else(
|| {
builder
.servo
.compositor
.borrow()
.rendering_context_size()
.to_f32()
},
|size| Size2D::new(size.width as f32, size.height as f32),
);
let webview = Self(Rc::new(RefCell::new(WebViewInner { let webview = Self(Rc::new(RefCell::new(WebViewInner {
id, id,
@ -103,7 +118,8 @@ impl WebView {
compositor: servo.compositor.clone(), compositor: servo.compositor.clone(),
delegate: builder.delegate, delegate: builder.delegate,
clipboard_delegate: Rc::new(DefaultClipboardDelegate), clipboard_delegate: Rc::new(DefaultClipboardDelegate),
rect: DeviceRect::zero(), rect: DeviceRect::from_origin_and_size(Point2D::origin(), size),
hidpi_scale_factor: builder.hidpi_scale_factor,
load_status: LoadStatus::Complete, load_status: LoadStatus::Complete,
url: None, url: None,
status_text: None, status_text: None,
@ -114,14 +130,14 @@ impl WebView {
cursor: Cursor::Pointer, cursor: Cursor::Pointer,
}))); })));
builder let viewport_details = webview.viewport_details();
.servo servo.compositor.borrow_mut().add_webview(
.compositor Box::new(ServoRendererWebView {
.borrow_mut()
.add_webview(Box::new(ServoRendererWebView {
weak_handle: webview.weak_handle(), weak_handle: webview.weak_handle(),
id, id,
})); }),
viewport_details,
);
servo servo
.webviews .webviews
@ -133,8 +149,8 @@ impl WebView {
Url::parse("about:blank").expect("Should always be able to parse 'about:blank'."), Url::parse("about:blank").expect("Should always be able to parse 'about:blank'."),
); );
let viewport_details = servo.compositor.borrow().default_webview_viewport_details(); builder
servo .servo
.constellation_proxy .constellation_proxy
.send(EmbedderToConstellationMessage::NewWebView( .send(EmbedderToConstellationMessage::NewWebView(
url.into(), url.into(),
@ -154,6 +170,17 @@ impl WebView {
self.0.borrow_mut() self.0.borrow_mut()
} }
pub(crate) fn viewport_details(&self) -> ViewportDetails {
// The division by 1 represents the page's default zoom of 100%,
// and gives us the appropriate CSSPixel type for the viewport.
let inner = self.inner();
let scaled_viewport_size = inner.rect.size() / inner.hidpi_scale_factor;
ViewportDetails {
size: scaled_viewport_size / Scale::new(1.0),
hidpi_scale_factor: Scale::new(inner.hidpi_scale_factor.0),
}
}
pub(crate) fn from_weak_handle(inner: &Weak<RefCell<WebViewInner>>) -> Option<Self> { pub(crate) fn from_weak_handle(inner: &Weak<RefCell<WebViewInner>>) -> Option<Self> {
inner.upgrade().map(WebView) inner.upgrade().map(WebView)
} }
@ -320,6 +347,25 @@ impl WebView {
.move_resize_webview(self.id(), rect); .move_resize_webview(self.id(), rect);
} }
pub fn hidpi_scale_factor(&self) -> Scale<f32, DeviceIndependentPixel, DevicePixel> {
self.inner().hidpi_scale_factor
}
pub fn set_hidpi_scale_factor(
&self,
new_scale_factor: Scale<f32, DeviceIndependentPixel, DevicePixel>,
) {
if self.inner().hidpi_scale_factor == new_scale_factor {
return;
}
self.inner_mut().hidpi_scale_factor = new_scale_factor;
self.inner()
.compositor
.borrow_mut()
.set_hidpi_scale_factor(self.id(), new_scale_factor);
}
pub fn show(&self, hide_others: bool) { pub fn show(&self, hide_others: bool) {
self.inner() self.inner()
.compositor .compositor
@ -437,14 +483,14 @@ impl WebView {
self.inner() self.inner()
.compositor .compositor
.borrow_mut() .borrow_mut()
.on_zoom_window_event(new_zoom); .on_zoom_window_event(self.id(), new_zoom);
} }
pub fn reset_zoom(&self) { pub fn reset_zoom(&self) {
self.inner() self.inner()
.compositor .compositor
.borrow_mut() .borrow_mut()
.on_zoom_reset_window_event(); .on_zoom_reset_window_event(self.id());
} }
pub fn set_pinch_zoom(&self, new_pinch_zoom: f32) { pub fn set_pinch_zoom(&self, new_pinch_zoom: f32) {
@ -535,6 +581,8 @@ pub struct WebViewBuilder<'servo> {
delegate: Rc<dyn WebViewDelegate>, delegate: Rc<dyn WebViewDelegate>,
auxiliary: bool, auxiliary: bool,
url: Option<Url>, url: Option<Url>,
size: Option<PhysicalSize<u32>>,
hidpi_scale_factor: Scale<f32, DeviceIndependentPixel, DevicePixel>,
} }
impl<'servo> WebViewBuilder<'servo> { impl<'servo> WebViewBuilder<'servo> {
@ -543,6 +591,8 @@ impl<'servo> WebViewBuilder<'servo> {
servo, servo,
auxiliary: false, auxiliary: false,
url: None, url: None,
size: None,
hidpi_scale_factor: Scale::new(1.0),
delegate: Rc::new(DefaultWebViewDelegate), delegate: Rc::new(DefaultWebViewDelegate),
} }
} }
@ -563,6 +613,19 @@ impl<'servo> WebViewBuilder<'servo> {
self self
} }
pub fn size(mut self, size: PhysicalSize<u32>) -> Self {
self.size = Some(size);
self
}
pub fn hidpi_scale_factor(
mut self,
hidpi_scale_factor: Scale<f32, DeviceIndependentPixel, DevicePixel>,
) -> Self {
self.hidpi_scale_factor = hidpi_scale_factor;
self
}
pub fn build(self) -> WebView { pub fn build(self) -> WebView {
WebView::new(self) WebView::new(self)
} }

View file

@ -70,7 +70,11 @@ pub enum CompositorMsg {
/// Script has handled a touch event, and either prevented or allowed default actions. /// Script has handled a touch event, and either prevented or allowed default actions.
TouchEventProcessed(WebViewId, TouchEventResult), TouchEventProcessed(WebViewId, TouchEventResult),
/// Composite to a PNG file and return the Image over a passed channel. /// Composite to a PNG file and return the Image over a passed channel.
CreatePng(Option<Rect<f32, CSSPixel>>, IpcSender<Option<Image>>), CreatePng(
WebViewId,
Option<Rect<f32, CSSPixel>>,
IpcSender<Option<Image>>,
),
/// A reply to the compositor asking if the output image is stable. /// A reply to the compositor asking if the output image is stable.
IsReadyToSaveImageReply(bool), IsReadyToSaveImageReply(bool),
/// Set whether to use less resources by stopping animations. /// Set whether to use less resources by stopping animations.

View file

@ -11,16 +11,12 @@ use std::rc::Rc;
use std::time::Instant; use std::time::Instant;
use std::{env, fs}; use std::{env, fs};
use euclid::Scale;
use log::{info, trace, warn}; use log::{info, trace, warn};
use servo::compositing::windowing::WindowMethods;
use servo::config::opts::Opts; use servo::config::opts::Opts;
use servo::config::prefs::Preferences; use servo::config::prefs::Preferences;
use servo::servo_config::pref; use servo::servo_config::pref;
use servo::servo_geometry::DeviceIndependentPixel;
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::webrender_api::units::DevicePixel;
use servo::webxr::glwindow::GlWindowDiscovery; use servo::webxr::glwindow::GlWindowDiscovery;
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
use servo::webxr::openxr::{AppInfo, OpenXrDiscovery}; use servo::webxr::openxr::{AppInfo, OpenXrDiscovery};
@ -139,15 +135,6 @@ impl App {
// Implements embedder methods, used by libservo and constellation. // Implements embedder methods, used by libservo and constellation.
let embedder = Box::new(EmbedderCallbacks::new(self.waker.clone(), xr_discovery)); let embedder = Box::new(EmbedderCallbacks::new(self.waker.clone(), xr_discovery));
// TODO: Remove this once dyn upcasting coercion stabilises
// <https://github.com/rust-lang/rust/issues/65991>
struct UpcastedWindow(Rc<dyn WindowPortsMethods>);
impl WindowMethods for UpcastedWindow {
fn hidpi_factor(&self) -> Scale<f32, DeviceIndependentPixel, DevicePixel> {
self.0.hidpi_factor()
}
}
let mut user_content_manager = UserContentManager::new(); let mut user_content_manager = UserContentManager::new();
for script in load_userscripts(self.servoshell_preferences.userscripts_directory.as_deref()) for script in load_userscripts(self.servoshell_preferences.userscripts_directory.as_deref())
.expect("Loading userscripts failed") .expect("Loading userscripts failed")
@ -160,7 +147,6 @@ impl App {
self.preferences.clone(), self.preferences.clone(),
window.rendering_context(), window.rendering_context(),
embedder, embedder,
Rc::new(UpcastedWindow(window.clone())),
user_content_manager, user_content_manager,
); );
servo.setup_logging(); servo.setup_logging();
@ -358,7 +344,7 @@ impl ApplicationHandler<WakerEvent> for App {
// Intercept any ScaleFactorChanged events away from EguiGlow::on_window_event, so // Intercept any ScaleFactorChanged events away from EguiGlow::on_window_event, so
// we can use our own logic for calculating the scale factor and set eguis // we can use our own logic for calculating the scale factor and set eguis
// scale factor to that value manually. // scale factor to that value manually.
let desired_scale_factor = window.hidpi_factor().get(); let desired_scale_factor = window.hidpi_scale_factor().get();
let effective_egui_zoom_factor = desired_scale_factor / scale_factor as f32; let effective_egui_zoom_factor = desired_scale_factor / scale_factor as f32;
info!( info!(
@ -371,6 +357,8 @@ impl ApplicationHandler<WakerEvent> for App {
.egui_ctx .egui_ctx
.set_zoom_factor(effective_egui_zoom_factor); .set_zoom_factor(effective_egui_zoom_factor);
state.hidpi_scale_factor_changed();
// Request a winit redraw event, so we can recomposite, update and paint // Request a winit redraw event, so we can recomposite, update and paint
// the minibrowser, and present the new frame. // the minibrowser, and present the new frame.
window.winit_window().unwrap().request_redraw(); window.winit_window().unwrap().request_redraw();

View file

@ -109,6 +109,7 @@ impl RunningAppState {
pub(crate) fn new_toplevel_webview(self: &Rc<Self>, url: Url) { pub(crate) fn new_toplevel_webview(self: &Rc<Self>, url: Url) {
let webview = WebViewBuilder::new(self.servo()) let webview = WebViewBuilder::new(self.servo())
.url(url) .url(url)
.hidpi_scale_factor(self.inner().window.hidpi_scale_factor())
.delegate(self.clone()) .delegate(self.clone())
.build(); .build();
@ -130,6 +131,14 @@ impl RunningAppState {
&self.servo &self.servo
} }
pub(crate) fn hidpi_scale_factor_changed(&self) {
let inner = self.inner();
let new_scale_factor = inner.window.hidpi_scale_factor();
for webview in inner.webviews.values() {
webview.set_hidpi_scale_factor(new_scale_factor);
}
}
pub(crate) fn save_output_image_if_necessary(&self) { pub(crate) fn save_output_image_if_necessary(&self) {
let Some(output_path) = self.servoshell_preferences.output_image_path.as_ref() else { let Some(output_path) = self.servoshell_preferences.output_image_path.as_ref() else {
return; return;
@ -462,6 +471,7 @@ impl WebViewDelegate for RunningAppState {
parent_webview: servo::WebView, parent_webview: servo::WebView,
) -> Option<servo::WebView> { ) -> Option<servo::WebView> {
let webview = WebViewBuilder::new_auxiliary(&self.servo) let webview = WebViewBuilder::new_auxiliary(&self.servo)
.hidpi_scale_factor(self.inner().window.hidpi_scale_factor())
.delegate(parent_webview.delegate()) .delegate(parent_webview.delegate())
.build(); .build();

View file

@ -14,7 +14,7 @@ use euclid::{Angle, Length, Point2D, Rotation3D, Scale, Size2D, UnknownUnit, Vec
use keyboard_types::{Modifiers, ShortcutMatcher}; use keyboard_types::{Modifiers, ShortcutMatcher};
use log::{debug, info}; use log::{debug, info};
use raw_window_handle::{HasDisplayHandle, HasWindowHandle, RawWindowHandle}; use raw_window_handle::{HasDisplayHandle, HasWindowHandle, RawWindowHandle};
use servo::compositing::windowing::{WebRenderDebugOption, WindowMethods}; use servo::compositing::windowing::WebRenderDebugOption;
use servo::servo_config::pref; use servo::servo_config::pref;
use servo::servo_geometry::DeviceIndependentPixel; use servo::servo_geometry::DeviceIndependentPixel;
use servo::webrender_api::ScrollLocation; use servo::webrender_api::ScrollLocation;
@ -252,7 +252,7 @@ impl Window {
/// Helper function to handle a click /// Helper function to handle a click
fn handle_mouse(&self, webview: &WebView, button: MouseButton, action: ElementState) { fn handle_mouse(&self, webview: &WebView, button: MouseButton, action: ElementState) {
let max_pixel_dist = 10.0 * self.hidpi_factor().get(); let max_pixel_dist = 10.0 * self.hidpi_scale_factor().get();
let mouse_button = match &button { let mouse_button = match &button {
MouseButton::Left => ServoMouseButton::Left, MouseButton::Left => ServoMouseButton::Left,
MouseButton::Right => ServoMouseButton::Right, MouseButton::Right => ServoMouseButton::Right,
@ -443,8 +443,11 @@ impl Window {
impl WindowPortsMethods for Window { impl WindowPortsMethods for Window {
fn screen_geometry(&self) -> ScreenGeometry { fn screen_geometry(&self) -> ScreenGeometry {
let hidpi_factor = self.hidpi_factor(); let hidpi_factor = self.hidpi_scale_factor();
let toolbar_size = Size2D::new(0.0, (self.toolbar_height.get() * self.hidpi_factor()).0); let toolbar_size = Size2D::new(
0.0,
(self.toolbar_height.get() * self.hidpi_scale_factor()).0,
);
let screen_size = self.screen_size.to_f32() * hidpi_factor; let screen_size = self.screen_size.to_f32() * hidpi_factor;
let available_screen_size = screen_size - toolbar_size; let available_screen_size = screen_size - toolbar_size;
@ -462,18 +465,18 @@ impl WindowPortsMethods for Window {
} }
} }
fn device_hidpi_factor(&self) -> Scale<f32, DeviceIndependentPixel, DevicePixel> { fn device_hidpi_scale_factor(&self) -> Scale<f32, DeviceIndependentPixel, DevicePixel> {
Scale::new(self.winit_window.scale_factor() as f32) Scale::new(self.winit_window.scale_factor() as f32)
} }
fn device_pixel_ratio_override( fn hidpi_scale_factor(&self) -> Scale<f32, DeviceIndependentPixel, DevicePixel> {
&self, self.device_pixel_ratio_override
) -> Option<Scale<f32, DeviceIndependentPixel, DevicePixel>> { .map(Scale::new)
self.device_pixel_ratio_override.map(Scale::new) .unwrap_or_else(|| self.device_hidpi_scale_factor())
} }
fn page_height(&self) -> f32 { fn page_height(&self) -> f32 {
let dpr = self.hidpi_factor(); let dpr = self.hidpi_scale_factor();
let size = self.winit_window.inner_size(); let size = self.winit_window.inner_size();
size.height as f32 * dpr.get() size.height as f32 * dpr.get()
} }
@ -483,7 +486,7 @@ impl WindowPortsMethods for Window {
} }
fn request_resize(&self, _: &WebView, size: DeviceIntSize) -> Option<DeviceIntSize> { fn request_resize(&self, _: &WebView, size: DeviceIntSize) -> Option<DeviceIntSize> {
let toolbar_height = self.toolbar_height() * self.hidpi_factor(); let toolbar_height = self.toolbar_height() * self.hidpi_scale_factor();
let toolbar_height = toolbar_height.get().ceil() as i32; let toolbar_height = toolbar_height.get().ceil() as i32;
let total_size = PhysicalSize::new(size.width, size.height + toolbar_height); let total_size = PhysicalSize::new(size.width, size.height + toolbar_height);
self.winit_window self.winit_window
@ -587,7 +590,7 @@ impl WindowPortsMethods for Window {
}, },
WindowEvent::CursorMoved { position, .. } => { WindowEvent::CursorMoved { position, .. } => {
let mut point = winit_position_to_euclid_point(position).to_f32(); let mut point = winit_position_to_euclid_point(position).to_f32();
point.y -= (self.toolbar_height() * self.hidpi_factor()).0; point.y -= (self.toolbar_height() * self.hidpi_scale_factor()).0;
self.webview_relative_mouse_point.set(point); self.webview_relative_mouse_point.set(point);
webview.notify_input_event(InputEvent::MouseMove(MouseMoveEvent { point })); webview.notify_input_event(InputEvent::MouseMove(MouseMoveEvent { point }));
@ -598,7 +601,7 @@ impl WindowPortsMethods for Window {
(dx as f64, (dy * LINE_HEIGHT) as f64, WheelMode::DeltaLine) (dx as f64, (dy * LINE_HEIGHT) as f64, WheelMode::DeltaLine)
}, },
MouseScrollDelta::PixelDelta(position) => { MouseScrollDelta::PixelDelta(position) => {
let scale_factor = self.device_hidpi_factor().inverse().get() as f64; let scale_factor = self.device_hidpi_scale_factor().inverse().get() as f64;
let position = position.to_logical(scale_factor); let position = position.to_logical(scale_factor);
(position.x, position.y, WheelMode::DeltaPixel) (position.x, position.y, WheelMode::DeltaPixel)
}, },
@ -728,7 +731,7 @@ impl WindowPortsMethods for Window {
// this prevents a crash in the compositor due to invalid surface size // this prevents a crash in the compositor due to invalid surface size
self.winit_window.set_min_inner_size(Some(PhysicalSize::new( self.winit_window.set_min_inner_size(Some(PhysicalSize::new(
1.0, 1.0,
1.0 + (self.toolbar_height() * self.hidpi_factor()).0, 1.0 + (self.toolbar_height() * self.hidpi_scale_factor()).0,
))); )));
} }
@ -761,13 +764,6 @@ impl WindowPortsMethods for Window {
} }
} }
impl WindowMethods for Window {
fn hidpi_factor(&self) -> Scale<f32, DeviceIndependentPixel, DevicePixel> {
self.device_pixel_ratio_override()
.unwrap_or_else(|| self.device_hidpi_factor())
}
}
fn winit_phase_to_touch_event_type(phase: TouchPhase) -> TouchEventType { fn winit_phase_to_touch_event_type(phase: TouchPhase) -> TouchEventType {
match phase { match phase {
TouchPhase::Started => TouchEventType::Down, TouchPhase::Started => TouchEventType::Down,

View file

@ -9,7 +9,6 @@ use std::rc::Rc;
use euclid::num::Zero; use euclid::num::Zero;
use euclid::{Length, Scale, Size2D}; use euclid::{Length, Scale, Size2D};
use servo::compositing::windowing::WindowMethods;
use servo::servo_geometry::DeviceIndependentPixel; use servo::servo_geometry::DeviceIndependentPixel;
use servo::webrender_api::units::{DeviceIntSize, DevicePixel}; use servo::webrender_api::units::{DeviceIntSize, DevicePixel};
use servo::{RenderingContext, ScreenGeometry, SoftwareRenderingContext}; use servo::{RenderingContext, ScreenGeometry, SoftwareRenderingContext};
@ -94,19 +93,18 @@ impl WindowPortsMethods for Window {
Some(new_size) Some(new_size)
} }
fn device_hidpi_factor(&self) -> Scale<f32, DeviceIndependentPixel, DevicePixel> { fn device_hidpi_scale_factor(&self) -> Scale<f32, DeviceIndependentPixel, DevicePixel> {
Scale::new(1.0) Scale::new(1.0)
} }
fn device_pixel_ratio_override( fn hidpi_scale_factor(&self) -> Scale<f32, DeviceIndependentPixel, DevicePixel> {
&self,
) -> Option<Scale<f32, DeviceIndependentPixel, DevicePixel>> {
self.device_pixel_ratio_override self.device_pixel_ratio_override
.unwrap_or_else(|| self.device_hidpi_scale_factor())
} }
fn page_height(&self) -> f32 { fn page_height(&self) -> f32 {
let height = self.inner_size.get().height; let height = self.inner_size.get().height;
let dpr = self.hidpi_factor(); let dpr = self.hidpi_scale_factor();
height as f32 * dpr.get() height as f32 * dpr.get()
} }
@ -145,10 +143,3 @@ impl WindowPortsMethods for Window {
self.rendering_context.clone() self.rendering_context.clone()
} }
} }
impl WindowMethods for Window {
fn hidpi_factor(&self) -> Scale<f32, DeviceIndependentPixel, DevicePixel> {
self.device_pixel_ratio_override()
.unwrap_or_else(|| self.device_hidpi_factor())
}
}

View file

@ -8,7 +8,6 @@
use std::rc::Rc; use std::rc::Rc;
use euclid::{Length, Scale}; use euclid::{Length, Scale};
use servo::compositing::windowing::WindowMethods;
use servo::servo_geometry::DeviceIndependentPixel; use servo::servo_geometry::DeviceIndependentPixel;
use servo::webrender_api::units::{DeviceIntPoint, DeviceIntSize, DevicePixel}; use servo::webrender_api::units::{DeviceIntPoint, DeviceIntSize, DevicePixel};
use servo::{Cursor, RenderingContext, ScreenGeometry, WebView}; use servo::{Cursor, RenderingContext, ScreenGeometry, WebView};
@ -18,13 +17,11 @@ use super::app_state::RunningAppState;
// This should vary by zoom level and maybe actual text size (focused or under cursor) // This should vary by zoom level and maybe actual text size (focused or under cursor)
pub const LINE_HEIGHT: f32 = 38.0; pub const LINE_HEIGHT: f32 = 38.0;
pub trait WindowPortsMethods: WindowMethods { pub trait WindowPortsMethods {
fn id(&self) -> winit::window::WindowId; fn id(&self) -> winit::window::WindowId;
fn screen_geometry(&self) -> ScreenGeometry; fn screen_geometry(&self) -> ScreenGeometry;
fn device_hidpi_factor(&self) -> Scale<f32, DeviceIndependentPixel, DevicePixel>; fn device_hidpi_scale_factor(&self) -> Scale<f32, DeviceIndependentPixel, DevicePixel>;
fn device_pixel_ratio_override( fn hidpi_scale_factor(&self) -> Scale<f32, DeviceIndependentPixel, DevicePixel>;
&self,
) -> Option<Scale<f32, DeviceIndependentPixel, DevicePixel>>;
fn page_height(&self) -> f32; fn page_height(&self) -> f32;
fn get_fullscreen(&self) -> bool; fn get_fullscreen(&self) -> bool;
fn handle_winit_event(&self, state: Rc<RunningAppState>, event: winit::event::WindowEvent); fn handle_winit_event(&self, state: Rc<RunningAppState>, event: winit::event::WindowEvent);

View file

@ -83,7 +83,6 @@ pub fn init(
let window_callbacks = Rc::new(ServoWindowCallbacks::new( let window_callbacks = Rc::new(ServoWindowCallbacks::new(
callbacks, callbacks,
RefCell::new(init_opts.coordinates), RefCell::new(init_opts.coordinates),
init_opts.density,
)); ));
let embedder_callbacks = Box::new(ServoEmbedderCallbacks::new( let embedder_callbacks = Box::new(ServoEmbedderCallbacks::new(
@ -97,13 +96,13 @@ pub fn init(
preferences, preferences,
rendering_context.clone(), rendering_context.clone(),
embedder_callbacks, embedder_callbacks,
window_callbacks.clone(),
Default::default(), Default::default(),
); );
APP.with(|app| { APP.with(|app| {
let app_state = RunningAppState::new( let app_state = RunningAppState::new(
init_opts.url, init_opts.url,
init_opts.density,
rendering_context, rendering_context,
servo, servo,
window_callbacks, window_callbacks,

View file

@ -11,7 +11,7 @@ use keyboard_types::{CompositionEvent, CompositionState};
use log::{debug, error, info, warn}; use log::{debug, error, info, warn};
use raw_window_handle::{RawWindowHandle, WindowHandle}; use raw_window_handle::{RawWindowHandle, WindowHandle};
use servo::base::id::WebViewId; use servo::base::id::WebViewId;
use servo::compositing::windowing::{EmbedderMethods, WindowMethods}; use servo::compositing::windowing::EmbedderMethods;
use servo::euclid::{Point2D, Rect, Scale, Size2D, Vector2D}; use servo::euclid::{Point2D, Rect, Scale, Size2D, Vector2D};
use servo::servo_geometry::DeviceIndependentPixel; use servo::servo_geometry::DeviceIndependentPixel;
use servo::webrender_api::ScrollLocation; use servo::webrender_api::ScrollLocation;
@ -45,19 +45,16 @@ impl Coordinates {
pub(super) struct ServoWindowCallbacks { pub(super) struct ServoWindowCallbacks {
host_callbacks: Box<dyn HostTrait>, host_callbacks: Box<dyn HostTrait>,
coordinates: RefCell<Coordinates>, coordinates: RefCell<Coordinates>,
hidpi_factor: Scale<f32, DeviceIndependentPixel, DevicePixel>,
} }
impl ServoWindowCallbacks { impl ServoWindowCallbacks {
pub(super) fn new( pub(super) fn new(
host_callbacks: Box<dyn HostTrait>, host_callbacks: Box<dyn HostTrait>,
coordinates: RefCell<Coordinates>, coordinates: RefCell<Coordinates>,
hidpi_factor: f32,
) -> Self { ) -> Self {
Self { Self {
host_callbacks, host_callbacks,
coordinates, coordinates,
hidpi_factor: Scale::new(hidpi_factor),
} }
} }
} }
@ -90,6 +87,9 @@ struct RunningAppStateInner {
/// Whether or not the animation state has changed. This is used to trigger /// Whether or not the animation state has changed. This is used to trigger
/// host callbacks indicating that animation state has changed. /// host callbacks indicating that animation state has changed.
animating_state_changed: Rc<Cell<bool>>, animating_state_changed: Rc<Cell<bool>>,
/// The HiDPI scaling factor to use for the display of [`WebView`]s.
hidpi_scale_factor: Scale<f32, DeviceIndependentPixel, DevicePixel>,
} }
struct ServoShellServoDelegate { struct ServoShellServoDelegate {
@ -115,7 +115,7 @@ impl ServoDelegate for ServoShellServoDelegate {
} }
impl WebViewDelegate for RunningAppState { impl WebViewDelegate for RunningAppState {
fn screen_geometry(&self, webview: WebView) -> Option<ScreenGeometry> { fn screen_geometry(&self, _webview: WebView) -> Option<ScreenGeometry> {
let coord = self.callbacks.coordinates.borrow(); let coord = self.callbacks.coordinates.borrow();
let screen_size = DeviceIntSize::new(coord.viewport.size.width, coord.viewport.size.height); let screen_size = DeviceIntSize::new(coord.viewport.size.width, coord.viewport.size.height);
Some(ScreenGeometry { Some(ScreenGeometry {
@ -214,6 +214,7 @@ impl WebViewDelegate for RunningAppState {
fn request_open_auxiliary_webview(&self, parent_webview: WebView) -> Option<WebView> { fn request_open_auxiliary_webview(&self, parent_webview: WebView) -> Option<WebView> {
let webview = WebViewBuilder::new_auxiliary(&self.servo) let webview = WebViewBuilder::new_auxiliary(&self.servo)
.delegate(parent_webview.delegate()) .delegate(parent_webview.delegate())
.hidpi_scale_factor(self.inner().hidpi_scale_factor)
.build(); .build();
self.add(webview.clone()); self.add(webview.clone());
Some(webview) Some(webview)
@ -275,6 +276,7 @@ impl WebViewDelegate for RunningAppState {
impl RunningAppState { impl RunningAppState {
pub(super) fn new( pub(super) fn new(
initial_url: Option<String>, initial_url: Option<String>,
hidpi_scale_factor: f32,
rendering_context: Rc<WindowRenderingContext>, rendering_context: Rc<WindowRenderingContext>,
servo: Servo, servo: Servo,
callbacks: Rc<ServoWindowCallbacks>, callbacks: Rc<ServoWindowCallbacks>,
@ -303,6 +305,7 @@ impl RunningAppState {
creation_order: vec![], creation_order: vec![],
focused_webview_id: None, focused_webview_id: None,
animating_state_changed, animating_state_changed,
hidpi_scale_factor: Scale::new(hidpi_scale_factor),
}), }),
}); });
@ -313,6 +316,7 @@ impl RunningAppState {
pub(crate) fn new_toplevel_webview(self: &Rc<Self>, url: Url) { pub(crate) fn new_toplevel_webview(self: &Rc<Self>, url: Url) {
let webview = WebViewBuilder::new(&self.servo) let webview = WebViewBuilder::new(&self.servo)
.url(url) .url(url)
.hidpi_scale_factor(self.inner().hidpi_scale_factor)
.delegate(self.clone()) .delegate(self.clone())
.build(); .build();
@ -715,9 +719,3 @@ impl EmbedderMethods for ServoEmbedderCallbacks {
} }
} }
} }
impl WindowMethods for ServoWindowCallbacks {
fn hidpi_factor(&self) -> Scale<f32, DeviceIndependentPixel, DevicePixel> {
self.hidpi_factor
}
}

View file

@ -124,7 +124,6 @@ pub fn init(
let window_callbacks = Rc::new(ServoWindowCallbacks::new( let window_callbacks = Rc::new(ServoWindowCallbacks::new(
callbacks, callbacks,
RefCell::new(coordinates), RefCell::new(coordinates),
options.display_density as f32,
)); ));
let embedder_callbacks = Box::new(ServoEmbedderCallbacks::new( let embedder_callbacks = Box::new(ServoEmbedderCallbacks::new(
@ -138,12 +137,12 @@ pub fn init(
preferences, preferences,
rendering_context.clone(), rendering_context.clone(),
embedder_callbacks, embedder_callbacks,
window_callbacks.clone(),
Default::default(), Default::default(),
); );
let app_state = RunningAppState::new( let app_state = RunningAppState::new(
Some(options.url), Some(options.url),
options.display_density as f32,
rendering_context, rendering_context,
servo, servo,
window_callbacks, window_callbacks,