vendor/golang.org/x/net/html/parse.go
changeset 251 1c52a0eeb952
parent 242 2a9ec03fe5a1
child 256 6d9efbef00a9
equal deleted inserted replaced
250:c040f992052f 251:1c52a0eeb952
   182 			panic("unreachable")
   182 			panic("unreachable")
   183 		}
   183 		}
   184 	}
   184 	}
   185 }
   185 }
   186 
   186 
       
   187 // parseGenericRawTextElements implements the generic raw text element parsing
       
   188 // algorithm defined in 12.2.6.2.
       
   189 // https://html.spec.whatwg.org/multipage/parsing.html#parsing-elements-that-contain-only-text
       
   190 // TODO: Since both RAWTEXT and RCDATA states are treated as tokenizer's part
       
   191 // officially, need to make tokenizer consider both states.
       
   192 func (p *parser) parseGenericRawTextElement() {
       
   193 	p.addElement()
       
   194 	p.originalIM = p.im
       
   195 	p.im = textIM
       
   196 }
       
   197 
   187 // generateImpliedEndTags pops nodes off the stack of open elements as long as
   198 // generateImpliedEndTags pops nodes off the stack of open elements as long as
   188 // the top node has a tag name of dd, dt, li, optgroup, option, p, rb, rp, rt or rtc.
   199 // the top node has a tag name of dd, dt, li, optgroup, option, p, rb, rp, rt or rtc.
   189 // If exceptions are specified, nodes with that name will not be popped off.
   200 // If exceptions are specified, nodes with that name will not be popped off.
   190 func (p *parser) generateImpliedEndTags(exceptions ...string) {
   201 func (p *parser) generateImpliedEndTags(exceptions ...string) {
   191 	var i int
   202 	var i int
   192 loop:
   203 loop:
   193 	for i = len(p.oe) - 1; i >= 0; i-- {
   204 	for i = len(p.oe) - 1; i >= 0; i-- {
   194 		n := p.oe[i]
   205 		n := p.oe[i]
   195 		if n.Type == ElementNode {
   206 		if n.Type != ElementNode {
   196 			switch n.DataAtom {
   207 			break
   197 			case a.Dd, a.Dt, a.Li, a.Optgroup, a.Option, a.P, a.Rb, a.Rp, a.Rt, a.Rtc:
   208 		}
   198 				for _, except := range exceptions {
   209 		switch n.DataAtom {
   199 					if n.Data == except {
   210 		case a.Dd, a.Dt, a.Li, a.Optgroup, a.Option, a.P, a.Rb, a.Rp, a.Rt, a.Rtc:
   200 						break loop
   211 			for _, except := range exceptions {
   201 					}
   212 				if n.Data == except {
   202 				}
   213 					break loop
   203 				continue
   214 				}
   204 			}
   215 			}
       
   216 			continue
   205 		}
   217 		}
   206 		break
   218 		break
   207 	}
   219 	}
   208 
   220 
   209 	p.oe = p.oe[:i+1]
   221 	p.oe = p.oe[:i+1]
   367 }
   379 }
   368 
   380 
   369 // Section 12.2.4.3.
   381 // Section 12.2.4.3.
   370 func (p *parser) clearActiveFormattingElements() {
   382 func (p *parser) clearActiveFormattingElements() {
   371 	for {
   383 	for {
   372 		n := p.afe.pop()
   384 		if n := p.afe.pop(); len(p.afe) == 0 || n.Type == scopeMarkerNode {
   373 		if len(p.afe) == 0 || n.Type == scopeMarkerNode {
       
   374 			return
   385 			return
   375 		}
   386 		}
   376 	}
   387 	}
   377 }
   388 }
   378 
   389 
   437 
   448 
   438 		switch n.DataAtom {
   449 		switch n.DataAtom {
   439 		case a.Select:
   450 		case a.Select:
   440 			if !last {
   451 			if !last {
   441 				for ancestor, first := n, p.oe[0]; ancestor != first; {
   452 				for ancestor, first := n, p.oe[0]; ancestor != first; {
   442 					if ancestor == first {
       
   443 						break
       
   444 					}
       
   445 					ancestor = p.oe[p.oe.index(ancestor)-1]
   453 					ancestor = p.oe[p.oe.index(ancestor)-1]
   446 					switch ancestor.DataAtom {
   454 					switch ancestor.DataAtom {
   447 					case a.Template:
   455 					case a.Template:
   448 						p.im = inSelectIM
   456 						p.im = inSelectIM
   449 						return
   457 						return
   626 		}
   634 		}
   627 	case StartTagToken:
   635 	case StartTagToken:
   628 		switch p.tok.DataAtom {
   636 		switch p.tok.DataAtom {
   629 		case a.Html:
   637 		case a.Html:
   630 			return inBodyIM(p)
   638 			return inBodyIM(p)
   631 		case a.Base, a.Basefont, a.Bgsound, a.Command, a.Link, a.Meta:
   639 		case a.Base, a.Basefont, a.Bgsound, a.Link, a.Meta:
   632 			p.addElement()
   640 			p.addElement()
   633 			p.oe.pop()
   641 			p.oe.pop()
   634 			p.acknowledgeSelfClosingTag()
   642 			p.acknowledgeSelfClosingTag()
   635 			return true
   643 			return true
   636 		case a.Script, a.Title, a.Noscript, a.Noframes, a.Style:
   644 		case a.Noscript:
       
   645 			if p.scripting {
       
   646 				p.parseGenericRawTextElement()
       
   647 				return true
       
   648 			}
       
   649 			p.addElement()
       
   650 			p.im = inHeadNoscriptIM
       
   651 			// Don't let the tokenizer go into raw text mode when scripting is disabled.
       
   652 			p.tokenizer.NextIsNotRawText()
       
   653 			return true
       
   654 		case a.Script, a.Title:
   637 			p.addElement()
   655 			p.addElement()
   638 			p.setOriginalIM()
   656 			p.setOriginalIM()
   639 			p.im = textIM
   657 			p.im = textIM
       
   658 			return true
       
   659 		case a.Noframes, a.Style:
       
   660 			p.parseGenericRawTextElement()
   640 			return true
   661 			return true
   641 		case a.Head:
   662 		case a.Head:
   642 			// Ignore the token.
   663 			// Ignore the token.
   643 			return true
   664 			return true
   644 		case a.Template:
   665 		case a.Template:
   690 		// Ignore the token.
   711 		// Ignore the token.
   691 		return true
   712 		return true
   692 	}
   713 	}
   693 
   714 
   694 	p.parseImpliedToken(EndTagToken, a.Head, a.Head.String())
   715 	p.parseImpliedToken(EndTagToken, a.Head, a.Head.String())
       
   716 	return false
       
   717 }
       
   718 
       
   719 // 12.2.6.4.5.
       
   720 func inHeadNoscriptIM(p *parser) bool {
       
   721 	switch p.tok.Type {
       
   722 	case DoctypeToken:
       
   723 		// Ignore the token.
       
   724 		return true
       
   725 	case StartTagToken:
       
   726 		switch p.tok.DataAtom {
       
   727 		case a.Html:
       
   728 			return inBodyIM(p)
       
   729 		case a.Basefont, a.Bgsound, a.Link, a.Meta, a.Noframes, a.Style:
       
   730 			return inHeadIM(p)
       
   731 		case a.Head, a.Noscript:
       
   732 			// Ignore the token.
       
   733 			return true
       
   734 		}
       
   735 	case EndTagToken:
       
   736 		switch p.tok.DataAtom {
       
   737 		case a.Noscript, a.Br:
       
   738 		default:
       
   739 			// Ignore the token.
       
   740 			return true
       
   741 		}
       
   742 	case TextToken:
       
   743 		s := strings.TrimLeft(p.tok.Data, whitespace)
       
   744 		if len(s) == 0 {
       
   745 			// It was all whitespace.
       
   746 			return inHeadIM(p)
       
   747 		}
       
   748 	case CommentToken:
       
   749 		return inHeadIM(p)
       
   750 	}
       
   751 	p.oe.pop()
       
   752 	if p.top().DataAtom != a.Head {
       
   753 		panic("html: the new current node will be a head element.")
       
   754 	}
       
   755 	p.im = inHeadIM
       
   756 	if p.tok.DataAtom == a.Noscript {
       
   757 		return true
       
   758 	}
   695 	return false
   759 	return false
   696 }
   760 }
   697 
   761 
   698 // Section 12.2.6.4.6.
   762 // Section 12.2.6.4.6.
   699 func afterHeadIM(p *parser) bool {
   763 func afterHeadIM(p *parser) bool {
   804 		case a.Html:
   868 		case a.Html:
   805 			if p.oe.contains(a.Template) {
   869 			if p.oe.contains(a.Template) {
   806 				return true
   870 				return true
   807 			}
   871 			}
   808 			copyAttributes(p.oe[0], p.tok)
   872 			copyAttributes(p.oe[0], p.tok)
   809 		case a.Base, a.Basefont, a.Bgsound, a.Command, a.Link, a.Meta, a.Noframes, a.Script, a.Style, a.Template, a.Title:
   873 		case a.Base, a.Basefont, a.Bgsound, a.Link, a.Meta, a.Noframes, a.Script, a.Style, a.Template, a.Title:
   810 			return inHeadIM(p)
   874 			return inHeadIM(p)
   811 		case a.Body:
   875 		case a.Body:
   812 			if p.oe.contains(a.Template) {
   876 			if p.oe.contains(a.Template) {
   813 				return true
   877 				return true
   814 			}
   878 			}
   830 			}
   894 			}
   831 			p.oe = p.oe[:1]
   895 			p.oe = p.oe[:1]
   832 			p.addElement()
   896 			p.addElement()
   833 			p.im = inFramesetIM
   897 			p.im = inFramesetIM
   834 			return true
   898 			return true
   835 		case a.Address, a.Article, a.Aside, a.Blockquote, a.Center, a.Details, a.Dir, a.Div, a.Dl, a.Fieldset, a.Figcaption, a.Figure, a.Footer, a.Header, a.Hgroup, a.Menu, a.Nav, a.Ol, a.P, a.Section, a.Summary, a.Ul:
   899 		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:
   836 			p.popUntil(buttonScope, a.P)
   900 			p.popUntil(buttonScope, a.P)
   837 			p.addElement()
   901 			p.addElement()
   838 		case a.H1, a.H2, a.H3, a.H4, a.H5, a.H6:
   902 		case a.H1, a.H2, a.H3, a.H4, a.H5, a.H6:
   839 			p.popUntil(buttonScope, a.P)
   903 			p.popUntil(buttonScope, a.P)
   840 			switch n := p.top(); n.DataAtom {
   904 			switch n := p.top(); n.DataAtom {
   902 			p.addElement()
   966 			p.addElement()
   903 			p.framesetOK = false
   967 			p.framesetOK = false
   904 		case a.A:
   968 		case a.A:
   905 			for i := len(p.afe) - 1; i >= 0 && p.afe[i].Type != scopeMarkerNode; i-- {
   969 			for i := len(p.afe) - 1; i >= 0 && p.afe[i].Type != scopeMarkerNode; i-- {
   906 				if n := p.afe[i]; n.Type == ElementNode && n.DataAtom == a.A {
   970 				if n := p.afe[i]; n.Type == ElementNode && n.DataAtom == a.A {
   907 					p.inBodyEndTagFormatting(a.A)
   971 					p.inBodyEndTagFormatting(a.A, "a")
   908 					p.oe.remove(n)
   972 					p.oe.remove(n)
   909 					p.afe.remove(n)
   973 					p.afe.remove(n)
   910 					break
   974 					break
   911 				}
   975 				}
   912 			}
   976 			}
   916 			p.reconstructActiveFormattingElements()
   980 			p.reconstructActiveFormattingElements()
   917 			p.addFormattingElement()
   981 			p.addFormattingElement()
   918 		case a.Nobr:
   982 		case a.Nobr:
   919 			p.reconstructActiveFormattingElements()
   983 			p.reconstructActiveFormattingElements()
   920 			if p.elementInScope(defaultScope, a.Nobr) {
   984 			if p.elementInScope(defaultScope, a.Nobr) {
   921 				p.inBodyEndTagFormatting(a.Nobr)
   985 				p.inBodyEndTagFormatting(a.Nobr, "nobr")
   922 				p.reconstructActiveFormattingElements()
   986 				p.reconstructActiveFormattingElements()
   923 			}
   987 			}
   924 			p.addFormattingElement()
   988 			p.addFormattingElement()
   925 		case a.Applet, a.Marquee, a.Object:
   989 		case a.Applet, a.Marquee, a.Object:
   926 			p.reconstructActiveFormattingElements()
   990 			p.reconstructActiveFormattingElements()
   963 			p.framesetOK = false
  1027 			p.framesetOK = false
   964 		case a.Image:
  1028 		case a.Image:
   965 			p.tok.DataAtom = a.Img
  1029 			p.tok.DataAtom = a.Img
   966 			p.tok.Data = a.Img.String()
  1030 			p.tok.Data = a.Img.String()
   967 			return false
  1031 			return false
   968 		case a.Isindex:
       
   969 			if p.form != nil {
       
   970 				// Ignore the token.
       
   971 				return true
       
   972 			}
       
   973 			action := ""
       
   974 			prompt := "This is a searchable index. Enter search keywords: "
       
   975 			attr := []Attribute{{Key: "name", Val: "isindex"}}
       
   976 			for _, t := range p.tok.Attr {
       
   977 				switch t.Key {
       
   978 				case "action":
       
   979 					action = t.Val
       
   980 				case "name":
       
   981 					// Ignore the attribute.
       
   982 				case "prompt":
       
   983 					prompt = t.Val
       
   984 				default:
       
   985 					attr = append(attr, t)
       
   986 				}
       
   987 			}
       
   988 			p.acknowledgeSelfClosingTag()
       
   989 			p.popUntil(buttonScope, a.P)
       
   990 			p.parseImpliedToken(StartTagToken, a.Form, a.Form.String())
       
   991 			if p.form == nil {
       
   992 				// NOTE: The 'isindex' element has been removed,
       
   993 				// and the 'template' element has not been designed to be
       
   994 				// collaborative with the index element.
       
   995 				//
       
   996 				// Ignore the token.
       
   997 				return true
       
   998 			}
       
   999 			if action != "" {
       
  1000 				p.form.Attr = []Attribute{{Key: "action", Val: action}}
       
  1001 			}
       
  1002 			p.parseImpliedToken(StartTagToken, a.Hr, a.Hr.String())
       
  1003 			p.parseImpliedToken(StartTagToken, a.Label, a.Label.String())
       
  1004 			p.addText(prompt)
       
  1005 			p.addChild(&Node{
       
  1006 				Type:     ElementNode,
       
  1007 				DataAtom: a.Input,
       
  1008 				Data:     a.Input.String(),
       
  1009 				Attr:     attr,
       
  1010 			})
       
  1011 			p.oe.pop()
       
  1012 			p.parseImpliedToken(EndTagToken, a.Label, a.Label.String())
       
  1013 			p.parseImpliedToken(StartTagToken, a.Hr, a.Hr.String())
       
  1014 			p.parseImpliedToken(EndTagToken, a.Form, a.Form.String())
       
  1015 		case a.Textarea:
  1032 		case a.Textarea:
  1016 			p.addElement()
  1033 			p.addElement()
  1017 			p.setOriginalIM()
  1034 			p.setOriginalIM()
  1018 			p.framesetOK = false
  1035 			p.framesetOK = false
  1019 			p.im = textIM
  1036 			p.im = textIM
  1020 		case a.Xmp:
  1037 		case a.Xmp:
  1021 			p.popUntil(buttonScope, a.P)
  1038 			p.popUntil(buttonScope, a.P)
  1022 			p.reconstructActiveFormattingElements()
  1039 			p.reconstructActiveFormattingElements()
  1023 			p.framesetOK = false
  1040 			p.framesetOK = false
  1024 			p.addElement()
  1041 			p.parseGenericRawTextElement()
  1025 			p.setOriginalIM()
       
  1026 			p.im = textIM
       
  1027 		case a.Iframe:
  1042 		case a.Iframe:
  1028 			p.framesetOK = false
  1043 			p.framesetOK = false
  1029 			p.addElement()
  1044 			p.parseGenericRawTextElement()
  1030 			p.setOriginalIM()
  1045 		case a.Noembed:
  1031 			p.im = textIM
  1046 			p.parseGenericRawTextElement()
  1032 		case a.Noembed, a.Noscript:
  1047 		case a.Noscript:
  1033 			p.addElement()
  1048 			if p.scripting {
  1034 			p.setOriginalIM()
  1049 				p.parseGenericRawTextElement()
  1035 			p.im = textIM
  1050 				return true
       
  1051 			}
       
  1052 			p.reconstructActiveFormattingElements()
       
  1053 			p.addElement()
       
  1054 			// Don't let the tokenizer go into raw text mode when scripting is disabled.
       
  1055 			p.tokenizer.NextIsNotRawText()
  1036 		case a.Select:
  1056 		case a.Select:
  1037 			p.reconstructActiveFormattingElements()
  1057 			p.reconstructActiveFormattingElements()
  1038 			p.addElement()
  1058 			p.addElement()
  1039 			p.framesetOK = false
  1059 			p.framesetOK = false
  1040 			p.im = inSelectIM
  1060 			p.im = inSelectIM
  1086 			if p.elementInScope(defaultScope, a.Body) {
  1106 			if p.elementInScope(defaultScope, a.Body) {
  1087 				p.parseImpliedToken(EndTagToken, a.Body, a.Body.String())
  1107 				p.parseImpliedToken(EndTagToken, a.Body, a.Body.String())
  1088 				return false
  1108 				return false
  1089 			}
  1109 			}
  1090 			return true
  1110 			return true
  1091 		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:
  1111 		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:
  1092 			p.popUntil(defaultScope, p.tok.DataAtom)
  1112 			p.popUntil(defaultScope, p.tok.DataAtom)
  1093 		case a.Form:
  1113 		case a.Form:
  1094 			if p.oe.contains(a.Template) {
  1114 			if p.oe.contains(a.Template) {
  1095 				i := p.indexOfElementInScope(defaultScope, a.Form)
  1115 				i := p.indexOfElementInScope(defaultScope, a.Form)
  1096 				if i == -1 {
  1116 				if i == -1 {
  1124 		case a.Dd, a.Dt:
  1144 		case a.Dd, a.Dt:
  1125 			p.popUntil(defaultScope, p.tok.DataAtom)
  1145 			p.popUntil(defaultScope, p.tok.DataAtom)
  1126 		case a.H1, a.H2, a.H3, a.H4, a.H5, a.H6:
  1146 		case a.H1, a.H2, a.H3, a.H4, a.H5, a.H6:
  1127 			p.popUntil(defaultScope, a.H1, a.H2, a.H3, a.H4, a.H5, a.H6)
  1147 			p.popUntil(defaultScope, a.H1, a.H2, a.H3, a.H4, a.H5, a.H6)
  1128 		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:
  1148 		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:
  1129 			p.inBodyEndTagFormatting(p.tok.DataAtom)
  1149 			p.inBodyEndTagFormatting(p.tok.DataAtom, p.tok.Data)
  1130 		case a.Applet, a.Marquee, a.Object:
  1150 		case a.Applet, a.Marquee, a.Object:
  1131 			if p.popUntil(defaultScope, p.tok.DataAtom) {
  1151 			if p.popUntil(defaultScope, p.tok.DataAtom) {
  1132 				p.clearActiveFormattingElements()
  1152 				p.clearActiveFormattingElements()
  1133 			}
  1153 			}
  1134 		case a.Br:
  1154 		case a.Br:
  1135 			p.tok.Type = StartTagToken
  1155 			p.tok.Type = StartTagToken
  1136 			return false
  1156 			return false
  1137 		case a.Template:
  1157 		case a.Template:
  1138 			return inHeadIM(p)
  1158 			return inHeadIM(p)
  1139 		default:
  1159 		default:
  1140 			p.inBodyEndTagOther(p.tok.DataAtom)
  1160 			p.inBodyEndTagOther(p.tok.DataAtom, p.tok.Data)
  1141 		}
  1161 		}
  1142 	case CommentToken:
  1162 	case CommentToken:
  1143 		p.addChild(&Node{
  1163 		p.addChild(&Node{
  1144 			Type: CommentNode,
  1164 			Type: CommentNode,
  1145 			Data: p.tok.Data,
  1165 			Data: p.tok.Data,
  1147 	case ErrorToken:
  1167 	case ErrorToken:
  1148 		// TODO: remove this divergence from the HTML5 spec.
  1168 		// TODO: remove this divergence from the HTML5 spec.
  1149 		if len(p.templateStack) > 0 {
  1169 		if len(p.templateStack) > 0 {
  1150 			p.im = inTemplateIM
  1170 			p.im = inTemplateIM
  1151 			return false
  1171 			return false
  1152 		} else {
  1172 		}
  1153 			for _, e := range p.oe {
  1173 		for _, e := range p.oe {
  1154 				switch e.DataAtom {
  1174 			switch e.DataAtom {
  1155 				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,
  1175 			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,
  1156 					a.Thead, a.Tr, a.Body, a.Html:
  1176 				a.Thead, a.Tr, a.Body, a.Html:
  1157 				default:
  1177 			default:
  1158 					return true
  1178 				return true
  1159 				}
       
  1160 			}
  1179 			}
  1161 		}
  1180 		}
  1162 	}
  1181 	}
  1163 
  1182 
  1164 	return true
  1183 	return true
  1165 }
  1184 }
  1166 
  1185 
  1167 func (p *parser) inBodyEndTagFormatting(tagAtom a.Atom) {
  1186 func (p *parser) inBodyEndTagFormatting(tagAtom a.Atom, tagName string) {
  1168 	// This is the "adoption agency" algorithm, described at
  1187 	// This is the "adoption agency" algorithm, described at
  1169 	// https://html.spec.whatwg.org/multipage/syntax.html#adoptionAgency
  1188 	// https://html.spec.whatwg.org/multipage/syntax.html#adoptionAgency
  1170 
  1189 
  1171 	// TODO: this is a fairly literal line-by-line translation of that algorithm.
  1190 	// TODO: this is a fairly literal line-by-line translation of that algorithm.
  1172 	// Once the code successfully parses the comprehensive test suite, we should
  1191 	// Once the code successfully parses the comprehensive test suite, we should
  1173 	// refactor this code to be more idiomatic.
  1192 	// refactor this code to be more idiomatic.
  1174 
  1193 
  1175 	// Steps 1-4. The outer loop.
  1194 	// Steps 1-2
       
  1195 	if current := p.oe.top(); current.Data == tagName && p.afe.index(current) == -1 {
       
  1196 		p.oe.pop()
       
  1197 		return
       
  1198 	}
       
  1199 
       
  1200 	// Steps 3-5. The outer loop.
  1176 	for i := 0; i < 8; i++ {
  1201 	for i := 0; i < 8; i++ {
  1177 		// Step 5. Find the formatting element.
  1202 		// Step 6. Find the formatting element.
  1178 		var formattingElement *Node
  1203 		var formattingElement *Node
  1179 		for j := len(p.afe) - 1; j >= 0; j-- {
  1204 		for j := len(p.afe) - 1; j >= 0; j-- {
  1180 			if p.afe[j].Type == scopeMarkerNode {
  1205 			if p.afe[j].Type == scopeMarkerNode {
  1181 				break
  1206 				break
  1182 			}
  1207 			}
  1184 				formattingElement = p.afe[j]
  1209 				formattingElement = p.afe[j]
  1185 				break
  1210 				break
  1186 			}
  1211 			}
  1187 		}
  1212 		}
  1188 		if formattingElement == nil {
  1213 		if formattingElement == nil {
  1189 			p.inBodyEndTagOther(tagAtom)
  1214 			p.inBodyEndTagOther(tagAtom, tagName)
  1190 			return
  1215 			return
  1191 		}
  1216 		}
       
  1217 
       
  1218 		// Step 7. Ignore the tag if formatting element is not in the stack of open elements.
  1192 		feIndex := p.oe.index(formattingElement)
  1219 		feIndex := p.oe.index(formattingElement)
  1193 		if feIndex == -1 {
  1220 		if feIndex == -1 {
  1194 			p.afe.remove(formattingElement)
  1221 			p.afe.remove(formattingElement)
  1195 			return
  1222 			return
  1196 		}
  1223 		}
       
  1224 		// Step 8. Ignore the tag if formatting element is not in the scope.
  1197 		if !p.elementInScope(defaultScope, tagAtom) {
  1225 		if !p.elementInScope(defaultScope, tagAtom) {
  1198 			// Ignore the tag.
  1226 			// Ignore the tag.
  1199 			return
  1227 			return
  1200 		}
  1228 		}
  1201 
  1229 
  1202 		// Steps 9-10. Find the furthest block.
  1230 		// Step 9. This step is omitted because it's just a parse error but no need to return.
       
  1231 
       
  1232 		// Steps 10-11. Find the furthest block.
  1203 		var furthestBlock *Node
  1233 		var furthestBlock *Node
  1204 		for _, e := range p.oe[feIndex:] {
  1234 		for _, e := range p.oe[feIndex:] {
  1205 			if isSpecialElement(e) {
  1235 			if isSpecialElement(e) {
  1206 				furthestBlock = e
  1236 				furthestBlock = e
  1207 				break
  1237 				break
  1214 			}
  1244 			}
  1215 			p.afe.remove(e)
  1245 			p.afe.remove(e)
  1216 			return
  1246 			return
  1217 		}
  1247 		}
  1218 
  1248 
  1219 		// Steps 11-12. Find the common ancestor and bookmark node.
  1249 		// Steps 12-13. Find the common ancestor and bookmark node.
  1220 		commonAncestor := p.oe[feIndex-1]
  1250 		commonAncestor := p.oe[feIndex-1]
  1221 		bookmark := p.afe.index(formattingElement)
  1251 		bookmark := p.afe.index(formattingElement)
  1222 
  1252 
  1223 		// Step 13. The inner loop. Find the lastNode to reparent.
  1253 		// Step 14. The inner loop. Find the lastNode to reparent.
  1224 		lastNode := furthestBlock
  1254 		lastNode := furthestBlock
  1225 		node := furthestBlock
  1255 		node := furthestBlock
  1226 		x := p.oe.index(node)
  1256 		x := p.oe.index(node)
  1227 		// Steps 13.1-13.2
  1257 		// Step 14.1.
  1228 		for j := 0; j < 3; j++ {
  1258 		j := 0
  1229 			// Step 13.3.
  1259 		for {
       
  1260 			// Step 14.2.
       
  1261 			j++
       
  1262 			// Step. 14.3.
  1230 			x--
  1263 			x--
  1231 			node = p.oe[x]
  1264 			node = p.oe[x]
  1232 			// Step 13.4 - 13.5.
  1265 			// Step 14.4. Go to the next step if node is formatting element.
       
  1266 			if node == formattingElement {
       
  1267 				break
       
  1268 			}
       
  1269 			// Step 14.5. Remove node from the list of active formatting elements if
       
  1270 			// inner loop counter is greater than three and node is in the list of
       
  1271 			// active formatting elements.
       
  1272 			if ni := p.afe.index(node); j > 3 && ni > -1 {
       
  1273 				p.afe.remove(node)
       
  1274 				// If any element of the list of active formatting elements is removed,
       
  1275 				// we need to take care whether bookmark should be decremented or not.
       
  1276 				// This is because the value of bookmark may exceed the size of the
       
  1277 				// list by removing elements from the list.
       
  1278 				if ni <= bookmark {
       
  1279 					bookmark--
       
  1280 				}
       
  1281 				continue
       
  1282 			}
       
  1283 			// Step 14.6. Continue the next inner loop if node is not in the list of
       
  1284 			// active formatting elements.
  1233 			if p.afe.index(node) == -1 {
  1285 			if p.afe.index(node) == -1 {
  1234 				p.oe.remove(node)
  1286 				p.oe.remove(node)
  1235 				continue
  1287 				continue
  1236 			}
  1288 			}
  1237 			// Step 13.6.
  1289 			// Step 14.7.
  1238 			if node == formattingElement {
       
  1239 				break
       
  1240 			}
       
  1241 			// Step 13.7.
       
  1242 			clone := node.clone()
  1290 			clone := node.clone()
  1243 			p.afe[p.afe.index(node)] = clone
  1291 			p.afe[p.afe.index(node)] = clone
  1244 			p.oe[p.oe.index(node)] = clone
  1292 			p.oe[p.oe.index(node)] = clone
  1245 			node = clone
  1293 			node = clone
  1246 			// Step 13.8.
  1294 			// Step 14.8.
  1247 			if lastNode == furthestBlock {
  1295 			if lastNode == furthestBlock {
  1248 				bookmark = p.afe.index(node) + 1
  1296 				bookmark = p.afe.index(node) + 1
  1249 			}
  1297 			}
  1250 			// Step 13.9.
  1298 			// Step 14.9.
  1251 			if lastNode.Parent != nil {
  1299 			if lastNode.Parent != nil {
  1252 				lastNode.Parent.RemoveChild(lastNode)
  1300 				lastNode.Parent.RemoveChild(lastNode)
  1253 			}
  1301 			}
  1254 			node.AppendChild(lastNode)
  1302 			node.AppendChild(lastNode)
  1255 			// Step 13.10.
  1303 			// Step 14.10.
  1256 			lastNode = node
  1304 			lastNode = node
  1257 		}
  1305 		}
  1258 
  1306 
  1259 		// Step 14. Reparent lastNode to the common ancestor,
  1307 		// Step 15. Reparent lastNode to the common ancestor,
  1260 		// or for misnested table nodes, to the foster parent.
  1308 		// or for misnested table nodes, to the foster parent.
  1261 		if lastNode.Parent != nil {
  1309 		if lastNode.Parent != nil {
  1262 			lastNode.Parent.RemoveChild(lastNode)
  1310 			lastNode.Parent.RemoveChild(lastNode)
  1263 		}
  1311 		}
  1264 		switch commonAncestor.DataAtom {
  1312 		switch commonAncestor.DataAtom {
  1265 		case a.Table, a.Tbody, a.Tfoot, a.Thead, a.Tr:
  1313 		case a.Table, a.Tbody, a.Tfoot, a.Thead, a.Tr:
  1266 			p.fosterParent(lastNode)
  1314 			p.fosterParent(lastNode)
  1267 		case a.Template:
       
  1268 			// TODO: remove namespace checking
       
  1269 			if commonAncestor.Namespace == "html" {
       
  1270 				commonAncestor = commonAncestor.LastChild
       
  1271 			}
       
  1272 			fallthrough
       
  1273 		default:
  1315 		default:
  1274 			commonAncestor.AppendChild(lastNode)
  1316 			commonAncestor.AppendChild(lastNode)
  1275 		}
  1317 		}
  1276 
  1318 
  1277 		// Steps 15-17. Reparent nodes from the furthest block's children
  1319 		// Steps 16-18. Reparent nodes from the furthest block's children
  1278 		// to a clone of the formatting element.
  1320 		// to a clone of the formatting element.
  1279 		clone := formattingElement.clone()
  1321 		clone := formattingElement.clone()
  1280 		reparentChildren(clone, furthestBlock)
  1322 		reparentChildren(clone, furthestBlock)
  1281 		furthestBlock.AppendChild(clone)
  1323 		furthestBlock.AppendChild(clone)
  1282 
  1324 
  1283 		// Step 18. Fix up the list of active formatting elements.
  1325 		// Step 19. Fix up the list of active formatting elements.
  1284 		if oldLoc := p.afe.index(formattingElement); oldLoc != -1 && oldLoc < bookmark {
  1326 		if oldLoc := p.afe.index(formattingElement); oldLoc != -1 && oldLoc < bookmark {
  1285 			// Move the bookmark with the rest of the list.
  1327 			// Move the bookmark with the rest of the list.
  1286 			bookmark--
  1328 			bookmark--
  1287 		}
  1329 		}
  1288 		p.afe.remove(formattingElement)
  1330 		p.afe.remove(formattingElement)
  1289 		p.afe.insert(bookmark, clone)
  1331 		p.afe.insert(bookmark, clone)
  1290 
  1332 
  1291 		// Step 19. Fix up the stack of open elements.
  1333 		// Step 20. Fix up the stack of open elements.
  1292 		p.oe.remove(formattingElement)
  1334 		p.oe.remove(formattingElement)
  1293 		p.oe.insert(p.oe.index(furthestBlock)+1, clone)
  1335 		p.oe.insert(p.oe.index(furthestBlock)+1, clone)
  1294 	}
  1336 	}
  1295 }
  1337 }
  1296 
  1338 
  1297 // inBodyEndTagOther performs the "any other end tag" algorithm for inBodyIM.
  1339 // inBodyEndTagOther performs the "any other end tag" algorithm for inBodyIM.
  1298 // "Any other end tag" handling from 12.2.6.5 The rules for parsing tokens in foreign content
  1340 // "Any other end tag" handling from 12.2.6.5 The rules for parsing tokens in foreign content
  1299 // https://html.spec.whatwg.org/multipage/syntax.html#parsing-main-inforeign
  1341 // https://html.spec.whatwg.org/multipage/syntax.html#parsing-main-inforeign
  1300 func (p *parser) inBodyEndTagOther(tagAtom a.Atom) {
  1342 func (p *parser) inBodyEndTagOther(tagAtom a.Atom, tagName string) {
  1301 	for i := len(p.oe) - 1; i >= 0; i-- {
  1343 	for i := len(p.oe) - 1; i >= 0; i-- {
  1302 		if p.oe[i].DataAtom == tagAtom {
  1344 		// Two element nodes have the same tag if they have the same Data (a
       
  1345 		// string-typed field). As an optimization, for common HTML tags, each
       
  1346 		// Data string is assigned a unique, non-zero DataAtom (a uint32-typed
       
  1347 		// field), since integer comparison is faster than string comparison.
       
  1348 		// Uncommon (custom) tags get a zero DataAtom.
       
  1349 		//
       
  1350 		// The if condition here is equivalent to (p.oe[i].Data == tagName).
       
  1351 		if (p.oe[i].DataAtom == tagAtom) &&
       
  1352 			((tagAtom != 0) || (p.oe[i].Data == tagName)) {
  1303 			p.oe = p.oe[:i]
  1353 			p.oe = p.oe[:i]
  1304 			break
  1354 			break
  1305 		}
  1355 		}
  1306 		if isSpecialElement(p.oe[i]) {
  1356 		if isSpecialElement(p.oe[i]) {
  1307 			break
  1357 			break
  1449 func inCaptionIM(p *parser) bool {
  1499 func inCaptionIM(p *parser) bool {
  1450 	switch p.tok.Type {
  1500 	switch p.tok.Type {
  1451 	case StartTagToken:
  1501 	case StartTagToken:
  1452 		switch p.tok.DataAtom {
  1502 		switch p.tok.DataAtom {
  1453 		case a.Caption, a.Col, a.Colgroup, a.Tbody, a.Td, a.Tfoot, a.Thead, a.Tr:
  1503 		case a.Caption, a.Col, a.Colgroup, a.Tbody, a.Td, a.Tfoot, a.Thead, a.Tr:
  1454 			if p.popUntil(tableScope, a.Caption) {
  1504 			if !p.popUntil(tableScope, a.Caption) {
  1455 				p.clearActiveFormattingElements()
       
  1456 				p.im = inTableIM
       
  1457 				return false
       
  1458 			} else {
       
  1459 				// Ignore the token.
  1505 				// Ignore the token.
  1460 				return true
  1506 				return true
  1461 			}
  1507 			}
       
  1508 			p.clearActiveFormattingElements()
       
  1509 			p.im = inTableIM
       
  1510 			return false
  1462 		case a.Select:
  1511 		case a.Select:
  1463 			p.reconstructActiveFormattingElements()
  1512 			p.reconstructActiveFormattingElements()
  1464 			p.addElement()
  1513 			p.addElement()
  1465 			p.framesetOK = false
  1514 			p.framesetOK = false
  1466 			p.im = inSelectInTableIM
  1515 			p.im = inSelectInTableIM
  1473 				p.clearActiveFormattingElements()
  1522 				p.clearActiveFormattingElements()
  1474 				p.im = inTableIM
  1523 				p.im = inTableIM
  1475 			}
  1524 			}
  1476 			return true
  1525 			return true
  1477 		case a.Table:
  1526 		case a.Table:
  1478 			if p.popUntil(tableScope, a.Caption) {
  1527 			if !p.popUntil(tableScope, a.Caption) {
  1479 				p.clearActiveFormattingElements()
       
  1480 				p.im = inTableIM
       
  1481 				return false
       
  1482 			} else {
       
  1483 				// Ignore the token.
  1528 				// Ignore the token.
  1484 				return true
  1529 				return true
  1485 			}
  1530 			}
       
  1531 			p.clearActiveFormattingElements()
       
  1532 			p.im = inTableIM
       
  1533 			return false
  1486 		case a.Body, a.Col, a.Colgroup, a.Html, a.Tbody, a.Td, a.Tfoot, a.Th, a.Thead, a.Tr:
  1534 		case a.Body, a.Col, a.Colgroup, a.Html, a.Tbody, a.Td, a.Tfoot, a.Th, a.Thead, a.Tr:
  1487 			// Ignore the token.
  1535 			// Ignore the token.
  1488 			return true
  1536 			return true
  1489 		}
  1537 		}
  1490 	}
  1538 	}
  1691 			if !p.elementInScope(tableScope, p.tok.DataAtom) {
  1739 			if !p.elementInScope(tableScope, p.tok.DataAtom) {
  1692 				// Ignore the token.
  1740 				// Ignore the token.
  1693 				return true
  1741 				return true
  1694 			}
  1742 			}
  1695 			// Close the cell and reprocess.
  1743 			// Close the cell and reprocess.
  1696 			p.popUntil(tableScope, a.Td, a.Th)
  1744 			if p.popUntil(tableScope, a.Td, a.Th) {
  1697 			p.clearActiveFormattingElements()
  1745 				p.clearActiveFormattingElements()
       
  1746 			}
  1698 			p.im = inRowIM
  1747 			p.im = inRowIM
  1699 			return false
  1748 			return false
  1700 		}
  1749 		}
  1701 	}
  1750 	}
  1702 	return inBodyIM(p)
  1751 	return inBodyIM(p)
  1723 			if p.top().DataAtom == a.Optgroup {
  1772 			if p.top().DataAtom == a.Optgroup {
  1724 				p.oe.pop()
  1773 				p.oe.pop()
  1725 			}
  1774 			}
  1726 			p.addElement()
  1775 			p.addElement()
  1727 		case a.Select:
  1776 		case a.Select:
  1728 			p.tok.Type = EndTagToken
  1777 			if !p.popUntil(selectScope, a.Select) {
  1729 			return false
  1778 				// Ignore the token.
       
  1779 				return true
       
  1780 			}
       
  1781 			p.resetInsertionMode()
  1730 		case a.Input, a.Keygen, a.Textarea:
  1782 		case a.Input, a.Keygen, a.Textarea:
  1731 			if p.elementInScope(selectScope, a.Select) {
  1783 			if p.elementInScope(selectScope, a.Select) {
  1732 				p.parseImpliedToken(EndTagToken, a.Select, a.Select.String())
  1784 				p.parseImpliedToken(EndTagToken, a.Select, a.Select.String())
  1733 				return false
  1785 				return false
  1734 			}
  1786 			}
  1752 			}
  1804 			}
  1753 			if p.oe[i].DataAtom == a.Optgroup {
  1805 			if p.oe[i].DataAtom == a.Optgroup {
  1754 				p.oe = p.oe[:i]
  1806 				p.oe = p.oe[:i]
  1755 			}
  1807 			}
  1756 		case a.Select:
  1808 		case a.Select:
  1757 			if p.popUntil(selectScope, a.Select) {
  1809 			if !p.popUntil(selectScope, a.Select) {
  1758 				p.resetInsertionMode()
  1810 				// Ignore the token.
  1759 			}
  1811 				return true
       
  1812 			}
       
  1813 			p.resetInsertionMode()
  1760 		case a.Template:
  1814 		case a.Template:
  1761 			return inHeadIM(p)
  1815 			return inHeadIM(p)
  1762 		}
  1816 		}
  1763 	case CommentToken:
  1817 	case CommentToken:
  1764 		p.addChild(&Node{
  1818 		p.addChild(&Node{
  1779 func inSelectInTableIM(p *parser) bool {
  1833 func inSelectInTableIM(p *parser) bool {
  1780 	switch p.tok.Type {
  1834 	switch p.tok.Type {
  1781 	case StartTagToken, EndTagToken:
  1835 	case StartTagToken, EndTagToken:
  1782 		switch p.tok.DataAtom {
  1836 		switch p.tok.DataAtom {
  1783 		case a.Caption, a.Table, a.Tbody, a.Tfoot, a.Thead, a.Tr, a.Td, a.Th:
  1837 		case a.Caption, a.Table, a.Tbody, a.Tfoot, a.Thead, a.Tr, a.Td, a.Th:
  1784 			if p.tok.Type == StartTagToken || p.elementInScope(tableScope, p.tok.DataAtom) {
  1838 			if p.tok.Type == EndTagToken && !p.elementInScope(tableScope, p.tok.DataAtom) {
  1785 				p.parseImpliedToken(EndTagToken, a.Select, a.Select.String())
       
  1786 				return false
       
  1787 			} else {
       
  1788 				// Ignore the token.
  1839 				// Ignore the token.
  1789 				return true
  1840 				return true
  1790 			}
  1841 			}
       
  1842 			// This is like p.popUntil(selectScope, a.Select), but it also
       
  1843 			// matches <math select>, not just <select>. Matching the MathML
       
  1844 			// tag is arguably incorrect (conceptually), but it mimics what
       
  1845 			// Chromium does.
       
  1846 			for i := len(p.oe) - 1; i >= 0; i-- {
       
  1847 				if n := p.oe[i]; n.DataAtom == a.Select {
       
  1848 					p.oe = p.oe[:i]
       
  1849 					break
       
  1850 				}
       
  1851 			}
       
  1852 			p.resetInsertionMode()
       
  1853 			return false
  1791 		}
  1854 		}
  1792 	}
  1855 	}
  1793 	return inSelectIM(p)
  1856 	return inSelectIM(p)
  1794 }
  1857 }
  1795 
  1858 
  2066 		p.addChild(&Node{
  2129 		p.addChild(&Node{
  2067 			Type: CommentNode,
  2130 			Type: CommentNode,
  2068 			Data: p.tok.Data,
  2131 			Data: p.tok.Data,
  2069 		})
  2132 		})
  2070 	case StartTagToken:
  2133 	case StartTagToken:
  2071 		b := breakout[p.tok.Data]
  2134 		if !p.fragment {
  2072 		if p.tok.DataAtom == a.Font {
  2135 			b := breakout[p.tok.Data]
  2073 		loop:
  2136 			if p.tok.DataAtom == a.Font {
  2074 			for _, attr := range p.tok.Attr {
  2137 			loop:
  2075 				switch attr.Key {
  2138 				for _, attr := range p.tok.Attr {
  2076 				case "color", "face", "size":
  2139 					switch attr.Key {
  2077 					b = true
  2140 					case "color", "face", "size":
  2078 					break loop
  2141 						b = true
  2079 				}
  2142 						break loop
  2080 			}
  2143 					}
  2081 		}
  2144 				}
  2082 		if b {
  2145 			}
  2083 			for i := len(p.oe) - 1; i >= 0; i-- {
  2146 			if b {
  2084 				n := p.oe[i]
  2147 				for i := len(p.oe) - 1; i >= 0; i-- {
  2085 				if n.Namespace == "" || htmlIntegrationPoint(n) || mathMLTextIntegrationPoint(n) {
  2148 					n := p.oe[i]
  2086 					p.oe = p.oe[:i+1]
  2149 					if n.Namespace == "" || htmlIntegrationPoint(n) || mathMLTextIntegrationPoint(n) {
  2087 					break
  2150 						p.oe = p.oe[:i+1]
  2088 				}
  2151 						break
  2089 			}
  2152 					}
  2090 			return false
  2153 				}
  2091 		}
  2154 				return false
  2092 		switch p.top().Namespace {
  2155 			}
       
  2156 		}
       
  2157 		current := p.adjustedCurrentNode()
       
  2158 		switch current.Namespace {
  2093 		case "math":
  2159 		case "math":
  2094 			adjustAttributeNames(p.tok.Attr, mathMLAttributeAdjustments)
  2160 			adjustAttributeNames(p.tok.Attr, mathMLAttributeAdjustments)
  2095 		case "svg":
  2161 		case "svg":
  2096 			// Adjust SVG tag names. The tokenizer lower-cases tag names, but
  2162 			// Adjust SVG tag names. The tokenizer lower-cases tag names, but
  2097 			// SVG wants e.g. "foreignObject" with a capital second "O".
  2163 			// SVG wants e.g. "foreignObject" with a capital second "O".
  2102 			adjustAttributeNames(p.tok.Attr, svgAttributeAdjustments)
  2168 			adjustAttributeNames(p.tok.Attr, svgAttributeAdjustments)
  2103 		default:
  2169 		default:
  2104 			panic("html: bad parser state: unexpected namespace")
  2170 			panic("html: bad parser state: unexpected namespace")
  2105 		}
  2171 		}
  2106 		adjustForeignAttributes(p.tok.Attr)
  2172 		adjustForeignAttributes(p.tok.Attr)
  2107 		namespace := p.top().Namespace
  2173 		namespace := current.Namespace
  2108 		p.addElement()
  2174 		p.addElement()
  2109 		p.top().Namespace = namespace
  2175 		p.top().Namespace = namespace
  2110 		if namespace != "" {
  2176 		if namespace != "" {
  2111 			// Don't let the tokenizer go into raw text mode in foreign content
  2177 			// Don't let the tokenizer go into raw text mode in foreign content
  2112 			// (e.g. in an SVG <title> tag).
  2178 			// (e.g. in an SVG <title> tag).
  2131 		// Ignore the token.
  2197 		// Ignore the token.
  2132 	}
  2198 	}
  2133 	return true
  2199 	return true
  2134 }
  2200 }
  2135 
  2201 
       
  2202 // Section 12.2.4.2.
       
  2203 func (p *parser) adjustedCurrentNode() *Node {
       
  2204 	if len(p.oe) == 1 && p.fragment && p.context != nil {
       
  2205 		return p.context
       
  2206 	}
       
  2207 	return p.oe.top()
       
  2208 }
       
  2209 
  2136 // Section 12.2.6.
  2210 // Section 12.2.6.
  2137 func (p *parser) inForeignContent() bool {
  2211 func (p *parser) inForeignContent() bool {
  2138 	if len(p.oe) == 0 {
  2212 	if len(p.oe) == 0 {
  2139 		return false
  2213 		return false
  2140 	}
  2214 	}
  2141 	n := p.oe[len(p.oe)-1]
  2215 	n := p.adjustedCurrentNode()
  2142 	if n.Namespace == "" {
  2216 	if n.Namespace == "" {
  2143 		return false
  2217 		return false
  2144 	}
  2218 	}
  2145 	if mathMLTextIntegrationPoint(n) {
  2219 	if mathMLTextIntegrationPoint(n) {
  2146 		if p.tok.Type == StartTagToken && p.tok.DataAtom != a.Mglyph && p.tok.DataAtom != a.Malignmark {
  2220 		if p.tok.Type == StartTagToken && p.tok.DataAtom != a.Mglyph && p.tok.DataAtom != a.Malignmark {
  2230 // <tag>s. Conversely, explicit <tag>s in r's data can be silently dropped,
  2304 // <tag>s. Conversely, explicit <tag>s in r's data can be silently dropped,
  2231 // with no corresponding node in the resulting tree.
  2305 // with no corresponding node in the resulting tree.
  2232 //
  2306 //
  2233 // The input is assumed to be UTF-8 encoded.
  2307 // The input is assumed to be UTF-8 encoded.
  2234 func Parse(r io.Reader) (*Node, error) {
  2308 func Parse(r io.Reader) (*Node, error) {
       
  2309 	return ParseWithOptions(r)
       
  2310 }
       
  2311 
       
  2312 // ParseFragment parses a fragment of HTML and returns the nodes that were
       
  2313 // found. If the fragment is the InnerHTML for an existing element, pass that
       
  2314 // element in context.
       
  2315 //
       
  2316 // It has the same intricacies as Parse.
       
  2317 func ParseFragment(r io.Reader, context *Node) ([]*Node, error) {
       
  2318 	return ParseFragmentWithOptions(r, context)
       
  2319 }
       
  2320 
       
  2321 // ParseOption configures a parser.
       
  2322 type ParseOption func(p *parser)
       
  2323 
       
  2324 // ParseOptionEnableScripting configures the scripting flag.
       
  2325 // https://html.spec.whatwg.org/multipage/webappapis.html#enabling-and-disabling-scripting
       
  2326 //
       
  2327 // By default, scripting is enabled.
       
  2328 func ParseOptionEnableScripting(enable bool) ParseOption {
       
  2329 	return func(p *parser) {
       
  2330 		p.scripting = enable
       
  2331 	}
       
  2332 }
       
  2333 
       
  2334 // ParseWithOptions is like Parse, with options.
       
  2335 func ParseWithOptions(r io.Reader, opts ...ParseOption) (*Node, error) {
  2235 	p := &parser{
  2336 	p := &parser{
  2236 		tokenizer: NewTokenizer(r),
  2337 		tokenizer: NewTokenizer(r),
  2237 		doc: &Node{
  2338 		doc: &Node{
  2238 			Type: DocumentNode,
  2339 			Type: DocumentNode,
  2239 		},
  2340 		},
  2240 		scripting:  true,
  2341 		scripting:  true,
  2241 		framesetOK: true,
  2342 		framesetOK: true,
  2242 		im:         initialIM,
  2343 		im:         initialIM,
  2243 	}
  2344 	}
  2244 	err := p.parse()
  2345 
  2245 	if err != nil {
  2346 	for _, f := range opts {
       
  2347 		f(p)
       
  2348 	}
       
  2349 
       
  2350 	if err := p.parse(); err != nil {
  2246 		return nil, err
  2351 		return nil, err
  2247 	}
  2352 	}
  2248 	return p.doc, nil
  2353 	return p.doc, nil
  2249 }
  2354 }
  2250 
  2355 
  2251 // ParseFragment parses a fragment of HTML and returns the nodes that were
  2356 // ParseFragmentWithOptions is like ParseFragment, with options.
  2252 // found. If the fragment is the InnerHTML for an existing element, pass that
  2357 func ParseFragmentWithOptions(r io.Reader, context *Node, opts ...ParseOption) ([]*Node, error) {
  2253 // element in context.
       
  2254 //
       
  2255 // It has the same intricacies as Parse.
       
  2256 func ParseFragment(r io.Reader, context *Node) ([]*Node, error) {
       
  2257 	contextTag := ""
  2358 	contextTag := ""
  2258 	if context != nil {
  2359 	if context != nil {
  2259 		if context.Type != ElementNode {
  2360 		if context.Type != ElementNode {
  2260 			return nil, errors.New("html: ParseFragment of non-element Node")
  2361 			return nil, errors.New("html: ParseFragment of non-element Node")
  2261 		}
  2362 		}
  2266 			return nil, fmt.Errorf("html: inconsistent Node: DataAtom=%q, Data=%q", context.DataAtom, context.Data)
  2367 			return nil, fmt.Errorf("html: inconsistent Node: DataAtom=%q, Data=%q", context.DataAtom, context.Data)
  2267 		}
  2368 		}
  2268 		contextTag = context.DataAtom.String()
  2369 		contextTag = context.DataAtom.String()
  2269 	}
  2370 	}
  2270 	p := &parser{
  2371 	p := &parser{
  2271 		tokenizer: NewTokenizerFragment(r, contextTag),
       
  2272 		doc: &Node{
  2372 		doc: &Node{
  2273 			Type: DocumentNode,
  2373 			Type: DocumentNode,
  2274 		},
  2374 		},
  2275 		scripting: true,
  2375 		scripting: true,
  2276 		fragment:  true,
  2376 		fragment:  true,
  2277 		context:   context,
  2377 		context:   context,
  2278 	}
  2378 	}
       
  2379 	if context != nil && context.Namespace != "" {
       
  2380 		p.tokenizer = NewTokenizer(r)
       
  2381 	} else {
       
  2382 		p.tokenizer = NewTokenizerFragment(r, contextTag)
       
  2383 	}
       
  2384 
       
  2385 	for _, f := range opts {
       
  2386 		f(p)
       
  2387 	}
  2279 
  2388 
  2280 	root := &Node{
  2389 	root := &Node{
  2281 		Type:     ElementNode,
  2390 		Type:     ElementNode,
  2282 		DataAtom: a.Html,
  2391 		DataAtom: a.Html,
  2283 		Data:     a.Html.String(),
  2392 		Data:     a.Html.String(),
  2294 			p.form = n
  2403 			p.form = n
  2295 			break
  2404 			break
  2296 		}
  2405 		}
  2297 	}
  2406 	}
  2298 
  2407 
  2299 	err := p.parse()
  2408 	if err := p.parse(); err != nil {
  2300 	if err != nil {
       
  2301 		return nil, err
  2409 		return nil, err
  2302 	}
  2410 	}
  2303 
  2411 
  2304 	parent := p.doc
  2412 	parent := p.doc
  2305 	if context != nil {
  2413 	if context != nil {