use super::*;
use std::{
error::Error,
ffi::CStr,
fmt,
mem::MaybeUninit,
os::raw::*
};
use browser_window_c::*;
use crate::window::WindowImpl;
#[derive(Clone,Copy)]
pub struct BrowserWindowImpl {
inner: *mut cbw_BrowserWindow
}
struct CreationCallbackData {
func: CreationCallbackFn,
data: *mut ()
}
struct EvalJsCallbackData {
callback: EvalJsCallbackFn,
data: *mut ()
}
#[derive(Debug)]
pub struct JsEvaluationError {
message: String
}
struct UserData {
func: ExternalInvocationHandlerFn,
data: *mut ()
}
impl BrowserWindowExt for BrowserWindowImpl {
fn cookie_jar(&self) -> CookieJarImpl {
let inner = unsafe { cbw_CookieJar_newGlobal() };
CookieJarImpl {
inner
}
}
fn eval_js( &self, js: &str, callback: EvalJsCallbackFn, callback_data: *mut () ) {
let data = Box::new( EvalJsCallbackData {
callback,
data: callback_data
} );
let data_ptr = Box::into_raw( data );
unsafe { cbw_BrowserWindow_evalJs( self.inner, js.into(), Some( ffi_eval_js_callback_handler ), data_ptr as _ ) }
}
fn eval_js_threadsafe( &self, js: &str, callback: EvalJsCallbackFn, callback_data: *mut () ) {
let data = Box::new( EvalJsCallbackData {
callback,
data: callback_data
} );
let data_ptr = Box::into_raw( data );
unsafe { cbw_BrowserWindow_evalJsThreaded( self.inner, js.into(), Some( ffi_eval_js_callback_handler ), data_ptr as _ ) }
}
fn navigate( &self, uri: &str ) {
unsafe { cbw_BrowserWindow_navigate( self.inner, uri.into() ) };
}
fn new(
app: ApplicationImpl,
parent: WindowImpl,
source: Source,
title: &str,
width: Option<u32>,
height: Option<u32>,
window_options: &WindowOptions,
browser_window_options: &BrowserWindowOptions,
handler: ExternalInvocationHandlerFn,
_user_data: *mut (),
creation_callback: CreationCallbackFn,
_callback_data: *mut ()
) {
let w: c_int = match width {
None => -1,
Some(x) => x as _
};
let h: c_int = match height {
None => -1,
Some(x) => x as _
};
let user_data = Box::new( UserData {
func: handler,
data: _user_data
} );
let callback_data = Box::new( CreationCallbackData {
func: creation_callback,
data: _callback_data
} );
unsafe { cbw_BrowserWindow_new(
app.inner,
parent.inner,
source,
title.into(),
w, h,
window_options as _,
browser_window_options as _,
Some( ffi_handler ),
Box::into_raw( user_data ) as _,
Some( ffi_creation_callback_handler ),
Box::into_raw( callback_data ) as _
) };
}
fn user_data( &self ) -> *mut () {
let c_user_data_ptr: *mut UserData = unsafe { (*self.inner).user_data as _ };
unsafe { (*c_user_data_ptr).data }
}
fn url<'a>(&'a self) -> Cow<'a, str> {
let mut slice: cbw_StrSlice = unsafe { MaybeUninit::uninit().assume_init() };
let owned = unsafe { cbw_BrowserWindow_getUrl(self.inner, &mut slice) };
if owned > 0 {
let url: String = slice.into();
unsafe { cbw_string_free(slice) };
url.into()
}
else {
let url: &'a str = slice.into();
url.into()
}
}
fn window( &self ) -> WindowImpl {
WindowImpl {
inner: unsafe { cbw_BrowserWindow_getWindow( self.inner ) }
}
}
}
impl JsEvaluationError {
pub(in super) unsafe fn new( err: *const cbw_Err ) -> Self {
let msg_ptr = ((*err).alloc_message.unwrap())( (*err).code, (*err).data );
let cstr = CStr::from_ptr( msg_ptr );
let message: String = cstr.to_string_lossy().into();
Self {
message: message
}
}
}
impl Error for JsEvaluationError {
fn source(&self) -> Option<&(dyn Error + 'static)> { None }
}
impl fmt::Display for JsEvaluationError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.message.as_str())
}
}
unsafe extern "C" fn ffi_creation_callback_handler( bw: *mut cbw_BrowserWindow, _data: *mut c_void ) {
let data_ptr = _data as *mut CreationCallbackData;
let data = Box::from_raw( data_ptr );
let handle = BrowserWindowImpl { inner: bw };
(data.func)( handle, data.data );
}
unsafe extern "C" fn ffi_eval_js_callback_handler( bw: *mut cbw_BrowserWindow, _data: *mut c_void, _result: *const c_char, error: *const cbw_Err ) {
let data_ptr = _data as *mut EvalJsCallbackData;
let data = Box::from_raw( data_ptr );
let (handle, result) = ffi_eval_js_callback_result( bw, _result, error );
(data.callback)( handle, data.data, result );
}
unsafe extern "C" fn ffi_handler( bw: *mut cbw_BrowserWindow, cmd: cbw_CStrSlice, args: *mut cbw_CStrSlice, arg_count: usize ) {
let handle = BrowserWindowImpl { inner: bw };
let data_ptr = (*bw).user_data as *mut UserData;
let data = &mut *data_ptr;
let cmd_string: &str = cmd.into();
let mut args_vec: Vec<String> = Vec::with_capacity( arg_count as usize );
for i in 0..arg_count {
args_vec.push( (*args.add( i as usize )).into() );
}
(data.func)( handle, cmd_string, args_vec );
}
unsafe fn ffi_eval_js_callback_result(
bw: *mut cbw_BrowserWindow,
result: *const c_char,
error: *const cbw_Err
) -> ( BrowserWindowImpl, Result<String, JsEvaluationError> ) {
let result_val: Result<String, JsEvaluationError> = if error.is_null() {
let result_str = CStr::from_ptr( result ).to_string_lossy().to_owned().to_string();
Ok( result_str )
}
else {
Err( JsEvaluationError::new( error ) )
};
let handle = BrowserWindowImpl { inner: bw };
( handle, result_val )
}