From 61ee9575c2eb49c50935b40d6b3c9473d7d82f9c Mon Sep 17 00:00:00 2001 From: Timon Schelling Date: Tue, 5 Aug 2025 18:11:12 +0000 Subject: [PATCH 1/3] Deny all browser popups --- desktop/src/cef/internal.rs | 9 +-- .../cef/internal/browser_process_client.rs | 8 ++- .../browser_process_life_span_handler.rs | 64 +++++++++++++++++++ 3 files changed, 76 insertions(+), 5 deletions(-) create mode 100644 desktop/src/cef/internal/browser_process_life_span_handler.rs diff --git a/desktop/src/cef/internal.rs b/desktop/src/cef/internal.rs index ad604b5957..03a520e669 100644 --- a/desktop/src/cef/internal.rs +++ b/desktop/src/cef/internal.rs @@ -1,12 +1,13 @@ mod browser_process_app; mod browser_process_client; mod browser_process_handler; +mod browser_process_life_span_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; -pub(crate) use render_handler::RenderHandlerImpl; -pub(crate) use render_process_app::RenderProcessAppImpl; +pub(super) use browser_process_app::BrowserProcessAppImpl; +pub(super) use browser_process_client::BrowserProcessClientImpl; +pub(super) use render_handler::RenderHandlerImpl; +pub(super) use render_process_app::RenderProcessAppImpl; diff --git a/desktop/src/cef/internal/browser_process_client.rs b/desktop/src/cef/internal/browser_process_client.rs index bd316964aa..d5bd70edf2 100644 --- a/desktop/src/cef/internal/browser_process_client.rs +++ b/desktop/src/cef/internal/browser_process_client.rs @@ -1,10 +1,12 @@ use cef::rc::{Rc, RcImpl}; use cef::sys::{_cef_client_t, cef_base_ref_counted_t}; -use cef::{ImplClient, RenderHandler, WrapClient}; +use cef::{ImplClient, LifeSpanHandler, RenderHandler, WrapClient}; use crate::cef::CefEventHandler; use crate::cef::ipc::{MessageType, UnpackMessage, UnpackedMessage}; +use super::browser_process_life_span_handler::BrowserProcessLifeSpanHandlerImpl; + pub(crate) struct BrowserProcessClientImpl { object: *mut RcImpl<_cef_client_t, Self>, render_handler: RenderHandler, @@ -47,6 +49,10 @@ impl ImplClient for BrowserProcessClientImpl { Some(self.render_handler.clone()) } + fn life_span_handler(&self) -> Option { + Some(LifeSpanHandler::new(BrowserProcessLifeSpanHandlerImpl::new())) + } + fn get_raw(&self) -> *mut _cef_client_t { self.object.cast() } diff --git a/desktop/src/cef/internal/browser_process_life_span_handler.rs b/desktop/src/cef/internal/browser_process_life_span_handler.rs new file mode 100644 index 0000000000..9d47074325 --- /dev/null +++ b/desktop/src/cef/internal/browser_process_life_span_handler.rs @@ -0,0 +1,64 @@ +use cef::rc::{Rc, RcImpl}; +use cef::sys::{_cef_life_span_handler_t, cef_base_ref_counted_t}; +use cef::{ImplLifeSpanHandler, WrapLifeSpanHandler}; + +pub(crate) struct BrowserProcessLifeSpanHandlerImpl { + object: *mut RcImpl<_cef_life_span_handler_t, Self>, +} +impl BrowserProcessLifeSpanHandlerImpl { + pub(crate) fn new() -> Self { + Self { object: std::ptr::null_mut() } + } +} + +impl ImplLifeSpanHandler for BrowserProcessLifeSpanHandlerImpl { + fn on_before_popup( + &self, + _browser: Option<&mut cef::Browser>, + _frame: Option<&mut cef::Frame>, + _popup_id: ::std::os::raw::c_int, + target_url: Option<&cef::CefString>, + _target_frame_name: Option<&cef::CefString>, + _target_disposition: cef::WindowOpenDisposition, + _user_gesture: ::std::os::raw::c_int, + _popup_features: Option<&cef::PopupFeatures>, + _window_info: Option<&mut cef::WindowInfo>, + _client: Option<&mut Option>, + _settings: Option<&mut cef::BrowserSettings>, + _extra_info: Option<&mut Option>, + _no_javascript_access: Option<&mut ::std::os::raw::c_int>, + ) -> ::std::os::raw::c_int { + let target = target_url.map(|url| url.to_string()).unwrap_or("unknown".to_string()); + tracing::error!("Browser tried to open a popup at URL: {}", target); + + // Deny any popup by returning 1 + 1 + } + + fn get_raw(&self) -> *mut _cef_life_span_handler_t { + self.object.cast() + } +} + +impl Clone for BrowserProcessLifeSpanHandlerImpl { + fn clone(&self) -> Self { + unsafe { + let rc_impl = &mut *self.object; + rc_impl.interface.add_ref(); + } + Self { object: self.object } + } +} +impl Rc for BrowserProcessLifeSpanHandlerImpl { + fn as_base(&self) -> &cef_base_ref_counted_t { + unsafe { + let base = &*self.object; + std::mem::transmute(&base.cef_object) + } + } +} +impl WrapLifeSpanHandler for BrowserProcessLifeSpanHandlerImpl { + fn wrap_rc(&mut self, object: *mut RcImpl<_cef_life_span_handler_t, Self>) { + self.object = object; + } +} From a8cda4cfe1c8cf9e4dce0c95f8a920e603e84e0d Mon Sep 17 00:00:00 2001 From: Timon Schelling Date: Tue, 5 Aug 2025 19:56:14 +0000 Subject: [PATCH 2/3] Open links with default browser --- Cargo.lock | 37 +++++++++++++++++++++++++++++++++++++ Cargo.toml | 1 + desktop/Cargo.toml | 1 + desktop/src/app.rs | 14 ++++++++++++-- 4 files changed, 51 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 10ddcda817..d5b04572c5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2114,6 +2114,7 @@ dependencies = [ "graphene-std", "graphite-editor", "include_dir", + "open", "rfd", "ron", "thiserror 2.0.12", @@ -2725,6 +2726,15 @@ dependencies = [ "serde", ] +[[package]] +name = "is-docker" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "928bae27f42bc99b60d9ac7334e3a21d10ad8f1835a4e12ec3ec0464765ed1b3" +dependencies = [ + "once_cell", +] + [[package]] name = "is-terminal" version = "0.4.16" @@ -2736,6 +2746,16 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "is-wsl" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "173609498df190136aa7dea1a91db051746d339e18476eed5ca40521f02d7aa5" +dependencies = [ + "is-docker", + "once_cell", +] + [[package]] name = "is_terminal_polyfill" version = "1.70.1" @@ -3647,6 +3667,17 @@ version = "11.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e" +[[package]] +name = "open" +version = "5.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2483562e62ea94312f3576a7aca397306df7990b8d89033e18766744377ef95" +dependencies = [ + "is-wsl", + "libc", + "pathdiff", +] + [[package]] name = "openssl" version = "0.10.73" @@ -3802,6 +3833,12 @@ dependencies = [ "svg", ] +[[package]] +name = "pathdiff" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3" + [[package]] name = "peniko" version = "0.4.0" diff --git a/Cargo.toml b/Cargo.toml index 89fee5170c..0b38eed6e0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -164,6 +164,7 @@ include_dir = "0.7.4" tracing-subscriber = { version = "0.3.19", features = ["env-filter"] } tracing = "0.1.41" rfd = "0.15.4" +open = "5.3.2" [profile.dev] opt-level = 1 diff --git a/desktop/Cargo.toml b/desktop/Cargo.toml index f6a1ce861e..dd017e2cef 100644 --- a/desktop/Cargo.toml +++ b/desktop/Cargo.toml @@ -38,3 +38,4 @@ glam = { workspace = true } vello = { workspace = true } derivative = { workspace = true } rfd = { workspace = true } +open ={ workspace = true } diff --git a/desktop/src/app.rs b/desktop/src/app.rs index 9dc16085e5..a42f47a4e0 100644 --- a/desktop/src/app.rs +++ b/desktop/src/app.rs @@ -55,6 +55,10 @@ impl WinitApp { } fn send_messages_to_editor(&mut self, mut responses: Vec) { + if responses.is_empty() { + return; + } + for message in responses.extract_if(.., |m| matches!(m, FrontendMessage::RenderOverlays(_))) { let FrontendMessage::RenderOverlays(overlay_context) = message else { unreachable!() }; if let Some(graphics_state) = &mut self.graphics_state { @@ -106,9 +110,15 @@ impl WinitApp { } } - if responses.is_empty() { - return; + for message in responses.extract_if(.., |m| matches!(m, FrontendMessage::TriggerVisitLink { .. })) { + let _ = thread::spawn(move || { + let FrontendMessage::TriggerVisitLink { url } = message else { unreachable!() }; + if let Err(e) = open::that(&url) { + tracing::error!("Failed to open URL: {}: {}", url, e); + } + }); } + let Ok(message) = ron::to_string(&responses) else { tracing::error!("Failed to serialize Messages"); return; From 212fbe89bbc6bfa0f76446d9ff95f1abb628af55 Mon Sep 17 00:00:00 2001 From: Timon Schelling Date: Tue, 5 Aug 2025 20:13:49 +0000 Subject: [PATCH 3/3] Fix review comments --- desktop/Cargo.toml | 2 +- desktop/src/app.rs | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/desktop/Cargo.toml b/desktop/Cargo.toml index dd017e2cef..03768efd38 100644 --- a/desktop/Cargo.toml +++ b/desktop/Cargo.toml @@ -38,4 +38,4 @@ glam = { workspace = true } vello = { workspace = true } derivative = { workspace = true } rfd = { workspace = true } -open ={ workspace = true } +open = { workspace = true } diff --git a/desktop/src/app.rs b/desktop/src/app.rs index a42f47a4e0..f0641759a6 100644 --- a/desktop/src/app.rs +++ b/desktop/src/app.rs @@ -55,10 +55,6 @@ impl WinitApp { } fn send_messages_to_editor(&mut self, mut responses: Vec) { - if responses.is_empty() { - return; - } - for message in responses.extract_if(.., |m| matches!(m, FrontendMessage::RenderOverlays(_))) { let FrontendMessage::RenderOverlays(overlay_context) = message else { unreachable!() }; if let Some(graphics_state) = &mut self.graphics_state { @@ -119,6 +115,9 @@ impl WinitApp { }); } + if responses.is_empty() { + return; + } let Ok(message) = ron::to_string(&responses) else { tracing::error!("Failed to serialize Messages"); return;