browser-window 0.12.3

A simple, optionally async, optionally threadsafe, electron-like browser toolkit for Rust.
Documentation
use std::{
	borrow::Cow,
	ffi::c_void,
	mem::MaybeUninit,
	ops::Add,
	os::raw::c_uint,
	ptr,
	time::{Duration, SystemTime},
};

use browser_window_c::*;

use super::*;

pub struct CookieImpl(pub(crate) *mut cbw_Cookie);

pub struct CookieMutImpl(CookieImpl);

pub struct CookieJarImpl(pub(crate) *mut cbw_CookieJar);

pub struct CookieIteratorImpl(pub(crate) *mut cbw_CookieIterator);

struct CookieStorageCallbackData {
	callback: CookieStorageCallbackFn,
	data: *mut (),
}

struct CookieDeleteCallbackData {
	callback: CookieDeleteCallbackFn,
	data: *mut (),
}

struct CookieIteratorNextCallbackData {
	callback: CookieIteratorNextCallbackFn,
	data: *mut (),
}

impl CookieExt for CookieImpl {
	fn creation_time(&self) -> SystemTime {
		let timestamp = unsafe { cbw_Cookie_getCreationTime(self.0) };

		SystemTime::UNIX_EPOCH.add(Duration::from_millis(timestamp))
	}

	fn expires(&self) -> Option<SystemTime> {
		let timestamp = unsafe { cbw_Cookie_getExpires(self.0) };

		if timestamp != 0 {
			Some(SystemTime::UNIX_EPOCH.add(Duration::from_millis(timestamp)))
		} else {
			None
		}
	}

	fn domain<'a>(&'a self) -> Cow<'a, str> {
		let slice;
		let owned;
		unsafe {
			let mut slice_uninit: MaybeUninit<cbw_StrSlice> = MaybeUninit::uninit();
			owned = cbw_Cookie_getDomain(self.0, slice_uninit.as_mut_ptr());
			slice = slice_uninit.assume_init();
		}

		if owned > 0 {
			let string: String = slice.into();
			unsafe { cbw_string_free(slice) };
			string.into()
		} else {
			let string: &str = slice.into();
			string.into()
		}
	}

	fn free(&mut self) { unsafe { cbw_Cookie_free(self.0) }; }

	fn is_http_only(&self) -> bool { (unsafe { cbw_Cookie_isHttpOnly(self.0) }) > 0 }

	fn is_secure(&self) -> bool { (unsafe { cbw_Cookie_isSecure(self.0) }) > 0 }

	fn name<'a>(&'a self) -> Cow<'a, str> {
		let slice;
		let owned;
		unsafe {
			let mut slice_uninit: MaybeUninit<cbw_StrSlice> = MaybeUninit::uninit();
			owned = cbw_Cookie_getName(self.0, slice_uninit.as_mut_ptr());
			slice = slice_uninit.assume_init();
		}

		if owned > 0 {
			let string: String = slice.into();
			unsafe { cbw_string_free(slice) };
			string.into()
		} else {
			let string: &str = slice.into();
			string.into()
		}
	}

	fn new(name: &str, value: &str) -> Self {
		let inner = unsafe { cbw_Cookie_new(name.into(), value.into()) };

		Self(inner)
	}

	fn path<'a>(&'a self) -> Cow<'a, str> {
		let slice;
		let owned;
		unsafe {
			let mut slice_uninit: MaybeUninit<cbw_StrSlice> = MaybeUninit::uninit();
			owned = cbw_Cookie_getPath(self.0, slice_uninit.as_mut_ptr());
			slice = slice_uninit.assume_init();
		}

		if owned > 0 {
			let string: String = slice.into();
			unsafe { cbw_string_free(slice) };
			string.into()
		} else {
			let string: &str = slice.into();
			string.into()
		}
	}

	fn value<'a>(&'a self) -> Cow<'a, str> {
		let slice;
		let owned;
		unsafe {
			let mut slice_uninit: MaybeUninit<cbw_StrSlice> = MaybeUninit::uninit();
			owned = cbw_Cookie_getValue(self.0, slice_uninit.as_mut_ptr());
			slice = slice_uninit.assume_init();
		}

		if owned > 0 {
			let string: String = slice.into();
			unsafe { cbw_string_free(slice) };
			string.into()
		} else {
			let string: &str = slice.into();
			string.into()
		}
	}

	fn make_http_only(&mut self) { unsafe { cbw_Cookie_makeHttpOnly(self.0) }; }

	fn make_secure(&mut self) { unsafe { cbw_Cookie_makeSecure(self.0) }; }

	fn set_creation_time(&mut self, time: &SystemTime) {
		unsafe {
			cbw_Cookie_setCreationTime(
				self.0,
				time.duration_since(SystemTime::UNIX_EPOCH)
					.unwrap()
					.as_millis() as _,
			)
		};
	}

	fn set_expires(&mut self, time: &SystemTime) {
		unsafe {
			cbw_Cookie_setExpires(
				self.0,
				time.duration_since(SystemTime::UNIX_EPOCH)
					.unwrap()
					.as_millis() as _,
			)
		};
	}

	fn set_domain(&mut self, domain: &str) {
		unsafe { cbw_Cookie_setDomain(self.0, domain.into()) };
	}

	fn set_name(&mut self, name: &str) { unsafe { cbw_Cookie_setName(self.0, name.into()) }; }

	fn set_path(&mut self, path: &str) { unsafe { cbw_Cookie_setPath(self.0, path.into()) }; }

	fn set_value(&mut self, value: &str) { unsafe { cbw_Cookie_setValue(self.0, value.into()) }; }
}

