diff --git a/.changes/refactor-windows-map.md b/.changes/refactor-windows-map.md new file mode 100644 index 000000000..753278125 --- /dev/null +++ b/.changes/refactor-windows-map.md @@ -0,0 +1,5 @@ +--- +"tauri-runtime-wry": minor +--- + +Changed `windows` map to be stored in a `RefCell` instead of a `Mutex`. diff --git a/core/tauri-runtime-wry/src/lib.rs b/core/tauri-runtime-wry/src/lib.rs index e269c1177..eb58fbb1c 100644 --- a/core/tauri-runtime-wry/src/lib.rs +++ b/core/tauri-runtime-wry/src/lib.rs @@ -81,6 +81,7 @@ pub use wry::application::platform::macos::{ }; use std::{ + cell::RefCell, collections::{ hash_map::Entry::{Occupied, Vacant}, HashMap, HashSet, @@ -90,7 +91,7 @@ use std::{ path::PathBuf, sync::{ mpsc::{channel, Sender}, - Arc, Mutex, MutexGuard, Weak, + Arc, Mutex, Weak, }, thread::{current as current_thread, ThreadId}, }; @@ -249,7 +250,7 @@ pub struct DispatcherMainThreadContext { pub global_shortcut_manager: Arc>, #[cfg(feature = "clipboard")] pub clipboard_manager: Arc>, - pub windows: Arc>>, + pub windows: Arc>>, #[cfg(all(desktop, feature = "system-tray"))] system_tray_manager: SystemTrayManager, } @@ -1757,7 +1758,7 @@ impl Wry { #[cfg(feature = "clipboard")] let clipboard_manager = Arc::new(Mutex::new(Clipboard::new())); - let windows = Arc::new(Mutex::new(HashMap::default())); + let windows = Arc::new(RefCell::new(HashMap::default())); let webview_id_map = WebviewIdStore::default(); #[cfg(all(desktop, feature = "system-tray"))] @@ -1888,8 +1889,7 @@ impl Runtime for Wry { .context .main_thread .windows - .lock() - .unwrap() + .borrow_mut() .insert(window_id, webview); Ok(DetachedWindow { @@ -2104,7 +2104,7 @@ impl Runtime for Wry { pub struct EventLoopIterationContext<'a, T: UserEvent> { pub callback: &'a mut (dyn FnMut(RunEvent) + 'static), pub webview_id_map: WebviewIdStore, - pub windows: Arc>>, + pub windows: Arc>>, #[cfg(all(desktop, feature = "global-shortcut"))] pub global_shortcut_manager: Arc>, #[cfg(all(desktop, feature = "global-shortcut"))] @@ -2116,12 +2116,12 @@ pub struct EventLoopIterationContext<'a, T: UserEvent> { } struct UserMessageContext { + windows: Arc>>, webview_id_map: WebviewIdStore, #[cfg(all(desktop, feature = "global-shortcut"))] global_shortcut_manager: Arc>, #[cfg(feature = "clipboard")] clipboard_manager: Arc>, - windows: Arc>>, #[cfg(all(desktop, feature = "system-tray"))] system_tray_manager: SystemTrayManager, } @@ -2146,12 +2146,7 @@ fn handle_user_message( Message::Task(task) => task(), Message::Window(id, window_message) => { if let WindowMessage::UpdateMenuItem(item_id, update) = window_message { - if let Some(menu_items) = windows - .lock() - .expect("poisoned webview collection") - .get_mut(&id) - .map(|w| &mut w.menu_items) - { + if let Some(menu_items) = windows.borrow_mut().get_mut(&id).map(|w| &mut w.menu_items) { if let Some(menu_items) = menu_items.as_mut() { let item = menu_items.get_mut(&item_id).expect("menu item not found"); match update { @@ -2166,17 +2161,14 @@ fn handle_user_message( } } } else { - let windows_lock = windows.lock().expect("poisoned webview collection"); - if let Some((Some(window), window_event_listeners, menu_event_listeners)) = - windows_lock.get(&id).map(|w| { - ( - w.inner.clone(), - w.window_event_listeners.clone(), - w.menu_event_listeners.clone(), - ) - }) - { - drop(windows_lock); + let w = windows.borrow().get(&id).map(|w| { + ( + w.inner.clone(), + w.window_event_listeners.clone(), + w.menu_event_listeners.clone(), + ) + }); + if let Some((Some(window), window_event_listeners, menu_event_listeners)) = w { match window_message { #[cfg(desktop)] WindowMessage::WithWebview(f) => { @@ -2369,11 +2361,8 @@ fn handle_user_message( } Message::Webview(id, webview_message) => match webview_message { WebviewMessage::EvaluateScript(script) => { - if let Some(WindowHandle::Webview(webview)) = windows - .lock() - .expect("poisoned webview collection") - .get(&id) - .and_then(|w| w.inner.as_ref()) + if let Some(WindowHandle::Webview(webview)) = + windows.borrow().get(&id).and_then(|w| w.inner.as_ref()) { if let Err(e) = webview.evaluate_script(&script) { debug_eprintln!("{}", e); @@ -2381,19 +2370,15 @@ fn handle_user_message( } } WebviewMessage::Print => { - if let Some(WindowHandle::Webview(webview)) = windows - .lock() - .expect("poisoned webview collection") - .get(&id) - .and_then(|w| w.inner.as_ref()) + if let Some(WindowHandle::Webview(webview)) = + windows.borrow().get(&id).and_then(|w| w.inner.as_ref()) { let _ = webview.print(); } } WebviewMessage::WebviewEvent(event) => { let window_event_listeners = windows - .lock() - .expect("poisoned webview collection") + .borrow() .get(&id) .map(|w| w.window_event_listeners.clone()); if let Some(window_event_listeners) = window_event_listeners { @@ -2409,10 +2394,7 @@ fn handle_user_message( }, Message::CreateWebview(window_id, handler) => match handler(event_loop, web_context) { Ok(webview) => { - windows - .lock() - .expect("poisoned webview collection") - .insert(window_id, webview); + windows.borrow_mut().insert(window_id, webview); } Err(e) => { debug_eprintln!("{}", e); @@ -2425,7 +2407,7 @@ fn handle_user_message( let w = Arc::new(window); - windows.lock().expect("poisoned webview collection").insert( + windows.borrow_mut().insert( window_id, WindowWrapper { label, @@ -2520,7 +2502,7 @@ fn handle_user_message( } let it = RunIteration { - window_count: windows.lock().expect("poisoned webview collection").len(), + window_count: windows.borrow().len(), }; it } @@ -2603,8 +2585,7 @@ fn handle_event_loop( *webview_id_map.0.lock().unwrap().values().next().unwrap() }; windows - .lock() - .unwrap() + .borrow() .get(&window_id) .unwrap() .menu_event_listeners @@ -2689,12 +2670,11 @@ fn handle_event_loop( // NOTE(amrbashir): we handle this event here instead of `match` statement below because // we want to focus the webview as soon as possible, especially on windows. if event == WryWindowEvent::Focused(true) { - if let Some(WindowHandle::Webview(webview)) = windows - .lock() - .expect("poisoned webview collection") + let w = windows + .borrow() .get(&window_id) - .and_then(|w| w.inner.as_ref()) - { + .and_then(|w| w.inner.clone()); + if let Some(WindowHandle::Webview(webview)) = w { // only focus the webview if the window is visible // somehow tao is sending a Focused(true) event even when the window is invisible, // which causes a deadlock: https://github.com/tauri-apps/tauri/issues/3534 @@ -2705,12 +2685,14 @@ fn handle_event_loop( } { - let windows_lock = windows.lock().expect("poisoned webview collection"); - if let Some(window) = windows_lock.get(&window_id) { + let windows_ref = windows.borrow(); + if let Some(window) = windows_ref.get(&window_id) { if let Some(event) = WindowEventWrapper::parse(&window.inner, &event).0 { let label = window.label.clone(); let window_event_listeners = window.window_event_listeners.clone(); - drop(windows_lock); + + drop(windows_ref); + callback(RunEvent::WindowEvent { label, event: event.clone(), @@ -2729,8 +2711,9 @@ fn handle_event_loop( on_close_requested(callback, window_id, windows.clone()); } WryWindowEvent::Destroyed => { - if windows.lock().unwrap().remove(&window_id).is_some() { - let is_empty = windows.lock().unwrap().is_empty(); + let removed = windows.borrow_mut().remove(&window_id).is_some(); + if removed { + let is_empty = windows.borrow().is_empty(); if is_empty { let (tx, rx) = channel(); callback(RunEvent::ExitRequested { tx }); @@ -2749,7 +2732,7 @@ fn handle_event_loop( } Event::UserEvent(message) => match message { Message::Window(id, WindowMessage::Close) => { - on_window_close(id, windows.lock().expect("poisoned webview collection")); + on_window_close(id, windows.clone()); } Message::UserEvent(t) => callback(RunEvent::UserEvent(t)), message => { @@ -2774,7 +2757,7 @@ fn handle_event_loop( } let it = RunIteration { - window_count: windows.lock().expect("poisoned webview collection").len(), + window_count: windows.borrow().len(), }; it } @@ -2782,14 +2765,16 @@ fn handle_event_loop( fn on_close_requested<'a, T: UserEvent>( callback: &'a mut (dyn FnMut(RunEvent) + 'static), window_id: WebviewId, - windows: Arc>>, + windows: Arc>>, ) { let (tx, rx) = channel(); - let windows_guard = windows.lock().expect("poisoned webview collection"); - if let Some(w) = windows_guard.get(&window_id) { + let windows_ref = windows.borrow(); + if let Some(w) = windows_ref.get(&window_id) { let label = w.label.clone(); let window_event_listeners = w.window_event_listeners.clone(); - drop(windows_guard); + + drop(windows_ref); + let listeners = window_event_listeners.lock().unwrap(); let handlers = listeners.values(); for handler in handlers { @@ -2803,19 +2788,13 @@ fn on_close_requested<'a, T: UserEvent>( }); if let Ok(true) = rx.try_recv() { } else { - on_window_close( - window_id, - windows.lock().expect("poisoned webview collection"), - ); + on_window_close(window_id, windows); } } } -fn on_window_close( - window_id: WebviewId, - mut windows: MutexGuard<'_, HashMap>, -) { - if let Some(mut window_wrapper) = windows.get_mut(&window_id) { +fn on_window_close(window_id: WebviewId, windows: Arc>>) { + if let Some(mut window_wrapper) = windows.borrow_mut().get_mut(&window_id) { window_wrapper.inner = None; } } @@ -2892,6 +2871,8 @@ fn create_webview( #[cfg(windows)] let proxy = context.proxy.clone(); + let window_event_listeners = WindowEventListeners::default(); + #[cfg(target_os = "macos")] { window_builder.inner = window_builder.inner.with_fullsize_content_view(true); @@ -2925,7 +2906,8 @@ fn create_webview( .unwrap() // safe to unwrap because we validate the URL beforehand .with_transparent(is_window_transparent); if webview_attributes.file_drop_handler_enabled { - webview_builder = webview_builder.with_file_drop_handler(create_file_drop_handler(&context)); + webview_builder = webview_builder + .with_file_drop_handler(create_file_drop_handler(window_event_listeners.clone())); } if let Some(handler) = ipc_handler { webview_builder = webview_builder.with_ipc_handler(create_ipc_handler( @@ -3025,7 +3007,7 @@ fn create_webview( label, inner: Some(WindowHandle::Webview(Arc::new(webview))), menu_items, - window_event_listeners: Default::default(), + window_event_listeners, menu_event_listeners: Default::default(), }) } @@ -3056,28 +3038,17 @@ fn create_ipc_handler( } /// Create a wry file drop handler. -fn create_file_drop_handler(context: &Context) -> Box { - let windows = context.main_thread.windows.clone(); - let webview_id_map = context.webview_id_map.clone(); - Box::new(move |window, event| { +fn create_file_drop_handler(window_event_listeners: WindowEventListeners) -> Box { + Box::new(move |_window, event| { let event: FileDropEvent = FileDropEventWrapper(event).into(); let window_event = WindowEvent::FileDrop(event); - let window_event_listeners = windows - .lock() - .unwrap() - .get(&webview_id_map.get(&window.id())) - .map(|w| w.window_event_listeners.clone()); - if let Some(window_event_listeners) = window_event_listeners { - let listeners_map = window_event_listeners.lock().unwrap(); - let has_listener = !listeners_map.is_empty(); - let handlers = listeners_map.values(); - for listener in handlers { - listener(&window_event); - } - // block the default OS action on file drop if we had a listener - has_listener - } else { - false + let listeners_map = window_event_listeners.lock().unwrap(); + let has_listener = !listeners_map.is_empty(); + let handlers = listeners_map.values(); + for listener in handlers { + listener(&window_event); } + // block the default OS action on file drop if we had a listener + has_listener }) }