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.
11 // context describes the state an HTML parser must be in when it reaches the
12 // portion of HTML produced by evaluating a particular template node.
14 // The zero value of type context is the start context for a template that
15 // produces an HTML fragment as defined at
16 // http://www.w3.org/TR/html5/syntax.html#the-end
17 // where the context element is null.
28 func (c context
) String() string {
29 return fmt
.Sprintf("{%v %v %v %v %v %v %v}", c
.state
, c
.delim
, c
.urlPart
, c
.jsCtx
, c
.attr
, c
.element
, c
.err
)
32 // eq reports whether two contexts are equal.
33 func (c context
) eq(d context
) bool {
34 return c
.state
== d
.state
&&
36 c
.urlPart
== d
.urlPart
&&
39 c
.element
== d
.element
&&
43 // mangle produces an identifier that includes a suffix that distinguishes it
44 // from template names mangled with different contexts.
45 func (c context
) mangle(templateName
string) string {
46 // The mangled name for the default context is the input templateName.
47 if c
.state
== stateText
{
50 s
:= templateName
+ "$htmltemplate_" + c
.state
.String()
52 s
+= "_" + c
.delim
.String()
55 s
+= "_" + c
.urlPart
.String()
58 s
+= "_" + c
.jsCtx
.String()
61 s
+= "_" + c
.attr
.String()
64 s
+= "_" + c
.element
.String()
69 // state describes a high-level HTML parser state.
71 // It bounds the top of the element stack, and by extension the HTML insertion
72 // mode, but also contains state that does not correspond to anything in the
73 // HTML5 parsing algorithm because a single token production in the HTML
74 // grammar may contain embedded actions in a template. For instance, the quoted
75 // HTML attribute produced by
76 // <div title="Hello {{.World}}">
77 // is a single token in HTML's grammar but in a template spans several nodes.
81 // stateText is parsed character data. An HTML parser is in
82 // this state when its parse position is outside an HTML tag,
83 // directive, comment, and special element body.
84 stateText state
= iota
85 // stateTag occurs before an HTML attribute or the end of a tag.
87 // stateAttrName occurs inside an attribute name.
88 // It occurs between the ^'s in ` ^name^ = value`.
90 // stateAfterName occurs after an attr name has ended but before any
91 // equals sign. It occurs between the ^'s in ` name^ ^= value`.
93 // stateBeforeValue occurs after the equals sign but before the value.
94 // It occurs between the ^'s in ` name =^ ^value`.
96 // stateHTMLCmt occurs inside an <!-- HTML comment -->.
98 // stateRCDATA occurs inside an RCDATA element (<textarea> or <title>)
99 // as described at http://www.w3.org/TR/html5/syntax.html#elements-0
101 // stateAttr occurs inside an HTML attribute whose content is text.
103 // stateURL occurs inside an HTML attribute whose content is a URL.
105 // stateSrcset occurs inside an HTML srcset attribute.
107 // stateJS occurs inside an event handler or script element.
109 // stateJSDqStr occurs inside a JavaScript double quoted string.
111 // stateJSSqStr occurs inside a JavaScript single quoted string.
113 // stateJSRegexp occurs inside a JavaScript regexp literal.
115 // stateJSBlockCmt occurs inside a JavaScript /* block comment */.
117 // stateJSLineCmt occurs inside a JavaScript // line comment.
119 // stateCSS occurs inside a <style> element or style attribute.
121 // stateCSSDqStr occurs inside a CSS double quoted string.
123 // stateCSSSqStr occurs inside a CSS single quoted string.
125 // stateCSSDqURL occurs inside a CSS double quoted url("...").
127 // stateCSSSqURL occurs inside a CSS single quoted url('...').
129 // stateCSSURL occurs inside a CSS unquoted url(...).
131 // stateCSSBlockCmt occurs inside a CSS /* block comment */.
133 // stateCSSLineCmt occurs inside a CSS // line comment.
135 // stateError is an infectious error state outside any valid
136 // HTML/CSS/JS construct.
140 var stateNames
= [...]string{
141 stateText
: "stateText",
142 stateTag
: "stateTag",
143 stateAttrName
: "stateAttrName",
144 stateAfterName
: "stateAfterName",
145 stateBeforeValue
: "stateBeforeValue",
146 stateHTMLCmt
: "stateHTMLCmt",
147 stateRCDATA
: "stateRCDATA",
148 stateAttr
: "stateAttr",
149 stateURL
: "stateURL",
150 stateSrcset
: "stateSrcset",
152 stateJSDqStr
: "stateJSDqStr",
153 stateJSSqStr
: "stateJSSqStr",
154 stateJSRegexp
: "stateJSRegexp",
155 stateJSBlockCmt
: "stateJSBlockCmt",
156 stateJSLineCmt
: "stateJSLineCmt",
157 stateCSS
: "stateCSS",
158 stateCSSDqStr
: "stateCSSDqStr",
159 stateCSSSqStr
: "stateCSSSqStr",
160 stateCSSDqURL
: "stateCSSDqURL",
161 stateCSSSqURL
: "stateCSSSqURL",
162 stateCSSURL
: "stateCSSURL",
163 stateCSSBlockCmt
: "stateCSSBlockCmt",
164 stateCSSLineCmt
: "stateCSSLineCmt",
165 stateError
: "stateError",
168 func (s state
) String() string {
169 if int(s
) < len(stateNames
) {
172 return fmt
.Sprintf("illegal state %d", int(s
))
175 // isComment is true for any state that contains content meant for template
176 // authors & maintainers, not for end-users or machines.
177 func isComment(s state
) bool {
179 case stateHTMLCmt
, stateJSBlockCmt
, stateJSLineCmt
, stateCSSBlockCmt
, stateCSSLineCmt
:
185 // isInTag return whether s occurs solely inside an HTML tag.
186 func isInTag(s state
) bool {
188 case stateTag
, stateAttrName
, stateAfterName
, stateBeforeValue
, stateAttr
:
194 // delim is the delimiter that will end the current HTML attribute.
198 // delimNone occurs outside any attribute.
199 delimNone delim
= iota
200 // delimDoubleQuote occurs when a double quote (") closes the attribute.
202 // delimSingleQuote occurs when a single quote (') closes the attribute.
204 // delimSpaceOrTagEnd occurs when a space or right angle bracket (>)
205 // closes the attribute.
209 var delimNames
= [...]string{
210 delimNone
: "delimNone",
211 delimDoubleQuote
: "delimDoubleQuote",
212 delimSingleQuote
: "delimSingleQuote",
213 delimSpaceOrTagEnd
: "delimSpaceOrTagEnd",
216 func (d delim
) String() string {
217 if int(d
) < len(delimNames
) {
220 return fmt
.Sprintf("illegal delim %d", int(d
))
223 // urlPart identifies a part in an RFC 3986 hierarchical URL to allow different
224 // encoding strategies.
228 // urlPartNone occurs when not in a URL, or possibly at the start:
229 // ^ in "^http://auth/path?k=v#frag".
230 urlPartNone urlPart
= iota
231 // urlPartPreQuery occurs in the scheme, authority, or path; between the
232 // ^s in "h^ttp://auth/path^?k=v#frag".
234 // urlPartQueryOrFrag occurs in the query portion between the ^s in
235 // "http://auth/path?^k=v#frag^".
237 // urlPartUnknown occurs due to joining of contexts both before and
238 // after the query separator.
242 var urlPartNames
= [...]string{
243 urlPartNone
: "urlPartNone",
244 urlPartPreQuery
: "urlPartPreQuery",
245 urlPartQueryOrFrag
: "urlPartQueryOrFrag",
246 urlPartUnknown
: "urlPartUnknown",
249 func (u urlPart
) String() string {
250 if int(u
) < len(urlPartNames
) {
251 return urlPartNames
[u
]
253 return fmt
.Sprintf("illegal urlPart %d", int(u
))
256 // jsCtx determines whether a '/' starts a regular expression literal or a
257 // division operator.
261 // jsCtxRegexp occurs where a '/' would start a regexp literal.
262 jsCtxRegexp jsCtx
= iota
263 // jsCtxDivOp occurs where a '/' would start a division operator.
265 // jsCtxUnknown occurs where a '/' is ambiguous due to context joining.
269 func (c jsCtx
) String() string {
276 return "jsCtxUnknown"
278 return fmt
.Sprintf("illegal jsCtx %d", int(c
))
281 // element identifies the HTML element when inside a start tag or special body.
282 // Certain HTML element (for example <script> and <style>) have bodies that are
283 // treated differently from stateText so the element type is necessary to
284 // transition into the correct context at the end of a tag and to identify the
285 // end delimiter for the body.
289 // elementNone occurs outside a special tag or special element body.
290 elementNone element
= iota
291 // elementScript corresponds to the raw text <script> element
292 // with JS MIME type or no type attribute.
294 // elementStyle corresponds to the raw text <style> element.
296 // elementTextarea corresponds to the RCDATA <textarea> element.
298 // elementTitle corresponds to the RCDATA <title> element.
302 var elementNames
= [...]string{
303 elementNone
: "elementNone",
304 elementScript
: "elementScript",
305 elementStyle
: "elementStyle",
306 elementTextarea
: "elementTextarea",
307 elementTitle
: "elementTitle",
310 func (e element
) String() string {
311 if int(e
) < len(elementNames
) {
312 return elementNames
[e
]
314 return fmt
.Sprintf("illegal element %d", int(e
))
317 // attr identifies the current HTML attribute when inside the attribute,
318 // that is, starting from stateAttrName until stateTag/stateText (exclusive).
322 // attrNone corresponds to a normal attribute or no attribute.
324 // attrScript corresponds to an event handler attribute.
326 // attrScriptType corresponds to the type attribute in script HTML element
328 // attrStyle corresponds to the style attribute whose value is CSS.
330 // attrURL corresponds to an attribute whose value is a URL.
332 // attrSrcset corresponds to a srcset attribute.
336 var attrNames
= [...]string{
337 attrNone
: "attrNone",
338 attrScript
: "attrScript",
339 attrScriptType
: "attrScriptType",
340 attrStyle
: "attrStyle",
342 attrSrcset
: "attrSrcset",
345 func (a attr
) String() string {
346 if int(a
) < len(attrNames
) {
349 return fmt
.Sprintf("illegal attr %d", int(a
))