// Copyright 2010 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package html

import (
	
	
	
	

	a 
)

// A parser implements the HTML5 parsing algorithm:
// https://html.spec.whatwg.org/multipage/syntax.html#tree-construction
type parser struct {
	// tokenizer provides the tokens for the parser.
	tokenizer *Tokenizer
	// tok is the most recently read token.
	tok Token
	// Self-closing tags like <hr/> are treated as start tags, except that
	// hasSelfClosingToken is set while they are being processed.
	hasSelfClosingToken bool
	// doc is the document root element.
	doc *Node
	// The stack of open elements (section 12.2.4.2) and active formatting
	// elements (section 12.2.4.3).
	oe, afe nodeStack
	// Element pointers (section 12.2.4.4).
	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
	// or inTableText insertion mode.
	originalIM insertionMode
	// fosterParenting is whether new elements should be inserted according to
	// the foster parenting rules (section 12.2.6.1).
	fosterParenting bool
	// quirks is whether the parser is operating in "quirks mode."
	quirks bool
	// fragment is whether the parser is parsing an HTML fragment.
	fragment bool
	// context is the context element when parsing an HTML fragment
	// (section 12.4).
	context *Node
}

func ( *parser) () *Node {
	if  := .oe.top();  != nil {
		return 
	}
	return .doc
}

// Stop tags for use in popUntil. These come from section 12.2.4.2.
var (
	defaultScopeStopTags = map[string][]a.Atom{
		"":     {a.Applet, a.Caption, a.Html, a.Table, a.Td, a.Th, a.Marquee, a.Object, a.Template},
		"math": {a.AnnotationXml, a.Mi, a.Mn, a.Mo, a.Ms, a.Mtext},
		"svg":  {a.Desc, a.ForeignObject, a.Title},
	}
)

type scope int

const (
	defaultScope scope = iota
	listItemScope
	buttonScope
	tableScope
	tableRowScope
	tableBodyScope
	selectScope
)

// popUntil pops the stack of open elements at the highest element whose tag
// is in matchTags, provided there is no higher element in the scope's stop
// tags (as defined in section 12.2.4.2). It returns whether or not there was
// such an element. If there was not, popUntil leaves the stack unchanged.
//
// For example, the set of stop tags for table scope is: "html", "table". If
// the stack was:
// ["html", "body", "font", "table", "b", "i", "u"]
// then popUntil(tableScope, "font") would return false, but
// popUntil(tableScope, "i") would return true and the stack would become:
// ["html", "body", "font", "table", "b"]
//
// If an element's tag is in both the stop tags and matchTags, then the stack
// will be popped and the function returns true (provided, of course, there was
// no higher element in the stack that was also in the stop tags). For example,
// popUntil(tableScope, "table") returns true and leaves:
// ["html", "body", "font"]
func ( *parser) ( scope,  ...a.Atom) bool {
	if  := .indexOfElementInScope(, ...);  != -1 {
		.oe = .oe[:]
		return true
	}
	return false
}

// indexOfElementInScope returns the index in p.oe of the highest element whose
// tag is in matchTags that is in scope. If no matching element is in scope, it
// returns -1.
func ( *parser) ( scope,  ...a.Atom) int {
	for  := len(.oe) - 1;  >= 0; -- {
		 := .oe[].DataAtom
		if .oe[].Namespace == "" {
			for ,  := range  {
				if  ==  {
					return 
				}
			}
			switch  {
			case defaultScope:
				// No-op.
			case listItemScope:
				if  == a.Ol ||  == a.Ul {
					return -1
				}
			case buttonScope:
				if  == a.Button {
					return -1
				}
			case tableScope:
				if  == a.Html ||  == a.Table ||  == a.Template {
					return -1
				}
			case selectScope:
				if  != a.Optgroup &&  != a.Option {
					return -1
				}
			default:
				panic("unreachable")
			}
		}
		switch  {
		case defaultScope, listItemScope, buttonScope:
			for ,  := range defaultScopeStopTags[.oe[].Namespace] {
				if  ==  {
					return -1
				}
			}
		}
	}
	return -1
}

// elementInScope is like popUntil, except that it doesn't modify the stack of
// open elements.
func ( *parser) ( scope,  ...a.Atom) bool {
	return .indexOfElementInScope(, ...) != -1
}

// clearStackToContext pops elements off the stack of open elements until a
// scope-defined element is found.
func ( *parser) ( scope) {
	for  := len(.oe) - 1;  >= 0; -- {
		 := .oe[].DataAtom
		switch  {
		case tableScope:
			if  == a.Html ||  == a.Table ||  == a.Template {
				.oe = .oe[:+1]
				return
			}
		case tableRowScope:
			if  == a.Html ||  == a.Tr ||  == a.Template {
				.oe = .oe[:+1]
				return
			}
		case tableBodyScope:
			if  == a.Html ||  == a.Tbody ||  == a.Tfoot ||  == a.Thead ||  == a.Template {
				.oe = .oe[:+1]
				return
			}
		default:
			panic("unreachable")
		}
	}
}

// parseGenericRawTextElement implements the generic raw text element parsing
// algorithm defined in 12.2.6.2.
// https://html.spec.whatwg.org/multipage/parsing.html#parsing-elements-that-contain-only-text
// TODO: Since both RAWTEXT and RCDATA states are treated as tokenizer's part
// officially, need to make tokenizer consider both states.
func ( *parser) () {
	.addElement()
	.originalIM = .im
	.im = textIM
}

