use super::*;
use std::ffi::{CStr, CString};
use std::os::raw::{c_char, c_void};
use day_spec::NodeId;
use day_toolkit_winui::{WinHandle, Xaml};
unsafe extern "C" {
fn day_webview_xaml_new(
url: *const c_char,
id: u64,
cb: extern "C" fn(u64, *const c_char),
inline_root: *const c_char,
inline_start: *const c_char,
inline_dir: *const c_char,
link_cb: extern "C" fn(u64, *const c_char),
transparent: bool,
session: u64,
profile: *const c_char,
directory: *const c_char,
private: bool,
) -> *mut c_void;
fn day_webview_xaml_tabs(
handle: *mut c_void,
foreground: *const c_char,
background: *const c_char,
callback: extern "C" fn(u64, *const c_char, bool),
);
fn day_webview_xaml_release(handle: *mut c_void);
fn day_webview_xaml_load(handle: *mut c_void, url: *const c_char);
fn day_webview_xaml_back(handle: *mut c_void);
fn day_webview_xaml_forward(handle: *mut c_void);
fn day_webview_xaml_stop(handle: *mut c_void);
fn day_webview_xaml_reload(handle: *mut c_void);
fn day_webview_xaml_force_reload(w: *mut c_void);
fn day_webview_xaml_set_eval_cb(cb: extern "C" fn(u64, u64, *const c_char));
fn day_webview_xaml_eval(handle: *mut c_void, req: u64, script: *const c_char);
}
extern "C" fn on_eval(id: u64, req: u64, payload: *const c_char) {
let text = if payload.is_null() {
String::new()
} else {
unsafe { CStr::from_ptr(payload) }
.to_string_lossy()
.into_owned()
};
day_toolkit_winui::emit(
NodeId(id),
Event::Custom {
tag: "webview:eval",
num: req as f64,
text,
},
);
}
extern "C" fn on_url(id: u64, url: *const c_char) {
if url.is_null() {
return;
}
let s = unsafe { CStr::from_ptr(url) }
.to_string_lossy()
.into_owned();
day_toolkit_winui::emit(NodeId(id), Event::custom("webview:url", s));
}
extern "C" fn on_link(id: u64, url: *const c_char) {
if url.is_null() {
return;
}
let s = unsafe { CStr::from_ptr(url) }
.to_string_lossy()
.into_owned();
day_toolkit_winui::emit(
NodeId(id),
Event::Custom {
tag: "webview:link",
num: super::LINK_REPORT,
text: s,
},
);
}
extern "C" fn on_tab(id: u64, url: *const c_char, background: bool) {
if !url.is_null() {
day_toolkit_winui::emit(
NodeId(id),
new_tab_event(
unsafe { CStr::from_ptr(url) }
.to_string_lossy()
.into_owned(),
background,
),
);
}
}
fn cstr(s: &str) -> CString {
CString::new(s).unwrap_or_default()
}
fn inline_dir(p: &WebProps) -> String {
if p.inline_root.is_empty() {
return String::new();
}
let Some(site) = day_spec::resolve_asset_dir(&p.inline_root) else {
return String::new();
};
let mut base = site;
for _ in p.inline_root.split('/').filter(|seg| !seg.is_empty()) {
let Some(parent) = base.parent().map(|dir| dir.to_path_buf()) else {
return String::new();
};
base = parent;
}
super::xaml_path::mapping_folder(&base)
}
fn make(_backend: &mut Xaml, p: &WebProps, id: NodeId) -> WinHandle {
static EVAL_CB: std::sync::Once = std::sync::Once::new();
EVAL_CB.call_once(|| unsafe { day_webview_xaml_set_eval_cb(on_eval) });
let dir = inline_dir(p);
if !p.inline_root.is_empty() && dir.is_empty() {
log::warn!(
"day-piece-webview: inline site {:?} not found in the staged assets",
p.inline_root
);
}
let handle = WinHandle(unsafe {
day_webview_xaml_new(
cstr(&p.url).as_ptr(),
id.0,
on_url,
cstr(&p.inline_root).as_ptr(),
cstr(&p.inline_start).as_ptr(),
cstr(&dir).as_ptr(),
on_link,
p.transparent,
p.session,
cstr(&p.profile.storage_key()).as_ptr(),
cstr(p.profile.directory.as_deref().unwrap_or("")).as_ptr(),
p.profile.private,
)
});
if let Some(labels) = &p.tabs {
unsafe {
day_webview_xaml_tabs(
handle.0,
cstr(&labels.foreground).as_ptr(),
cstr(&labels.background).as_ptr(),
on_tab,
);
}
}
handle
}
fn update(_backend: &mut Xaml, h: &WinHandle, patch: &WebPatch) {
unsafe {
match patch {
WebPatch::Load(url) => day_webview_xaml_load(h.0, cstr(url).as_ptr()),
WebPatch::Back => day_webview_xaml_back(h.0),
WebPatch::Forward => day_webview_xaml_forward(h.0),
WebPatch::Stop => day_webview_xaml_stop(h.0),
WebPatch::Reload => day_webview_xaml_reload(h.0),
WebPatch::ForceReload => day_webview_xaml_force_reload(h.0),
WebPatch::Eval { req, script } => {
day_webview_xaml_eval(h.0, *req, cstr(script).as_ptr())
}
}
}
}
fn release(_backend: &mut Xaml, h: &WinHandle) {
unsafe { day_webview_xaml_release(h.0) };
}
day_pieces::renderer!(day_toolkit_winui::RENDERERS, Xaml,
kind: KIND, props: WebProps, patch: WebPatch,
make: make, update: update, measure: day_pieces::fill_measure, release: release);