feat(callback_host): close helper page on Drop via browser WS

🤖 Generated with [Qoder][https://qoder.com]
This commit is contained in:
木炎
2026-04-14 09:09:59 +08:00
parent 32e2c59a40
commit adb64429ee

View File

@@ -324,6 +324,13 @@ impl BrowserCallbackExecutor for LiveBrowserCallbackHost {
impl Drop for LiveBrowserCallbackHost { impl Drop for LiveBrowserCallbackHost {
fn drop(&mut self) { fn drop(&mut self) {
// Best-effort: tell the browser to close the helper page tab.
close_helper_page(
&self.browser_ws_url,
self.host.helper_url(),
self.use_hidden_domain,
);
self.shutdown.store(true, Ordering::Relaxed); self.shutdown.store(true, Ordering::Relaxed);
if let Some(handle) = self.server_thread.lock().unwrap().take() { if let Some(handle) = self.server_thread.lock().unwrap().take() {
let _ = handle.join(); let _ = handle.join();
@@ -373,6 +380,46 @@ fn bootstrap_helper_page(
Ok(()) Ok(())
} }
/// Best-effort attempt to close the helper page tab via browser WebSocket.
/// Silently ignores all errors — this runs during Drop and must not panic.
fn close_helper_page(browser_ws_url: &str, helper_url: &str, use_hidden_domain: bool) {
let close_action = if use_hidden_domain {
"sgHideBrowerserClosePage"
} else {
"sgBrowserClosePage"
};
let result: Result<(), Box<dyn std::error::Error>> = (|| {
// Use a raw TcpStream with timeouts instead of tungstenite::connect
// which does not expose a connection timeout.
let addr = browser_ws_url
.trim_start_matches("ws://")
.trim_start_matches("wss://");
let stream = TcpStream::connect_timeout(
&addr.parse().map_err(|e| format!("addr parse: {e}"))?,
Duration::from_millis(100),
)?;
stream.set_read_timeout(Some(Duration::from_millis(200)))?;
stream.set_write_timeout(Some(Duration::from_millis(200)))?;
let (mut websocket, _) = tungstenite::client(
browser_ws_url,
stream,
)?;
websocket.send(Message::Text(
r#"{"type":"register","role":"web"}"#.to_string().into(),
))?;
// Drain the welcome prelude (best-effort, ignore timeout).
let _ = websocket.read();
let payload = json!([helper_url, close_action, helper_url]).to_string();
websocket.send(Message::Text(payload.into()))?;
Ok(())
})();
if let Err(err) = result {
eprintln!("close_helper_page best-effort failed (harmless): {err}");
}
}
fn recv_bootstrap_prelude( fn recv_bootstrap_prelude(
websocket: &mut tungstenite::WebSocket<tungstenite::stream::MaybeTlsStream<TcpStream>>, websocket: &mut tungstenite::WebSocket<tungstenite::stream::MaybeTlsStream<TcpStream>>,
) -> Result<(), PipeError> { ) -> Result<(), PipeError> {