aboutsummaryrefslogtreecommitdiff
path: root/libgo/go/html/template
diff options
context:
space:
mode:
Diffstat (limited to 'libgo/go/html/template')
-rw-r--r--libgo/go/html/template/clone_test.go12
-rw-r--r--libgo/go/html/template/context.go3
-rw-r--r--libgo/go/html/template/escape_test.go124
-rw-r--r--libgo/go/html/template/example_test.go37
-rw-r--r--libgo/go/html/template/template.go21
-rw-r--r--libgo/go/html/template/template_test.go29
-rw-r--r--libgo/go/html/template/transition.go2
7 files changed, 162 insertions, 66 deletions
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='&apos;`,
- context{state: stateURL, delim: delimSingleQuote, urlPart: urlPartPreQuery},
+ context{state: stateURL, delim: delimSingleQuote, urlPart: urlPartPreQuery, attr: attrURL},
},
{
`<a href="&quot;`,
- context{state: stateURL, delim: delimDoubleQuote, urlPart: urlPartPreQuery},
+ context{state: stateURL, delim: delimDoubleQuote, urlPart: urlPartPreQuery, attr: attrURL},
},
{
`<a href="&#34;`,
- context{state: stateURL, delim: delimDoubleQuote, urlPart: urlPartPreQuery},
+ context{state: stateURL, delim: delimDoubleQuote, urlPart: urlPartPreQuery, attr: attrURL},
},
{
`<a href=&quot;`,
- 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="&quot;`,
- context{state: stateJSDqStr, delim: delimDoubleQuote},
+ context{state: stateJSDqStr, delim: delimDoubleQuote, attr: attrScript},
},
{
`<a onclick='&quot;foo&quot;`,
- context{state: stateJS, delim: delimSingleQuote, jsCtx: jsCtxDivOp},
+ context{state: stateJS, delim: delimSingleQuote, jsCtx: jsCtxDivOp, attr: attrScript},
},
{
`<a onclick=&#39;foo&#39;`,
- context{state: stateJS, delim: delimSpaceOrTagEnd, jsCtx: jsCtxDivOp},
+ context{state: stateJS, delim: delimSpaceOrTagEnd, jsCtx: jsCtxDivOp, attr: attrScript},
},
{
`<a onclick=&#39;foo`,
- context{state: stateJSSqStr, delim: delimSpaceOrTagEnd},
+ context{state: stateJSSqStr, delim: delimSpaceOrTagEnd, attr: attrScript},
},
{
`<a onclick="&quot;foo'`,
- context{state: stateJSDqStr, delim: delimDoubleQuote},
+ context{state: stateJSDqStr, delim: delimDoubleQuote, attr: attrScript},
},
{
`<a onclick="'foo&quot;`,
- 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: &quot;`,
- 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(&#x22;/`,
- 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
}