ltk_webkit/wpe/print/
request.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
// SPDX-License-Identifier: LGPL-2.1-only
// Copyright (C) 2026 Liberux Labs, S. L. <info@liberux.net>

//! `window.print()` reaching the host instead of doing nothing, as it does in WPE.

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

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

use super::super::content_filter::user_content_manager;
use super::super::popups::drop_weak;
use super::super::Inner;

const REQUEST_JS: &[ u8 ] = concat!( include_str!( "request.js" ), "\0" ).as_bytes();
const MARK: &[ u8 ] = b"\x01ltk-print";

type ScriptDialogFn = unsafe extern "C" fn( *mut WebKitWebView, *mut WebKitScriptDialog, gpointer ) -> gboolean;

static INSTALL: Once = Once::new();

pub( in super::super ) unsafe fn connect( webview: *mut WebKitWebView, inner: Weak<Mutex<Inner>> )
{
	// SAFETY: the manager lives for the process and takes its own reference on the script, released here.
	INSTALL.call_once( || unsafe
	{
		let script = webkit_user_script_new(
			REQUEST_JS.as_ptr() as *const c_char,
			WEBKIT_USER_CONTENT_INJECT_ALL_FRAMES,
			WEBKIT_USER_SCRIPT_INJECT_AT_DOCUMENT_START,
			ptr::null(),
			ptr::null(),
		);
		if script.is_null() { return; }
		webkit_user_content_manager_add_script( user_content_manager(), script );
		webkit_user_script_unref( script );
	} );
	// SAFETY: the handler matches the signal's signature; the closure owns its boxed `Weak`.
	unsafe
	{
		let handler = std::mem::transmute::<ScriptDialogFn, unsafe extern "C" fn()>( on_script_dialog );
		let data    = Box::into_raw( Box::new( inner ) ) as gpointer;
		g_signal_connect_data( webview as *mut GObject, b"script-dialog\0".as_ptr() as *const c_char, Some( handler ), data, Some( drop_weak ), 0 );
	}
}

unsafe extern "C" fn on_script_dialog( _webview: *mut WebKitWebView, dialog: *mut WebKitScriptDialog, user_data: gpointer ) -> gboolean
{
	// SAFETY: WebKit hands a live dialog for the length of the emission; its message is owned by it.
	let ours = unsafe
	{
		webkit_script_dialog_get_dialog_type( dialog ) == WEBKIT_SCRIPT_DIALOG_PROMPT && {
			let message = webkit_script_dialog_get_message( dialog );
			!message.is_null() && CStr::from_ptr( message ).to_bytes() == MARK
		}
	};
	if !ours { return GFALSE; }
	// SAFETY: `user_data` is the boxed `Weak` owned by the closure.
	if let Some( inner ) = unsafe { &*( user_data as *const Weak<Mutex<Inner>> ) }.upgrade()
	{
		if let Ok( mut g ) = inner.lock() { g.print_requested = true; }
	}
	GTRUE
}