Extract CEF rendered UI into a separate process and crate

This commit is contained in:
Timon
2026-07-10 00:27:12 +00:00
parent 97f8113fe4
commit 5daea64585
79 changed files with 4159 additions and 1257 deletions
+35
View File
@@ -0,0 +1,35 @@
use std::os::unix::process::CommandExt;
use std::process::Command;
#[cfg(feature = "accelerated_paint")]
use crate::frames::plane;
pub(crate) fn setup_command(command: &mut Command, #[cfg(feature = "accelerated_paint")] host_frame_fd: Option<std::os::fd::RawFd>) {
let parent_pid = std::process::id() as libc::pid_t;
// SAFETY: the closure runs in the forked child before exec and only makes async-signal-safe calls
unsafe {
command.pre_exec(move || {
// Tie the host's lifetime to the parent process
if libc::prctl(libc::PR_SET_PDEATHSIG, libc::SIGKILL) != 0 {
return Err(std::io::Error::last_os_error());
}
if libc::getppid() != parent_pid {
return Err(std::io::Error::other("main process died before PDEATHSIG was set"));
}
// Move the host end of the frame socket to its advertised fd
#[cfg(feature = "accelerated_paint")]
if let Some(fd) = host_frame_fd {
let target = plane::FRAME_SOCKET_CHILD_FD;
if fd == target {
if libc::fcntl(target, libc::F_SETFD, 0) != 0 {
return Err(std::io::Error::last_os_error());
}
} else if libc::dup2(fd, target) == -1 {
return Err(std::io::Error::last_os_error());
}
}
Ok(())
});
}
}
+63
View File
@@ -0,0 +1,63 @@
use objc2::rc::Retained;
use objc2::runtime::Bool;
use objc2::{ClassType, define_class, msg_send};
use objc2_app_kit::{NSApplication, NSApplicationActivationPolicy, NSEvent, NSResponder};
use objc2_foundation::NSObject;
use std::sync::atomic::{AtomicBool, Ordering};
use std::time::Duration;
use cef::application_mac::{CefAppProtocol, CrAppControlProtocol, CrAppProtocol};
static HANDLING_SEND_EVENT: AtomicBool = AtomicBool::new(false);
define_class!(
#[unsafe(super(NSApplication, NSResponder, NSObject))]
#[name = "GraphiteCefHostApplication"]
struct CefHostApplication;
unsafe impl CrAppProtocol for CefHostApplication {
#[unsafe(method(isHandlingSendEvent))]
fn is_handling_send_event(&self) -> Bool {
Bool::new(HANDLING_SEND_EVENT.load(Ordering::Relaxed))
}
}
unsafe impl CrAppControlProtocol for CefHostApplication {
#[unsafe(method(setHandlingSendEvent:))]
fn set_handling_send_event(&self, handling: Bool) {
HANDLING_SEND_EVENT.store(handling.as_bool(), Ordering::Relaxed);
}
}
unsafe impl CefAppProtocol for CefHostApplication {}
impl CefHostApplication {
#[unsafe(method(sendEvent:))]
fn send_event(&self, event: &NSEvent) {
let was_handling = HANDLING_SEND_EVENT.swap(true, Ordering::Relaxed);
let _: () = unsafe { msg_send![super(self), sendEvent: event] };
HANDLING_SEND_EVENT.store(was_handling, Ordering::Relaxed);
}
}
);
pub(crate) fn install_application() {
let app: Retained<NSApplication> = unsafe { msg_send![CefHostApplication::class(), sharedApplication] };
app.setActivationPolicy(NSApplicationActivationPolicy::Prohibited);
}
pub(crate) fn spawn_parent_watchdog(main_pid: u32) {
let result = std::thread::Builder::new().name("parent-watchdog".to_string()).spawn(move || {
loop {
// SAFETY: getppid is always safe to call.
if unsafe { libc::getppid() } as u32 != main_pid {
tracing::warn!("Main process is gone, exiting CEF host");
std::process::exit(0);
}
std::thread::sleep(Duration::from_millis(500));
}
});
if let Err(e) = result {
tracing::error!("Failed to spawn the parent watchdog: {e}");
}
}
+39
View File
@@ -0,0 +1,39 @@
use windows::Win32::Foundation::{CloseHandle, HANDLE};
use windows::Win32::System::JobObjects::{
AssignProcessToJobObject, CreateJobObjectW, JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE, JOBOBJECT_EXTENDED_LIMIT_INFORMATION, JobObjectExtendedLimitInformation, SetInformationJobObject,
};
use windows::core::PCWSTR;
pub(crate) struct KillOnCloseJob(HANDLE);
// SAFETY: job object handles may be used and closed from any thread.
unsafe impl Send for KillOnCloseJob {}
unsafe impl Sync for KillOnCloseJob {}
impl KillOnCloseJob {
pub(crate) fn assign(child: &std::process::Child) -> windows::core::Result<Self> {
use std::os::windows::io::AsRawHandle;
unsafe {
let job = CreateJobObjectW(None, PCWSTR::null())?;
let job = Self(job);
let mut info = JOBOBJECT_EXTENDED_LIMIT_INFORMATION::default();
info.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
SetInformationJobObject(
job.0,
JobObjectExtendedLimitInformation,
&info as *const _ as *const core::ffi::c_void,
std::mem::size_of::<JOBOBJECT_EXTENDED_LIMIT_INFORMATION>() as u32,
)?;
AssignProcessToJobObject(job.0, HANDLE(child.as_raw_handle()))?;
Ok(job)
}
}
}
impl Drop for KillOnCloseJob {
fn drop(&mut self) {
unsafe {
let _ = CloseHandle(self.0);
}
}
}