...

Source file src/html/template/context.go

Documentation: html/template

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

View as plain text