diff options
author | Ian Lance Taylor <iant@google.com> | 2016-02-03 21:58:02 +0000 |
---|---|---|
committer | Ian Lance Taylor <ian@gcc.gnu.org> | 2016-02-03 21:58:02 +0000 |
commit | f98dd1a338867a408f7c72d73fbad7fe7fc93e3a (patch) | |
tree | 2f8da9862a9c1fe0df138917f997b03439c02773 /libgo/go/html | |
parent | b081ed4efc144da0c45a6484aebfd10e0eb9fda3 (diff) | |
download | gcc-f98dd1a338867a408f7c72d73fbad7fe7fc93e3a.zip gcc-f98dd1a338867a408f7c72d73fbad7fe7fc93e3a.tar.gz gcc-f98dd1a338867a408f7c72d73fbad7fe7fc93e3a.tar.bz2 |
libgo: Update to go1.6rc1.
Reviewed-on: https://go-review.googlesource.com/19200
From-SVN: r233110
Diffstat (limited to 'libgo/go/html')
-rw-r--r-- | libgo/go/html/escape.go | 60 | ||||
-rw-r--r-- | libgo/go/html/escape_test.go | 20 | ||||
-rw-r--r-- | libgo/go/html/template/clone_test.go | 12 | ||||
-rw-r--r-- | libgo/go/html/template/context.go | 3 | ||||
-rw-r--r-- | libgo/go/html/template/escape_test.go | 124 | ||||
-rw-r--r-- | libgo/go/html/template/example_test.go | 37 | ||||
-rw-r--r-- | libgo/go/html/template/template.go | 21 | ||||
-rw-r--r-- | libgo/go/html/template/template_test.go | 29 | ||||
-rw-r--r-- | libgo/go/html/template/transition.go | 2 |
9 files changed, 211 insertions, 97 deletions
diff --git a/libgo/go/html/escape.go b/libgo/go/html/escape.go index f50a4b9..ab6fd1c 100644 --- a/libgo/go/html/escape.go +++ b/libgo/go/html/escape.go @@ -57,8 +57,9 @@ var replacementTable = [...]rune{ // unescapeEntity reads an entity like "<" from b[src:] and writes the // corresponding "<" to b[dst:], returning the incremented dst and src cursors. // Precondition: b[src] == '&' && dst <= src. -// attribute should be true if parsing an attribute value. -func unescapeEntity(b []byte, dst, src int, attribute bool) (dst1, src1 int) { +func unescapeEntity(b []byte, dst, src int) (dst1, src1 int) { + const attribute = false + // http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html#consume-a-character-reference // i starts at 1 because we already know that s[0] == '&'. @@ -139,14 +140,14 @@ func unescapeEntity(b []byte, dst, src int, attribute bool) (dst1, src1 int) { break } - entityName := string(s[1:i]) - if entityName == "" { + entityName := s[1:i] + if len(entityName) == 0 { // No-op. } else if attribute && entityName[len(entityName)-1] != ';' && len(s) > i && s[i] == '=' { // No-op. - } else if x := entity[entityName]; x != 0 { + } else if x := entity[string(entityName)]; x != 0 { return dst + utf8.EncodeRune(b[dst:], x), src + i - } else if x := entity2[entityName]; x[0] != 0 { + } else if x := entity2[string(entityName)]; x[0] != 0 { dst1 := dst + utf8.EncodeRune(b[dst:], x[0]) return dst1 + utf8.EncodeRune(b[dst1:], x[1]), src + i } else if !attribute { @@ -155,7 +156,7 @@ func unescapeEntity(b []byte, dst, src int, attribute bool) (dst1, src1 int) { maxLen = longestEntityWithoutSemicolon } for j := maxLen; j > 1; j-- { - if x := entity[entityName[:j]]; x != 0 { + if x := entity[string(entityName[:j])]; x != 0 { return dst + utf8.EncodeRune(b[dst:], x), src + j + 1 } } @@ -166,26 +167,6 @@ func unescapeEntity(b []byte, dst, src int, attribute bool) (dst1, src1 int) { return dst1, src1 } -// unescape unescapes b's entities in-place, so that "a<b" becomes "a<b". -func unescape(b []byte) []byte { - for i, c := range b { - if c == '&' { - dst, src := unescapeEntity(b, i, i, false) - for src < len(b) { - c := b[src] - if c == '&' { - dst, src = unescapeEntity(b, dst, src, false) - } else { - b[dst] = c - dst, src = dst+1, src+1 - } - } - return b[0:dst] - } - } - return b -} - var htmlEscaper = strings.NewReplacer( `&`, "&", `'`, "'", // "'" is shorter than "'" and apos was not in HTML until HTML5. @@ -208,8 +189,29 @@ func EscapeString(s string) string { // UnescapeString(EscapeString(s)) == s always holds, but the converse isn't // always true. func UnescapeString(s string) string { - if !strings.Contains(s, "&") { + i := strings.IndexByte(s, '&') + + if i < 0 { return s } - return string(unescape([]byte(s))) + + b := []byte(s) + dst, src := unescapeEntity(b, i, i) + for len(s[src:]) > 0 { + if s[src] == '&' { + i = 0 + } else { + i = strings.IndexByte(s[src:], '&') + } + if i < 0 { + dst += copy(b[dst:], s[src:]) + break + } + + if i > 0 { + copy(b[dst:], s[src:src+i]) + } + dst, src = unescapeEntity(b, dst+i, src+i) + } + return string(b[:dst]) } diff --git a/libgo/go/html/escape_test.go b/libgo/go/html/escape_test.go index 3702626..8b51a55 100644 --- a/libgo/go/html/escape_test.go +++ b/libgo/go/html/escape_test.go @@ -118,8 +118,10 @@ func TestUnescapeEscape(t *testing.T) { } var ( - benchEscapeData = strings.Repeat("AAAAA < BBBBB > CCCCC & DDDDD ' EEEEE \" ", 100) - benchEscapeNone = strings.Repeat("AAAAA x BBBBB x CCCCC x DDDDD x EEEEE x ", 100) + benchEscapeData = strings.Repeat("AAAAA < BBBBB > CCCCC & DDDDD ' EEEEE \" ", 100) + benchEscapeNone = strings.Repeat("AAAAA x BBBBB x CCCCC x DDDDD x EEEEE x ", 100) + benchUnescapeSparse = strings.Repeat(strings.Repeat("AAAAA x BBBBB x CCCCC x DDDDD x EEEEE x ", 10)+"&", 10) + benchUnescapeDense = strings.Repeat("&< & <", 100) ) func BenchmarkEscape(b *testing.B) { @@ -151,3 +153,17 @@ func BenchmarkUnescapeNone(b *testing.B) { n += len(UnescapeString(s)) } } + +func BenchmarkUnescapeSparse(b *testing.B) { + n := 0 + for i := 0; i < b.N; i++ { + n += len(UnescapeString(benchUnescapeSparse)) + } +} + +func BenchmarkUnescapeDense(b *testing.B) { + n := 0 + for i := 0; i < b.N; i++ { + n += len(UnescapeString(benchUnescapeDense)) + } +} diff --git a/libgo/go/html/template/clone_test.go b/libgo/go/html/template/clone_test.go index c89d22a6..d7c62fa3 100644 --- a/libgo/go/html/template/clone_test.go +++ b/libgo/go/html/template/clone_test.go @@ -78,9 +78,17 @@ func TestClone(t *testing.T) { Must(t0.Parse(`{{define "lhs"}} ( {{end}}`)) Must(t0.Parse(`{{define "rhs"}} ) {{end}}`)) - // Clone t0 as t4. Redefining the "lhs" template should fail. + // Clone t0 as t4. Redefining the "lhs" template should not fail. t4 := Must(t0.Clone()) - if _, err := t4.Parse(`{{define "lhs"}} FAIL {{end}}`); err == nil { + if _, err := t4.Parse(`{{define "lhs"}} OK {{end}}`); err != nil { + t.Errorf(`redefine "lhs": got err %v want nil`, err) + } + // Cloning t1 should fail as it has been executed. + if _, err := t1.Clone(); err == nil { + t.Error("cloning t1: got nil err want non-nil") + } + // Redefining the "lhs" template in t1 should fail as it has been executed. + if _, err := t1.Parse(`{{define "lhs"}} OK {{end}}`); err == nil { t.Error(`redefine "lhs": got nil err want non-nil`) } diff --git a/libgo/go/html/template/context.go b/libgo/go/html/template/context.go index 59e794d..c90fc1f 100644 --- a/libgo/go/html/template/context.go +++ b/libgo/go/html/template/context.go @@ -310,7 +310,8 @@ func (e element) String() string { return fmt.Sprintf("illegal element %d", int(e)) } -// attr identifies the most recent HTML attribute when inside a start tag. +// attr identifies the current HTML attribute when inside the attribute, +// that is, starting from stateAttrName until stateTag/stateText (exclusive). type attr uint8 const ( diff --git a/libgo/go/html/template/escape_test.go b/libgo/go/html/template/escape_test.go index bea2d13..707394e 100644 --- a/libgo/go/html/template/escape_test.go +++ b/libgo/go/html/template/escape_test.go @@ -1054,7 +1054,7 @@ func TestEscapeText(t *testing.T) { }, { `<a href=x`, - context{state: stateURL, delim: delimSpaceOrTagEnd, urlPart: urlPartPreQuery}, + context{state: stateURL, delim: delimSpaceOrTagEnd, urlPart: urlPartPreQuery, attr: attrURL}, }, { `<a href=x `, @@ -1070,7 +1070,7 @@ func TestEscapeText(t *testing.T) { }, { `<a href ='`, - context{state: stateURL, delim: delimSingleQuote}, + context{state: stateURL, delim: delimSingleQuote, attr: attrURL}, }, { `<a href=''`, @@ -1078,7 +1078,7 @@ func TestEscapeText(t *testing.T) { }, { `<a href= "`, - context{state: stateURL, delim: delimDoubleQuote}, + context{state: stateURL, delim: delimDoubleQuote, attr: attrURL}, }, { `<a href=""`, @@ -1090,35 +1090,35 @@ func TestEscapeText(t *testing.T) { }, { `<a HREF='http:`, - context{state: stateURL, delim: delimSingleQuote, urlPart: urlPartPreQuery}, + context{state: stateURL, delim: delimSingleQuote, urlPart: urlPartPreQuery, attr: attrURL}, }, { `<a Href='/`, - context{state: stateURL, delim: delimSingleQuote, urlPart: urlPartPreQuery}, + context{state: stateURL, delim: delimSingleQuote, urlPart: urlPartPreQuery, attr: attrURL}, }, { `<a href='"`, - context{state: stateURL, delim: delimSingleQuote, urlPart: urlPartPreQuery}, + context{state: stateURL, delim: delimSingleQuote, urlPart: urlPartPreQuery, attr: attrURL}, }, { `<a href="'`, - context{state: stateURL, delim: delimDoubleQuote, urlPart: urlPartPreQuery}, + context{state: stateURL, delim: delimDoubleQuote, urlPart: urlPartPreQuery, attr: attrURL}, }, { `<a href=''`, - context{state: stateURL, delim: delimSingleQuote, urlPart: urlPartPreQuery}, + context{state: stateURL, delim: delimSingleQuote, urlPart: urlPartPreQuery, attr: attrURL}, }, { `<a href=""`, - context{state: stateURL, delim: delimDoubleQuote, urlPart: urlPartPreQuery}, + context{state: stateURL, delim: delimDoubleQuote, urlPart: urlPartPreQuery, attr: attrURL}, }, { `<a href=""`, - context{state: stateURL, delim: delimDoubleQuote, urlPart: urlPartPreQuery}, + context{state: stateURL, delim: delimDoubleQuote, urlPart: urlPartPreQuery, attr: attrURL}, }, { `<a href="`, - context{state: stateURL, delim: delimSpaceOrTagEnd, urlPart: urlPartPreQuery}, + context{state: stateURL, delim: delimSpaceOrTagEnd, urlPart: urlPartPreQuery, attr: attrURL}, }, { `<img alt="1">`, @@ -1138,83 +1138,83 @@ func TestEscapeText(t *testing.T) { }, { `<a onclick="`, - context{state: stateJS, delim: delimDoubleQuote}, + context{state: stateJS, delim: delimDoubleQuote, attr: attrScript}, }, { `<a onclick="//foo`, - context{state: stateJSLineCmt, delim: delimDoubleQuote}, + context{state: stateJSLineCmt, delim: delimDoubleQuote, attr: attrScript}, }, { "<a onclick='//\n", - context{state: stateJS, delim: delimSingleQuote}, + context{state: stateJS, delim: delimSingleQuote, attr: attrScript}, }, { "<a onclick='//\r\n", - context{state: stateJS, delim: delimSingleQuote}, + context{state: stateJS, delim: delimSingleQuote, attr: attrScript}, }, { "<a onclick='//\u2028", - context{state: stateJS, delim: delimSingleQuote}, + context{state: stateJS, delim: delimSingleQuote, attr: attrScript}, }, { `<a onclick="/*`, - context{state: stateJSBlockCmt, delim: delimDoubleQuote}, + context{state: stateJSBlockCmt, delim: delimDoubleQuote, attr: attrScript}, }, { `<a onclick="/*/`, - context{state: stateJSBlockCmt, delim: delimDoubleQuote}, + context{state: stateJSBlockCmt, delim: delimDoubleQuote, attr: attrScript}, }, { `<a onclick="/**/`, - context{state: stateJS, delim: delimDoubleQuote}, + context{state: stateJS, delim: delimDoubleQuote, attr: attrScript}, }, { `<a onkeypress=""`, - context{state: stateJSDqStr, delim: delimDoubleQuote}, + context{state: stateJSDqStr, delim: delimDoubleQuote, attr: attrScript}, }, { `<a onclick='"foo"`, - context{state: stateJS, delim: delimSingleQuote, jsCtx: jsCtxDivOp}, + context{state: stateJS, delim: delimSingleQuote, jsCtx: jsCtxDivOp, attr: attrScript}, }, { `<a onclick='foo'`, - context{state: stateJS, delim: delimSpaceOrTagEnd, jsCtx: jsCtxDivOp}, + context{state: stateJS, delim: delimSpaceOrTagEnd, jsCtx: jsCtxDivOp, attr: attrScript}, }, { `<a onclick='foo`, - context{state: stateJSSqStr, delim: delimSpaceOrTagEnd}, + context{state: stateJSSqStr, delim: delimSpaceOrTagEnd, attr: attrScript}, }, { `<a onclick=""foo'`, - context{state: stateJSDqStr, delim: delimDoubleQuote}, + context{state: stateJSDqStr, delim: delimDoubleQuote, attr: attrScript}, }, { `<a onclick="'foo"`, - context{state: stateJSSqStr, delim: delimDoubleQuote}, + context{state: stateJSSqStr, delim: delimDoubleQuote, attr: attrScript}, }, { `<A ONCLICK="'`, - context{state: stateJSSqStr, delim: delimDoubleQuote}, + context{state: stateJSSqStr, delim: delimDoubleQuote, attr: attrScript}, }, { `<a onclick="/`, - context{state: stateJSRegexp, delim: delimDoubleQuote}, + context{state: stateJSRegexp, delim: delimDoubleQuote, attr: attrScript}, }, { `<a onclick="'foo'`, - context{state: stateJS, delim: delimDoubleQuote, jsCtx: jsCtxDivOp}, + context{state: stateJS, delim: delimDoubleQuote, jsCtx: jsCtxDivOp, attr: attrScript}, }, { `<a onclick="'foo\'`, - context{state: stateJSSqStr, delim: delimDoubleQuote}, + context{state: stateJSSqStr, delim: delimDoubleQuote, attr: attrScript}, }, { `<a onclick="'foo\'`, - context{state: stateJSSqStr, delim: delimDoubleQuote}, + context{state: stateJSSqStr, delim: delimDoubleQuote, attr: attrScript}, }, { `<a onclick="/foo/`, - context{state: stateJS, delim: delimDoubleQuote, jsCtx: jsCtxDivOp}, + context{state: stateJS, delim: delimDoubleQuote, jsCtx: jsCtxDivOp, attr: attrScript}, }, { `<script>/foo/ /=`, @@ -1222,111 +1222,111 @@ func TestEscapeText(t *testing.T) { }, { `<a onclick="1 /foo`, - context{state: stateJS, delim: delimDoubleQuote, jsCtx: jsCtxDivOp}, + context{state: stateJS, delim: delimDoubleQuote, jsCtx: jsCtxDivOp, attr: attrScript}, }, { `<a onclick="1 /*c*/ /foo`, - context{state: stateJS, delim: delimDoubleQuote, jsCtx: jsCtxDivOp}, + context{state: stateJS, delim: delimDoubleQuote, jsCtx: jsCtxDivOp, attr: attrScript}, }, { `<a onclick="/foo[/]`, - context{state: stateJSRegexp, delim: delimDoubleQuote}, + context{state: stateJSRegexp, delim: delimDoubleQuote, attr: attrScript}, }, { `<a onclick="/foo\/`, - context{state: stateJSRegexp, delim: delimDoubleQuote}, + context{state: stateJSRegexp, delim: delimDoubleQuote, attr: attrScript}, }, { `<a onclick="/foo/`, - context{state: stateJS, delim: delimDoubleQuote, jsCtx: jsCtxDivOp}, + context{state: stateJS, delim: delimDoubleQuote, jsCtx: jsCtxDivOp, attr: attrScript}, }, { `<input checked style="`, - context{state: stateCSS, delim: delimDoubleQuote}, + context{state: stateCSS, delim: delimDoubleQuote, attr: attrStyle}, }, { `<a style="//`, - context{state: stateCSSLineCmt, delim: delimDoubleQuote}, + context{state: stateCSSLineCmt, delim: delimDoubleQuote, attr: attrStyle}, }, { `<a style="//</script>`, - context{state: stateCSSLineCmt, delim: delimDoubleQuote}, + context{state: stateCSSLineCmt, delim: delimDoubleQuote, attr: attrStyle}, }, { "<a style='//\n", - context{state: stateCSS, delim: delimSingleQuote}, + context{state: stateCSS, delim: delimSingleQuote, attr: attrStyle}, }, { "<a style='//\r", - context{state: stateCSS, delim: delimSingleQuote}, + context{state: stateCSS, delim: delimSingleQuote, attr: attrStyle}, }, { `<a style="/*`, - context{state: stateCSSBlockCmt, delim: delimDoubleQuote}, + context{state: stateCSSBlockCmt, delim: delimDoubleQuote, attr: attrStyle}, }, { `<a style="/*/`, - context{state: stateCSSBlockCmt, delim: delimDoubleQuote}, + context{state: stateCSSBlockCmt, delim: delimDoubleQuote, attr: attrStyle}, }, { `<a style="/**/`, - context{state: stateCSS, delim: delimDoubleQuote}, + context{state: stateCSS, delim: delimDoubleQuote, attr: attrStyle}, }, { `<a style="background: '`, - context{state: stateCSSSqStr, delim: delimDoubleQuote}, + context{state: stateCSSSqStr, delim: delimDoubleQuote, attr: attrStyle}, }, { `<a style="background: "`, - context{state: stateCSSDqStr, delim: delimDoubleQuote}, + context{state: stateCSSDqStr, delim: delimDoubleQuote, attr: attrStyle}, }, { `<a style="background: '/foo?img=`, - context{state: stateCSSSqStr, delim: delimDoubleQuote, urlPart: urlPartQueryOrFrag}, + context{state: stateCSSSqStr, delim: delimDoubleQuote, urlPart: urlPartQueryOrFrag, attr: attrStyle}, }, { `<a style="background: '/`, - context{state: stateCSSSqStr, delim: delimDoubleQuote, urlPart: urlPartPreQuery}, + context{state: stateCSSSqStr, delim: delimDoubleQuote, urlPart: urlPartPreQuery, attr: attrStyle}, }, { `<a style="background: url("/`, - context{state: stateCSSDqURL, delim: delimDoubleQuote, urlPart: urlPartPreQuery}, + context{state: stateCSSDqURL, delim: delimDoubleQuote, urlPart: urlPartPreQuery, attr: attrStyle}, }, { `<a style="background: url('/`, - context{state: stateCSSSqURL, delim: delimDoubleQuote, urlPart: urlPartPreQuery}, + context{state: stateCSSSqURL, delim: delimDoubleQuote, urlPart: urlPartPreQuery, attr: attrStyle}, }, { `<a style="background: url('/)`, - context{state: stateCSSSqURL, delim: delimDoubleQuote, urlPart: urlPartPreQuery}, + context{state: stateCSSSqURL, delim: delimDoubleQuote, urlPart: urlPartPreQuery, attr: attrStyle}, }, { `<a style="background: url('/ `, - context{state: stateCSSSqURL, delim: delimDoubleQuote, urlPart: urlPartPreQuery}, + context{state: stateCSSSqURL, delim: delimDoubleQuote, urlPart: urlPartPreQuery, attr: attrStyle}, }, { `<a style="background: url(/`, - context{state: stateCSSURL, delim: delimDoubleQuote, urlPart: urlPartPreQuery}, + context{state: stateCSSURL, delim: delimDoubleQuote, urlPart: urlPartPreQuery, attr: attrStyle}, }, { `<a style="background: url( `, - context{state: stateCSSURL, delim: delimDoubleQuote}, + context{state: stateCSSURL, delim: delimDoubleQuote, attr: attrStyle}, }, { `<a style="background: url( /image?name=`, - context{state: stateCSSURL, delim: delimDoubleQuote, urlPart: urlPartQueryOrFrag}, + context{state: stateCSSURL, delim: delimDoubleQuote, urlPart: urlPartQueryOrFrag, attr: attrStyle}, }, { `<a style="background: url(x)`, - context{state: stateCSS, delim: delimDoubleQuote}, + context{state: stateCSS, delim: delimDoubleQuote, attr: attrStyle}, }, { `<a style="background: url('x'`, - context{state: stateCSS, delim: delimDoubleQuote}, + context{state: stateCSS, delim: delimDoubleQuote, attr: attrStyle}, }, { `<a style="background: url( x `, - context{state: stateCSS, delim: delimDoubleQuote}, + context{state: stateCSS, delim: delimDoubleQuote, attr: attrStyle}, }, { `<!-- foo`, @@ -1466,7 +1466,7 @@ func TestEscapeText(t *testing.T) { }, { `<a svg:style='`, - context{state: stateCSS, delim: delimSingleQuote}, + context{state: stateCSS, delim: delimSingleQuote, attr: attrStyle}, }, { `<svg:font-face`, @@ -1474,7 +1474,11 @@ func TestEscapeText(t *testing.T) { }, { `<svg:a svg:onclick="`, - context{state: stateJS, delim: delimDoubleQuote}, + context{state: stateJS, delim: delimDoubleQuote, attr: attrScript}, + }, + { + `<svg:a svg:onclick="x()">`, + context{}, }, } diff --git a/libgo/go/html/template/example_test.go b/libgo/go/html/template/example_test.go index 3ea3dca..de7cdbb 100644 --- a/libgo/go/html/template/example_test.go +++ b/libgo/go/html/template/example_test.go @@ -11,6 +11,7 @@ import ( "html/template" "log" "os" + "strings" ) func Example() { @@ -32,6 +33,7 @@ func Example() { } } t, err := template.New("webpage").Parse(tpl) + check(err) data := struct { Title string @@ -122,3 +124,38 @@ func Example_escape() { // %22Fran+%26+Freddie%27s+Diner%2232%3Ctasty%40example.com%3E } + +// The following example is duplicated in text/template; keep them in sync. + +func ExampleTemplate_block() { + const ( + master = `Names:{{block "list" .}}{{"\n"}}{{range .}}{{println "-" .}}{{end}}{{end}}` + overlay = `{{define "list"}} {{join . ", "}}{{end}} ` + ) + var ( + funcs = template.FuncMap{"join": strings.Join} + guardians = []string{"Gamora", "Groot", "Nebula", "Rocket", "Star-Lord"} + ) + masterTmpl, err := template.New("master").Funcs(funcs).Parse(master) + if err != nil { + log.Fatal(err) + } + overlayTmpl, err := template.Must(masterTmpl.Clone()).Parse(overlay) + if err != nil { + log.Fatal(err) + } + if err := masterTmpl.Execute(os.Stdout, guardians); err != nil { + log.Fatal(err) + } + if err := overlayTmpl.Execute(os.Stdout, guardians); err != nil { + log.Fatal(err) + } + // Output: + // Names: + // - Gamora + // - Groot + // - Nebula + // - Rocket + // - Star-Lord + // Names: Gamora, Groot, Nebula, Rocket, Star-Lord +} diff --git a/libgo/go/html/template/template.go b/libgo/go/html/template/template.go index bb9140a..96ab268 100644 --- a/libgo/go/html/template/template.go +++ b/libgo/go/html/template/template.go @@ -17,7 +17,7 @@ import ( // Template is a specialized Template from "text/template" that produces a safe // HTML document fragment. type Template struct { - // Sticky error if escaping fails. + // Sticky error if escaping fails, or escapeOK if succeeded. escapeErr error // We could embed the text/template field, but it's safer not to because // we need to keep our version of the name space and the underlying @@ -80,7 +80,7 @@ func (t *Template) escape() error { defer t.nameSpace.mu.Unlock() if t.escapeErr == nil { if t.Tree == nil { - return fmt.Errorf("template: %q is an incomplete or empty template%s", t.Name(), t.text.DefinedTemplates()) + return fmt.Errorf("template: %q is an incomplete or empty template%s", t.Name(), t.DefinedTemplates()) } if err := escapeTemplate(t, t.text.Root, t.Name()); err != nil { return err @@ -143,6 +143,13 @@ func (t *Template) lookupAndEscapeTemplate(name string) (tmpl *Template, err err return tmpl, err } +// DefinedTemplates returns a string listing the defined templates, +// prefixed by the string "; defined templates are: ". If there are none, +// it returns the empty string. Used to generate an error message. +func (t *Template) DefinedTemplates() string { + return t.text.DefinedTemplates() +} + // Parse parses a string into a template. Nested template definitions // will be associated with the top-level template t. Parse may be // called multiple times to parse definitions of templates to associate @@ -169,6 +176,8 @@ func (t *Template) Parse(src string) (*Template, error) { tmpl := t.set[name] if tmpl == nil { tmpl = t.new(name) + } else if tmpl.escapeErr != nil { + return nil, fmt.Errorf("html/template: cannot redefine %q after it has executed", name) } // Restore our record of this text/template to its unescaped original state. tmpl.escapeErr = nil @@ -228,6 +237,7 @@ func (t *Template) Clone() (*Template, error) { set: make(map[string]*Template), }, } + ret.set[ret.Name()] = ret for _, x := range textClone.Templates() { name := x.Name() src := t.set[name] @@ -413,3 +423,10 @@ func parseGlob(t *Template, pattern string) (*Template, error) { } return parseFiles(t, filenames...) } + +// IsTrue reports whether the value is 'true', in the sense of not the zero of its type, +// and whether the value has a meaningful truth value. This is the definition of +// truth used by if and other such actions. +func IsTrue(val interface{}) (truth, ok bool) { + return template.IsTrue(val) +} diff --git a/libgo/go/html/template/template_test.go b/libgo/go/html/template/template_test.go new file mode 100644 index 0000000..6f70d67 --- /dev/null +++ b/libgo/go/html/template/template_test.go @@ -0,0 +1,29 @@ +package template + +import ( + "bytes" + "testing" +) + +func TestTemplateClone(t *testing.T) { + // https://golang.org/issue/12996 + orig := New("name") + clone, err := orig.Clone() + if err != nil { + t.Fatal(err) + } + if len(clone.Templates()) != len(orig.Templates()) { + t.Fatalf("Invalid lenth of t.Clone().Templates()") + } + + const want = "stuff" + parsed := Must(clone.Parse(want)) + var buf bytes.Buffer + err = parsed.Execute(&buf, nil) + if err != nil { + t.Fatal(err) + } + if got := buf.String(); got != want { + t.Fatalf("got %q; want %q", got, want) + } +} diff --git a/libgo/go/html/template/transition.go b/libgo/go/html/template/transition.go index d2e0287..aefe035 100644 --- a/libgo/go/html/template/transition.go +++ b/libgo/go/html/template/transition.go @@ -169,7 +169,7 @@ func tBeforeValue(c context, s []byte) (context, int) { case '"': delim, i = delimDoubleQuote, i+1 } - c.state, c.delim, c.attr = attrStartStates[c.attr], delim, attrNone + c.state, c.delim = attrStartStates[c.attr], delim return c, i } |