libservo: Add an initial WebView data structure to the API (#35119)

This patch introduces a new handle-based webview API to libservo, with
two main design goals:

1. The lifetime of the handles controls the lifetime of the webview,
   giving the embedder full control over exactly when webviews are
   created and destroyed. This is consistent with how WebKitGTK’s
   WebView works; the engine can only create webviews via a create
   request, and can only destroy them via a close request.
2. All methods are infallible; if the constellation dies, the embedder
   finds out when calling Servo::handle_events.

For the moment, the embedder is only responsible for creating the
WebView id, and not the internal TopLevelBrowsingContext data
structures. This is so that the ScriptThread is able to get a handle on
the new WebView's WindowProxy in the case that it's an auxiliary
browsing context. In the future, the embedder should also be responsible
for creating the TopLevelBrowsingContext and the ScriptThread should
have mechanism to associate the two views so that WebView creation is
always executed through the same code path in the embedding layer. For
now, it's enough that the embedder can get a handle to the new WebView
when it's creation is requested.

Once we replace EmbedderMsg with a webview delegate trait, we will pass
WebView handles to the embedder, rather than webview ids. We’ll also add
detailed docs, once the design settles.

Signed-off-by: Delan Azabani <dazabani@igalia.com>
Co-authored-by: Martin Robinson <mrobinson@igalia.com>
This commit is contained in:
Delan Azabani 2025-01-25 16:17:50 +08:00 committed by GitHub
parent d5d7b0d34f
commit 2ce7709b8b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 348 additions and 104 deletions

View file

@ -156,7 +156,7 @@ pub struct IOCompositor {
ready_to_save_state: ReadyState,
/// The webrender renderer.
webrender: webrender::Renderer,
webrender: Option<webrender::Renderer>,
/// The active webrender document.
webrender_document: DocumentId,
@ -386,7 +386,7 @@ impl IOCompositor {
constellation_chan: state.constellation_chan,
time_profiler_chan: state.time_profiler_chan,
ready_to_save_state: ReadyState::Unknown,
webrender: state.webrender,
webrender: Some(state.webrender),
webrender_document: state.webrender_document,
webrender_api: state.webrender_api,
rendering_context: state.rendering_context,
@ -411,11 +411,13 @@ impl IOCompositor {
compositor
}
pub fn deinit(self) {
pub fn deinit(&mut self) {
if let Err(err) = self.rendering_context.make_current() {
warn!("Failed to make the rendering context current: {:?}", err);
}
self.webrender.deinit();
if let Some(webrender) = self.webrender.take() {
webrender.deinit();
}
}
fn update_cursor(&mut self, result: CompositorHitTestResult) {
@ -515,10 +517,12 @@ impl IOCompositor {
self.remove_webview(top_level_browsing_context_id);
},
// TODO: remove this
CompositorMsg::MoveResizeWebView(webview_id, rect) => {
self.move_resize_webview(webview_id, rect);
},
// TODO: remove this
CompositorMsg::ShowWebView(webview_id, hide_others) => {
if let Err(UnknownWebView(webview_id)) = self.show_webview(webview_id, hide_others)
{
@ -526,12 +530,14 @@ impl IOCompositor {
}
},
// TODO: remove this
CompositorMsg::HideWebView(webview_id) => {
if let Err(UnknownWebView(webview_id)) = self.hide_webview(webview_id) {
warn!("{webview_id}: HideWebView on unknown webview id");
}
},
// TODO: remove this
CompositorMsg::RaiseWebViewToTop(webview_id, hide_others) => {
if let Err(UnknownWebView(webview_id)) =
self.raise_webview_to_top(webview_id, hide_others)
@ -1941,7 +1947,8 @@ impl IOCompositor {
for id in self.pipeline_details.keys() {
if let Some(WebRenderEpoch(epoch)) = self
.webrender
.current_epoch(self.webrender_document, id.into())
.as_ref()
.and_then(|wr| wr.current_epoch(self.webrender_document, id.into()))
{
let epoch = Epoch(epoch);
pipeline_epochs.insert(*id, epoch);
@ -2016,7 +2023,9 @@ impl IOCompositor {
}
self.assert_no_gl_error();
self.webrender.update();
if let Some(webrender) = self.webrender.as_mut() {
webrender.update();
}
let wait_for_stable_image = matches!(
target,
@ -2073,7 +2082,9 @@ impl IOCompositor {
// Paint the scene.
// TODO(gw): Take notice of any errors the renderer returns!
self.clear_background();
self.webrender.render(size, 0 /* buffer_age */).ok();
if let Some(webrender) = self.webrender.as_mut() {
webrender.render(size, 0 /* buffer_age */).ok();
}
},
);
@ -2204,7 +2215,8 @@ impl IOCompositor {
for (pipeline_id, pending_epochs) in pending_paint_metrics.iter_mut() {
let Some(WebRenderEpoch(current_epoch)) = self
.webrender
.current_epoch(self.webrender_document, pipeline_id.into())
.as_ref()
.and_then(|wr| wr.current_epoch(self.webrender_document, pipeline_id.into()))
else {
continue;
};
@ -2427,7 +2439,10 @@ impl IOCompositor {
}
pub fn toggle_webrender_debug(&mut self, option: WebRenderDebugOption) {
let mut flags = self.webrender.get_debug_flags();
let Some(webrender) = self.webrender.as_mut() else {
return;
};
let mut flags = webrender.get_debug_flags();
let flag = match option {
WebRenderDebugOption::Profiler => {
webrender::DebugFlags::PROFILER_DBG |
@ -2438,7 +2453,7 @@ impl IOCompositor {
WebRenderDebugOption::RenderTargetDebug => webrender::DebugFlags::RENDER_TARGET_DBG,
};
flags.toggle(flag);
self.webrender.set_debug_flags(flags);
webrender.set_debug_flags(flags);
let mut txn = Transaction::new();
self.generate_frame(&mut txn, RenderReasons::TESTING);