aboutsummaryrefslogtreecommitdiff
path: root/libgo/go/html
diff options
context:
space:
mode:
authorIan Lance Taylor <iant@golang.org>2017-01-14 00:05:42 +0000
committerIan Lance Taylor <ian@gcc.gnu.org>2017-01-14 00:05:42 +0000
commitc2047754c300b68c05d65faa8dc2925fe67b71b4 (patch)
treee183ae81a1f48a02945cb6de463a70c5be1b06f6 /libgo/go/html
parent829afb8f05602bb31c9c597b24df7377fed4f059 (diff)
downloadgcc-c2047754c300b68c05d65faa8dc2925fe67b71b4.zip
gcc-c2047754c300b68c05d65faa8dc2925fe67b71b4.tar.gz
gcc-c2047754c300b68c05d65faa8dc2925fe67b71b4.tar.bz2
libgo: update to Go 1.8 release candidate 1
Compiler changes: * Change map assignment to use mapassign and assign value directly. * Change string iteration to use decoderune, faster for ASCII strings. * Change makeslice to take int, and use makeslice64 for larger values. * Add new noverflow field to hmap struct used for maps. Unresolved problems, to be fixed later: * Commented out test in go/types/sizes_test.go that doesn't compile. * Commented out reflect.TestStructOf test for padding after zero-sized field. Reviewed-on: https://go-review.googlesource.com/35231 gotools/: Updates for Go 1.8rc1. * Makefile.am (go_cmd_go_files): Add bug.go. (s-zdefaultcc): Write defaultPkgConfig. * Makefile.in: Rebuild. From-SVN: r244456
Diffstat (limited to 'libgo/go/html')
-rw-r--r--libgo/go/html/template/clone_test.go68
-rw-r--r--libgo/go/html/template/content_test.go41
-rw-r--r--libgo/go/html/template/context.go14
-rw-r--r--libgo/go/html/template/doc.go2
-rw-r--r--libgo/go/html/template/error.go2
-rw-r--r--libgo/go/html/template/escape.go14
-rw-r--r--libgo/go/html/template/escape_test.go14
-rw-r--r--libgo/go/html/template/js.go44
-rw-r--r--libgo/go/html/template/js_test.go19
-rw-r--r--libgo/go/html/template/template.go81
-rw-r--r--libgo/go/html/template/template_test.go130
-rw-r--r--libgo/go/html/template/transition.go30
-rw-r--r--libgo/go/html/template/url.go2
13 files changed, 411 insertions, 50 deletions
diff --git a/libgo/go/html/template/clone_test.go b/libgo/go/html/template/clone_test.go
index d7c62fa3..b500715 100644
--- a/libgo/go/html/template/clone_test.go
+++ b/libgo/go/html/template/clone_test.go
@@ -7,7 +7,9 @@ package template
import (
"bytes"
"errors"
+ "fmt"
"io/ioutil"
+ "sync"
"testing"
"text/template/parse"
)
@@ -194,3 +196,69 @@ func TestFuncMapWorksAfterClone(t *testing.T) {
t.Errorf("clone error message mismatch want %q got %q", wantErr, gotErr)
}
}
+
+// https://golang.org/issue/16101
+func TestTemplateCloneExecuteRace(t *testing.T) {
+ const (
+ input = `<title>{{block "a" .}}a{{end}}</title><body>{{block "b" .}}b{{end}}<body>`
+ overlay = `{{define "b"}}A{{end}}`
+ )
+ outer := Must(New("outer").Parse(input))
+ tmpl := Must(Must(outer.Clone()).Parse(overlay))
+
+ var wg sync.WaitGroup
+ for i := 0; i < 10; i++ {
+ wg.Add(1)
+ go func() {
+ defer wg.Done()
+ for i := 0; i < 100; i++ {
+ if err := tmpl.Execute(ioutil.Discard, "data"); err != nil {
+ panic(err)
+ }
+ }
+ }()
+ }
+ wg.Wait()
+}
+
+func TestTemplateCloneLookup(t *testing.T) {
+ // Template.escape makes an assumption that the template associated
+ // with t.Name() is t. Check that this holds.
+ tmpl := Must(New("x").Parse("a"))
+ tmpl = Must(tmpl.Clone())
+ if tmpl.Lookup(tmpl.Name()) != tmpl {
+ t.Error("after Clone, tmpl.Lookup(tmpl.Name()) != tmpl")
+ }
+}
+
+func TestCloneGrowth(t *testing.T) {
+ tmpl := Must(New("root").Parse(`<title>{{block "B". }}Arg{{end}}</title>`))
+ tmpl = Must(tmpl.Clone())
+ Must(tmpl.Parse(`{{define "B"}}Text{{end}}`))
+ for i := 0; i < 10; i++ {
+ tmpl.Execute(ioutil.Discard, nil)
+ }
+ if len(tmpl.DefinedTemplates()) > 200 {
+ t.Fatalf("too many templates: %v", len(tmpl.DefinedTemplates()))
+ }
+}
+
+// https://golang.org/issue/17735
+func TestCloneRedefinedName(t *testing.T) {
+ const base = `
+{{ define "a" -}}<title>{{ template "b" . -}}</title>{{ end -}}
+{{ define "b" }}{{ end -}}
+`
+ const page = `{{ template "a" . }}`
+
+ t1 := Must(New("a").Parse(base))
+
+ for i := 0; i < 2; i++ {
+ t2 := Must(t1.Clone())
+ t2 = Must(t2.New(fmt.Sprintf("%d", i)).Parse(page))
+ err := t2.Execute(ioutil.Discard, nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+ }
+}
diff --git a/libgo/go/html/template/content_test.go b/libgo/go/html/template/content_test.go
index e698328..0b4365c 100644
--- a/libgo/go/html/template/content_test.go
+++ b/libgo/go/html/template/content_test.go
@@ -162,6 +162,47 @@ func TestTypedContent(t *testing.T) {
},
},
{
+ `<script type="text/javascript">alert("{{.}}")</script>`,
+ []string{
+ `\x3cb\x3e \x22foo%\x22 O\x27Reilly \x26bar;`,
+ `a[href =~ \x22\/\/example.com\x22]#foo`,
+ `Hello, \x3cb\x3eWorld\x3c\/b\x3e \x26amp;tc!`,
+ ` dir=\x22ltr\x22`,
+ `c \x26\x26 alert(\x22Hello, World!\x22);`,
+ // Escape sequence not over-escaped.
+ `Hello, World \x26 O\x27Reilly\x21`,
+ `greeting=H%69\x26addressee=(World)`,
+ },
+ },
+ {
+ `<script type="text/javascript">alert({{.}})</script>`,
+ []string{
+ `"\u003cb\u003e \"foo%\" O'Reilly \u0026bar;"`,
+ `"a[href =~ \"//example.com\"]#foo"`,
+ `"Hello, \u003cb\u003eWorld\u003c/b\u003e \u0026amp;tc!"`,
+ `" dir=\"ltr\""`,
+ // Not escaped.
+ `c && alert("Hello, World!");`,
+ // Escape sequence not over-escaped.
+ `"Hello, World & O'Reilly\x21"`,
+ `"greeting=H%69\u0026addressee=(World)"`,
+ },
+ },
+ {
+ // Not treated as JS. The output is same as for <div>{{.}}</div>
+ `<script type="text/template">{{.}}</script>`,
+ []string{
+ `&lt;b&gt; &#34;foo%&#34; O&#39;Reilly &amp;bar;`,
+ `a[href =~ &#34;//example.com&#34;]#foo`,
+ // Not escaped.
+ `Hello, <b>World</b> &amp;tc!`,
+ ` dir=&#34;ltr&#34;`,
+ `c &amp;&amp; alert(&#34;Hello, World!&#34;);`,
+ `Hello, World &amp; O&#39;Reilly\x21`,
+ `greeting=H%69&amp;addressee=(World)`,
+ },
+ },
+ {
`<button onclick='alert("{{.}}")'>`,
[]string{
`\x3cb\x3e \x22foo%\x22 O\x27Reilly \x26bar;`,
diff --git a/libgo/go/html/template/context.go b/libgo/go/html/template/context.go
index c90fc1f..37a3faf 100644
--- a/libgo/go/html/template/context.go
+++ b/libgo/go/html/template/context.go
@@ -285,7 +285,8 @@ type element uint8
const (
// elementNone occurs outside a special tag or special element body.
elementNone element = iota
- // elementScript corresponds to the raw text <script> element.
+ // elementScript corresponds to the raw text <script> element
+ // with JS MIME type or no type attribute.
elementScript
// elementStyle corresponds to the raw text <style> element.
elementStyle
@@ -319,6 +320,8 @@ const (
attrNone attr = iota
// attrScript corresponds to an event handler attribute.
attrScript
+ // attrScriptType corresponds to the type attribute in script HTML element
+ attrScriptType
// attrStyle corresponds to the style attribute whose value is CSS.
attrStyle
// attrURL corresponds to an attribute whose value is a URL.
@@ -326,10 +329,11 @@ const (
)
var attrNames = [...]string{
- attrNone: "attrNone",
- attrScript: "attrScript",
- attrStyle: "attrStyle",
- attrURL: "attrURL",
+ attrNone: "attrNone",
+ attrScript: "attrScript",
+ attrScriptType: "attrScriptType",
+ attrStyle: "attrStyle",
+ attrURL: "attrURL",
}
func (a attr) String() string {
diff --git a/libgo/go/html/template/doc.go b/libgo/go/html/template/doc.go
index e1e9cad..cb89812 100644
--- a/libgo/go/html/template/doc.go
+++ b/libgo/go/html/template/doc.go
@@ -129,7 +129,7 @@ then the template output is
<script>var pair = {"A": "foo", "B": "bar"};</script>
-See package json to understand how non-string content is marshalled for
+See package json to understand how non-string content is marshaled for
embedding in JavaScript contexts.
diff --git a/libgo/go/html/template/error.go b/libgo/go/html/template/error.go
index 5637384..cbcaf92 100644
--- a/libgo/go/html/template/error.go
+++ b/libgo/go/html/template/error.go
@@ -44,7 +44,7 @@ const (
// OK indicates the lack of an error.
OK ErrorCode = iota
- // ErrAmbigContext: "... appears in an ambiguous URL context"
+ // ErrAmbigContext: "... appears in an ambiguous context within a URL"
// Example:
// <a href="
// {{if .C}}
diff --git a/libgo/go/html/template/escape.go b/libgo/go/html/template/escape.go
index 8f2fe46..0e7d2be 100644
--- a/libgo/go/html/template/escape.go
+++ b/libgo/go/html/template/escape.go
@@ -161,7 +161,7 @@ func (e *escaper) escapeAction(c context, n *parse.ActionNode) context {
case urlPartUnknown:
return context{
state: stateError,
- err: errorf(ErrAmbigContext, n, n.Line, "%s appears in an ambiguous URL context", n),
+ err: errorf(ErrAmbigContext, n, n.Line, "%s appears in an ambiguous context within a URL", n),
}
default:
panic(c.urlPart.String())
@@ -673,6 +673,8 @@ func contextAfterText(c context, s []byte) (context, int) {
return transitionFunc[c.state](c, s[:i])
}
+ // We are at the beginning of an attribute value.
+
i := bytes.IndexAny(s, delimEnds[c.delim])
if i == -1 {
i = len(s)
@@ -703,13 +705,21 @@ func contextAfterText(c context, s []byte) (context, int) {
}
return c, len(s)
}
+
+ element := c.element
+
+ // If this is a non-JS "type" attribute inside "script" tag, do not treat the contents as JS.
+ if c.state == stateAttr && c.element == elementScript && c.attr == attrScriptType && !isJSType(string(s[:i])) {
+ element = elementNone
+ }
+
if c.delim != delimSpaceOrTagEnd {
// Consume any quote.
i++
}
// On exiting an attribute, we discard all state information
// except the state and element.
- return context{state: stateTag, element: c.element}, i
+ return context{state: stateTag, element: element}, i
}
// editActionNode records a change to an action pipeline for later commit.
diff --git a/libgo/go/html/template/escape_test.go b/libgo/go/html/template/escape_test.go
index 023ee57..f6ace49 100644
--- a/libgo/go/html/template/escape_test.go
+++ b/libgo/go/html/template/escape_test.go
@@ -903,7 +903,7 @@ func TestErrors(t *testing.T) {
},
{
`<a href="{{if .F}}/foo?a={{else}}/bar/{{end}}{{.H}}">`,
- "z:1:47: {{.H}} appears in an ambiguous URL context",
+ "z:1:47: {{.H}} appears in an ambiguous context within a URL",
},
{
`<a onclick="alert('Hello \`,
@@ -1365,6 +1365,10 @@ func TestEscapeText(t *testing.T) {
context{state: stateTag, element: elementScript},
},
{
+ `<script>`,
+ context{state: stateJS, jsCtx: jsCtxRegexp, element: elementScript},
+ },
+ {
`<script>foo`,
context{state: stateJS, jsCtx: jsCtxDivOp, element: elementScript},
},
@@ -1389,6 +1393,14 @@ func TestEscapeText(t *testing.T) {
context{state: stateText},
},
{
+ `<script type="text/template">`,
+ context{state: stateText},
+ },
+ {
+ `<script type="notjs">`,
+ context{state: stateText},
+ },
+ {
`<Script>`,
context{state: stateJS, element: elementScript},
},
diff --git a/libgo/go/html/template/js.go b/libgo/go/html/template/js.go
index f6d166b..6434fa3 100644
--- a/libgo/go/html/template/js.go
+++ b/libgo/go/html/template/js.go
@@ -162,14 +162,14 @@ func jsValEscaper(args ...interface{}) string {
// a division operator it is not turned into a line comment:
// x/{{y}}
// turning into
- // x//* error marshalling y:
+ // x//* error marshaling y:
// second line of error message */null
return fmt.Sprintf(" /* %s */null ", strings.Replace(err.Error(), "*/", "* /", -1))
}
// TODO: maybe post-process output to prevent it from containing
// "<!--", "-->", "<![CDATA[", "]]>", or "</script"
- // in case custom marshallers produce output containing those.
+ // in case custom marshalers produce output containing those.
// TODO: Maybe abbreviate \u00ab to \xab to produce more compact output.
if len(b) == 0 {
@@ -362,3 +362,43 @@ func isJSIdentPart(r rune) bool {
}
return false
}
+
+// isJSType returns true if the given MIME type should be considered JavaScript.
+//
+// It is used to determine whether a script tag with a type attribute is a javascript container.
+func isJSType(mimeType string) bool {
+ // per
+ // https://www.w3.org/TR/html5/scripting-1.html#attr-script-type
+ // https://tools.ietf.org/html/rfc7231#section-3.1.1
+ // https://tools.ietf.org/html/rfc4329#section-3
+ // https://www.ietf.org/rfc/rfc4627.txt
+
+ // discard parameters
+ if i := strings.Index(mimeType, ";"); i >= 0 {
+ mimeType = mimeType[:i]
+ }
+ mimeType = strings.TrimSpace(mimeType)
+ switch mimeType {
+ case
+ "application/ecmascript",
+ "application/javascript",
+ "application/json",
+ "application/x-ecmascript",
+ "application/x-javascript",
+ "text/ecmascript",
+ "text/javascript",
+ "text/javascript1.0",
+ "text/javascript1.1",
+ "text/javascript1.2",
+ "text/javascript1.3",
+ "text/javascript1.4",
+ "text/javascript1.5",
+ "text/jscript",
+ "text/livescript",
+ "text/x-ecmascript",
+ "text/x-javascript":
+ return true
+ default:
+ return false
+ }
+}
diff --git a/libgo/go/html/template/js_test.go b/libgo/go/html/template/js_test.go
index 7af7997..7484f60 100644
--- a/libgo/go/html/template/js_test.go
+++ b/libgo/go/html/template/js_test.go
@@ -332,6 +332,25 @@ func TestEscapersOnLower7AndSelectHighCodepoints(t *testing.T) {
}
}
+func TestIsJsMimeType(t *testing.T) {
+ tests := []struct {
+ in string
+ out bool
+ }{
+ {"application/javascript;version=1.8", true},
+ {"application/javascript;version=1.8;foo=bar", true},
+ {"application/javascript/version=1.8", false},
+ {"text/javascript", true},
+ {"application/json", true},
+ }
+
+ for _, test := range tests {
+ if isJSType(test.in) != test.out {
+ t.Errorf("isJSType(%q) = %v, want %v", test.in, !test.out, test.out)
+ }
+ }
+}
+
func BenchmarkJSValEscaperWithNum(b *testing.B) {
for i := 0; i < b.N; i++ {
jsValEscaper(3.141592654)
diff --git a/libgo/go/html/template/template.go b/libgo/go/html/template/template.go
index 063e46d..b313a6b 100644
--- a/libgo/go/html/template/template.go
+++ b/libgo/go/html/template/template.go
@@ -33,8 +33,9 @@ var escapeOK = fmt.Errorf("template escaped correctly")
// nameSpace is the data structure shared by all templates in an association.
type nameSpace struct {
- mu sync.Mutex
- set map[string]*Template
+ mu sync.Mutex
+ set map[string]*Template
+ escaped bool
}
// Templates returns a slice of the templates associated with t, including t
@@ -74,13 +75,28 @@ func (t *Template) Option(opt ...string) *Template {
return t
}
+// checkCanParse checks whether it is OK to parse templates.
+// If not, it returns an error.
+func (t *Template) checkCanParse() error {
+ if t == nil {
+ return nil
+ }
+ t.nameSpace.mu.Lock()
+ defer t.nameSpace.mu.Unlock()
+ if t.nameSpace.escaped {
+ return fmt.Errorf("html/template: cannot Parse after Execute")
+ }
+ return nil
+}
+
// escape escapes all associated templates.
func (t *Template) escape() error {
t.nameSpace.mu.Lock()
defer t.nameSpace.mu.Unlock()
+ t.nameSpace.escaped = true
if t.escapeErr == nil {
if t.Tree == nil {
- return fmt.Errorf("template: %q is an incomplete or empty template%s", t.Name(), t.DefinedTemplates())
+ return fmt.Errorf("template: %q is an incomplete or empty template", t.Name())
}
if err := escapeTemplate(t, t.text.Root, t.Name()); err != nil {
return err
@@ -124,6 +140,7 @@ func (t *Template) ExecuteTemplate(wr io.Writer, name string, data interface{})
func (t *Template) lookupAndEscapeTemplate(name string) (tmpl *Template, err error) {
t.nameSpace.mu.Lock()
defer t.nameSpace.mu.Unlock()
+ t.nameSpace.escaped = true
tmpl = t.set[name]
if tmpl == nil {
return nil, fmt.Errorf("html/template: %q is undefined", name)
@@ -150,22 +167,27 @@ 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
-// with t. It is an error if a resulting template is non-empty (contains
-// content other than template definitions) and would replace a
-// non-empty template with the same name. (In multiple calls to Parse
-// with the same receiver template, only one call can contain text
-// other than space, comments, and template definitions.)
-func (t *Template) Parse(src string) (*Template, error) {
- t.nameSpace.mu.Lock()
- t.escapeErr = nil
- t.nameSpace.mu.Unlock()
- ret, err := t.text.Parse(src)
+// Parse parses text as a template body for t.
+// Named template definitions ({{define ...}} or {{block ...}} statements) in text
+// define additional templates associated with t and are removed from the
+// definition of t itself.
+//
+// Templates can be redefined in successive calls to Parse,
+// before the first use of Execute on t or any associated template.
+// A template definition with a body containing only white space and comments
+// is considered empty and will not replace an existing template's body.
+// This allows using Parse to add new named template definitions without
+// overwriting the main template body.
+func (t *Template) Parse(text string) (*Template, error) {
+ if err := t.checkCanParse(); err != nil {
+ return nil, err
+ }
+
+ ret, err := t.text.Parse(text)
if err != nil {
return nil, err
}
+
// In general, all the named templates might have changed underfoot.
// Regardless, some new ones may have been defined.
// The template.Template set has been updated; update ours.
@@ -176,11 +198,7 @@ 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
tmpl.text = v
tmpl.Tree = v.Tree
}
@@ -190,13 +208,14 @@ func (t *Template) Parse(src string) (*Template, error) {
// 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.
+// It returns an error if t or any associated template has already been executed.
func (t *Template) AddParseTree(name string, tree *parse.Tree) (*Template, error) {
+ if err := t.checkCanParse(); err != nil {
+ return nil, err
+ }
+
t.nameSpace.mu.Lock()
defer t.nameSpace.mu.Unlock()
- if t.escapeErr != nil {
- 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
@@ -252,7 +271,8 @@ func (t *Template) Clone() (*Template, error) {
ret.nameSpace,
}
}
- return ret, nil
+ // Return the template associated with the name of this template.
+ return ret.set[ret.Name()], nil
}
// New allocates a new HTML template with the given name.
@@ -361,6 +381,8 @@ func ParseFiles(filenames ...string) (*Template, error) {
//
// When parsing multiple files with the same name in different directories,
// the last one mentioned will be the one that results.
+//
+// ParseFiles returns an error if t or any associated template has already been executed.
func (t *Template) ParseFiles(filenames ...string) (*Template, error) {
return parseFiles(t, filenames...)
}
@@ -368,6 +390,10 @@ func (t *Template) ParseFiles(filenames ...string) (*Template, error) {
// parseFiles is the helper for the method and function. If the argument
// template is nil, it is created from the first file.
func parseFiles(t *Template, filenames ...string) (*Template, error) {
+ if err := t.checkCanParse(); err != nil {
+ return nil, err
+ }
+
if len(filenames) == 0 {
// Not really a problem, but be consistent.
return nil, fmt.Errorf("html/template: no files named in call to ParseFiles")
@@ -422,12 +448,17 @@ func ParseGlob(pattern string) (*Template, error) {
//
// When parsing multiple files with the same name in different directories,
// the last one mentioned will be the one that results.
+//
+// ParseGlob returns an error if t or any associated template has already been executed.
func (t *Template) ParseGlob(pattern string) (*Template, error) {
return parseGlob(t, pattern)
}
// parseGlob is the implementation of the function and method ParseGlob.
func parseGlob(t *Template, pattern string) (*Template, error) {
+ if err := t.checkCanParse(); err != nil {
+ return nil, err
+ }
filenames, err := filepath.Glob(pattern)
if err != nil {
return nil, err
diff --git a/libgo/go/html/template/template_test.go b/libgo/go/html/template/template_test.go
index 46df1f8..90c5a73 100644
--- a/libgo/go/html/template/template_test.go
+++ b/libgo/go/html/template/template_test.go
@@ -1,7 +1,13 @@
-package template
+// Copyright 2016 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_test
import (
"bytes"
+ . "html/template"
+ "strings"
"testing"
)
@@ -27,3 +33,125 @@ func TestTemplateClone(t *testing.T) {
t.Fatalf("got %q; want %q", got, want)
}
}
+
+func TestRedefineNonEmptyAfterExecution(t *testing.T) {
+ c := newTestCase(t)
+ c.mustParse(c.root, `foo`)
+ c.mustExecute(c.root, nil, "foo")
+ c.mustNotParse(c.root, `bar`)
+}
+
+func TestRedefineEmptyAfterExecution(t *testing.T) {
+ c := newTestCase(t)
+ c.mustParse(c.root, ``)
+ c.mustExecute(c.root, nil, "")
+ c.mustNotParse(c.root, `foo`)
+ c.mustExecute(c.root, nil, "")
+}
+
+func TestRedefineAfterNonExecution(t *testing.T) {
+ c := newTestCase(t)
+ c.mustParse(c.root, `{{if .}}<{{template "X"}}>{{end}}{{define "X"}}foo{{end}}`)
+ c.mustExecute(c.root, 0, "")
+ c.mustNotParse(c.root, `{{define "X"}}bar{{end}}`)
+ c.mustExecute(c.root, 1, "&lt;foo>")
+}
+
+func TestRedefineAfterNamedExecution(t *testing.T) {
+ c := newTestCase(t)
+ c.mustParse(c.root, `<{{template "X" .}}>{{define "X"}}foo{{end}}`)
+ c.mustExecute(c.root, nil, "&lt;foo>")
+ c.mustNotParse(c.root, `{{define "X"}}bar{{end}}`)
+ c.mustExecute(c.root, nil, "&lt;foo>")
+}
+
+func TestRedefineNestedByNameAfterExecution(t *testing.T) {
+ c := newTestCase(t)
+ c.mustParse(c.root, `{{define "X"}}foo{{end}}`)
+ c.mustExecute(c.lookup("X"), nil, "foo")
+ c.mustNotParse(c.root, `{{define "X"}}bar{{end}}`)
+ c.mustExecute(c.lookup("X"), nil, "foo")
+}
+
+func TestRedefineNestedByTemplateAfterExecution(t *testing.T) {
+ c := newTestCase(t)
+ c.mustParse(c.root, `{{define "X"}}foo{{end}}`)
+ c.mustExecute(c.lookup("X"), nil, "foo")
+ c.mustNotParse(c.lookup("X"), `bar`)
+ c.mustExecute(c.lookup("X"), nil, "foo")
+}
+
+func TestRedefineSafety(t *testing.T) {
+ c := newTestCase(t)
+ c.mustParse(c.root, `<html><a href="{{template "X"}}">{{define "X"}}{{end}}`)
+ c.mustExecute(c.root, nil, `<html><a href="">`)
+ // Note: Every version of Go prior to Go 1.8 accepted the redefinition of "X"
+ // on the next line, but luckily kept it from being used in the outer template.
+ // Now we reject it, which makes clearer that we're not going to use it.
+ c.mustNotParse(c.root, `{{define "X"}}" bar="baz{{end}}`)
+ c.mustExecute(c.root, nil, `<html><a href="">`)
+}
+
+func TestRedefineTopUse(t *testing.T) {
+ c := newTestCase(t)
+ c.mustParse(c.root, `{{template "X"}}{{.}}{{define "X"}}{{end}}`)
+ c.mustExecute(c.root, 42, `42`)
+ c.mustNotParse(c.root, `{{define "X"}}<script>{{end}}`)
+ c.mustExecute(c.root, 42, `42`)
+}
+
+func TestRedefineOtherParsers(t *testing.T) {
+ c := newTestCase(t)
+ c.mustParse(c.root, ``)
+ c.mustExecute(c.root, nil, ``)
+ if _, err := c.root.ParseFiles("no.template"); err == nil || !strings.Contains(err.Error(), "Execute") {
+ t.Errorf("ParseFiles: %v\nwanted error about already having Executed", err)
+ }
+ if _, err := c.root.ParseGlob("*.no.template"); err == nil || !strings.Contains(err.Error(), "Execute") {
+ t.Errorf("ParseGlob: %v\nwanted error about already having Executed", err)
+ }
+ if _, err := c.root.AddParseTree("t1", c.root.Tree); err == nil || !strings.Contains(err.Error(), "Execute") {
+ t.Errorf("AddParseTree: %v\nwanted error about already having Executed", err)
+ }
+}
+
+type testCase struct {
+ t *testing.T
+ root *Template
+}
+
+func newTestCase(t *testing.T) *testCase {
+ return &testCase{
+ t: t,
+ root: New("root"),
+ }
+}
+
+func (c *testCase) lookup(name string) *Template {
+ return c.root.Lookup(name)
+}
+
+func (c *testCase) mustParse(t *Template, text string) {
+ _, err := t.Parse(text)
+ if err != nil {
+ c.t.Fatalf("parse: %v", err)
+ }
+}
+
+func (c *testCase) mustNotParse(t *Template, text string) {
+ _, err := t.Parse(text)
+ if err == nil {
+ c.t.Fatalf("parse: unexpected success")
+ }
+}
+
+func (c *testCase) mustExecute(t *Template, val interface{}, want string) {
+ var buf bytes.Buffer
+ err := t.Execute(&buf, val)
+ if err != nil {
+ c.t.Fatalf("execute: %v", err)
+ }
+ if buf.String() != want {
+ c.t.Fatalf("template output:\n%s\nwant:\n%s", buf.String(), want)
+ }
+}
diff --git a/libgo/go/html/template/transition.go b/libgo/go/html/template/transition.go
index aefe035..4a4716d 100644
--- a/libgo/go/html/template/transition.go
+++ b/libgo/go/html/template/transition.go
@@ -105,14 +105,21 @@ func tTag(c context, s []byte) (context, int) {
err: errorf(ErrBadHTML, nil, 0, "expected space, attr name, or end of tag, but got %q", s[i:]),
}, len(s)
}
- switch attrType(string(s[i:j])) {
- case contentTypeURL:
- attr = attrURL
- case contentTypeCSS:
- attr = attrStyle
- case contentTypeJS:
- attr = attrScript
+
+ attrName := string(s[i:j])
+ if c.element == elementScript && attrName == "type" {
+ attr = attrScriptType
+ } else {
+ switch attrType(attrName) {
+ case contentTypeURL:
+ attr = attrURL
+ case contentTypeCSS:
+ attr = attrStyle
+ case contentTypeJS:
+ attr = attrScript
+ }
}
+
if j == len(s) {
state = stateAttrName
} else {
@@ -149,10 +156,11 @@ func tAfterName(c context, s []byte) (context, int) {
}
var attrStartStates = [...]state{
- attrNone: stateAttr,
- attrScript: stateJS,
- attrStyle: stateCSS,
- attrURL: stateURL,
+ attrNone: stateAttr,
+ attrScript: stateJS,
+ attrScriptType: stateAttr,
+ attrStyle: stateCSS,
+ attrURL: stateURL,
}
// tBeforeValue is the context transition function for stateBeforeValue.
diff --git a/libgo/go/html/template/url.go b/libgo/go/html/template/url.go
index 246bfd3..02123b2 100644
--- a/libgo/go/html/template/url.go
+++ b/libgo/go/html/template/url.go
@@ -32,7 +32,7 @@ func urlEscaper(args ...interface{}) string {
return urlProcessor(false, args...)
}
-// urlEscaper normalizes URL content so it can be embedded in a quote-delimited
+// urlNormalizer normalizes URL content so it can be embedded in a quote-delimited
// string or parenthesis delimited url(...).
// The normalizer does not encode all HTML specials. Specifically, it does not
// encode '&' so correct embedding in an HTML attribute requires escaping of