html: add "in template" insertion mode support

See:
https://html.spec.whatwg.org/multipage/parsing.html#parsing-main-intemplate

Updates golang/go#23071

Change-Id: I36529b7cf5d2adf159ed5c471fba9f67890b7eb9
Reviewed-on: https://go-review.googlesource.com/94838
Run-TryBot: Kunpei Sakai <namusyaka@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Nigel Tao <nigeltao@golang.org>
diff --git a/html/node.go b/html/node.go
index 6f136c4..2c1cade 100644
--- a/html/node.go
+++ b/html/node.go
@@ -174,6 +174,16 @@
 	return -1
 }
 
+// contains returns whether a is within s.
+func (s *nodeStack) contains(a atom.Atom) bool {
+	for _, n := range *s {
+		if n.DataAtom == a {
+			return true
+		}
+	}
+	return false
+}
+
 // insert inserts a node at the given index.
 func (s *nodeStack) insert(i int, n *Node) {
 	(*s) = append(*s, nil)
@@ -192,3 +202,19 @@
 	(*s)[j] = nil
 	*s = (*s)[:j]
 }
+
+type insertionModeStack []insertionMode
+
+func (s *insertionModeStack) pop() (im insertionMode) {
+	i := len(*s)
+	im = (*s)[i-1]
+	*s = (*s)[:i-1]
+	return im
+}
+
+func (s *insertionModeStack) top() insertionMode {
+	if i := len(*s); i > 0 {
+		return (*s)[i-1]
+	}
+	return nil
+}
diff --git a/html/parse.go b/html/parse.go
index 2a5abdd..46c1cc6 100644
--- a/html/parse.go
+++ b/html/parse.go
@@ -32,6 +32,8 @@
 	head, form *Node
 	// Other parsing state flags (section 12.2.4.5).
 	scripting, framesetOK bool
+	// The stack of template insertion modes
+	templateStack insertionModeStack
 	// im is the current insertion mode.
 	im insertionMode
 	// originalIM is the insertion mode to go back to after completing a text
@@ -126,7 +128,7 @@
 					return -1
 				}
 			case tableScope:
