Skip to content

Commit c6ec3a2

Browse files
Desktop: Buffer web messages until connection is initialized (#3082)
Buffer web messages until connection is initialized
1 parent a4ec50d commit c6ec3a2

9 files changed

Lines changed: 74 additions & 14 deletions

File tree

desktop/src/app.rs

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ pub(crate) struct WinitApp {
3535
last_ui_update: Instant,
3636
avg_frame_time: f32,
3737
start_render_sender: SyncSender<()>,
38+
web_communication_initialized: bool,
39+
web_communication_startup_buffer: Vec<Vec<u8>>,
3840
}
3941

4042
impl WinitApp {
@@ -61,6 +63,8 @@ impl WinitApp {
6163
last_ui_update: Instant::now(),
6264
avg_frame_time: 0.,
6365
start_render_sender,
66+
web_communication_initialized: false,
67+
web_communication_startup_buffer: Vec::new(),
6468
}
6569
}
6670

@@ -71,7 +75,7 @@ impl WinitApp {
7175
tracing::error!("Failed to serialize frontend messages");
7276
return;
7377
};
74-
self.cef_context.send_web_message(bytes);
78+
self.send_or_queue_web_message(bytes);
7579
}
7680
DesktopFrontendMessage::OpenFileDialog { title, filters, context } => {
7781
let event_loop_proxy = self.event_loop_proxy.clone();
@@ -161,6 +165,14 @@ impl WinitApp {
161165
let responses = self.desktop_wrapper.dispatch(message);
162166
self.handle_desktop_frontend_messages(responses);
163167
}
168+
169+
fn send_or_queue_web_message(&mut self, message: Vec<u8>) {
170+
if self.web_communication_initialized {
171+
self.cef_context.send_web_message(message);
172+
} else {
173+
self.web_communication_startup_buffer.push(message);
174+
}
175+
}
164176
}
165177

