Skip to content

Commit 1063c48

Browse files
authored
fix(macos/ios): Add handler for web content process termination (fix #14371) (#14523)
1 parent 25e1f51 commit 1063c48

7 files changed

Lines changed: 115 additions & 0 deletions

File tree

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
"tauri": "minor:feat"
3+
"tauri-runtime": "minor:feat"
4+
"tauri-runtime-wry": "minor:feat"
5+
---
6+
7+
Add handler for web content process termination on macOS and iOS.

crates/tauri-runtime-wry/src/lib.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5054,6 +5054,35 @@ You may have it installed on another user account, but it is not available for t
50545054

50555055
webview_builder =
50565056
webview_builder.with_allow_link_preview(webview_attributes.allow_link_preview);
5057+
5058+
if let Some(on_web_content_process_terminate_handler) =
5059+
pending.on_web_content_process_terminate_handler
5060+
{
5061+
webview_builder = webview_builder
5062+
.with_on_web_content_process_terminate_handler(on_web_content_process_terminate_handler);
5063+
} else {
5064+
log::debug!("web content process terminated");
5065+
let context_ = context.clone();
5066+
let window_id_ = window_id.clone();
5067+
webview_builder = webview_builder.with_on_web_content_process_terminate_handler(move || {
5068+
if let Ok(windows) = &context_.main_thread.windows.0.try_borrow() {
5069+
if let Some(window) = windows.get(&*window_id_.lock().unwrap()) {
5070+
if let Some(webview) = window.webviews.iter().find(|w| w.id == id) {
5071+
match webview.reload() {
5072+
Ok(_) => log::debug!("webview reloaded"),
5073+
Err(e) => log::error!("failed to reload webview: {}", e),
5074+
}
5075+
} else {
5076+
log::error!("failed to find webview")
5077+
}
5078+
} else {
5079+
log::error!("failed to get window")
5080+
}
5081+
} else {
5082+
log::error!("failed to borrow windows")
5083+
}
5084+
});
5085+
}
50575086
}
50585087

50595088
#[cfg(target_os = "ios")]

crates/tauri-runtime/src/webview.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,9 @@ type DocumentTitleChangedHandler = dyn Fn(String) + Send + 'static;
4141

4242
type DownloadHandler = dyn Fn(DownloadEvent) -> bool + Send + Sync;
4343

44+
#[cfg(any(target_os = "macos", target_os = "ios"))]
45+
type OnWebContentProcessTerminateHandler = dyn Fn() + Send;
46+
4447
#[cfg(target_os = "ios")]
4548
type InputAccessoryViewBuilderFn = dyn Fn(&objc2_ui_kit::UIView) -> Option<objc2::rc::Retained<objc2_ui_kit::UIView>>
4649
+ Send
@@ -225,6 +228,9 @@ pub struct PendingWebview<T: UserEvent, R: Runtime<T>> {
225228
pub on_page_load_handler: Option<Box<OnPageLoadHandler>>,
226229

227230
pub download_handler: Option<Arc<DownloadHandler>>,
231+
232+
#[cfg(any(target_os = "macos", target_os = "ios"))]
233+
pub on_web_content_process_terminate_handler: Option<Box<OnWebContentProcessTerminateHandler>>,
228234
}
229235

230236
impl<T: UserEvent, R: Runtime<T>> PendingWebview<T, R> {
@@ -251,6 +257,8 @@ impl<T: UserEvent, R: Runtime<T>> PendingWebview<T, R> {
251257
web_resource_request_handler: None,
252258
on_page_load_handler: None,
253259
download_handler: None,
260+
#[cfg(any(target_os = "macos", target_os = "ios"))]
261+
on_web_content_process_terminate_handler: None,
254262
})
255263
}
256264
}

