diff options
author | ian <ian@138bc75d-0d04-0410-961f-82ee72b054a4> | 2012-03-02 20:01:37 +0000 |
---|---|---|
committer | ian <ian@138bc75d-0d04-0410-961f-82ee72b054a4> | 2012-03-02 20:01:37 +0000 |
commit | ef10b1dda693467283a3645a5da610e810149764 (patch) | |
tree | 3eeb8918d39d675108073c8b76d6dd10586a608c /libgo/go/html | |
parent | ea6ad4ae9afbd45b5c19aadab39ddbf1db0d50f8 (diff) | |
download | gcc-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.go | 90 | ||||
-rw-r--r-- | libgo/go/html/template/clone_test.go | 175 | ||||
-rw-r--r-- | libgo/go/html/template/content.go | 18 | ||||
-rw-r--r-- | libgo/go/html/template/content_test.go | 40 | ||||
-rw-r--r-- | libgo/go/html/template/doc.go | 12 | ||||
-rw-r--r-- | libgo/go/html/template/escape.go | 94 | ||||
-rw-r--r-- | libgo/go/html/template/template.go | 73 |
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>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, <World>!", - }, - { - `Hello, {{if false}}{{.X}}{{else}}{{"<World>"}}{{end}}!`, - "Hello, <World>!", - "Hello, <World>!", - }, - { - `Hello, {{with "<World>"}}{{.}}{{end}}!`, - "Hello, <World>!", - "Hello, <World>!", - }, - { - `{{range .}}<p>{{.}}</p>{{end}}`, - "<p>foo</p><p><bar></p><p>baz</p>", - "<p>foo</p><p><bar></p><p>baz</p>", - }, - { - `Hello, {{"<World>" | html}}!`, - "Hello, <World>!", - "Hello, <World>!", - }, - { - `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: "\u003ci\u003e*/" "></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(), ` ( <i>*/ ) `; 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. |