Web pages: browsers are made at once, so none outlives its view (M-12)

make_browsers creates each browser synchronously and adopts it in the
same call, so no view waits on a browser: it leaves before creation
(skipped) or has a browser to close. The late-close branch in
on_after_created is gone. webprobe drops 5 views before the pump and
10 within 27 ms; webview.sh wants all 35 freed before the hold.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
ZacharyZhang-NYandClaude Opus 5.5 committed 2026-10-10 13:47:45 -04:00
1 parent c5cb153f72
commit 0d4dd64640
10 files changed
+50 -24

No files matched your search

+21 -2
View File
@@ -16,6 +16,10 @@ use gpui::{
/// How many views open and close to show their release.
const CHURN: usize = 20;
/// Views dropped before the pump makes their browsers.
const UNMADE: usize = 5;
/// Views dropped within 3 ms steps of opening.
const EARLY: u64 = 10;
struct Probe {
view: Entity<WebView>,
@@ -182,14 +186,29 @@ impl Probe {
/// Opens and drops pages; each must free its memory.
fn churn(&mut self, window: &mut Window, cx: &mut Context<Self>) {
let page = self.fixtures.join("probe.html");
if self.churned == CHURN {
println!("probe: closed {CHURN}");
for _ in 0..UNMADE {
drop(WebView::open(&page, &self.fixtures, window, cx).expect("a view"));
}
for step in 0..EARLY {
let view = WebView::open(&page, &self.fixtures, window, cx).expect("a view");
self.others.push(view.clone());
later(cx.weak_entity(), step * 3, window, cx, move |probe, _, _| {
probe.others.retain(|other| *other != view);
});
}
println!("probe: dropped {UNMADE} unmade, {EARLY} early");
let this = cx.weak_entity();
later(this, 2000, window, cx, |_, _, _| println!("probe: hold"));
later(this, 2000, window, cx, |_, _, _| {
// The log marks what freed before the hold.
log::info!("probe: hold");
println!("probe: hold");
});
return;
}
self.churned += 1;
let page = self.fixtures.join("probe.html");
let view = WebView::open(&page, &self.fixtures, window, cx).expect("a view");
cx.subscribe_in(&view, window, |probe, view, event, window, cx| {
if matches!(event, Event::Loaded) {
+11 -4
View File
@@ -201,7 +201,8 @@ fn make_browsers() {
windowless_frame_rate: 60,
..Default::default()
};
let made = browser_host_create_browser(
// Made at once: no view ever waits on a browser.
let made = browser_host_create_browser_sync(
Some(&info),
Some(&mut handlers::client(&surface)),
Some(&url.as_str().into()),
@@ -209,9 +210,15 @@ fn make_browsers() {
None,
None,
);
if made != 1 {
surface.lock().expect("the surface lock").failed =
Some(format!("CEF made no browser for {url}"));
match made {
Some(browser) => {
handlers::adopt(&browser, surface.clone());
surface.lock().expect("the surface lock").browser = Some(browser);
}
None => {
surface.lock().expect("the surface lock").failed =
Some(format!("CEF made no browser for {url}"))
}
}
}
}
+2 -13
View File
@@ -345,20 +345,9 @@ wrap_life_span_handler! {
impl LifeSpanHandler {
fn on_after_created(&self, browser: Option<&mut Browser>) {
let Some(browser) = browser.map(|b| b.clone()) else { return };
log::info!("web: browser {} made", browser.identifier());
// Its view went before it came: close it, keep nothing.
if self.surface.lock().expect("the surface lock").closed {
log::info!("web: browser {} closes: its view is gone", browser.identifier());
crate::engine::later(move || {
if let Some(host) = browser.host() {
host.close_browser(true as _);
}
});
return;
if let Some(browser) = browser {
log::info!("web: browser {} made", browser.identifier());
}
adopt(&browser, self.surface.clone());
self.surface.lock().expect("the surface lock").browser = Some(browser);
}
fn on_before_close(&self, browser: Option<&mut Browser>) {