From bd305164905c0b612c7736d519408053a9cf76fe Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Mon, 27 Jan 2025 14:57:20 -0600 Subject: [PATCH] Fix web eval return (#3656) * Fix web eval return * add regression test to playwright --- packages/playwright-tests/web/src/main.rs | 7 ++++ packages/web/src/document.rs | 43 ++++++++++++----------- 2 files changed, 30 insertions(+), 20 deletions(-) diff --git a/packages/playwright-tests/web/src/main.rs b/packages/playwright-tests/web/src/main.rs index 58163ebd7..6e385959c 100644 --- a/packages/playwright-tests/web/src/main.rs +++ b/packages/playwright-tests/web/src/main.rs @@ -25,6 +25,13 @@ fn app() -> Element { button { class: "eval-button", 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( r#" window.document.title = 'Hello from Dioxus Eval!'; diff --git a/packages/web/src/document.rs b/packages/web/src/document.rs index 66853c4e1..b97402e7e 100644 --- a/packages/web/src/document.rs +++ b/packages/web/src/document.rs @@ -5,14 +5,17 @@ use dioxus_document::{ ScriptProps, StyleProps, }; use dioxus_history::History; +use futures_util::FutureExt; use generational_box::{AnyStorage, GenerationalBox, UnsyncStorage}; use js_sys::Function; use serde::Serialize; use serde_json::Value; use std::future::Future; use std::pin::Pin; +use std::result; use std::{rc::Rc, str::FromStr}; use wasm_bindgen::prelude::*; +use wasm_bindgen_futures::JsFuture; use crate::history::WebHistory; @@ -131,10 +134,9 @@ impl Document for WebDocument { /// Required to avoid blocking the Rust WASM thread. const PROMISE_WRAPPER: &str = r#" - return new Promise(async (resolve, _reject) => { + return (async function(){ {JS_CODE} - resolve(null); - }); + })(); "#; type NextPoll = Pin>>>; @@ -143,7 +145,7 @@ type NextPoll = Pin struct WebEvaluator { channels: WeakDioxusChannel, next_future: Option, - result: Option>, + result: Pin>>>, } impl WebEvaluator { @@ -163,7 +165,15 @@ impl WebEvaluator { let result = match Function::new_with_args("dioxus", &code).call1(&JsValue::NULL, &channels) { 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() { let string: String = stringified.into(); Value::from_str(&string).map_err(|e| { @@ -171,23 +181,20 @@ impl WebEvaluator { }) } else { Err(EvalError::Communication( - "Failed to stringify result".into(), + "Failed to stringify result - undefined or not valid utf16".to_string(), )) } - } else { - Err(EvalError::Communication( - "Failed to stringify result".into(), - )) - } + }) + as Pin>>> } - Err(err) => Err(EvalError::InvalidJs( + Err(err) => Box::pin(futures_util::future::ready(Err(EvalError::InvalidJs( err.as_string().unwrap_or("unknown".to_string()), - )), + )))), }; owner.insert(Box::new(Self { channels: weak_channels, - result: Some(result), + result, next_future: None, }) as Box) } @@ -197,13 +204,9 @@ impl Evaluator for WebEvaluator { /// Runs the evaluated JavaScript. fn poll_join( &mut self, - _cx: &mut std::task::Context<'_>, + cx: &mut std::task::Context<'_>, ) -> std::task::Poll> { - if let Some(result) = self.result.take() { - std::task::Poll::Ready(result) - } else { - std::task::Poll::Ready(Err(EvalError::Finished)) - } + self.result.poll_unpin(cx) } /// Sends a message to the evaluated JavaScript.