Fix web eval return (#3656)

* Fix web eval return

* add regression test to playwright
This commit is contained in:
Evan Almloff authored and GitHub committed 2025-01-27 12:57:20 -08:00
1 parent fb7b1a30bf
commit bd30516490
2 files changed
+30 -20

No files matched your search

@@ -25,6 +25,13 @@ fn app() -> Element {
button { button {
class: "eval-button", class: "eval-button",
onclick: move |_| async move { onclick: move |_| async move {
// Make sure normal return values work. Regression test for https://github.com/DioxusLabs/dioxus/issues/3655
let eval = document::eval(r#"return "hello world";"#);
let result = eval.await.unwrap();
assert_eq!(result, "hello world");
// Make sure dioxus.send/dioxus.recv works
let mut eval = document::eval( let mut eval = document::eval(
r#" r#"
window.document.title = 'Hello from Dioxus Eval!'; window.document.title = 'Hello from Dioxus Eval!';
+23 -20
View File
@@ -5,14 +5,17 @@ use dioxus_document::{
ScriptProps, StyleProps, ScriptProps, StyleProps,
}; };
use dioxus_history::History; use dioxus_history::History;
use futures_util::FutureExt;
use generational_box::{AnyStorage, GenerationalBox, UnsyncStorage}; use generational_box::{AnyStorage, GenerationalBox, UnsyncStorage};
use js_sys::Function; use js_sys::Function;
use serde::Serialize; use serde::Serialize;
use serde_json::Value; use serde_json::Value;
use std::future::Future; use std::future::Future;
use std::pin::Pin; use std::pin::Pin;
use std::result;
use std::{rc::Rc, str::FromStr}; use std::{rc::Rc, str::FromStr};
use wasm_bindgen::prelude::*; use wasm_bindgen::prelude::*;
use wasm_bindgen_futures::JsFuture;
use crate::history::WebHistory; use crate::history::WebHistory;
@@ -131,10 +134,9 @@ impl Document for WebDocument {
/// Required to avoid blocking the Rust WASM thread. /// Required to avoid blocking the Rust WASM thread.
const PROMISE_WRAPPER: &str = r#" const PROMISE_WRAPPER: &str = r#"
return new Promise(async (resolve, _reject) => { return (async function(){
{JS_CODE} {JS_CODE}
resolve(null); })();
});
"#; "#;
type NextPoll = Pin<Box<dyn Future<Output = Result<serde_json::Value, EvalError>>>>; type NextPoll = Pin<Box<dyn Future<Output = Result<serde_json::Value, EvalError>>>>;
@@ -143,7 +145,7 @@ type NextPoll = Pin<Box<dyn Future<Output = Result<serde_json::Value, EvalError>
struct WebEvaluator { struct WebEvaluator {
channels: WeakDioxusChannel, channels: WeakDioxusChannel,
next_future: Option<NextPoll>, next_future: Option<NextPoll>,
result: Option<Result<serde_json::Value, EvalError>>, result: Pin<Box<dyn Future<Output = result::Result<Value, EvalError>>>>,
} }
impl WebEvaluator { impl WebEvaluator {
@@ -163,7 +165,15 @@ impl WebEvaluator {
let result = match Function::new_with_args("dioxus", &code).call1(&JsValue::NULL, &channels) let result = match Function::new_with_args("dioxus", &code).call1(&JsValue::NULL, &channels)
{ {
Ok(result) => { Ok(result) => {
if let Ok(stringified) = js_sys::JSON::stringify(&result) { let future = js_sys::Promise::resolve(&result);
let js_future = JsFuture::from(future);
Box::pin(async move {
let result = js_future.await.map_err(|e| {
EvalError::Communication(format!("Failed to await result - {:?}", e))
})?;
let stringified = js_sys::JSON::stringify(&result).map_err(|e| {
EvalError::Communication(format!("Failed to stringify result - {:?}", e))
})?;
if !stringified.is_undefined() && stringified.is_valid_utf16() { if !stringified.is_undefined() && stringified.is_valid_utf16() {
let string: String = stringified.into(); let string: String = stringified.into();
Value::from_str(&string).map_err(|e| { Value::from_str(&string).map_err(|e| {
@@ -171,23 +181,20 @@ impl WebEvaluator {
}) })
} else { } else {
Err(EvalError::Communication( Err(EvalError::Communication(
"Failed to stringify result".into(), "Failed to stringify result - undefined or not valid utf16".to_string(),
)) ))
} }
} else { })
Err(EvalError::Communication( as Pin<Box<dyn Future<Output = result::Result<Value, EvalError>>>>
"Failed to stringify result".into(),
))
}
} }
Err(err) => Err(EvalError::InvalidJs( Err(err) => Box::pin(futures_util::future::ready(Err(EvalError::InvalidJs(
err.as_string().unwrap_or("unknown".to_string()), err.as_string().unwrap_or("unknown".to_string()),
)), )))),
}; };
owner.insert(Box::new(Self { owner.insert(Box::new(Self {
channels: weak_channels, channels: weak_channels,
result: Some(result), result,
next_future: None, next_future: None,
}) as Box<dyn Evaluator>) }) as Box<dyn Evaluator>)
} }
@@ -197,13 +204,9 @@ impl Evaluator for WebEvaluator {
/// Runs the evaluated JavaScript. /// Runs the evaluated JavaScript.
fn poll_join( fn poll_join(
&mut self, &mut self,
_cx: &mut std::task::Context<'_>, cx: &mut std::task::Context<'_>,
) -> std::task::Poll<Result<serde_json::Value, EvalError>> { ) -> std::task::Poll<Result<serde_json::Value, EvalError>> {
if let Some(result) = self.result.take() { self.result.poll_unpin(cx)
std::task::Poll::Ready(result)
} else {
std::task::Poll::Ready(Err(EvalError::Finished))
}
} }
/// Sends a message to the evaluated JavaScript. /// Sends a message to the evaluated JavaScript.