This commit is contained in:
2026-05-21 15:44:26 -04:00
parent 7fc2967793
commit aa8182bec4
11 changed files with 282 additions and 97 deletions
+34 -4
View File
@@ -147,6 +147,29 @@ impl RenderedFrame {
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct WebViewSnapshotPending {
has_pending_frame: bool,
has_pending_metadata: bool,
}
impl WebViewSnapshotPending {
#[must_use]
pub fn new(has_pending_frame: bool, has_pending_metadata: bool) -> Self {
Self { has_pending_frame, has_pending_metadata }
}
#[must_use]
pub fn has_pending_frame(&self) -> bool {
self.has_pending_frame
}
#[must_use]
pub fn has_pending_metadata(&self) -> bool {
self.has_pending_metadata
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct WebViewSnapshot {
webview_id: WebViewId,
@@ -155,7 +178,7 @@ pub struct WebViewSnapshot {
state: WebViewState,
url: Option<String>,
title: Option<String>,
has_pending_frame: bool,
pending: WebViewSnapshotPending,
}
impl WebViewSnapshot {
@@ -167,9 +190,9 @@ impl WebViewSnapshot {
state: WebViewState,
url: Option<String>,
title: Option<String>,
has_pending_frame: bool,
pending: WebViewSnapshotPending,
) -> Self {
Self { webview_id, tab_id, profile_id, state, url, title, has_pending_frame }
Self { webview_id, tab_id, profile_id, state, url, title, pending }
}
#[must_use]
@@ -204,7 +227,14 @@ impl WebViewSnapshot {
#[must_use]
pub fn has_pending_frame(&self) -> bool {
self.has_pending_frame
self.pending.has_pending_frame()
}
/// Returns true when URL, title, or load-state changed since the
/// last embedder snapshot observation.
#[must_use]
pub fn has_pending_metadata(&self) -> bool {
self.pending.has_pending_metadata()
}
}
+1 -1
View File
@@ -18,7 +18,7 @@ pub use host::{
HidpiScaleRequest, KeyboardTextRequest, MouseClickRequest, MouseDragRequest, MouseHoverRequest,
NavigationRequest, PageZoomRequest, PermissionDecision, PermissionRequest, RenderedFrame,
RenderedFrameSummary, ResizeRequest, ScrollRequest, ServoHost, TouchTapRequest,
WebViewSnapshot, WebViewState,
WebViewSnapshot, WebViewSnapshotPending, WebViewState,
};
#[cfg(feature = "servo-engine")]
pub use runtime::{RenderingContextKind, ServoSurfaceSize, SoftwareServoHost};
+49 -1
View File
@@ -45,6 +45,9 @@ pub struct SoftwareServoHost {
default_surface_size: ServoSurfaceSize,
rendering_context_kind: RenderingContextKind,
webviews: HashMap<WebViewId, HostWebView>,
// Servo 0.1.0 can still run script tasks after a remote page moves away.
// Retain hidden about:blank WebViews until host shutdown releases them together.
retired_webviews: Vec<HostWebView>,
permissions: PermissionStore,
wake_requested: Arc<AtomicBool>,
last_rendered_frame: Option<RenderedFrame>,
@@ -120,7 +123,27 @@ impl SoftwareServoHost {
}
pub fn close_webview(&mut self, webview_id: &WebViewId) -> bool {
self.webviews.remove(webview_id).is_some()
let Some(webview) = self.webviews.remove(webview_id) else {
return false;
};
self.retire_webview(webview);
true
}
fn retire_webview(&mut self, webview: HostWebView) {
webview.webview.hide();
if let Ok(blank_url) = Url::parse("about:blank") {
webview.webview.load(blank_url);
}
for _ in 0..32 {
self.servo.spin_event_loop();
if webview.current_url().as_deref() == Some("about:blank")
&& matches!(webview.state(), WebViewState::Complete)
{
break;
}
}
self.retired_webviews.push(webview);
}
fn new_started(
@@ -143,6 +166,7 @@ impl SoftwareServoHost {
default_surface_size: size,
rendering_context_kind,
webviews: HashMap::new(),
retired_webviews: Vec::new(),
permissions: Rc::new(RefCell::new(HashMap::new())),
wake_requested,
last_rendered_frame: None,
@@ -190,6 +214,24 @@ impl SoftwareServoHost {
self.last_rendered_frame = Some(rendered_frame);
Ok(())
}
/// Returns the current snapshot and acknowledges metadata-only
/// updates without clearing Servo's frame-ready signal.
pub fn snapshot_and_mark_metadata_observed(
&self,
webview_id: &WebViewId,
) -> Result<WebViewSnapshot, ServoHostError> {
let webview = self.webview(webview_id)?;
let snapshot = webview.snapshot(webview_id);
webview.delegate.mark_metadata_observed();
Ok(snapshot)
}
fn drain_after_webview_close(&self) {
for _ in 0..16 {
self.servo.spin_event_loop();
}
}
}
fn install_rustls_provider() {
@@ -395,6 +437,12 @@ impl ServoHost for SoftwareServoHost {
impl Drop for SoftwareServoHost {
fn drop(&mut self) {
let webviews = std::mem::take(&mut self.webviews);
for webview in webviews.into_values() {
self.retire_webview(webview);
}
self.retired_webviews.clear();
self.drain_after_webview_close();
SERVO_RUNTIME_STARTED.store(false, Ordering::Release);
}
}
+27 -10
View File
@@ -5,7 +5,7 @@ use servo::{LoadStatus, RenderingContext, WebView, WebViewDelegate};
use url::Url;
use crate::{
PermissionDecision, WebViewSnapshot, WebViewState,
PermissionDecision, WebViewSnapshot, WebViewSnapshotPending, WebViewState,
runtime_permissions::{PermissionStore, permission_decision_for_webview},
};
@@ -27,7 +27,10 @@ impl HostWebView {
self.state(),
self.current_url(),
self.current_title(),
self.delegate.has_pending_frame(),
WebViewSnapshotPending::new(
self.delegate.has_pending_frame(),
self.delegate.has_pending_metadata(),
),
)
}
@@ -62,6 +65,7 @@ pub(super) struct HostWebViewDelegate {
url: RefCell<Option<String>>,
title: RefCell<Option<String>>,
has_pending_frame: Cell<bool>,
has_pending_metadata: Cell<bool>,
}
impl HostWebViewDelegate {
@@ -73,6 +77,7 @@ impl HostWebViewDelegate {
url: RefCell::new(None),
title: RefCell::new(None),
has_pending_frame: Cell::new(false),
has_pending_metadata: Cell::new(false),
}
}
@@ -94,12 +99,12 @@ impl HostWebViewDelegate {
fn record_url_change(&self, url: String) {
self.url.replace(Some(url));
self.has_pending_frame.set(true);
self.has_pending_metadata.set(true);
}
fn record_title_change(&self, title: Option<String>) {
self.title.replace(title);
self.has_pending_frame.set(true);
self.has_pending_metadata.set(true);
}
fn record_load_status(&self, status: LoadStatus) {
@@ -108,16 +113,24 @@ impl HostWebViewDelegate {
LoadStatus::Complete => WebViewState::Complete,
};
self.set_state(state);
self.has_pending_frame.set(true);
self.has_pending_metadata.set(true);
}
pub(super) fn has_pending_frame(&self) -> bool {
self.has_pending_frame.get()
}
pub(super) fn has_pending_metadata(&self) -> bool {
self.has_pending_metadata.get()
}
pub(super) fn mark_frame_presented(&self) {
self.has_pending_frame.set(false);
}
pub(super) fn mark_metadata_observed(&self) {
self.has_pending_metadata.set(false);
}
}
impl WebViewDelegate for HostWebViewDelegate {
@@ -172,21 +185,25 @@ mod tests {
use super::HostWebViewDelegate;
#[test]
fn metadata_changes_mark_pending_frame() {
fn metadata_changes_are_separate_from_pending_frame() {
let delegate =
HostWebViewDelegate::new(ProfileId::new(), Rc::new(RefCell::new(HashMap::new())));
assert!(!delegate.has_pending_frame());
assert!(!delegate.has_pending_metadata());
delegate.record_title_change(Some("Example Domain".to_string()));
assert_eq!(delegate.title().as_deref(), Some("Example Domain"));
assert!(delegate.has_pending_frame());
delegate.mark_frame_presented();
assert!(!delegate.has_pending_frame());
assert!(delegate.has_pending_metadata());
delegate.mark_metadata_observed();
assert!(!delegate.has_pending_frame());
assert!(!delegate.has_pending_metadata());
delegate.record_url_change("https://example.com/".to_string());
assert_eq!(delegate.url().as_deref(), Some("https://example.com/"));
assert!(delegate.has_pending_frame());
assert!(!delegate.has_pending_frame());
assert!(delegate.has_pending_metadata());
}
}