libservo: Create a WebViewBuilder class to construct WebViews (#36483)

This exposes a new method of creating `WebView`s using the Rust builder
pattern. This will be more important as we add more kinds of
configuration options for `WebView` such as size and HiDPI scaling.

Testing: The API currently doesn't have tests, but functionality is
ensured by the fact that servoshell is the test harness.
Signed-off-by: Martin Robinson <mrobinson@igalia.com>

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
This commit is contained in:
Martin Robinson 2025-04-12 20:59:16 +02:00 committed by GitHub
parent 2454e00a68
commit 084fe007a1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 100 additions and 51 deletions

View file

@ -7,7 +7,9 @@ use std::rc::Rc;
use compositing::windowing::{EmbedderMethods, WindowMethods};
use euclid::{Point2D, Scale, Size2D};
use servo::{RenderingContext, Servo, TouchEventType, WebView, WindowRenderingContext};
use servo::{
RenderingContext, Servo, TouchEventType, WebView, WebViewBuilder, WindowRenderingContext,
};
use servo_geometry::DeviceIndependentPixel;
use tracing::warn;
use url::Url;
@ -53,8 +55,9 @@ impl ::servo::WebViewDelegate for AppState {
}
fn request_open_auxiliary_webview(&self, parent_webview: WebView) -> Option<WebView> {
let webview = self.servo.new_auxiliary_webview();
webview.set_delegate(parent_webview.delegate());
let webview = WebViewBuilder::new_auxiliary(&self.servo)
.delegate(parent_webview.delegate())
.build();
webview.focus();
webview.raise_to_top(true);
self.webviews.borrow_mut().push(webview.clone());
@ -115,8 +118,10 @@ impl ApplicationHandler<WakerEvent> for App {
let url = Url::parse("https://demo.servo.org/experiments/twgl-tunnel/")
.expect("Guaranteed by argument");
let webview = app_state.servo.new_webview(url);
webview.set_delegate(app_state.clone());
let webview = WebViewBuilder::new(&app_state.servo)
.url(url)
.delegate(app_state.clone())
.build();
webview.focus();
webview.raise_to_top(true);

View file

@ -119,7 +119,7 @@ pub use {bluetooth, bluetooth_traits};
use crate::proxies::ConstellationProxy;
use crate::responders::ServoErrorChannel;
pub use crate::servo_delegate::{ServoDelegate, ServoError};
pub use crate::webview::WebView;
pub use crate::webview::{WebView, WebViewBuilder};
pub use crate::webview_delegate::{
AllowOrDenyRequest, AuthenticationRequest, FormControl, NavigationRequest, PermissionRequest,
SelectElement, WebResourceLoad, WebViewDelegate,
@ -661,29 +661,6 @@ impl Servo {
self.compositor.borrow_mut().deinit();
}
pub fn new_webview(&self, url: url::Url) -> WebView {
let webview = WebView::new(&self.constellation_proxy, self.compositor.clone());
self.webviews
.borrow_mut()
.insert(webview.id(), webview.weak_handle());
let viewport_details = self.compositor.borrow().default_webview_viewport_details();
self.constellation_proxy
.send(EmbedderToConstellationMessage::NewWebView(
url.into(),
webview.id(),
viewport_details,
));
webview
}
pub fn new_auxiliary_webview(&self) -> WebView {
let webview = WebView::new(&self.constellation_proxy, self.compositor.clone());
self.webviews
.borrow_mut()
.insert(webview.id(), webview.weak_handle());
webview
}
fn get_webview_handle(&self, id: WebViewId) -> Option<WebView> {
self.webviews
.borrow()

View file

@ -20,9 +20,9 @@ use url::Url;
use webrender_api::ScrollLocation;
use webrender_api::units::{DeviceIntPoint, DeviceRect};
use crate::ConstellationProxy;
use crate::clipboard_delegate::{ClipboardDelegate, DefaultClipboardDelegate};
use crate::webview_delegate::{DefaultWebViewDelegate, WebViewDelegate};
use crate::{ConstellationProxy, Servo};
/// A handle to a Servo webview. If you clone this handle, it does not create a new webview,
/// but instead creates a new handle to the webview. Once the last handle is dropped, Servo
@ -93,16 +93,15 @@ impl Drop for WebViewInner {
}
impl WebView {
pub(crate) fn new(
constellation_proxy: &ConstellationProxy,
compositor: Rc<RefCell<IOCompositor>>,
) -> Self {
pub(crate) fn new(builder: WebViewBuilder) -> Self {
let id = WebViewId::new();
let servo = builder.servo;
let webview = Self(Rc::new(RefCell::new(WebViewInner {
id,
constellation_proxy: constellation_proxy.clone(),
compositor: compositor.clone(),
delegate: Rc::new(DefaultWebViewDelegate),
constellation_proxy: servo.constellation_proxy.clone(),
compositor: servo.compositor.clone(),
delegate: builder.delegate,
clipboard_delegate: Rc::new(DefaultClipboardDelegate),
rect: DeviceRect::zero(),
load_status: LoadStatus::Complete,
@ -115,13 +114,35 @@ impl WebView {
cursor: Cursor::Pointer,
})));
compositor
builder
.servo
.compositor
.borrow_mut()
.add_webview(Box::new(ServoRendererWebView {
weak_handle: webview.weak_handle(),
id,
}));
servo
.webviews
.borrow_mut()
.insert(webview.id(), webview.weak_handle());
if !builder.auxiliary {
let url = builder.url.unwrap_or(
Url::parse("about:blank").expect("Should always be able to parse 'about:blank'."),
);
let viewport_details = servo.compositor.borrow().default_webview_viewport_details();
servo
.constellation_proxy
.send(EmbedderToConstellationMessage::NewWebView(
url.into(),
webview.id(),
viewport_details,
));
}
webview
}
@ -508,3 +529,41 @@ impl RendererWebView for ServoRendererWebView {
}
}
}
pub struct WebViewBuilder<'servo> {
servo: &'servo Servo,
delegate: Rc<dyn WebViewDelegate>,
auxiliary: bool,
url: Option<Url>,
}
impl<'servo> WebViewBuilder<'servo> {
pub fn new(servo: &'servo Servo) -> Self {
Self {
servo,
auxiliary: false,
url: None,
delegate: Rc::new(DefaultWebViewDelegate),
}
}
pub fn new_auxiliary(servo: &'servo Servo) -> Self {
let mut builder = Self::new(servo);
builder.auxiliary = true;
builder
}
pub fn delegate(mut self, delegate: Rc<dyn WebViewDelegate>) -> Self {
self.delegate = delegate;
self
}
pub fn url(mut self, url: Url) -> Self {
self.url = Some(url);
self
}
pub fn build(self) -> WebView {
WebView::new(self)
}
}

View file

@ -19,7 +19,7 @@ use servo::webrender_api::units::{DeviceIntPoint, DeviceIntRect, DeviceIntSize};
use servo::{
AllowOrDenyRequest, AuthenticationRequest, FilterPattern, FormControl, GamepadHapticEffectType,
LoadStatus, PermissionRequest, Servo, ServoDelegate, ServoError, SimpleDialog, TouchEventType,
WebView, WebViewDelegate,
WebView, WebViewBuilder, WebViewDelegate,
};
use url::Url;
@ -107,8 +107,10 @@ impl RunningAppState {
}
pub(crate) fn new_toplevel_webview(self: &Rc<Self>, url: Url) {
let webview = self.servo().new_webview(url);
webview.set_delegate(self.clone());
let webview = WebViewBuilder::new(self.servo())
.url(url)
.delegate(self.clone())
.build();
webview.focus();
webview.raise_to_top(true);
@ -459,8 +461,9 @@ impl WebViewDelegate for RunningAppState {
&self,
parent_webview: servo::WebView,
) -> Option<servo::WebView> {
let webview = self.servo.new_auxiliary_webview();
webview.set_delegate(parent_webview.delegate());
let webview = WebViewBuilder::new_auxiliary(&self.servo)
.delegate(parent_webview.delegate())
.build();
webview.focus();
webview.raise_to_top(true);

View file

@ -21,8 +21,8 @@ use servo::{
InputMethodType, Key, KeyState, KeyboardEvent, LoadStatus, MediaSessionActionType,
MediaSessionEvent, MouseButton, MouseButtonAction, MouseButtonEvent, MouseMoveEvent,
NavigationRequest, PermissionRequest, RenderingContext, ScreenGeometry, Servo, ServoDelegate,
ServoError, SimpleDialog, TouchEvent, TouchEventType, TouchId, WebView, WebViewDelegate,
WindowRenderingContext,
ServoError, SimpleDialog, TouchEvent, TouchEventType, TouchId, WebView, WebViewBuilder,
WebViewDelegate, WindowRenderingContext,
};
use url::Url;
@ -211,10 +211,12 @@ impl WebViewDelegate for RunningAppState {
}
}
fn request_open_auxiliary_webview(&self, _parent_webview: WebView) -> Option<WebView> {
let new_webview = self.servo.new_auxiliary_webview();
self.add(new_webview.clone());
Some(new_webview)
fn request_open_auxiliary_webview(&self, parent_webview: WebView) -> Option<WebView> {
let webview = WebViewBuilder::new_auxiliary(&self.servo)
.delegate(parent_webview.delegate())
.build();
self.add(webview.clone());
Some(webview)
}
fn request_permission(&self, webview: WebView, request: PermissionRequest) {
@ -309,8 +311,11 @@ impl RunningAppState {
}
pub(crate) fn new_toplevel_webview(self: &Rc<Self>, url: Url) {
let webview = self.servo.new_webview(url);
webview.set_delegate(self.clone());
let webview = WebViewBuilder::new(&self.servo)
.url(url)
.delegate(self.clone())
.build();
webview.focus();
self.add(webview.clone());
}