Merge pull request #942 from Demonthos/dangerous-inner-html-ssr

This commit is contained in:
Jon Kelley 2023-04-07 10:21:21 -07:00 committed by GitHub
commit d4b2ff47db
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
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
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)?;
}

View file

@ -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(),

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