diff --git a/crates/tauri-runtime-wry/src/lib.rs b/crates/tauri-runtime-wry/src/lib.rs index 61799d0394c9..c8af13e56f87 100644 --- a/crates/tauri-runtime-wry/src/lib.rs +++ b/crates/tauri-runtime-wry/src/lib.rs @@ -1427,6 +1427,15 @@ pub enum WebviewMessage { EvaluateScript(String), #[cfg(all(feature = "tracing", not(target_os = "android")))] EvaluateScript(String, Sender<()>, tracing::Span), + #[cfg(not(all(feature = "tracing", not(target_os = "android"))))] + EvaluateScriptWithCallback(String, Box), + #[cfg(all(feature = "tracing", not(target_os = "android")))] + EvaluateScriptWithCallback( + String, + Box, + Sender<()>, + tracing::Span, + ), CookiesForUrl(Url, Sender>>>), Cookies(Sender>>>), SetCookie(tauri_runtime::Cookie<'static>), @@ -1782,6 +1791,46 @@ impl WebviewDispatch for WryWebviewDispatcher { ) } + #[cfg(all(feature = "tracing", not(target_os = "android")))] + fn eval_script_with_callback>( + &self, + script: S, + callback: impl Fn(String) + Send + 'static, + ) -> Result<()> { + // use a channel so the EvaluateScript task uses the current span as parent + let (tx, rx) = channel(); + getter!( + self, + rx, + Message::Webview( + *self.window_id.lock().unwrap(), + self.webview_id, + WebviewMessage::EvaluateScriptWithCallback( + script.into(), + Box::new(callback), + tx, + tracing::Span::current(), + ), + ) + ) + } + + #[cfg(not(all(feature = "tracing", not(target_os = "android"))))] + fn eval_script_with_callback>( + &self, + script: S, + callback: impl Fn(String) + Send + 'static, + ) -> Result<()> { + send_user_message( + &self.context, + Message::Webview( + *self.window_id.lock().unwrap(), + self.webview_id, + WebviewMessage::EvaluateScriptWithCallback(script.into(), Box::new(callback)), + ), + ) + } + fn set_zoom(&self, scale_factor: f64) -> Result<()> { send_user_message( &self.context, @@ -3644,6 +3693,20 @@ fn handle_user_message( log::error!("{e}"); } } + #[cfg(all(feature = "tracing", not(target_os = "android")))] + WebviewMessage::EvaluateScriptWithCallback(script, callback, tx, span) => { + let _span = span.entered(); + if let Err(e) = webview.evaluate_script_with_callback(&script, callback) { + log::error!("{e}"); + } + tx.send(()).unwrap(); + } + #[cfg(not(all(feature = "tracing", not(target_os = "android"))))] + WebviewMessage::EvaluateScriptWithCallback(script, callback) => { + if let Err(e) = webview.evaluate_script_with_callback(&script, callback) { + log::error!("{e}"); + } + } WebviewMessage::Navigate(url) => { if let Err(e) = webview.load_url(url.as_str()) { log::error!("failed to navigate to url {}: {}", url, e); diff --git a/crates/tauri-runtime/src/lib.rs b/crates/tauri-runtime/src/lib.rs index 5bf869dcde55..8baeee2168de 100644 --- a/crates/tauri-runtime/src/lib.rs +++ b/crates/tauri-runtime/src/lib.rs @@ -576,6 +576,18 @@ pub trait WebviewDispatch: Debug + Clone + Send + Sync + Sized + ' /// Executes javascript on the window this [`WindowDispatch`] represents. fn eval_script>(&self, script: S) -> Result<()>; + /// Evaluate JavaScript with callback function on the window this [`WindowDispatch`] represents. + /// The evaluation result will be serialized into a JSON string and passed to the callback function. + /// + /// Exception is ignored because of the limitation on windows. You can catch it yourself and return as string as a workaround. + /// + /// - **Android:** Not implemented yet. + fn eval_script_with_callback>( + &self, + script: S, + callback: impl Fn(String) + Send + 'static, + ) -> Result<()>; + /// Moves the webview to the given window. fn reparent(&self, window_id: WindowId) -> Result<()>; diff --git a/crates/tauri/src/test/mock_runtime.rs b/crates/tauri/src/test/mock_runtime.rs index 5fbedd013fb4..584682d70cff 100644 --- a/crates/tauri/src/test/mock_runtime.rs +++ b/crates/tauri/src/test/mock_runtime.rs @@ -578,6 +578,19 @@ impl WebviewDispatch for MockWebviewDispatcher { Ok(()) } + fn eval_script_with_callback>( + &self, + script: S, + callback: impl Fn(String) + Send + 'static, + ) -> Result<()> { + self + .last_evaluated_script + .lock() + .unwrap() + .replace(script.into()); + Ok(()) + } + fn url(&self) -> Result { Ok(self.url.lock().unwrap().clone()) } diff --git a/crates/tauri/src/webview/mod.rs b/crates/tauri/src/webview/mod.rs index 8842382bf15a..f7076b921e9a 100644 --- a/crates/tauri/src/webview/mod.rs +++ b/crates/tauri/src/webview/mod.rs @@ -1901,6 +1901,24 @@ tauri::Builder::default() .map_err(Into::into) } + /// Evaluate JavaScript with callback function on this window. + /// The evaluation result will be serialized into a JSON string and passed to the callback function. + /// + /// Exception is ignored because of the limitation on windows. You can catch it yourself and return as string as a workaround. + /// + /// - **Android:** Not implemented yet. + pub fn eval_with_callback( + &self, + js: impl Into, + callback: impl Fn(String) + Send + 'static, + ) -> crate::Result<()> { + self + .webview + .dispatcher + .eval_script_with_callback(js.into(), callback) + .map_err(Into::into) + } + /// Register a JS event listener and return its identifier. pub(crate) fn listen_js( &self, diff --git a/crates/tauri/src/webview/webview_window.rs b/crates/tauri/src/webview/webview_window.rs index 290730067f5d..ca68d648ebd9 100644 --- a/crates/tauri/src/webview/webview_window.rs +++ b/crates/tauri/src/webview/webview_window.rs @@ -2324,6 +2324,20 @@ impl WebviewWindow { self.webview.eval(js) } + /// Evaluate JavaScript with callback function on this window. + /// The evaluation result will be serialized into a JSON string and passed to the callback function. + /// + /// Exception is ignored because of the limitation on windows. You can catch it yourself and return as string as a workaround. + /// + /// - **Android:** Not implemented yet. + pub fn eval_with_callback( + &self, + js: impl Into, + callback: impl Fn(String) + Send + 'static, + ) -> crate::Result<()> { + self.webview.eval_with_callback(js, callback) + } + /// Opens the developer tools window (Web Inspector). /// The devtools is only enabled on debug builds or with the `devtools` feature flag. ///