diff --git a/packages/desktop/src/config.rs b/packages/desktop/src/config.rs index 18e343fe02..4c616f2333 100644 --- a/packages/desktop/src/config.rs +++ b/packages/desktop/src/config.rs @@ -20,6 +20,10 @@ type CustomEventHandler = Box< ), >; +/// A function taking a URL and returning whether the webview should navigate to it or open it in +/// the browser. If missing in the config, all URLs will be allowed. +type NavigationHandler = Box bool + 'static>; + /// The closing behaviour of specific application window. #[derive(Debug, Copy, Clone, Eq, PartialEq)] #[non_exhaustive] @@ -70,6 +74,7 @@ pub struct Config { pub(crate) disable_dma_buf_on_wayland: bool, pub(crate) additional_windows_args: Option, pub(crate) tray_icon_show_window_on_click: bool, + pub(crate) navigation_handler: Option, #[allow(clippy::type_complexity)] pub(crate) on_window: Option, &mut VirtualDom) + 'static>>, @@ -124,6 +129,7 @@ impl Config { on_window: None, additional_windows_args: None, tray_icon_show_window_on_click: true, + navigation_handler: None, } } @@ -350,6 +356,13 @@ impl Config { self.tray_icon_show_window_on_click = show; self } + + /// Set a custom navigation handler for non-dioxus URLs. + /// Return true to allow navigation inside the webview, false to block. + pub fn with_navigation_handler(mut self, f: impl Fn(&str) -> bool + 'static) -> Self { + self.navigation_handler = Some(Box::new(f)); + self + } } impl Default for Config { diff --git a/packages/desktop/src/webview.rs b/packages/desktop/src/webview.rs index 83d726eae7..028a0b87ab 100644 --- a/packages/desktop/src/webview.rs +++ b/packages/desktop/src/webview.rs @@ -349,6 +349,7 @@ impl WebviewInstance { } }; + let navigation_handler = cfg.navigation_handler.take(); let page_loaded = AtomicBool::new(false); let mut webview = WebViewBuilder::new_with_web_context(&mut web_context) @@ -363,15 +364,25 @@ impl WebviewInstance { .with_url("dioxus://index.html/") .with_ipc_handler(ipc_handler) .with_navigation_handler(move |var| { - // We don't want to allow any navigation - // We only want to serve the index file and assets + // Serve the index and assets. if var.starts_with("dioxus://") || var.starts_with("http://dioxus.") || var.starts_with("https://dioxus.") { // After the page has loaded once, don't allow any more navigation let page_loaded = page_loaded.swap(true, std::sync::atomic::Ordering::SeqCst); - !page_loaded + return !page_loaded; + } + + // By default, navigation is allowed. Users can have more granular control by + // providing a navigation handler. If not allowed, valid URLs will be opened in the + // browser. + let allow_nav = match navigation_handler.as_ref() { + Some(handler) => handler(&var), + None => true, + }; + if allow_nav { + true } else { if var.starts_with("http://") || var.starts_with("https://")