166178
impl ApplicationHandler<CustomEvent> for WinitApp {
@@ -215,6 +227,12 @@ impl ApplicationHandler<CustomEvent> for WinitApp {
215227

216228
fn user_event(&mut self, _: &ActiveEventLoop, event: CustomEvent) {
217229
match event {
230+
CustomEvent::WebCommunicationInitialized => {
231+
self.web_communication_initialized = true;
232+
for message in self.web_communication_startup_buffer.drain(..) {
233+
self.cef_context.send_web_message(message);
234+
}
235+
}
218236
CustomEvent::DesktopWrapperMessage(message) => self.dispatch_desktop_wrapper_message(message),
219237
CustomEvent::NodeGraphExecutionResult(result) => match result {
220238
NodeGraphExecutionResult::HasRun(texture) => {

desktop/src/cef.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ pub(crate) trait CefEventHandler: Clone {
4646
/// Scheudule the main event loop to run the cef event loop after the timeout
4747
/// [`_cef_browser_process_handler_t::on_schedule_message_pump_work`] for more documentation.
4848
fn schedule_cef_message_loop_work(&self, scheduled_time: Instant);
49+
fn initialized_web_communication(&self);
4950
fn receive_web_message(&self, message: &[u8]);
5051
}
5152

@@ -145,6 +146,10 @@ impl CefEventHandler for CefHandler {
145146
let _ = self.event_loop_proxy.send_event(CustomEvent::ScheduleBrowserWork(scheduled_time));
146147
}
147148

149+
fn initialized_web_communication(&self) {
150+
let _ = self.event_loop_proxy.send_event(CustomEvent::WebCommunicationInitialized);
151+
}
152+
148153
fn receive_web_message(&self, message: &[u8]) {
149154
let Some(desktop_wrapper_message) = deserialize_editor_message(message) else {
150155
tracing::error!("Failed to deserialize web message");

desktop/src/cef/internal/browser_process_client.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ impl<H: CefEventHandler> ImplClient for BrowserProcessClientImpl<H> {
3232
) -> ::std::os::raw::c_int {
3333
let unpacked_message = unsafe { message.and_then(|m| m.unpack()) };
3434
match unpacked_message {
35+
Some(UnpackedMessage {
36+
message_type: MessageType::Initialized,
37+
data: _,
38+
}) => self.event_handler.initialized_web_communication(),
3539
Some(UnpackedMessage {
3640
message_type: MessageType::SendToNative,
3741
data,

desktop/src/cef/internal/render_process_handler.rs

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -76,24 +76,30 @@ impl ImplRenderProcessHandler for RenderProcessHandlerImpl {
7676
}
7777

7878
fn on_context_created(&self, _browser: Option<&mut cef::Browser>, _frame: Option<&mut cef::Frame>, context: Option<&mut cef::V8Context>) {
79-
let function_name = "sendNativeMessage";
79+
let register_js_function = |context: &mut cef::V8Context, name: &'static str| {
80+
let mut v8_handler = V8Handler::new(BrowserProcessV8HandlerImpl::new());
81+
let Some(mut function) = v8_value_create_function(Some(&CefString::from(name)), Some(&mut v8_handler)) else {
82+
tracing::error!("Failed to create V8 function {name}");
83+
return;
84+
};
85+
86+
let Some(global) = context.global() else {
87+
tracing::error!("Global object is not available in V8 context");
88+
return;
89+
};
90+
global.set_value_bykey(Some(&CefString::from(name)), Some(&mut function), V8Propertyattribute::default());
91+
};
8092

8193
let Some(context) = context else {
8294
tracing::error!("V8 context is not available");
8395
return;
8496
};
8597

86-
let mut v8_handler = V8Handler::new(BrowserProcessV8HandlerImpl::new());
87-
let Some(mut function) = v8_value_create_function(Some(&CefString::from(function_name)), Some(&mut v8_handler)) else {
88-
tracing::error!("Failed to create V8 function {function_name}");
89-
return;
90-
};
98+
let initialized_function_name = "initializeNativeCommunication";
99+
let send_function_name = "sendNativeMessage";
91100

92-
let Some(global) = context.global() else {
93-
tracing::error!("Global object is not available in V8 context");
94-
return;
95-
};
96-
global.set_value_bykey(Some(&CefString::from(function_name)), Some(&mut function), V8Propertyattribute::default());
101+
register_js_function(context, initialized_function_name);
102+
register_js_function(context, send_function_name);
97103
}
98104

99105
fn get_raw(&self) -> *mut _cef_render_process_handler_t {

desktop/src/cef/internal/render_process_v8_handler.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,11 @@ impl ImplV8Handler for BrowserProcessV8HandlerImpl {
2121
_retval: Option<&mut Option<V8Value>>,
2222
_exception: Option<&mut cef::CefString>,
2323
) -> ::std::os::raw::c_int {
24-
if let Some(name) = name {
25-
if name.to_string() == "sendNativeMessage" {
24+
match name.map(|s| s.to_string()).unwrap_or_default().as_str() {
25+
"initializeNativeCommunication" => {
26+
v8_context_get_current_context().send_message(MessageType::Initialized, vec![0u8].as_slice());
27+
}
28+
"sendNativeMessage" => {
2629
let Some(args) = arguments else {
2730
tracing::error!("No arguments provided to sendNativeMessage");
2831
return 0;
@@ -48,6 +51,9 @@ impl ImplV8Handler for BrowserProcessV8HandlerImpl {
4851

4952
return 1;
5053
}
54+
name => {
55+
tracing::error!("Unknown V8 function called: {}", name);
56+
}
5157
}
5258
1
5359
}

desktop/src/cef/ipc.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,17 @@
11
use cef::{CefString, Frame, ImplBinaryValue, ImplFrame, ImplListValue, ImplProcessMessage, ImplV8Context, ProcessId, V8Context, sys::cef_process_id_t};
22

33
pub(crate) enum MessageType {
4+
Initialized,
45
SendToJS,
56
SendToNative,
67
}
78
impl From<MessageType> for MessageInfo {
89
fn from(val: MessageType) -> Self {
910
match val {
11+
MessageType::Initialized => MessageInfo {
12+
name: "initialized".to_string(),
13+
target: cef_process_id_t::PID_BROWSER.into(),
14+
},
1015
MessageType::SendToJS => MessageInfo {
1116
name: "send_to_js".to_string(),
1217
target: cef_process_id_t::PID_RENDERER.into(),
@@ -22,6 +27,7 @@ impl TryFrom<String> for MessageType {
2227
type Error = ();
2328
fn try_from(value: String) -> Result<Self, Self::Error> {
2429
match value.as_str() {
30+
"initialized" => Ok(MessageType::Initialized),
2531
"send_to_js" => Ok(MessageType::SendToJS),
2632
"send_to_native" => Ok(MessageType::SendToNative),
2733
_ => Err(()),

desktop/src/main.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ use graphite_desktop_wrapper::{NodeGraphExecutionResult, WgpuContext};
2121
pub(crate) enum CustomEvent {
2222
UiUpdate(wgpu::Texture),
2323
ScheduleBrowserWork(Instant),
24+
WebCommunicationInitialized,
2425
DesktopWrapperMessage(DesktopWrapperMessage),
2526
NodeGraphExecutionResult(NodeGraphExecutionResult),
2627
}

frontend/wasm/src/editor_api.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,9 @@ impl EditorHandle {
242242

243243
#[wasm_bindgen(js_name = initAfterFrontendReady)]
244244
pub fn init_after_frontend_ready(&self, platform: String) {
245+
#[cfg(feature = "native")]
246+
crate::native_communcation::initialize_native_communication();
247+
245248
// Send initialization messages
246249
let platform = match platform.as_str() {
247250
"Windows" => Platform::Windows,

frontend/wasm/src/native_communcation.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,17 @@ pub fn receive_native_message(buffer: ArrayBuffer) {
2020
}
2121
}
2222

23+
pub fn initialize_native_communication() {
24+
let global = js_sys::global();
25+
26+
// Get the function by name
27+
let func = js_sys::Reflect::get(&global, &JsValue::from_str("initializeNativeCommunication")).expect("Function not found");
28+
let func = func.dyn_into::<js_sys::Function>().expect("Not a function");
29+
30+
// Call it
31+
func.call0(&JsValue::NULL).expect("Function call failed");
32+
}
33+
2334
pub fn send_message_to_cef(message: String) {
2435
let global = js_sys::global();
2536

0 commit comments

Comments
 (0)