Don't use different escaping rules for attributes

This commit is contained in:
Chris Wong 2015-01-11 10:58:27 +13:00
parent 0b531572cf
commit b1aa300884
3 changed files with 20 additions and 78 deletions
maud/src
maud_macros/src

View file

@ -7,27 +7,9 @@ use std::fmt::Writer as FmtWriter;
pub type FmtResult<T> = Result<T, fmt::Error>; pub type FmtResult<T> = Result<T, fmt::Error>;
/// Utilities for escaping HTML5 markup. /// Escape an HTML value.
/// pub fn escape(s: &str) -> String {
/// These follow the *HTML fragment serialization algorithm*, as render(|w| rt::escape(w, |w| w.write_str(s)))
/// specified by the [HTML 5.1 Working Draft][1].
///
/// [1]: http://www.w3.org/TR/html51/syntax.html#escapingString
pub mod escape {
use std::fmt::Writer as FmtWriter;
use super::render;
use super::rt;
/// Escape a double-quoted attribute value, as per HTML5 rules.
pub fn attribute(s: &str) -> String {
render(|w| rt::escape_attribute(w, |w| w.write_str(s)))
}
/// Escape non-attribute text content, as per HTML5 rules.
pub fn non_attribute(s: &str) -> String {
render(|w| rt::escape_non_attribute(w, |w| w.write_str(s)))
}
} }
/// Internal functions used by the `maud_macros` package. You should /// Internal functions used by the `maud_macros` package. You should
@ -38,36 +20,19 @@ pub mod rt {
use std::fmt::Writer as FmtWriter; use std::fmt::Writer as FmtWriter;
use super::FmtResult; use super::FmtResult;
struct AttrEscaper<'a, 'b: 'a> { struct Escaper<'a, 'b: 'a> {
inner: &'a mut (FmtWriter + 'b), inner: &'a mut (FmtWriter + 'b),
} }
impl<'a, 'b> FmtWriter for AttrEscaper<'a, 'b> { impl<'a, 'b> FmtWriter for Escaper<'a, 'b> {
fn write_str(&mut self, s: &str) -> FmtResult<()> { fn write_str(&mut self, s: &str) -> FmtResult<()> {
for c in s.chars() { for c in s.chars() {
try!(match c { try!(match c {
'&' => self.inner.write_str("&amp;"), '&' => self.inner.write_str("&amp;"),
'\u{A0}' => self.inner.write_str("&nbsp;"),
'"' => self.inner.write_str("&quot;"),
_ => write!(self.inner, "{}", c),
});
}
Ok(())
}
}
struct NonAttrEscaper<'a, 'b: 'a> {
inner: &'a mut (FmtWriter + 'b),
}
impl<'a, 'b> FmtWriter for NonAttrEscaper<'a, 'b> {
fn write_str(&mut self, s: &str) -> FmtResult<()> {
for c in s.chars() {
try!(match c {
'&' => self.inner.write_str("&amp;"),
'\u{A0}' => self.inner.write_str("&nbsp;"),
'<' => self.inner.write_str("&lt;"), '<' => self.inner.write_str("&lt;"),
'>' => self.inner.write_str("&gt;"), '>' => self.inner.write_str("&gt;"),
'"' => self.inner.write_str("&quot;"),
'\'' => self.inner.write_str("&#39;"),
_ => write!(self.inner, "{}", c), _ => write!(self.inner, "{}", c),
}); });
} }
@ -76,17 +41,10 @@ pub mod rt {
} }
#[inline] #[inline]
pub fn escape_attribute<F>(w: &mut FmtWriter, f: F) -> FmtResult<()> where pub fn escape<F>(w: &mut FmtWriter, f: F) -> FmtResult<()> where
F: FnOnce(&mut FmtWriter) -> FmtResult<()> F: FnOnce(&mut FmtWriter) -> FmtResult<()>
{ {
f(&mut AttrEscaper { inner: w }) f(&mut Escaper { inner: w })
}
#[inline]
pub fn escape_non_attribute<F>(w: &mut FmtWriter, f: F) -> FmtResult<()> where
F: FnOnce(&mut FmtWriter) -> FmtResult<()>
{
f(&mut NonAttrEscaper { inner: w })
} }
} }

View file

