aboutsummaryrefslogtreecommitdiff
path: root/src/html/template
diff options
context:
space:
mode:
Diffstat (limited to 'src/html/template')
-rw-r--r--src/html/template/context.go23
-rw-r--r--src/html/template/escape_test.go14
-rw-r--r--src/html/template/transition.go30
3 files changed, 37 insertions, 30 deletions
diff --git a/src/html/template/context.go b/src/html/template/context.go
index 63d5c31b01..b78f0f7325 100644
--- a/src/html/template/context.go
+++ b/src/html/template/context.go
@@ -17,16 +17,19 @@ import (
// https://www.w3.org/TR/html5/syntax.html#the-end
// where the context element is null.
type context struct {
- state state
- delim delim
- urlPart urlPart
- jsCtx jsCtx
- jsTmplExprDepth int
- jsBraceDepth int
- attr attr
- element element
- n parse.Node // for range break/continue
- err *Error
+ state state
+ delim delim
+ urlPart urlPart
+ jsCtx jsCtx
+ // jsBraceDepth contains the current depth, for each JS template literal
+ // string interpolation expression, of braces we've seen. This is used to
+ // determine if the next } will close a JS template literal string
+ // interpolation expression or not.
+ jsBraceDepth []int
+ attr attr
+ element element
+ n parse.Node // for range break/continue
+ err *Error
}
func (c context) String() string {
diff --git a/src/html/template/escape_test.go b/src/html/template/escape_test.go
index 91fbfb9a3c..497ead8716 100644
--- a/src/html/template/escape_test.go
+++ b/src/html/template/escape_test.go
@@ -1797,13 +1797,25 @@ func TestEscapeText(t *testing.T) {
context{state: stateJS, element: elementScript, jsCtx: jsCtxDivOp},
},
{
- "<script>`${ { `` }`",
+ "<script>`${ { `` }",
context{state: stateJS, element: elementScript},
},
{
"<script>`${ { }`",
context{state: stateJSTmplLit, element: elementScript},
},
+ {
+ "<script>var foo = `${ foo({ a: { c: `${",
+ context{state: stateJS, element: elementScript},
+ },
+ {
+ "<script>var foo = `${ foo({ a: { c: `${ {{.}} }` }, b: ",
+ context{state: stateJS, element: elementScript},
+ },
+ {
+ "<script>`${ `}",
+ context{state: stateJSTmplLit, element: elementScript},
+ },
}
for _, test := range tests {
diff --git a/src/html/template/transition.go b/src/html/template/transition.go
index 4aa2920986..d5a05f66da 100644
--- a/src/html/template/transition.go
+++ b/src/html/template/transition.go
@@ -323,29 +323,23 @@ func tJS(c context, s []byte) (context, int) {
case '{':
// We only care about tracking brace depth if we are inside of a
// template literal.
- if c.jsTmplExprDepth == 0 {
+ if len(c.jsBraceDepth) == 0 {
return c, i + 1
}
- c.jsBraceDepth++
+ c.jsBraceDepth[len(c.jsBraceDepth)-1]++
case '}':
- if c.jsTmplExprDepth == 0 {
+ if len(c.jsBraceDepth) == 0 {
return c, i + 1
}
- for j := 0; j <= i; j++ {
- switch s[j] {
- case '\\':
- j++
- case '{':
- c.jsBraceDepth++
- case '}':
- c.jsBraceDepth--
- }
- }
- if c.jsBraceDepth >= 0 {
+ // There are no cases where a brace can be escaped in the JS context
+ // that are not syntax errors, it seems. Because of this we can just
+ // count "\}" as "}" and move on, the script is already broken as
+ // fully fledged parsers will just fail anyway.
+ c.jsBraceDepth[len(c.jsBraceDepth)-1]--
+ if c.jsBraceDepth[len(c.jsBraceDepth)-1] >= 0 {
return c, i + 1
}
- c.jsTmplExprDepth--
- c.jsBraceDepth = 0
+ c.jsBraceDepth = c.jsBraceDepth[:len(c.jsBraceDepth)-1]
c.state = stateJSTmplLit
default:
panic("unreachable")
@@ -354,7 +348,6 @@ func tJS(c context, s []byte) (context, int) {
}
func tJSTmpl(c context, s []byte) (context, int) {
- c.jsBraceDepth = 0
var k int
for {
i := k + bytes.IndexAny(s[k:], "`\\$")
@@ -372,8 +365,7 @@ func tJSTmpl(c context, s []byte) (context, int) {
}
case '$':
if len(s) >= i+2 && s[i+1] == '{' {
- c.jsTmplExprDepth++
- c.jsBraceDepth = 0
+ c.jsBraceDepth = append(c.jsBraceDepth, 0)
c.state = stateJS
return c, i + 2
}