From 662ed758088f9748d6052c9df655581bf28f4548 Mon Sep 17 00:00:00 2001 From: JustJ01 Date: Thu, 19 Feb 2026 00:35:37 +0530 Subject: [PATCH 1/4] add crash dialog in RAF path --- .../document/node_graph/node_properties.rs | 1 + frontend/wasm/src/editor_api.rs | 6 +- frontend/wasm/src/lib.rs | 65 ++++++++++++++++++- 3 files changed, 68 insertions(+), 4 deletions(-) diff --git a/editor/src/messages/portfolio/document/node_graph/node_properties.rs b/editor/src/messages/portfolio/document/node_graph/node_properties.rs index eb08935e7c..8a58a799d1 100644 --- a/editor/src/messages/portfolio/document/node_graph/node_properties.rs +++ b/editor/src/messages/portfolio/document/node_graph/node_properties.rs @@ -1284,6 +1284,7 @@ pub fn query_assign_colors_randomize(node_id: NodeId, context: &NodePropertiesCo } pub(crate) fn brightness_contrast_properties(node_id: NodeId, context: &mut NodePropertiesContext) -> Vec { + panic!(); use graphene_std::raster::brightness_contrast::*; // Use Classic diff --git a/frontend/wasm/src/editor_api.rs b/frontend/wasm/src/editor_api.rs index aaa17a1d22..b1336d63e6 100644 --- a/frontend/wasm/src/editor_api.rs +++ b/frontend/wasm/src/editor_api.rs @@ -5,7 +5,7 @@ // on the dispatcher messaging system and more complex Rust data types. // use crate::helpers::translate_key; -use crate::{EDITOR_HANDLE, EDITOR_HAS_CRASHED, Error, MESSAGE_BUFFER}; +use crate::{EDITOR_HANDLE, EDITOR_HAS_CRASHED, Error, MESSAGE_BUFFER, PANIC_DIALOG_MESSAGE_CALLBACK}; use editor::consts::FILE_EXTENSION; use editor::messages::clipboard::utility_types::ClipboardContentRaw; use editor::messages::input_mapper::utility_types::input_keyboard::ModifierKeys; @@ -97,6 +97,7 @@ impl EditorHandle { uuid_random_seed, ); + let panic_callback = frontend_message_handler_callback.clone(); let editor_handle = EditorHandle { frontend_message_handler_callback }; if EDITOR.with(|handle| handle.lock().ok().map(|mut guard| *guard = Some(editor))).is_none() { log::error!("Attempted to initialize the editor more than once"); @@ -104,15 +105,18 @@ impl EditorHandle { if EDITOR_HANDLE.with(|handle| handle.lock().ok().map(|mut guard| *guard = Some(editor_handle.clone()))).is_none() { log::error!("Attempted to initialize the editor handle more than once"); } + PANIC_DIALOG_MESSAGE_CALLBACK.with_borrow_mut(|callback| *callback = Some(panic_callback)); editor_handle } #[cfg(feature = "native")] pub fn create(_platform: String, _uuid_random_seed: u64, frontend_message_handler_callback: js_sys::Function) -> EditorHandle { + let panic_callback = frontend_message_handler_callback.clone(); let editor_handle = EditorHandle { frontend_message_handler_callback }; if EDITOR_HANDLE.with(|handle| handle.lock().ok().map(|mut guard| *guard = Some(editor_handle.clone()))).is_none() { log::error!("Attempted to initialize the editor handle more than once"); } + PANIC_DIALOG_MESSAGE_CALLBACK.with_borrow_mut(|callback| *callback = Some(panic_callback)); editor_handle } diff --git a/frontend/wasm/src/lib.rs b/frontend/wasm/src/lib.rs index d3141288b3..2a7ae955fa 100644 --- a/frontend/wasm/src/lib.rs +++ b/frontend/wasm/src/lib.rs @@ -12,6 +12,7 @@ use editor::messages::prelude::*; use std::panic; use std::sync::Mutex; use std::sync::atomic::{AtomicBool, Ordering}; +use wasm_bindgen::JsCast; use wasm_bindgen::prelude::*; // Set up the persistent editor backend state @@ -24,6 +25,7 @@ thread_local! { pub static EDITOR: Mutex> = const { Mutex::new(None) }; pub static MESSAGE_BUFFER: std::cell::RefCell> = const { std::cell::RefCell::new(Vec::new()) }; pub static EDITOR_HANDLE: Mutex> = const { Mutex::new(None) }; + pub static PANIC_DIALOG_MESSAGE_CALLBACK: std::cell::RefCell> = const { std::cell::RefCell::new(None) }; } /// Initialize the backend @@ -72,12 +74,69 @@ pub fn panic_hook(info: &panic::PanicHookInfo) { log::error!("{info}"); + // Prefer using the raw JS callback to avoid mutex lock contention inside the panic hook. + // Fall back to the editor handle path if needed. + if !(send_panic_dialog_via_callback(&info) || send_panic_dialog(&info)) { + send_panic_dialog_deferred(info); + } +} + +fn send_panic_dialog_via_callback(panic_info: &str) -> bool { + let message = FrontendMessage::DisplayDialogPanic { panic_info: panic_info.to_string() }; + let message_type = message.to_discriminant().local_name(); + let Ok(message_data) = serde_wasm_bindgen::to_value(&message) else { + log::error!("Failed to serialize crash dialog panic message"); + return false; + }; + + PANIC_DIALOG_MESSAGE_CALLBACK.with(|callback| { + let callback_ref = callback.borrow(); + let Some(callback) = callback_ref.as_ref() else { + return false; + }; + + if let Err(error) = callback.call2(&JsValue::null(), &JsValue::from(message_type), &message_data) { + log::error!("Failed to send crash dialog panic message to JS: {:?}", error); + return false; + } + + true + }) +} + +fn send_panic_dialog(panic_info: &str) -> bool { EDITOR_HANDLE.with(|editor_handle| { - let mut guard = editor_handle.lock(); - if let Ok(Some(handle)) = guard.as_deref_mut() { - handle.send_frontend_message_to_js_rust_proxy(FrontendMessage::DisplayDialogPanic { panic_info: info.to_string() }); + let mut guard = editor_handle.try_lock(); + let Ok(Some(handle)) = guard.as_deref_mut() else { + return false; + }; + + handle.send_frontend_message_to_js_rust_proxy(FrontendMessage::DisplayDialogPanic { panic_info: panic_info.to_string() }); + true + }) +} + +#[cfg(not(feature = "native"))] +fn send_panic_dialog_deferred(panic_info: String) { + let callback = Closure::once_into_js(move || { + if !send_panic_dialog(&panic_info) { + log::error!("Failed to send crash dialog after panic because the editor handle is unavailable"); } }); + + let Some(window) = web_sys::window() else { + log::error!("Failed to schedule crash dialog after panic because no window exists"); + return; + }; + + if window.set_timeout_with_callback_and_timeout_and_arguments_0(callback.unchecked_ref(), 0).is_err() { + log::error!("Failed to schedule crash dialog after panic with setTimeout"); + } +} + +#[cfg(feature = "native")] +fn send_panic_dialog_deferred(_panic_info: String) { + // Native builds do not use `setTimeout`, so just log the failure in the caller's context. } #[wasm_bindgen] From afbc9b890b8bf1dcdc7d99de558f25319fac814f Mon Sep 17 00:00:00 2001 From: JustJ01 Date: Thu, 19 Feb 2026 01:37:20 +0530 Subject: [PATCH 2/4] removed panic!() --- .../messages/portfolio/document/node_graph/node_properties.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/editor/src/messages/portfolio/document/node_graph/node_properties.rs b/editor/src/messages/portfolio/document/node_graph/node_properties.rs index 8a58a799d1..eb08935e7c 100644 --- a/editor/src/messages/portfolio/document/node_graph/node_properties.rs +++ b/editor/src/messages/portfolio/document/node_graph/node_properties.rs @@ -1284,7 +1284,6 @@ pub fn query_assign_colors_randomize(node_id: NodeId, context: &NodePropertiesCo } pub(crate) fn brightness_contrast_properties(node_id: NodeId, context: &mut NodePropertiesContext) -> Vec { - panic!(); use graphene_std::raster::brightness_contrast::*; // Use Classic From 1c041fa44b34e003bb4f1c922e92888803a0e062 Mon Sep 17 00:00:00 2001 From: JustJ01 Date: Thu, 19 Feb 2026 02:39:53 +0530 Subject: [PATCH 3/4] removed redundant string allocationa dn duplication between function --- frontend/wasm/src/editor_api.rs | 27 +++++++++++++-------------- frontend/wasm/src/lib.rs | 9 ++++----- 2 files changed, 17 insertions(+), 19 deletions(-) diff --git a/frontend/wasm/src/editor_api.rs b/frontend/wasm/src/editor_api.rs index b1336d63e6..240f451a9b 100644 --- a/frontend/wasm/src/editor_api.rs +++ b/frontend/wasm/src/editor_api.rs @@ -78,6 +78,16 @@ impl EditorHandle { pub fn send_frontend_message_to_js_rust_proxy(&self, message: FrontendMessage) { self.send_frontend_message_to_js(message); } + + fn initialize_handle(frontend_message_handler_callback: js_sys::Function) -> EditorHandle { + let panic_callback = frontend_message_handler_callback.clone(); + let editor_handle = EditorHandle { frontend_message_handler_callback }; + if EDITOR_HANDLE.with(|handle| handle.lock().ok().map(|mut guard| *guard = Some(editor_handle.clone()))).is_none() { + log::error!("Attempted to initialize the editor handle more than once"); + } + PANIC_DIALOG_MESSAGE_CALLBACK.with_borrow_mut(|callback| *callback = Some(panic_callback)); + editor_handle + } } #[wasm_bindgen] @@ -97,27 +107,16 @@ impl EditorHandle { uuid_random_seed, ); - let panic_callback = frontend_message_handler_callback.clone(); - let editor_handle = EditorHandle { frontend_message_handler_callback }; if EDITOR.with(|handle| handle.lock().ok().map(|mut guard| *guard = Some(editor))).is_none() { log::error!("Attempted to initialize the editor more than once"); } - if EDITOR_HANDLE.with(|handle| handle.lock().ok().map(|mut guard| *guard = Some(editor_handle.clone()))).is_none() { - log::error!("Attempted to initialize the editor handle more than once"); - } - PANIC_DIALOG_MESSAGE_CALLBACK.with_borrow_mut(|callback| *callback = Some(panic_callback)); - editor_handle + + Self::initialize_handle(frontend_message_handler_callback) } #[cfg(feature = "native")] pub fn create(_platform: String, _uuid_random_seed: u64, frontend_message_handler_callback: js_sys::Function) -> EditorHandle { - let panic_callback = frontend_message_handler_callback.clone(); - let editor_handle = EditorHandle { frontend_message_handler_callback }; - if EDITOR_HANDLE.with(|handle| handle.lock().ok().map(|mut guard| *guard = Some(editor_handle.clone()))).is_none() { - log::error!("Attempted to initialize the editor handle more than once"); - } - PANIC_DIALOG_MESSAGE_CALLBACK.with_borrow_mut(|callback| *callback = Some(panic_callback)); - editor_handle + Self::initialize_handle(frontend_message_handler_callback) } // Sends a message to the dispatcher in the Editor Backend diff --git a/frontend/wasm/src/lib.rs b/frontend/wasm/src/lib.rs index 2a7ae955fa..6e38fc6216 100644 --- a/frontend/wasm/src/lib.rs +++ b/frontend/wasm/src/lib.rs @@ -12,7 +12,6 @@ use editor::messages::prelude::*; use std::panic; use std::sync::Mutex; use std::sync::atomic::{AtomicBool, Ordering}; -use wasm_bindgen::JsCast; use wasm_bindgen::prelude::*; // Set up the persistent editor backend state @@ -81,8 +80,8 @@ pub fn panic_hook(info: &panic::PanicHookInfo) { } } -fn send_panic_dialog_via_callback(panic_info: &str) -> bool { - let message = FrontendMessage::DisplayDialogPanic { panic_info: panic_info.to_string() }; +fn send_panic_dialog_via_callback(panic_info: &String) -> bool { + let message = FrontendMessage::DisplayDialogPanic { panic_info: panic_info.clone() }; let message_type = message.to_discriminant().local_name(); let Ok(message_data) = serde_wasm_bindgen::to_value(&message) else { log::error!("Failed to serialize crash dialog panic message"); @@ -104,14 +103,14 @@ fn send_panic_dialog_via_callback(panic_info: &str) -> bool { }) } -fn send_panic_dialog(panic_info: &str) -> bool { +fn send_panic_dialog(panic_info: &String) -> bool { EDITOR_HANDLE.with(|editor_handle| { let mut guard = editor_handle.try_lock(); let Ok(Some(handle)) = guard.as_deref_mut() else { return false; }; - handle.send_frontend_message_to_js_rust_proxy(FrontendMessage::DisplayDialogPanic { panic_info: panic_info.to_string() }); + handle.send_frontend_message_to_js_rust_proxy(FrontendMessage::DisplayDialogPanic { panic_info: panic_info.clone() }); true }) } From 9581d0d966c77f464863f47abd1d0bd2e771464c Mon Sep 17 00:00:00 2001 From: JustJ01 Date: Thu, 19 Feb 2026 04:25:10 +0530 Subject: [PATCH 4/4] fixed allocation --- frontend/wasm/src/lib.rs | 37 ++++++++++++++++++++++++------------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/frontend/wasm/src/lib.rs b/frontend/wasm/src/lib.rs index 6e38fc6216..aa756b8074 100644 --- a/frontend/wasm/src/lib.rs +++ b/frontend/wasm/src/lib.rs @@ -75,50 +75,61 @@ pub fn panic_hook(info: &panic::PanicHookInfo) { // Prefer using the raw JS callback to avoid mutex lock contention inside the panic hook. // Fall back to the editor handle path if needed. - if !(send_panic_dialog_via_callback(&info) || send_panic_dialog(&info)) { - send_panic_dialog_deferred(info); + if let Err(info) = send_panic_dialog_via_callback(info) { + if let Err(info) = send_panic_dialog(info) { + send_panic_dialog_deferred(info); + } } } -fn send_panic_dialog_via_callback(panic_info: &String) -> bool { - let message = FrontendMessage::DisplayDialogPanic { panic_info: panic_info.clone() }; +fn send_panic_dialog_via_callback(panic_info: String) -> Result<(), String> { + let message = FrontendMessage::DisplayDialogPanic { panic_info }; let message_type = message.to_discriminant().local_name(); let Ok(message_data) = serde_wasm_bindgen::to_value(&message) else { log::error!("Failed to serialize crash dialog panic message"); - return false; + let FrontendMessage::DisplayDialogPanic { panic_info } = message else { + unreachable!("Message variant changed unexpectedly") + }; + return Err(panic_info); }; PANIC_DIALOG_MESSAGE_CALLBACK.with(|callback| { let callback_ref = callback.borrow(); let Some(callback) = callback_ref.as_ref() else { - return false; + let FrontendMessage::DisplayDialogPanic { panic_info } = message else { + unreachable!("Message variant changed unexpectedly") + }; + return Err(panic_info); }; if let Err(error) = callback.call2(&JsValue::null(), &JsValue::from(message_type), &message_data) { log::error!("Failed to send crash dialog panic message to JS: {:?}", error); - return false; + let FrontendMessage::DisplayDialogPanic { panic_info } = message else { + unreachable!("Message variant changed unexpectedly") + }; + return Err(panic_info); } - true + Ok(()) }) } -fn send_panic_dialog(panic_info: &String) -> bool { +fn send_panic_dialog(panic_info: String) -> Result<(), String> { EDITOR_HANDLE.with(|editor_handle| { let mut guard = editor_handle.try_lock(); let Ok(Some(handle)) = guard.as_deref_mut() else { - return false; + return Err(panic_info); }; - handle.send_frontend_message_to_js_rust_proxy(FrontendMessage::DisplayDialogPanic { panic_info: panic_info.clone() }); - true + handle.send_frontend_message_to_js_rust_proxy(FrontendMessage::DisplayDialogPanic { panic_info }); + Ok(()) }) } #[cfg(not(feature = "native"))] fn send_panic_dialog_deferred(panic_info: String) { let callback = Closure::once_into_js(move || { - if !send_panic_dialog(&panic_info) { + if send_panic_dialog(panic_info).is_err() { log::error!("Failed to send crash dialog after panic because the editor handle is unavailable"); } });