diff --git a/desktop/src/app.rs b/desktop/src/app.rs index 9eebd77df0..cb0798d443 100644 --- a/desktop/src/app.rs +++ b/desktop/src/app.rs @@ -2,10 +2,11 @@ use crate::CustomEvent; use crate::WindowSize; use crate::render::GraphicsState; use crate::render::WgpuContext; +use ::cef::CefString; +use ::cef::ImplBrowser; +use ::cef::ImplFrame; use graphite_editor::application::Editor; -use graphite_editor::dispatcher::Dispatcher; use graphite_editor::messages::prelude::Message; -use std::collections::VecDeque; use std::sync::Arc; use std::sync::mpsc::Sender; use std::time::Duration; @@ -109,10 +110,21 @@ impl ApplicationHandler for WinitApp { tracing::error!("Message could not be deserialized: {:?}", message); return; }; - println!("Message received: {message:?}"); let responses = self.editor.handle_message(message); - println!("responses: {:?}", responses); // Send response to CEF + let Some(frame) = self.cef_context.browser.as_ref().unwrap().main_frame() else { + tracing::error!("Could not get frame after editor processed messages"); + return; + }; + for frontend_message in responses { + let Ok(serialized_message) = serde_json::to_string(&frontend_message) else { + tracing::error!("Failed to serialize frontend message in CustomEvent::MessageReceived"); + continue; + }; + let message = format!("window.handle.sendMessageToFrontendFromCEF(\'{serialized_message}\')"); + let code = CefString::from(message.as_str()); + frame.execute_java_script(Some(&code), None, 0); + } } } } diff --git a/desktop/src/cef.rs b/desktop/src/cef.rs index 64dc53d673..00a5cf0d07 100644 --- a/desktop/src/cef.rs +++ b/desktop/src/cef.rs @@ -20,7 +20,7 @@ pub(crate) trait CefEventHandler: Clone { /// [`_cef_browser_process_handler_t::on_schedule_message_pump_work`] for more documentation. fn schedule_cef_message_loop_work(&self, scheduled_time: Instant); - fn send_message_to_editior(&self, message: String); + fn send_message_to_editor(&self, message: String); } #[derive(Clone, Copy)] @@ -118,7 +118,7 @@ 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 send_message_to_editior(&self, message: String) { + fn send_message_to_editor(&self, message: String) { let _ = self.event_loop_proxy.send_event(CustomEvent::MessageReceived { message }); } } diff --git a/desktop/src/cef/internal.rs b/desktop/src/cef/internal.rs index 37e140e4bf..96f620cd5b 100644 --- a/desktop/src/cef/internal.rs +++ b/desktop/src/cef/internal.rs @@ -5,6 +5,7 @@ mod non_browser_app; mod non_browser_render_process_handler; mod non_browser_v8_handler; mod render_handler; +mod utility; pub(crate) use app::AppImpl; pub(crate) use client::ClientImpl; diff --git a/desktop/src/cef/internal/client.rs b/desktop/src/cef/internal/client.rs index 91f28442b7..cfc12aa9c8 100644 --- a/desktop/src/cef/internal/client.rs +++ b/desktop/src/cef/internal/client.rs @@ -30,25 +30,19 @@ impl ImplClient for ClientImpl { fn on_process_message_received( &self, - browser: Option<&mut cef::Browser>, - frame: Option<&mut cef::Frame>, - source_process: cef::ProcessId, + _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 Some(message) = message else { - tracing::event!(tracing::Level::ERROR, "No message in RenderProcessHandlerImpl::on_process_message_received"); + tracing::error!("No message in RenderProcessHandlerImpl::on_process_message_received"); return 1; }; let pointer: *mut cef::sys::_cef_string_utf16_t = message.name().into(); - let message = unsafe { - let str = (*pointer).str_; - let len = (*pointer).length; - let slice = std::slice::from_raw_parts(str, len as usize); - String::from_utf16(slice).unwrap() - }; - - let _ = self.event_handler.send_message_to_editior(message); + let string_message = super::utility::pointer_to_string(pointer); + let _ = self.event_handler.send_message_to_editor(string_message); 0 } } diff --git a/desktop/src/cef/internal/non_browser_v8_handler.rs b/desktop/src/cef/internal/non_browser_v8_handler.rs index a06d437b1e..bca5c945e1 100644 --- a/desktop/src/cef/internal/non_browser_v8_handler.rs +++ b/desktop/src/cef/internal/non_browser_v8_handler.rs @@ -24,12 +24,7 @@ impl ImplV8Handler for NonBrowserV8HandlerImpl { let string = arguments.unwrap().first().unwrap().as_ref().unwrap().string_value(); let pointer: *mut cef::sys::_cef_string_utf16_t = string.into(); - let message = unsafe { - let str = (*pointer).str_; - let len = (*pointer).length; - let slice = std::slice::from_raw_parts(str, len); - String::from_utf16(slice).unwrap() - }; + let message = super::utility::pointer_to_string(pointer); let Some(mut process_message) = process_message_create(Some(&CefString::from(message.as_str()))) else { tracing::event!(tracing::Level::ERROR, "Failed to create process message"); diff --git a/desktop/src/cef/internal/utility.rs b/desktop/src/cef/internal/utility.rs new file mode 100644 index 0000000000..bb7273c38d --- /dev/null +++ b/desktop/src/cef/internal/utility.rs @@ -0,0 +1,8 @@ +pub fn pointer_to_string(pointer: *mut cef::sys::_cef_string_utf16_t) -> String { + unsafe { + let str = (*pointer).str_; + let len = (*pointer).length; + let slice = std::slice::from_raw_parts(str, len as usize); + String::from_utf16(slice).unwrap() + } +} diff --git a/frontend/src/App.svelte b/frontend/src/App.svelte index cb7a23e83c..5a3b9bb8b4 100644 --- a/frontend/src/App.svelte +++ b/frontend/src/App.svelte @@ -14,6 +14,7 @@ editor = createEditor(); + window.handle = editor.handle; // Auto save every 15 seconds autoSaveAllDocumentsId = setInterval(() => { editor?.handle.autoSaveAllDocuments(); diff --git a/frontend/wasm/src/native.rs b/frontend/wasm/src/native.rs index 53ed32a5e9..8cc3b2a74a 100644 --- a/frontend/wasm/src/native.rs +++ b/frontend/wasm/src/native.rs @@ -1,21 +1,48 @@ -use std::cell::RefCell; -use std::time::Duration; - use crate::Message; use editor::messages::prelude::*; -use wasm_bindgen::closure::Closure; +use serde::ser::Serialize; use wasm_bindgen::prelude::wasm_bindgen; use wasm_bindgen::{JsCast, JsValue}; #[wasm_bindgen] #[derive(Clone)] -pub struct EditorHandle; +pub struct EditorHandle { + /// TODO: Remove + /// We current do frontend message in native -> serde serialize -> json string -> serde deserialize -> frontend message in wasm -> JSValue -> browser + /// We should do native -> V8Value -> browser. + frontend_message_handler_callback: js_sys::Function, +} #[wasm_bindgen] impl EditorHandle { #[wasm_bindgen(constructor)] pub fn new(frontend_message_handler_callback: js_sys::Function) -> Self { - EditorHandle + EditorHandle { frontend_message_handler_callback } + } + + /// TODO: Remove + #[wasm_bindgen(js_name = sendMessageToFrontendFromCEF)] + pub fn send_message_to_frontend_from_cef(&self, message: String) { + let Ok(mut message) = serde_json::from_str::(&message) else { return }; + + if let FrontendMessage::UpdateDocumentLayerStructure { data_buffer } = message { + message = FrontendMessage::UpdateDocumentLayerStructureJs { data_buffer: data_buffer.into() }; + } + + let message_type = message.to_discriminant().local_name(); + + let serializer = serde_wasm_bindgen::Serializer::new().serialize_large_number_types_as_bigints(true); + let message_data = message.serialize(&serializer).expect("Failed to serialize FrontendMessage"); + + let js_return_value = self.frontend_message_handler_callback.call2(&JsValue::null(), &JsValue::from(message_type), &message_data); + + if let Err(error) = js_return_value { + error!( + "While handling FrontendMessage \"{:?}\", JavaScript threw an error: {:?}", + message.to_discriminant().local_name(), + error, + ) + } } } diff --git a/package.json b/package.json index 7197418fb0..9bdfdae2e2 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "scripts": { "---------- DEV SERVER ----------": "", "start": "cd frontend && npm start", - "start-desktop": "cd frontend && npm run build-dev && cargo run -p graphite-desktop", + "start-desktop": "cd frontend && npm run build-native && cargo run -p graphite-desktop", "profiling": "cd frontend && npm run profiling", "production": "cd frontend && npm run production", "---------- BUILDS ----------": "",