Skip to content

Commit 552070d

Browse files
feat:Add opt‑in navigation handler for external URLs (#5531)
* add navigation handler config option code from #5231 * Re-order branches The branches should be reorder so that the branch that checks for the index and assets is always taken first. Otherwise, they will fail to load for users who forget to allow them in their custom handlers, which will be a crappy experience and hard to debug. * remove return * Change behavior when handler not set --------- Co-authored-by: not a cow <agielow@proton.me>
1 parent f2e4353 commit 552070d

2 files changed

Lines changed: 27 additions & 3 deletions

File tree

packages/desktop/src/config.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ type CustomEventHandler = Box<
2020
),
2121
>;
2222

23+
/// A function taking a URL and returning whether the webview should navigate to it or open it in
24+
/// the browser. If missing in the config, all URLs will be allowed.
25+
type NavigationHandler = Box<dyn Fn(&str) -> bool + 'static>;
26+
2327
/// The closing behaviour of specific application window.
2428
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
2529
#[non_exhaustive]
@@ -70,6 +74,7 @@ pub struct Config {
7074
pub(crate) disable_dma_buf_on_wayland: bool,
7175
pub(crate) additional_windows_args: Option<String>,
7276
pub(crate) tray_icon_show_window_on_click: bool,
77+
pub(crate) navigation_handler: Option<NavigationHandler>,
7378

7479
#[allow(clippy::type_complexity)]
7580
pub(crate) on_window: Option<Box<dyn FnMut(Arc<Window>, &mut VirtualDom) + 'static>>,
@@ -124,6 +129,7 @@ impl Config {
124129
on_window: None,
125130
additional_windows_args: None,
126131
tray_icon_show_window_on_click: true,
132+
navigation_handler: None,
127133
}
128134
}
129135

@@ -350,6 +356,13 @@ impl Config {
350356
self.tray_icon_show_window_on_click = show;
351357
self
352358
}
359+
360+
/// Set a custom navigation handler for non-dioxus URLs.
361+
/// Return true to allow navigation inside the webview, false to block.
362+
pub fn with_navigation_handler(mut self, f: impl Fn(&str) -> bool + 'static) -> Self {
363+
self.navigation_handler = Some(Box::new(f));
364+
self
365+
}
353366
}
354367

355368
impl Default for Config {

packages/desktop/src/webview.rs

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -349,6 +349,7 @@ impl WebviewInstance {
349349
}
350350
};
351351

352+
let navigation_handler = cfg.navigation_handler.take();
352353
let page_loaded = AtomicBool::new(false);
353354

354355
let mut webview = WebViewBuilder::new_with_web_context(&mut web_context)
@@ -363,15 +364,25 @@ impl WebviewInstance {
363364
.with_url("dioxus://index.html/")
364365
.with_ipc_handler(ipc_handler)
365366
.with_navigation_handler(move |var| {
366-
// We don't want to allow any navigation
367-
// We only want to serve the index file and assets
367+
// Serve the index and assets.
368368
if var.starts_with("dioxus://")
369369
|| var.starts_with("http://dioxus.")
370370
|| var.starts_with("https://dioxus.")
371371
{
372372
// After the page has loaded once, don't allow any more navigation
373373
let page_loaded = page_loaded.swap(true, std::sync::atomic::Ordering::SeqCst);
374-
!page_loaded
374+
return !page_loaded;
375+
}
376+
377+
// By default, navigation is allowed. Users can have more granular control by
378+
// providing a navigation handler. If not allowed, valid URLs will be opened in the
379+
// browser.
380+
let allow_nav = match navigation_handler.as_ref() {
381+
Some(handler) => handler(&var),
382+
None => true,
383+
};
384+
if allow_nav {
385+
true
375386
} else {
376387
if var.starts_with("http://")
377388
|| var.starts_with("https://")

0 commit comments

Comments
 (0)