ltk_webkit/wpe/
browsing.rsuse std::ffi::{ c_char, c_void, CStr, CString };
use std::ptr;
use glib_sys::{ g_bytes_get_data, g_bytes_new, g_bytes_unref };
use wpe_platform_sys::*;
use wpe_webkit_sys::*;
use ltk::types::Point;
use super::WebView;
const FIND_MAX_MATCHES: u32 = 1000;
pub( crate ) unsafe fn copy_user_agent( from: *mut WebKitWebView, to: *mut WebKitWebView )
{
unsafe
{
let source = webkit_web_view_get_settings( from );
let target = webkit_web_view_get_settings( to );
if source.is_null() || target.is_null() || source == target { return; }
webkit_settings_set_user_agent( target, webkit_settings_get_user_agent( source ) );
}
}
impl WebView
{
pub( crate ) fn webview_ptr( &self ) -> Option<*mut WebKitWebView>
{
let ptr = self.inner.lock().ok()?.webview;
if ptr.is_null() { None } else { Some( ptr ) }
}
fn view_ptr( &self ) -> Option<*mut WPEView>
{
let ptr = self.inner.lock().ok()?.view;
if ptr.is_null() { None } else { Some( ptr ) }
}
pub fn load_uri( &self, uri: &str )
{
let Some( webview ) = self.webview_ptr() else { return };
let Ok( uri_c ) = CString::new( uri ) else { return };
self.mark_host_load();
unsafe { webkit_web_view_load_uri( webview, uri_c.as_ptr() ); }
}
pub fn load_html( &self, html: &str, base_uri: Option<&str> )
{
let Some( webview ) = self.webview_ptr() else { return };
let Ok( html_c ) = CString::new( html ) else { return };
let base_c = base_uri.and_then( |b| CString::new( b ).ok() );
let base_ptr = base_c.as_ref().map( |b| b.as_ptr() ).unwrap_or( ptr::null() );
self.mark_host_load();
unsafe { webkit_web_view_load_html( webview, html_c.as_ptr(), base_ptr ); }
}
pub fn go_back( &self )
{
let Some( webview ) = self.webview_ptr() else { return };
self.mark_host_load();
unsafe { webkit_web_view_go_back( webview ); }
}
pub fn go_forward( &self )
{
let Some( webview ) = self.webview_ptr() else { return };
self.mark_host_load();
unsafe { webkit_web_view_go_forward( webview ); }
}
pub fn reload( &self )
{
let Some( webview ) = self.webview_ptr() else { return };
self.mark_host_load();
unsafe { webkit_web_view_reload( webview ); }
}
pub fn stop( &self )
{
let Some( webview ) = self.webview_ptr() else { return };
unsafe { webkit_web_view_stop_loading( webview ); }
}
pub fn can_go_back( &self ) -> bool
{
self.webview_ptr().is_some_and( |w| unsafe { webkit_web_view_can_go_back( w ) } != 0 )
}
pub fn can_go_forward( &self ) -> bool
{
self.webview_ptr().is_some_and( |w| unsafe { webkit_web_view_can_go_forward( w ) } != 0 )
}
pub fn uri( &self ) -> String
{
self.webview_ptr().map( |w| owned( unsafe { webkit_web_view_get_uri( w ) } ) ).unwrap_or_default()
}
pub fn title( &self ) -> String
{
self.webview_ptr().map( |w| owned( unsafe { webkit_web_view_get_title( w ) } ) ).unwrap_or_default()
}
pub fn is_loading( &self ) -> bool
{
self.webview_ptr().is_some_and( |w| unsafe { webkit_web_view_is_loading( w ) } != 0 )
}
pub fn load_progress( &self ) -> f64
{
self.webview_ptr().map( |w| unsafe { webkit_web_view_get_estimated_load_progress( w ) } ).unwrap_or( 0.0 )
}
pub fn session_state( &self ) -> Option<Vec<u8>>
{
let webview = self.webview_ptr()?;
unsafe
{
let state = webkit_web_view_get_session_state( webview );
if state.is_null() { return None; }
let bytes = webkit_web_view_session_state_serialize( state );
webkit_web_view_session_state_unref( state );
if bytes.is_null() { return None; }
let mut size = 0;
let data = g_bytes_get_data( bytes, &mut size ) as *const u8;
let out = if data.is_null() { None } else { Some( std::slice::from_raw_parts( data, size ).to_vec() ) };
g_bytes_unref( bytes );
out
}
}
pub fn restore_session_state( &self, saved: &[ u8 ] ) -> bool
{
let Some( webview ) = self.webview_ptr() else { return false };
unsafe
{
let bytes = g_bytes_new( saved.as_ptr() as *const c_void, saved.len() );
let state = webkit_web_view_session_state_new( bytes );
g_bytes_unref( bytes );
if state.is_null() { return false; }
webkit_web_view_restore_session_state( webview, state );
webkit_web_view_session_state_unref( state );
let list = webkit_web_view_get_back_forward_list( webview );
let current = if list.is_null() { ptr::null_mut() } else { webkit_back_forward_list_get_current_item( list ) };
if current.is_null() { return false; }
webkit_web_view_go_to_back_forward_list_item( webview, current );
true
}
}
pub fn zoom( &self ) -> f64
{
self.webview_ptr().map( |w| unsafe { webkit_web_view_get_zoom_level( w ) } ).unwrap_or( 1.0 )
}
pub fn set_zoom( &self, level: f64 )
{
let Some( webview ) = self.webview_ptr() else { return };
unsafe { webkit_web_view_set_zoom_level( webview, level ); }
}
pub fn set_user_agent( &self, user_agent: &str )
{
let Some( webview ) = self.webview_ptr() else { return };
let Ok( user_agent ) = CString::new( user_agent ) else { return };
unsafe
{
let settings = webkit_web_view_get_settings( webview );
if !settings.is_null() { webkit_settings_set_user_agent( settings, user_agent.as_ptr() ); }
}
}
pub fn find( &self, text: &str )
{
if text.is_empty()
{
self.find_finish();
return;
}
let Some( webview ) = self.webview_ptr() else { return };
let Ok( text_c ) = CString::new( text ) else { return };
let options = WEBKIT_FIND_OPTIONS_CASE_INSENSITIVE | WEBKIT_FIND_OPTIONS_WRAP_AROUND;
unsafe
{
let controller = webkit_web_view_get_find_controller( webview );
webkit_find_controller_search( controller, text_c.as_ptr(), options, FIND_MAX_MATCHES );
}
}
pub fn find_next( &self )
{
let Some( webview ) = self.webview_ptr() else { return };
unsafe { webkit_find_controller_search_next( webkit_web_view_get_find_controller( webview ) ); }
}
pub fn find_previous( &self )
{
let Some( webview ) = self.webview_ptr() else { return };
unsafe { webkit_find_controller_search_previous( webkit_web_view_get_find_controller( webview ) ); }
}
pub fn find_finish( &self )
{
let Some( webview ) = self.webview_ptr() else { return };
unsafe { webkit_find_controller_search_finish( webkit_web_view_get_find_controller( webview ) ); }
}
pub fn set_visible( &self, visible: bool )
{
let Some( view ) = self.view_ptr() else { return };
unsafe
{
if visible
{
wpe_view_set_visible( view, 1 );
wpe_view_focus_in( view );
} else {
wpe_view_focus_out( view );
wpe_view_set_visible( view, 0 );
}
}
if visible
{
if let Ok( mut g ) = self.inner.lock() { g.needs_redraw = true; }
}
}
pub fn set_dark_mode( &self, dark: bool )
{
let display = match self.inner.lock() { Ok( g ) => g.display, Err( _ ) => return };
if display.is_null() { return; }
unsafe
{
let settings = wpe_display_get_settings( display );
if settings.is_null() { return; }
let mut err: *mut glib_sys::GError = ptr::null_mut();
wpe_settings_set_boolean(
settings,
WPE_SETTING_DARK_MODE.as_ptr() as *const c_char,
dark as glib_sys::gboolean,
WPE_SETTINGS_SOURCE_APPLICATION,
&mut err,
);
if !err.is_null() { glib_sys::g_error_free( err ); }
}
}
pub fn set_scale( &self, scale: f64 )
{
if scale <= 0.0 { return; }
let ( view, ( lw, lh ) ) = match self.inner.lock()
{
Ok( mut g ) =>
{
if ( g.scale - scale ).abs() < f64::EPSILON { return; }
g.scale = scale;
( g.view, g.logical_size() )
}
Err( _ ) => return,
};
if view.is_null() { return; }
unsafe
{
let top = wpe_view_get_toplevel( view );
if top.is_null() { return; }
wpe_toplevel_scale_changed( top, scale );
wpe_toplevel_resize( top, lw, lh );
}
}
pub fn contains( &self, x: f32, y: f32 ) -> bool
{
self.inner.lock().map( |g| g.last_rect.contains( Point { x, y } ) ).unwrap_or( false )
}
pub fn take_new_window_requests( &self ) -> Vec<String>
{
self.inner.lock().map( |mut g| std::mem::take( &mut g.new_window_requests ) ).unwrap_or_default()
}
}
pub( crate ) fn owned( s: *const c_char ) -> String
{
if s.is_null() { return String::new(); }
unsafe { CStr::from_ptr( s ).to_string_lossy().into_owned() }
}