fix: add missing attribute-escaping logic in `leptos_meta` and `class` attributes in SSR (closes #1238) (#1241)

This commit is contained in:
Greg Johnston 2023-06-27 11:53:23 -04:00 committed by GitHub
parent ee7dbafc85
commit 13f7cb9a9a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 72 additions and 33 deletions

View File

@ -581,7 +581,7 @@ fn attribute_to_tokens_ssr<'a>(
|| name.strip_prefix("style:").is_some()
{
// ignore props for SSR
// ignore classes and sdtyles: we'll handle these separately
// ignore classes and styles: we'll handle these separately
} else if name == "inner_html" {
return attr.value();
} else {
@ -606,7 +606,9 @@ fn attribute_to_tokens_ssr<'a>(
if let Some(value) = value_to_string(value) {
template.push_str(&name);
template.push_str("=\"");
template.push_str(&value);
template.push_str(&html_escape::encode_quoted_attribute(
&value,
));
template.push('"');
} else {
template.push_str("{}");
@ -729,7 +731,9 @@ fn set_class_attribute_ssr(
{
template.push_str(" class=\"");
template.push_str(&static_class_attr);
template.push_str(&html_escape::encode_quoted_attribute(
&static_class_attr,
));
for (_span, value) in dyn_class_attr {
if let Some(value) = value {

View File

@ -1,26 +1,37 @@
use cfg_if::cfg_if;
use leptos::*;
#[cfg(feature = "ssr")]
use std::{cell::RefCell, rc::Rc};
/// Contains the current metadata for the document's `<body>`.
#[derive(Clone, Default)]
pub struct BodyContext {
#[cfg(feature = "ssr")]
class: Rc<RefCell<Option<TextProp>>>,
#[cfg(feature = "ssr")]
attributes: Rc<RefCell<Option<MaybeSignal<AdditionalAttributes>>>>,
}
impl BodyContext {
/// Converts the `<body>` metadata into an HTML string.
#[cfg(any(feature = "ssr", doc))]
pub fn as_string(&self) -> Option<String> {
let class = self
.class
.borrow()
.as_ref()
.map(|val| format!("class=\"{}\"", val.get()));
let class = self.class.borrow().as_ref().map(|val| {
format!(
"class=\"{}\"",
leptos::leptos_dom::ssr::escape_attr(&val.get())
)
});
let attributes = self.attributes.borrow().as_ref().map(|val| {
val.with(|val| {
val.into_iter()
.map(|(n, v)| format!("{}=\"{}\"", n, v.get()))
.map(|(n, v)| {
format!(
"{}=\"{}\"",
n,
leptos::leptos_dom::ssr::escape_attr(&v.get())
)
})
.collect::<Vec<_>>()
.join(" ")
})
@ -81,9 +92,6 @@ pub fn Body(
#[prop(optional, into)]
attributes: Option<MaybeSignal<AdditionalAttributes>>,
) -> impl IntoView {
#[cfg(debug_assertions)]
crate::feature_warning();
cfg_if! {
if #[cfg(any(feature = "csr", feature = "hydrate"))] {
let el = document().body().expect("there to be a <body> element");
@ -110,10 +118,17 @@ pub fn Body(
});
}
}
} else {
} else if #[cfg(feature = "ssr")] {
let meta = crate::use_head(cx);
*meta.body.class.borrow_mut() = class;
*meta.body.attributes.borrow_mut() = attributes;
} else {
_ = cx;
_ = class;
_ = attributes;
#[cfg(debug_assertions)]
crate::feature_warning();
}
}
}

View File

@ -1,38 +1,53 @@
use cfg_if::cfg_if;
use leptos::*;
#[cfg(feature = "ssr")]
use std::{cell::RefCell, rc::Rc};
/// Contains the current metadata for the document's `<html>`.
#[derive(Clone, Default)]
pub struct HtmlContext {
#[cfg(feature = "ssr")]
lang: Rc<RefCell<Option<TextProp>>>,
#[cfg(feature = "ssr")]
dir: Rc<RefCell<Option<TextProp>>>,
#[cfg(feature = "ssr")]
class: Rc<RefCell<Option<TextProp>>>,
#[cfg(feature = "ssr")]
attributes: Rc<RefCell<Option<MaybeSignal<AdditionalAttributes>>>>,
}
impl HtmlContext {
/// Converts the `<html>` metadata into an HTML string.
#[cfg(any(feature = "ssr", doc))]
pub fn as_string(&self) -> Option<String> {
let lang = self
.lang
.borrow()
.as_ref()
.map(|val| format!("lang=\"{}\"", val.get()));
let dir = self
.dir
.borrow()
.as_ref()
.map(|val| format!("dir=\"{}\"", val.get()));
let class = self
.class
.borrow()
.as_ref()
.map(|val| format!("class=\"{}\"", val.get()));
let lang = self.lang.borrow().as_ref().map(|val| {
format!(
"lang=\"{}\"",
leptos::leptos_dom::ssr::escape_attr(&val.get())
)
});
let dir = self.dir.borrow().as_ref().map(|val| {
format!(
"dir=\"{}\"",
leptos::leptos_dom::ssr::escape_attr(&val.get())
)
});
let class = self.class.borrow().as_ref().map(|val| {
format!(
"class=\"{}\"",
leptos::leptos_dom::ssr::escape_attr(&val.get())
)
});
let attributes = self.attributes.borrow().as_ref().map(|val| {
val.with(|val| {
val.into_iter()
.map(|(n, v)| format!("{}=\"{}\"", n, v.get()))
.map(|(n, v)| {
format!(
"{}=\"{}\"",
n,
leptos::leptos_dom::ssr::escape_attr(&v.get())
)
})
.collect::<Vec<_>>()
.join(" ")
})
@ -96,9 +111,6 @@ pub fn Html(
#[prop(optional, into)]
attributes: Option<MaybeSignal<AdditionalAttributes>>,
) -> impl IntoView {
#[cfg(debug_assertions)]
crate::feature_warning();
cfg_if! {
if #[cfg(any(feature = "csr", feature = "hydrate"))] {
let el = document().document_element().expect("there to be a <html> element");
@ -139,12 +151,20 @@ pub fn Html(
});
}
}
} else {
} else if #[cfg(feature = "ssr")] {
let meta = crate::use_head(cx);
*meta.html.lang.borrow_mut() = lang;
*meta.html.dir.borrow_mut() = dir;
*meta.html.class.borrow_mut() = class;
*meta.html.attributes.borrow_mut() = attributes;
} else {
_ = cx;
_ = lang;
_ = dir;
_ = class;
_ = attributes;
#[cfg(debug_assertions)]
crate::feature_warning();
}
}
}