Browse Source

fix dangerous_inner_html with SSR

Evan Almloff 2 years ago
parent
commit
fe75138e42
3 changed files with 62 additions and 3 deletions
  1. 15 1
      packages/ssr/src/cache.rs
  2. 21 2
      packages/ssr/src/renderer.rs
  3. 26 0
      packages/ssr/tests/inner_html.rs

+ 15 - 1
packages/ssr/src/cache.rs

@@ -23,6 +23,8 @@ pub enum Segment {
         // This will be true if there are static styles
         inside_style_tag: bool,
     },
+    /// A marker for where to insert a dynamic inner html
+    InnerHtmlMarker,
 }
 
 impl std::fmt::Write for StringChain {
@@ -69,6 +71,9 @@ impl StringCache {
                 write!(chain, "<{tag}")?;
                 // we need to collect the styles and write them at the end
                 let mut styles = Vec::new();
+                // we need to collect the inner html and write it at the end
+                let mut inner_html = None;
+                // we need to keep track of if we have dynamic attrs to know if we need to insert a style and inner_html marker
                 let mut has_dynamic_attrs = false;
                 for attr in *attrs {
                     match attr {
@@ -77,7 +82,9 @@ impl StringCache {
                             value,
                             namespace,
                         } => {
-                            if let Some("style") = namespace {
+                            if *name == "dangerous_inner_html" {
+                                inner_html = Some(value);
+                            } else if let Some("style") = namespace {
                                 styles.push((name, value));
                             } else {
                                 write!(chain, " {name}=\"{value}\"")?;
@@ -110,6 +117,13 @@ impl StringCache {
                     write!(chain, "/>")?;
                 } else {
                     write!(chain, ">")?;
+                    // Write the static inner html, or insert a marker if dynamic inner html is possible
+                    if let Some(inner_html) = inner_html {
+                        chain.write_str(inner_html)?;
+                    } else if has_dynamic_attrs {
+                        chain.segments.push(Segment::InnerHtmlMarker);
+                    }
+
                     for child in *children {
                         Self::recurse(child, cur_path, root_idx, chain)?;
                     }

+ 21 - 2
packages/ssr/src/renderer.rs

@@ -70,6 +70,8 @@ impl Renderer {
             .or_insert_with(|| Rc::new(StringCache::from_template(template).unwrap()))
             .clone();
 
+        let mut inner_html = None;
+
         // We need to keep track of the dynamic styles so we can insert them into the right place
         let mut accumulated_dynamic_styles = Vec::new();
 
@@ -77,7 +79,9 @@ impl Renderer {
             match segment {
                 Segment::Attr(idx) => {
                     let attr = &template.dynamic_attrs[*idx];
-                    if attr.namespace == Some("style") {
+                    if attr.name == "dangerous_inner_html" {
+                        inner_html = Some(attr);
+                    } else if attr.namespace == Some("style") {
                         accumulated_dynamic_styles.push(attr);
                     } else {
                         match attr.value {
@@ -165,6 +169,19 @@ impl Renderer {
                         accumulated_dynamic_styles.clear();
                     }
                 }
+
+                Segment::InnerHtmlMarker => {
+                    if let Some(inner_html) = inner_html.take() {
+                        let inner_html = &inner_html.value;
+                        match inner_html {
+                            AttributeValue::Text(value) => write!(buf, "{}", value)?,
+                            AttributeValue::Bool(value) => write!(buf, "{}", value)?,
+                            AttributeValue::Float(f) => write!(buf, "{}", f)?,
+                            AttributeValue::Int(i) => write!(buf, "{}", i)?,
+                            _ => {}
+                        }
+                    }
+                }
             }
         }
 
@@ -208,7 +225,9 @@ fn to_string_works() {
                     StyleMarker {
                         inside_style_tag: false,
                     },
-                    PreRendered(">Hello world 1 --&gt;".into(),),
+                    PreRendered(">".into()),
+                    InnerHtmlMarker,
+                    PreRendered("Hello world 1 --&gt;".into(),),
                     Node(0,),
                     PreRendered(
                         "&lt;-- Hello world 2<div>nest 1</div><div></div><div>nest 2</div>".into(),

+ 26 - 0
packages/ssr/tests/inner_html.rs

@@ -0,0 +1,26 @@
+use dioxus::prelude::*;
+
+#[test]
+fn static_inner_html() {
+    fn app(cx: Scope) -> Element {
+        render! { div { dangerous_inner_html: "<div>1234</div>" } }
+    }
+
+    let mut dom = VirtualDom::new(app);
+    _ = dom.rebuild();
+
+    assert_eq!(dioxus_ssr::render(&dom), r#"<div><div>1234</div></div>"#);
+}
+
+#[test]
+fn dynamic_inner_html() {
+    fn app(cx: Scope) -> Element {
+        let inner_html = "<div>1234</div>";
+        render! { div { dangerous_inner_html: "{inner_html}" } }
+    }
+
+    let mut dom = VirtualDom::new(app);
+    _ = dom.rebuild();
+
+    assert_eq!(dioxus_ssr::render(&dom), r#"<div><div>1234</div></div>"#);
+}