From 52a46bb77352bd911a478d870acb7453234c52f3 Mon Sep 17 00:00:00 2001 From: Mike Samuel Date: Sun, 18 Sep 2011 12:04:40 -0700 Subject: [PATCH] exp/template/html: normalize '<' in text and RCDATA nodes. The template <{{.}} would violate the structure preservation property if allowed and not normalized, because when {{.}} emitted "", the "<" would be part of a text node, but if {{.}} emitted "a", the "<" would not be part of a text node. This change rewrites '<' in text nodes and RCDATA text nodes to '<' allowing template authors to write the common, and arguably more readable: Your price: {{.P1}} < list price {{.P2}} while preserving the structure preservation property. It also lays the groundwork for comment elision, rewriting Foo Bar to Foo Bar R=nigeltao CC=golang-dev https://golang.org/cl/5043043 --- src/pkg/exp/template/html/escape.go | 94 ++++++++++++++++-------- src/pkg/exp/template/html/escape_test.go | 19 ++++- 2 files changed, 81 insertions(+), 32 deletions(-) diff --git a/src/pkg/exp/template/html/escape.go b/src/pkg/exp/template/html/escape.go index c6156da1229..f629930df70 100644 --- a/src/pkg/exp/template/html/escape.go +++ b/src/pkg/exp/template/html/escape.go @@ -100,12 +100,12 @@ type escaper struct { derived map[string]*template.Template // called[templateName] is a set of called mangled template names. called map[string]bool - // actionNodeEdits and templateNodeEdits are the accumulated edits to - // apply during commit. Such edits are not applied immediately in case - // a template set executes a given template in different escaping - // contexts. + // xxxNodeEdits are the accumulated edits to apply during commit. + // Such edits are not applied immediately in case a template set + // executes a given template in different escaping contexts. actionNodeEdits map[*parse.ActionNode][]string templateNodeEdits map[*parse.TemplateNode]string + textNodeEdits map[*parse.TextNode][]byte } // newEscaper creates a blank escaper for the given set. @@ -117,6 +117,7 @@ func newEscaper(s *template.Set) *escaper { map[string]bool{}, map[*parse.ActionNode][]string{}, map[*parse.TemplateNode]string{}, + map[*parse.TextNode][]byte{}, } } @@ -141,7 +142,7 @@ func (e *escaper) escape(c context, n parse.Node) context { case *parse.TemplateNode: return e.escapeTemplate(c, n) case *parse.TextNode: - return e.escapeText(c, n.Text) + return e.escapeText(c, n) case *parse.WithNode: return e.escapeBranch(c, &n.BranchNode, "with") } @@ -386,6 +387,9 @@ func (e *escaper) escapeListConditionally(c context, n *parse.ListNode, filter f for k, v := range e1.templateNodeEdits { e.editTemplateNode(k, v) } + for k, v := range e1.textNodeEdits { + e.editTextNode(k, v) + } } return c, ok } @@ -493,38 +497,57 @@ var delimEnds = [...]string{ } // escapeText escapes a text template node. -func (e *escaper) escapeText(c context, s []byte) context { +func (e *escaper) escapeText(c context, n *parse.TextNode) context { + s, written := n.Text, 0 + var b bytes.Buffer for len(s) > 0 { - if c.delim == delimNone { - c, s = transitionFunc[c.state](c, s) - continue - } - - i := bytes.IndexAny(s, delimEnds[c.delim]) - if i == -1 { - // Remain inside the attribute. - // Decode the value so non-HTML rules can easily handle - //