impl CookieJarExt for CookieJarImpl {
	fn delete(
		&mut self, url: &str, name: &str, complete_cb: CookieDeleteCallbackFn, cb_data: *mut (),
	) {
		let data = Box::into_raw(Box::new(CookieDeleteCallbackData {
			callback: complete_cb,
			data: cb_data,
		}));

		unsafe {
			cbw_CookieJar_delete(
				self.0,
				url.into(),
				name.into(),
				Some(ffi_cookie_delete_callback_handler),
				data as _,
			)
		};
	}

	fn free(&mut self) { unsafe { cbw_CookieJar_free(self.0) }; }

	fn global() -> Option<CookieJarImpl> {
		let inner = unsafe { cbw_CookieJar_newGlobal() };

		if inner != ptr::null_mut() {
			Some(CookieJarImpl(inner))
		} else {
			None
		}
	}

	fn iterator<'a>(&'a self, url: &str, include_http_only: bool) -> CookieIteratorImpl {
		unsafe {
			let mut iterator: *mut cbw_CookieIterator = ptr::null_mut();
			cbw_CookieJar_iterator(
				self.0,
				&mut iterator as _,
				if include_http_only { 1 } else { 0 },
				url.into(),
			);

			return CookieIteratorImpl(iterator);
		}
	}

	fn iterator_all<'a>(&'a self) -> CookieIteratorImpl {
		let mut iterator = CookieIteratorImpl(ptr::null_mut());
		unsafe {
			cbw_CookieJar_iteratorAll(self.0, &mut iterator.0);
		}
		iterator
	}

	fn store(
		&mut self, url: &str, cookie: &CookieImpl, complete_cb: Option<CookieStorageCallbackFn>,
		cb_data: *mut (),
	) {
		let data = if !complete_cb.is_none() {
			Box::into_raw(Box::new(CookieStorageCallbackData {
				callback: complete_cb.unwrap(),
				data: cb_data,
			}))
		} else {
			ptr::null_mut()
		};

		unsafe {
			let mut error = cbw_CookieJar_store(
				self.0,
				url.into(),
				cookie.0,
				complete_cb.map(|_| ffi_cookie_storage_callback_handler as _),
				data as _,
			);

			if error.code != 0 && !complete_cb.is_none() {
				(complete_cb.unwrap())(
					CookieJarImpl(self.0),
					cb_data,
					Err(CookieStorageError::Unknown),
				);
			}

			cbw_Err_free(&mut error);
		}
	}
}

impl CookieIteratorExt for CookieIteratorImpl {
	fn free(&mut self) { unsafe { cbw_CookieIterator_free(self.0) } }

	fn next(&mut self, on_next: CookieIteratorNextCallbackFn, cb_data: *mut ()) -> bool {
		let data = Box::into_raw(Box::new(CookieIteratorNextCallbackData {
			callback: on_next,
			data: cb_data,
		}));

		let success = unsafe {
			cbw_CookieIterator_next(self.0, Some(ffi_cookie_iterator_next_handler), data as _)
		};

		return success > 0;
	}
}

unsafe extern "C" fn ffi_cookie_storage_callback_handler(
	cookie_jar: *mut cbw_CookieJar, _data: *mut c_void, error: cbw_Err,
) {
	let data_ptr = _data as *mut CookieStorageCallbackData;
	let data: Box<CookieStorageCallbackData> = Box::from_raw(data_ptr);

	let handle = CookieJarImpl(cookie_jar);
	let result = if error.code == 0 {
		Ok(())
	} else {
		Err(CookieStorageError::Unknown)
	};

	(data.callback)(handle, data.data, result);
}

unsafe extern "C" fn ffi_cookie_delete_callback_handler(
	cookie_jar: *mut cbw_CookieJar, _data: *mut c_void, deleted: c_uint,
) {
	let data_ptr = _data as *mut CookieDeleteCallbackData;
	let data: Box<CookieDeleteCallbackData> = Box::from_raw(data_ptr);

	let handle = CookieJarImpl(cookie_jar);

	(data.callback)(handle, data.data, deleted as _);
}

unsafe extern "C" fn ffi_cookie_iterator_next_handler(
	cookie_iterator: *mut cbw_CookieIterator, _data: *mut c_void, inner: *mut cbw_Cookie,
) {
	let data_ptr = _data as *mut CookieIteratorNextCallbackData;
	let data: Box<CookieIteratorNextCallbackData> = Box::from_raw(data_ptr);

	let handle = CookieIteratorImpl(cookie_iterator);
	let cookie = if inner == ptr::null_mut() {
		None
	} else {
		Some(CookieImpl(inner))
	};

	(data.callback)(handle, data.data, cookie);
}