// generateImpliedEndTags pops nodes off the stack of open elements as long as
// the top node has a tag name of dd, dt, li, optgroup, option, p, rb, rp, rt or rtc.
// If exceptions are specified, nodes with that name will not be popped off.
func ( *parser) ( ...string) {
	var  int
:
	for  = len(.oe) - 1;  >= 0; -- {
		 := .oe[]
		if .Type != ElementNode {
			break
		}
		switch .DataAtom {
		case a.Dd, a.Dt, a.Li, a.Optgroup, a.Option, a.P, a.Rb, a.Rp, a.Rt, a.Rtc:
			for ,  := range  {
				if .Data ==  {
					break 
				}
			}
			continue
		}
		break
	}

	.oe = .oe[:+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 ( *parser) ( *Node) {
	if .shouldFosterParent() {
		.fosterParent()
	} else {
		.top().AppendChild()
	}

	if .Type == ElementNode {
		.oe = append(.oe, )
	}
}

// shouldFosterParent returns whether the next node to be added should be
// foster parented.
func ( *parser) () bool {
	if .fosterParenting {
		switch .top().DataAtom {
		case a.Table, a.Tbody, a.Tfoot, a.Thead, a.Tr:
			return true
		}
	}
	return false
}

// fosterParent adds a child node according to the foster parenting rules.
// Section 12.2.6.1, "foster parenting".
func ( *parser) ( *Node) {
	var , , ,  *Node
	var  int
	for  = len(.oe) - 1;  >= 0; -- {
		if .oe[].DataAtom == a.Table {
			 = .oe[]
			break
		}
	}

	var  int
	for  = len(.oe) - 1;  >= 0; -- {
		if .oe[].DataAtom == a.Template {
			 = .oe[]
			break
		}
	}

	if  != nil && ( == nil ||  > ) {
		.AppendChild()
		return
	}

	if  == nil {
		// The foster parent is the html element.
		 = .oe[0]
	} else {
		 = .Parent
	}
	if  == nil {
		 = .oe[-1]
	}

	if  != nil {
		 = .PrevSibling
	} else {
		 = .LastChild
	}
	if  != nil && .Type == TextNode && .Type == TextNode {
		.Data += .Data
		return
	}

	.InsertBefore(, )
}

// addText adds text to the preceding node if it is a text node, or else it
// calls addChild with a new text node.
func ( *parser) ( string) {
	if  == "" {
		return
	}

	if .shouldFosterParent() {
		.fosterParent(&Node{
			Type: TextNode,
			Data: ,
		})
		return
	}

	 := .top()
	if  := .LastChild;  != nil && .Type == TextNode {
		.Data += 
		return
	}
	.addChild(&Node{
		Type: TextNode,
		Data: ,
	})
}

// addElement adds a child element based on the current token.
func ( *parser) () {
	.addChild(&Node{
		Type:     ElementNode,
		DataAtom: .tok.DataAtom,
		Data:     .tok.Data,
		Attr:     .tok.Attr,
	})
}

// Section 12.2.4.3.
func ( *parser) () {
	,  := .tok.DataAtom, .tok.Attr
	.addElement()

	// Implement the Noah's Ark clause, but with three per family instead of two.
	 := 0
:
	for  := len(.afe) - 1;  >= 0; -- {
		 := .afe[]
		if .Type == scopeMarkerNode {
			break
		}
		if .Type != ElementNode {
			continue
		}
		if .Namespace != "" {
			continue
		}
		if .DataAtom !=  {
			continue
		}
		if len(.Attr) != len() {
			continue
		}
	:
		for ,  := range .Attr {
			for ,  := range  {
				if .Key == .Key && .Namespace == .Namespace && .Val == .Val {
					// Found a match for this attribute, continue with the next attribute.
					continue 
				}
			}
			// If we get here, there is no attribute that matches a.
			// Therefore the element is not identical to the new one.
			continue 
		}

		++
		if  >= 3 {
			.afe.remove()
		}
	}

	.afe = append(.afe, .top())
}

// Section 12.2.4.3.
func ( *parser) () {
	for {
		if  := .afe.pop(); len(.afe) == 0 || .Type == scopeMarkerNode {
			return
		}
	}
}

// Section 12.2.4.3.
func ( *parser) () {
	 := .afe.top()
	if  == nil {
		return
	}
	if .Type == scopeMarkerNode || .oe.index() != -1 {
		return
	}
	 := len(.afe) - 1
	for .Type != scopeMarkerNode && .oe.index() == -1 {
		if  == 0 {
			 = -1
			break
		}
		--
		 = .afe[]
	}
	for {
		++
		 := .afe[].clone()
		.addChild()
		.afe[] = 
		if  == len(.afe)-1 {
			break
		}
	}
}

// Section 12.2.5.
func ( *parser) () {
	.hasSelfClosingToken = false
}

// An insertion mode (section 12.2.4.1) is the state transition function from
// a particular state in the HTML5 parser's state machine. It updates the
// parser's fields depending on parser.tok (where ErrorToken means EOF).
// It returns whether the token was consumed.
type insertionMode func(*parser) bool

// setOriginalIM sets the insertion mode to return to after completing a text or
// inTableText insertion mode.
// Section 12.2.4.1, "using the rules for".
func ( *parser) () {
	if .originalIM != nil {
		panic("html: bad parser state: originalIM was set twice")
	}
	.originalIM = .im
}

// Section 12.2.4.1, "reset the insertion mode".
func ( *parser) () {
	for  := len(.oe) - 1;  >= 0; -- {
		 := .oe[]
		 :=  == 0
		if  && .context != nil {
			 = .context
		}

		switch .DataAtom {
		case a.Select:
			if ! {
				for ,  := , .oe[0];  != ; {
					 = .oe[.oe.index()-1]
					switch .DataAtom {
					case a.Template:
						.im = inSelectIM
						return
					case a.Table:
						.im = inSelectInTableIM
						return
					}
				}
			}
			.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
			.im = inCellIM
		case a.Tr:
			.im = inRowIM
		case a.Tbody, a.Thead, a.Tfoot:
			.im = inTableBodyIM
		case a.Caption:
			.im = inCaptionIM
		case a.Colgroup:
			.im = inColumnGroupIM
		case a.Table:
			.im = inTableIM
		case a.Template:
			// TODO: remove this divergence from the HTML5 spec.
			if .Namespace != "" {
				continue
			}
			.im = .templateStack.top()
		case a.Head:
			// TODO: remove this divergence from the HTML5 spec.
			//
			// See https://bugs.chromium.org/p/chromium/issues/detail?id=829668
			.im = inHeadIM
		case a.Body:
			.im = inBodyIM
		case a.Frameset:
			.im = inFramesetIM
		case a.Html:
			if .head == nil {
				.im = beforeHeadIM
			} else {
				.im = afterHeadIM
			}
		default:
			if  {
				.im = inBodyIM
				return
			}
			continue
		}
		return
	}
}

const whitespace = " \t\r\n\f"

// Section 12.2.6.4.1.
func initialIM( *parser) bool {
	switch .tok.Type {
	case TextToken:
		.tok.Data = strings.TrimLeft(.tok.Data, whitespace)
		if len(.tok.Data) == 0 {
			// It was all whitespace, so ignore it.
			return true
		}
	case CommentToken:
		.doc.AppendChild(&Node{
			Type: CommentNode,
			Data: .tok.Data,
		})
		return true
	case DoctypeToken:
		,  := parseDoctype(.tok.Data)
		.doc.AppendChild()
		.quirks = 
		.im = beforeHTMLIM
		return true
	}
	.quirks = true
	.im = beforeHTMLIM
	return false
}

// Section 12.2.6.4.2.
func beforeHTMLIM( *parser) bool {
	switch .tok.Type {
	case DoctypeToken:
		// Ignore the token.
		return true
	case TextToken:
		.tok.Data = strings.TrimLeft(.tok.Data, whitespace)
		if len(.tok.Data) == 0 {
			// It was all whitespace, so ignore it.
			return true
		}
	case StartTagToken:
		if .tok.DataAtom == a.Html {
			.addElement()
			.im = beforeHeadIM
			return true
		}
	case EndTagToken:
		switch .tok.DataAtom {
		case a.Head, a.Body, a.Html, a.Br:
			.parseImpliedToken(StartTagToken, a.Html, a.Html.String())
			return false
		default:
			// Ignore the token.
			return true
		}
	case CommentToken:
		.doc.AppendChild(&Node{
			Type: CommentNode,
			Data: .tok.Data,
		})
		return true
	}
	.parseImpliedToken(StartTagToken, a.Html, a.Html.String())
	return false
}

// Section 12.2.6.4.3.
func beforeHeadIM( *parser) bool {
	switch .tok.Type {
	case TextToken:
		.tok.Data = strings.TrimLeft(.tok.Data, whitespace)
		if len(.tok.Data) == 0 {
			// It was all whitespace, so ignore it.
			return true
		}
	case StartTagToken:
		switch .tok.DataAtom {
		case a.Head:
			.addElement()
			.head = .top()
			.im = inHeadIM
			return true
		case a.Html:
			return inBodyIM()
		}
	case EndTagToken:
		switch .tok.DataAtom {
		case a.Head, a.Body, a.Html, a.Br:
			.parseImpliedToken(StartTagToken, a.Head, a.Head.String())
			return false
		default:
			// Ignore the token.
			return true
		}
	case CommentToken:
		.addChild(&Node{
			Type: CommentNode,
			Data: .tok.Data,
		})
		return true
	case DoctypeToken:
		// Ignore the token.
		return true
	}

	.parseImpliedToken(StartTagToken, a.Head, a.Head.String())
	return false
}

// Section 12.2.6.4.4.
func inHeadIM( *parser) bool {
	switch .tok.Type {
	case TextToken:
		 := strings.TrimLeft(.tok.Data, whitespace)
		if len() < len(.tok.Data) {
			// Add the initial whitespace to the current node.
			.addText(.tok.Data[:len(.tok.Data)-len()])
			if  == "" {
				return true
			}
			.tok.Data = 
		}
	case StartTagToken:
		switch .tok.DataAtom {
		case a.Html:
			return inBodyIM()
		case a.Base, a.Basefont, a.Bgsound, a.Link, a.Meta:
			.addElement()
			.oe.pop()
			.acknowledgeSelfClosingTag()
			return true
		case a.Noscript:
			if .scripting {
				.parseGenericRawTextElement()
				return true
			}
			.addElement()
			.im = inHeadNoscriptIM
			// Don't let the tokenizer go into raw text mode when scripting is disabled.
			.tokenizer.NextIsNotRawText()
			return true
		case a.Script, a.Title:
			.addElement()
			.setOriginalIM()
			.im = textIM
			return true
		case a.Noframes, a.Style:
			.parseGenericRawTextElement()
			return true
		case a.Head:
			// Ignore the token.
			return true
		case a.Template:
			// TODO: remove this divergence from the HTML5 spec.
			//
			// We don't handle all of the corner cases when mixing foreign
			// content (i.e. <math> or <svg>) with <template>. Without this
			// early return, we can get into an infinite loop, possibly because
			// of the "TODO... further divergence" a little below.
			//
			// As a workaround, if we are mixing foreign content and templates,
			// just ignore the rest of the HTML. Foreign content is rare and a
			// relatively old HTML feature. Templates are also rare and a
			// relatively new HTML feature. Their combination is very rare.
			for ,  := range .oe {
				if .Namespace != "" {
					.im = ignoreTheRemainingTokens
					return true
				}
			}

			.addElement()
			.afe = append(.afe, &scopeMarker)
			.framesetOK = false
			.im = inTemplateIM
			.templateStack = append(.templateStack, inTemplateIM)
			return true
		}
	case EndTagToken:
		switch .tok.DataAtom {
		case a.Head:
			.oe.pop()
			.im = afterHeadIM
			return true
		case a.Body, a.Html, a.Br:
			.parseImpliedToken(EndTagToken, a.Head, a.Head.String())
			return false
		case a.Template:
			if !.oe.contains(a.Template) {
				return true
			}
			// TODO: remove this further divergence from the HTML5 spec.
			//
			// See https://bugs.chromium.org/p/chromium/issues/detail?id=829668
			.generateImpliedEndTags()
			for  := len(.oe) - 1;  >= 0; -- {
				if  := .oe[]; .Namespace == "" && .DataAtom == a.Template {
					.oe = .oe[:]
					break
				}
			}
			.clearActiveFormattingElements()
			.templateStack.pop()
			.resetInsertionMode()
			return true
		default:
			// Ignore the token.
			return true
		}
	case CommentToken:
		.addChild(&Node{
			Type: CommentNode,
			Data: .tok.Data,
		})
		return true
	case DoctypeToken:
		// Ignore the token.
		return true
	}

	.parseImpliedToken(EndTagToken, a.Head, a.Head.String())
	return false
}

// Section 12.2.6.4.5.
func inHeadNoscriptIM( *parser) bool {
	switch .tok.Type {
	case DoctypeToken:
		// Ignore the token.
		return true
	case StartTagToken:
		switch .tok.DataAtom {
		case a.Html:
			return inBodyIM()
		case a.Basefont, a.Bgsound, a.Link, a.Meta, a.Noframes, a.Style:
			return inHeadIM()
		case a.Head:
			// Ignore the token.
			return true
		case a.Noscript:
			// Don't let the tokenizer go into raw text mode even when a <noscript>
			// tag is in "in head noscript" insertion mode.
			.tokenizer.NextIsNotRawText()
			// Ignore the token.
			return true
		}
	case EndTagToken:
		switch .tok.DataAtom {
		case a.Noscript, a.Br:
		default:
			// Ignore the token.
			return true
		}
	case TextToken:
		 := strings.TrimLeft(.tok.Data, whitespace)
		if len() == 0 {
			// It was all whitespace.
			return inHeadIM()
		}
	case CommentToken:
		return inHeadIM()
	}
	.oe.pop()
	if .top().DataAtom != a.Head {
		panic("html: the new current node will be a head element.")
	}
	.im = inHeadIM
	if .tok.DataAtom == a.Noscript {
		return true
	}
	return false
}

// Section 12.2.6.4.6.
func afterHeadIM( *parser) bool {
	switch .tok.Type {
	case TextToken:
		 := strings.TrimLeft(.tok.Data, whitespace)
		if len() < len(.tok.Data) {
			// Add the initial whitespace to the current node.
			.addText(.tok.Data[:len(.tok.Data)-len()])
			if  == "" {
				return true
			}
			.tok.Data = 
		}
	case StartTagToken:
		switch .tok.DataAtom {
		case a.Html:
			return inBodyIM()
		case a.Body:
			.addElement()
			.framesetOK = false
			.im = inBodyIM
			return true
		case a.Frameset:
			.addElement()
			.im = inFramesetIM
			return true
		case a.Base, a.Basefont, a.Bgsound, a.Link, a.Meta, a.Noframes, a.Script, a.Style, a.Template, a.Title:
			.oe = append(.oe, .head)
			defer .oe.remove(.head)
			return inHeadIM()
		case a.Head:
			// Ignore the token.
			return true
		}
	case EndTagToken:
		switch .tok.DataAtom {
		case a.Body, a.Html, a.Br:
			// Drop down to creating an implied <body> tag.
		case a.Template:
			return inHeadIM()
		default:
			// Ignore the token.
			return true
		}
	case CommentToken:
		.addChild(&Node{
			Type: CommentNode,
			Data: .tok.Data,
		})
		return true
	case DoctypeToken:
		// Ignore the token.
		return true
	}

	.parseImpliedToken(StartTagToken, a.Body, a.Body.String())
	.framesetOK = true
	return false
}

// copyAttributes copies attributes of src not found on dst to dst.
func copyAttributes( *Node,  Token) {
	if len(.Attr) == 0 {
		return
	}
	 := map[string]string{}
	for ,  := range .Attr {
		[.Key] = .Val
	}
	for ,  := range .Attr {
		if ,  := [.Key]; ! {
			.Attr = append(.Attr, )
			[.Key] = .Val
		}
	}
}

// Section 12.2.6.4.7.
func inBodyIM( *parser) bool {
	switch .tok.Type {
	case TextToken:
		 := .tok.Data
		switch  := .oe.top(); .DataAtom {
		case a.Pre, a.Listing:
			if .FirstChild == nil {
				// Ignore a newline at the start of a <pre> block.
				if  != "" && [0] == '\r' {
					 = [1:]
				}
				if  != "" && [0] == '\n' {
					 = [1:]
				}
			}
		}
		 = strings.Replace(, "\x00", "", -1)
		if  == "" {
			return true
		}
		.reconstructActiveFormattingElements()
		.addText()
		if .framesetOK && strings.TrimLeft(, whitespace) != "" {
			// There were non-whitespace characters inserted.
			.framesetOK = false
		}
	case StartTagToken:
		switch .tok.DataAtom {
		case a.Html:
			if .oe.contains(a.Template) {
				return true
			}
			copyAttributes(.oe[0], .tok)
		case a.Base, a.Basefont, a.Bgsound, a.Link, a.Meta, a.Noframes, a.Script, a.Style, a.Template, a.Title:
			return inHeadIM()
		case a.Body:
			if .oe.contains(a.Template) {
				return true
			}
			if len(.oe) >= 2 {
				 := .oe[1]
				if .Type == ElementNode && .DataAtom == a.Body {
					.framesetOK = false
					copyAttributes(, .tok)
				}
			}
		case a.Frameset:
			if !.framesetOK || len(.oe) < 2 || .oe[1].DataAtom != a.Body {
				// Ignore the token.
				return true
			}
			 := .oe[1]
			if .Parent != nil {
				.Parent.RemoveChild()
			}
			.oe = .oe[:1]
			.addElement()
			.im = inFramesetIM
			return true
		case a.Address, a.Article, a.Aside, a.Blockquote, a.Center, a.Details, a.Dialog, a.Dir, a.Div, a.Dl, a.Fieldset, a.Figcaption, a.Figure, a.Footer, a.Header, a.Hgroup, a.Main, a.Menu, a.Nav, a.Ol, a.P, a.Section, a.Summary, a.Ul:
			.popUntil(buttonScope, a.P)
			.addElement()
		case a.H1, a.H2, a.H3, a.H4, a.H5, a.H6:
			.popUntil(buttonScope, a.P)
			switch  := .top(); .DataAtom {
			case a.H1, a.H2, a.H3, a.H4, a.H5, a.H6:
				.oe.pop()
			}
			.addElement()
		case a.Pre, a.Listing:
			.popUntil(buttonScope, a.P)
			.addElement()
			// The newline, if any, will be dealt with by the TextToken case.
			.framesetOK = false
		case a.Form:
			if .form != nil && !.oe.contains(a.Template) {
				// Ignore the token
				return true
			}
			.popUntil(buttonScope, a.P)
			.addElement()
			if !.oe.contains(a.Template) {
				.form = .top()
			}
		case a.Li:
			.framesetOK = false
			for  := len(.oe) - 1;  >= 0; -- {
				 := .oe[]
				switch .DataAtom {
				case a.Li:
					.oe = .oe[:]
				case a.Address, a.Div, a.P:
					continue
				default:
					if !isSpecialElement() {
						continue
					}
				}
				break
			}
			.popUntil(buttonScope, a.P)
			.addElement()
		case a.Dd, a.Dt:
			.framesetOK = false
			for  := len(.oe) - 1;  >= 0; -- {
				 := .oe[]
				switch .DataAtom {
				case a.Dd, a.Dt:
					.oe = .oe[:]
				case a.Address, a.Div, a.P:
					continue
				default:
					if !isSpecialElement() {
						continue
					}
				}
				break
			}
			.popUntil(buttonScope, a.P)
			.addElement()
		case a.Plaintext:
			.popUntil(buttonScope, a.P)
			.addElement()
		case a.Button:
			.popUntil(defaultScope, a.Button)
			.reconstructActiveFormattingElements()
			.addElement()
			.framesetOK = false
		case a.A:
			for  := len(.afe) - 1;  >= 0 && .afe[].Type != scopeMarkerNode; -- {
				if  := .afe[]; .Type == ElementNode && .DataAtom == a.A {
					.inBodyEndTagFormatting(a.A, "a")
					.oe.remove()
					.afe.remove()
					break
				}
			}
			.reconstructActiveFormattingElements()
			.addFormattingElement()
		case a.B, a.Big, a.Code, a.Em, a.Font, a.I, a.S, a.Small, a.Strike, a.Strong, a.Tt, a.U:
			.reconstructActiveFormattingElements()
			.addFormattingElement()
		case a.Nobr:
			.reconstructActiveFormattingElements()
			if .elementInScope(defaultScope, a.Nobr) {
				.inBodyEndTagFormatting(a.Nobr, "nobr")
				.reconstructActiveFormattingElements()
			}
			.addFormattingElement()
		case a.Applet, a.Marquee, a.Object:
			.reconstructActiveFormattingElements()
			.addElement()
			.afe = append(.afe, &scopeMarker)
			.framesetOK = false
		case a.Table:
			if !.quirks {
				.popUntil(buttonScope, a.P)
			}
			.addElement()
			.framesetOK = false
			.im = inTableIM
			return true
		case a.Area, a.Br, a.Embed, a.Img, a.Input, a.Keygen, a.Wbr:
			.reconstructActiveFormattingElements()
			.addElement()
			.oe.pop()
			.acknowledgeSelfClosingTag()
			if .tok.DataAtom == a.Input {
				for ,  := range .tok.Attr {
					if .Key == "type" {
						if strings.ToLower(.Val) == "hidden" {
							// Skip setting framesetOK = false
							return true
						}
					}
				}
			}
			.framesetOK = false
		case a.Param, a.Source, a.Track:
			.addElement()
			.oe.pop()
			.acknowledgeSelfClosingTag()
		case a.Hr:
			.popUntil(buttonScope, a.P)
			.addElement()
			.oe.pop()
			.acknowledgeSelfClosingTag()
			.framesetOK = false
		case a.Image:
			.tok.DataAtom = a.Img
			.tok.Data = a.Img.String()
			return false
		case a.Textarea:
			.addElement()
			.setOriginalIM()
			.framesetOK = false
			.im = textIM
		case a.Xmp:
			.popUntil(buttonScope, a.P)
			.reconstructActiveFormattingElements()
			.framesetOK = false
			.parseGenericRawTextElement()
		case a.Iframe:
			.framesetOK = false
			.parseGenericRawTextElement()
		case a.Noembed:
			.parseGenericRawTextElement()
		case a.Noscript:
			if .scripting {
				.parseGenericRawTextElement()
				return true
			}
			.reconstructActiveFormattingElements()
			.addElement()
			// Don't let the tokenizer go into raw text mode when scripting is disabled.
			.tokenizer.NextIsNotRawText()
		case a.Select:
			.reconstructActiveFormattingElements()
			.addElement()
			.framesetOK = false
			.im = inSelectIM
			return true
		case a.Optgroup, a.Option:
			if .top().DataAtom == a.Option {
				.oe.pop()
			}
			.reconstructActiveFormattingElements()
			.addElement()
		case a.Rb, a.Rtc:
			if .elementInScope(defaultScope, a.Ruby) {
				.generateImpliedEndTags()
			}
			.addElement()
		case a.Rp, a.Rt:
			if .elementInScope(defaultScope, a.Ruby) {
				.generateImpliedEndTags("rtc")
			}
			.addElement()
		case a.Math, a.Svg:
			.reconstructActiveFormattingElements()
			if .tok.DataAtom == a.Math {
				adjustAttributeNames(.tok.Attr, mathMLAttributeAdjustments)
			} else {
				adjustAttributeNames(.tok.Attr, svgAttributeAdjustments)
			}
			adjustForeignAttributes(.tok.Attr)
			.addElement()
			.top().Namespace = .tok.Data
			if .hasSelfClosingToken {
				.oe.pop()
				.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:
			// Ignore the token.
		default:
			.reconstructActiveFormattingElements()
			.addElement()
		}
	case EndTagToken:
		switch .tok.DataAtom {
		case a.Body:
			if .elementInScope(defaultScope, a.Body) {
				.im = afterBodyIM
			}
		case a.Html:
			if .elementInScope(defaultScope, a.Body) {
				.parseImpliedToken(EndTagToken, a.Body, a.Body.String())
				return false
			}
			return true
		case a.Address, a.Article, a.Aside, a.Blockquote, a.Button, a.Center, a.Details, a.Dialog, a.Dir, a.Div, a.Dl, a.Fieldset, a.Figcaption, a.Figure, a.Footer, a.Header, a.Hgroup, a.Listing, a.Main, a.Menu, a.Nav, a.Ol, a.Pre, a.Section, a.Summary, a.Ul:
			.popUntil(defaultScope, .tok.DataAtom)
		case a.Form:
			if .oe.contains(a.Template) {
				 := .indexOfElementInScope(defaultScope, a.Form)
				if  == -1 {
					// Ignore the token.
					return true
				}
				.generateImpliedEndTags()
				if .oe[].DataAtom != a.Form {
					// Ignore the token.
					return true
				}
				.popUntil(defaultScope, a.Form)
			} else {
				 := .form
				.form = nil
				 := .indexOfElementInScope(defaultScope, a.Form)
				if  == nil ||  == -1 || .oe[] !=  {
					// Ignore the token.
					return true
				}
				.generateImpliedEndTags()
				.oe.remove()
			}
		case a.P:
			if !.elementInScope(buttonScope, a.P) {
				.parseImpliedToken(StartTagToken, a.P, a.P.String())
			}
			.popUntil(buttonScope, a.P)
		case a.Li:
			.popUntil(listItemScope, a.Li)
		case a.Dd, a.Dt:
			.popUntil(defaultScope, .tok.DataAtom)
		case a.H1, a.H2, a.H3, a.H4, a.H5, a.H6:
			.popUntil(defaultScope, a.H1, a.H2, a.H3, a.H4, a.H5, a.H6)
		case a.A, a.B, a.Big, a.Code, a.Em, a.Font, a.I, a.Nobr, a.S, a.Small, a.Strike, a.Strong, a.Tt, a.U:
			.inBodyEndTagFormatting(.tok.DataAtom, .tok.Data)
		case a.Applet, a.Marquee, a.Object:
			if .popUntil(defaultScope, .tok.DataAtom) {
				.clearActiveFormattingElements()
			}
		case a.Br:
			.tok.Type = StartTagToken
			return false
		case a.Template:
			return inHeadIM()
		default:
			.inBodyEndTagOther(.tok.DataAtom, .tok.Data)
		}
	case CommentToken:
		.addChild(&Node{
			Type: CommentNode,
			Data: .tok.Data,
		})
	case ErrorToken:
		// TODO: remove this divergence from the HTML5 spec.
		if len(.templateStack) > 0 {
			.im = inTemplateIM
			return false
		}
		for ,  := range .oe {
			switch .DataAtom {
			case a.Dd, a.Dt, a.Li, a.Optgroup, a.Option, a.P, a.Rb, a.Rp, a.Rt, a.Rtc, a.Tbody, a.Td, a.Tfoot, a.Th,
				a.Thead, a.Tr, a.Body, a.Html:
			default:
				return true
			}
		}
	}

	return true
}

func ( *parser) ( a.Atom,  string) {
	// This is the "adoption agency" algorithm, described at
	// https://html.spec.whatwg.org/multipage/syntax.html#adoptionAgency

	// TODO: this is a fairly literal line-by-line translation of that algorithm.
	// Once the code successfully parses the comprehensive test suite, we should
	// refactor this code to be more idiomatic.

	// Steps 1-2
	if  := .oe.top(); .Data ==  && .afe.index() == -1 {
		.oe.pop()
		return
	}

	// Steps 3-5. The outer loop.
	for  := 0;  < 8; ++ {
		// Step 6. Find the formatting element.
		var  *Node
		for  := len(.afe) - 1;  >= 0; -- {
			if .afe[].Type == scopeMarkerNode {
				break
			}
			if .afe[].DataAtom ==  {
				 = .afe[]
				break
			}
		}
		if  == nil {
			.inBodyEndTagOther(, )
			return
		}

		// Step 7. Ignore the tag if formatting element is not in the stack of open elements.
		 := .oe.index()
		if  == -1 {
			.afe.remove()
			return
		}
		// Step 8. Ignore the tag if formatting element is not in the scope.
		if !.elementInScope(defaultScope, ) {
			// Ignore the tag.
			return
		}

		// Step 9. This step is omitted because it's just a parse error but no need to return.

		// Steps 10-11. Find the furthest block.
		var  *Node
		for ,  := range .oe[:] {
			if isSpecialElement() {
				 = 
				break
			}
		}
		if  == nil {
			 := .oe.pop()
			for  !=  {
				 = .oe.pop()
			}
			.afe.remove()
			return
		}

		// Steps 12-13. Find the common ancestor and bookmark node.
		 := .oe[-1]
		 := .afe.index()

		// Step 14. The inner loop. Find the lastNode to reparent.
		 := 
		 := 
		 := .oe.index()
		// Step 14.1.
		 := 0
		for {
			// Step 14.2.
			++
			// Step. 14.3.
			--
			 = .oe[]
			// Step 14.4. Go to the next step if node is formatting element.
			if  ==  {
				break
			}
			// Step 14.5. Remove node from the list of active formatting elements if
			// inner loop counter is greater than three and node is in the list of
			// active formatting elements.
			if  := .afe.index();  > 3 &&  > -1 {
				.afe.remove()
				// If any element of the list of active formatting elements is removed,
				// we need to take care whether bookmark should be decremented or not.
				// This is because the value of bookmark may exceed the size of the
				// list by removing elements from the list.
				if  <=  {
					--
				}
				continue
			}
			// Step 14.6. Continue the next inner loop if node is not in the list of
			// active formatting elements.
			if .afe.index() == -1 {
				.oe.remove()
				continue
			}
			// Step 14.7.
			 := .clone()
			.afe[.afe.index()] = 
			.oe[.oe.index()] = 
			 = 
			// Step 14.8.
			if  ==  {
				 = .afe.index() + 1
			}
			// Step 14.9.
			if .Parent != nil {
				.Parent.RemoveChild()
			}
			.AppendChild()
			// Step 14.10.
			 = 
		}

		// Step 15. Reparent lastNode to the common ancestor,
		// or for misnested table nodes, to the foster parent.
		if .Parent != nil {
			.Parent.RemoveChild()
		}
		switch .DataAtom {
		case a.Table, a.Tbody, a.Tfoot, a.Thead, a.Tr:
			.fosterParent()
		default:
			.AppendChild()
		}

		// Steps 16-18. Reparent nodes from the furthest block's children
		// to a clone of the formatting element.
		 := .clone()
		reparentChildren(, )
		.AppendChild()

		// Step 19. Fix up the list of active formatting elements.
		if  := .afe.index();  != -1 &&  <  {
			// Move the bookmark with the rest of the list.
			--
		}
		.afe.remove()
		.afe.insert(, )

		// Step 20. Fix up the stack of open elements.
		.oe.remove()
		.oe.insert(.oe.index()+1, )
	}
}

// inBodyEndTagOther performs the "any other end tag" algorithm for inBodyIM.
// "Any other end tag" handling from 12.2.6.5 The rules for parsing tokens in foreign content
// https://html.spec.whatwg.org/multipage/syntax.html#parsing-main-inforeign
func ( *parser) ( a.Atom,  string) {
	for  := len(.oe) - 1;  >= 0; -- {
		// Two element nodes have the same tag if they have the same Data (a
		// string-typed field). As an optimization, for common HTML tags, each
		// Data string is assigned a unique, non-zero DataAtom (a uint32-typed
		// field), since integer comparison is faster than string comparison.
		// Uncommon (custom) tags get a zero DataAtom.
		//
		// The if condition here is equivalent to (p.oe[i].Data == tagName).
		if (.oe[].DataAtom == ) &&
			(( != 0) || (.oe[].Data == )) {
			.oe = .oe[:]
			break
		}
		if isSpecialElement(.oe[]) {
			break
		}
	}
}

// Section 12.2.6.4.8.
func textIM( *parser) bool {
	switch .tok.Type {
	case ErrorToken:
		.oe.pop()
	case TextToken:
		 := .tok.Data
		if  := .oe.top(); .DataAtom == a.Textarea && .FirstChild == nil {
			// Ignore a newline at the start of a <textarea> block.
			if  != "" && [0] == '\r' {
				 = [1:]
			}
			if  != "" && [0] == '\n' {
				 = [1:]
			}
		}
		if  == "" {
			return true
		}
		.addText()
		return true
	case EndTagToken:
		.oe.pop()
	}
	.im = .originalIM
	.originalIM = nil
	return .tok.Type == EndTagToken
}

// Section 12.2.6.4.9.
func inTableIM( *parser) bool {
	switch .tok.Type {
	case TextToken:
		.tok.Data = strings.Replace(.tok.Data, "\x00", "", -1)
		switch .oe.top().DataAtom {
		case a.Table, a.Tbody, a.Tfoot, a.Thead, a.Tr:
			if strings.Trim(.tok.Data, whitespace) == "" {
				.addText(.tok.Data)
				return true
			}
		}
	case StartTagToken:
		switch .tok.DataAtom {
		case a.Caption:
			.clearStackToContext(tableScope)
			.afe = append(.afe, &scopeMarker)
			.addElement()
			.im = inCaptionIM
			return true
		case a.Colgroup:
			.clearStackToContext(tableScope)
			.addElement()
			.im = inColumnGroupIM
			return true
		case a.Col:
			.parseImpliedToken(StartTagToken, a.Colgroup, a.Colgroup.String())
			return false
		case a.Tbody, a.Tfoot, a.Thead:
			.clearStackToContext(tableScope)
			.addElement()
			.im = inTableBodyIM
			return true
		case a.Td, a.Th, a.Tr:
			.parseImpliedToken(StartTagToken, a.Tbody, a.Tbody.String())
			return false
		case a.Table:
			if .popUntil(tableScope, a.Table) {
				.resetInsertionMode()
				return false
			}
			// Ignore the token.
			return true
		case a.Style, a.Script, a.Template:
			return inHeadIM()
		case a.Input:
			for ,  := range .tok.Attr {
				if .Key == "type" && strings.ToLower(.Val) == "hidden" {
					.addElement()
					.oe.pop()
					return true
				}
			}
			// Otherwise drop down to the default action.
		case a.Form:
			if .oe.contains(a.Template) || .form != nil {
				// Ignore the token.
				return true
			}
			.addElement()
			.form = .oe.pop()
		case a.Select:
			.reconstructActiveFormattingElements()
			switch .top().DataAtom {
			case a.Table, a.Tbody, a.Tfoot, a.Thead, a.Tr:
				.fosterParenting = true
			}
			.addElement()
			.fosterParenting = false
			.framesetOK = false
			.im = inSelectInTableIM
			return true
		}
	case EndTagToken:
		switch .tok.DataAtom {
		case a.Table:
			if .popUntil(tableScope, a.Table) {
				.resetInsertionMode()
				return true
			}
			// Ignore the token.
			return true
		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()
		}
	case CommentToken:
		.addChild(&Node{
			Type: CommentNode,
			Data: .tok.Data,
		})
		return true
	case DoctypeToken:
		// Ignore the token.
		return true
	case ErrorToken:
		return inBodyIM()
	}

	.fosterParenting = true
	defer func() { .fosterParenting = false }()

	return inBodyIM()
}

// Section 12.2.6.4.11.
func inCaptionIM( *parser) bool {
	switch .tok.Type {
	case StartTagToken:
		switch .tok.DataAtom {
		case a.Caption, a.Col, a.Colgroup, a.Tbody, a.Td, a.Tfoot, a.Thead, a.Tr:
			if !.popUntil(tableScope, a.Caption) {
				// Ignore the token.
				return true
			}
			.clearActiveFormattingElements()
			.im = inTableIM
			return false
		case a.Select:
			.reconstructActiveFormattingElements()
			.addElement()
			.framesetOK = false
			.im = inSelectInTableIM
			return true
		}
	case EndTagToken:
		switch .tok.DataAtom {
		case a.Caption:
			if .popUntil(tableScope, a.Caption) {
				.clearActiveFormattingElements()
				.im = inTableIM
			}
			return true
		case a.Table:
			if !.popUntil(tableScope, a.Caption) {
				// Ignore the token.
				return true
			}
			.clearActiveFormattingElements()
			.im = inTableIM
			return false
		case a.Body, a.Col, a.Colgroup, a.Html, a.Tbody, a.Td, a.Tfoot, a.Th, a.Thead, a.Tr:
			// Ignore the token.
			return true
		}
	}
	return inBodyIM()
}

// Section 12.2.6.4.12.
func inColumnGroupIM( *parser) bool {
	switch .tok.Type {
	case TextToken:
		 := strings.TrimLeft(.tok.Data, whitespace)
		if len() < len(.tok.Data) {
			// Add the initial whitespace to the current node.
			.addText(.tok.Data[:len(.tok.Data)-len()])
			if  == "" {
				return true
			}
			.tok.Data = 
		}
	case CommentToken:
		.addChild(&Node{
			Type: CommentNode,
			Data: .tok.Data,
		})
		return true
	case DoctypeToken:
		// Ignore the token.
		return true
	case StartTagToken:
		switch .tok.DataAtom {
		case a.Html:
			return inBodyIM()
		case a.Col:
			.addElement()
			.oe.pop()
			.acknowledgeSelfClosingTag()
			return true
		case a.Template:
			return inHeadIM()
		}
	case EndTagToken:
		switch .tok.DataAtom {
		case a.Colgroup:
			if .oe.top().DataAtom == a.Colgroup {
				.oe.pop()
				.im = inTableIM
			}
			return true
		case a.Col:
			// Ignore the token.
			return true
		case a.Template:
			return inHeadIM()
		}
	case ErrorToken:
		return inBodyIM()
	}
	if .oe.top().DataAtom != a.Colgroup {
		return true
	}
	.oe.pop()
	.im = inTableIM
	return false
}

// Section 12.2.6.4.13.
func inTableBodyIM( *parser) bool {
	switch .tok.Type {
	case StartTagToken:
		switch .tok.DataAtom {
		case a.Tr:
			.clearStackToContext(tableBodyScope)
			.addElement()
			.im = inRowIM
			return true
		case a.Td, a.Th:
			.parseImpliedToken(StartTagToken, a.Tr, a.Tr.String())
			return false
		case a.Caption, a.Col, a.Colgroup, a.Tbody, a.Tfoot, a.Thead:
			if .popUntil(tableScope, a.Tbody, a.Thead, a.Tfoot) {
				.im = inTableIM
				return false
			}
			// Ignore the token.
			return true
		}
	case EndTagToken:
		switch .tok.DataAtom {
		case a.Tbody, a.Tfoot, a.Thead:
			if .elementInScope(tableScope, .tok.DataAtom) {
				.clearStackToContext(tableBodyScope)
				.oe.pop()
				.im = inTableIM
			}
			return true
		case a.Table:
			if .popUntil(tableScope, a.Tbody, a.Thead, a.Tfoot) {
				.im = inTableIM
				return false
			}
			// Ignore the token.
			return true
		case a.Body, a.Caption, a.Col, a.Colgroup, a.Html, a.Td, a.Th, a.Tr:
			// Ignore the token.
			return true
		}
	case CommentToken:
		.addChild(&Node{
			Type: CommentNode,
			Data: .tok.Data,
		})
		return true
	}

	return inTableIM()
}

// Section 12.2.6.4.14.
func inRowIM( *parser) bool {
	switch .tok.Type {
	case StartTagToken:
		switch .tok.DataAtom {
		case a.Td, a.Th:
			.clearStackToContext(tableRowScope)
			.addElement()
			.afe = append(.afe, &scopeMarker)
			.im = inCellIM
			return true
		case a.Caption, a.Col, a.Colgroup, a.Tbody, a.Tfoot, a.Thead, a.Tr:
			if .popUntil(tableScope, a.Tr) {
				.im = inTableBodyIM
				return false
			}
			// Ignore the token.
			return true
		}
	case EndTagToken:
		switch .tok.DataAtom {
		case a.Tr:
			if .popUntil(tableScope, a.Tr) {
				.im = inTableBodyIM
				return true
			}
			// Ignore the token.
			return true
		case a.Table:
			if .popUntil(tableScope, a.Tr) {
				.im = inTableBodyIM
				return false
			}
			// Ignore the token.
			return true
		case a.Tbody, a.Tfoot, a.Thead:
			if .elementInScope(tableScope, .tok.DataAtom) {
				.parseImpliedToken(EndTagToken, a.Tr, a.Tr.String())
				return false
			}
			// Ignore the token.
			return true
		case a.Body, a.Caption, a.Col, a.Colgroup, a.Html, a.Td, a.Th:
			// Ignore the token.
			return true
		}
	}

	return inTableIM()
}

// Section 12.2.6.4.15.
func inCellIM( *parser) bool {
	switch .tok.Type {
	case StartTagToken:
		switch .tok.DataAtom {
		case a.Caption, a.Col, a.Colgroup, a.Tbody, a.Td, a.Tfoot, a.Th, a.Thead, a.Tr:
			if .popUntil(tableScope, a.Td, a.Th) {
				// Close the cell and reprocess.
				.clearActiveFormattingElements()
				.im = inRowIM
				return false
			}
			// Ignore the token.
			return true
		case a.Select:
			.reconstructActiveFormattingElements()
			.addElement()
			.framesetOK = false
			.im = inSelectInTableIM
			return true
		}
	case EndTagToken:
		switch .tok.DataAtom {
		case a.Td, a.Th:
			if !.popUntil(tableScope, .tok.DataAtom) {
				// Ignore the token.
				return true
			}
			.clearActiveFormattingElements()
			.im = inRowIM
			return true
		case a.Body, a.Caption, a.Col, a.Colgroup, a.Html:
			// Ignore the token.
			return true
		case a.Table, a.Tbody, a.Tfoot, a.Thead, a.Tr:
			if !.elementInScope(tableScope, .tok.DataAtom) {
				// Ignore the token.
				return true
			}
			// Close the cell and reprocess.
			if .popUntil(tableScope, a.Td, a.Th) {
				.clearActiveFormattingElements()
			}
			.im = inRowIM
			return false
		}
	}
	return inBodyIM()
}

// Section 12.2.6.4.16.
func inSelectIM( *parser) bool {
	switch .tok.Type {
	case TextToken:
		.addText(strings.Replace(.tok.Data, "\x00", "", -1))
	case StartTagToken:
		switch .tok.DataAtom {
		case a.Html:
			return inBodyIM()
		case a.Option:
			if .top().DataAtom == a.Option {
				.oe.pop()
			}
			.addElement()
		case a.Optgroup:
			if .top().DataAtom == a.Option {
				.oe.pop()
			}
			if .top().DataAtom == a.Optgroup {
				.oe.pop()
			}
			.addElement()
		case a.Select:
			if !.popUntil(selectScope, a.Select) {
				// Ignore the token.
				return true
			}
			.resetInsertionMode()
		case a.Input, a.Keygen, a.Textarea:
			if .elementInScope(selectScope, a.Select) {
				.parseImpliedToken(EndTagToken, a.Select, a.Select.String())
				return false
			}
			// In order to properly ignore <textarea>, we need to change the tokenizer mode.
			.tokenizer.NextIsNotRawText()
			// Ignore the token.
			return true
		case a.Script, a.Template:
			return inHeadIM()
		case a.Iframe, a.Noembed, a.Noframes, a.Noscript, a.Plaintext, a.Style, a.Title, a.Xmp:
			// Don't let the tokenizer go into raw text mode when there are raw tags
			// to be ignored. These tags should be ignored from the tokenizer
			// properly.
			.tokenizer.NextIsNotRawText()
			// Ignore the token.
			return true
		}
	case EndTagToken:
		switch .tok.DataAtom {
		case a.Option:
			if .top().DataAtom == a.Option {
				.oe.pop()
			}
		case a.Optgroup:
			 := len(.oe) - 1
			if .oe[].DataAtom == a.Option {
				--
			}
			if .oe[].DataAtom == a.Optgroup {
				.oe = .oe[:]
			}
		case a.Select:
			if !.popUntil(selectScope, a.Select) {
				// Ignore the token.
				return true
			}
			.resetInsertionMode()
		case a.Template:
			return inHeadIM()
		}
	case CommentToken:
		.addChild(&Node{
			Type: CommentNode,
			Data: .tok.Data,
		})
	case DoctypeToken:
		// Ignore the token.
		return true
	case ErrorToken:
		return inBodyIM()
	}

	return true
}

// Section 12.2.6.4.17.
func inSelectInTableIM( *parser) bool {
	switch .tok.Type {
	case StartTagToken, EndTagToken:
		switch .tok.DataAtom {
		case a.Caption, a.Table, a.Tbody, a.Tfoot, a.Thead, a.Tr, a.Td, a.Th:
			if .tok.Type == EndTagToken && !.elementInScope(tableScope, .tok.DataAtom) {
				// Ignore the token.
				return true
			}
			// This is like p.popUntil(selectScope, a.Select), but it also
			// matches <math select>, not just <select>. Matching the MathML
			// tag is arguably incorrect (conceptually), but it mimics what
			// Chromium does.
			for  := len(.oe) - 1;  >= 0; -- {
				if  := .oe[]; .DataAtom == a.Select {
					.oe = .oe[:]
					break
				}
			}
			.resetInsertionMode()
			return false
		}
	}
	return inSelectIM()
}

// Section 12.2.6.4.18.
func inTemplateIM( *parser) bool {
	switch .tok.Type {
	case TextToken, CommentToken, DoctypeToken:
		return inBodyIM()
	case StartTagToken:
		switch .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()
		case a.Caption, a.Colgroup, a.Tbody, a.Tfoot, a.Thead:
			.templateStack.pop()
			.templateStack = append(.templateStack, inTableIM)
			.im = inTableIM
			return false
		case a.Col:
			.templateStack.pop()
			.templateStack = append(.templateStack, inColumnGroupIM)
			.im = inColumnGroupIM
			return false
		case a.Tr:
			.templateStack.pop()
			.templateStack = append(.templateStack, inTableBodyIM)
			.im = inTableBodyIM
			return false
		case a.Td, a.Th:
			.templateStack.pop()
			.templateStack = append(.templateStack, inRowIM)
			.im = inRowIM
			return false
		default:
			.templateStack.pop()
			.templateStack = append(.templateStack, inBodyIM)
			.im = inBodyIM
			return false
		}
	case EndTagToken:
		switch .tok.DataAtom {
		case a.Template:
			return inHeadIM()
		default:
			// Ignore the token.
			return true
		}
	case ErrorToken:
		if !.oe.contains(a.Template) {
			// Ignore the token.
			return true
		}
		// TODO: remove this divergence from the HTML5 spec.
		//
		// See https://bugs.chromium.org/p/chromium/issues/detail?id=829668
		.generateImpliedEndTags()
		for  := len(.oe) - 1;  >= 0; -- {
			if  := .oe[]; .Namespace == "" && .DataAtom == a.Template {
				.oe = .oe[:]
				break
			}
		}
		.clearActiveFormattingElements()
		.templateStack.pop()
		.resetInsertionMode()
		return false
	}
	return false
}

// Section 12.2.6.4.19.
func afterBodyIM( *parser) bool {
	switch .tok.Type {
	case ErrorToken:
		// Stop parsing.
		return true
	case TextToken:
		 := strings.TrimLeft(.tok.Data, whitespace)
		if len() == 0 {
			// It was all whitespace.
			return inBodyIM()
		}
	case StartTagToken:
		if .tok.DataAtom == a.Html {
			return inBodyIM()
		}
	case EndTagToken:
		if .tok.DataAtom == a.Html {
			if !.fragment {
				.im = afterAfterBodyIM
			}
			return true
		}
	case CommentToken:
		// The comment is attached to the <html> element.
		if len(.oe) < 1 || .oe[0].DataAtom != a.Html {
			panic("html: bad parser state: <html> element not found, in the after-body insertion mode")
		}
		.oe[0].AppendChild(&Node{
			Type: CommentNode,
			Data: .tok.Data,
		})
		return true
	}
	.im = inBodyIM
	return false
}

// Section 12.2.6.4.20.
func inFramesetIM( *parser) bool {
	switch .tok.Type {
	case CommentToken:
		.addChild(&Node{
			Type: CommentNode,
			Data: .tok.Data,
		})
	case TextToken:
		// Ignore all text but whitespace.
		 := strings.Map(func( rune) rune {
			switch  {
			case ' ', '\t', '\n', '\f', '\r':
				return 
			}
			return -1
		}, .tok.Data)
		if  != "" {
			.addText()
		}
	case StartTagToken:
		switch .tok.DataAtom {
		case a.Html:
			return inBodyIM()
		case a.Frameset:
			.addElement()
		case a.Frame:
			.addElement()
			.oe.pop()
			.acknowledgeSelfClosingTag()
		case a.Noframes:
			return inHeadIM()
		}
	case EndTagToken:
		switch .tok.DataAtom {
		case a.Frameset:
			if .oe.top().DataAtom != a.Html {
				.oe.pop()
				if .oe.top().DataAtom != a.Frameset {
					.im = afterFramesetIM
					return true
				}
			}
		}
	default:
		// Ignore the token.
	}
	return true
}

// Section 12.2.6.4.21.
func afterFramesetIM( *parser) bool {
	switch .tok.Type {
	case CommentToken:
		.addChild(&Node{
			Type: CommentNode,
			Data: .tok.Data,
		})
	case TextToken:
		// Ignore all text but whitespace.
		 := strings.Map(func( rune) rune {
			switch  {
			case ' ', '\t', '\n', '\f', '\r':
				return 
			}
			return -1
		}, .tok.Data)
		if  != "" {
			.addText()
		}
	case StartTagToken:
		switch .tok.DataAtom {
		case a.Html:
			return inBodyIM()
		case a.Noframes:
			return inHeadIM()
		}
	case EndTagToken:
		switch .tok.DataAtom {
		case a.Html:
			.im = afterAfterFramesetIM
			return true
		}
	default:
		// Ignore the token.
	}
	return true
}

// Section 12.2.6.4.22.
func afterAfterBodyIM( *parser) bool {
	switch .tok.Type {
	case ErrorToken:
		// Stop parsing.
		return true
	case TextToken:
		 := strings.TrimLeft(.tok.Data, whitespace)
		if len() == 0 {
			// It was all whitespace.
			return inBodyIM()
		}
	case StartTagToken:
		if .tok.DataAtom == a.Html {
			return inBodyIM()
		}
	case CommentToken:
		.doc.AppendChild(&Node{
			Type: CommentNode,
			Data: .tok.Data,
		})
		return true
	case DoctypeToken:
		return inBodyIM()
	}
	.im = inBodyIM
	return false
}

// Section 12.2.6.4.23.
func afterAfterFramesetIM( *parser) bool {
	switch .tok.Type {
	case CommentToken:
		.doc.AppendChild(&Node{
			Type: CommentNode,
			Data: .tok.Data,
		})
	case TextToken:
		// Ignore all text but whitespace.
		 := strings.Map(func( rune) rune {
			switch  {
			case ' ', '\t', '\n', '\f', '\r':
				return 
			}
			return -1
		}, .tok.Data)
		if  != "" {
			.tok.Data = 
			return inBodyIM()
		}
	case StartTagToken:
		switch .tok.DataAtom {
		case a.Html:
			return inBodyIM()
		case a.Noframes:
			return inHeadIM()
		}
	case DoctypeToken:
		return inBodyIM()
	default:
		// Ignore the token.
	}
	return true
}

func ignoreTheRemainingTokens( *parser) bool {
	return true
}

const whitespaceOrNUL = whitespace + "\x00"

// Section 12.2.6.5
func parseForeignContent( *parser) bool {
	switch .tok.Type {
	case TextToken:
		if .framesetOK {
			.framesetOK = strings.TrimLeft(.tok.Data, whitespaceOrNUL) == ""
		}
		.tok.Data = strings.Replace(.tok.Data, "\x00", "\ufffd", -1)
		.addText(.tok.Data)
	case CommentToken:
		.addChild(&Node{
			Type: CommentNode,
			Data: .tok.Data,
		})
	case StartTagToken:
		if !.fragment {
			 := breakout[.tok.Data]
			if .tok.DataAtom == a.Font {
			:
				for ,  := range .tok.Attr {
					switch .Key {
					case "color", "face", "size":
						 = true
						break 
					}
				}
			}
			if  {
				for  := len(.oe) - 1;  >= 0; -- {
					 := .oe[]
					if .Namespace == "" || htmlIntegrationPoint() || mathMLTextIntegrationPoint() {
						.oe = .oe[:+1]
						break
					}
				}
				return false
			}
		}
		 := .adjustedCurrentNode()
		switch .Namespace {
		case "math":
			adjustAttributeNames(.tok.Attr, mathMLAttributeAdjustments)
		case "svg":
			// Adjust SVG tag names. The tokenizer lower-cases tag names, but
			// SVG wants e.g. "foreignObject" with a capital second "O".
			if  := svgTagNameAdjustments[.tok.Data];  != "" {
				.tok.DataAtom = a.Lookup([]byte())
				.tok.Data = 
			}
			adjustAttributeNames(.tok.Attr, svgAttributeAdjustments)
		default:
			panic("html: bad parser state: unexpected namespace")
		}
		adjustForeignAttributes(.tok.Attr)
		 := .Namespace
		.addElement()
		.top().Namespace = 
		if  != "" {
			// Don't let the tokenizer go into raw text mode in foreign content
			// (e.g. in an SVG <title> tag).
			.tokenizer.NextIsNotRawText()
		}
		if .hasSelfClosingToken {
			.oe.pop()
			.acknowledgeSelfClosingTag()
		}
	case EndTagToken:
		for  := len(.oe) - 1;  >= 0; -- {
			if .oe[].Namespace == "" {
				return .im()
			}
			if strings.EqualFold(.oe[].Data, .tok.Data) {
				.oe = .oe[:]
				break
			}
		}
		return true
	default:
		// Ignore the token.
	}
	return true
}

// Section 12.2.4.2.
func ( *parser) () *Node {
	if len(.oe) == 1 && .fragment && .context != nil {
		return .context
	}
	return .oe.top()
}

// Section 12.2.6.
func ( *parser) () bool {
	if len(.oe) == 0 {
		return false
	}
	 := .adjustedCurrentNode()
	if .Namespace == "" {
		return false
	}
	if mathMLTextIntegrationPoint() {
		if .tok.Type == StartTagToken && .tok.DataAtom != a.Mglyph && .tok.DataAtom != a.Malignmark {
			return false
		}
		if .tok.Type == TextToken {
			return false
		}
	}
	if .Namespace == "math" && .DataAtom == a.AnnotationXml && .tok.Type == StartTagToken && .tok.DataAtom == a.Svg {
		return false
	}
	if htmlIntegrationPoint() && (.tok.Type == StartTagToken || .tok.Type == TextToken) {
		return false
	}
	if .tok.Type == ErrorToken {
		return false
	}
	return true
}

// parseImpliedToken parses a token as though it had appeared in the parser's
// input.
func ( *parser) ( TokenType,  a.Atom,  string) {
	,  := .tok, .hasSelfClosingToken
	.tok = Token{
		Type:     ,
		DataAtom: ,
		Data:     ,
	}
	.hasSelfClosingToken = false
	.parseCurrentToken()
	.tok, .hasSelfClosingToken = , 
}

// parseCurrentToken runs the current token through the parsing routines
// until it is consumed.
func ( *parser) () {
	if .tok.Type == SelfClosingTagToken {
		.hasSelfClosingToken = true
		.tok.Type = StartTagToken
	}

	 := false
	for ! {
		if .inForeignContent() {
			 = parseForeignContent()
		} else {
			 = .im()
		}
	}

	if .hasSelfClosingToken {
		// This is a parse error, but ignore it.
		.hasSelfClosingToken = false
	}
}

func ( *parser) () error {
	// Iterate until EOF. Any other error will cause an early return.
	var  error
	for  != io.EOF {
		// CDATA sections are allowed only in foreign content.
		 := .oe.top()
		.tokenizer.AllowCDATA( != nil && .Namespace != "")
		// Read and parse the next token.
		.tokenizer.Next()
		.tok = .tokenizer.Token()
		if .tok.Type == ErrorToken {
			 = .tokenizer.Err()
			if  != nil &&  != io.EOF {
				return 
			}
		}
		.parseCurrentToken()
	}
	return nil
}

// Parse returns the parse tree for the HTML from the given Reader.
//
// It implements the HTML5 parsing algorithm
// (https://html.spec.whatwg.org/multipage/syntax.html#tree-construction),
// which is very complicated. The resultant tree can contain implicitly created
// nodes that have no explicit <tag> listed in r's data, and nodes' parents can
// differ from the nesting implied by a naive processing of start and end
// <tag>s. Conversely, explicit <tag>s in r's data can be silently dropped,
// with no corresponding node in the resulting tree.
//
// The input is assumed to be UTF-8 encoded.
func ( io.Reader) (*Node, error) {
	return ParseWithOptions()
}

// ParseFragment parses a fragment of HTML and returns the nodes that were
// found. If the fragment is the InnerHTML for an existing element, pass that
// element in context.
//
// It has the same intricacies as Parse.
func ( io.Reader,  *Node) ([]*Node, error) {
	return ParseFragmentWithOptions(, )
}

// ParseOption configures a parser.
type ParseOption func(p *parser)

// ParseOptionEnableScripting configures the scripting flag.
// https://html.spec.whatwg.org/multipage/webappapis.html#enabling-and-disabling-scripting
//
// By default, scripting is enabled.
func ( bool) ParseOption {
	return func( *parser) {
		.scripting = 
	}
}

// ParseWithOptions is like Parse, with options.
func ( io.Reader,  ...ParseOption) (*Node, error) {
	 := &parser{
		tokenizer: NewTokenizer(),
		doc: &Node{
			Type: DocumentNode,
		},
		scripting:  true,
		framesetOK: true,
		im:         initialIM,
	}

	for ,  := range  {
		()
	}

	if  := .parse();  != nil {
		return nil, 
	}
	return .doc, nil
}

// ParseFragmentWithOptions is like ParseFragment, with options.
func ( io.Reader,  *Node,  ...ParseOption) ([]*Node, error) {
	 := ""
	if  != nil {
		if .Type != ElementNode {
			return nil, errors.New("html: ParseFragment of non-element Node")
		}
		// The next check isn't just context.DataAtom.String() == context.Data because
		// it is valid to pass an element whose tag isn't a known atom. For example,
		// DataAtom == 0 and Data = "tagfromthefuture" is perfectly consistent.
		if .DataAtom != a.Lookup([]byte(.Data)) {
			return nil, fmt.Errorf("html: inconsistent Node: DataAtom=%q, Data=%q", .DataAtom, .Data)
		}
		 = .DataAtom.String()
	}
	 := &parser{
		doc: &Node{
			Type: DocumentNode,
		},
		scripting: true,
		fragment:  true,
		context:   ,
	}
	if  != nil && .Namespace != "" {
		.tokenizer = NewTokenizer()
	} else {
		.tokenizer = NewTokenizerFragment(, )
	}

	for ,  := range  {
		()
	}

	 := &Node{
		Type:     ElementNode,
		DataAtom: a.Html,
		Data:     a.Html.String(),
	}
	.doc.AppendChild()
	.oe = nodeStack{}
	if  != nil && .DataAtom == a.Template {
		.templateStack = append(.templateStack, inTemplateIM)
	}
	.resetInsertionMode()

	for  := ;  != nil;  = .Parent {
		if .Type == ElementNode && .DataAtom == a.Form {
			.form = 
			break
		}
	}

	if  := .parse();  != nil {
		return nil, 
	}

	 := .doc
	if  != nil {
		 = 
	}

	var  []*Node
	for  := .FirstChild;  != nil; {
		 := .NextSibling
		.RemoveChild()
		 = append(, )
		 = 
	}
	return , nil
}