ltk_webkit/wpe/
session.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
61
62
63
64
65
66
67
68
69
70
// SPDX-License-Identifier: LGPL-2.1-only
// Copyright (C) 2026 Liberux Labs, S. L. <info@liberux.net>

//! The network session every [`crate::WebView`] loads through, and where it keeps cookies and website data.

use std::ffi::{ CStr, CString, OsStr };
use std::fs;
use std::os::unix::ffi::OsStrExt;
use std::path::Path;
use std::sync::OnceLock;

use wpe_webkit_sys::*;

static DIRECTORIES: OnceLock<( CString, CString )> = OnceLock::new();
static SESSION:     OnceLock<usize>                = OnceLock::new();

/// Keep website data and cookies in `data` and caches in `cache`. Unset, WebKit names its directories after the program, so every WPE application without a name of its own shares them. Call it before anything else in this crate; a later call is ignored.
pub fn set_data_directories( data: &Path, cache: &Path )
{
	let ( Ok( data ), Ok( cache ) ) = ( CString::new( data.as_os_str().as_bytes() ), CString::new( cache.as_os_str().as_bytes() ) ) else { return };
	let _ = DIRECTORIES.set( ( data, cache ) );
}

/// Resolve `host` ahead of a load that is likely to follow, such as the address being typed.
pub fn prefetch_dns( host: &str )
{
	let Ok( host ) = CString::new( host ) else { return };
	let session = network_session();
	if session.is_null() { return; }
	// SAFETY: WebKit copies the name; the session lives for the process.
	unsafe { webkit_network_session_prefetch_dns( session, host.as_ptr() ); }
}

/// Lives for the process.
pub( crate ) fn network_session() -> *mut WebKitNetworkSession
{
	*SESSION.get_or_init( ||
	{
		// SAFETY: WebKit copies the directory names; the session is never released.
		unsafe
		{
			let session = match DIRECTORIES.get()
			{
				Some( ( data, cache ) ) => webkit_network_session_new( data.as_ptr(), cache.as_ptr() ),
				None                    => webkit_network_session_get_default(),
			};
			keep_cookies( session );
			session as usize
		}
	} ) as *mut WebKitNetworkSession
}

/// A session keeps its cookies in memory, and loses them on exit, until it is given a file.
unsafe fn keep_cookies( session: *mut WebKitNetworkSession )
{
	// SAFETY: `session` is alive; the directory string belongs to its data manager.
	unsafe
	{
		if session.is_null() || webkit_network_session_is_ephemeral( session ) != 0 { return; }
		let manager = webkit_network_session_get_website_data_manager( session );
		if manager.is_null() { return; }
		let directory = webkit_website_data_manager_get_base_data_directory( manager );
		if directory.is_null() { return; }
		let directory = Path::new( OsStr::from_bytes( CStr::from_ptr( directory ).to_bytes() ) );
		if fs::create_dir_all( directory ).is_err() { return; }
		let Ok( file ) = CString::new( directory.join( "cookies.sqlite" ).as_os_str().as_bytes() ) else { return };
		let cookies = webkit_network_session_get_cookie_manager( session );
		if !cookies.is_null() { webkit_cookie_manager_set_persistent_storage( cookies, file.as_ptr(), WEBKIT_COOKIE_PERSISTENT_STORAGE_SQLITE ); }
	}
}