use std::{error::Error, ffi::CStr, fmt, mem::MaybeUninit, os::raw::*};
use browser_window_c::*;
use super::{super::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) -> Option<CookieJarImpl> {
let inner = unsafe { cbw_CookieJar_newGlobal() };
Some(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 free(&self) {
unsafe {
Box::<UserData>::from_raw((*self.inner).user_data 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,
});
let mut _url: String = "file:///".into(); let source2 = match &source {
Source::File(path) => {
_url += path.to_str().unwrap();
cbw_BrowserWindowSource {
data: _url.as_str().into(),
is_html: 0,
}
}
Source::Html(html) => cbw_BrowserWindowSource {
data: html.as_str().into(),
is_html: 1,
},
Source::Url(url) => cbw_BrowserWindowSource {
data: url.as_str().into(),
is_html: 0,
},
};
unsafe {
let browser = cbw_BrowserWindow_new(
app.inner,
parent.inner,
title.into(),
w,
h,
window_options as _,
Some(ffi_handler),
Box::into_raw(user_data) as _,
);
cbw_BrowserWindow_create(
browser,
w,
h,
source2,
browser_window_options 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(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 }
}
}
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())
}
}
pub(super) 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<JsValue> = Vec::with_capacity(arg_count as usize);
for i in 0..arg_count {
args_vec.push(JsValue::from_string((*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<JsValue, JsEvaluationError>) {
let result_val: Result<JsValue, JsEvaluationError> = if error.is_null() {
let result_str = CStr::from_ptr(result).to_string_lossy().to_string();
Ok(JsValue::from_string(&result_str))
} else {
Err(JsEvaluationError::new(error))
};
let handle = BrowserWindowImpl { inner: bw };
(handle, result_val)
}