From d6e55a88b59adc5695eaefb595be9fa1ae849ee8 Mon Sep 17 00:00:00 2001 From: Timon Schelling Date: Mon, 28 Jul 2025 03:20:56 +0000 Subject: [PATCH 1/6] Rename CEF implementations to match the process they are called in --- desktop/src/cef/internal.rs | 12 ++++++------ .../cef/internal/{app.rs => browser_process_app.rs} | 0 .../{client.rs => browser_process_client.rs} | 0 .../{non_browser_app.rs => render_process_app.rs} | 0 4 files changed, 6 insertions(+), 6 deletions(-) rename desktop/src/cef/internal/{app.rs => browser_process_app.rs} (100%) rename desktop/src/cef/internal/{client.rs => browser_process_client.rs} (100%) rename desktop/src/cef/internal/{non_browser_app.rs => render_process_app.rs} (100%) diff --git a/desktop/src/cef/internal.rs b/desktop/src/cef/internal.rs index 8473884fd3..e9491e3315 100644 --- a/desktop/src/cef/internal.rs +++ b/desktop/src/cef/internal.rs @@ -1,10 +1,10 @@ -mod app; +mod browser_process_app; +mod browser_process_client; mod browser_process_handler; -mod client; -mod non_browser_app; mod render_handler; +mod render_process_app; -pub(crate) use app::AppImpl; -pub(crate) use client::ClientImpl; -pub(crate) use non_browser_app::NonBrowserAppImpl; +pub(crate) use browser_process_app::AppImpl; +pub(crate) use browser_process_client::ClientImpl; pub(crate) use render_handler::RenderHandlerImpl; +pub(crate) use render_process_app::NonBrowserAppImpl; diff --git a/desktop/src/cef/internal/app.rs b/desktop/src/cef/internal/browser_process_app.rs similarity index 100% rename from desktop/src/cef/internal/app.rs rename to desktop/src/cef/internal/browser_process_app.rs diff --git a/desktop/src/cef/internal/client.rs b/desktop/src/cef/internal/browser_process_client.rs similarity index 100% rename from desktop/src/cef/internal/client.rs rename to desktop/src/cef/internal/browser_process_client.rs diff --git a/desktop/src/cef/internal/non_browser_app.rs b/desktop/src/cef/internal/render_process_app.rs similarity index 100% rename from desktop/src/cef/internal/non_browser_app.rs rename to desktop/src/cef/internal/render_process_app.rs From 0263b66a433e5b2a50055c7016f28f41a5ee2475 Mon Sep 17 00:00:00 2001 From: Timon Schelling Date: Sun, 27 Jul 2025 21:50:14 +0000 Subject: [PATCH 2/6] Rename CEF implementations to match the process they are called in --- desktop/src/cef/context.rs | 10 +++++----- desktop/src/cef/internal.rs | 6 +++--- .../src/cef/internal/browser_process_app.rs | 13 +++++++------ .../cef/internal/browser_process_client.rs | 19 ++++++++++++------- .../src/cef/internal/render_process_app.rs | 12 ++++++------ 5 files changed, 33 insertions(+), 27 deletions(-) diff --git a/desktop/src/cef/context.rs b/desktop/src/cef/context.rs index a26aa93329..cf823448b5 100644 --- a/desktop/src/cef/context.rs +++ b/desktop/src/cef/context.rs @@ -10,7 +10,7 @@ use super::input::InputState; use super::scheme_handler::{FRONTEND_DOMAIN, GRAPHITE_SCHEME}; use super::{CefEventHandler, input}; -use super::internal::{AppImpl, ClientImpl, NonBrowserAppImpl, RenderHandlerImpl}; +use super::internal::{BrowserProcessAppImpl, BrowserProcessClientImpl, RenderHandlerImpl, RenderProcessAppImpl}; pub(crate) struct Setup {} pub(crate) struct Initialized {} @@ -42,7 +42,7 @@ impl Context { if !is_browser_process { let process_type = CefString::from(&cmd.switch_value(Some(&switch))); - let mut app = NonBrowserAppImpl::app(); + let mut app = RenderProcessAppImpl::app(); let ret = execute_process(Some(args.as_main_args()), Some(&mut app), std::ptr::null_mut()); if ret >= 0 { return Err(SetupError::SubprocessFailed(process_type.to_string())); @@ -70,7 +70,7 @@ impl Context { }; // Attention! Wrapping this in an extra App is necessary, otherwise the program still compiles but segfaults - let mut cef_app = App::new(AppImpl::new(event_handler.clone())); + let mut cef_app = App::new(BrowserProcessAppImpl::new(event_handler.clone())); let result = initialize(Some(self.args.as_main_args()), Some(&settings), Some(&mut cef_app), std::ptr::null_mut()); if result != 1 { @@ -81,8 +81,8 @@ impl Context { return Err(InitError::InitializationFailed(cef_exit_code)); } - let render_handler = RenderHandlerImpl::new(event_handler.clone()); - let mut client = Client::new(ClientImpl::new(RenderHandler::new(render_handler))); + let render_handler = RenderHandler::new(RenderHandlerImpl::new(event_handler.clone())); + let mut client = Client::new(BrowserProcessClientImpl::new(render_handler, event_handler.clone())); let url = CefString::from(format!("{GRAPHITE_SCHEME}://{FRONTEND_DOMAIN}/").as_str()); diff --git a/desktop/src/cef/internal.rs b/desktop/src/cef/internal.rs index e9491e3315..b36c704d64 100644 --- a/desktop/src/cef/internal.rs +++ b/desktop/src/cef/internal.rs @@ -4,7 +4,7 @@ mod browser_process_handler; mod render_handler; mod render_process_app; -pub(crate) use browser_process_app::AppImpl; -pub(crate) use browser_process_client::ClientImpl; +pub(crate) use browser_process_app::BrowserProcessAppImpl; +pub(crate) use browser_process_client::BrowserProcessClientImpl; pub(crate) use render_handler::RenderHandlerImpl; -pub(crate) use render_process_app::NonBrowserAppImpl; +pub(crate) use render_process_app::RenderProcessAppImpl; diff --git a/desktop/src/cef/internal/browser_process_app.rs b/desktop/src/cef/internal/browser_process_app.rs index 1ec22d6ec8..82999c5872 100644 --- a/desktop/src/cef/internal/browser_process_app.rs +++ b/desktop/src/cef/internal/browser_process_app.rs @@ -5,15 +5,16 @@ use cef::sys::{_cef_app_t, cef_base_ref_counted_t}; use cef::{BrowserProcessHandler, CefString, ImplApp, ImplCommandLine, SchemeRegistrar, WrapApp}; use crate::cef::CefEventHandler; + use crate::cef::scheme_handler::GraphiteSchemeHandlerFactory; use super::browser_process_handler::BrowserProcessHandlerImpl; -pub(crate) struct AppImpl { +pub(crate) struct BrowserProcessAppImpl { object: *mut RcImpl<_cef_app_t, Self>, event_handler: H, } -impl AppImpl { +impl BrowserProcessAppImpl { pub(crate) fn new(event_handler: H) -> Self { Self { object: std::ptr::null_mut(), @@ -22,7 +23,7 @@ impl AppImpl { } } -impl ImplApp for AppImpl { +impl ImplApp for BrowserProcessAppImpl { fn browser_process_handler(&self) -> Option { Some(BrowserProcessHandler::new(BrowserProcessHandlerImpl::new(self.event_handler.clone()))) } @@ -58,7 +59,7 @@ impl ImplApp for AppImpl { } } -impl Clone for AppImpl { +impl Clone for BrowserProcessAppImpl { fn clone(&self) -> Self { unsafe { let rc_impl = &mut *self.object; @@ -70,7 +71,7 @@ impl Clone for AppImpl { } } } -impl Rc for AppImpl { +impl Rc for BrowserProcessAppImpl { fn as_base(&self) -> &cef_base_ref_counted_t { unsafe { let base = &*self.object; @@ -78,7 +79,7 @@ impl Rc for AppImpl { } } } -impl WrapApp for AppImpl { +impl WrapApp for BrowserProcessAppImpl { fn wrap_rc(&mut self, object: *mut RcImpl<_cef_app_t, Self>) { self.object = object; } diff --git a/desktop/src/cef/internal/browser_process_client.rs b/desktop/src/cef/internal/browser_process_client.rs index 543f9c590d..f59e2a64f0 100644 --- a/desktop/src/cef/internal/browser_process_client.rs +++ b/desktop/src/cef/internal/browser_process_client.rs @@ -2,20 +2,24 @@ use cef::rc::{Rc, RcImpl}; use cef::sys::{_cef_client_t, cef_base_ref_counted_t}; use cef::{ImplClient, RenderHandler, WrapClient}; -pub(crate) struct ClientImpl { +use crate::cef::CefEventHandler; + +pub(crate) struct BrowserProcessClientImpl { object: *mut RcImpl<_cef_client_t, Self>, render_handler: RenderHandler, + event_handler: H, } -impl ClientImpl { - pub(crate) fn new(render_handler: RenderHandler) -> Self { +impl BrowserProcessClientImpl { + pub(crate) fn new(render_handler: RenderHandler, event_handler: H) -> Self { Self { object: std::ptr::null_mut(), render_handler, + event_handler, } } } -impl ImplClient for ClientImpl { +impl ImplClient for BrowserProcessClientImpl { fn render_handler(&self) -> Option { Some(self.render_handler.clone()) } @@ -25,7 +29,7 @@ impl ImplClient for ClientImpl { } } -impl Clone for ClientImpl { +impl Clone for BrowserProcessClientImpl { fn clone(&self) -> Self { unsafe { let rc_impl = &mut *self.object; @@ -34,10 +38,11 @@ impl Clone for ClientImpl { Self { object: self.object, render_handler: self.render_handler.clone(), + event_handler: self.event_handler.clone(), } } } -impl Rc for ClientImpl { +impl Rc for BrowserProcessClientImpl { fn as_base(&self) -> &cef_base_ref_counted_t { unsafe { let base = &*self.object; @@ -45,7 +50,7 @@ impl Rc for ClientImpl { } } } -impl WrapClient for ClientImpl { +impl WrapClient for BrowserProcessClientImpl { fn wrap_rc(&mut self, object: *mut RcImpl<_cef_client_t, Self>) { self.object = object; } diff --git a/desktop/src/cef/internal/render_process_app.rs b/desktop/src/cef/internal/render_process_app.rs index 04007d729d..55796a7e01 100644 --- a/desktop/src/cef/internal/render_process_app.rs +++ b/desktop/src/cef/internal/render_process_app.rs @@ -4,16 +4,16 @@ use cef::{App, ImplApp, SchemeRegistrar, WrapApp}; use crate::cef::scheme_handler::GraphiteSchemeHandlerFactory; -pub(crate) struct NonBrowserAppImpl { +pub(crate) struct RenderProcessAppImpl { object: *mut RcImpl<_cef_app_t, Self>, } -impl NonBrowserAppImpl { +impl RenderProcessAppImpl { pub(crate) fn app() -> App { App::new(Self { object: std::ptr::null_mut() }) } } -impl ImplApp for NonBrowserAppImpl { +impl ImplApp for RenderProcessAppImpl { fn on_register_custom_schemes(&self, registrar: Option<&mut SchemeRegistrar>) { GraphiteSchemeHandlerFactory::register_schemes(registrar); } @@ -23,7 +23,7 @@ impl ImplApp for NonBrowserAppImpl { } } -impl Clone for NonBrowserAppImpl { +impl Clone for RenderProcessAppImpl { fn clone(&self) -> Self { unsafe { let rc_impl = &mut *self.object; @@ -32,7 +32,7 @@ impl Clone for NonBrowserAppImpl { Self { object: self.object } } } -impl Rc for NonBrowserAppImpl { +impl Rc for RenderProcessAppImpl { fn as_base(&self) -> &cef_base_ref_counted_t { unsafe { let base = &*self.object; @@ -40,7 +40,7 @@ impl Rc for NonBrowserAppImpl { } } } -impl WrapApp for NonBrowserAppImpl { +impl WrapApp for RenderProcessAppImpl { fn wrap_rc(&mut self, object: *mut RcImpl<_cef_app_t, Self>) { self.object = object; } From 2f6e8814ed81c7dbbc9138c17020b663519ed188 Mon Sep 17 00:00:00 2001 From: Timon Schelling Date: Mon, 28 Jul 2025 06:23:03 +0000 Subject: [PATCH 3/6] Implement ipc abstraction --- desktop/src/cef.rs | 2 + desktop/src/cef/internal.rs | 1 + .../cef/internal/browser_process_client.rs | 21 +++ .../src/cef/internal/render_process_app.rs | 18 ++- .../cef/internal/render_process_handler.rs | 63 +++++++++ desktop/src/cef/ipc.rs | 121 ++++++++++++++++++ desktop/src/cef/utility.rs | 6 + desktop/src/main.rs | 2 - 8 files changed, 229 insertions(+), 5 deletions(-) create mode 100644 desktop/src/cef/internal/render_process_handler.rs create mode 100644 desktop/src/cef/ipc.rs create mode 100644 desktop/src/cef/utility.rs diff --git a/desktop/src/cef.rs b/desktop/src/cef.rs index d84e9ad672..454ec2d5bf 100644 --- a/desktop/src/cef.rs +++ b/desktop/src/cef.rs @@ -8,7 +8,9 @@ mod context; mod dirs; mod input; mod internal; +mod ipc; mod scheme_handler; +mod utility; pub(crate) use context::{Context, InitError, Initialized, Setup, SetupError}; use winit::event_loop::EventLoopProxy; diff --git a/desktop/src/cef/internal.rs b/desktop/src/cef/internal.rs index b36c704d64..2cab906404 100644 --- a/desktop/src/cef/internal.rs +++ b/desktop/src/cef/internal.rs @@ -3,6 +3,7 @@ mod browser_process_client; mod browser_process_handler; mod render_handler; mod render_process_app; +mod render_process_handler; pub(crate) use browser_process_app::BrowserProcessAppImpl; pub(crate) use browser_process_client::BrowserProcessClientImpl; diff --git a/desktop/src/cef/internal/browser_process_client.rs b/desktop/src/cef/internal/browser_process_client.rs index f59e2a64f0..6e13c80327 100644 --- a/desktop/src/cef/internal/browser_process_client.rs +++ b/desktop/src/cef/internal/browser_process_client.rs @@ -3,6 +3,7 @@ use cef::sys::{_cef_client_t, cef_base_ref_counted_t}; use cef::{ImplClient, RenderHandler, WrapClient}; use crate::cef::CefEventHandler; +use crate::cef::ipc::{MessageType, UnpackMessage, UnpackedMessage}; pub(crate) struct BrowserProcessClientImpl { object: *mut RcImpl<_cef_client_t, Self>, @@ -20,6 +21,26 @@ impl BrowserProcessClientImpl { } impl ImplClient for BrowserProcessClientImpl { + fn on_process_message_received( + &self, + _browser: Option<&mut cef::Browser>, + _frame: Option<&mut cef::Frame>, + _source_process: cef::ProcessId, + message: Option<&mut cef::ProcessMessage>, + ) -> ::std::os::raw::c_int { + match message.unpack() { + Some(UnpackedMessage { + message_type: MessageType::SendToNative, + data, + }) => {} + _ => { + tracing::error!("Unexpected message type received in browser process"); + return 0; + } + } + 1 + } + fn render_handler(&self) -> Option { Some(self.render_handler.clone()) } diff --git a/desktop/src/cef/internal/render_process_app.rs b/desktop/src/cef/internal/render_process_app.rs index 55796a7e01..d9e66bafbd 100644 --- a/desktop/src/cef/internal/render_process_app.rs +++ b/desktop/src/cef/internal/render_process_app.rs @@ -1,15 +1,20 @@ use cef::rc::{Rc, RcImpl}; use cef::sys::{_cef_app_t, cef_base_ref_counted_t}; -use cef::{App, ImplApp, SchemeRegistrar, WrapApp}; +use cef::{App, ImplApp, RenderProcessHandler, SchemeRegistrar, WrapApp}; +use super::render_process_handler::RenderProcessHandlerImpl; use crate::cef::scheme_handler::GraphiteSchemeHandlerFactory; pub(crate) struct RenderProcessAppImpl { object: *mut RcImpl<_cef_app_t, Self>, + render_process_handler: RenderProcessHandler, } impl RenderProcessAppImpl { pub(crate) fn app() -> App { - App::new(Self { object: std::ptr::null_mut() }) + App::new(Self { + object: std::ptr::null_mut(), + render_process_handler: RenderProcessHandler::new(RenderProcessHandlerImpl::new()), + }) } } @@ -18,6 +23,10 @@ impl ImplApp for RenderProcessAppImpl { GraphiteSchemeHandlerFactory::register_schemes(registrar); } + fn render_process_handler(&self) -> Option { + Some(self.render_process_handler.clone()) + } + fn get_raw(&self) -> *mut _cef_app_t { self.object.cast() } @@ -29,7 +38,10 @@ impl Clone for RenderProcessAppImpl { let rc_impl = &mut *self.object; rc_impl.interface.add_ref(); } - Self { object: self.object } + Self { + object: self.object, + render_process_handler: self.render_process_handler.clone(), + } } } impl Rc for RenderProcessAppImpl { diff --git a/desktop/src/cef/internal/render_process_handler.rs b/desktop/src/cef/internal/render_process_handler.rs new file mode 100644 index 0000000000..b50aaee42a --- /dev/null +++ b/desktop/src/cef/internal/render_process_handler.rs @@ -0,0 +1,63 @@ +use cef::rc::{Rc, RcImpl}; +use cef::sys::{_cef_render_process_handler_t, cef_base_ref_counted_t, cef_render_process_handler_t}; +use cef::{ImplRenderProcessHandler, WrapRenderProcessHandler}; + +use crate::cef::ipc::{MessageType, UnpackMessage, UnpackedMessage}; + +pub(crate) struct RenderProcessHandlerImpl { + object: *mut RcImpl, +} +impl RenderProcessHandlerImpl { + pub(crate) fn new() -> Self { + Self { object: std::ptr::null_mut() } + } +} + +impl ImplRenderProcessHandler for RenderProcessHandlerImpl { + fn on_process_message_received( + &self, + _browser: Option<&mut cef::Browser>, + _frame: Option<&mut cef::Frame>, + _source_process: cef::ProcessId, + message: Option<&mut cef::ProcessMessage>, + ) -> ::std::os::raw::c_int { + match message.unpack() { + Some(UnpackedMessage { + message_type: MessageType::SendToJS, + data, + }) => {} + _ => { + tracing::error!("Unexpected message type received in render process"); + return 0; + } + } + 1 + } + + fn get_raw(&self) -> *mut _cef_render_process_handler_t { + self.object.cast() + } +} + +impl Clone for RenderProcessHandlerImpl { + fn clone(&self) -> Self { + unsafe { + let rc_impl = &mut *self.object; + rc_impl.interface.add_ref(); + } + Self { object: self.object } + } +} +impl Rc for RenderProcessHandlerImpl { + fn as_base(&self) -> &cef_base_ref_counted_t { + unsafe { + let base = &*self.object; + std::mem::transmute(&base.cef_object) + } + } +} +impl WrapRenderProcessHandler for RenderProcessHandlerImpl { + fn wrap_rc(&mut self, object: *mut RcImpl<_cef_render_process_handler_t, Self>) { + self.object = object; + } +} diff --git a/desktop/src/cef/ipc.rs b/desktop/src/cef/ipc.rs new file mode 100644 index 0000000000..9241c20230 --- /dev/null +++ b/desktop/src/cef/ipc.rs @@ -0,0 +1,121 @@ +use cef::{CefString, Frame, ImplBinaryValue, ImplBrowser, ImplFrame, ImplListValue, ImplProcessMessage, ImplV8Context, ProcessId, V8Context, rc::ConvertParam, sys::cef_process_id_t}; + +use super::{Context, Initialized}; + +pub(crate) enum MessageType { + SendToJS, + SendToNative, +} +impl From for MessageInfo { + fn from(val: MessageType) -> Self { + match val { + MessageType::SendToJS => MessageInfo { + name: "send_to_js".to_string(), + target: cef_process_id_t::PID_RENDERER.into(), + }, + MessageType::SendToNative => MessageInfo { + name: "send_to_native".to_string(), + target: cef_process_id_t::PID_BROWSER.into(), + }, + } + } +} +impl TryFrom for MessageType { + type Error = (); + fn try_from(value: String) -> Result { + match value.as_str() { + "send_to_js" => Ok(MessageType::SendToJS), + "send_to_native" => Ok(MessageType::SendToNative), + _ => Err(()), + } + } +} + +pub(crate) struct MessageInfo { + name: String, + target: ProcessId, +} + +pub(crate) trait SendMessage { + fn send_message(&self, message_type: MessageType, message: &[u8]); +} +impl SendMessage for Context { + fn send_message(&self, message_type: MessageType, message: &[u8]) { + let Some(browser) = &self.browser else { + tracing::error!("Browser is not initialized, cannot send message"); + return; + }; + + let Some(frame) = browser.main_frame() else { + tracing::error!("Main frame is not available, cannot send message"); + return; + }; + + frame.send_message(message_type, message); + } +} +impl SendMessage for Option { + fn send_message(&self, message_type: MessageType, message: &[u8]) { + let Some(context) = self else { + tracing::error!("Current V8 context is not available, cannot send message"); + return; + }; + + context.send_message(message_type, message); + } +} +impl SendMessage for V8Context { + fn send_message(&self, message_type: MessageType, message: &[u8]) { + let Some(frame) = self.frame() else { + tracing::error!("Current V8 context does not have a frame, cannot send message"); + return; + }; + + frame.send_message(message_type, message); + } +} +impl SendMessage for Frame { + fn send_message(&self, message_type: MessageType, message: &[u8]) { + let MessageInfo { name, target } = message_type.into(); + + let Some(mut process_message) = cef::process_message_create(Some(&CefString::from(name.as_str()))) else { + tracing::error!("Failed to create process message: {}", name); + return; + }; + let Some(arg_list) = process_message.argument_list() else { return }; + let mut value = ::cef::binary_value_create(Some(message)); + arg_list.set_binary(0, value.as_mut()); + + self.send_process_message(target, Some(&mut process_message)); + } +} + +pub(crate) struct UnpackedMessage<'a> { + pub(crate) message_type: MessageType, + pub(crate) data: &'a [u8], +} +pub(crate) trait UnpackMessage { + fn unpack(&'_ self) -> Option>; +} +impl UnpackMessage for Option<&mut cef::ProcessMessage> { + fn unpack(&'_ self) -> Option> { + let Some(message) = self else { return None }; + message.unpack() + } +} +impl UnpackMessage for cef::ProcessMessage { + fn unpack(&'_ self) -> Option> { + let pointer: *mut cef::sys::_cef_string_utf16_t = self.name().into(); + let message = unsafe { super::utility::pointer_to_string(pointer) }; + let Ok(message_type) = message.try_into() else { + tracing::error!("Failed to get message type from process message"); + return None; + }; + let arglist = self.argument_list()?; + let binary = arglist.binary(0)?; + let size = binary.size(); + let ptr = binary.raw_data(); + let buffer = unsafe { std::slice::from_raw_parts(ptr as *const u8, size) }; + Some(UnpackedMessage { message_type, data: buffer }) + } +} diff --git a/desktop/src/cef/utility.rs b/desktop/src/cef/utility.rs new file mode 100644 index 0000000000..d56593642f --- /dev/null +++ b/desktop/src/cef/utility.rs @@ -0,0 +1,6 @@ +pub unsafe fn pointer_to_string(pointer: *mut cef::sys::_cef_string_utf16_t) -> String { + let str = unsafe { (*pointer).str_ }; + let len = unsafe { (*pointer).length }; + let slice = unsafe { std::slice::from_raw_parts(str, len) }; + String::from_utf16(slice).unwrap() +} diff --git a/desktop/src/main.rs b/desktop/src/main.rs index 6ce3517971..edaafcd272 100644 --- a/desktop/src/main.rs +++ b/desktop/src/main.rs @@ -51,8 +51,6 @@ fn main() { } }; - tracing::info!("Cef initialized successfully"); - let mut winit_app = WinitApp::new(cef_context, window_size_sender, wgpu_context); event_loop.run_app(&mut winit_app).unwrap(); From 07f652905d5004f92bf571073dd592bb82cf5db2 Mon Sep 17 00:00:00 2001 From: Timon Schelling Date: Mon, 28 Jul 2025 08:31:03 +0000 Subject: [PATCH 4/6] Call js function `receiveNativeMessage` for all SendToJS ipc messages --- .../cef/internal/render_process_handler.rs | 46 +++++++++++++++++-- 1 file changed, 41 insertions(+), 5 deletions(-) diff --git a/desktop/src/cef/internal/render_process_handler.rs b/desktop/src/cef/internal/render_process_handler.rs index b50aaee42a..60a7467519 100644 --- a/desktop/src/cef/internal/render_process_handler.rs +++ b/desktop/src/cef/internal/render_process_handler.rs @@ -1,6 +1,6 @@ -use cef::rc::{Rc, RcImpl}; -use cef::sys::{_cef_render_process_handler_t, cef_base_ref_counted_t, cef_render_process_handler_t}; -use cef::{ImplRenderProcessHandler, WrapRenderProcessHandler}; +use cef::rc::{ConvertReturnValue, Rc, RcImpl}; +use cef::sys::{_cef_render_process_handler_t, cef_base_ref_counted_t, cef_render_process_handler_t, cef_v8_propertyattribute_t, cef_v8_value_create_array_buffer_with_copy}; +use cef::{CefString, ImplFrame, ImplRenderProcessHandler, ImplV8Context, ImplV8Value, V8Value, WrapRenderProcessHandler, v8_context_get_entered_context}; use crate::cef::ipc::{MessageType, UnpackMessage, UnpackedMessage}; @@ -17,7 +17,7 @@ impl ImplRenderProcessHandler for RenderProcessHandlerImpl { fn on_process_message_received( &self, _browser: Option<&mut cef::Browser>, - _frame: Option<&mut cef::Frame>, + frame: Option<&mut cef::Frame>, _source_process: cef::ProcessId, message: Option<&mut cef::ProcessMessage>, ) -> ::std::os::raw::c_int { @@ -25,7 +25,43 @@ impl ImplRenderProcessHandler for RenderProcessHandlerImpl { Some(UnpackedMessage { message_type: MessageType::SendToJS, data, - }) => {} + }) => { + let Some(frame) = frame else { + tracing::error!("Frame is not available"); + return 0; + }; + let Some(context) = frame.v8_context() else { + tracing::error!("V8 context is not available"); + return 0; + }; + if context.enter() == 0 { + tracing::error!("Failed to enter V8 context"); + return 0; + } + let mut value: V8Value = unsafe { cef_v8_value_create_array_buffer_with_copy(data.as_ptr() as *mut std::ffi::c_void, data.len()) }.wrap_result(); + let Some(global) = context.global() else { + tracing::error!("Global object is not available in V8 context"); + return 0; + }; + + let function_name = "receiveNativeMessage"; + let property_name = "receiveNativeMessageData"; + + let function_call = format!("window.{function_name}(window.{property_name})"); + + global.set_value_bykey( + Some(&CefString::from(property_name)), + Some(&mut value), + cef_v8_propertyattribute_t::V8_PROPERTY_ATTRIBUTE_READONLY.wrap_result(), + ); + + frame.execute_java_script(Some(&CefString::from(function_call.as_str())), None, 0); + + if context.exit() == 0 { + tracing::error!("Failed to exit V8 context"); + return 0; + } + } _ => { tracing::error!("Unexpected message type received in render process"); return 0; From 50fc393fe3ed2cd1cd5797ca5daa250c99d6987f Mon Sep 17 00:00:00 2001 From: Timon Schelling Date: Mon, 28 Jul 2025 09:12:50 +0000 Subject: [PATCH 5/6] Allow js to call `sendNativeMessage` for sending messages to the browser process Co-authored-by: Adam Co-authored-by: Dennis Kobert --- desktop/src/cef.rs | 3 + desktop/src/cef/context.rs | 5 ++ desktop/src/cef/internal.rs | 1 + .../cef/internal/browser_process_client.rs | 3 +- .../cef/internal/render_process_handler.rs | 28 ++++++- .../cef/internal/render_process_v8_handler.rs | 83 +++++++++++++++++++ 6 files changed, 121 insertions(+), 2 deletions(-) create mode 100644 desktop/src/cef/internal/render_process_v8_handler.rs diff --git a/desktop/src/cef.rs b/desktop/src/cef.rs index 454ec2d5bf..8096ba9e1f 100644 --- a/desktop/src/cef.rs +++ b/desktop/src/cef.rs @@ -21,6 +21,7 @@ pub(crate) trait CefEventHandler: Clone { /// Scheudule the main event loop to run the cef event loop after the timeout /// [`_cef_browser_process_handler_t::on_schedule_message_pump_work`] for more documentation. fn schedule_cef_message_loop_work(&self, scheduled_time: Instant); + fn receive_web_message(&self, message: &[u8]); } #[derive(Clone, Copy)] @@ -118,4 +119,6 @@ impl CefEventHandler for CefHandler { fn schedule_cef_message_loop_work(&self, scheduled_time: std::time::Instant) { let _ = self.event_loop_proxy.send_event(CustomEvent::ScheduleBrowserWork(scheduled_time)); } + + fn receive_web_message(&self, message: &[u8]) {} } diff --git a/desktop/src/cef/context.rs b/desktop/src/cef/context.rs index cf823448b5..5a0f642c2a 100644 --- a/desktop/src/cef/context.rs +++ b/desktop/src/cef/context.rs @@ -7,6 +7,7 @@ use winit::event::WindowEvent; use crate::cef::dirs::{cef_cache_dir, cef_data_dir}; use super::input::InputState; +use super::ipc::{MessageType, SendMessage}; use super::scheme_handler::{FRONTEND_DOMAIN, GRAPHITE_SCHEME}; use super::{CefEventHandler, input}; @@ -129,6 +130,10 @@ impl Context { browser.host().unwrap().was_resized(); } } + + pub(crate) fn send_web_message(&self, message: &[u8]) { + self.send_message(MessageType::SendToJS, message); + } } impl Drop for Context { diff --git a/desktop/src/cef/internal.rs b/desktop/src/cef/internal.rs index 2cab906404..ad604b5957 100644 --- a/desktop/src/cef/internal.rs +++ b/desktop/src/cef/internal.rs @@ -4,6 +4,7 @@ mod browser_process_handler; mod render_handler; mod render_process_app; mod render_process_handler; +mod render_process_v8_handler; pub(crate) use browser_process_app::BrowserProcessAppImpl; pub(crate) use browser_process_client::BrowserProcessClientImpl; diff --git a/desktop/src/cef/internal/browser_process_client.rs b/desktop/src/cef/internal/browser_process_client.rs index 6e13c80327..7d7349d700 100644 --- a/desktop/src/cef/internal/browser_process_client.rs +++ b/desktop/src/cef/internal/browser_process_client.rs @@ -32,7 +32,8 @@ impl ImplClient for BrowserProcessClientImpl { Some(UnpackedMessage { message_type: MessageType::SendToNative, data, - }) => {} + }) => self.event_handler.receive_web_message(data), + _ => { tracing::error!("Unexpected message type received in browser process"); return 0; diff --git a/desktop/src/cef/internal/render_process_handler.rs b/desktop/src/cef/internal/render_process_handler.rs index 60a7467519..87f3d165f8 100644 --- a/desktop/src/cef/internal/render_process_handler.rs +++ b/desktop/src/cef/internal/render_process_handler.rs @@ -1,9 +1,14 @@ use cef::rc::{ConvertReturnValue, Rc, RcImpl}; use cef::sys::{_cef_render_process_handler_t, cef_base_ref_counted_t, cef_render_process_handler_t, cef_v8_propertyattribute_t, cef_v8_value_create_array_buffer_with_copy}; -use cef::{CefString, ImplFrame, ImplRenderProcessHandler, ImplV8Context, ImplV8Value, V8Value, WrapRenderProcessHandler, v8_context_get_entered_context}; +use cef::{ + CefString, ImplFrame, ImplRenderProcessHandler, ImplV8Context, ImplV8Value, V8Handler, V8Propertyattribute, V8Value, WrapRenderProcessHandler, v8_context_get_entered_context, + v8_value_create_function, +}; use crate::cef::ipc::{MessageType, UnpackMessage, UnpackedMessage}; +use super::render_process_v8_handler::BrowserProcessV8HandlerImpl; + pub(crate) struct RenderProcessHandlerImpl { object: *mut RcImpl, } @@ -70,6 +75,27 @@ impl ImplRenderProcessHandler for RenderProcessHandlerImpl { 1 } + fn on_context_created(&self, _browser: Option<&mut cef::Browser>, _frame: Option<&mut cef::Frame>, context: Option<&mut cef::V8Context>) { + let function_name = "sendNativeMessage"; + + let Some(context) = context else { + tracing::error!("V8 context is not available"); + return; + }; + + let mut v8_handler = V8Handler::new(BrowserProcessV8HandlerImpl::new()); + let Some(mut function) = v8_value_create_function(Some(&CefString::from(function_name)), Some(&mut v8_handler)) else { + tracing::error!("Failed to create V8 function {function_name}"); + return; + }; + + let Some(global) = context.global() else { + tracing::error!("Global object is not available in V8 context"); + return; + }; + global.set_value_bykey(Some(&CefString::from(function_name)), Some(&mut function), V8Propertyattribute::default()); + } + fn get_raw(&self) -> *mut _cef_render_process_handler_t { self.object.cast() } diff --git a/desktop/src/cef/internal/render_process_v8_handler.rs b/desktop/src/cef/internal/render_process_v8_handler.rs new file mode 100644 index 0000000000..e742ac9da7 --- /dev/null +++ b/desktop/src/cef/internal/render_process_v8_handler.rs @@ -0,0 +1,83 @@ +use cef::{ImplV8Handler, ImplV8Value, V8Value, WrapV8Handler, rc::Rc, v8_context_get_current_context}; + +use crate::cef::ipc::{MessageType, SendMessage}; + +pub struct BrowserProcessV8HandlerImpl { + object: *mut cef::rc::RcImpl, +} + +impl BrowserProcessV8HandlerImpl { + pub(crate) fn new() -> Self { + Self { object: std::ptr::null_mut() } + } +} + +impl ImplV8Handler for BrowserProcessV8HandlerImpl { + fn execute( + &self, + name: Option<&cef::CefString>, + _object: Option<&mut V8Value>, + arguments: Option<&[Option]>, + _retval: Option<&mut Option>, + _exception: Option<&mut cef::CefString>, + ) -> ::std::os::raw::c_int { + if let Some(name) = name { + if name.to_string() == "sendNativeMessage" { + let Some(args) = arguments else { + tracing::error!("No arguments provided to sendNativeMessage"); + return 0; + }; + let Some(arg1) = args.first() else { + tracing::error!("No arguments provided to sendNativeMessage"); + return 0; + }; + let Some(arg1) = arg1.as_ref() else { + tracing::error!("First argument to sendNativeMessage is not an ArrayBuffer"); + return 0; + }; + if arg1.is_array_buffer() == 0 { + tracing::error!("First argument to sendNativeMessage is not an ArrayBuffer"); + return 0; + } + + let size = arg1.array_buffer_byte_length(); + let ptr = arg1.array_buffer_data(); + let data = unsafe { std::slice::from_raw_parts_mut(ptr as *mut u8, size) }; + + v8_context_get_current_context().send_message(MessageType::SendToNative, data); + + return 1; + } + } + 1 + } + + fn get_raw(&self) -> *mut cef::sys::_cef_v8_handler_t { + self.object.cast() + } +} + +impl Clone for BrowserProcessV8HandlerImpl { + fn clone(&self) -> Self { + unsafe { + let rc_impl = &mut *self.object; + rc_impl.interface.add_ref(); + } + Self { object: self.object } + } +} + +impl Rc for BrowserProcessV8HandlerImpl { + fn as_base(&self) -> &cef::sys::cef_base_ref_counted_t { + unsafe { + let base = &*self.object; + std::mem::transmute(&base.cef_object) + } + } +} + +impl WrapV8Handler for BrowserProcessV8HandlerImpl { + fn wrap_rc(&mut self, object: *mut cef::rc::RcImpl) { + self.object = object; + } +} From e44b46ab1662e643fff2541698f2d3dc5f9047ce Mon Sep 17 00:00:00 2001 From: Timon Schelling Date: Mon, 28 Jul 2025 10:42:16 +0000 Subject: [PATCH 6/6] Fix missing safety consideration --- .../cef/internal/browser_process_client.rs | 3 ++- .../cef/internal/render_process_handler.rs | 3 ++- desktop/src/cef/ipc.rs | 20 ++++++++++--------- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/desktop/src/cef/internal/browser_process_client.rs b/desktop/src/cef/internal/browser_process_client.rs index 7d7349d700..bd316964aa 100644 --- a/desktop/src/cef/internal/browser_process_client.rs +++ b/desktop/src/cef/internal/browser_process_client.rs @@ -28,7 +28,8 @@ impl ImplClient for BrowserProcessClientImpl { _source_process: cef::ProcessId, message: Option<&mut cef::ProcessMessage>, ) -> ::std::os::raw::c_int { - match message.unpack() { + let unpacked_message = unsafe { message.and_then(|m| m.unpack()) }; + match unpacked_message { Some(UnpackedMessage { message_type: MessageType::SendToNative, data, diff --git a/desktop/src/cef/internal/render_process_handler.rs b/desktop/src/cef/internal/render_process_handler.rs index 87f3d165f8..2d125ae897 100644 --- a/desktop/src/cef/internal/render_process_handler.rs +++ b/desktop/src/cef/internal/render_process_handler.rs @@ -26,7 +26,8 @@ impl ImplRenderProcessHandler for RenderProcessHandlerImpl { _source_process: cef::ProcessId, message: Option<&mut cef::ProcessMessage>, ) -> ::std::os::raw::c_int { - match message.unpack() { + let unpacked_message = unsafe { message.and_then(|m| m.unpack()) }; + match unpacked_message { Some(UnpackedMessage { message_type: MessageType::SendToJS, data, diff --git a/desktop/src/cef/ipc.rs b/desktop/src/cef/ipc.rs index 9241c20230..2c37c2c3af 100644 --- a/desktop/src/cef/ipc.rs +++ b/desktop/src/cef/ipc.rs @@ -94,17 +94,19 @@ pub(crate) struct UnpackedMessage<'a> { pub(crate) message_type: MessageType, pub(crate) data: &'a [u8], } -pub(crate) trait UnpackMessage { - fn unpack(&'_ self) -> Option>; -} -impl UnpackMessage for Option<&mut cef::ProcessMessage> { - fn unpack(&'_ self) -> Option> { - let Some(message) = self else { return None }; - message.unpack() - } + +trait Sealed {} +impl Sealed for cef::ProcessMessage {} +#[allow(private_bounds)] +pub(crate) trait UnpackMessage: Sealed { + /// # Safety + /// + /// The caller must ensure that the message is valid. + /// Message should come from cef. + unsafe fn unpack(&self) -> Option>; } impl UnpackMessage for cef::ProcessMessage { - fn unpack(&'_ self) -> Option> { + unsafe fn unpack(&self) -> Option> { let pointer: *mut cef::sys::_cef_string_utf16_t = self.name().into(); let message = unsafe { super::utility::pointer_to_string(pointer) }; let Ok(message_type) = message.try_into() else {