mirror of
https://github.com/golang/go
synced 2024-11-22 07:44:43 -07:00
exp/template/html: change transition functions to return indices
Formulaic changes to transition functions in preparation for CL 5074041. This should be completely semantics preserving. R=nigeltao CC=golang-dev https://golang.org/cl/5091041
This commit is contained in:
parent
3c3a86ccc7
commit
3a013f1175
@ -547,22 +547,22 @@ var delimEnds = [...]string{
|
|||||||
|
|
||||||
// escapeText escapes a text template node.
|
// escapeText escapes a text template node.
|
||||||
func (e *escaper) escapeText(c context, n *parse.TextNode) context {
|
func (e *escaper) escapeText(c context, n *parse.TextNode) context {
|
||||||
s, written := n.Text, 0
|
s, written, i, b := n.Text, 0, 0, new(bytes.Buffer)
|
||||||
var b bytes.Buffer
|
for i != len(s) {
|
||||||
for len(s) > 0 {
|
c1, nread := contextAfterText(c, s[i:])
|
||||||
c1, s1 := contextAfterText(c, s)
|
i1 := i + nread
|
||||||
if c.state == c1.state && (c.state == stateText || c.state == stateRCDATA) {
|
if c.state == c1.state && (c.state == stateText || c.state == stateRCDATA) {
|
||||||
i0, i1 := len(n.Text)-len(s), len(n.Text)-len(s1)
|
for j := i; j < i1; j++ {
|
||||||
for i := i0; i < i1; i++ {
|
if s[j] == '<' {
|
||||||
if n.Text[i] == '<' {
|
b.Write(s[written:j])
|
||||||
b.Write(n.Text[written:i])
|
|
||||||
b.WriteString("<")
|
b.WriteString("<")
|
||||||
written = i + 1
|
written = j + 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
c, s = c1, s1
|
c, i = c1, i1
|
||||||
}
|
}
|
||||||
|
|
||||||
if written != 0 && c.state != stateError {
|
if written != 0 && c.state != stateError {
|
||||||
b.Write(n.Text[written:])
|
b.Write(n.Text[written:])
|
||||||
e.editTextNode(n, b.Bytes())
|
e.editTextNode(n, b.Bytes())
|
||||||
@ -572,7 +572,7 @@ func (e *escaper) escapeText(c context, n *parse.TextNode) context {
|
|||||||
|
|
||||||
// contextAfterText starts in context c, consumes some tokens from the front of
|
// contextAfterText starts in context c, consumes some tokens from the front of
|
||||||
// s, then returns the context after those tokens and the unprocessed suffix.
|
// s, then returns the context after those tokens and the unprocessed suffix.
|
||||||
func contextAfterText(c context, s []byte) (context, []byte) {
|
func contextAfterText(c context, s []byte) (context, int) {
|
||||||
if c.delim == delimNone {
|
if c.delim == delimNone {
|
||||||
return transitionFunc[c.state](c, s)
|
return transitionFunc[c.state](c, s)
|
||||||
}
|
}
|
||||||
@ -584,9 +584,10 @@ func contextAfterText(c context, s []byte) (context, []byte) {
|
|||||||
// <button onclick="alert("Hi!")">
|
// <button onclick="alert("Hi!")">
|
||||||
// without having to entity decode token boundaries.
|
// without having to entity decode token boundaries.
|
||||||
for u := []byte(html.UnescapeString(string(s))); len(u) != 0; {
|
for u := []byte(html.UnescapeString(string(s))); len(u) != 0; {
|
||||||
c, u = transitionFunc[c.state](c, u)
|
c1, i1 := transitionFunc[c.state](c, u)
|
||||||
|
c, u = c1, u[i1:]
|
||||||
}
|
}
|
||||||
return c, nil
|
return c, len(s)
|
||||||
}
|
}
|
||||||
if c.delim != delimSpaceOrTagEnd {
|
if c.delim != delimSpaceOrTagEnd {
|
||||||
// Consume any quote.
|
// Consume any quote.
|
||||||
@ -594,7 +595,7 @@ func contextAfterText(c context, s []byte) (context, []byte) {
|
|||||||
}
|
}
|
||||||
// On exiting an attribute, we discard all state information
|
// On exiting an attribute, we discard all state information
|
||||||
// except the state and element.
|
// except the state and element.
|
||||||
return context{state: stateTag, element: c.element}, s[i:]
|
return context{state: stateTag, element: c.element}, i
|
||||||
}
|
}
|
||||||
|
|
||||||
// editActionNode records a change to an action pipeline for later commit.
|
// editActionNode records a change to an action pipeline for later commit.
|
||||||
|
@ -165,43 +165,44 @@ func htmlReplacer(s string, replacementTable []string, badRunes bool) string {
|
|||||||
// For example, `<b>¡Hi!</b> <script>...</script>` -> `¡Hi! `.
|
// For example, `<b>¡Hi!</b> <script>...</script>` -> `¡Hi! `.
|
||||||
func stripTags(html string) string {
|
func stripTags(html string) string {
|
||||||
var b bytes.Buffer
|
var b bytes.Buffer
|
||||||
s, c := []byte(html), context{}
|
s, c, i := []byte(html), context{}, 0
|
||||||
// Using the transition funcs helps us avoid mangling
|
// Using the transition funcs helps us avoid mangling
|
||||||
// `<div title="1>2">` or `I <3 Ponies!`.
|
// `<div title="1>2">` or `I <3 Ponies!`.
|
||||||
for len(s) > 0 {
|
for i != len(s) {
|
||||||
if c.delim == delimNone {
|
if c.delim == delimNone {
|
||||||
d, t := transitionFunc[c.state](c, s)
|
d, nread := transitionFunc[c.state](c, s[i:])
|
||||||
|
i1 := i + nread
|
||||||
if c.state == stateText || c.state == stateRCDATA {
|
if c.state == stateText || c.state == stateRCDATA {
|
||||||
i := len(s) - len(t)
|
|
||||||
// Emit text up to the start of the tag or comment.
|
// Emit text up to the start of the tag or comment.
|
||||||
|
j := i1
|
||||||
if d.state != c.state {
|
if d.state != c.state {
|
||||||
for j := i - 1; j >= 0; j-- {
|
for j1 := j - 1; j1 >= i; j1-- {
|
||||||
if s[j] == '<' {
|
if s[j1] == '<' {
|
||||||
i = j
|
j = j1
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
b.Write(s[:i])
|
b.Write(s[i:j])
|
||||||
}
|
}
|
||||||
c, s = d, t
|
c, i = d, i1
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
i := bytes.IndexAny(s, delimEnds[c.delim])
|
i1 := i + bytes.IndexAny(s[i:], delimEnds[c.delim])
|
||||||
if i == -1 {
|
if i1 < i {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
if c.delim != delimSpaceOrTagEnd {
|
if c.delim != delimSpaceOrTagEnd {
|
||||||
// Consume any quote.
|
// Consume any quote.
|
||||||
i++
|
i1++
|
||||||
}
|
}
|
||||||
c, s = context{state: stateTag, element: c.element}, s[i:]
|
c, i = context{state: stateTag, element: c.element}, i1
|
||||||
}
|
}
|
||||||
if c.state == stateText {
|
if c.state == stateText {
|
||||||
if b.Len() == 0 {
|
if b.Len() == 0 {
|
||||||
return html
|
return html
|
||||||
}
|
}
|
||||||
b.Write(s)
|
b.Write(s[i:])
|
||||||
}
|
}
|
||||||
return b.String()
|
return b.String()
|
||||||
}
|
}
|
||||||
|
@ -14,8 +14,9 @@ import (
|
|||||||
|
|
||||||
// transitionFunc is the array of context transition functions for text nodes.
|
// transitionFunc is the array of context transition functions for text nodes.
|
||||||
// A transition function takes a context and template text input, and returns
|
// A transition function takes a context and template text input, and returns
|
||||||
// the updated context and any unconsumed text.
|
// the updated context and the number of bytes consumed from the front of the
|
||||||
var transitionFunc = [...]func(context, []byte) (context, []byte){
|
// input.
|
||||||
|
var transitionFunc = [...]func(context, []byte) (context, int){
|
||||||
stateText: tText,
|
stateText: tText,
|
||||||
stateTag: tTag,
|
stateTag: tTag,
|
||||||
stateAttrName: tAttrName,
|
stateAttrName: tAttrName,
|
||||||
@ -46,27 +47,28 @@ var commentStart = []byte("<!--")
|
|||||||
var commentEnd = []byte("-->")
|
var commentEnd = []byte("-->")
|
||||||
|
|
||||||
// tText is the context transition function for the text state.
|
// tText is the context transition function for the text state.
|
||||||
func tText(c context, s []byte) (context, []byte) {
|
func tText(c context, s []byte) (context, int) {
|
||||||
|
k := 0
|
||||||
for {
|
for {
|
||||||
i := bytes.IndexByte(s, '<')
|
i := k + bytes.IndexByte(s[k:], '<')
|
||||||
if i == -1 || i+1 == len(s) {
|
if i < k || i+1 == len(s) {
|
||||||
return c, nil
|
return c, len(s)
|
||||||
} else if i+4 <= len(s) && bytes.Equal(commentStart, s[i:i+4]) {
|
} else if i+4 <= len(s) && bytes.Equal(commentStart, s[i:i+4]) {
|
||||||
return context{state: stateHTMLCmt}, s[i+4:]
|
return context{state: stateHTMLCmt}, i + 4
|
||||||
}
|
}
|
||||||
i++
|
i++
|
||||||
if s[i] == '/' {
|
if s[i] == '/' {
|
||||||
if i+1 == len(s) {
|
if i+1 == len(s) {
|
||||||
return c, nil
|
return c, len(s)
|
||||||
}
|
}
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
j, e := eatTagName(s, i)
|
j, e := eatTagName(s, i)
|
||||||
if j != i {
|
if j != i {
|
||||||
// We've found an HTML tag.
|
// We've found an HTML tag.
|
||||||
return context{state: stateTag, element: e}, s[j:]
|
return context{state: stateTag, element: e}, j
|
||||||
}
|
}
|
||||||
s = s[j:]
|
k = j
|
||||||
}
|
}
|
||||||
panic("unreachable")
|
panic("unreachable")
|
||||||
}
|
}
|
||||||
@ -80,21 +82,21 @@ var elementContentType = [...]state{
|
|||||||
}
|
}
|
||||||
|
|
||||||
// tTag is the context transition function for the tag state.
|
// tTag is the context transition function for the tag state.
|
||||||
func tTag(c context, s []byte) (context, []byte) {
|
func tTag(c context, s []byte) (context, int) {
|
||||||
// Find the attribute name.
|
// Find the attribute name.
|
||||||
i := eatWhiteSpace(s, 0)
|
i := eatWhiteSpace(s, 0)
|
||||||
if i == len(s) {
|
if i == len(s) {
|
||||||
return c, nil
|
return c, len(s)
|
||||||
}
|
}
|
||||||
if s[i] == '>' {
|
if s[i] == '>' {
|
||||||
return context{
|
return context{
|
||||||
state: elementContentType[c.element],
|
state: elementContentType[c.element],
|
||||||
element: c.element,
|
element: c.element,
|
||||||
}, s[i+1:]
|
}, i + 1
|
||||||
}
|
}
|
||||||
j, err := eatAttrName(s, i)
|
j, err := eatAttrName(s, i)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return context{state: stateError, err: err}, nil
|
return context{state: stateError, err: err}, len(s)
|
||||||
}
|
}
|
||||||
state, attr := stateTag, attrNone
|
state, attr := stateTag, attrNone
|
||||||
if i != j {
|
if i != j {
|
||||||
@ -112,35 +114,35 @@ func tTag(c context, s []byte) (context, []byte) {
|
|||||||
state = stateAfterName
|
state = stateAfterName
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return context{state: state, element: c.element, attr: attr}, s[j:]
|
return context{state: state, element: c.element, attr: attr}, j
|
||||||
}
|
}
|
||||||
|
|
||||||
// tAttrName is the context transition function for stateAttrName.
|
// tAttrName is the context transition function for stateAttrName.
|
||||||
func tAttrName(c context, s []byte) (context, []byte) {
|
func tAttrName(c context, s []byte) (context, int) {
|
||||||
i, err := eatAttrName(s, 0)
|
i, err := eatAttrName(s, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return context{state: stateError, err: err}, nil
|
return context{state: stateError, err: err}, len(s)
|
||||||
} else if i == len(s) {
|
} else if i == len(s) {
|
||||||
return c, nil
|
return c, len(s)
|
||||||
}
|
}
|
||||||
c.state = stateAfterName
|
c.state = stateAfterName
|
||||||
return c, s[i:]
|
return c, i
|
||||||
}
|
}
|
||||||
|
|
||||||
// tAfterName is the context transition function for stateAfterName.
|
// tAfterName is the context transition function for stateAfterName.
|
||||||
func tAfterName(c context, s []byte) (context, []byte) {
|
func tAfterName(c context, s []byte) (context, int) {
|
||||||
// Look for the start of the value.
|
// Look for the start of the value.
|
||||||
i := eatWhiteSpace(s, 0)
|
i := eatWhiteSpace(s, 0)
|
||||||
if i == len(s) {
|
if i == len(s) {
|
||||||
return c, nil
|
return c, len(s)
|
||||||
} else if s[i] != '=' {
|
} else if s[i] != '=' {
|
||||||
// Occurs due to tag ending '>', and valueless attribute.
|
// Occurs due to tag ending '>', and valueless attribute.
|
||||||
c.state = stateTag
|
c.state = stateTag
|
||||||
return c, s[i:]
|
return c, i
|
||||||
}
|
}
|
||||||
c.state = stateBeforeValue
|
c.state = stateBeforeValue
|
||||||
// Consume the "=".
|
// Consume the "=".
|
||||||
return c, s[i+1:]
|
return c, i + 1
|
||||||
}
|
}
|
||||||
|
|
||||||
var attrStartStates = [...]state{
|
var attrStartStates = [...]state{
|
||||||
@ -151,10 +153,10 @@ var attrStartStates = [...]state{
|
|||||||
}
|
}
|
||||||
|
|
||||||
// tBeforeValue is the context transition function for stateBeforeValue.
|
// tBeforeValue is the context transition function for stateBeforeValue.
|
||||||
func tBeforeValue(c context, s []byte) (context, []byte) {
|
func tBeforeValue(c context, s []byte) (context, int) {
|
||||||
i := eatWhiteSpace(s, 0)
|
i := eatWhiteSpace(s, 0)
|
||||||
if i == len(s) {
|
if i == len(s) {
|
||||||
return c, nil
|
return c, len(s)
|
||||||
}
|
}
|
||||||
// Find the attribute delimiter.
|
// Find the attribute delimiter.
|
||||||
delim := delimSpaceOrTagEnd
|
delim := delimSpaceOrTagEnd
|
||||||
@ -165,16 +167,16 @@ func tBeforeValue(c context, s []byte) (context, []byte) {
|
|||||||
delim, i = delimDoubleQuote, i+1
|
delim, i = delimDoubleQuote, i+1
|
||||||
}
|
}
|
||||||
c.state, c.delim, c.attr = attrStartStates[c.attr], delim, attrNone
|
c.state, c.delim, c.attr = attrStartStates[c.attr], delim, attrNone
|
||||||
return c, s[i:]
|
return c, i
|
||||||
}
|
}
|
||||||
|
|
||||||
// tHTMLCmt is the context transition function for stateHTMLCmt.
|
// tHTMLCmt is the context transition function for stateHTMLCmt.
|
||||||
func tHTMLCmt(c context, s []byte) (context, []byte) {
|
func tHTMLCmt(c context, s []byte) (context, int) {
|
||||||
i := bytes.Index(s, commentEnd)
|
i := bytes.Index(s, commentEnd)
|
||||||
if i != -1 {
|
if i != -1 {
|
||||||
return context{}, s[i+3:]
|
return context{}, i + 3
|
||||||
}
|
}
|
||||||
return c, nil
|
return c, len(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
// specialTagEndMarkers maps element types to the character sequence that
|
// specialTagEndMarkers maps element types to the character sequence that
|
||||||
@ -188,24 +190,24 @@ var specialTagEndMarkers = [...]string{
|
|||||||
|
|
||||||
// tSpecialTagEnd is the context transition function for raw text and RCDATA
|
// tSpecialTagEnd is the context transition function for raw text and RCDATA
|
||||||
// element states.
|
// element states.
|
||||||
func tSpecialTagEnd(c context, s []byte) (context, []byte) {
|
func tSpecialTagEnd(c context, s []byte) (context, int) {
|
||||||
if c.element != elementNone {
|
if c.element != elementNone {
|
||||||
end := specialTagEndMarkers[c.element]
|
end := specialTagEndMarkers[c.element]
|
||||||
i := strings.Index(strings.ToLower(string(s)), end)
|
i := strings.Index(strings.ToLower(string(s)), end)
|
||||||
if i != -1 {
|
if i != -1 {
|
||||||
return context{state: stateTag}, s[i+len(end):]
|
return context{state: stateTag}, i + len(end)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return c, nil
|
return c, len(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
// tAttr is the context transition function for the attribute state.
|
// tAttr is the context transition function for the attribute state.
|
||||||
func tAttr(c context, s []byte) (context, []byte) {
|
func tAttr(c context, s []byte) (context, int) {
|
||||||
return c, nil
|
return c, len(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
// tURL is the context transition function for the URL state.
|
// tURL is the context transition function for the URL state.
|
||||||
func tURL(c context, s []byte) (context, []byte) {
|
func tURL(c context, s []byte) (context, int) {
|
||||||
if bytes.IndexAny(s, "#?") >= 0 {
|
if bytes.IndexAny(s, "#?") >= 0 {
|
||||||
c.urlPart = urlPartQueryOrFrag
|
c.urlPart = urlPartQueryOrFrag
|
||||||
} else if len(s) != eatWhiteSpace(s, 0) && c.urlPart == urlPartNone {
|
} else if len(s) != eatWhiteSpace(s, 0) && c.urlPart == urlPartNone {
|
||||||
@ -213,20 +215,20 @@ func tURL(c context, s []byte) (context, []byte) {
|
|||||||
// attrs: http://www.w3.org/TR/html5/index.html#attributes-1
|
// attrs: http://www.w3.org/TR/html5/index.html#attributes-1
|
||||||
c.urlPart = urlPartPreQuery
|
c.urlPart = urlPartPreQuery
|
||||||
}
|
}
|
||||||
return c, nil
|
return c, len(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
// tJS is the context transition function for the JS state.
|
// tJS is the context transition function for the JS state.
|
||||||
func tJS(c context, s []byte) (context, []byte) {
|
func tJS(c context, s []byte) (context, int) {
|
||||||
if d, t := tSpecialTagEnd(c, s); t != nil {
|
if d, i := tSpecialTagEnd(c, s); i != len(s) {
|
||||||
return d, t
|
return d, i
|
||||||
}
|
}
|
||||||
|
|
||||||
i := bytes.IndexAny(s, `"'/`)
|
i := bytes.IndexAny(s, `"'/`)
|
||||||
if i == -1 {
|
if i == -1 {
|
||||||
// Entire input is non string, comment, regexp tokens.
|
// Entire input is non string, comment, regexp tokens.
|
||||||
c.jsCtx = nextJSCtx(s, c.jsCtx)
|
c.jsCtx = nextJSCtx(s, c.jsCtx)
|
||||||
return c, nil
|
return c, len(s)
|
||||||
}
|
}
|
||||||
c.jsCtx = nextJSCtx(s[:i], c.jsCtx)
|
c.jsCtx = nextJSCtx(s[:i], c.jsCtx)
|
||||||
switch s[i] {
|
switch s[i] {
|
||||||
@ -248,18 +250,18 @@ func tJS(c context, s []byte) (context, []byte) {
|
|||||||
return context{
|
return context{
|
||||||
state: stateError,
|
state: stateError,
|
||||||
err: errorf(ErrSlashAmbig, 0, "'/' could start div or regexp: %.32q", s[i:]),
|
err: errorf(ErrSlashAmbig, 0, "'/' could start div or regexp: %.32q", s[i:]),
|
||||||
}, nil
|
}, len(s)
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
panic("unreachable")
|
panic("unreachable")
|
||||||
}
|
}
|
||||||
return c, s[i+1:]
|
return c, i + 1
|
||||||
}
|
}
|
||||||
|
|
||||||
// tJSStr is the context transition function for the JS string states.
|
// tJSStr is the context transition function for the JS string states.
|
||||||
func tJSStr(c context, s []byte) (context, []byte) {
|
func tJSStr(c context, s []byte) (context, int) {
|
||||||
if d, t := tSpecialTagEnd(c, s); t != nil {
|
if d, i := tSpecialTagEnd(c, s); i != len(s) {
|
||||||
return d, t
|
return d, i
|
||||||
}
|
}
|
||||||
|
|
||||||
quoteAndEsc := `\"`
|
quoteAndEsc := `\"`
|
||||||
@ -267,55 +269,54 @@ func tJSStr(c context, s []byte) (context, []byte) {
|
|||||||
quoteAndEsc = `\'`
|
quoteAndEsc = `\'`
|
||||||
}
|
}
|
||||||
|
|
||||||
b := s
|
k := 0
|
||||||
for {
|
for {
|
||||||
i := bytes.IndexAny(b, quoteAndEsc)
|
i := k + bytes.IndexAny(s[k:], quoteAndEsc)
|
||||||
if i == -1 {
|
if i < k {
|
||||||
return c, nil
|
return c, len(s)
|
||||||
}
|
}
|
||||||
if b[i] == '\\' {
|
if s[i] == '\\' {
|
||||||
i++
|
i++
|
||||||
if i == len(b) {
|
if i == len(s) {
|
||||||
return context{
|
return context{
|
||||||
state: stateError,
|
state: stateError,
|
||||||
err: errorf(ErrPartialEscape, 0, "unfinished escape sequence in JS string: %q", s),
|
err: errorf(ErrPartialEscape, 0, "unfinished escape sequence in JS string: %q", s),
|
||||||
}, nil
|
}, len(s)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
c.state, c.jsCtx = stateJS, jsCtxDivOp
|
c.state, c.jsCtx = stateJS, jsCtxDivOp
|
||||||
return c, b[i+1:]
|
return c, i + 1
|
||||||
}
|
}
|
||||||
b = b[i+1:]
|
k = i + 1
|
||||||
}
|
}
|
||||||
panic("unreachable")
|
panic("unreachable")
|
||||||
}
|
}
|
||||||
|
|
||||||
// tJSRegexp is the context transition function for the /RegExp/ literal state.
|
// tJSRegexp is the context transition function for the /RegExp/ literal state.
|
||||||
func tJSRegexp(c context, s []byte) (context, []byte) {
|
func tJSRegexp(c context, s []byte) (context, int) {
|
||||||
if d, t := tSpecialTagEnd(c, s); t != nil {
|
if d, i := tSpecialTagEnd(c, s); i != len(s) {
|
||||||
return d, t
|
return d, i
|
||||||
}
|
}
|
||||||
|
|
||||||
b := s
|
k, inCharset := 0, false
|
||||||
inCharset := false
|
|
||||||
for {
|
for {
|
||||||
i := bytes.IndexAny(b, `/[\]`)
|
i := k + bytes.IndexAny(s[k:], `\/[]`)
|
||||||
if i == -1 {
|
if i < k {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
switch b[i] {
|
switch s[i] {
|
||||||
case '/':
|
case '/':
|
||||||
if !inCharset {
|
if !inCharset {
|
||||||
c.state, c.jsCtx = stateJS, jsCtxDivOp
|
c.state, c.jsCtx = stateJS, jsCtxDivOp
|
||||||
return c, b[i+1:]
|
return c, i + 1
|
||||||
}
|
}
|
||||||
case '\\':
|
case '\\':
|
||||||
i++
|
i++
|
||||||
if i == len(b) {
|
if i == len(s) {
|
||||||
return context{
|
return context{
|
||||||
state: stateError,
|
state: stateError,
|
||||||
err: errorf(ErrPartialEscape, 0, "unfinished escape sequence in JS regexp: %q", s),
|
err: errorf(ErrPartialEscape, 0, "unfinished escape sequence in JS regexp: %q", s),
|
||||||
}, nil
|
}, len(s)
|
||||||
}
|
}
|
||||||
case '[':
|
case '[':
|
||||||
inCharset = true
|
inCharset = true
|
||||||
@ -324,7 +325,7 @@ func tJSRegexp(c context, s []byte) (context, []byte) {
|
|||||||
default:
|
default:
|
||||||
panic("unreachable")
|
panic("unreachable")
|
||||||
}
|
}
|
||||||
b = b[i+1:]
|
k = i + 1
|
||||||
}
|
}
|
||||||
|
|
||||||
if inCharset {
|
if inCharset {
|
||||||
@ -333,22 +334,22 @@ func tJSRegexp(c context, s []byte) (context, []byte) {
|
|||||||
return context{
|
return context{
|
||||||
state: stateError,
|
state: stateError,
|
||||||
err: errorf(ErrPartialCharset, 0, "unfinished JS regexp charset: %q", s),
|
err: errorf(ErrPartialCharset, 0, "unfinished JS regexp charset: %q", s),
|
||||||
}, nil
|
}, len(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
return c, nil
|
return c, len(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
var blockCommentEnd = []byte("*/")
|
var blockCommentEnd = []byte("*/")
|
||||||
|
|
||||||
// tBlockCmt is the context transition function for /*comment*/ states.
|
// tBlockCmt is the context transition function for /*comment*/ states.
|
||||||
func tBlockCmt(c context, s []byte) (context, []byte) {
|
func tBlockCmt(c context, s []byte) (context, int) {
|
||||||
if d, t := tSpecialTagEnd(c, s); t != nil {
|
if d, i := tSpecialTagEnd(c, s); i != len(s) {
|
||||||
return d, t
|
return d, i
|
||||||
}
|
}
|
||||||
i := bytes.Index(s, blockCommentEnd)
|
i := bytes.Index(s, blockCommentEnd)
|
||||||
if i == -1 {
|
if i == -1 {
|
||||||
return c, nil
|
return c, len(s)
|
||||||
}
|
}
|
||||||
switch c.state {
|
switch c.state {
|
||||||
case stateJSBlockCmt:
|
case stateJSBlockCmt:
|
||||||
@ -358,13 +359,13 @@ func tBlockCmt(c context, s []byte) (context, []byte) {
|
|||||||
default:
|
default:
|
||||||
panic(c.state.String())
|
panic(c.state.String())
|
||||||
}
|
}
|
||||||
return c, s[i+2:]
|
return c, i + 2
|
||||||
}
|
}
|
||||||
|
|
||||||
// tLineCmt is the context transition function for //comment states.
|
// tLineCmt is the context transition function for //comment states.
|
||||||
func tLineCmt(c context, s []byte) (context, []byte) {
|
func tLineCmt(c context, s []byte) (context, int) {
|
||||||
if d, t := tSpecialTagEnd(c, s); t != nil {
|
if d, i := tSpecialTagEnd(c, s); i != len(s) {
|
||||||
return d, t
|
return d, i
|
||||||
}
|
}
|
||||||
var lineTerminators string
|
var lineTerminators string
|
||||||
var endState state
|
var endState state
|
||||||
@ -386,21 +387,21 @@ func tLineCmt(c context, s []byte) (context, []byte) {
|
|||||||
|
|
||||||
i := bytes.IndexAny(s, lineTerminators)
|
i := bytes.IndexAny(s, lineTerminators)
|
||||||
if i == -1 {
|
if i == -1 {
|
||||||
return c, nil
|
return c, len(s)
|
||||||
}
|
}
|
||||||
c.state = endState
|
c.state = endState
|
||||||
// Per section 7.4 of EcmaScript 5 : http://es5.github.com/#x7.4
|
// Per section 7.4 of EcmaScript 5 : http://es5.github.com/#x7.4
|
||||||
// "However, the LineTerminator at the end of the line is not
|
// "However, the LineTerminator at the end of the line is not
|
||||||
// considered to be part of the single-line comment; it is recognised
|
// considered to be part of the single-line comment; it is
|
||||||
// separately by the lexical grammar and becomes part of the stream of
|
// recognized separately by the lexical grammar and becomes part
|
||||||
// input elements for the syntactic grammar."
|
// of the stream of input elements for the syntactic grammar."
|
||||||
return c, s[i:]
|
return c, i
|
||||||
}
|
}
|
||||||
|
|
||||||
// tCSS is the context transition function for the CSS state.
|
// tCSS is the context transition function for the CSS state.
|
||||||
func tCSS(c context, s []byte) (context, []byte) {
|
func tCSS(c context, s []byte) (context, int) {
|
||||||
if d, t := tSpecialTagEnd(c, s); t != nil {
|
if d, i := tSpecialTagEnd(c, s); i != len(s) {
|
||||||
return d, t
|
return d, i
|
||||||
}
|
}
|
||||||
|
|
||||||
// CSS quoted strings are almost never used except for:
|
// CSS quoted strings are almost never used except for:
|
||||||
@ -430,55 +431,55 @@ func tCSS(c context, s []byte) (context, []byte) {
|
|||||||
// have the attribute name available if our conservative assumption
|
// have the attribute name available if our conservative assumption
|
||||||
// proves problematic for real code.
|
// proves problematic for real code.
|
||||||
|
|
||||||
|
k := 0
|
||||||
for {
|
for {
|
||||||
i := bytes.IndexAny(s, `("'/`)
|
i := k + bytes.IndexAny(s[k:], `("'/`)
|
||||||
if i == -1 {
|
if i < k {
|
||||||
return c, nil
|
return c, len(s)
|
||||||
}
|
}
|
||||||
switch s[i] {
|
switch s[i] {
|
||||||
case '(':
|
case '(':
|
||||||
// Look for url to the left.
|
// Look for url to the left.
|
||||||
p := bytes.TrimRight(s[:i], "\t\n\f\r ")
|
p := bytes.TrimRight(s[:i], "\t\n\f\r ")
|
||||||
if endsWithCSSKeyword(p, "url") {
|
if endsWithCSSKeyword(p, "url") {
|
||||||
q := bytes.TrimLeft(s[i+1:], "\t\n\f\r ")
|
j := len(s) - len(bytes.TrimLeft(s[i+1:], "\t\n\f\r "))
|
||||||
switch {
|
switch {
|
||||||
case len(q) != 0 && q[0] == '"':
|
case j != len(s) && s[j] == '"':
|
||||||
c.state, s = stateCSSDqURL, q[1:]
|
c.state, j = stateCSSDqURL, j+1
|
||||||
case len(q) != 0 && q[0] == '\'':
|
case j != len(s) && s[j] == '\'':
|
||||||
c.state, s = stateCSSSqURL, q[1:]
|
c.state, j = stateCSSSqURL, j+1
|
||||||
|
|
||||||
default:
|
default:
|
||||||
c.state, s = stateCSSURL, q
|
c.state = stateCSSURL
|
||||||
}
|
}
|
||||||
return c, s
|
return c, j
|
||||||
}
|
}
|
||||||
case '/':
|
case '/':
|
||||||
if i+1 < len(s) {
|
if i+1 < len(s) {
|
||||||
switch s[i+1] {
|
switch s[i+1] {
|
||||||
case '/':
|
case '/':
|
||||||
c.state = stateCSSLineCmt
|
c.state = stateCSSLineCmt
|
||||||
return c, s[i+2:]
|
return c, i + 2
|
||||||
case '*':
|
case '*':
|
||||||
c.state = stateCSSBlockCmt
|
c.state = stateCSSBlockCmt
|
||||||
return c, s[i+2:]
|
return c, i + 2
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case '"':
|
case '"':
|
||||||
c.state = stateCSSDqStr
|
c.state = stateCSSDqStr
|
||||||
return c, s[i+1:]
|
return c, i + 1
|
||||||
case '\'':
|
case '\'':
|
||||||
c.state = stateCSSSqStr
|
c.state = stateCSSSqStr
|
||||||
return c, s[i+1:]
|
return c, i + 1
|
||||||
}
|
}
|
||||||
s = s[i+1:]
|
k = i + 1
|
||||||
}
|
}
|
||||||
panic("unreachable")
|
panic("unreachable")
|
||||||
}
|
}
|
||||||
|
|
||||||
// tCSSStr is the context transition function for the CSS string and URL states.
|
// tCSSStr is the context transition function for the CSS string and URL states.
|
||||||
func tCSSStr(c context, s []byte) (context, []byte) {
|
func tCSSStr(c context, s []byte) (context, int) {
|
||||||
if d, t := tSpecialTagEnd(c, s); t != nil {
|
if d, i := tSpecialTagEnd(c, s); i != len(s) {
|
||||||
return d, t
|
return d, i
|
||||||
}
|
}
|
||||||
|
|
||||||
var endAndEsc string
|
var endAndEsc string
|
||||||
@ -495,33 +496,34 @@ func tCSSStr(c context, s []byte) (context, []byte) {
|
|||||||
panic(c.state.String())
|
panic(c.state.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
b := s
|
k := 0
|
||||||
for {
|
for {
|
||||||
i := bytes.IndexAny(b, endAndEsc)
|
i := k + bytes.IndexAny(s[k:], endAndEsc)
|
||||||
if i == -1 {
|
if i < k {
|
||||||
return tURL(c, decodeCSS(b))
|
c, nread := tURL(c, decodeCSS(s[k:]))
|
||||||
|
return c, k + nread
|
||||||
}
|
}
|
||||||
if b[i] == '\\' {
|
if s[i] == '\\' {
|
||||||
i++
|
i++
|
||||||
if i == len(b) {
|
if i == len(s) {
|
||||||
return context{
|
return context{
|
||||||
state: stateError,
|
state: stateError,
|
||||||
err: errorf(ErrPartialEscape, 0, "unfinished escape sequence in CSS string: %q", s),
|
err: errorf(ErrPartialEscape, 0, "unfinished escape sequence in CSS string: %q", s),
|
||||||
}, nil
|
}, len(s)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
c.state = stateCSS
|
c.state = stateCSS
|
||||||
return c, b[i+1:]
|
return c, i + 1
|
||||||
}
|
}
|
||||||
c, _ = tURL(c, decodeCSS(b[:i+1]))
|
c, _ = tURL(c, decodeCSS(s[:i+1]))
|
||||||
b = b[i+1:]
|
k = i + 1
|
||||||
}
|
}
|
||||||
panic("unreachable")
|
panic("unreachable")
|
||||||
}
|
}
|
||||||
|
|
||||||
// tError is the context transition function for the error state.
|
// tError is the context transition function for the error state.
|
||||||
func tError(c context, s []byte) (context, []byte) {
|
func tError(c context, s []byte) (context, int) {
|
||||||
return c, nil
|
return c, len(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
// eatAttrName returns the largest j such that s[i:j] is an attribute name.
|
// eatAttrName returns the largest j such that s[i:j] is an attribute name.
|
||||||
|
Loading…
Reference in New Issue
Block a user