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/the-end.html#parsing-html-fragments
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 returns 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://dev.w3.org/html5/spec/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 // stateJS occurs inside an event handler or script element.
107 // stateJSDqStr occurs inside a JavaScript double quoted string.
109 // stateJSSqStr occurs inside a JavaScript single quoted string.
111 // stateJSRegexp occurs inside a JavaScript regexp literal.
113 // stateJSBlockCmt occurs inside a JavaScript /* block comment */.
115 // stateJSLineCmt occurs inside a JavaScript // line comment.
117 // stateCSS occurs inside a <style> element or style attribute.
119 // stateCSSDqStr occurs inside a CSS double quoted string.
121 // stateCSSSqStr occurs inside a CSS single quoted string.
123 // stateCSSDqURL occurs inside a CSS double quoted url("...").
125 // stateCSSSqURL occurs inside a CSS single quoted url('...').
127 // stateCSSURL occurs inside a CSS unquoted url(...).
129 // stateCSSBlockCmt occurs inside a CSS /* block comment */.
131 // stateCSSLineCmt occurs inside a CSS // line comment.
133 // stateError is an infectious error state outside any valid
134 // HTML/CSS/JS construct.
138 var stateNames = [...]string{
139 stateText: "stateText",
140 stateTag: "stateTag",
141 stateAttrName: "stateAttrName",
142 stateAfterName: "stateAfterName",
143 stateBeforeValue: "stateBeforeValue",
144 stateHTMLCmt: "stateHTMLCmt",
145 stateRCDATA: "stateRCDATA",
146 stateAttr: "stateAttr",
147 stateURL: "stateURL",
149 stateJSDqStr: "stateJSDqStr",
150 stateJSSqStr: "stateJSSqStr",
151 stateJSRegexp: "stateJSRegexp",
152 stateJSBlockCmt: "stateJSBlockCmt",
153 stateJSLineCmt: "stateJSLineCmt",
154 stateCSS: "stateCSS",
155 stateCSSDqStr: "stateCSSDqStr",
156 stateCSSSqStr: "stateCSSSqStr",
157 stateCSSDqURL: "stateCSSDqURL",
158 stateCSSSqURL: "stateCSSSqURL",
159 stateCSSURL: "stateCSSURL",
160 stateCSSBlockCmt: "stateCSSBlockCmt",
161 stateCSSLineCmt: "stateCSSLineCmt",
162 stateError: "stateError",
165 func (s state) String() string {
166 if int(s) < len(stateNames) {
169 return fmt.Sprintf("illegal state %d", int(s))
172 // isComment is true for any state that contains content meant for template
173 // authors & maintainers, not for end-users or machines.
174 func isComment(s state) bool {
176 case stateHTMLCmt, stateJSBlockCmt, stateJSLineCmt, stateCSSBlockCmt, stateCSSLineCmt:
182 // isInTag return whether s occurs solely inside an HTML tag.
183 func isInTag(s state) bool {
185 case stateTag, stateAttrName, stateAfterName, stateBeforeValue, stateAttr:
191 // delim is the delimiter that will end the current HTML attribute.
195 // delimNone occurs outside any attribute.
196 delimNone delim = iota
197 // delimDoubleQuote occurs when a double quote (") closes the attribute.
199 // delimSingleQuote occurs when a single quote (') closes the attribute.
201 // delimSpaceOrTagEnd occurs when a space or right angle bracket (>)
202 // closes the attribute.
206 var delimNames = [...]string{
207 delimNone: "delimNone",
208 delimDoubleQuote: "delimDoubleQuote",
209 delimSingleQuote: "delimSingleQuote",
210 delimSpaceOrTagEnd: "delimSpaceOrTagEnd",
213 func (d delim) String() string {
214 if int(d) < len(delimNames) {
217 return fmt.Sprintf("illegal delim %d", int(d))
220 // urlPart identifies a part in an RFC 3986 hierarchical URL to allow different
221 // encoding strategies.
225 // urlPartNone occurs when not in a URL, or possibly at the start:
226 // ^ in "^http://auth/path?k=v#frag".
227 urlPartNone urlPart = iota
228 // urlPartPreQuery occurs in the scheme, authority, or path; between the
229 // ^s in "h^ttp://auth/path^?k=v#frag".
231 // urlPartQueryOrFrag occurs in the query portion between the ^s in
232 // "http://auth/path?^k=v#frag^".
234 // urlPartUnknown occurs due to joining of contexts both before and
235 // after the query separator.
239 var urlPartNames = [...]string{
240 urlPartNone: "urlPartNone",
241 urlPartPreQuery: "urlPartPreQuery",
242 urlPartQueryOrFrag: "urlPartQueryOrFrag",
243 urlPartUnknown: "urlPartUnknown",
246 func (u urlPart) String() string {
247 if int(u) < len(urlPartNames) {
248 return urlPartNames[u]
250 return fmt.Sprintf("illegal urlPart %d", int(u))
253 // jsCtx determines whether a '/' starts a regular expression literal or a
254 // division operator.
258 // jsCtxRegexp occurs where a '/' would start a regexp literal.
259 jsCtxRegexp jsCtx = iota
260 // jsCtxDivOp occurs where a '/' would start a division operator.
262 // jsCtxUnknown occurs where a '/' is ambiguous due to context joining.
266 func (c jsCtx) String() string {
273 return "jsCtxUnknown"
275 return fmt.Sprintf("illegal jsCtx %d", int(c))
278 // element identifies the HTML element when inside a start tag or special body.
279 // Certain HTML element (for example <script> and <style>) have bodies that are
280 // treated differently from stateText so the element type is necessary to
281 // transition into the correct context at the end of a tag and to identify the
282 // end delimiter for the body.
286 // elementNone occurs outside a special tag or special element body.
287 elementNone element = iota
288 // elementScript corresponds to the raw text <script> element.
290 // elementStyle corresponds to the raw text <style> element.
292 // elementTextarea corresponds to the RCDATA <textarea> element.
294 // elementTitle corresponds to the RCDATA <title> element.
298 var elementNames = [...]string{
299 elementNone: "elementNone",
300 elementScript: "elementScript",
301 elementStyle: "elementStyle",
302 elementTextarea: "elementTextarea",
303 elementTitle: "elementTitle",
306 func (e element) String() string {
307 if int(e) < len(elementNames) {
308 return elementNames[e]
310 return fmt.Sprintf("illegal element %d", int(e))
313 // attr identifies the most recent HTML attribute when inside a start tag.
317 // attrNone corresponds to a normal attribute or no attribute.
319 // attrScript corresponds to an event handler attribute.
321 // attrStyle corresponds to the style attribute whose value is CSS.
323 // attrURL corresponds to an attribute whose value is a URL.
327 var attrNames = [...]string{
328 attrNone: "attrNone",
329 attrScript: "attrScript",
330 attrStyle: "attrStyle",
334 func (a attr) String() string {
335 if int(a) < len(attrNames) {
338 return fmt.Sprintf("illegal attr %d", int(a))