Close Servo live sessions for removed tabs
This commit is contained in:
@@ -99,6 +99,10 @@ impl ServoLiveClient {
|
||||
self.request(LiveRequest::Poll { tab_id })
|
||||
}
|
||||
|
||||
pub fn close(&mut self, tab_id: String) -> Result<(), ServoLiveError> {
|
||||
self.request(LiveRequest::Close { tab_id }).map(|_| ())
|
||||
}
|
||||
|
||||
fn request(&mut self, request: LiveRequest) -> Result<Option<ServoLiveFrame>, ServoLiveError> {
|
||||
serde_json::to_writer(&mut self.stdin, &request)?;
|
||||
self.stdin.write_all(b"\n").map_err(ServoLiveError::Command)?;
|
||||
|
||||
@@ -27,6 +27,9 @@ pub(super) enum LiveRequest {
|
||||
Poll {
|
||||
tab_id: String,
|
||||
},
|
||||
Close {
|
||||
tab_id: String,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
@@ -156,3 +159,19 @@ pub(super) fn log_frame_perf(summary: &LiveFramePerfSummary) {
|
||||
"frame_perf",
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use serde_json::json;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn close_request_serializes_to_wire() -> Result<(), serde_json::Error> {
|
||||
let value =
|
||||
serde_json::to_value(LiveRequest::Close { tab_id: "tab-live-close".to_string() })?;
|
||||
|
||||
assert_eq!(value, json!({"type": "close", "tab_id": "tab-live-close"}));
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,6 +97,22 @@ impl WebSurfaceStore {
|
||||
result
|
||||
}
|
||||
|
||||
pub(super) fn retain_tabs(&mut self, open_tab_ids: &[TabId]) {
|
||||
let stale_tab_ids = self
|
||||
.surfaces
|
||||
.keys()
|
||||
.filter(|tab_id| !open_tab_ids.iter().any(|open_tab_id| open_tab_id == *tab_id))
|
||||
.cloned()
|
||||
.collect::<Vec<_>>();
|
||||
for tab_id in stale_tab_ids {
|
||||
self.close_surface(&tab_id);
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn close_surface(&mut self, tab_id: &TabId) {
|
||||
self.close_surface_for_tab(tab_id);
|
||||
}
|
||||
|
||||
pub(super) fn record_scroll_delta(
|
||||
&mut self,
|
||||
tab_id: &TabId,
|
||||
@@ -334,6 +350,14 @@ impl WebSurfaceStore {
|
||||
self.surfaces.entry(tab_id.clone()).or_insert_with(PerTabSurface::new)
|
||||
}
|
||||
|
||||
fn close_surface_for_tab(&mut self, tab_id: &TabId) {
|
||||
self.runtime.close_tab(tab_id);
|
||||
self.surfaces.remove(tab_id);
|
||||
if self.keyboard_focus.as_ref().is_some_and(|focus| focus.tab_id == *tab_id) {
|
||||
self.keyboard_focus = None;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub(super) fn surface_for_test(&self, tab_id: &TabId) -> Option<&PerTabSurface> {
|
||||
self.surfaces.get(tab_id)
|
||||
|
||||
@@ -51,12 +51,13 @@ impl ElyShell {
|
||||
}
|
||||
|
||||
pub(super) fn tick_external_web_surfaces(&mut self) -> bool {
|
||||
let visible_tab_ids = match &self.state {
|
||||
let (visible_tab_ids, open_tab_ids) = match &self.state {
|
||||
super::ShellState::Ready(core) => {
|
||||
core.visible_content_tab_ids().unwrap_or_else(|_| Vec::new())
|
||||
(core.visible_content_tab_ids().unwrap_or_else(|_| Vec::new()), core.open_tab_ids())
|
||||
}
|
||||
super::ShellState::StartupError(_) => Vec::new(),
|
||||
super::ShellState::StartupError(_) => (Vec::new(), Vec::new()),
|
||||
};
|
||||
self.web_surfaces.retain_tabs(&open_tab_ids);
|
||||
let result = self.web_surfaces.tick(&visible_tab_ids);
|
||||
let mut url_changed = false;
|
||||
for url_change in result.url_changes {
|
||||
|
||||
@@ -109,7 +109,10 @@ fn render_web_surface_frame(
|
||||
);
|
||||
store.ensure_surface(&tab, ProfileDataMode::Transient, &[]);
|
||||
|
||||
match wait_for_ready_frame(store, tab.id(), case) {
|
||||
let result = wait_for_ready_frame(store, tab.id(), case);
|
||||
store.close_surface(tab.id());
|
||||
|
||||
match result {
|
||||
Ok(frame) => return Ok(frame),
|
||||
Err(error) => last_error = error,
|
||||
}
|
||||
|
||||
@@ -149,6 +149,15 @@ impl WebSurfaceRuntime {
|
||||
frames
|
||||
}
|
||||
|
||||
pub(super) fn close_tab(&mut self, tab_id: &TabId) {
|
||||
let Some(session) = self.sessions.remove(tab_id) else {
|
||||
return;
|
||||
};
|
||||
if let Some(client) = self.clients.get_mut(&session.scope) {
|
||||
let _ = client.client.close(tab_id.as_str().to_string());
|
||||
}
|
||||
}
|
||||
|
||||
fn ensure_runtime(&mut self, scope: WebSurfaceRuntimeScope) -> Result<(), String> {
|
||||
if self.clients.contains_key(&scope) {
|
||||
return Ok(());
|
||||
@@ -200,6 +209,8 @@ trait LiveRuntimeClient {
|
||||
fn ensure(&mut self, request: ServoLiveEnsureRequest) -> Result<Option<WebLiveFrame>, String>;
|
||||
|
||||
fn poll(&mut self, tab_id: String) -> Result<Option<WebLiveFrame>, String>;
|
||||
|
||||
fn close(&mut self, tab_id: String) -> Result<(), String>;
|
||||
}
|
||||
|
||||
type WebLiveFrame = crate::services::servo_live::ServoLiveFrame;
|
||||
@@ -212,6 +223,10 @@ impl LiveRuntimeClient for ServoLiveClient {
|
||||
fn poll(&mut self, tab_id: String) -> Result<Option<WebLiveFrame>, String> {
|
||||
ServoLiveClient::poll(self, tab_id).map_err(|error| error.to_string())
|
||||
}
|
||||
|
||||
fn close(&mut self, tab_id: String) -> Result<(), String> {
|
||||
ServoLiveClient::close(self, tab_id).map_err(|error| error.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
fn new_servo_live_client(config_dir: PathBuf) -> Result<Box<dyn LiveRuntimeClient>, String> {
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
use std::collections::BTreeMap;
|
||||
use std::{
|
||||
collections::BTreeMap,
|
||||
sync::atomic::{AtomicUsize, Ordering},
|
||||
};
|
||||
|
||||
use ely_domain::{BrowserTab, ProfileId, SpaceId, TabId, UrlText};
|
||||
|
||||
@@ -12,6 +15,8 @@ use crate::{
|
||||
|
||||
use super::*;
|
||||
|
||||
static FAKE_CLOSE_COUNT: AtomicUsize = AtomicUsize::new(0);
|
||||
|
||||
#[test]
|
||||
fn runtime_keeps_independent_clients_for_profile_scopes() -> Result<(), String> {
|
||||
let mut runtime = WebSurfaceRuntime::new_with_client_factory(fake_client_factory);
|
||||
@@ -54,6 +59,23 @@ fn runtime_keeps_independent_clients_for_profile_scopes() -> Result<(), String>
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn close_tab_removes_session_and_closes_client() -> Result<(), String> {
|
||||
let before = FAKE_CLOSE_COUNT.load(Ordering::SeqCst);
|
||||
let mut runtime = WebSurfaceRuntime::new_with_client_factory(fake_client_factory);
|
||||
let tab = web_tab(TabId::new(), ProfileId::new(), "https://example.com/close")?;
|
||||
|
||||
runtime.ensure_tab(&tab, surface_size(), ProfileDataMode::Transient, &[], pending_input())?;
|
||||
runtime.close_tab(tab.id());
|
||||
|
||||
assert_eq!(runtime.session_scope_for_test(tab.id()), None);
|
||||
assert_eq!(FAKE_CLOSE_COUNT.load(Ordering::SeqCst), before + 1);
|
||||
|
||||
runtime.close_tab(tab.id());
|
||||
assert_eq!(FAKE_CLOSE_COUNT.load(Ordering::SeqCst), before + 1);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn session_scope_change_resets_tab_state() {
|
||||
let tab_id = TabId::new();
|
||||
@@ -88,6 +110,11 @@ impl LiveRuntimeClient for FakeLiveRuntimeClient {
|
||||
fn poll(&mut self, _tab_id: String) -> Result<Option<WebLiveFrame>, String> {
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
fn close(&mut self, _tab_id: String) -> Result<(), String> {
|
||||
FAKE_CLOSE_COUNT.fetch_add(1, Ordering::SeqCst);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn fake_client_factory(
|
||||
|
||||
Reference in New Issue
Block a user