browser_window_core/browser_window/
c.rs

1use super::*;
2
3use std::{
4	error::Error,
5	ffi::CStr,
6	fmt,
7	mem::MaybeUninit,
8	os::raw::*
9};
10
11use browser_window_c::*;
12
13use crate::window::WindowImpl;
14
15
16
17#[derive(Clone,Copy)]
18pub struct BrowserWindowImpl {
19	inner: *mut cbw_BrowserWindow
20}
21
22struct CreationCallbackData {
23	func: CreationCallbackFn,
24	data: *mut ()
25}
26
27struct EvalJsCallbackData {
28	callback: EvalJsCallbackFn,
29	data: *mut ()
30}
31
32/// An error that may occur when evaluating or executing JavaScript code.
33#[derive(Debug)]
34pub struct JsEvaluationError {
35	message: String
36	// TODO: Add line and column number files, and perhaps even more info about the JS error
37}
38
39struct UserData {
40	func: ExternalInvocationHandlerFn,
41	data: *mut ()
42}
43
44
45impl BrowserWindowExt for BrowserWindowImpl {
46
47	fn cookie_jar(&self) -> CookieJarImpl {
48		let inner = unsafe { cbw_CookieJar_newGlobal() };
49
50		CookieJarImpl {
51			inner
52		}
53	}
54
55	fn eval_js( &self, js: &str, callback: EvalJsCallbackFn, callback_data: *mut () ) {
56		let data = Box::new( EvalJsCallbackData {
57			callback,
58			data: callback_data
59		} );
60
61		let data_ptr = Box::into_raw( data );
62
63		unsafe { cbw_BrowserWindow_evalJs( self.inner, js.into(), Some( ffi_eval_js_callback_handler ), data_ptr as _ ) }
64	}
65
66	fn eval_js_threadsafe( &self, js: &str, callback: EvalJsCallbackFn, callback_data: *mut () ) {
67		let data = Box::new( EvalJsCallbackData {
68			callback,
69			data: callback_data
70		} );
71
72		let data_ptr = Box::into_raw( data );
73
74		unsafe { cbw_BrowserWindow_evalJsThreaded( self.inner, js.into(), Some( ffi_eval_js_callback_handler ), data_ptr as _ ) }
75	}
76
77	fn navigate( &self, uri: &str ) {
78		unsafe { cbw_BrowserWindow_navigate( self.inner, uri.into() ) };
79	}
80
81	fn new(
82		app: ApplicationImpl,
83		parent: WindowImpl,
84		source: Source,
85		title: &str,
86		width: Option<u32>,
87		height: Option<u32>,
88		window_options: &WindowOptions,
89		browser_window_options: &BrowserWindowOptions,
90		handler: ExternalInvocationHandlerFn,
91		_user_data: *mut (),
92		creation_callback: CreationCallbackFn,
93		_callback_data: *mut ()
94	) {
95		// Convert width and height to -1 if unspecified.
96		// Also convert to c_int as required by the C interface.
97		let w: c_int = match width {
98			None => -1,
99			Some(x) => x as _
100		};
101		let h: c_int = match height {
102			None => -1,
103			Some(x) => x as _
104		};
105
106		// Wrap the callback functions so that they invoke our Rust functions from C
107		let user_data = Box::new( UserData {
108			func: handler,
109			data: _user_data
110		} );
111		let callback_data = Box::new( CreationCallbackData {
112			func: creation_callback,
113			data: _callback_data
114		} );
115
116		unsafe { cbw_BrowserWindow_new(
117			app.inner,
118			parent.inner,
119			source,
120			title.into(),
121			w, h,
122			window_options as _,
123			browser_window_options as _,
124			Some( ffi_handler ),
125			Box::into_raw( user_data ) as _,
126			Some( ffi_creation_callback_handler ),
127			Box::into_raw( callback_data ) as _
128		) };
129	}
130
131	fn user_data( &self ) -> *mut () {
132		let c_user_data_ptr: *mut UserData = unsafe { (*self.inner).user_data as _ };
133
134		// The actual user data pointer is stored within the `UserData` struct that is stored within the C handle
135		unsafe { (*c_user_data_ptr).data }
136	}
137
138	fn url<'a>(&'a self) -> Cow<'a, str> {
139		let mut slice: cbw_StrSlice = unsafe { MaybeUninit::uninit().assume_init() };
140		let owned = unsafe { cbw_BrowserWindow_getUrl(self.inner, &mut slice) };
141
142		if owned > 0 {
143			let url: String = slice.into();
144			unsafe { cbw_string_free(slice) };
145			url.into()
146		}
147		else {
148			let url: &'a str = slice.into();
149			url.into()
150		}
151	}
152
153	fn window( &self ) -> WindowImpl {
154		WindowImpl {
155			inner: unsafe { cbw_BrowserWindow_getWindow( self.inner ) }
156		}
157	}
158}
159
160
161
162impl JsEvaluationError {
163	pub(in super) unsafe fn new( err: *const cbw_Err ) -> Self {
164
165		let msg_ptr = ((*err).alloc_message.unwrap())( (*err).code, (*err).data );
166		let cstr = CStr::from_ptr( msg_ptr );
167		let message: String = cstr.to_string_lossy().into();
168
169		Self {
170			message: message
171		}
172	}
173}
174
175impl Error for JsEvaluationError {
176	fn source(&self) -> Option<&(dyn Error + 'static)> { None }
177}
178
179impl fmt::Display for JsEvaluationError {
180
181	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
182
183		write!(f, "{}", self.message.as_str())
184	}
185}
186
187
188
189/****************************************************************************************************************
190 * The C handler functions that are invoked by external C code, and that in turn invoke relevant Rust handlers. *
191 ****************************************************************************************************************/
192
193unsafe extern "C" fn ffi_creation_callback_handler( bw: *mut cbw_BrowserWindow, _data: *mut c_void ) {
194
195	let data_ptr = _data as *mut CreationCallbackData;
196	let data = Box::from_raw( data_ptr );
197
198	let handle = BrowserWindowImpl { inner: bw };
199
200	(data.func)( handle, data.data );
201}
202
203unsafe extern "C" fn ffi_eval_js_callback_handler( bw: *mut cbw_BrowserWindow, _data: *mut c_void, _result: *const c_char, error: *const cbw_Err ) {
204
205	let data_ptr = _data as *mut EvalJsCallbackData;
206	let data = Box::from_raw( data_ptr );
207
208	let (handle, result) = ffi_eval_js_callback_result( bw, _result, error );
209
210	(data.callback)( handle, data.data, result );
211}
212
213unsafe extern "C" fn ffi_handler( bw: *mut cbw_BrowserWindow, cmd: cbw_CStrSlice, args: *mut cbw_CStrSlice, arg_count: usize ) {
214
215	let handle = BrowserWindowImpl { inner: bw };
216
217	let data_ptr = (*bw).user_data as *mut UserData;
218	let data = &mut *data_ptr;
219
220	// Convert the command and args to a String and `Vec<&str>`
221	let cmd_string: &str = cmd.into();
222	let mut args_vec: Vec<String> = Vec::with_capacity( arg_count as usize );
223	for i in 0..arg_count {
224		args_vec.push( (*args.add( i as usize )).into() );
225	}
226	
227	(data.func)( handle, cmd_string, args_vec );
228}
229
230/// Processes the result received from the C function, and returns it in a Rust Result.
231unsafe fn ffi_eval_js_callback_result(
232	bw: *mut cbw_BrowserWindow,
233	result: *const c_char,
234	error: *const cbw_Err
235) -> ( BrowserWindowImpl, Result<String, JsEvaluationError> ) {
236
237
238	// Construct a result value depending on whether the result or error parameters are set
239	let result_val: Result<String, JsEvaluationError> = if error.is_null() {
240		let result_str = CStr::from_ptr( result ).to_string_lossy().to_owned().to_string();
241		Ok( result_str )
242	}
243	else {
244		Err( JsEvaluationError::new( error ) )
245	};
246
247	let handle = BrowserWindowImpl { inner: bw };
248
249	// return
250	( handle, result_val )
251}