Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 16 additions & 8 deletions packages/desktop/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use wry::http::{Request as HttpRequest, Response as HttpResponse};
use wry::{RequestAsyncResponder, WebViewId};

use crate::ipc::UserWindowEvent;
use crate::menubar::{default_menu_bar, DioxusMenu};
use crate::menubar::{DioxusMenu, default_menu_bar};

type CustomEventHandler = Box<
dyn 'static
Expand All @@ -20,6 +20,8 @@ type CustomEventHandler = Box<
),
>;

type NavigationHandler = Box<dyn Fn(&str) -> bool + 'static>;

/// The closing behaviour of specific application window.
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
#[non_exhaustive]
Expand Down Expand Up @@ -69,6 +71,7 @@ pub struct Config {
pub(crate) disable_file_drop_handler: bool,
pub(crate) disable_dma_buf_on_wayland: bool,
pub(crate) additional_windows_args: Option<String>,
pub(crate) navigation_handler: Option<NavigationHandler>,

#[allow(clippy::type_complexity)]
pub(crate) on_window: Option<Box<dyn FnMut(Arc<Window>, &mut VirtualDom) + 'static>>,
Expand All @@ -81,10 +84,8 @@ pub(crate) type WryProtocol = (
Box<dyn Fn(WebViewId, HttpRequest<Vec<u8>>) -> HttpResponse<Cow<'static, [u8]>> + 'static>,
);

pub(crate) type AsyncWryProtocol = (
String,
Box<dyn Fn(WebViewId, HttpRequest<Vec<u8>>, RequestAsyncResponder) + 'static>,
);
pub(crate) type AsyncWryProtocol =
(String, Box<dyn Fn(WebViewId, HttpRequest<Vec<u8>>, RequestAsyncResponder) + 'static>);

impl Config {
/// Initializes a new `WindowBuilder` with default values.
Expand Down Expand Up @@ -122,6 +123,7 @@ impl Config {
disable_dma_buf_on_wayland: true,
on_window: None,
additional_windows_args: None,
navigation_handler: None,
}
}

Expand Down Expand Up @@ -201,7 +203,7 @@ impl Config {
pub fn with_custom_event_handler(
mut self,
f: impl FnMut(&tao::event::Event<'_, UserWindowEvent>, &EventLoopWindowTarget<UserWindowEvent>)
+ 'static,
+ 'static,
) -> Self {
self.custom_event_handler = Some(Box::new(f));
self
Expand Down Expand Up @@ -246,8 +248,7 @@ impl Config {
where
F: Fn(WebViewId, HttpRequest<Vec<u8>>, RequestAsyncResponder) + 'static,
{
self.asynchronous_protocols
.push((name.to_string(), Box::new(handler)));
self.asynchronous_protocols.push((name.to_string(), Box::new(handler)));
self
}

Expand Down Expand Up @@ -317,6 +318,13 @@ impl Config {
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
}

/// Set whether or not DMA-BUF usage should be disabled on Wayland.
///
/// Defaults to true to avoid issues on some systems. If you want to enable DMA-BUF usage, set this to false.
Expand Down
96 changes: 33 additions & 63 deletions packages/desktop/src/webview.rs
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
use crate::PendingDesktopContext;
use crate::file_upload::{DesktopFileData, DesktopFileDragEvent};
use crate::menubar::DioxusMenu;
use crate::PendingDesktopContext;
use crate::{
app::SharedContext, assets::AssetHandlerRegistry, edits::WryQueue,
file_upload::NativeFileHover, ipc::UserWindowEvent, protocol, waker::tao_waker, Config,
DesktopContext, DesktopService,
Config, DesktopContext, DesktopService, app::SharedContext, assets::AssetHandlerRegistry,
edits::WryQueue, file_upload::NativeFileHover, ipc::UserWindowEvent, protocol,
waker::tao_waker,
};
use crate::{document::DesktopDocument, WeakDesktopContext};
use crate::{WeakDesktopContext, document::DesktopDocument};
use crate::{element::DesktopElement, file_upload::DesktopFormData};
use base64::prelude::BASE64_STANDARD;
use dioxus_core::{consume_context, provide_context, Runtime, ScopeId, VirtualDom};
use dioxus_core::{Runtime, ScopeId, VirtualDom, consume_context, provide_context};
use dioxus_document::Document;
use dioxus_history::{History, MemoryHistory};
use dioxus_hooks::to_owned;
use dioxus_html::{FileData, FormValue, HtmlEvent, PlatformEventData, SerializedFileData};
use futures_util::{pin_mut, FutureExt};
use std::sync::{atomic::AtomicBool, Arc};
use futures_util::{FutureExt, pin_mut};
use std::sync::{Arc, atomic::AtomicBool};
use std::{cell::OnceCell, time::Duration};
use std::{rc::Rc, task::Waker};
use wry::{DragDropEvent, RequestAsyncResponder, WebContext, WebViewBuilder, WebViewId};
Expand All @@ -29,11 +29,7 @@ pub(crate) struct WebviewEdits {

impl WebviewEdits {
fn new(runtime: Rc<Runtime>, wry_queue: WryQueue) -> Self {
Self {
runtime,
wry_queue,
desktop_context: Default::default(),
}
Self { runtime, wry_queue, desktop_context: Default::default() }
}

fn set_desktop_context(&self, context: WeakDesktopContext) {
Expand All @@ -45,9 +41,7 @@ impl WebviewEdits {
request: wry::http::Request<Vec<u8>>,
responder: wry::RequestAsyncResponder,
) {
let body = self
.try_handle_event(request)
.expect("Writing bodies to succeed");
let body = self.try_handle_event(request).expect("Writing bodies to succeed");
responder.respond(wry::http::Response::new(body))
}

Expand Down Expand Up @@ -80,7 +74,7 @@ impl WebviewEdits {
// we need to wait for the mutex lock to let us munge the main thread..
let _lock = crate::android_sync_lock::android_runtime_lock();
self.handle_html_event(event)
}
},
Err(err) => {
tracing::error!(
"Error parsing user_event: {:?}. \n Contents: {:?}, \nraw: {:#?}",
Expand All @@ -89,7 +83,7 @@ impl WebviewEdits {
request
);
SynchronousEventResponse::new(false)
}
},
};

serde_json::to_vec(&response).inspect_err(|err| {
Expand All @@ -98,12 +92,7 @@ impl WebviewEdits {
}

pub fn handle_html_event(&self, event: HtmlEvent) -> SynchronousEventResponse {
let HtmlEvent {
element,
name,
bubbles,
data,
} = event;
let HtmlEvent { element, name, bubbles, data } = event;
let Some(desktop_context) = self.desktop_context.get() else {
tracing::error!(
"Tried to handle event before setting the desktop context on the event handler"
Expand All @@ -121,7 +110,7 @@ impl WebviewEdits {
dioxus_html::EventData::Mounted => {
let element = DesktopElement::new(element, desktop_context.clone(), query.clone());
Rc::new(PlatformEventData::new(Box::new(element)))
}
},
dioxus_html::EventData::Form(form) => {
Rc::new(PlatformEventData::new(Box::new(DesktopFormData {
value: form.value,
Expand Down Expand Up @@ -151,7 +140,7 @@ impl WebviewEdits {
})
.collect(),
})))
}
},
// Which also includes drops...
dioxus_html::EventData::Drag(ref drag) => {
// we want to override this with a native file engine, provided by the most recent drag event
Expand All @@ -166,23 +155,18 @@ impl WebviewEdits {
.iter()
.find(|p| p.ends_with(&f.path))
.unwrap_or(&f.path);
SerializedFileData {
path: new_path.clone(),
..f.clone()
}
SerializedFileData { path: new_path.clone(), ..f.clone() }
})
.collect::<Vec<_>>();
let new_xfer_data = dioxus_html::SerializedDataTransfer {
files: new_file_data,
..xfer_data
};
let new_xfer_data =
dioxus_html::SerializedDataTransfer { files: new_file_data, ..xfer_data };

Rc::new(PlatformEventData::new(Box::new(DesktopFileDragEvent {
mouse: drag.mouse.clone(),
data_transfer: new_xfer_data,
files: full_file_paths,
})))
}
},
_ => data.into_any(),
};

Expand Down Expand Up @@ -272,13 +256,7 @@ impl WebviewInstance {
let headless = !cfg.window.window.visible;

let request_handler = {
to_owned![
cfg.custom_head,
cfg.custom_index,
cfg.root_name,
asset_handlers,
edits
];
to_owned![cfg.custom_head, cfg.custom_index, cfg.root_name, asset_handlers, edits];

#[cfg(feature = "tokio_runtime")]
let tokio_rt = tokio::runtime::Handle::current();
Expand Down Expand Up @@ -326,28 +304,26 @@ impl WebviewInstance {
// Solution: this glue code to mimic drag drop events.
file_hover.set(evt.clone());
match evt {
wry::DragDropEvent::Drop {
paths: _,
position: _,
} => {
wry::DragDropEvent::Drop { paths: _, position: _ } => {
_ = proxy.send_event(UserWindowEvent::WindowsDragDrop(window_id));
}
},
wry::DragDropEvent::Over { position } => {
_ = proxy.send_event(UserWindowEvent::WindowsDragOver(
window_id, position.0, position.1,
));
}
},
wry::DragDropEvent::Leave => {
_ = proxy.send_event(UserWindowEvent::WindowsDragLeave(window_id));
}
_ => {}
},
_ => {},
}
}

false
}
};

let navigation_handler = cfg.navigation_handler.take();
let page_loaded = AtomicBool::new(false);

let mut webview = WebViewBuilder::new_with_web_context(&mut web_context)
Expand All @@ -372,6 +348,9 @@ impl WebviewInstance {
let page_loaded = page_loaded.swap(true, std::sync::atomic::Ordering::SeqCst);
!page_loaded
} else {
if let Some(handler) = navigation_handler.as_ref() {
return handler(&var);
}
if var.starts_with("http://")
|| var.starts_with("https://")
|| var.starts_with("mailto:")
Expand Down Expand Up @@ -536,12 +515,7 @@ impl WebviewInstance {
// find the socket has been closed, we create a new socket and send it to
// the webview to continue on
// https://github.com/DioxusLabs/dioxus/issues/4374
if self
.edits
.wry_queue
.poll_new_edits_location(&mut cx)
.is_ready()
{
if self.edits.wry_queue.poll_new_edits_location(&mut cx).is_ready() {
_ = self.desktop_context.webview.evaluate_script(&format!(
"window.interpreter.waitForRequest(\"{edits_path}\", \"{expected_key}\");",
edits_path = self.edits.wry_queue.edits_path(),
Expand All @@ -562,17 +536,15 @@ impl WebviewInstance {
pin_mut!(fut);

match fut.poll_unpin(&mut cx) {
std::task::Poll::Ready(_) => {}
std::task::Poll::Ready(_) => {},
std::task::Poll::Pending => return,
}
}

// lock the hack-ed in lock sync wry has some thread-safety issues with event handlers
let _lock = crate::android_sync_lock::android_runtime_lock();

self.edits
.wry_queue
.with_mutation_state_mut(|f| self.dom.render_immediate(f));
self.edits.wry_queue.with_mutation_state_mut(|f| self.dom.render_immediate(f));
self.edits.wry_queue.send_edits();
}
}
Expand Down Expand Up @@ -647,9 +619,7 @@ impl PendingWebview {
pub(crate) fn create_window(self, shared: &Rc<SharedContext>) -> WebviewInstance {
let window = WebviewInstance::new(self.cfg, self.dom, shared.clone());

let cx = window
.dom
.in_scope(ScopeId::ROOT, consume_context::<Rc<DesktopService>>);
let cx = window.dom.in_scope(ScopeId::ROOT, consume_context::<Rc<DesktopService>>);
_ = self.sender.send(cx);

window
Expand Down