fix dangerous_inner_html with SSR

This commit is contained in:
Evan Almloff 2023-04-07 09:35:32 -05:00
parent 61d100e1f8
commit fe75138e42
3 changed files with 62 additions and 3 deletions

View file

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

View file

@ -70,6 +70,8 @@ impl Renderer {
.or_insert_with(|| Rc::new(StringCache::from_template(template).unwrap())) .or_insert_with(|| Rc::new(StringCache::from_template(template).unwrap()))
.clone(); .clone();
let mut inner_html = None;
// We need to keep track of the dynamic styles so we can insert them into the right place // 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(); let mut accumulated_dynamic_styles = Vec::new();
@ -77,7 +79,9 @@ impl Renderer {
match segment { match segment {
Segment::Attr(idx) => { Segment::Attr(idx) => {
let attr = &template.dynamic_attrs[*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); accumulated_dynamic_styles.push(attr);
} else { } else {
match attr.value { match attr.value {
@ -165,6 +169,19 @@ impl Renderer {
accumulated_dynamic_styles.clear(); 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 { StyleMarker {
inside_style_tag: false, inside_style_tag: false,
}, },
PreRendered(">Hello world 1 --&gt;".into(),), PreRendered(">".into()),
InnerHtmlMarker,
PreRendered("Hello world 1 --&gt;".into(),),
Node(0,), Node(0,),
PreRendered( PreRendered(
"&lt;-- Hello world 2<div>nest 1</div><div></div><div>nest 2</div>".into(), "&lt;-- Hello world 2<div>nest 1</div><div></div><div>nest 2</div>".into(),

View file

@ -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>"#);
}