ltk_webkit/wpe/
scripting.rsuse std::ffi::{ c_char, c_uint, c_void, CString };
use std::ptr;
use gio::ffi::GAsyncResult;
use glib_sys::{ g_error_free, g_free, gpointer, GError };
use gobject_sys::{ g_object_unref, GObject };
use wpe_webkit_sys::*;
use super::browsing::owned;
use super::WebView;
#[ repr( C ) ]
pub( super ) struct JSCValue
{
_private: [ u8; 0 ],
}
extern "C"
{
fn webkit_web_view_evaluate_javascript_finish( web_view: *mut WebKitWebView, result: *mut GAsyncResult, error: *mut *mut GError ) -> *mut c_void;
pub( super ) fn webkit_web_view_call_async_javascript_function_finish( web_view: *mut WebKitWebView, result: *mut GAsyncResult, error: *mut *mut GError ) -> *mut JSCValue;
fn jsc_value_to_json( value: *mut c_void, indent: c_uint ) -> *mut c_char;
}
type ScriptDone = Box<dyn FnOnce( Result<String, String> )>;
const CONSOLE_ENV: &str = "LTK_WEBKIT_CONSOLE";
pub( crate ) unsafe fn console_to_stdout_if_asked( webview: *mut WebKitWebView )
{
if std::env::var_os( CONSOLE_ENV ).as_deref() != Some( std::ffi::OsStr::new( "1" ) ) { return; }
unsafe
{
let settings = webkit_web_view_get_settings( webview );
if !settings.is_null() { webkit_settings_set_enable_write_console_messages_to_stdout( settings, 1 ); }
}
}
impl WebView
{
pub fn set_javascript_enabled( &self, enabled: bool )
{
let Some( webview ) = self.webview_ptr() else { return };
unsafe
{
let settings = webkit_web_view_get_settings( webview );
if !settings.is_null() { webkit_settings_set_enable_javascript( settings, enabled as i32 ); }
}
}
pub fn javascript_enabled( &self ) -> bool
{
self.webview_ptr().is_some_and( |w| unsafe
{
let settings = webkit_web_view_get_settings( w );
!settings.is_null() && webkit_settings_get_enable_javascript( settings ) != 0
} )
}
pub fn evaluate_javascript( &self, script: &str, done: impl FnOnce( Result<String, String> ) + 'static )
{
let Some( webview ) = self.webview_ptr() else { return };
let Ok( script_c ) = CString::new( script ) else { return };
let data = Box::into_raw( Box::new( Box::new( done ) as ScriptDone ) ) as gpointer;
unsafe { webkit_web_view_evaluate_javascript( webview, script_c.as_ptr(), -1, ptr::null(), ptr::null(), ptr::null_mut(), Some( on_evaluated ), data ); }
}
}
pub( crate ) fn call_async_in_world( webview: *mut WebKitWebView, body: &str, world: &'static [ u8 ], done: impl FnOnce( Result<String, String> ) + 'static )
{
let Ok( body_c ) = CString::new( body ) else { return done( Err( "script contains NUL".to_string() ) ) };
let data = Box::into_raw( Box::new( Box::new( done ) as ScriptDone ) ) as gpointer;
unsafe
{
webkit_web_view_call_async_javascript_function( webview, body_c.as_ptr(), -1, ptr::null_mut(), world.as_ptr() as *const c_char, ptr::null(), ptr::null_mut(), Some( on_called ), data );
}
}
unsafe extern "C" fn on_evaluated( source: *mut GObject, result: *mut GAsyncResult, user_data: gpointer )
{
let done = unsafe { Box::from_raw( user_data as *mut ScriptDone ) };
let mut err: *mut GError = ptr::null_mut();
let value = unsafe { webkit_web_view_evaluate_javascript_finish( source as *mut WebKitWebView, result, &mut err ) };
( *done )( unsafe { json_of( value, err ) } );
}
unsafe extern "C" fn on_called( source: *mut GObject, result: *mut GAsyncResult, user_data: gpointer )
{
let done = unsafe { Box::from_raw( user_data as *mut ScriptDone ) };
let mut err: *mut GError = ptr::null_mut();
let value = unsafe { webkit_web_view_call_async_javascript_function_finish( source as *mut WebKitWebView, result, &mut err ) } as *mut c_void;
( *done )( unsafe { json_of( value, err ) } );
}
unsafe fn json_of( value: *mut c_void, err: *mut GError ) -> Result<String, String>
{
unsafe
{
if value.is_null()
{
let message = super::error_message_or_default( err, "unknown" );
if !err.is_null() { g_error_free( err ); }
return Err( message );
}
let json = jsc_value_to_json( value, 0 );
let text = if json.is_null() { "null".to_string() } else { owned( json ) };
if !json.is_null() { g_free( json as gpointer ); }
g_object_unref( value as *mut GObject );
Ok( text )
}
}