From 08fa2382b023c13d83624f21c85d6b0a42eb10ae Mon Sep 17 00:00:00 2001 From: Ilya Maximov Date: Thu, 17 Mar 2022 22:55:26 +0100 Subject: [PATCH 1/9] add eval to `DesktopContext` --- packages/desktop/src/desktop_context.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/packages/desktop/src/desktop_context.rs b/packages/desktop/src/desktop_context.rs index 93162842b..def9a21d0 100644 --- a/packages/desktop/src/desktop_context.rs +++ b/packages/desktop/src/desktop_context.rs @@ -119,6 +119,11 @@ impl DesktopContext { pub fn devtool(&self) { let _ = self.proxy.send_event(DevTool); } + + /// run (evaluate) a script in the WebView context + pub fn eval(&self, script: impl std::fmt::Display) { + let _ = self.proxy.send_event(Eval(script.to_string())); + } } #[derive(Debug)] @@ -144,6 +149,8 @@ pub enum UserWindowEvent { SetDecorations(bool), DevTool, + + Eval(String), } pub(super) fn handler( @@ -185,5 +192,7 @@ pub(super) fn handler( SetDecorations(state) => window.set_decorations(state), DevTool => webview.devtool(), + + Eval(code) => webview.evaluate_script(code.as_str()).expect("failed to eval script"), } } From 5cbf2698fdd9c7d94075d266dba1f2c2d7872e9d Mon Sep 17 00:00:00 2001 From: Ilya Maximov Date: Thu, 17 Mar 2022 23:47:11 +0100 Subject: [PATCH 2/9] add `use_eval()`to desktop and web --- packages/desktop/src/desktop_context.rs | 17 ++++++++++++++++- packages/desktop/src/lib.rs | 2 +- packages/web/src/lib.rs | 2 ++ packages/web/src/util.rs | 23 +++++++++++++++++++++++ src/lib.rs | 6 ++++++ 5 files changed, 48 insertions(+), 2 deletions(-) diff --git a/packages/desktop/src/desktop_context.rs b/packages/desktop/src/desktop_context.rs index def9a21d0..b15e26ea3 100644 --- a/packages/desktop/src/desktop_context.rs +++ b/packages/desktop/src/desktop_context.rs @@ -193,6 +193,21 @@ pub(super) fn handler( DevTool => webview.devtool(), - Eval(code) => webview.evaluate_script(code.as_str()).expect("failed to eval script"), + Eval(code) => webview + .evaluate_script(code.as_str()) + .expect("failed to eval script"), } } + +/// Get a closure that executes any JavaScript in the WebView context. +/// +/// # Panics +/// +/// The closure will cause the message processing thread to panic if the +/// provided script is not valid JavaScript code or if it returns an uncaught +/// error. +pub fn use_eval(cx: &ScopeState) -> impl Fn(S) + '_ { + let desktop = use_window(&cx); + + move |script| desktop.eval(script) +} diff --git a/packages/desktop/src/lib.rs b/packages/desktop/src/lib.rs index 4b2615d54..5f41b98ce 100644 --- a/packages/desktop/src/lib.rs +++ b/packages/desktop/src/lib.rs @@ -11,7 +11,7 @@ mod events; mod protocol; use desktop_context::UserWindowEvent; -pub use desktop_context::{use_window, DesktopContext}; +pub use desktop_context::{use_window, use_eval, DesktopContext}; pub use wry; pub use wry::application as tao; diff --git a/packages/web/src/lib.rs b/packages/web/src/lib.rs index c82dc4975..1ec9fe068 100644 --- a/packages/web/src/lib.rs +++ b/packages/web/src/lib.rs @@ -62,12 +62,14 @@ use dioxus::VirtualDom; pub use dioxus_core as dioxus; use dioxus_core::prelude::Component; use futures_util::FutureExt; +pub use crate::util::use_eval; mod cache; mod cfg; mod dom; mod rehydrate; mod ric_raf; +mod util; /// Launch the VirtualDOM given a root component and a configuration. /// diff --git a/packages/web/src/util.rs b/packages/web/src/util.rs index 638cf3dc2..696af7b21 100644 --- a/packages/web/src/util.rs +++ b/packages/web/src/util.rs @@ -1 +1,24 @@ //! Utilities specific to websys + +use dioxus_core::*; + +/// Get a closure that executes any JavaScript in the webpage. +/// +/// # Safety +/// +/// Please be very careful with this function. A script with too many dynamic +/// parts is practically asking for a hacker to find an XSS vulnerability in +/// it. **This applies especially to web targets, where the JavaScript context +/// has access to most, if not all of your application data.** +/// +/// # Panics +/// +/// The closure will panic if the provided script is not valid JavaScript code +/// or if it returns an uncaught error. +pub fn use_eval(_cx: &ScopeState) -> impl Fn(S) { + |script| { + js_sys::Function::new_no_args(&script.to_string()) + .call0(&wasm_bindgen::JsValue::NULL) + .expect("failed to eval script"); + } +} diff --git a/src/lib.rs b/src/lib.rs index cebffcf17..2ba5a9045 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -44,3 +44,9 @@ pub mod prelude { #[cfg(feature = "fermi")] pub use fermi::{use_atom_ref, use_init_atom_root, use_read, use_set, Atom, AtomRef}; } + +#[cfg(all(target = "wasm", feature = "web"))] +pub use dioxus_web::use_eval; + +#[cfg(all(not(target = "wasm"), feature = "desktop"))] +pub use dioxus_desktop::use_eval; \ No newline at end of file From b4a5fe9845a3a184081acabca94f0cdfcc2ce668 Mon Sep 17 00:00:00 2001 From: Ilya Maximov Date: Sat, 19 Mar 2022 02:57:39 +0100 Subject: [PATCH 3/9] use `ToString` --- packages/desktop/src/desktop_context.rs | 4 ++-- packages/web/src/util.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/desktop/src/desktop_context.rs b/packages/desktop/src/desktop_context.rs index b15e26ea3..5a2d50f31 100644 --- a/packages/desktop/src/desktop_context.rs +++ b/packages/desktop/src/desktop_context.rs @@ -121,7 +121,7 @@ impl DesktopContext { } /// run (evaluate) a script in the WebView context - pub fn eval(&self, script: impl std::fmt::Display) { + pub fn eval(&self, script: impl std::string::ToString) { let _ = self.proxy.send_event(Eval(script.to_string())); } } @@ -206,7 +206,7 @@ pub(super) fn handler( /// The closure will cause the message processing thread to panic if the /// provided script is not valid JavaScript code or if it returns an uncaught /// error. -pub fn use_eval(cx: &ScopeState) -> impl Fn(S) + '_ { +pub fn use_eval(cx: &ScopeState) -> impl Fn(S) + '_ { let desktop = use_window(&cx); move |script| desktop.eval(script) diff --git a/packages/web/src/util.rs b/packages/web/src/util.rs index 696af7b21..3e8f92806 100644 --- a/packages/web/src/util.rs +++ b/packages/web/src/util.rs @@ -15,7 +15,7 @@ use dioxus_core::*; /// /// The closure will panic if the provided script is not valid JavaScript code /// or if it returns an uncaught error. -pub fn use_eval(_cx: &ScopeState) -> impl Fn(S) { +pub fn use_eval(_cx: &ScopeState) -> impl Fn(S) { |script| { js_sys::Function::new_no_args(&script.to_string()) .call0(&wasm_bindgen::JsValue::NULL) From 40869b36ad2ba90ae0794b18e2bffc99103e3486 Mon Sep 17 00:00:00 2001 From: Ilya Maximov Date: Sat, 19 Mar 2022 03:08:08 +0100 Subject: [PATCH 4/9] stuff `use_eval` into `dioxus::hooks` --- src/lib.rs | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 2ba5a9045..2e50d9750 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,8 +2,16 @@ pub use dioxus_core as core; -#[cfg(feature = "hooks")] -pub use dioxus_hooks as hooks; +pub mod hooks { + #[cfg(feature = "hooks")] + pub use dioxus_hooks::*; + + #[cfg(all(target = "wasm", feature = "web"))] + pub use dioxus_web::use_eval; + + #[cfg(all(not(target = "wasm"), feature = "desktop"))] + pub use dioxus_desktop::use_eval; +} #[cfg(feature = "router")] pub use dioxus_router as router; @@ -35,7 +43,7 @@ pub mod prelude { pub use dioxus_core::prelude::*; pub use dioxus_core_macro::{format_args_f, inline_props, rsx, Props}; pub use dioxus_elements::{GlobalAttributes, SvgAttributes}; - pub use dioxus_hooks::*; + pub use crate::hooks::*; pub use dioxus_html as dioxus_elements; #[cfg(feature = "router")] @@ -44,9 +52,3 @@ pub mod prelude { #[cfg(feature = "fermi")] pub use fermi::{use_atom_ref, use_init_atom_root, use_read, use_set, Atom, AtomRef}; } - -#[cfg(all(target = "wasm", feature = "web"))] -pub use dioxus_web::use_eval; - -#[cfg(all(not(target = "wasm"), feature = "desktop"))] -pub use dioxus_desktop::use_eval; \ No newline at end of file From 5e475fadd900fc336cf7a891d08e414bf9f914d3 Mon Sep 17 00:00:00 2001 From: Ilya Maximov Date: Sat, 19 Mar 2022 03:11:30 +0100 Subject: [PATCH 5/9] add a desktop eval example --- examples/eval.rs | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 examples/eval.rs diff --git a/examples/eval.rs b/examples/eval.rs new file mode 100644 index 000000000..c0801b507 --- /dev/null +++ b/examples/eval.rs @@ -0,0 +1,25 @@ +use dioxus::prelude::*; + +fn main() { + dioxus::desktop::launch(app); +} + +fn app(cx: Scope) -> Element { + let script = use_state(&cx, String::new); + let eval = use_eval(&cx); + + cx.render(rsx! { + div { + input { + placeholder: "Enter an expression", + value: "{script}", + oninput: move |e| script.set(e.value.clone()), + } + button { + onclick: move |_| eval(script), + + "Execute" + } + } + }) +} From c194fa8372b5ad5f86325623906c5c6bb0986d3b Mon Sep 17 00:00:00 2001 From: Ilya Maximov Date: Sat, 19 Mar 2022 03:13:34 +0100 Subject: [PATCH 6/9] remove panics from desktop docs `WebView::evaluate_script()` always returns `Ok()` --- packages/desktop/src/desktop_context.rs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/packages/desktop/src/desktop_context.rs b/packages/desktop/src/desktop_context.rs index 5a2d50f31..4475fe978 100644 --- a/packages/desktop/src/desktop_context.rs +++ b/packages/desktop/src/desktop_context.rs @@ -195,17 +195,11 @@ pub(super) fn handler( Eval(code) => webview .evaluate_script(code.as_str()) - .expect("failed to eval script"), + .expect("eval shouldn't panic"), } } /// Get a closure that executes any JavaScript in the WebView context. -/// -/// # Panics -/// -/// The closure will cause the message processing thread to panic if the -/// provided script is not valid JavaScript code or if it returns an uncaught -/// error. pub fn use_eval(cx: &ScopeState) -> impl Fn(S) + '_ { let desktop = use_window(&cx); From 6e8fdc1e4d615f9ace527a6dc2e87abe101da826 Mon Sep 17 00:00:00 2001 From: Ilya Maximov Date: Mon, 21 Mar 2022 21:57:29 +0100 Subject: [PATCH 7/9] make the closure dyn --- packages/desktop/src/desktop_context.rs | 6 +++--- packages/web/src/util.rs | 14 ++++++++------ 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/packages/desktop/src/desktop_context.rs b/packages/desktop/src/desktop_context.rs index 4475fe978..4ad2ff3a7 100644 --- a/packages/desktop/src/desktop_context.rs +++ b/packages/desktop/src/desktop_context.rs @@ -200,8 +200,8 @@ pub(super) fn handler( } /// Get a closure that executes any JavaScript in the WebView context. -pub fn use_eval(cx: &ScopeState) -> impl Fn(S) + '_ { - let desktop = use_window(&cx); +pub fn use_eval(cx: &ScopeState) -> &dyn Fn(S) { + let desktop = use_window(&cx).clone(); - move |script| desktop.eval(script) + cx.use_hook(|_| move |script| desktop.eval(script)) } diff --git a/packages/web/src/util.rs b/packages/web/src/util.rs index 3e8f92806..3e7232035 100644 --- a/packages/web/src/util.rs +++ b/packages/web/src/util.rs @@ -15,10 +15,12 @@ use dioxus_core::*; /// /// The closure will panic if the provided script is not valid JavaScript code /// or if it returns an uncaught error. -pub fn use_eval(_cx: &ScopeState) -> impl Fn(S) { - |script| { - js_sys::Function::new_no_args(&script.to_string()) - .call0(&wasm_bindgen::JsValue::NULL) - .expect("failed to eval script"); - } +pub fn use_eval(cx: &ScopeState) -> &dyn Fn(S) { + cx.use_hook(|_| { + |script: S| { + js_sys::Function::new_no_args(&script.to_string()) + .call0(&wasm_bindgen::JsValue::NULL) + .expect("failed to eval script"); + } + }) } From f4b58c9bb6131abeca3d0c97c836ca27ec42d13e Mon Sep 17 00:00:00 2001 From: Ilya Maximov Date: Mon, 21 Mar 2022 21:57:51 +0100 Subject: [PATCH 8/9] add the example to the list --- examples/README.md | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/examples/README.md b/examples/README.md index e108c56cd..4a4ccbf9c 100644 --- a/examples/README.md +++ b/examples/README.md @@ -41,6 +41,7 @@ These examples are not necessarily meant to be run, but rather serve as a refere | [Anti-patterns](./antipatterns.rs) | A collection of discouraged patterns | ✅ | | [Complete rsx reference](./rsx_usage.rs) | A complete reference for all rsx! usage | ✅ | | [Event Listeners](./listener.rs) | Attach closures to events on elements | ✅ | +| [Eval](./eval.rs) | Evaluate dynamic JavaScript code | ✅ | ## Show me some examples! @@ -51,7 +52,7 @@ In our collection of examples, guides, and tutorials, we have: - The reference (a collection of examples with heavy documentation) - The general examples - The platform-specific examples (web, ssr, desktop, mobile, server) - + Here's what a few common tasks look like in Dioxus: Nested components with children and internal state: @@ -80,7 +81,7 @@ Controlled inputs: ```rust fn App(cx: Scope) -> Element { let value = use_state(&cx, String::new); - cx.render(rsx!( + cx.render(rsx!( input { "type": "text", value: "{value}", @@ -96,16 +97,16 @@ fn App(cx: Scope) -> Element { let list = (0..10).map(|i| { rsx!(li { key: "{i}", "Value: {i}" }) }); - + let title = match list.len() { 0 => rsx!("Not enough"), _ => rsx!("Plenty!"), }; if should_show { - cx.render(rsx!( + cx.render(rsx!( title, - ul { list } + ul { list } )) } else { None @@ -165,18 +166,18 @@ fn App(cx: Scope) -> Element { Route::Home => rsx!( Home {} ), Route::Post(id) => rsx!( Post { id: id }) } - })) + })) } ``` -Suspense +Suspense ```rust fn App(cx: Scope) -> Element { let doggo = use_suspense(cx, || async { reqwest::get("https://dog.ceo/api/breeds/image/random").await.unwrap().json::().await.unwrap() }, |response| cx.render(rsx!( img { src: "{response.message}" })) ); - + cx.render(rsx!{ div { "One doggo coming right up:", From d31edfa7d7055c9e36eea3d1002e98c8771e5a0f Mon Sep 17 00:00:00 2001 From: Ilya Maximov Date: Mon, 21 Mar 2022 22:02:04 +0100 Subject: [PATCH 9/9] rustfmt when the --- packages/desktop/src/lib.rs | 2 +- packages/web/src/lib.rs | 2 +- src/lib.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/desktop/src/lib.rs b/packages/desktop/src/lib.rs index 5f41b98ce..596956c53 100644 --- a/packages/desktop/src/lib.rs +++ b/packages/desktop/src/lib.rs @@ -11,7 +11,7 @@ mod events; mod protocol; use desktop_context::UserWindowEvent; -pub use desktop_context::{use_window, use_eval, DesktopContext}; +pub use desktop_context::{use_eval, use_window, DesktopContext}; pub use wry; pub use wry::application as tao; diff --git a/packages/web/src/lib.rs b/packages/web/src/lib.rs index 1ec9fe068..da3955d1b 100644 --- a/packages/web/src/lib.rs +++ b/packages/web/src/lib.rs @@ -57,12 +57,12 @@ use std::rc::Rc; pub use crate::cfg::WebConfig; +pub use crate::util::use_eval; use dioxus::SchedulerMsg; use dioxus::VirtualDom; pub use dioxus_core as dioxus; use dioxus_core::prelude::Component; use futures_util::FutureExt; -pub use crate::util::use_eval; mod cache; mod cfg; diff --git a/src/lib.rs b/src/lib.rs index 2e50d9750..748772aa4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -40,10 +40,10 @@ pub mod events { } pub mod prelude { + pub use crate::hooks::*; pub use dioxus_core::prelude::*; pub use dioxus_core_macro::{format_args_f, inline_props, rsx, Props}; pub use dioxus_elements::{GlobalAttributes, SvgAttributes}; - pub use crate::hooks::*; pub use dioxus_html as dioxus_elements; #[cfg(feature = "router")]