crates/tauri/src/app.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,9 @@ pub type SetupHook<R> =
6767
Box<dyn FnOnce(&mut App<R>) -> std::result::Result<(), Box<dyn std::error::Error>> + Send>;
6868
/// A closure that is run every time a page starts or finishes loading.
6969
pub type OnPageLoad<R> = dyn Fn(&Webview<R>, &PageLoadPayload<'_>) + Send + Sync + 'static;
70+
/// A closure that is run when the web content process terminates.
71+
#[cfg(any(target_os = "macos", target_os = "ios"))]
72+
pub type OnWebContentProcessTerminate<R> = dyn Fn(&Webview<R>) + Send + Sync + 'static;
7073
pub type ChannelInterceptor<R> =
7174
Box<dyn Fn(&Webview<R>, CallbackFn, usize, &InvokeResponseBody) -> bool + Send + Sync + 'static>;
7275

@@ -1483,6 +1486,10 @@ pub struct Builder<R: Runtime> {
14831486
/// Page load hook.
14841487
on_page_load: Option<Arc<OnPageLoad<R>>>,
14851488

1489+
/// Web content process termination hook.
1490+
#[cfg(any(target_os = "macos", target_os = "ios"))]
1491+
on_web_content_process_terminate: Option<Arc<OnWebContentProcessTerminate<R>>>,
1492+
14861493
/// All passed plugins
14871494
plugins: PluginStore<R>,
14881495

@@ -1569,6 +1576,8 @@ impl<R: Runtime> Builder<R> {
15691576
.into_string(),
15701577
channel_interceptor: None,
15711578
on_page_load: None,
1579+
#[cfg(any(target_os = "macos", target_os = "ios"))]
1580+
on_web_content_process_terminate: None,
15721581
plugins: PluginStore::default(),
15731582
uri_scheme_protocols: Default::default(),
15741583
state: StateManager::new(),
@@ -1749,6 +1758,23 @@ tauri::Builder::default()
17491758
self
17501759
}
17511760

1761+
/// Defines the web content process termination hook.
1762+
///
1763+
/// ## Platform-specific
1764+
///
1765+
/// - **Linux / Windows / Android:** Unsupported.
1766+
#[cfg(any(target_os = "macos", target_os = "ios"))]
1767+
#[must_use]
1768+
pub fn on_web_content_process_terminate<F>(mut self, on_web_content_process_terminate: F) -> Self
1769+
where
1770+
F: Fn(&Webview<R>) + Send + Sync + 'static,
1771+
{
1772+
self
1773+
.on_web_content_process_terminate
1774+
.replace(Arc::new(on_web_content_process_terminate));
1775+
self
1776+
}
1777+
17521778
/// Adds a Tauri application plugin.
17531779
///
17541780
/// A plugin is created using the [`crate::plugin::Builder`] struct.Check its documentation for more information.
@@ -2197,6 +2223,8 @@ tauri::Builder::default()
21972223
self.plugins,
21982224
self.invoke_handler,
21992225
self.on_page_load,
2226+
#[cfg(any(target_os = "macos", target_os = "ios"))]
2227+
self.on_web_content_process_terminate,
22002228
self.uri_scheme_protocols,
22012229
self.state,
22022230
#[cfg(desktop)]

crates/tauri/src/ipc/protocol.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -571,6 +571,8 @@ mod tests {
571571
PluginStore::default(),
572572
Box::new(|_| false),
573573
None,
574+
#[cfg(any(target_os = "macos", target_os = "ios"))]
575+
None,
574576
Default::default(),
575577
StateManager::new(),
576578
Default::default(),
@@ -687,6 +689,8 @@ mod tests {
687689
PluginStore::default(),
688690
Box::new(|_| false),
689691
None,
692+
#[cfg(any(target_os = "macos", target_os = "ios"))]
693+
None,
690694
Default::default(),
691695
StateManager::new(),
692696
Default::default(),

crates/tauri/src/manager/mod.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@ use crate::{
3131
Assets, Context, DebugAppIcon, EventName, Pattern, Runtime, StateManager, Webview, Window,
3232
};
3333

34+
#[cfg(any(target_os = "macos", target_os = "ios"))]
35+
use crate::app::OnWebContentProcessTerminate;
36+
3437
#[cfg(desktop)]
3538
mod menu;
3639
#[cfg(all(desktop, feature = "tray-icon"))]
@@ -251,6 +254,9 @@ impl<R: Runtime> AppManager<R> {
251254
plugins: PluginStore<R>,
252255
invoke_handler: Box<InvokeHandler<R>>,
253256
on_page_load: Option<Arc<OnPageLoad<R>>>,
257+
#[cfg(any(target_os = "macos", target_os = "ios"))] on_web_content_process_terminate: Option<
258+
Arc<OnWebContentProcessTerminate<R>>,
259+
>,
254260
uri_scheme_protocols: HashMap<String, Arc<webview::UriSchemeProtocol<R>>>,
255261
state: StateManager,
256262
#[cfg(desktop)] menu_event_listener: Vec<crate::app::GlobalMenuEventListener<AppHandle<R>>>,
@@ -284,6 +290,8 @@ impl<R: Runtime> AppManager<R> {
284290
webviews: Mutex::default(),
285291
invoke_handler,
286292
on_page_load,
293+
#[cfg(any(target_os = "macos", target_os = "ios"))]
294+
on_web_content_process_terminate,
287295
uri_scheme_protocols: Mutex::new(uri_scheme_protocols),
288296
event_listeners: Arc::new(webview_event_listeners),
289297
invoke_initialization_script,
@@ -756,6 +764,8 @@ mod test {
756764
PluginStore::default(),
757765
Box::new(|_| false),
758766
None,
767+
#[cfg(any(target_os = "macos", target_os = "ios"))]
768+
None,
759769
Default::default(),
760770
StateManager::new(),
761771
Default::default(),

crates/tauri/src/manager/webview.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ use crate::{
2828
EventLoopMessage, EventTarget, Manager, Runtime, Scopes, UriSchemeContext, Webview, Window,
2929
};
3030

31+
#[cfg(any(target_os = "macos", target_os = "ios"))]
32+
use crate::app::OnWebContentProcessTerminate;
33+
3134
use super::{
3235
window::{DragDropPayload, DRAG_DROP_EVENT, DRAG_ENTER_EVENT, DRAG_LEAVE_EVENT, DRAG_OVER_EVENT},
3336
{AppManager, EmitPayload},
@@ -70,6 +73,9 @@ pub struct WebviewManager<R: Runtime> {
7073
pub invoke_handler: Box<InvokeHandler<R>>,
7174
/// The page load hook, invoked when the webview performs a navigation.
7275
pub on_page_load: Option<Arc<OnPageLoad<R>>>,
76+
/// The web content process termination hook.
77+
#[cfg(any(target_os = "macos", target_os = "ios"))]
78+
pub on_web_content_process_terminate: Option<Arc<OnWebContentProcessTerminate<R>>>,
7379
/// The webview protocols available to all webviews.
7480
pub uri_scheme_protocols: Mutex<HashMap<String, Arc<UriSchemeProtocol<R>>>>,
7581
/// Webview event listeners to all webviews.
@@ -304,6 +310,29 @@ impl<R: Runtime> WebviewManager<R> {
304310
}
305311
}));
306312

313+
#[cfg(any(target_os = "macos", target_os = "ios"))]
314+
if pending.on_web_content_process_terminate_handler.is_none() {
315+
let app_manager_ = manager.manager_owned();
316+
if app_manager_
317+
.webview
318+
.on_web_content_process_terminate
319+
.is_some()
320+
{
321+
let label_ = pending.label.clone();
322+
pending
323+
.on_web_content_process_terminate_handler
324+
.replace(Box::new(move || {
325+
if let Some(w) = app_manager_.get_webview(&label_) {
326+
if let Some(on_web_content_process_terminate) =
327+
&app_manager_.webview.on_web_content_process_terminate
328+
{
329+
on_web_content_process_terminate(&w);
330+
}
331+
}
332+
}));
333+
}
334+
}
335+
307336
#[cfg(feature = "protocol-asset")]
308337
if !registered_scheme_protocols.contains(&"asset".into()) {
309338
let asset_scope = app_manager

0 commit comments

Comments
 (0)