1// Copyright 2011 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package template
6
7import (
8	"fmt"
9	"text/template/parse"
10)
11
12// context describes the state an HTML parser must be in when it reaches the
13// portion of HTML produced by evaluating a particular template node.
14//
15// The zero value of type context is the start context for a template that
16// produces an HTML fragment as defined at
17// https://www.w3.org/TR/html5/syntax.html#the-end
18// where the context element is null.
19type context struct {
20	state   state
21	delim   delim
22	urlPart urlPart
23	jsCtx   jsCtx
24	attr    attr
25	element element
26	n       parse.Node // for range break/continue
27	err     *Error
28}
29
30func (c context) String() string {
31	var err error
32	if c.err != nil {
33		err = c.err
34	}
35	return fmt.Sprintf("{%v %v %v %v %v %v %v}", c.state, c.delim, c.urlPart, c.jsCtx, c.attr, c.element, err)
36}
37
38// eq reports whether two contexts are equal.
39func (c context) eq(d context) bool {
40	return c.state == d.state &&
41		c.delim == d.delim &&
42		c.urlPart == d.urlPart &&
43		c.jsCtx == d.jsCtx &&
44		c.attr == d.attr &&
45		c.element == d.element &&
46		c.err == d.err
47}
48
49// mangle produces an identifier that includes a suffix that distinguishes it
50// from template names mangled with different contexts.
51func (c context) mangle(templateName string) string {
52	// The mangled name for the default context is the input templateName.
53	if c.state == stateText {
54		return templateName
55	}
56	s := templateName + "$htmltemplate_" + c.state.String()
57	if c.delim != delimNone {
58		s += "_" + c.delim.String()
59	}
60	if c.urlPart != urlPartNone {
61		s += "_" + c.urlPart.String()
62	}
63	if c.jsCtx != jsCtxRegexp {
64		s += "_" + c.jsCtx.String()
65	}
66	if c.attr != attrNone {
67		s += "_" + c.attr.String()
68	}
69	if c.element != elementNone {
70		s += "_" + c.element.String()
71	}
72	return s
73}
74
75// state describes a high-level HTML parser state.
76//
77// It bounds the top of the element stack, and by extension the HTML insertion
78// mode, but also contains state that does not correspond to anything in the
79// HTML5 parsing algorithm because a single token production in the HTML
80// grammar may contain embedded actions in a template. For instance, the quoted
81// HTML attribute produced by
82//     <div title="Hello {{.World}}">
83// is a single token in HTML's grammar but in a template spans several nodes.
84type state uint8
85
86//go:generate stringer -type state
87
88const (
89	// stateText is parsed character data. An HTML parser is in
90	// this state when its parse position is outside an HTML tag,
91	// directive, comment, and special element body.
92	stateText state = iota
93	// stateTag occurs before an HTML attribute or the end of a tag.
94	stateTag
95	// stateAttrName occurs inside an attribute name.
96	// It occurs between the ^'s in ` ^name^ = value`.
97	stateAttrName
98	// stateAfterName occurs after an attr name has ended but before any
99	// equals sign. It occurs between the ^'s in ` name^ ^= value`.
100	stateAfterName
101	// stateBeforeValue occurs after the equals sign but before the value.
102	// It occurs between the ^'s in ` name =^ ^value`.
103	stateBeforeValue
104	// stateHTMLCmt occurs inside an <!-- HTML comment -->.
105	stateHTMLCmt
106	// stateRCDATA occurs inside an RCDATA element (<textarea> or <title>)
107	// as described at https://www.w3.org/TR/html5/syntax.html#elements-0
108	stateRCDATA
109	// stateAttr occurs inside an HTML attribute whose content is text.
110	stateAttr
111	// stateURL occurs inside an HTML attribute whose content is a URL.
112	stateURL
113	// stateSrcset occurs inside an HTML srcset attribute.
114	stateSrcset
115	// stateJS occurs inside an event handler or script element.
116	stateJS
117	// stateJSDqStr occurs inside a JavaScript double quoted string.
118	stateJSDqStr
119	// stateJSSqStr occurs inside a JavaScript single quoted string.
120	stateJSSqStr
121	// stateJSRegexp occurs inside a JavaScript regexp literal.
122	stateJSRegexp
123	// stateJSBlockCmt occurs inside a JavaScript /* block comment */.
124	stateJSBlockCmt
125	// stateJSLineCmt occurs inside a JavaScript // line comment.
126	stateJSLineCmt
127	// stateCSS occurs inside a <style> element or style attribute.
128	stateCSS
129	// stateCSSDqStr occurs inside a CSS double quoted string.
130	stateCSSDqStr
131	// stateCSSSqStr occurs inside a CSS single quoted string.
132	stateCSSSqStr
133	// stateCSSDqURL occurs inside a CSS double quoted url("...").
134	stateCSSDqURL
135	// stateCSSSqURL occurs inside a CSS single quoted url('...').
136	stateCSSSqURL
137	// stateCSSURL occurs inside a CSS unquoted url(...).
138	stateCSSURL
139	// stateCSSBlockCmt occurs inside a CSS /* block comment */.
140	stateCSSBlockCmt
141	// stateCSSLineCmt occurs inside a CSS // line comment.
142	stateCSSLineCmt
143	// stateError is an infectious error state outside any valid
144	// HTML/CSS/JS construct.
145	stateError
146	// stateDead marks unreachable code after a {{break}} or {{continue}}.
147	stateDead
148)
149
150// isComment is true for any state that contains content meant for template
151// authors & maintainers, not for end-users or machines.
152func isComment(s state) bool {
153	switch s {
154	case stateHTMLCmt, stateJSBlockCmt, stateJSLineCmt, stateCSSBlockCmt, stateCSSLineCmt:
155		return true
156	}
157	return false
158}
159
160// isInTag return whether s occurs solely inside an HTML tag.
161func isInTag(s state) bool {
162	switch s {
163	case stateTag, stateAttrName, stateAfterName, stateBeforeValue, stateAttr:
164		return true
165	}
166	return false
167}
168
169// delim is the delimiter that will end the current HTML attribute.
170type delim uint8
171
172//go:generate stringer -type delim
173
174const (
175	// delimNone occurs outside any attribute.
176	delimNone delim = iota
177	// delimDoubleQuote occurs when a double quote (") closes the attribute.
178	delimDoubleQuote
179	// delimSingleQuote occurs when a single quote (') closes the attribute.
180	delimSingleQuote
181	// delimSpaceOrTagEnd occurs when a space or right angle bracket (>)
182	// closes the attribute.
183	delimSpaceOrTagEnd
184)
185
186// urlPart identifies a part in an RFC 3986 hierarchical URL to allow different
187// encoding strategies.
188type urlPart uint8
189
190//go:generate stringer -type urlPart
191
192const (
193	// urlPartNone occurs when not in a URL, or possibly at the start:
194	// ^ in "^http://auth/path?k=v#frag".
195	urlPartNone urlPart = iota
196	// urlPartPreQuery occurs in the scheme, authority, or path; between the
197	// ^s in "h^ttp://auth/path^?k=v#frag".
198	urlPartPreQuery
199	// urlPartQueryOrFrag occurs in the query portion between the ^s in
200	// "http://auth/path?^k=v#frag^".
201	urlPartQueryOrFrag
202	// urlPartUnknown occurs due to joining of contexts both before and
203	// after the query separator.
204	urlPartUnknown
205)
206
207// jsCtx determines whether a '/' starts a regular expression literal or a
208// division operator.
209type jsCtx uint8
210
211//go:generate stringer -type jsCtx
212
213const (
214	// jsCtxRegexp occurs where a '/' would start a regexp literal.
215	jsCtxRegexp jsCtx = iota
216	// jsCtxDivOp occurs where a '/' would start a division operator.
217	jsCtxDivOp
218	// jsCtxUnknown occurs where a '/' is ambiguous due to context joining.
219	jsCtxUnknown
220)
221
222// element identifies the HTML element when inside a start tag or special body.
223// Certain HTML element (for example <script> and <style>) have bodies that are
224// treated differently from stateText so the element type is necessary to
225// transition into the correct context at the end of a tag and to identify the
226// end delimiter for the body.
227type element uint8
228
229//go:generate stringer -type element
230
231const (
232	// elementNone occurs outside a special tag or special element body.
233	elementNone element = iota
234	// elementScript corresponds to the raw text <script> element
235	// with JS MIME type or no type attribute.
236	elementScript
237	// elementStyle corresponds to the raw text <style> element.
238	elementStyle
239	// elementTextarea corresponds to the RCDATA <textarea> element.
240	elementTextarea
241	// elementTitle corresponds to the RCDATA <title> element.
242	elementTitle
243)
244
245//go:generate stringer -type attr
246
247// attr identifies the current HTML attribute when inside the attribute,
248// that is, starting from stateAttrName until stateTag/stateText (exclusive).
249type attr uint8
250
251const (
252	// attrNone corresponds to a normal attribute or no attribute.
253	attrNone attr = iota
254	// attrScript corresponds to an event handler attribute.
255	attrScript
256	// attrScriptType corresponds to the type attribute in script HTML element
257	attrScriptType
258	// attrStyle corresponds to the style attribute whose value is CSS.
259	attrStyle
260	// attrURL corresponds to an attribute whose value is a URL.
261	attrURL
262	// attrSrcset corresponds to a srcset attribute.
263	attrSrcset
264)
265