use std::ffi::{CStr, c_char, c_void};
use std::ptr;
use servo::{PrefValue, Preferences};
pub type ServoPreferences = servo::Preferences;
const INVALID_PREFERENCE_NAME_MSG: &str = "Preference name must be a valid UTF8 string";
#[unsafe(no_mangle)]
pub extern "C" fn servo_preferences_create() -> *mut ServoPreferences {
let preferences = Box::new(servo::Preferences::default());
Box::into_raw(preferences)
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn servo_preferences_free(preferences: *mut ServoPreferences) {
assert!(
!preferences.is_null(),
"preferences pointer must not be null"
);
unsafe {
let _ = Box::from_raw(preferences);
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn servo_preferences_set_bool(
preferences: *mut ServoPreferences,
name: *const c_char,
value: bool,
) {
assert!(
!preferences.is_null(),
"preferences pointer must not be null"
);
assert!(!name.is_null(), "name pointer must not be null");
let name = unsafe { CStr::from_ptr(name) }
.to_str()
.expect(INVALID_PREFERENCE_NAME_MSG);
assert!(Preferences::exists(name), "unknown preference: {name}");
assert_eq!(
Preferences::type_of(name),
"bool",
"preference '{name}' is not a bool"
);
unsafe { &mut *preferences }.set_value(name, PrefValue::Bool(value));
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn servo_preferences_get_bool(
preferences: *const ServoPreferences,
name: *const c_char,
) -> bool {
assert!(
!preferences.is_null(),
"preferences pointer must not be null"
);
assert!(!name.is_null(), "name pointer must not be null");
let name = unsafe { CStr::from_ptr(name) }
.to_str()
.expect(INVALID_PREFERENCE_NAME_MSG);
assert!(Preferences::exists(name), "unknown preference: {name}");
assert_eq!(
Preferences::type_of(name),
"bool",
"preference '{name}' is not a bool"
);
match unsafe { &*preferences }.get_value(name) {
PrefValue::Bool(value) => value,
_ => unreachable!(),
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn servo_preferences_set_string(
preferences: *mut ServoPreferences,
name: *const c_char,
value: *const c_char,
) {
assert!(
!preferences.is_null(),
"preferences pointer must not be null"
);
assert!(!name.is_null(), "name pointer must not be null");
assert!(!value.is_null(), "value pointer must not be null");
let name = unsafe { CStr::from_ptr(name) }
.to_str()
.expect(INVALID_PREFERENCE_NAME_MSG);
let value = unsafe { CStr::from_ptr(value) }
.to_str()
.expect("invalid string value")
.to_string();
assert!(Preferences::exists(name), "unknown preference: {name}");
assert_eq!(
Preferences::type_of(name),
"alloc::string::String",
"preference '{name}' is not a string"
);
unsafe { &mut *preferences }.set_value(name, PrefValue::Str(value));
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn servo_preferences_get_string(
preferences: *const ServoPreferences,
name: *const c_char,
) -> *mut c_char {
assert!(
!preferences.is_null(),
"preferences pointer must not be null"
);
assert!(!name.is_null(), "name pointer must not be null");
let name = unsafe { CStr::from_ptr(name) }
.to_str()
.expect(INVALID_PREFERENCE_NAME_MSG);
assert!(Preferences::exists(name), "unknown preference: {name}");
assert_eq!(
Preferences::type_of(name),
"alloc::string::String",
"preference '{name}' is not a string"
);
match unsafe { &*preferences }.get_value(name) {
PrefValue::Str(value) => create_libc_string(&value),
_ => unreachable!(),
}
}
fn create_libc_string(string: &str) -> *mut c_char {
let Some(buffer_size) = string.len().checked_add(1) else {
return ptr::null_mut();
};
let dest_buffer = unsafe { libc::malloc(buffer_size) as *mut u8 };
if dest_buffer.is_null() {
return ptr::null_mut();
}
let source_buffer = string.as_ptr();
unsafe {
ptr::copy_nonoverlapping(source_buffer, dest_buffer, buffer_size - 1);
dest_buffer.add(buffer_size - 1).write(0);
}
dest_buffer as *mut c_char
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn servo_preferences_set_i64(
preferences: *mut ServoPreferences,
name: *const c_char,
value: i64,
) {
assert!(
!preferences.is_null(),
"preferences pointer must not be null"
);
assert!(!name.is_null(), "name pointer must not be null");
let name = unsafe { CStr::from_ptr(name) }
.to_str()
.expect(INVALID_PREFERENCE_NAME_MSG);
assert!(Preferences::exists(name), "unknown preference: {name}");
assert_eq!(
Preferences::type_of(name),
"i64",
"preference '{name}' is not an i64"
);
unsafe { &mut *preferences }.set_value(name, PrefValue::Int(value));
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn servo_preferences_get_i64(
preferences: *const ServoPreferences,
name: *const c_char,
) -> i64 {
assert!(
!preferences.is_null(),
"preferences pointer must not be null"
);
assert!(!name.is_null(), "name pointer must not be null");
let name = unsafe { CStr::from_ptr(name) }
.to_str()
.expect(INVALID_PREFERENCE_NAME_MSG);
assert!(Preferences::exists(name), "unknown preference: {name}");
assert_eq!(
Preferences::type_of(name),
"i64",
"preference '{name}' is not an i64"
);
match unsafe { &*preferences }.get_value(name) {
PrefValue::Int(value) => value,
_ => unreachable!(),
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn servo_preferences_set_u64(
preferences: *mut ServoPreferences,
name: *const c_char,
value: u64,
) {
assert!(
!preferences.is_null(),
"preferences pointer must not be null"
);
assert!(!name.is_null(), "name pointer must not be null");
let name = unsafe { CStr::from_ptr(name) }
.to_str()
.expect(INVALID_PREFERENCE_NAME_MSG);
assert!(Preferences::exists(name), "unknown preference: {name}");
assert_eq!(
Preferences::type_of(name),
"u64",
"preference '{name}' is not a u64"
);
unsafe { &mut *preferences }.set_value(name, PrefValue::UInt(value));
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn servo_preferences_get_u64(
preferences: *const ServoPreferences,
name: *const c_char,
) -> u64 {
assert!(
!preferences.is_null(),
"preferences pointer must not be null"
);
assert!(!name.is_null(), "name pointer must not be null");
let name = unsafe { CStr::from_ptr(name) }
.to_str()
.expect(INVALID_PREFERENCE_NAME_MSG);
assert!(Preferences::exists(name), "unknown preference: {name}");
assert_eq!(
Preferences::type_of(name),
"u64",
"preference '{name}' is not a u64"
);
match unsafe { &*preferences }.get_value(name) {
PrefValue::UInt(value) => value,
_ => unreachable!(),
}
}