ltk_webkit/wpe/
termination.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
// SPDX-License-Identifier: LGPL-2.1-only
// Copyright (C) 2026 Liberux Labs, S. L. <info@liberux.net>

//! A page losing its web process: WebKit's `web-process-terminated` signal, and the host's way to cause it.

use std::ffi::c_char;
use std::sync::{ Mutex, Weak };

use glib_sys::gpointer;
use gobject_sys::{ g_signal_connect_data, GObject };
use wpe_webkit_sys::*;

use super::{ popups, Inner, WebView };
use crate::types::Termination;

type TerminatedFn = unsafe extern "C" fn( *mut WebKitWebView, WebKitWebProcessTerminationReason, gpointer );

impl WebView
{
	/// Kill the page's web process, freeing its memory at once. Views related to this one share the process and lose it too.
	pub fn terminate_web_process( &self )
	{
		let Ok( g ) = self.inner.lock() else { return };
		let webview = g.webview;
		// The lock is released: the signal handler takes it again.
		drop( g );
		// SAFETY: `webview` is owned by `Inner`, which `self` keeps alive.
		if !webview.is_null() { unsafe { webkit_web_view_terminate_web_process( webview ); } }
	}

	/// Why the web process went away, once, if it did since the previous call.
	pub fn take_termination( &self ) -> Option<Termination>
	{
		self.inner.lock().ok().and_then( |mut g| g.termination.take() )
	}
}

pub( crate ) unsafe fn connect( webview: *mut WebKitWebView, inner: Weak<Mutex<Inner>> )
{
	// SAFETY: the handler matches the signal's signature; the closure owns its boxed `Weak`.
	unsafe
	{
		let handler = std::mem::transmute::<TerminatedFn, unsafe extern "C" fn()>( on_terminated );
		let data    = Box::into_raw( Box::new( inner ) ) as gpointer;
		g_signal_connect_data( webview as *mut GObject, b"web-process-terminated\0".as_ptr() as *const c_char, Some( handler ), data, Some( popups::drop_weak ), 0 );
	}
}

unsafe extern "C" fn on_terminated( _webview: *mut WebKitWebView, reason: WebKitWebProcessTerminationReason, user_data: gpointer )
{
	// SAFETY: `user_data` is the boxed `Weak` owned by the closure.
	let Some( inner ) = unsafe { &*( user_data as *const Weak<Mutex<Inner>> ) }.upgrade() else { return };
	let termination = match reason
	{
		WEBKIT_WEB_PROCESS_EXCEEDED_MEMORY_LIMIT => Termination::ExceededMemoryLimit,
		WEBKIT_WEB_PROCESS_TERMINATED_BY_API     => Termination::Requested,
		_                                        => Termination::Crashed,
	};
	if let Ok( mut g ) = inner.lock() { g.termination = Some( termination ); };
}