From 5faa313a70d6ad47af18ad4b2f6517696118ef07 Mon Sep 17 00:00:00 2001 From: Kamil Jarosz Date: Thu, 17 Oct 2024 19:08:57 +0200 Subject: [PATCH] desktop: Close dialogs when the player is destroyed (#18238) * frontend_utils: Handle failures when sending SocketAction Instead of panicking, we now handle the failures by closing the connection. The failures are always logged as warnings. * desktop: Close dialogs when the player is destroyed This patch closes all dialogs upon destroying a player (e.g. by closing the file or opening a new one). Only dialogs that have a notifier are closed. --- desktop/src/app.rs | 2 +- desktop/src/gui.rs | 5 +++ desktop/src/gui/controller.rs | 6 +++ desktop/src/gui/dialogs.rs | 10 +++++ frontend-utils/src/backends/navigator.rs | 56 +++++++++++++----------- 5 files changed, 52 insertions(+), 27 deletions(-) diff --git a/desktop/src/app.rs b/desktop/src/app.rs index defe009ee5df..48b5b4c74a9c 100644 --- a/desktop/src/app.rs +++ b/desktop/src/app.rs @@ -555,7 +555,7 @@ impl ApplicationHandler for App { (Some(main_window), RuffleEvent::CloseFile) => { main_window.gui.window().set_title("Ruffle"); // Reset title since file has been closed. - main_window.player.destroy(); + main_window.gui.close_movie(&mut main_window.player); } (Some(main_window), RuffleEvent::EnterFullScreen) => { diff --git a/desktop/src/gui.rs b/desktop/src/gui.rs index 7d7a1892c8ce..819403b644d2 100644 --- a/desktop/src/gui.rs +++ b/desktop/src/gui.rs @@ -213,6 +213,11 @@ impl RuffleGui { self.context_menu.is_some() } + /// Notifies the GUI that the player has been destroyed. + fn on_player_destroyed(&mut self) { + self.dialogs.close_dialogs_with_notifiers(); + } + /// Notifies the GUI that a new player was created. fn on_player_created( &mut self, diff --git a/desktop/src/gui/controller.rs b/desktop/src/gui/controller.rs index e5f313de82f4..705138f75072 100644 --- a/desktop/src/gui/controller.rs +++ b/desktop/src/gui/controller.rs @@ -227,12 +227,18 @@ impl GuiController { response.consumed } + pub fn close_movie(&mut self, player: &mut PlayerController) { + player.destroy(); + self.gui.on_player_destroyed(); + } + pub fn create_movie( &mut self, player: &mut PlayerController, opt: LaunchOptions, movie_url: Url, ) { + self.close_movie(player); let movie_view = MovieView::new( self.movie_view_renderer.clone(), &self.descriptors.device, diff --git a/desktop/src/gui/dialogs.rs b/desktop/src/gui/dialogs.rs index a240e35a3753..929d621210f0 100644 --- a/desktop/src/gui/dialogs.rs +++ b/desktop/src/gui/dialogs.rs @@ -106,6 +106,16 @@ impl Dialogs { self.picker.clone() } + /// Close all dialogs that have someone waiting for an answer. + /// + /// This method may be used when the original receiver is closed, + /// e.g. by loading a new movie or destroying the existing one. + pub fn close_dialogs_with_notifiers(&mut self) { + self.network_access_dialog_queue.clear(); + self.filesystem_access_dialog = None; + self.filesystem_access_dialog_queue.clear(); + } + pub fn recreate_open_dialog( &mut self, opt: LaunchOptions, diff --git a/frontend-utils/src/backends/navigator.rs b/frontend-utils/src/backends/navigator.rs index f6a4c38bbe9e..a9c733c51e63 100644 --- a/frontend-utils/src/backends/navigator.rs +++ b/frontend-utils/src/backends/navigator.rs @@ -315,6 +315,18 @@ impl NavigatorBackend receiver: Receiver>, sender: Sender, ) { + /// Tries to send the given action properly handling failures. + /// + /// Returns `true` when the action has been sent properly, + /// `false` when the channel is closed. + async fn send_action(sender: &Sender, action: SocketAction) -> bool { + sender + .send(action) + .await + .inspect_err(|err| tracing::warn!("Failed to send SocketAction: {}", err)) + .is_ok() + } + let addr = format!("{}:{}", host, port); let is_allowed = self.socket_allowed.contains(&addr); let socket_mode = self.socket_mode; @@ -325,9 +337,8 @@ impl NavigatorBackend (false, SocketMode::Allow) | (true, _) => {} // the process is allowed to continue. just dont do anything. (false, SocketMode::Deny) => { // Just fail the connection. - sender - .try_send(SocketAction::Connect(handle, ConnectionState::Failed)) - .expect("working channel send"); + let action = SocketAction::Connect(handle, ConnectionState::Failed); + let _ = send_action(&sender, action).await; tracing::warn!( "SWF tried to open a socket, but opening a socket is not allowed" @@ -340,10 +351,8 @@ impl NavigatorBackend if !attempt_sandbox_connect { // fail the connection. - sender - .try_send(SocketAction::Connect(handle, ConnectionState::Failed)) - .expect("working channel send"); - + let action = SocketAction::Connect(handle, ConnectionState::Failed); + let _ = send_action(&sender, action).await; return; } } @@ -359,23 +368,21 @@ impl NavigatorBackend let mut stream = match TcpStream::connect((host, port)).or(timeout).await { Err(e) if e.kind() == ErrorKind::TimedOut => { warn!("Connection to {}:{} timed out", host2, port); - sender - .try_send(SocketAction::Connect(handle, ConnectionState::TimedOut)) - .expect("working channel send"); + let action = SocketAction::Connect(handle, ConnectionState::TimedOut); + let _ = send_action(&sender, action).await; return; } Ok(stream) => { - sender - .try_send(SocketAction::Connect(handle, ConnectionState::Connected)) - .expect("working channel send"); - + let action = SocketAction::Connect(handle, ConnectionState::Connected); + if !send_action(&sender, action).await { + return; + } stream } Err(err) => { warn!("Failed to connect to {}:{}, error: {}", host2, port, err); - sender - .try_send(SocketAction::Connect(handle, ConnectionState::Failed)) - .expect("working channel send"); + let action = SocketAction::Connect(handle, ConnectionState::Failed); + let _ = send_action(&sender, action).await; return; } }; @@ -392,17 +399,16 @@ impl NavigatorBackend match read.read(&mut buffer).await { Err(e) if e.kind() == ErrorKind::TimedOut => {} // try again later. Err(_) | Ok(0) => { - sender - .try_send(SocketAction::Close(handle)) - .expect("working channel send"); + let _ = send_action(&sender, SocketAction::Close(handle)).await; break; } Ok(read) => { let buffer = buffer.into_iter().take(read).collect::>(); - sender - .try_send(SocketAction::Data(handle, buffer)) - .expect("working channel send"); + let action = SocketAction::Data(handle, buffer); + if !send_action(&sender, action).await { + return; + } } }; } @@ -431,9 +437,7 @@ impl NavigatorBackend match write.write(&pending_write).await { Err(e) if e.kind() == ErrorKind::TimedOut => {} // try again later. Err(_) => { - sender2 - .try_send(SocketAction::Close(handle)) - .expect("working channel send"); + let _ = send_action(&sender2, SocketAction::Close(handle)).await; return; } Ok(written) => {