@ -9,9 +9,8 @@ use super::render::Renderer;
#[derive(Copy, PartialEq, Show)] #[derive(Copy, PartialEq, Show)]
pub enum Escape { pub enum Escape {
None, PassThru,
Attr, Escape,
Body,
} }
macro_rules! guard { macro_rules! guard {
@ -85,14 +84,6 @@ impl<'cx: 'r, 's: 'cx, 'i, 'r, 'o: 'r> Parser<'cx, 's, 'i, 'r, 'o> {
self.input = self.input.slice_from(n); self.input = self.input.slice_from(n);
} }
fn choose_escape(&self) -> Escape {
if self.in_attr {
Escape::Attr
} else {
Escape::Body
}
}
/// Construct a Rust AST parser from the given token tree. /// Construct a Rust AST parser from the given token tree.
fn new_rust_parser(&self, tts: Vec<TokenTree>) -> RustParser<'s> { fn new_rust_parser(&self, tts: Vec<TokenTree>) -> RustParser<'s> {
parse::tts_to_parser(self.render.cx.parse_sess, tts, self.render.cx.cfg.clone()) parse::tts_to_parser(self.render.cx.parse_sess, tts, self.render.cx.cfg.clone())
@ -135,10 +126,7 @@ impl<'cx: 'r, 's: 'cx, 'i, 'r, 'o: 'r> Parser<'cx, 's, 'i, 'r, 'o> {
}; };
let lit = self.new_rust_parser(vec![tt.clone()]).parse_lit(); let lit = self.new_rust_parser(vec![tt.clone()]).parse_lit();
match lit_to_string(self.render.cx, lit, minus) { match lit_to_string(self.render.cx, lit, minus) {
Some(s) => { Some(s) => self.render.string(s.as_slice(), Escape::Escape),
let escape = self.choose_escape();
self.render.string(s.as_slice(), escape);
},
None => return false, None => return false,
} }
true true
@ -148,11 +136,11 @@ impl<'cx: 'r, 's: 'cx, 'i, 'r, 'o: 'r> Parser<'cx, 's, 'i, 'r, 'o> {
let (escape, sp) = match self.input { let (escape, sp) = match self.input {
[ref tt @ dollar!(), dollar!(), ..] => { [ref tt @ dollar!(), dollar!(), ..] => {
self.shift(2); self.shift(2);
(Escape::None, tt.get_span()) (Escape::PassThru, tt.get_span())
}, },
[ref tt @ dollar!(), ..] => { [ref tt @ dollar!(), ..] => {
self.shift(1); self.shift(1);
(self.choose_escape(), tt.get_span()) (Escape::Escape, tt.get_span())
}, },
_ => return false, _ => return false,
}; };

View file

@ -4,8 +4,8 @@ use syntax::ext::base::ExtCtxt;
use syntax::parse::token; use syntax::parse::token;
use syntax::ptr::P; use syntax::ptr::P;
use maud;
use super::parse::Escape; use super::parse::Escape;
use maud::escape;
pub struct Renderer<'cx, 's: 'cx, 'o> { pub struct Renderer<'cx, 's: 'cx, 'o> {
pub cx: &'cx mut ExtCtxt<'s>, pub cx: &'cx mut ExtCtxt<'s>,
@ -43,9 +43,8 @@ impl<'cx, 's: 'cx, 'o> Renderer<'cx, 's, 'o> {
/// Append a literal string, with the specified escaping method. /// Append a literal string, with the specified escaping method.
pub fn string(&mut self, s: &str, escape: Escape) { pub fn string(&mut self, s: &str, escape: Escape) {
let s = match escape { let s = match escape {
Escape::None => s.into_cow(), Escape::PassThru => s.into_cow(),
Escape::Attr => escape::attribute(s).into_cow(), Escape::Escape => maud::escape(s).into_cow(),
Escape::Body => escape::non_attribute(s).into_cow(),
}; };
self.write(s.as_slice()); self.write(s.as_slice());
} }
@ -54,13 +53,10 @@ impl<'cx, 's: 'cx, 'o> Renderer<'cx, 's, 'o> {
pub fn splice(&mut self, expr: P<Expr>, escape: Escape) { pub fn splice(&mut self, expr: P<Expr>, escape: Escape) {
let w = self.w; let w = self.w;
self.stmts.push(match escape { self.stmts.push(match escape {
Escape::None => quote_stmt!(self.cx, try!(write!($w, "{}", $expr))), Escape::PassThru => quote_stmt!(self.cx, try!(write!($w, "{}", $expr))),
Escape::Attr => Escape::Escape =>
quote_stmt!(self.cx, quote_stmt!(self.cx,
try!(::maud::rt::escape_attribute($w, |w| write!(w, "{}", $expr)))), try!(::maud::rt::escape($w, |w| write!(w, "{}", $expr)))),
Escape::Body =>
quote_stmt!(self.cx,
try!(::maud::rt::escape_non_attribute($w, |w| write!(w, "{}", $expr)))),
}); });
} }