-				if tagAtom == a.Html || tagAtom == a.Table {
+				if tagAtom == a.Html || tagAtom == a.Table || tagAtom == a.Template {
 					return -1
 				}
 			case selectScope:
@@ -162,17 +164,17 @@
 		tagAtom := p.oe[i].DataAtom
 		switch s {
 		case tableScope:
-			if tagAtom == a.Html || tagAtom == a.Table {
+			if tagAtom == a.Html || tagAtom == a.Table || tagAtom == a.Template {
 				p.oe = p.oe[:i+1]
 				return
 			}
 		case tableRowScope:
-			if tagAtom == a.Html || tagAtom == a.Tr {
+			if tagAtom == a.Html || tagAtom == a.Tr || tagAtom == a.Template {
 				p.oe = p.oe[:i+1]
 				return
 			}
 		case tableBodyScope:
-			if tagAtom == a.Html || tagAtom == a.Tbody || tagAtom == a.Tfoot || tagAtom == a.Thead {
+			if tagAtom == a.Html || tagAtom == a.Tbody || tagAtom == a.Tfoot || tagAtom == a.Thead || tagAtom == a.Template {
 				p.oe = p.oe[:i+1]
 				return
 			}
@@ -183,7 +185,7 @@
 }
 
 // generateImpliedEndTags pops nodes off the stack of open elements as long as
-// the top node has a tag name of dd, dt, li, option, optgroup, p, rp, or rt.
+// the top node has a tag name of dd, dt, li, optgroup, option, p, rp, or rt.
 // If exceptions are specified, nodes with that name will not be popped off.
 func (p *parser) generateImpliedEndTags(exceptions ...string) {
 	var i int
@@ -192,7 +194,9 @@
 		n := p.oe[i]
 		if n.Type == ElementNode {
 			switch n.DataAtom {
-			case a.Dd, a.Dt, a.Li, a.Option, a.Optgroup, a.P, a.Rp, a.Rt:
+			// TODO: add a.Rb and a.Rtc, to match the spec. This needs additions to the
+			// atom package. Ditto for generateAllImpliedEndTags.
+			case a.Dd, a.Dt, a.Li, a.Optgroup, a.Option, a.P, a.Rp, a.Rt:
 				for _, except := range exceptions {
 					if n.Data == except {
 						break loop
@@ -207,6 +211,27 @@
 	p.oe = p.oe[:i+1]
 }
 
+// generateAllImpliedEndTags pops nodes off the stack of open elements as long as
+// the top node has a tag name of caption, colgroup, dd, div, dt, li, optgroup, option, p, rp, rt,
+// span, tbody, td, tfoot, th, thead or tr.
+func (p *parser) generateAllImpliedEndTags() {
+	var i int
+	for i = len(p.oe) - 1; i >= 0; i-- {
+		n := p.oe[i]
+		if n.Type == ElementNode {
+			switch n.DataAtom {
+			// TODO: remove this divergence from the HTML5 spec
+			case a.Caption, a.Colgroup, a.Dd, a.Div, a.Dt, a.Li, a.Optgroup, a.Option, a.P, a.Rp, a.Rt,
+				a.Span, a.Tbody, a.Td, a.Tfoot, a.Th, a.Thead, a.Tr:
+				continue
+			}
+		}
+		break
+	}
+
+	p.oe = p.oe[:i+1]
+}
+
 // addChild adds a child node n to the top element, and pushes n onto the stack
 // of open elements if it is an element node.
 func (p *parser) addChild(n *Node) {
@@ -236,7 +261,7 @@
 // fosterParent adds a child node according to the foster parenting rules.
 // Section 12.2.6.1, "foster parenting".
 func (p *parser) fosterParent(n *Node) {
-	var table, parent, prev *Node
+	var table, parent, prev, template *Node
 	var i int
 	for i = len(p.oe) - 1; i >= 0; i-- {
 		if p.oe[i].DataAtom == a.Table {
@@ -245,6 +270,19 @@
 		}
 	}
 
+	var j int
+	for j = len(p.oe) - 1; j >= 0; j-- {
+		if p.oe[j].DataAtom == a.Template {
+			template = p.oe[j]
+			break
+		}
+	}
+
+	if template != nil && (table == nil || j < i) {
+		template.AppendChild(n)
+		return
+	}
+
 	if table == nil {
 		// The foster parent is the html element.
 		parent = p.oe[0]
@@ -415,14 +453,34 @@
 func (p *parser) resetInsertionMode() {
 	for i := len(p.oe) - 1; i >= 0; i-- {
 		n := p.oe[i]
-		if i == 0 && p.context != nil {
+		last := i == 0
+		if last && p.context != nil {
 			n = p.context
 		}
 
 		switch n.DataAtom {
 		case a.Select:
+			if !last {
+				for ancestor, first := n, p.oe[0]; ancestor != first; {
+					if ancestor == first {
+						break
+					}
+					ancestor = p.oe[p.oe.index(ancestor)-1]
+					switch ancestor.DataAtom {
+					case a.Template:
+						p.im = inSelectIM
+						return
+					case a.Table:
+						p.im = inSelectInTableIM
+						return
+					}
+				}
+			}
 			p.im = inSelectIM
 		case a.Td, a.Th:
+			// TODO: remove this divergence from the HTML5 spec.
+			//
+			// See https://bugs.chromium.org/p/chromium/issues/detail?id=829668
 			p.im = inCellIM
 		case a.Tr:
 			p.im = inRowIM
@@ -434,20 +492,32 @@
 			p.im = inColumnGroupIM
 		case a.Table:
 			p.im = inTableIM
+		case a.Template:
+			p.im = p.templateStack.top()
 		case a.Head:
-			p.im = inBodyIM
+			// TODO: remove this divergence from the HTML5 spec.
+			//
+			// See https://bugs.chromium.org/p/chromium/issues/detail?id=829668
+			p.im = inHeadIM
 		case a.Body:
 			p.im = inBodyIM
 		case a.Frameset:
 			p.im = inFramesetIM
 		case a.Html:
-			p.im = beforeHeadIM
+			if p.head == nil {
+				p.im = beforeHeadIM
+			} else {
+				p.im = afterHeadIM
+			}
 		default:
+			if last {
+				p.im = inBodyIM
+				return
+			}
 			continue
 		}
 		return
 	}
-	p.im = inBodyIM
 }
 
 const whitespace = " \t\r\n\f"
@@ -590,19 +660,36 @@
 		case a.Head:
 			// Ignore the token.
 			return true
+		case a.Template:
+			p.addElement()
+			p.afe = append(p.afe, &scopeMarker)
+			p.framesetOK = false
+			p.im = inTemplateIM
+			p.templateStack = append(p.templateStack, inTemplateIM)
+			return true
 		}
 	case EndTagToken:
 		switch p.tok.DataAtom {
 		case a.Head:
-			n := p.oe.pop()
-			if n.DataAtom != a.Head {
-				panic("html: bad parser state: <head> element not found, in the in-head insertion mode")
-			}
+			p.oe.pop()
 			p.im = afterHeadIM
 			return true
 		case a.Body, a.Html, a.Br:
 			p.parseImpliedToken(EndTagToken, a.Head, a.Head.String())
 			return false
+		case a.Template:
+			if !p.oe.contains(a.Template) {
+				return true
+			}
+			p.generateAllImpliedEndTags()
+			if n := p.oe.top(); n.DataAtom != a.Template {
+				return true
+			}
+			p.popUntil(defaultScope, a.Template)
+			p.clearActiveFormattingElements()
+			p.templateStack.pop()
+			p.resetInsertionMode()
+			return true
 		default:
 			// Ignore the token.
 			return true
@@ -648,7 +735,7 @@
 			p.addElement()
 			p.im = inFramesetIM
 			return true
-		case a.Base, a.Basefont, a.Bgsound, a.Link, a.Meta, a.Noframes, a.Script, a.Style, a.Title:
+		case a.Base, a.Basefont, a.Bgsound, a.Link, a.Meta, a.Noframes, a.Script, a.Style, a.Template, a.Title:
 			p.oe = append(p.oe, p.head)
 			defer p.oe.remove(p.head)
 			return inHeadIM(p)
@@ -660,6 +747,8 @@
 		switch p.tok.DataAtom {
 		case a.Body, a.Html, a.Br:
 			// Drop down to creating an implied <body> tag.
+		case a.Template:
+			return inHeadIM(p)
 		default:
 			// Ignore the token.
 			return true
@@ -727,10 +816,16 @@
 	case StartTagToken:
 		switch p.tok.DataAtom {
 		case a.Html:
+			if p.oe.contains(a.Template) {
+				return true
+			}
 			copyAttributes(p.oe[0], p.tok)
-		case a.Base, a.Basefont, a.Bgsound, a.Command, a.Link, a.Meta, a.Noframes, a.Script, a.Style, a.Title:
+		case a.Base, a.Basefont, a.Bgsound, a.Command, a.Link, a.Meta, a.Noframes, a.Script, a.Style, a.Template, a.Title:
 			return inHeadIM(p)
 		case a.Body:
+			if p.oe.contains(a.Template) {
+				return true
+			}
 			if len(p.oe) >= 2 {
 				body := p.oe[1]
 				if body.Type == ElementNode && body.DataAtom == a.Body {
@@ -767,7 +862,7 @@
 			// The newline, if any, will be dealt with by the TextToken case.
 			p.framesetOK = false
 		case a.Form:
-			if p.form == nil {
+			if p.oe.contains(a.Template) || p.form == nil {
 				p.popUntil(buttonScope, a.P)
 				p.addElement()
 				p.form = p.top()
@@ -972,7 +1067,13 @@
 				p.acknowledgeSelfClosingTag()
 			}
 			return true
-		case a.Caption, a.Col, a.Colgroup, a.Frame, a.Head, a.Tbody, a.Td, a.Tfoot, a.Th, a.Thead, a.Tr:
+		case a.Frame:
+			// TODO: remove this divergence from the HTML5 spec.
+			if p.oe.contains(a.Template) {
+				p.addElement()
+				return true
+			}
+		case a.Caption, a.Col, a.Colgroup, a.Head, a.Tbody, a.Td, a.Tfoot, a.Th, a.Thead, a.Tr:
 			// Ignore the token.
 		default:
 			p.reconstructActiveFormattingElements()
@@ -993,15 +1094,28 @@
 		case a.Address, a.Article, a.Aside, a.Blockquote, a.Button, a.Center, a.Details, a.Dir, a.Div, a.Dl, a.Fieldset, a.Figcaption, a.Figure, a.Footer, a.Header, a.Hgroup, a.Listing, a.Menu, a.Nav, a.Ol, a.Pre, a.Section, a.Summary, a.Ul:
 			p.popUntil(defaultScope, p.tok.DataAtom)
 		case a.Form:
-			node := p.form
-			p.form = nil
-			i := p.indexOfElementInScope(defaultScope, a.Form)
-			if node == nil || i == -1 || p.oe[i] != node {
-				// Ignore the token.
-				return true
+			if p.oe.contains(a.Template) {
+				if !p.oe.contains(a.Form) {
+					// Ignore the token.
+					return true
+				}
+				p.generateImpliedEndTags()
+				if p.tok.DataAtom == a.Form {
+					// Ignore the token.
+					return true
+				}
+				p.popUntil(defaultScope, a.Form)
+			} else {
+				node := p.form
+				p.form = nil
+				i := p.indexOfElementInScope(defaultScope, a.Form)
+				if node == nil || i == -1 || p.oe[i] != node {
+					// Ignore the token.
+					return true
+				}
+				p.generateImpliedEndTags()
+				p.oe.remove(node)
 			}
-			p.generateImpliedEndTags()
-			p.oe.remove(node)
 		case a.P:
 			if !p.elementInScope(buttonScope, a.P) {
 				p.parseImpliedToken(StartTagToken, a.P, a.P.String())
@@ -1022,6 +1136,8 @@
 		case a.Br:
 			p.tok.Type = StartTagToken
 			return false
+		case a.Template:
+			return inHeadIM(p)
 		default:
 			p.inBodyEndTagOther(p.tok.DataAtom)
 		}
@@ -1030,6 +1146,22 @@
 			Type: CommentNode,
 			Data: p.tok.Data,
 		})
+	case ErrorToken:
+		// TODO: remove this divergence from the HTML5 spec.
+		if len(p.templateStack) > 0 {
+			p.im = inTemplateIM
+			return false
+		} else {
+			for _, e := range p.oe {
+				// TODO(namusyaka): rb and rtc elements should be added after updating atom.
+				switch e.DataAtom {
+				case a.Dd, a.Dt, a.Li, a.Optgroup, a.Option, a.P, a.Rp, a.Rt, a.Tbody, a.Td, a.Tfoot, a.Th,
+					a.Thead, a.Tr, a.Body, a.Html:
+				default:
+					return true
+				}
+			}
+		}
 	}
 
 	return true
@@ -1135,6 +1267,12 @@
 		switch commonAncestor.DataAtom {
 		case a.Table, a.Tbody, a.Tfoot, a.Thead, a.Tr:
 			p.fosterParent(lastNode)
+		case a.Template:
+			// TODO: remove namespace checking
+			if commonAncestor.Namespace == "html" {
+				commonAncestor = commonAncestor.LastChild
+			}
+			fallthrough
 		default:
 			commonAncestor.AppendChild(lastNode)
 		}
@@ -1249,7 +1387,7 @@
 			}
 			// Ignore the token.
 			return true
-		case a.Style, a.Script:
+		case a.Style, a.Script, a.Template:
 			return inHeadIM(p)
 		case a.Input:
 			for _, t := range p.tok.Attr {
@@ -1261,7 +1399,7 @@
 			}
 			// Otherwise drop down to the default action.
 		case a.Form:
-			if p.form != nil {
+			if p.oe.contains(a.Template) || p.form != nil {
 				// Ignore the token.
 				return true
 			}
@@ -1291,6 +1429,8 @@
 		case a.Body, a.Caption, a.Col, a.Colgroup, a.Html, a.Tbody, a.Td, a.Tfoot, a.Th, a.Thead, a.Tr:
 			// Ignore the token.
 			return true
+		case a.Template:
+			return inHeadIM(p)
 		}
 	case CommentToken:
 		p.addChild(&Node{
@@ -1386,11 +1526,13 @@
 			p.oe.pop()
 			p.acknowledgeSelfClosingTag()
 			return true
+		case a.Template:
+			return inHeadIM(p)
 		}
 	case EndTagToken:
 		switch p.tok.DataAtom {
 		case a.Colgroup:
-			if p.oe.top().DataAtom != a.Html {
+			if p.oe.top().DataAtom == a.Colgroup {
 				p.oe.pop()
 				p.im = inTableIM
 			}
@@ -1398,14 +1540,16 @@
 		case a.Col:
 			// Ignore the token.
 			return true
+		case a.Template:
+			return inHeadIM(p)
 		}
 	}
-	if p.oe.top().DataAtom != a.Html {
-		p.oe.pop()
-		p.im = inTableIM
-		return false
+	if p.oe.top().DataAtom != a.Colgroup {
+		return true
 	}
-	return true
+	p.oe.pop()
+	p.im = inTableIM
+	return false
 }
 
 // Section 12.2.6.4.13.
@@ -1597,7 +1741,7 @@
 			p.tokenizer.NextIsNotRawText()
 			// Ignore the token.
 			return true
-		case a.Script:
+		case a.Script, a.Template:
 			return inHeadIM(p)
 		}
 	case EndTagToken:
@@ -1618,6 +1762,8 @@
 			if p.popUntil(selectScope, a.Select) {
 				p.resetInsertionMode()
 			}
+		case a.Template:
+			return inHeadIM(p)
 		}
 	case CommentToken:
 		p.addChild(&Node{
@@ -1650,6 +1796,61 @@
 	return inSelectIM(p)
 }
 
+// Section 12.2.6.4.18.
+func inTemplateIM(p *parser) bool {
+	switch p.tok.Type {
+	case TextToken, CommentToken, DoctypeToken:
+		return inBodyIM(p)
+	case StartTagToken:
+		switch p.tok.DataAtom {
+		case a.Base, a.Basefont, a.Bgsound, a.Link, a.Meta, a.Noframes, a.Script, a.Style, a.Template, a.Title:
+			return inHeadIM(p)
+		case a.Caption, a.Colgroup, a.Tbody, a.Tfoot, a.Thead:
+			p.templateStack.pop()
+			p.templateStack = append(p.templateStack, inTableIM)
+			p.im = inTableIM
+			return false
+		case a.Col:
+			p.templateStack.pop()
+			p.templateStack = append(p.templateStack, inColumnGroupIM)
+			p.im = inColumnGroupIM
+			return false
+		case a.Tr:
+			p.templateStack.pop()
+			p.templateStack = append(p.templateStack, inTableBodyIM)
+			p.im = inTableBodyIM
+			return false
+		case a.Td, a.Th:
+			p.templateStack.pop()
+			p.templateStack = append(p.templateStack, inRowIM)
+			p.im = inRowIM
+			return false
+		default:
+			p.templateStack.pop()
+			p.templateStack = append(p.templateStack, inBodyIM)
+			p.im = inBodyIM
+			return false
+		}
+	case EndTagToken:
+		switch p.tok.DataAtom {
+		case a.Template:
+			return inHeadIM(p)
+		default:
+			// Ignore the token.
+			return true
+		}
+	}
+	if !p.oe.contains(a.Template) {
+		// Ignore the token.
+		return true
+	}
+	p.popUntil(defaultScope, a.Template)
+	p.clearActiveFormattingElements()
+	p.templateStack.pop()
+	p.resetInsertionMode()
+	return false
+}
+
 // Section 12.2.6.4.19.
 func afterBodyIM(p *parser) bool {
 	switch p.tok.Type {
@@ -1720,6 +1921,11 @@
 			p.acknowledgeSelfClosingTag()
 		case a.Noframes:
 			return inHeadIM(p)
+		case a.Template:
+			// TODO: remove this divergence from the HTML5 spec.
+			//
+			// See https://bugs.chromium.org/p/chromium/issues/detail?id=829668
+			return inTemplateIM(p)
 		}
 	case EndTagToken:
 		switch p.tok.DataAtom {
@@ -2064,6 +2270,9 @@
 	}
 	p.doc.AppendChild(root)
 	p.oe = nodeStack{root}
+	if context.DataAtom == a.Template {
+		p.templateStack = append(p.templateStack, inTemplateIM)
+	}
 	p.resetInsertionMode()
 
 	for n := context; n != nil; n = n.Parent {
diff --git a/html/parse_test.go b/html/parse_test.go
index 7e47d11..186f2ad 100644
--- a/html/parse_test.go
+++ b/html/parse_test.go
@@ -125,6 +125,7 @@
 
 func dumpLevel(w io.Writer, n *Node, level int) error {
 	dumpIndent(w, level)
+	level++
 	switch n.Type {
 	case ErrorNode:
 		return errors.New("unexpected ErrorNode")
@@ -140,13 +141,19 @@
 		sort.Sort(attr)
 		for _, a := range attr {
 			io.WriteString(w, "\n")
-			dumpIndent(w, level+1)
+			dumpIndent(w, level)
 			if a.Namespace != "" {
 				fmt.Fprintf(w, `%s %s="%s"`, a.Namespace, a.Key, a.Val)
 			} else {
 				fmt.Fprintf(w, `%s="%s"`, a.Key, a.Val)
 			}
 		}
+		if n.Namespace == "" && n.DataAtom == atom.Template {
+			io.WriteString(w, "\n")
+			dumpIndent(w, level)
+			level++
+			io.WriteString(w, "content")
+		}
 	case TextNode:
 		fmt.Fprintf(w, `"%s"`, n.Data)
 	case CommentNode:
@@ -176,7 +183,7 @@
 	}
 	io.WriteString(w, "\n")
 	for c := n.FirstChild; c != nil; c = c.NextSibling {
-		if err := dumpLevel(w, c, level+1); err != nil {
+		if err := dumpLevel(w, c, level); err != nil {
 			return err
 		}
 	}
diff --git a/html/testdata/webkit/template.dat b/html/testdata/webkit/template.dat
new file mode 100644
index 0000000..e25f690
--- /dev/null
+++ b/html/testdata/webkit/template.dat
@@ -0,0 +1,1117 @@
+#data
+<body><template>Hello</template>
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     <template>
+|       content
+|         "Hello"
+
+#data
+<template>Hello</template>
+#errors
+#document
+| <html>
+|   <head>
+|     <template>
+|       content
+|         "Hello"
+|   <body>
+
+#data
+<template></template><div></div>
+#errors
+#document
+| <html>
+|   <head>
+|     <template>
+|       content
+|   <body>
+|     <div>
+
+#data
+<html><template>Hello</template>
+#errors
+#document
+| <html>
+|   <head>
+|     <template>
+|       content
+|         "Hello"
+|   <body>
+
+#data
+<head><template><div></div></template></head>
+#errors
+#document
+| <html>
+|   <head>
+|     <template>
+|       content
+|         <div>
+|   <body>
+
+#data
+<div><template><div><span></template><b>
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     <div>
+|       <template>
+|         content
+|           <div>
+|             <span>
+|       <b>
+
+#data
+<div><template></div>Hello
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     <div>
+|       <template>
+|         content
+|           "Hello"
+
+#data
+<div></template></div>
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     <div>
+
+#data
+<table><template></template></table>
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     <table>
+|       <template>
+|         content
+
+#data
+<table><template></template></div>
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     <table>
+|       <template>
+|         content
+
+#data
+<table><div><template></template></div>
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     <div>
+|       <template>
+|         content
+|     <table>
+
+#data
+<table><template></template><div></div>
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     <div>
+|     <table>
+|       <template>
+|         content
+
+#data
+<table>   <template></template></table>
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     <table>
+|       "   "
+|       <template>
+|         content
+
+#data
+<table><tbody><template></template></tbody>
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     <table>
+|       <tbody>
+|         <template>
+|           content
+
+#data
+<table><tbody><template></tbody></template>
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     <table>
+|       <tbody>
+|         <template>
+|           content
+
+#data
+<table><tbody><template></template></tbody></table>
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     <table>
+|       <tbody>
+|         <template>
+|           content
+
+#data
+<table><thead><template></template></thead>
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     <table>
+|       <thead>
+|         <template>
+|           content
+
+#data
+<table><tfoot><template></template></tfoot>
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     <table>
+|       <tfoot>
+|         <template>
+|           content
+
+#data
+<select><template></template></select>
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     <select>
+|       <template>
+|         content
+
+#data
+<select><template><option></option></template></select>
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     <select>
+|       <template>
+|         content
+|           <option>
+
+#data
+<template><option></option></select><option></option></template>
+#errors
+#document
+| <html>
+|   <head>
+|     <template>
+|       content
+|         <option>
+|         <option>
+|   <body>
+
+#data
+<select><template></template><option></select>
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     <select>
+|       <template>
+|         content
+|       <option>
+
+#data
+<select><option><template></template></select>
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     <select>
+|       <option>
+|         <template>
+|           content
+
+#data
+<select><template>
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     <select>
+|       <template>
+|         content
+
+#data
+<select><option></option><template>
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     <select>
+|       <option>
+|       <template>
+|         content
+
+#data
+<select><option></option><template><option>
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     <select>
+|       <option>
+|       <template>
+|         content
+|           <option>
+
+#data
+<table><thead><template><td></template></table>
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     <table>
+|       <thead>
+|         <template>
+|           content
+|             <td>
+
+#data
+<table><template><thead></template></table>
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     <table>
+|       <template>
+|         content
+|           <thead>
+
+#data
+<body><table><template><td></tr><div></template></table>
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     <table>
+|       <template>
+|         content
+|           <td>
+|             <div>
+
+#data
+<table><template><thead></template></thead></table>
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     <table>
+|       <template>
+|         content
+|           <thead>
+
+#data
+<table><thead><template><tr></template></table>
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     <table>
+|       <thead>
+|         <template>
+|           content
+|             <tr>
+
+#data
+<table><template><tr></template></table>
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     <table>
+|       <template>
+|         content
+|           <tr>
+
+#data
+<table><tr><template><td>
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     <table>
+|       <tbody>
+|         <tr>
+|           <template>
+|             content
+|               <td>
+
+#data
+<table><template><tr><template><td></template></tr></template></table>
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     <table>
+|       <template>
+|         content
+|           <tr>
+|             <template>
+|               content
+|                 <td>
+
+#data
+<table><template><tr><template><td></td></template></tr></template></table>
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     <table>
+|       <template>
+|         content
+|           <tr>
+|             <template>
+|               content
+|                 <td>
+
+#data
+<table><template><td></template>
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     <table>
+|       <template>
+|         content
+|           <td>
+
+#data
+<body><template><td></td></template>
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     <template>
+|       content
+|         <td>
+
+#data
+<body><template><template><tr></tr></template><td></td></template>
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     <template>
+|       content
+|         <template>
+|           content
+|             <tr>
+|         <td>
+
+#data
+<table><colgroup><template><col>
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     <table>
+|       <colgroup>
+|         <template>
+|           content
+|             <col>
+
+#data
+<frameset><template><frame></frame></template></frameset>
+#errors
+#document
+| <html>
+|   <head>
+|   <frameset>
+|     <template>
+|       content
+|         <frame>
+
+#data
+<template><frame></frame></frameset><frame></frame></template>
+#errors
+#document
+| <html>
+|   <head>
+|     <template>
+|       content
+|         <frame>
+|         <frame>
+|   <body>
+
+#data
+<template><div><frameset><span></span></div><span></span></template>
+#errors
+#document
+| <html>
+|   <head>
+|     <template>
+|       content
+|         <div>
+|           <span>
+|         <span>
+|   <body>
+
+#data
+<body><template><div><frameset><span></span></div><span></span></template></body>
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     <template>
+|       content
+|         <div>
+|           <span>
+|         <span>
+
+#data
+<body><template><script>var i = 1;</script><td></td></template>
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     <template>
+|       content
+|         <script>
+|           "var i = 1;"
+|         <td>
+
+#data
+<body><template><tr><div></div></tr></template>
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     <template>
+|       content
+|         <tr>
+|         <div>
+
+#data
+<body><template><tr></tr><td></td></template>
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     <template>
+|       content
+|         <tr>
+|         <tr>
+|           <td>
+
+#data
+<body><template><td></td></tr><td></td></template>
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     <template>
+|       content
+|         <td>
+|         <td>
+
+#data
+<body><template><td></td><tbody><td></td></template>
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     <template>
+|       content
+|         <td>
+|         <td>
+
+#data
+<body><template><td></td><caption></caption><td></td></template>
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     <template>
+|       content
+|         <td>
+|         <td>
+
+#data
+<body><template><td></td><colgroup></caption><td></td></template>
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     <template>
+|       content
+|         <td>
+|         <td>
+
+#data
+<body><template><td></td></table><td></td></template>
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     <template>
+|       content
+|         <td>
+|         <td>
+
+#data
+<body><template><tr></tr><tbody><tr></tr></template>
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     <template>
+|       content
+|         <tr>
+|         <tr>
+
+#data
+<body><template><tr></tr><caption><tr></tr></template>
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     <template>
+|       content
+|         <tr>
+|         <tr>
+
+#data
+<body><template><tr></tr></table><tr></tr></template>
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     <template>
+|       content
+|         <tr>
+|         <tr>
+
+#data
+<body><template><thead></thead><caption></caption><tbody></tbody></template>
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     <template>
+|       content
+|         <thead>
+|         <caption>
+|         <tbody>
+
+#data
+<body><template><thead></thead></table><tbody></tbody></template></body>
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     <template>
+|       content
+|         <thead>
+|         <tbody>
+
+#data
+<body><template><div><tr></tr></div></template>
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     <template>
+|       content
+|         <div>
+
+#data
+<body><template><em>Hello</em></template>
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     <template>
+|       content
+|         <em>
+|           "Hello"
+
+#data
+<body><template><!--comment--></template>
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     <template>
+|       content
+|         <!-- comment -->
+
+#data
+<body><template><style></style><td></td></template>
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     <template>
+|       content
+|         <style>
+|         <td>
+
+#data
+<body><template><meta><td></td></template>
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     <template>
+|       content
+|         <meta>
+|         <td>
+
+#data
+<body><template><link><td></td></template>
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     <template>
+|       content
+|         <link>
+|         <td>
+
+#data
+<body><template><template><tr></tr></template><td></td></template>
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     <template>
+|       content
+|         <template>
+|           content
+|             <tr>
+|         <td>
+
+#data
+<body><table><colgroup><template><col></col></template></colgroup></table></body>
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     <table>
+|       <colgroup>
+|         <template>
+|           content
+|             <col>
+
+#data
+<body a=b><template><div></div><body c=d><div></div></body></template></body>
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     a="b"
+|     <template>
+|       content
+|         <div>
+|         <div>
+
+#data
+<html a=b><template><div><html b=c><span></template>
+#errors
+#document
+| <html>
+|   a="b"
+|   <head>
+|     <template>
+|       content
+|         <div>
+|           <span>
+|   <body>
+
+#data
+<html a=b><template><col></col><html b=c><col></col></template>
+#errors
+#document
+| <html>
+|   a="b"
+|   <head>
+|     <template>
+|       content
+|         <col>
+|         <col>
+|   <body>
+
+#data
+<html a=b><template><frame></frame><html b=c><frame></frame></template>
+#errors
+#document
+| <html>
+|   a="b"
+|   <head>
+|     <template>
+|       content
+|         <frame>
+|         <frame>
+|   <body>
+
+#data
+<body><template><tr></tr><template></template><td></td></template>
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     <template>
+|       content
+|         <tr>
+|         <template>
+|           content
+|         <tr>
+|           <td>
+
+#data
+<body><template><thead></thead><template><tr></tr></template><tr></tr><tfoot></tfoot></template>
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     <template>
+|       content
+|         <thead>
+|         <template>
+|           content
+|             <tr>
+|         <tbody>
+|           <tr>
+|         <tfoot>
+
+#data
+<body><template><col><colgroup>
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     <template>
+|       content
+|         <col>
+
+#data
+<body><template><col></colgroup>
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     <template>
+|       content
+|         <col>
+
+#data
+<body><template><col><colgroup></template></body>
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     <template>
+|       content
+|         <col>
+
+#data
+<body><template><col><div>
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     <template>
+|       content
+|         <col>
+
+#data
+<body><template><col></div>
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     <template>
+|       content
+|         <col>
+
+#data
+<body><template><col>Hello
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     <template>
+|       content
+|         <col>
+
+#data
+<body><template><i><menu>Foo</i>
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     <template>
+|       content
+|         <i>
+|         <menu>
+|           <i>
+|             "Foo"
+
+#data
+<body><template></div><div>Foo</div><template></template><tr></tr>
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     <template>
+|       content
+|         <div>
+|           "Foo"
+|         <template>
+|           content
+
+#data
+<body><div><template></div><tr><td>Foo</td></tr></template>
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     <div>
+|       <template>
+|         content
+|           <tr>
+|             <td>
+|               "Foo"
+
+#data
+<template></figcaption><sub><table></table>
+#errors
+#document
+| <html>
+|   <head>
+|     <template>
+|       content
+|         <sub>
+|           <table>
+|   <body>
+
+#data
+<template><template>
+#errors
+#document
+| <html>
+|   <head>
+|     <template>
+|       content
+|         <template>
+|           content
+|   <body>
+
+#data
+<template><div>
+#errors
+#document
+| <html>
+|   <head>
+|     <template>
+|       content
+|         <div>
+|   <body>
+
+#data
+<template><template><div>
+#errors
+#document
+| <html>
+|   <head>
+|     <template>
+|       content
+|         <template>
+|           content
+|             <div>
+|   <body>
+
+#data
+<template><template><script>var i
+#errors
+#document
+| <html>
+|   <head>
+|     <template>
+|       content
+|         <template>
+|           content
+|             <script>
+|               "var i"
+|   <body>
+
+#data
+<template><template><style>var i
+#errors
+#document
+| <html>
+|   <head>
+|     <template>
+|       content
+|         <template>
+|           content
+|             <style>
+|               "var i"
+|   <body>
+
+#data
+<template><svg><template>
+#errors
+#document
+| <html>
+|   <head>
+|     <template>
+|       content
+|         <svg svg>
+|           <svg template>
+|   <body>
+
+#data
+<dummy><template><span></dummy>
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     <dummy>
+|       <template>
+|         content
+|           <span>
+
+#data
+<body><table><tr><td><select><template>Foo</template><caption>A</table>
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     <table>
+|       <tbody>
+|         <tr>
+|           <td>
+|             <select>
+|               <template>
+|                 content
+|                   "Foo"
+|       <caption>
+|         "A"
+
+#data
+<body></body><template>
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     <template>
+|       content
+
+#data
+<head></head><template>
+#errors
+#document
+| <html>
+|   <head>
+|     <template>
+|       content
+|   <body>
+
+#data
+<head></head><template>Foo</template>
+#errors
+#document
+| <html>
+|   <head>
+|     <template>
+|       content
+|         "Foo"
+|   <body>