diff --git a/desktop/src/cef.rs b/desktop/src/cef.rs index d84e9ad672..8096ba9e1f 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; @@ -19,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)] @@ -116,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 a26aa93329..5a0f642c2a 100644 --- a/desktop/src/cef/context.rs +++ b/desktop/src/cef/context.rs @@ -7,10 +7,11 @@ 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}; -use super::internal::{AppImpl, ClientImpl, NonBrowserAppImpl, RenderHandlerImpl}; +use super::internal::{BrowserProcessAppImpl, BrowserProcessClientImpl, RenderHandlerImpl, RenderProcessAppImpl}; pub(crate) struct Setup {} pub(crate) struct Initialized {} @@ -42,7 +43,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 +71,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 +82,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()); @@ -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 8473884fd3..ad604b5957 100644 --- a/desktop/src/cef/internal.rs +++ b/desktop/src/cef/internal.rs @@ -1,10 +1,12 @@ -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; +mod render_process_handler; +mod render_process_v8_handler; -pub(crate) use app::AppImpl; -pub(crate) use client::ClientImpl; -pub(crate) use non_browser_app::NonBrowserAppImpl; +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::RenderProcessAppImpl; diff --git a/desktop/src/cef/internal/app.rs b/desktop/src/cef/internal/browser_process_app.rs similarity index 84% rename from desktop/src/cef/internal/app.rs rename to desktop/src/cef/internal/browser_process_app.rs index 1ec22d6ec8..82999c5872 100644 --- a/desktop/src/cef/internal/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 new file mode 100644 index 0000000000..bd316964aa --- /dev/null +++ b/desktop/src/cef/internal/browser_process_client.rs @@ -0,0 +1,80 @@ +use cef::rc::{Rc, RcImpl}; +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>, + render_handler: RenderHandler, + event_handler: H, +} +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 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 { + let unpacked_message = unsafe { message.and_then(|m| m.unpack()) }; + match unpacked_message { + 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; + } + } + 1 + } + + fn render_handler(&self) -> Option { + Some(self.render_handler.clone()) + } + + fn get_raw(&self) -> *mut _cef_client_t { + self.object.cast() + } +} + +impl Clone for BrowserProcessClientImpl { + fn clone(&self) -> Self { + unsafe { + let rc_impl = &mut *self.object; + rc_impl.interface.add_ref(); + } + Self { + object: self.object, + render_handler: self.render_handler.clone(), + event_handler: self.event_handler.clone(), + } + } +} +impl Rc for BrowserProcessClientImpl { + fn as_base(&self) -> &cef_base_ref_counted_t { + unsafe { + let base = &*self.object; + std::mem::transmute(&base.cef_object) + } + } +} +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/client.rs b/desktop/src/cef/internal/client.rs deleted file mode 100644 index 543f9c590d..0000000000 --- a/desktop/src/cef/internal/client.rs +++ /dev/null @@ -1,52 +0,0 @@ -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 { - object: *mut RcImpl<_cef_client_t, Self>, - render_handler: RenderHandler, -} -impl ClientImpl { - pub(crate) fn new(render_handler: RenderHandler) -> Self { - Self { - object: std::ptr::null_mut(), - render_handler, - } - } -} - -impl ImplClient for ClientImpl { - fn render_handler(&self) -> Option { - Some(self.render_handler.clone()) - } - - fn get_raw(&self) -> *mut _cef_client_t { - self.object.cast() - } -} - -impl Clone for ClientImpl { - fn clone(&self) -> Self { - unsafe { - let rc_impl = &mut *self.object; - rc_impl.interface.add_ref(); - } - Self { - object: self.object, - render_handler: self.render_handler.clone(), - } - } -} -impl Rc for ClientImpl { - fn as_base(&self) -> &cef_base_ref_counted_t { - unsafe { - let base = &*self.object; - std::mem::transmute(&base.cef_object) - } - } -} -impl WrapClient for ClientImpl { - fn wrap_rc(&mut self, object: *mut RcImpl<_cef_client_t, Self>) { - self.object = object; - } -} diff --git a/desktop/src/cef/internal/non_browser_app.rs b/desktop/src/cef/internal/render_process_app.rs similarity index 50% rename from desktop/src/cef/internal/non_browser_app.rs rename to desktop/src/cef/internal/render_process_app.rs index 04007d729d..d9e66bafbd 100644 --- a/desktop/src/cef/internal/non_browser_app.rs +++ b/desktop/src/cef/internal/render_process_app.rs @@ -1,38 +1,50 @@ 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 NonBrowserAppImpl { +pub(crate) struct RenderProcessAppImpl { object: *mut RcImpl<_cef_app_t, Self>, + render_process_handler: RenderProcessHandler, } -impl NonBrowserAppImpl { +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()), + }) } } -impl ImplApp for NonBrowserAppImpl { +impl ImplApp for RenderProcessAppImpl { fn on_register_custom_schemes(&self, registrar: Option<&mut SchemeRegistrar>) { 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() } } -impl Clone for NonBrowserAppImpl { +impl Clone for RenderProcessAppImpl { fn clone(&self) -> Self { unsafe { 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 NonBrowserAppImpl { +impl Rc for RenderProcessAppImpl { fn as_base(&self) -> &cef_base_ref_counted_t { unsafe { let base = &*self.object; @@ -40,7 +52,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; } 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..2d125ae897 --- /dev/null +++ b/desktop/src/cef/internal/render_process_handler.rs @@ -0,0 +1,126 @@ +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, 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, +} +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 { + let unpacked_message = unsafe { message.and_then(|m| m.unpack()) }; + match unpacked_message { + 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; + } + } + 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() + } +} + +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/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; + } +} diff --git a/desktop/src/cef/ipc.rs b/desktop/src/cef/ipc.rs new file mode 100644 index 0000000000..2c37c2c3af --- /dev/null +++ b/desktop/src/cef/ipc.rs @@ -0,0 +1,123 @@ +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], +} + +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 { + 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 { + 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();