diff options
Diffstat (limited to 'src/pkg/exp/template/html/context.go')
-rw-r--r-- | src/pkg/exp/template/html/context.go | 202 |
1 files changed, 182 insertions, 20 deletions
diff --git a/src/pkg/exp/template/html/context.go b/src/pkg/exp/template/html/context.go index 428b3d0b3..de073f134 100644 --- a/src/pkg/exp/template/html/context.go +++ b/src/pkg/exp/template/html/context.go @@ -20,13 +20,46 @@ type context struct { delim delim urlPart urlPart jsCtx jsCtx - errLine int - errStr string + attr attr + element element + err *Error } // eq returns whether two contexts are equal. func (c context) eq(d context) bool { - return c.state == d.state && c.delim == d.delim && c.urlPart == d.urlPart && c.jsCtx == d.jsCtx && c.errLine == d.errLine && c.errStr == d.errStr + return c.state == d.state && + c.delim == d.delim && + c.urlPart == d.urlPart && + c.jsCtx == d.jsCtx && + c.attr == d.attr && + c.element == d.element && + c.err == d.err +} + +// mangle produces an identifier that includes a suffix that distinguishes it +// from template names mangled with different contexts. +func (c context) mangle(templateName string) string { + // The mangled name for the default context is the input templateName. + if c.state == stateText { + return templateName + } + s := templateName + "$htmltemplate_" + c.state.String() + if c.delim != 0 { + s += "_" + c.delim.String() + } + if c.urlPart != 0 { + s += "_" + c.urlPart.String() + } + if c.jsCtx != 0 { + s += "_" + c.jsCtx.String() + } + if c.attr != 0 { + s += "_" + c.attr.String() + } + if c.element != 0 { + s += "_" + c.element.String() + } + return s } // state describes a high-level HTML parser state. @@ -47,6 +80,20 @@ const ( stateText state = iota // stateTag occurs before an HTML attribute or the end of a tag. stateTag + // stateAttrName occurs inside an attribute name. + // It occurs between the ^'s in ` ^name^ = value`. + stateAttrName + // stateAfterName occurs after an attr name has ended but before any + // equals sign. It occurs between the ^'s in ` name^ ^= value`. + stateAfterName + // stateBeforeValue occurs after the equals sign but before the value. + // It occurs between the ^'s in ` name =^ ^value`. + stateBeforeValue + // stateHTMLCmt occurs inside an <!-- HTML comment -->. + stateHTMLCmt + // stateRCDATA occurs inside an RCDATA element (<textarea> or <title>) + // as described at http://dev.w3.org/html5/spec/syntax.html#elements-0 + stateRCDATA // stateAttr occurs inside an HTML attribute whose content is text. stateAttr // stateURL occurs inside an HTML attribute whose content is a URL. @@ -63,30 +110,78 @@ const ( stateJSBlockCmt // stateJSLineCmt occurs inside a JavaScript // line comment. stateJSLineCmt + // stateCSS occurs inside a <style> element or style attribute. + stateCSS + // stateCSSDqStr occurs inside a CSS double quoted string. + stateCSSDqStr + // stateCSSSqStr occurs inside a CSS single quoted string. + stateCSSSqStr + // stateCSSDqURL occurs inside a CSS double quoted url("..."). + stateCSSDqURL + // stateCSSSqURL occurs inside a CSS single quoted url('...'). + stateCSSSqURL + // stateCSSURL occurs inside a CSS unquoted url(...). + stateCSSURL + // stateCSSBlockCmt occurs inside a CSS /* block comment */. + stateCSSBlockCmt + // stateCSSLineCmt occurs inside a CSS // line comment. + stateCSSLineCmt // stateError is an infectious error state outside any valid // HTML/CSS/JS construct. stateError ) var stateNames = [...]string{ - stateText: "stateText", - stateTag: "stateTag", - stateAttr: "stateAttr", - stateURL: "stateURL", - stateJS: "stateJS", - stateJSDqStr: "stateJSDqStr", - stateJSSqStr: "stateJSSqStr", - stateJSRegexp: "stateJSRegexp", - stateJSBlockCmt: "stateJSBlockCmt", - stateJSLineCmt: "stateJSLineCmt", - stateError: "stateError", + stateText: "stateText", + stateTag: "stateTag", + stateAttrName: "stateAttrName", + stateAfterName: "stateAfterName", + stateBeforeValue: "stateBeforeValue", + stateHTMLCmt: "stateHTMLCmt", + stateRCDATA: "stateRCDATA", + stateAttr: "stateAttr", + stateURL: "stateURL", + stateJS: "stateJS", + stateJSDqStr: "stateJSDqStr", + stateJSSqStr: "stateJSSqStr", + stateJSRegexp: "stateJSRegexp", + stateJSBlockCmt: "stateJSBlockCmt", + stateJSLineCmt: "stateJSLineCmt", + stateCSS: "stateCSS", + stateCSSDqStr: "stateCSSDqStr", + stateCSSSqStr: "stateCSSSqStr", + stateCSSDqURL: "stateCSSDqURL", + stateCSSSqURL: "stateCSSSqURL", + stateCSSURL: "stateCSSURL", + stateCSSBlockCmt: "stateCSSBlockCmt", + stateCSSLineCmt: "stateCSSLineCmt", + stateError: "stateError", } func (s state) String() string { if int(s) < len(stateNames) { return stateNames[s] } - return fmt.Sprintf("illegal state %d", s) + return fmt.Sprintf("illegal state %d", int(s)) +} + +// isComment is true for any state that contains content meant for template +// authors & maintainers, not for end-users or machines. +func isComment(s state) bool { + switch s { + case stateHTMLCmt, stateJSBlockCmt, stateJSLineCmt, stateCSSBlockCmt, stateCSSLineCmt: + return true + } + return false +} + +// isInTag return whether s occurs solely inside an HTML tag. +func isInTag(s state) bool { + switch s { + case stateTag, stateAttrName, stateAfterName, stateBeforeValue, stateAttr: + return true + } + return false } // delim is the delimiter that will end the current HTML attribute. @@ -115,7 +210,7 @@ func (d delim) String() string { if int(d) < len(delimNames) { return delimNames[d] } - return fmt.Sprintf("illegal delim %d", d) + return fmt.Sprintf("illegal delim %d", int(d)) } // urlPart identifies a part in an RFC 3986 hierarchical URL to allow different @@ -132,8 +227,8 @@ const ( // urlPartQueryOrFrag occurs in the query portion between the ^s in // "http://auth/path?^k=v#frag^". urlPartQueryOrFrag - // urlPartUnknown occurs due to joining of contexts both before and after - // the query separator. + // urlPartUnknown occurs due to joining of contexts both before and + // after the query separator. urlPartUnknown ) @@ -148,7 +243,7 @@ func (u urlPart) String() string { if int(u) < len(urlPartNames) { return urlPartNames[u] } - return fmt.Sprintf("illegal urlPart %d", u) + return fmt.Sprintf("illegal urlPart %d", int(u)) } // jsCtx determines whether a '/' starts a regular expression literal or a @@ -160,6 +255,8 @@ const ( jsCtxRegexp jsCtx = iota // jsCtxDivOp occurs where a '/' would start a division operator. jsCtxDivOp + // jsCtxUnknown occurs where a '/' is ambiguous due to context joining. + jsCtxUnknown ) func (c jsCtx) String() string { @@ -168,6 +265,71 @@ func (c jsCtx) String() string { return "jsCtxRegexp" case jsCtxDivOp: return "jsCtxDivOp" + case jsCtxUnknown: + return "jsCtxUnknown" + } + return fmt.Sprintf("illegal jsCtx %d", int(c)) +} + +// element identifies the HTML element when inside a start tag or special body. +// Certain HTML element (for example <script> and <style>) have bodies that are +// treated differently from stateText so the element type is necessary to +// transition into the correct context at the end of a tag and to identify the +// end delimiter for the body. +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 + // elementStyle corresponds to the raw text <style> element. + elementStyle + // elementTextarea corresponds to the RCDATA <textarea> element. + elementTextarea + // elementTitle corresponds to the RCDATA <title> element. + elementTitle +) + +var elementNames = [...]string{ + elementNone: "elementNone", + elementScript: "elementScript", + elementStyle: "elementStyle", + elementTextarea: "elementTextarea", + elementTitle: "elementTitle", +} + +func (e element) String() string { + if int(e) < len(elementNames) { + return elementNames[e] + } + return fmt.Sprintf("illegal element %d", int(e)) +} + +// attr identifies the most recent HTML attribute when inside a start tag. +type attr uint8 + +const ( + // attrNone corresponds to a normal attribute or no attribute. + attrNone attr = iota + // attrScript corresponds to an event handler attribute. + attrScript + // attrStyle corresponds to the style attribute whose value is CSS. + attrStyle + // attrURL corresponds to an attribute whose value is a URL. + attrURL +) + +var attrNames = [...]string{ + attrNone: "attrNone", + attrScript: "attrScript", + attrStyle: "attrStyle", + attrURL: "attrURL", +} + +func (a attr) String() string { + if int(a) < len(attrNames) { + return attrNames[a] } - return fmt.Sprintf("illegal jsCtx %d", c) + return fmt.Sprintf("illegal attr %d", int(a)) } |