diff options
Diffstat (limited to 'libgo/go/html/template')
-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 |
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=''`, - 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 } |