summaryrefslogtreecommitdiff
path: root/libgo/go/html
diff options
context:
space:
mode:
authorian <ian@138bc75d-0d04-0410-961f-82ee72b054a4>2012-03-02 20:01:37 +0000
committerian <ian@138bc75d-0d04-0410-961f-82ee72b054a4>2012-03-02 20:01:37 +0000
commitef10b1dda693467283a3645a5da610e810149764 (patch)
tree3eeb8918d39d675108073c8b76d6dd10586a608c /libgo/go/html
parentea6ad4ae9afbd45b5c19aadab39ddbf1db0d50f8 (diff)
downloadgcc-ef10b1dda693467283a3645a5da610e810149764.tar.gz
libgo: Update to weekly.2012-02-22 release.
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@184819 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'libgo/go/html')
-rw-r--r--libgo/go/html/template/clone.go90
-rw-r--r--libgo/go/html/template/clone_test.go175
-rw-r--r--libgo/go/html/template/content.go18
-rw-r--r--libgo/go/html/template/content_test.go40
-rw-r--r--libgo/go/html/template/doc.go12
-rw-r--r--libgo/go/html/template/escape.go94
-rw-r--r--libgo/go/html/template/template.go73
7 files changed, 274 insertions, 228 deletions
diff --git a/libgo/go/html/template/clone.go b/libgo/go/html/template/clone.go
deleted file mode 100644
index d0d8ea46733..00000000000
--- a/libgo/go/html/template/clone.go
+++ /dev/null
@@ -1,90 +0,0 @@
-// Copyright 2011 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package template
-
-import (
- "text/template/parse"
-)
-
-// clone clones a template Node.
-func clone(n parse.Node) parse.Node {
- switch t := n.(type) {
- case *parse.ActionNode:
- return cloneAction(t)
- case *parse.IfNode:
- b := new(parse.IfNode)
- copyBranch(&b.BranchNode, &t.BranchNode)
- return b
- case *parse.ListNode:
- return cloneList(t)
- case *parse.RangeNode:
- b := new(parse.RangeNode)
- copyBranch(&b.BranchNode, &t.BranchNode)
- return b
- case *parse.TemplateNode:
- return cloneTemplate(t)
- case *parse.TextNode:
- return cloneText(t)
- case *parse.WithNode:
- b := new(parse.WithNode)
- copyBranch(&b.BranchNode, &t.BranchNode)
- return b
- }
- panic("cloning " + n.String() + " is unimplemented")
-}
-
-// cloneAction returns a deep clone of n.
-func cloneAction(n *parse.ActionNode) *parse.ActionNode {
- // We use keyless fields because they won't compile if a field is added.
- return &parse.ActionNode{n.NodeType, n.Line, clonePipe(n.Pipe)}
-}
-
-// cloneList returns a deep clone of n.
-func cloneList(n *parse.ListNode) *parse.ListNode {
- if n == nil {
- return nil
- }
- // We use keyless fields because they won't compile if a field is added.
- c := parse.ListNode{n.NodeType, make([]parse.Node, len(n.Nodes))}
- for i, child := range n.Nodes {
- c.Nodes[i] = clone(child)
- }
- return &c
-}
-
-// clonePipe returns a shallow clone of n.
-// The escaper does not modify pipe descendants in place so there's no need to
-// clone deeply.
-func clonePipe(n *parse.PipeNode) *parse.PipeNode {
- if n == nil {
- return nil
- }
- // We use keyless fields because they won't compile if a field is added.
- return &parse.PipeNode{n.NodeType, n.Line, n.Decl, n.Cmds}
-}
-
-// cloneTemplate returns a deep clone of n.
-func cloneTemplate(n *parse.TemplateNode) *parse.TemplateNode {
- // We use keyless fields because they won't compile if a field is added.
- return &parse.TemplateNode{n.NodeType, n.Line, n.Name, clonePipe(n.Pipe)}
-}
-
-// cloneText clones the given node sharing its []byte.
-func cloneText(n *parse.TextNode) *parse.TextNode {
- // We use keyless fields because they won't compile if a field is added.
- return &parse.TextNode{n.NodeType, n.Text}
-}
-
-// copyBranch clones src into dst.
-func copyBranch(dst, src *parse.BranchNode) {
- // We use keyless fields because they won't compile if a field is added.
- *dst = parse.BranchNode{
- src.NodeType,
- src.Line,
- clonePipe(src.Pipe),
- cloneList(src.List),
- cloneList(src.ElseList),
- }
-}
diff --git a/libgo/go/html/template/clone_test.go b/libgo/go/html/template/clone_test.go
index 39788173b99..c612775d4f0 100644
--- a/libgo/go/html/template/clone_test.go
+++ b/libgo/go/html/template/clone_test.go
@@ -7,86 +7,109 @@ package template
import (
"bytes"
"testing"
+ "text/template/parse"
)
+func TestAddParseTree(t *testing.T) {
+ root := Must(New("root").Parse(`{{define "a"}} {{.}} {{template "b"}} {{.}} "></a>{{end}}`))
+ tree, err := parse.Parse("t", `{{define "b"}}<a href="{{end}}`, "", "", nil, nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+ added := Must(root.AddParseTree("b", tree["b"]))
+ b := new(bytes.Buffer)
+ err = added.ExecuteTemplate(b, "a", "1>0")
+ if err != nil {
+ t.Fatal(err)
+ }
+ if got, want := b.String(), ` 1&gt;0 <a href=" 1%3e0 "></a>`; got != want {
+ t.Errorf("got %q want %q", got, want)
+ }
+}
+
func TestClone(t *testing.T) {
- tests := []struct {
- input, want, wantClone string
- }{
- {
- `Hello, {{if true}}{{"<World>"}}{{end}}!`,
- "Hello, <World>!",
- "Hello, &lt;World&gt;!",
- },
- {
- `Hello, {{if false}}{{.X}}{{else}}{{"<World>"}}{{end}}!`,
- "Hello, <World>!",
- "Hello, &lt;World&gt;!",
- },
- {
- `Hello, {{with "<World>"}}{{.}}{{end}}!`,
- "Hello, <World>!",
- "Hello, &lt;World&gt;!",
- },
- {
- `{{range .}}<p>{{.}}</p>{{end}}`,
- "<p>foo</p><p><bar></p><p>baz</p>",
- "<p>foo</p><p>&lt;bar&gt;</p><p>baz</p>",
- },
- {
- `Hello, {{"<World>" | html}}!`,
- "Hello, &lt;World&gt;!",
- "Hello, &lt;World&gt;!",
- },
- {
- `Hello{{if 1}}, World{{else}}{{template "d"}}{{end}}!`,
- "Hello, World!",
- "Hello, World!",
- },
+ // The {{.}} will be executed with data "<i>*/" in different contexts.
+ // In the t0 template, it will be in a text context.
+ // In the t1 template, it will be in a URL context.
+ // In the t2 template, it will be in a JavaScript context.
+ // In the t3 template, it will be in a CSS context.
+ const tmpl = `{{define "a"}}{{template "lhs"}}{{.}}{{template "rhs"}}{{end}}`
+ b := new(bytes.Buffer)
+
+ // Create an incomplete template t0.
+ t0 := Must(New("t0").Parse(tmpl))
+
+ // Clone t0 as t1.
+ t1 := Must(t0.Clone())
+ Must(t1.Parse(`{{define "lhs"}} <a href=" {{end}}`))
+ Must(t1.Parse(`{{define "rhs"}} "></a> {{end}}`))
+
+ // Execute t1.
+ b.Reset()
+ if err := t1.ExecuteTemplate(b, "a", "<i>*/"); err != nil {
+ t.Fatal(err)
+ }
+ if got, want := b.String(), ` <a href=" %3ci%3e*/ "></a> `; got != want {
+ t.Errorf("t1: got %q want %q", got, want)
+ }
+
+ // Clone t0 as t2.
+ t2 := Must(t0.Clone())
+ Must(t2.Parse(`{{define "lhs"}} <p onclick="javascript: {{end}}`))
+ Must(t2.Parse(`{{define "rhs"}} "></p> {{end}}`))
+
+ // Execute t2.
+ b.Reset()
+ if err := t2.ExecuteTemplate(b, "a", "<i>*/"); err != nil {
+ t.Fatal(err)
+ }
+ if got, want := b.String(), ` <p onclick="javascript: &#34;\u003ci\u003e*/&#34; "></p> `; got != want {
+ t.Errorf("t2: got %q want %q", got, want)
}
- for _, test := range tests {
- s, err := New("s").Parse(test.input)
- if err != nil {
- t.Errorf("input=%q: unexpected parse error %v", test.input, err)
- }
-
- d, _ := New("d").Parse(test.input)
- // Hack: just replace the root of the tree.
- d.text.Root = cloneList(s.text.Root)
-
- if want, got := s.text.Root.String(), d.text.Root.String(); want != got {
- t.Errorf("want %q, got %q", want, got)
- }
-
- err = escapeTemplates(d, "d")
- if err != nil {
- t.Errorf("%q: failed to escape: %s", test.input, err)
- continue
- }
-
- if want, got := "s", s.Name(); want != got {
- t.Errorf("want %q, got %q", want, got)
- continue
- }
- if want, got := "d", d.Name(); want != got {
- t.Errorf("want %q, got %q", want, got)
- continue
- }
-
- data := []string{"foo", "<bar>", "baz"}
-
- var b bytes.Buffer
- d.Execute(&b, data)
- if got := b.String(); got != test.wantClone {
- t.Errorf("input=%q: want %q, got %q", test.input, test.wantClone, got)
- }
-
- // Make sure escaping d did not affect s.
- b.Reset()
- s.text.Execute(&b, data)
- if got := b.String(); got != test.want {
- t.Errorf("input=%q: want %q, got %q", test.input, test.want, got)
- }
+ // Clone t0 as t3, but do not execute t3 yet.
+ t3 := Must(t0.Clone())
+ Must(t3.Parse(`{{define "lhs"}} <style> {{end}}`))
+ Must(t3.Parse(`{{define "rhs"}} </style> {{end}}`))
+
+ // Complete t0.
+ Must(t0.Parse(`{{define "lhs"}} ( {{end}}`))
+ Must(t0.Parse(`{{define "rhs"}} ) {{end}}`))
+
+ // Clone t0 as t4. Redefining the "lhs" template should fail.
+ t4 := Must(t0.Clone())
+ if _, err := t4.Parse(`{{define "lhs"}} FAIL {{end}}`); err == nil {
+ t.Error(`redefine "lhs": got nil err want non-nil`)
+ }
+
+ // Execute t0.
+ b.Reset()
+ if err := t0.ExecuteTemplate(b, "a", "<i>*/"); err != nil {
+ t.Fatal(err)
+ }
+ if got, want := b.String(), ` ( &lt;i&gt;*/ ) `; got != want {
+ t.Errorf("t0: got %q want %q", got, want)
+ }
+
+ // Clone t0. This should fail, as t0 has already executed.
+ if _, err := t0.Clone(); err == nil {
+ t.Error(`t0.Clone(): got nil err want non-nil`)
+ }
+
+ // Similarly, cloning sub-templates should fail.
+ if _, err := t0.Lookup("a").Clone(); err == nil {
+ t.Error(`t0.Lookup("a").Clone(): got nil err want non-nil`)
+ }
+ if _, err := t0.Lookup("lhs").Clone(); err == nil {
+ t.Error(`t0.Lookup("lhs").Clone(): got nil err want non-nil`)
+ }
+
+ // Execute t3.
+ b.Reset()
+ if err := t3.ExecuteTemplate(b, "a", "<i>*/"); err != nil {
+ t.Fatal(err)
+ }
+ if got, want := b.String(), ` <style> ZgotmplZ </style> `; got != want {
+ t.Errorf("t3: got %q want %q", got, want)
}
}
diff --git a/libgo/go/html/template/content.go b/libgo/go/html/template/content.go
index 4de7ccde912..539664f9729 100644
--- a/libgo/go/html/template/content.go
+++ b/libgo/go/html/template/content.go
@@ -85,6 +85,22 @@ func indirect(a interface{}) interface{} {
return v.Interface()
}
+var (
+ errorType = reflect.TypeOf((*error)(nil)).Elem()
+ fmtStringerType = reflect.TypeOf((*fmt.Stringer)(nil)).Elem()
+)
+
+// indirectToStringerOrError returns the value, after dereferencing as many times
+// as necessary to reach the base type (or nil) or an implementation of fmt.Stringer
+// or error,
+func indirectToStringerOrError(a interface{}) interface{} {
+ v := reflect.ValueOf(a)
+ for !v.Type().Implements(fmtStringerType) && !v.Type().Implements(errorType) && v.Kind() == reflect.Ptr && !v.IsNil() {
+ v = v.Elem()
+ }
+ return v.Interface()
+}
+
// stringify converts its arguments to a string and the type of the content.
// All pointers are dereferenced, as in the text/template package.
func stringify(args ...interface{}) (string, contentType) {
@@ -107,7 +123,7 @@ func stringify(args ...interface{}) (string, contentType) {
}
}
for i, arg := range args {
- args[i] = indirect(arg)
+ args[i] = indirectToStringerOrError(arg)
}
return fmt.Sprint(args...), contentTypePlain
}
diff --git a/libgo/go/html/template/content_test.go b/libgo/go/html/template/content_test.go
index c96a521a59c..3c32e5e89cf 100644
--- a/libgo/go/html/template/content_test.go
+++ b/libgo/go/html/template/content_test.go
@@ -6,6 +6,7 @@ package template
import (
"bytes"
+ "fmt"
"strings"
"testing"
)
@@ -219,3 +220,42 @@ func TestTypedContent(t *testing.T) {
}
}
}
+
+// Test that we print using the String method. Was issue 3073.
+type stringer struct {
+ v int
+}
+
+func (s *stringer) String() string {
+ return fmt.Sprintf("string=%d", s.v)
+}
+
+type errorer struct {
+ v int
+}
+
+func (s *errorer) Error() string {
+ return fmt.Sprintf("error=%d", s.v)
+}
+
+func TestStringer(t *testing.T) {
+ s := &stringer{3}
+ b := new(bytes.Buffer)
+ tmpl := Must(New("x").Parse("{{.}}"))
+ if err := tmpl.Execute(b, s); err != nil {
+ t.Fatal(err)
+ }
+ var expect = "string=3"
+ if b.String() != expect {
+ t.Errorf("expected %q got %q", expect, b.String())
+ }
+ e := &errorer{7}
+ b.Reset()
+ if err := tmpl.Execute(b, e); err != nil {
+ t.Fatal(err)
+ }
+ expect = "error=7"
+ if b.String() != expect {
+ t.Errorf("expected %q got %q", expect, b.String())
+ }
+}
diff --git a/libgo/go/html/template/doc.go b/libgo/go/html/template/doc.go
index 6fe507abea4..7f60f3b9680 100644
--- a/libgo/go/html/template/doc.go
+++ b/libgo/go/html/template/doc.go
@@ -17,11 +17,11 @@ Introduction
This package wraps package text/template so you can share its template API
to parse and execute HTML templates safely.
- set, err := new(template.Set).Parse(...)
+ tmpl, err := template.New("name").Parse(...)
// Error checking elided
- err = set.Execute(out, "Foo", data)
+ err = tmpl.Execute(out, "Foo", data)
-If successful, set will now be injection-safe. Otherwise, err is an error
+If successful, tmpl will now be injection-safe. Otherwise, err is an error
defined in the docs for ErrorCode.
HTML templates treat data values as plain text which should be encoded so they
@@ -172,18 +172,18 @@ This package assumes that template authors are trusted, that Execute's data
parameter is not, and seeks to preserve the properties below in the face
of untrusted data:
-Structure Preservation Property
+Structure Preservation Property:
"... when a template author writes an HTML tag in a safe templating language,
the browser will interpret the corresponding portion of the output as a tag
regardless of the values of untrusted data, and similarly for other structures
such as attribute boundaries and JS and CSS string boundaries."
-Code Effect Property
+Code Effect Property:
"... only code specified by the template author should run as a result of
injecting the template output into a page and all code specified by the
template author should run as a result of the same."
-Least Surprise Property
+Least Surprise Property:
"A developer (or code reviewer) familiar with HTML, CSS, and JavaScript, who
knows that contextual autoescaping happens should be able to look at a {{.}}
and correctly infer what sanitization happens."
diff --git a/libgo/go/html/template/escape.go b/libgo/go/html/template/escape.go
index c6f723ae4a4..02fa3eaad6b 100644
--- a/libgo/go/html/template/escape.go
+++ b/libgo/go/html/template/escape.go
@@ -46,30 +46,30 @@ func escapeTemplates(tmpl *Template, names ...string) error {
// funcMap maps command names to functions that render their inputs safe.
var funcMap = template.FuncMap{
- "exp_template_html_attrescaper": attrEscaper,
- "exp_template_html_commentescaper": commentEscaper,
- "exp_template_html_cssescaper": cssEscaper,
- "exp_template_html_cssvaluefilter": cssValueFilter,
- "exp_template_html_htmlnamefilter": htmlNameFilter,
- "exp_template_html_htmlescaper": htmlEscaper,
- "exp_template_html_jsregexpescaper": jsRegexpEscaper,
- "exp_template_html_jsstrescaper": jsStrEscaper,
- "exp_template_html_jsvalescaper": jsValEscaper,
- "exp_template_html_nospaceescaper": htmlNospaceEscaper,
- "exp_template_html_rcdataescaper": rcdataEscaper,
- "exp_template_html_urlescaper": urlEscaper,
- "exp_template_html_urlfilter": urlFilter,
- "exp_template_html_urlnormalizer": urlNormalizer,
+ "html_template_attrescaper": attrEscaper,
+ "html_template_commentescaper": commentEscaper,
+ "html_template_cssescaper": cssEscaper,
+ "html_template_cssvaluefilter": cssValueFilter,
+ "html_template_htmlnamefilter": htmlNameFilter,
+ "html_template_htmlescaper": htmlEscaper,
+ "html_template_jsregexpescaper": jsRegexpEscaper,
+ "html_template_jsstrescaper": jsStrEscaper,
+ "html_template_jsvalescaper": jsValEscaper,
+ "html_template_nospaceescaper": htmlNospaceEscaper,
+ "html_template_rcdataescaper": rcdataEscaper,
+ "html_template_urlescaper": urlEscaper,
+ "html_template_urlfilter": urlFilter,
+ "html_template_urlnormalizer": urlNormalizer,
}
// equivEscapers matches contextual escapers to equivalent template builtins.
var equivEscapers = map[string]string{
- "exp_template_html_attrescaper": "html",
- "exp_template_html_htmlescaper": "html",
- "exp_template_html_nospaceescaper": "html",
- "exp_template_html_rcdataescaper": "html",
- "exp_template_html_urlescaper": "urlquery",
- "exp_template_html_urlnormalizer": "urlquery",
+ "html_template_attrescaper": "html",
+ "html_template_htmlescaper": "html",
+ "html_template_nospaceescaper": "html",
+ "html_template_rcdataescaper": "html",
+ "html_template_urlescaper": "urlquery",
+ "html_template_urlnormalizer": "urlquery",
}
// escaper collects type inferences about templates and changes needed to make
@@ -147,17 +147,17 @@ func (e *escaper) escapeAction(c context, n *parse.ActionNode) context {
case stateURL, stateCSSDqStr, stateCSSSqStr, stateCSSDqURL, stateCSSSqURL, stateCSSURL:
switch c.urlPart {
case urlPartNone:
- s = append(s, "exp_template_html_urlfilter")
+ s = append(s, "html_template_urlfilter")
fallthrough
case urlPartPreQuery:
switch c.state {
case stateCSSDqStr, stateCSSSqStr:
- s = append(s, "exp_template_html_cssescaper")
+ s = append(s, "html_template_cssescaper")
default:
- s = append(s, "exp_template_html_urlnormalizer")
+ s = append(s, "html_template_urlnormalizer")
}
case urlPartQueryOrFrag:
- s = append(s, "exp_template_html_urlescaper")
+ s = append(s, "html_template_urlescaper")
case urlPartUnknown:
return context{
state: stateError,
@@ -167,27 +167,27 @@ func (e *escaper) escapeAction(c context, n *parse.ActionNode) context {
panic(c.urlPart.String())
}
case stateJS:
- s = append(s, "exp_template_html_jsvalescaper")
+ s = append(s, "html_template_jsvalescaper")
// A slash after a value starts a div operator.
c.jsCtx = jsCtxDivOp
case stateJSDqStr, stateJSSqStr:
- s = append(s, "exp_template_html_jsstrescaper")
+ s = append(s, "html_template_jsstrescaper")
case stateJSRegexp:
- s = append(s, "exp_template_html_jsregexpescaper")
+ s = append(s, "html_template_jsregexpescaper")
case stateCSS:
- s = append(s, "exp_template_html_cssvaluefilter")
+ s = append(s, "html_template_cssvaluefilter")
case stateText:
- s = append(s, "exp_template_html_htmlescaper")
+ s = append(s, "html_template_htmlescaper")
case stateRCDATA:
- s = append(s, "exp_template_html_rcdataescaper")
+ s = append(s, "html_template_rcdataescaper")
case stateAttr:
// Handled below in delim check.
case stateAttrName, stateTag:
c.state = stateAttrName
- s = append(s, "exp_template_html_htmlnamefilter")
+ s = append(s, "html_template_htmlnamefilter")
default:
if isComment(c.state) {
- s = append(s, "exp_template_html_commentescaper")
+ s = append(s, "html_template_commentescaper")
} else {
panic("unexpected state " + c.state.String())
}
@@ -196,9 +196,9 @@ func (e *escaper) escapeAction(c context, n *parse.ActionNode) context {
case delimNone:
// No extra-escaping needed for raw text content.
case delimSpaceOrTagEnd:
- s = append(s, "exp_template_html_nospaceescaper")
+ s = append(s, "html_template_nospaceescaper")
default:
- s = append(s, "exp_template_html_attrescaper")
+ s = append(s, "html_template_attrescaper")
}
e.editActionNode(n, s)
return c
@@ -260,22 +260,22 @@ func ensurePipelineContains(p *parse.PipeNode, s []string) {
// redundantFuncs[a][b] implies that funcMap[b](funcMap[a](x)) == funcMap[a](x)
// for all x.
var redundantFuncs = map[string]map[string]bool{
- "exp_template_html_commentescaper": {
- "exp_template_html_attrescaper": true,
- "exp_template_html_nospaceescaper": true,
- "exp_template_html_htmlescaper": true,
+ "html_template_commentescaper": {
+ "html_template_attrescaper": true,
+ "html_template_nospaceescaper": true,
+ "html_template_htmlescaper": true,
},
- "exp_template_html_cssescaper": {
- "exp_template_html_attrescaper": true,
+ "html_template_cssescaper": {
+ "html_template_attrescaper": true,
},
- "exp_template_html_jsregexpescaper": {
- "exp_template_html_attrescaper": true,
+ "html_template_jsregexpescaper": {
+ "html_template_attrescaper": true,
},
- "exp_template_html_jsstrescaper": {
- "exp_template_html_attrescaper": true,
+ "html_template_jsstrescaper": {
+ "html_template_attrescaper": true,
},
- "exp_template_html_urlescaper": {
- "exp_template_html_urlnormalizer": true,
+ "html_template_urlescaper": {
+ "html_template_urlnormalizer": true,
},
}
@@ -505,7 +505,7 @@ func (e *escaper) escapeTree(c context, name string, line int) (context, string)
dt := e.template(dname)
if dt == nil {
dt = template.New(dname)
- dt.Tree = &parse.Tree{Name: dname, Root: cloneList(t.Root)}
+ dt.Tree = &parse.Tree{Name: dname, Root: t.Root.CopyList()}
e.derived[dname] = dt
}
t = dt
diff --git a/libgo/go/html/template/template.go b/libgo/go/html/template/template.go
index 9ffe41413a8..b0bae7a54fb 100644
--- a/libgo/go/html/template/template.go
+++ b/libgo/go/html/template/template.go
@@ -50,7 +50,7 @@ func (t *Template) Execute(wr io.Writer, data interface{}) (err error) {
// ExecuteTemplate applies the template associated with t that has the given
// name to the specified data object and writes the output to wr.
func (t *Template) ExecuteTemplate(wr io.Writer, name string, data interface{}) error {
- tmpl, err := t.lookupAndEscapeTemplate(wr, name)
+ tmpl, err := t.lookupAndEscapeTemplate(name)
if err != nil {
return err
}
@@ -60,7 +60,7 @@ func (t *Template) ExecuteTemplate(wr io.Writer, name string, data interface{})
// lookupAndEscapeTemplate guarantees that the template with the given name
// is escaped, or returns an error if it cannot be. It returns the named
// template.
-func (t *Template) lookupAndEscapeTemplate(wr io.Writer, name string) (tmpl *Template, err error) {
+func (t *Template) lookupAndEscapeTemplate(name string) (tmpl *Template, err error) {
t.nameSpace.mu.Lock()
defer t.nameSpace.mu.Unlock()
tmpl = t.set[name]
@@ -106,14 +106,71 @@ func (t *Template) Parse(src string) (*Template, error) {
return t, nil
}
-// AddParseTree is unimplemented.
-func (t *Template) AddParseTree(name string, tree *parse.Tree) error {
- return fmt.Errorf("html/template: AddParseTree unimplemented")
+// AddParseTree creates a new template with the name and parse tree
+// and associates it with t.
+//
+// It returns an error if t has already been executed.
+func (t *Template) AddParseTree(name string, tree *parse.Tree) (*Template, error) {
+ t.nameSpace.mu.Lock()
+ defer t.nameSpace.mu.Unlock()
+ if t.escaped {
+ return nil, fmt.Errorf("html/template: cannot AddParseTree to %q after it has executed", t.Name())
+ }
+ text, err := t.text.AddParseTree(name, tree)
+ if err != nil {
+ return nil, err
+ }
+ ret := &Template{
+ false,
+ text,
+ t.nameSpace,
+ }
+ t.set[name] = ret
+ return ret, nil
}
-// Clone is unimplemented.
-func (t *Template) Clone(name string) error {
- return fmt.Errorf("html/template: Clone unimplemented")
+// Clone returns a duplicate of the template, including all associated
+// templates. The actual representation is not copied, but the name space of
+// associated templates is, so further calls to Parse in the copy will add
+// templates to the copy but not to the original. Clone can be used to prepare
+// common templates and use them with variant definitions for other templates
+// by adding the variants after the clone is made.
+//
+// It returns an error if t has already been executed.
+func (t *Template) Clone() (*Template, error) {
+ t.nameSpace.mu.Lock()
+ defer t.nameSpace.mu.Unlock()
+ if t.escaped {
+ return nil, fmt.Errorf("html/template: cannot Clone %q after it has executed", t.Name())
+ }
+ textClone, err := t.text.Clone()
+ if err != nil {
+ return nil, err
+ }
+ ret := &Template{
+ false,
+ textClone,
+ &nameSpace{
+ set: make(map[string]*Template),
+ },
+ }
+ for _, x := range textClone.Templates() {
+ name := x.Name()
+ src := t.set[name]
+ if src == nil || src.escaped {
+ return nil, fmt.Errorf("html/template: cannot Clone %q after it has executed", t.Name())
+ }
+ x.Tree = &parse.Tree{
+ Name: x.Tree.Name,
+ Root: x.Tree.Root.CopyList(),
+ }
+ ret.set[name] = &Template{
+ false,
+ x,
+ ret.nameSpace,
+ }
+ }
+ return ret, nil
}
// New allocates a new HTML template with the given name.