browser_window/core/browser_window/
c.rs1use std::{
2 error::Error as StdError, ffi::CStr, fmt, mem::MaybeUninit, os::raw::*, ptr, slice, str,
3};
4
5use browser_window_c::*;
6
7use super::{
8 super::{error::Error, window::WindowImpl},
9 *,
10};
11use crate::{def_browser_event, def_event, rc::*};
12
13
14#[derive(Clone)]
15pub struct BrowserWindowImpl {
16 inner: *mut cbw_BrowserWindow,
17}
18
19struct CreationCallbackData {
20 func: CreationCallbackFn,
21 data: *mut (),
22}
23
24struct EvalJsCallbackData {
25 callback: EvalJsCallbackFn,
26 data: *mut (),
27}
28
29#[derive(Debug)]
31pub struct JsEvaluationError {
32 message: String, }
35
36struct EventData<C, A> {
37 owner: Weak<BrowserWindowOwner>,
38 handler: BrowserWindowEventHandler<A>,
39 converter: unsafe fn(&C) -> A,
40}
41
42
43#[doc(hidden)]
44#[macro_export]
45macro_rules! def_browser_event_c {
46 ($name:ident<$carg_type:ty, $rarg_type:ty> => $converter:ident => $c_event_name:ident) => {
47 def_browser_event!($name<$rarg_type>(&mut self, handler) {
48 if let Some(upgraded) = self.owner.upgrade() {
49 let c_ptr = unsafe { &mut *upgraded.0.inner.inner };
50 if c_ptr.events.$c_event_name.callback.is_some() {
52 unsafe { let _ = Box::from_raw(c_ptr.events.$c_event_name.data as *mut EventData<$carg_type, $rarg_type>); }
53 }
54
55 let event_data = EventData::<$carg_type, $rarg_type> {
57 owner: self.owner.clone(),
58 handler,
59 converter: $converter,
60 };
61 let event_data_ptr = Box::into_raw(Box::new(event_data));
62 c_ptr.events.$c_event_name = cbw_Event {
63 callback: Some(ffi_browser_window_event_callback::<$carg_type, $rarg_type>),
64 data: event_data_ptr as _
65 };
66 }
67 });
68 }
69}
70
71
72impl BrowserWindowExt for BrowserWindowImpl {
73 fn cookie_jar(&self) -> Option<CookieJarImpl> {
74 let inner = unsafe { cbw_CookieJar_newGlobal() };
75
76 Some(CookieJarImpl(inner))
77 }
78
79 fn eval_js(&self, js: &str, callback: EvalJsCallbackFn, callback_data: *mut ()) {
80 let data = Box::new(EvalJsCallbackData {
81 callback,
82 data: callback_data,
83 });
84 let data_ptr = Box::into_raw(data);
85
86 unsafe {
87 cbw_BrowserWindow_evalJs(
88 self.inner,
89 js.into(),
90 Some(ffi_eval_js_callback_handler),
91 data_ptr as _,
92 )
93 }
94 }
95
96 fn eval_js_threadsafe(&self, js: &str, callback: EvalJsCallbackFn, callback_data: *mut ()) {
97 let data = Box::new(EvalJsCallbackData {
98 callback,
99 data: callback_data,
100 });
101
102 let data_ptr = Box::into_raw(data);
103
104 unsafe {
105 cbw_BrowserWindow_evalJsThreaded(
106 self.inner,
107 js.into(),
108 Some(ffi_eval_js_callback_handler),
109 data_ptr as _,
110 )
111 }
112 }
113
114 fn free(&self) { unsafe { cbw_BrowserWindow_free(self.inner) } }
115
116 fn navigate(&self, uri: &str) { unsafe { cbw_BrowserWindow_navigate(self.inner, uri.into()) }; }
117
118 fn url<'a>(&'a self) -> Cow<'a, str> {
119 let owned;
120 let slice;
121 unsafe {
122 let mut slice_uninit: MaybeUninit<cbw_StrSlice> = MaybeUninit::uninit();
123 owned = cbw_BrowserWindow_getUrl(self.inner, slice_uninit.as_mut_ptr());
124 slice = slice_uninit.assume_init();
125 }
126
127 if owned > 0 {
128 let url: String = slice.into();
129 unsafe { cbw_string_free(slice) };
130 url.into()
131 } else {
132 let url: &'a str = slice.into();
133 url.into()
134 }
135 }
136
137 fn window(&self) -> WindowImpl {
138 WindowImpl {
139 inner: unsafe { cbw_BrowserWindow_getWindow(self.inner) },
140 }
141 }
142
143 fn new(
144 app: ApplicationImpl, parent: WindowImpl, source: Source, title: &str, width: Option<u32>,
145 height: Option<u32>, window_options: &WindowOptions,
146 browser_window_options: &BrowserWindowOptions, creation_callback: CreationCallbackFn,
147 _callback_data: *mut (),
148 ) {
149 let w: c_int = match width {
152 None => -1,
153 Some(x) => x as _,
154 };
155 let h: c_int = match height {
156 None => -1,
157 Some(x) => x as _,
158 };
159
160 let callback_data = Box::new(CreationCallbackData {
162 func: creation_callback,
163 data: _callback_data,
164 });
165
166 let mut _url: String = "file:///".into(); let source2 = match &source {
169 Source::File(path) => {
172 _url += path.to_str().unwrap();
173
174 cbw_BrowserWindowSource {
175 data: _url.as_str().into(),
176 is_html: 0,
177 }
178 }
179 Source::Html(html) => cbw_BrowserWindowSource {
180 data: html.as_str().into(),
181 is_html: 1,
182 },
183 Source::Url(url) => cbw_BrowserWindowSource {
184 data: url.as_str().into(),
185 is_html: 0,
186 },
187 };
188
189 unsafe {
190 let browser = cbw_BrowserWindow_new(
191 app.inner,
192 parent.inner,
193 title.into(),
194 w,
195 h,
196 window_options as _,
197 );
198 cbw_BrowserWindow_create(
199 browser,
200 w,
201 h,
202 source2,
203 browser_window_options as _,
204 Some(ffi_creation_callback_handler),
205 Box::into_raw(callback_data) as _,
206 );
207 };
208 }
209}
210
211impl BrowserWindowEventExt for BrowserWindowImpl {
212 fn on_address_changed(&self, handle: Weak<BrowserWindowOwner>) -> AddressChangedEvent {
213 AddressChangedEvent::new(handle)
214 }
215
216 fn on_console_message(&self, handle: Weak<BrowserWindowOwner>) -> ConsoleMessageEvent {
217 ConsoleMessageEvent::new(handle)
218 }
219
220 fn on_favicon_changed(&self, handle: Weak<BrowserWindowOwner>) -> FaviconChangedEvent {
221 FaviconChangedEvent::new(handle)
222 }
223
224 fn on_fullscreen_mode_changed(
225 &self, handle: Weak<BrowserWindowOwner>,
226 ) -> FullscreenModeChangedEvent {
227 FullscreenModeChangedEvent::new(handle)
228 }
229
230 fn on_loading_progress_changed(
231 &self, handle: Weak<BrowserWindowOwner>,
232 ) -> LoadingProgressChangedEvent {
233 LoadingProgressChangedEvent::new(handle)
234 }
235
236 fn on_message(&self, handle: Weak<BrowserWindowOwner>) -> MessageEvent {
237 MessageEvent::new(handle)
238 }
239
240 fn on_navigation_end(&self, handle: Weak<BrowserWindowOwner>) -> NavigationEndEvent {
241 NavigationEndEvent::new(handle)
242 }
243
244 fn on_navigation_start(&self, handle: Weak<BrowserWindowOwner>) -> NavigationStartEvent {
245 NavigationStartEvent::new(handle)
246 }
247
248 fn on_page_title_changed(&self, handle: Weak<BrowserWindowOwner>) -> PageTitleChangedEvent {
249 PageTitleChangedEvent::new(handle)
250 }
251
252 fn on_status_message(&self, handle: Weak<BrowserWindowOwner>) -> StatusMessageEvent {
253 StatusMessageEvent::new(handle)
254 }
255
256 fn on_tooltip(&self, handle: Weak<BrowserWindowOwner>) -> TooltipEvent {
257 TooltipEvent::new(handle)
258 }
259}
260
261def_browser_event_c!(AddressChangedEvent<cbw_CStrSlice, String> => str_converter => on_address_changed);
262def_browser_event_c!(ConsoleMessageEvent<cbw_CStrSlice, String> => str_converter => on_console_message);
263def_browser_event_c!(FaviconChangedEvent<cbw_CStrSlice, String> => str_converter => on_favicon_changed);
264def_browser_event_c!(FullscreenModeChangedEvent<c_int, bool> => bool_converter => on_fullscreen_mode_changed);
265def_browser_event_c!(LoadingProgressChangedEvent<c_double, f64> => f64_converter => on_loading_progress_changed);
266def_browser_event_c!(MessageEvent<cbw_BrowserWindowMessageArgs, MessageEventArgs> => message_args_converter => on_message);
267def_browser_event_c!(NavigationStartEvent<(), ()> => no_converter => on_navigation_start);
268def_browser_event_c!(NavigationEndEvent<cbw_Err, Result<(), Error>> => error_converter => on_navigation_end);
269def_browser_event_c!(PageTitleChangedEvent<cbw_CStrSlice, String> => str_converter => on_page_title_changed);
270def_browser_event_c!(StatusMessageEvent<cbw_CStrSlice, String> => str_converter => on_status_message);
271def_browser_event_c!(TooltipEvent<cbw_CStrSlice, String> => str_converter => on_tooltip);
272
273impl JsEvaluationError {
274 pub(super) unsafe fn new(err: *const cbw_Err) -> Self {
275 let msg_ptr = ((*err).alloc_message.unwrap())((*err).code, (*err).data);
276 let cstr = CStr::from_ptr(msg_ptr);
277 let message: String = cstr.to_string_lossy().into();
278
279 Self { message }
280 }
281}
282
283impl StdError for JsEvaluationError {
284 fn source(&self) -> Option<&(dyn StdError + 'static)> { None }
285}
286
287impl fmt::Display for JsEvaluationError {
288 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
289 write!(f, "{}", self.message.as_str())
290 }
291}
292
293pub(super) unsafe extern "C" fn ffi_creation_callback_handler(
300 bw: *mut cbw_BrowserWindow, _data: *mut c_void,
301) {
302 let data_ptr = _data as *mut CreationCallbackData;
303 let data = Box::from_raw(data_ptr);
304
305 let handle = BrowserWindowImpl { inner: bw };
306
307 (data.func)(handle, data.data);
308}
309
310unsafe extern "C" fn ffi_eval_js_callback_handler(
311 bw: *mut cbw_BrowserWindow, _data: *mut c_void, _result: *const c_char, error: *const cbw_Err,
312) {
313 let data_ptr = _data as *mut EvalJsCallbackData;
314 let data = Box::from_raw(data_ptr);
315
316 let (handle, result) = ffi_eval_js_callback_result(bw, _result, error);
317
318 (data.callback)(handle, data.data, result);
319}
320
321unsafe fn ffi_eval_js_callback_result(
324 bw: *mut cbw_BrowserWindow, result: *const c_char, error: *const cbw_Err,
325) -> (BrowserWindowImpl, Result<JsValue, JsEvaluationError>) {
326 let result_val: Result<JsValue, JsEvaluationError> = if error.is_null() {
329 let result_str = CStr::from_ptr(result).to_string_lossy().to_string();
330
331 Ok(JsValue::from_string(&result_str))
333 } else {
334 Err(JsEvaluationError::new(error))
335 };
336
337 let handle = BrowserWindowImpl { inner: bw };
338
339 (handle, result_val)
341}
342
343unsafe extern "C" fn ffi_browser_window_event_callback<C, A>(
344 handler_data: *mut c_void, arg_ptr: *mut c_void,
345) -> i32 {
346 let event_data_ptr = handler_data as *mut EventData<C, A>;
347 let event_data = &mut *event_data_ptr;
348 let arg_ptr2 = arg_ptr as *mut C;
349 let carg = &*arg_ptr2;
350
351 let rarg = (event_data.converter)(carg);
353
354 let rc_handle = event_data
356 .owner
357 .upgrade()
358 .expect("browser window handle is gone");
359 match &mut event_data.handler {
360 EventHandler::Sync(callback) => {
361 (callback)(&*rc_handle, rarg);
362 }
363 EventHandler::Async(callback) => {
364 let app = rc_handle.0.app();
365 let future = (callback)(BrowserWindow(rc_handle.clone()), rarg);
366 app.spawn(future);
367 }
368 }
369 return 0;
370}
371
372unsafe fn no_converter(_input: &()) -> () { () }
373
374unsafe fn error_converter(input: &cbw_Err) -> Result<(), Error> {
375 if input.code == 0 {
376 Ok(())
377 } else {
378 let err2 = input.clone();
379 Err(Error::from(err2))
380 }
381}
382
383unsafe fn bool_converter(input: &c_int) -> bool { *input > 0 }
384
385unsafe fn f64_converter(input: &c_double) -> f64 { *input }
386
387unsafe fn str_converter(input: &cbw_CStrSlice) -> String {
388 let string =
389 str::from_utf8_unchecked(slice::from_raw_parts(input.data as *const u8, input.len) as _);
390 string.to_string()
391}
392
393unsafe fn message_args_converter(input: &cbw_BrowserWindowMessageArgs) -> MessageEventArgs {
394 let cmd_string = str::from_utf8_unchecked(slice::from_raw_parts(
396 input.cmd.data as *const u8,
397 input.cmd.len,
398 ));
399 let mut args_vec: Vec<JsValue> = Vec::with_capacity(input.arg_count as usize);
400 for i in 0..input.arg_count {
401 args_vec.push(JsValue::from_string((*input.args.add(i as usize)).into()));
402 }
403
404 MessageEventArgs {
405 cmd: cmd_string.to_string(),
406 args: args_vec,
407 }
408}
409
410#[allow(non_snake_case)]
411#[no_mangle]
412extern "C" fn bw_Window_freeUserData(w: *mut c_void) {
413 let w_ptr = w as *mut cbw_Window;
414 unsafe {
415 if (*w_ptr).user_data != ptr::null_mut() {
416 BrowserWindowImpl::free_user_data((*w_ptr).user_data as _);
417 (*w_ptr).user_data = ptr::null_mut();
418 }
419 }
420}