Best K6 code snippet using html.TFoot
parse.go
Source:parse.go
1// Copyright 2010 The Go Authors. All rights reserved.2// Use of this source code is governed by a BSD-style3// license that can be found in the LICENSE file.4// golang.org/x/net/html/parse.go5package html6import (7 "errors"8 "fmt"9 "io"10 "strings"11 a "golang.org/x/net/html/atom"12)13// A parser implements the HTML5 parsing algorithm:14// https://html.spec.whatwg.org/multipage/syntax.html#tree-construction15type parser struct {16 // tokenizer provides the tokens for the parser.17 tokenizer *Tokenizer18 // tok is the most recently read token.19 tok Token20 // Self-closing tags like <hr/> are treated as start tags, except that21 // hasSelfClosingToken is set while they are being processed.22 hasSelfClosingToken bool23 // doc is the document root element.24 doc *Node25 // The stack of open elements (section 12.2.3.2) and active formatting26 // elements (section 12.2.3.3).27 oe, afe nodeStack28 // Element pointers (section 12.2.3.4).29 head, form *Node30 // Other parsing state flags (section 12.2.3.5).31 scripting, framesetOK bool32 // im is the current insertion mode.33 im insertionMode34 // originalIM is the insertion mode to go back to after completing a text35 // or inTableText insertion mode.36 originalIM insertionMode37 // fosterParenting is whether new elements should be inserted according to38 // the foster parenting rules (section 12.2.5.3).39 fosterParenting bool40 // quirks is whether the parser is operating in "quirks mode."41 quirks bool42 // fragment is whether the parser is parsing an HTML fragment.43 fragment bool44 // context is the context element when parsing an HTML fragment45 // (section 12.4).46 context *Node47}48func (p *parser) top() *Node {49 if n := p.oe.top(); n != nil {50 return n51 }52 return p.doc53}54// Stop tags for use in popUntil. These come from section 12.2.3.2.55var (56 defaultScopeStopTags = map[string][]a.Atom{57 "": {a.Applet, a.Caption, a.Html, a.Table, a.Td, a.Th, a.Marquee, a.Object, a.Template},58 "math": {a.AnnotationXml, a.Mi, a.Mn, a.Mo, a.Ms, a.Mtext},59 "svg": {a.Desc, a.ForeignObject, a.Title},60 }61)62type scope int63const (64 defaultScope scope = iota65 listItemScope66 buttonScope67 tableScope68 tableRowScope69 tableBodyScope70 selectScope71)72// popUntil pops the stack of open elements at the highest element whose tag73// is in matchTags, provided there is no higher element in the scope's stop74// tags (as defined in section 12.2.3.2). It returns whether or not there was75// such an element. If there was not, popUntil leaves the stack unchanged.76//77// For example, the set of stop tags for table scope is: "html", "table". If78// the stack was:79// ["html", "body", "font", "table", "b", "i", "u"]80// then popUntil(tableScope, "font") would return false, but81// popUntil(tableScope, "i") would return true and the stack would become:82// ["html", "body", "font", "table", "b"]83//84// If an element's tag is in both the stop tags and matchTags, then the stack85// will be popped and the function returns true (provided, of course, there was86// no higher element in the stack that was also in the stop tags). For example,87// popUntil(tableScope, "table") returns true and leaves:88// ["html", "body", "font"]89func (p *parser) popUntil(s scope, matchTags ...a.Atom) bool {90 if i := p.indexOfElementInScope(s, matchTags...); i != -1 {91 p.oe = p.oe[:i]92 return true93 }94 return false95}96// indexOfElementInScope returns the index in p.oe of the highest element whose97// tag is in matchTags that is in scope. If no matching element is in scope, it98// returns -1.99func (p *parser) indexOfElementInScope(s scope, matchTags ...a.Atom) int {100 for i := len(p.oe) - 1; i >= 0; i-- {101 tagAtom := p.oe[i].DataAtom102 if p.oe[i].Namespace == "" {103 for _, t := range matchTags {104 if t == tagAtom {105 return i106 }107 }108 switch s {109 case defaultScope:110 // No-op.111 case listItemScope:112 if tagAtom == a.Ol || tagAtom == a.Ul {113 return -1114 }115 case buttonScope:116 if tagAtom == a.Button {117 return -1118 }119 case tableScope:120 if tagAtom == a.Html || tagAtom == a.Table {121 return -1122 }123 case selectScope:124 if tagAtom != a.Optgroup && tagAtom != a.Option {125 return -1126 }127 default:128 panic("unreachable")129 }130 }131 switch s {132 case defaultScope, listItemScope, buttonScope:133 for _, t := range defaultScopeStopTags[p.oe[i].Namespace] {134 if t == tagAtom {135 return -1136 }137 }138 }139 }140 return -1141}142// elementInScope is like popUntil, except that it doesn't modify the stack of143// open elements.144func (p *parser) elementInScope(s scope, matchTags ...a.Atom) bool {145 return p.indexOfElementInScope(s, matchTags...) != -1146}147// clearStackToContext pops elements off the stack of open elements until a148// scope-defined element is found.149func (p *parser) clearStackToContext(s scope) {150 for i := len(p.oe) - 1; i >= 0; i-- {151 tagAtom := p.oe[i].DataAtom152 switch s {153 case tableScope:154 if tagAtom == a.Html || tagAtom == a.Table {155 p.oe = p.oe[:i+1]156 return157 }158 case tableRowScope:159 if tagAtom == a.Html || tagAtom == a.Tr {160 p.oe = p.oe[:i+1]161 return162 }163 case tableBodyScope:164 if tagAtom == a.Html || tagAtom == a.Tbody || tagAtom == a.Tfoot || tagAtom == a.Thead {165 p.oe = p.oe[:i+1]166 return167 }168 default:169 panic("unreachable")170 }171 }172}173// generateImpliedEndTags pops nodes off the stack of open elements as long as174// the top node has a tag name of dd, dt, li, option, optgroup, p, rp, or rt.175// If exceptions are specified, nodes with that name will not be popped off.176func (p *parser) generateImpliedEndTags(exceptions ...string) {177 var i int178loop:179 for i = len(p.oe) - 1; i >= 0; i-- {180 n := p.oe[i]181 if n.Type == ElementNode {182 switch n.DataAtom {183 case a.Dd, a.Dt, a.Li, a.Option, a.Optgroup, a.P, a.Rp, a.Rt:184 for _, except := range exceptions {185 if n.Data == except {186 break loop187 }188 }189 continue190 }191 }192 break193 }194 p.oe = p.oe[:i+1]195}196// addChild adds a child node n to the top element, and pushes n onto the stack197// of open elements if it is an element node.198func (p *parser) addChild(n *Node) {199 if p.shouldFosterParent() {200 p.fosterParent(n)201 } else {202 p.top().AppendChild(n)203 }204 if n.Type == ElementNode {205 p.oe = append(p.oe, n)206 }207}208// shouldFosterParent returns whether the next node to be added should be209// foster parented.210func (p *parser) shouldFosterParent() bool {211 if p.fosterParenting {212 switch p.top().DataAtom {213 case a.Table, a.Tbody, a.Tfoot, a.Thead, a.Tr:214 return true215 }216 }217 return false218}219// fosterParent adds a child node according to the foster parenting rules.220// Section 12.2.5.3, "foster parenting".221func (p *parser) fosterParent(n *Node) {222 var table, parent, prev *Node223 var i int224 for i = len(p.oe) - 1; i >= 0; i-- {225 if p.oe[i].DataAtom == a.Table {226 table = p.oe[i]227 break228 }229 }230 if table == nil {231 // The foster parent is the html element.232 parent = p.oe[0]233 } else {234 parent = table.Parent235 }236 if parent == nil {237 parent = p.oe[i-1]238 }239 if table != nil {240 prev = table.PrevSibling241 } else {242 prev = parent.LastChild243 }244 if prev != nil && prev.Type == TextNode && n.Type == TextNode {245 prev.Data += n.Data246 return247 }248 parent.InsertBefore(n, table)249}250// addText adds text to the preceding node if it is a text node, or else it251// calls addChild with a new text node.252func (p *parser) addText(text string) {253 if text == "" {254 return255 }256 if p.shouldFosterParent() {257 p.fosterParent(&Node{258 Type: TextNode,259 Data: text,260 })261 return262 }263 t := p.top()264 if n := t.LastChild; n != nil && n.Type == TextNode {265 n.Data += text266 return267 }268 p.addChild(&Node{269 Type: TextNode,270 Data: text,271 })272}273// addElement adds a child element based on the current token.274func (p *parser) addElement() {275 p.addChild(&Node{276 Type: ElementNode,277 DataAtom: p.tok.DataAtom,278 Data: p.tok.Data,279 Attr: p.tok.Attr,280 })281}282// Section 12.2.3.3.283func (p *parser) addFormattingElement() {284 tagAtom, attr := p.tok.DataAtom, p.tok.Attr285 p.addElement()286 // Implement the Noah's Ark clause, but with three per family instead of two.287 identicalElements := 0288findIdenticalElements:289 for i := len(p.afe) - 1; i >= 0; i-- {290 n := p.afe[i]291 if n.Type == scopeMarkerNode {292 break293 }294 if n.Type != ElementNode {295 continue296 }297 if n.Namespace != "" {298 continue299 }300 if n.DataAtom != tagAtom {301 continue302 }303 if len(n.Attr) != len(attr) {304 continue305 }306 compareAttributes:307 for _, t0 := range n.Attr {308 for _, t1 := range attr {309 if t0.Key == t1.Key && t0.Namespace == t1.Namespace && t0.Val == t1.Val {310 // Found a match for this attribute, continue with the next attribute.311 continue compareAttributes312 }313 }314 // If we get here, there is no attribute that matches a.315 // Therefore the element is not identical to the new one.316 continue findIdenticalElements317 }318 identicalElements++319 if identicalElements >= 3 {320 p.afe.remove(n)321 }322 }323 p.afe = append(p.afe, p.top())324}325// Section 12.2.3.3.326func (p *parser) clearActiveFormattingElements() {327 for {328 n := p.afe.pop()329 if len(p.afe) == 0 || n.Type == scopeMarkerNode {330 return331 }332 }333}334// Section 12.2.3.3.335func (p *parser) reconstructActiveFormattingElements() {336 n := p.afe.top()337 if n == nil {338 return339 }340 if n.Type == scopeMarkerNode || p.oe.index(n) != -1 {341 return342 }343 i := len(p.afe) - 1344 for n.Type != scopeMarkerNode && p.oe.index(n) == -1 {345 if i == 0 {346 i = -1347 break348 }349 i--350 n = p.afe[i]351 }352 for {353 i++354 clone := p.afe[i].clone()355 p.addChild(clone)356 p.afe[i] = clone357 if i == len(p.afe)-1 {358 break359 }360 }361}362// Section 12.2.4.363func (p *parser) acknowledgeSelfClosingTag() {364 p.hasSelfClosingToken = false365}366// An insertion mode (section 12.2.3.1) is the state transition function from367// a particular state in the HTML5 parser's state machine. It updates the368// parser's fields depending on parser.tok (where ErrorToken means EOF).369// It returns whether the token was consumed.370type insertionMode func(*parser) bool371// setOriginalIM sets the insertion mode to return to after completing a text or372// inTableText insertion mode.373// Section 12.2.3.1, "using the rules for".374func (p *parser) setOriginalIM() {375 if p.originalIM != nil {376 panic("html: bad parser state: originalIM was set twice")377 }378 p.originalIM = p.im379}380// Section 12.2.3.1, "reset the insertion mode".381func (p *parser) resetInsertionMode() {382 for i := len(p.oe) - 1; i >= 0; i-- {383 n := p.oe[i]384 if i == 0 && p.context != nil {385 n = p.context386 }387 switch n.DataAtom {388 case a.Select:389 p.im = inSelectIM390 case a.Td, a.Th:391 p.im = inCellIM392 case a.Tr:393 p.im = inRowIM394 case a.Tbody, a.Thead, a.Tfoot:395 p.im = inTableBodyIM396 case a.Caption:397 p.im = inCaptionIM398 case a.Colgroup:399 p.im = inColumnGroupIM400 case a.Table:401 p.im = inTableIM402 case a.Head:403 p.im = inBodyIM404 case a.Body:405 p.im = inBodyIM406 case a.Frameset:407 p.im = inFramesetIM408 case a.Html:409 p.im = beforeHeadIM410 default:411 continue412 }413 return414 }415 p.im = inBodyIM416}417const whitespace = " \t\r\n\f"418// Section 12.2.5.4.1.419func initialIM(p *parser) bool {420 switch p.tok.Type {421 case TextToken:422 p.tok.Data = strings.TrimLeft(p.tok.Data, whitespace)423 if len(p.tok.Data) == 0 {424 // It was all whitespace, so ignore it.425 return true426 }427 case CommentToken:428 p.doc.AppendChild(&Node{429 Type: CommentNode,430 Data: p.tok.Data,431 })432 return true433 case DoctypeToken:434 n, quirks := parseDoctype(p.tok.Data)435 p.doc.AppendChild(n)436 p.quirks = quirks437 p.im = beforeHTMLIM438 return true439 }440 p.quirks = true441 p.im = beforeHTMLIM442 return false443}444// Section 12.2.5.4.2.445func beforeHTMLIM(p *parser) bool {446 switch p.tok.Type {447 case DoctypeToken:448 // Ignore the token.449 return true450 case TextToken:451 p.tok.Data = strings.TrimLeft(p.tok.Data, whitespace)452 if len(p.tok.Data) == 0 {453 // It was all whitespace, so ignore it.454 return true455 }456 case StartTagToken:457 if p.tok.DataAtom == a.Html {458 p.addElement()459 p.im = beforeHeadIM460 return true461 }462 case EndTagToken:463 switch p.tok.DataAtom {464 case a.Head, a.Body, a.Html, a.Br:465 p.parseImpliedToken(StartTagToken, a.Html, a.Html.String())466 return false467 default:468 // Ignore the token.469 return true470 }471 case CommentToken:472 p.doc.AppendChild(&Node{473 Type: CommentNode,474 Data: p.tok.Data,475 })476 return true477 }478 p.parseImpliedToken(StartTagToken, a.Html, a.Html.String())479 return false480}481// Section 12.2.5.4.3.482func beforeHeadIM(p *parser) bool {483 switch p.tok.Type {484 case TextToken:485 p.tok.Data = strings.TrimLeft(p.tok.Data, whitespace)486 if len(p.tok.Data) == 0 {487 // It was all whitespace, so ignore it.488 return true489 }490 case StartTagToken:491 switch p.tok.DataAtom {492 case a.Head:493 p.addElement()494 p.head = p.top()495 p.im = inHeadIM496 return true497 case a.Html:498 return inBodyIM(p)499 }500 case EndTagToken:501 switch p.tok.DataAtom {502 case a.Head, a.Body, a.Html, a.Br:503 p.parseImpliedToken(StartTagToken, a.Head, a.Head.String())504 return false505 default:506 // Ignore the token.507 return true508 }509 case CommentToken:510 p.addChild(&Node{511 Type: CommentNode,512 Data: p.tok.Data,513 })514 return true515 case DoctypeToken:516 // Ignore the token.517 return true518 }519 p.parseImpliedToken(StartTagToken, a.Head, a.Head.String())520 return false521}522// Section 12.2.5.4.4.523func inHeadIM(p *parser) bool {524 switch p.tok.Type {525 case TextToken:526 s := strings.TrimLeft(p.tok.Data, whitespace)527 if len(s) < len(p.tok.Data) {528 // Add the initial whitespace to the current node.529 p.addText(p.tok.Data[:len(p.tok.Data)-len(s)])530 if s == "" {531 return true532 }533 p.tok.Data = s534 }535 case StartTagToken:536 switch p.tok.DataAtom {537 case a.Html:538 return inBodyIM(p)539 case a.Base, a.Basefont, a.Bgsound, a.Command, a.Link, a.Meta:540 p.addElement()541 p.oe.pop()542 p.acknowledgeSelfClosingTag()543 return true544 case a.Script, a.Title, a.Noscript, a.Noframes, a.Style:545 p.addElement()546 p.setOriginalIM()547 p.im = textIM548 return true549 case a.Head:550 // Ignore the token.551 return true552 }553 case EndTagToken:554 switch p.tok.DataAtom {555 case a.Head:556 n := p.oe.pop()557 if n.DataAtom != a.Head {558 panic("html: bad parser state: <head> element not found, in the in-head insertion mode")559 }560 p.im = afterHeadIM561 return true562 case a.Body, a.Html, a.Br:563 p.parseImpliedToken(EndTagToken, a.Head, a.Head.String())564 return false565 default:566 // Ignore the token.567 return true568 }569 case CommentToken:570 p.addChild(&Node{571 Type: CommentNode,572 Data: p.tok.Data,573 })574 return true575 case DoctypeToken:576 // Ignore the token.577 return true578 }579 p.parseImpliedToken(EndTagToken, a.Head, a.Head.String())580 return false581}582// Section 12.2.5.4.6.583func afterHeadIM(p *parser) bool {584 switch p.tok.Type {585 case TextToken:586 s := strings.TrimLeft(p.tok.Data, whitespace)587 if len(s) < len(p.tok.Data) {588 // Add the initial whitespace to the current node.589 p.addText(p.tok.Data[:len(p.tok.Data)-len(s)])590 if s == "" {591 return true592 }593 p.tok.Data = s594 }595 case StartTagToken:596 switch p.tok.DataAtom {597 case a.Html:598 return inBodyIM(p)599 case a.Body:600 p.addElement()601 p.framesetOK = false602 p.im = inBodyIM603 return true604 case a.Frameset:605 p.addElement()606 p.im = inFramesetIM607 return true608 case a.Base, a.Basefont, a.Bgsound, a.Link, a.Meta, a.Noframes, a.Script, a.Style, a.Title:609 p.oe = append(p.oe, p.head)610 defer p.oe.remove(p.head)611 return inHeadIM(p)612 case a.Head:613 // Ignore the token.614 return true615 }616 case EndTagToken:617 switch p.tok.DataAtom {618 case a.Body, a.Html, a.Br:619 // Drop down to creating an implied <body> tag.620 default:621 // Ignore the token.622 return true623 }624 case CommentToken:625 p.addChild(&Node{626 Type: CommentNode,627 Data: p.tok.Data,628 })629 return true630 case DoctypeToken:631 // Ignore the token.632 return true633 }634 p.parseImpliedToken(StartTagToken, a.Body, a.Body.String())635 p.framesetOK = true636 return false637}638// copyAttributes copies attributes of src not found on dst to dst.639func copyAttributes(dst *Node, src Token) {640 if len(src.Attr) == 0 {641 return642 }643 attr := map[string]string{}644 for _, t := range dst.Attr {645 attr[t.Key] = t.Val646 }647 for _, t := range src.Attr {648 if _, ok := attr[t.Key]; !ok {649 dst.Attr = append(dst.Attr, t)650 attr[t.Key] = t.Val651 }652 }653}654// Section 12.2.5.4.7.655func inBodyIM(p *parser) bool {656 switch p.tok.Type {657 case TextToken:658 d := p.tok.Data659 switch n := p.oe.top(); n.DataAtom {660 case a.Pre, a.Listing:661 if n.FirstChild == nil {662 // Ignore a newline at the start of a <pre> block.663 if d != "" && d[0] == '\r' {664 d = d[1:]665 }666 if d != "" && d[0] == '\n' {667 d = d[1:]668 }669 }670 }671 d = strings.Replace(d, "\x00", "", -1)672 if d == "" {673 return true674 }675 p.reconstructActiveFormattingElements()676 p.addText(d)677 if p.framesetOK && strings.TrimLeft(d, whitespace) != "" {678 // There were non-whitespace characters inserted.679 p.framesetOK = false680 }681 case StartTagToken:682 switch p.tok.DataAtom {683 case a.Html:684 copyAttributes(p.oe[0], p.tok)685 case a.Base, a.Basefont, a.Bgsound, a.Command, a.Link, a.Meta, a.Noframes, a.Script, a.Style, a.Title:686 return inHeadIM(p)687 case a.Body:688 if len(p.oe) >= 2 {689 body := p.oe[1]690 if body.Type == ElementNode && body.DataAtom == a.Body {691 p.framesetOK = false692 copyAttributes(body, p.tok)693 }694 }695 case a.Frameset:696 if !p.framesetOK || len(p.oe) < 2 || p.oe[1].DataAtom != a.Body {697 // Ignore the token.698 return true699 }700 body := p.oe[1]701 if body.Parent != nil {702 body.Parent.RemoveChild(body)703 }704 p.oe = p.oe[:1]705 p.addElement()706 p.im = inFramesetIM707 return true708 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:709 p.popUntil(buttonScope, a.P)710 p.addElement()711 case a.H1, a.H2, a.H3, a.H4, a.H5, a.H6:712 p.popUntil(buttonScope, a.P)713 switch n := p.top(); n.DataAtom {714 case a.H1, a.H2, a.H3, a.H4, a.H5, a.H6:715 p.oe.pop()716 }717 p.addElement()718 case a.Pre, a.Listing:719 p.popUntil(buttonScope, a.P)720 p.addElement()721 // The newline, if any, will be dealt with by the TextToken case.722 p.framesetOK = false723 case a.Form:724 if p.form == nil {725 p.popUntil(buttonScope, a.P)726 p.addElement()727 p.form = p.top()728 }729 case a.Li:730 p.framesetOK = false731 for i := len(p.oe) - 1; i >= 0; i-- {732 node := p.oe[i]733 switch node.DataAtom {734 case a.Li:735 p.oe = p.oe[:i]736 case a.Address, a.Div, a.P:737 continue738 default:739 if !isSpecialElement(node) {740 continue741 }742 }743 break744 }745 p.popUntil(buttonScope, a.P)746 p.addElement()747 case a.Dd, a.Dt:748 p.framesetOK = false749 for i := len(p.oe) - 1; i >= 0; i-- {750 node := p.oe[i]751 switch node.DataAtom {752 case a.Dd, a.Dt:753 p.oe = p.oe[:i]754 case a.Address, a.Div, a.P:755 continue756 default:757 if !isSpecialElement(node) {758 continue759 }760 }761 break762 }763 p.popUntil(buttonScope, a.P)764 p.addElement()765 case a.Plaintext:766 p.popUntil(buttonScope, a.P)767 p.addElement()768 case a.Button:769 p.popUntil(defaultScope, a.Button)770 p.reconstructActiveFormattingElements()771 p.addElement()772 p.framesetOK = false773 case a.A:774 for i := len(p.afe) - 1; i >= 0 && p.afe[i].Type != scopeMarkerNode; i-- {775 if n := p.afe[i]; n.Type == ElementNode && n.DataAtom == a.A {776 p.inBodyEndTagFormatting(a.A)777 p.oe.remove(n)778 p.afe.remove(n)779 break780 }781 }782 p.reconstructActiveFormattingElements()783 p.addFormattingElement()784 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:785 p.reconstructActiveFormattingElements()786 p.addFormattingElement()787 case a.Nobr:788 p.reconstructActiveFormattingElements()789 if p.elementInScope(defaultScope, a.Nobr) {790 p.inBodyEndTagFormatting(a.Nobr)791 p.reconstructActiveFormattingElements()792 }793 p.addFormattingElement()794 case a.Applet, a.Marquee, a.Object:795 p.reconstructActiveFormattingElements()796 p.addElement()797 p.afe = append(p.afe, &scopeMarker)798 p.framesetOK = false799 case a.Table:800 if !p.quirks {801 p.popUntil(buttonScope, a.P)802 }803 p.addElement()804 p.framesetOK = false805 p.im = inTableIM806 return true807 case a.Area, a.Br, a.Embed, a.Img, a.Input, a.Keygen, a.Wbr:808 p.reconstructActiveFormattingElements()809 p.addElement()810 p.oe.pop()811 p.acknowledgeSelfClosingTag()812 if p.tok.DataAtom == a.Input {813 for _, t := range p.tok.Attr {814 if t.Key == "type" {815 if strings.ToLower(t.Val) == "hidden" {816 // Skip setting framesetOK = false817 return true818 }819 }820 }821 }822 p.framesetOK = false823 case a.Param, a.Source, a.Track:824 p.addElement()825 p.oe.pop()826 p.acknowledgeSelfClosingTag()827 case a.Hr:828 p.popUntil(buttonScope, a.P)829 p.addElement()830 p.oe.pop()831 p.acknowledgeSelfClosingTag()832 p.framesetOK = false833 case a.Image:834 p.tok.DataAtom = a.Img835 p.tok.Data = a.Img.String()836 return false837 case a.Isindex:838 if p.form != nil {839 // Ignore the token.840 return true841 }842 action := ""843 prompt := "This is a searchable index. Enter search keywords: "844 attr := []Attribute{{Key: "name", Val: "isindex"}}845 for _, t := range p.tok.Attr {846 switch t.Key {847 case "action":848 action = t.Val849 case "name":850 // Ignore the attribute.851 case "prompt":852 prompt = t.Val853 default:854 attr = append(attr, t)855 }856 }857 p.acknowledgeSelfClosingTag()858 p.popUntil(buttonScope, a.P)859 p.parseImpliedToken(StartTagToken, a.Form, a.Form.String())860 if action != "" {861 p.form.Attr = []Attribute{{Key: "action", Val: action}}862 }863 p.parseImpliedToken(StartTagToken, a.Hr, a.Hr.String())864 p.parseImpliedToken(StartTagToken, a.Label, a.Label.String())865 p.addText(prompt)866 p.addChild(&Node{867 Type: ElementNode,868 DataAtom: a.Input,869 Data: a.Input.String(),870 Attr: attr,871 })872 p.oe.pop()873 p.parseImpliedToken(EndTagToken, a.Label, a.Label.String())874 p.parseImpliedToken(StartTagToken, a.Hr, a.Hr.String())875 p.parseImpliedToken(EndTagToken, a.Form, a.Form.String())876 case a.Textarea:877 p.addElement()878 p.setOriginalIM()879 p.framesetOK = false880 p.im = textIM881 case a.Xmp:882 p.popUntil(buttonScope, a.P)883 p.reconstructActiveFormattingElements()884 p.framesetOK = false885 p.addElement()886 p.setOriginalIM()887 p.im = textIM888 case a.Iframe:889 p.framesetOK = false890 p.addElement()891 p.setOriginalIM()892 p.im = textIM893 case a.Noembed, a.Noscript:894 p.addElement()895 p.setOriginalIM()896 p.im = textIM897 case a.Select:898 p.reconstructActiveFormattingElements()899 p.addElement()900 p.framesetOK = false901 p.im = inSelectIM902 return true903 case a.Optgroup, a.Option:904 if p.top().DataAtom == a.Option {905 p.oe.pop()906 }907 p.reconstructActiveFormattingElements()908 p.addElement()909 case a.Rp, a.Rt:910 if p.elementInScope(defaultScope, a.Ruby) {911 p.generateImpliedEndTags()912 }913 p.addElement()914 case a.Math, a.Svg:915 p.reconstructActiveFormattingElements()916 if p.tok.DataAtom == a.Math {917 adjustAttributeNames(p.tok.Attr, mathMLAttributeAdjustments)918 } else {919 adjustAttributeNames(p.tok.Attr, svgAttributeAdjustments)920 }921 adjustForeignAttributes(p.tok.Attr)922 p.addElement()923 p.top().Namespace = p.tok.Data924 if p.hasSelfClosingToken {925 p.oe.pop()926 p.acknowledgeSelfClosingTag()927 }928 return true929 case a.Caption, a.Col, a.Colgroup, a.Frame, a.Head, a.Tbody, a.Td, a.Tfoot, a.Th, a.Thead, a.Tr:930 // Ignore the token.931 default:932 p.reconstructActiveFormattingElements()933 p.addElement()934 }935 case EndTagToken:936 switch p.tok.DataAtom {937 case a.Body:938 if p.elementInScope(defaultScope, a.Body) {939 p.im = afterBodyIM940 }941 case a.Html:942 if p.elementInScope(defaultScope, a.Body) {943 p.parseImpliedToken(EndTagToken, a.Body, a.Body.String())944 return false945 }946 return true947 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:948 p.popUntil(defaultScope, p.tok.DataAtom)949 case a.Form:950 node := p.form951 p.form = nil952 i := p.indexOfElementInScope(defaultScope, a.Form)953 if node == nil || i == -1 || p.oe[i] != node {954 // Ignore the token.955 return true956 }957 p.generateImpliedEndTags()958 p.oe.remove(node)959 case a.P:960 if !p.elementInScope(buttonScope, a.P) {961 p.parseImpliedToken(StartTagToken, a.P, a.P.String())962 }963 p.popUntil(buttonScope, a.P)964 case a.Li:965 p.popUntil(listItemScope, a.Li)966 case a.Dd, a.Dt:967 p.popUntil(defaultScope, p.tok.DataAtom)968 case a.H1, a.H2, a.H3, a.H4, a.H5, a.H6:969 p.popUntil(defaultScope, a.H1, a.H2, a.H3, a.H4, a.H5, a.H6)970 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:971 p.inBodyEndTagFormatting(p.tok.DataAtom)972 case a.Applet, a.Marquee, a.Object:973 if p.popUntil(defaultScope, p.tok.DataAtom) {974 p.clearActiveFormattingElements()975 }976 case a.Br:977 p.tok.Type = StartTagToken978 return false979 default:980 p.inBodyEndTagOther(p.tok.DataAtom)981 }982 case CommentToken:983 p.addChild(&Node{984 Type: CommentNode,985 Data: p.tok.Data,986 })987 }988 return true989}990func (p *parser) inBodyEndTagFormatting(tagAtom a.Atom) {991 // This is the "adoption agency" algorithm, described at992 // https://html.spec.whatwg.org/multipage/syntax.html#adoptionAgency993 // TODO: this is a fairly literal line-by-line translation of that algorithm.994 // Once the code successfully parses the comprehensive test suite, we should995 // refactor this code to be more idiomatic.996 // Steps 1-4. The outer loop.997 for i := 0; i < 8; i++ {998 // Step 5. Find the formatting element.999 var formattingElement *Node1000 for j := len(p.afe) - 1; j >= 0; j-- {1001 if p.afe[j].Type == scopeMarkerNode {1002 break1003 }1004 if p.afe[j].DataAtom == tagAtom {1005 formattingElement = p.afe[j]1006 break1007 }1008 }1009 if formattingElement == nil {1010 p.inBodyEndTagOther(tagAtom)1011 return1012 }1013 feIndex := p.oe.index(formattingElement)1014 if feIndex == -1 {1015 p.afe.remove(formattingElement)1016 return1017 }1018 if !p.elementInScope(defaultScope, tagAtom) {1019 // Ignore the tag.1020 return1021 }1022 // Steps 9-10. Find the furthest block.1023 var furthestBlock *Node1024 for _, e := range p.oe[feIndex:] {1025 if isSpecialElement(e) {1026 furthestBlock = e1027 break1028 }1029 }1030 if furthestBlock == nil {1031 e := p.oe.pop()1032 for e != formattingElement {1033 e = p.oe.pop()1034 }1035 p.afe.remove(e)1036 return1037 }1038 // Steps 11-12. Find the common ancestor and bookmark node.1039 commonAncestor := p.oe[feIndex-1]1040 bookmark := p.afe.index(formattingElement)1041 // Step 13. The inner loop. Find the lastNode to reparent.1042 lastNode := furthestBlock1043 node := furthestBlock1044 x := p.oe.index(node)1045 // Steps 13.1-13.21046 for j := 0; j < 3; j++ {1047 // Step 13.3.1048 x--1049 node = p.oe[x]1050 // Step 13.4 - 13.5.1051 if p.afe.index(node) == -1 {1052 p.oe.remove(node)1053 continue1054 }1055 // Step 13.6.1056 if node == formattingElement {1057 break1058 }1059 // Step 13.7.1060 clone := node.clone()1061 p.afe[p.afe.index(node)] = clone1062 p.oe[p.oe.index(node)] = clone1063 node = clone1064 // Step 13.8.1065 if lastNode == furthestBlock {1066 bookmark = p.afe.index(node) + 11067 }1068 // Step 13.9.1069 if lastNode.Parent != nil {1070 lastNode.Parent.RemoveChild(lastNode)1071 }1072 node.AppendChild(lastNode)1073 // Step 13.10.1074 lastNode = node1075 }1076 // Step 14. Reparent lastNode to the common ancestor,1077 // or for misnested table nodes, to the foster parent.1078 if lastNode.Parent != nil {1079 lastNode.Parent.RemoveChild(lastNode)1080 }1081 switch commonAncestor.DataAtom {1082 case a.Table, a.Tbody, a.Tfoot, a.Thead, a.Tr:1083 p.fosterParent(lastNode)1084 default:1085 commonAncestor.AppendChild(lastNode)1086 }1087 // Steps 15-17. Reparent nodes from the furthest block's children1088 // to a clone of the formatting element.1089 clone := formattingElement.clone()1090 reparentChildren(clone, furthestBlock)1091 furthestBlock.AppendChild(clone)1092 // Step 18. Fix up the list of active formatting elements.1093 if oldLoc := p.afe.index(formattingElement); oldLoc != -1 && oldLoc < bookmark {1094 // Move the bookmark with the rest of the list.1095 bookmark--1096 }1097 p.afe.remove(formattingElement)1098 p.afe.insert(bookmark, clone)1099 // Step 19. Fix up the stack of open elements.1100 p.oe.remove(formattingElement)1101 p.oe.insert(p.oe.index(furthestBlock)+1, clone)1102 }1103}1104// inBodyEndTagOther performs the "any other end tag" algorithm for inBodyIM.1105// "Any other end tag" handling from 12.2.5.5 The rules for parsing tokens in foreign content1106// https://html.spec.whatwg.org/multipage/syntax.html#parsing-main-inforeign1107func (p *parser) inBodyEndTagOther(tagAtom a.Atom) {1108 for i := len(p.oe) - 1; i >= 0; i-- {1109 if p.oe[i].DataAtom == tagAtom {1110 p.oe = p.oe[:i]1111 break1112 }1113 if isSpecialElement(p.oe[i]) {1114 break1115 }1116 }1117}1118// Section 12.2.5.4.8.1119func textIM(p *parser) bool {1120 switch p.tok.Type {1121 case ErrorToken:1122 p.oe.pop()1123 case TextToken:1124 d := p.tok.Data1125 if n := p.oe.top(); n.DataAtom == a.Textarea && n.FirstChild == nil {1126 // Ignore a newline at the start of a <textarea> block.1127 if d != "" && d[0] == '\r' {1128 d = d[1:]1129 }1130 if d != "" && d[0] == '\n' {1131 d = d[1:]1132 }1133 }1134 if d == "" {1135 return true1136 }1137 p.addText(d)1138 return true1139 case EndTagToken:1140 p.oe.pop()1141 }1142 p.im = p.originalIM1143 p.originalIM = nil1144 return p.tok.Type == EndTagToken1145}1146// Section 12.2.5.4.9.1147func inTableIM(p *parser) bool {1148 switch p.tok.Type {1149 case ErrorToken:1150 // Stop parsing.1151 return true1152 case TextToken:1153 p.tok.Data = strings.Replace(p.tok.Data, "\x00", "", -1)1154 switch p.oe.top().DataAtom {1155 case a.Table, a.Tbody, a.Tfoot, a.Thead, a.Tr:1156 if strings.Trim(p.tok.Data, whitespace) == "" {1157 p.addText(p.tok.Data)1158 return true1159 }1160 }1161 case StartTagToken:1162 switch p.tok.DataAtom {1163 case a.Caption:1164 p.clearStackToContext(tableScope)1165 p.afe = append(p.afe, &scopeMarker)1166 p.addElement()1167 p.im = inCaptionIM1168 return true1169 case a.Colgroup:1170 p.clearStackToContext(tableScope)1171 p.addElement()1172 p.im = inColumnGroupIM1173 return true1174 case a.Col:1175 p.parseImpliedToken(StartTagToken, a.Colgroup, a.Colgroup.String())1176 return false1177 case a.Tbody, a.Tfoot, a.Thead:1178 p.clearStackToContext(tableScope)1179 p.addElement()1180 p.im = inTableBodyIM1181 return true1182 case a.Td, a.Th, a.Tr:1183 p.parseImpliedToken(StartTagToken, a.Tbody, a.Tbody.String())1184 return false1185 case a.Table:1186 if p.popUntil(tableScope, a.Table) {1187 p.resetInsertionMode()1188 return false1189 }1190 // Ignore the token.1191 return true1192 case a.Style, a.Script:1193 return inHeadIM(p)1194 case a.Input:1195 for _, t := range p.tok.Attr {1196 if t.Key == "type" && strings.ToLower(t.Val) == "hidden" {1197 p.addElement()1198 p.oe.pop()1199 return true1200 }1201 }1202 // Otherwise drop down to the default action.1203 case a.Form:1204 if p.form != nil {1205 // Ignore the token.1206 return true1207 }1208 p.addElement()1209 p.form = p.oe.pop()1210 case a.Select:1211 p.reconstructActiveFormattingElements()1212 switch p.top().DataAtom {1213 case a.Table, a.Tbody, a.Tfoot, a.Thead, a.Tr:1214 p.fosterParenting = true1215 }1216 p.addElement()1217 p.fosterParenting = false1218 p.framesetOK = false1219 p.im = inSelectInTableIM1220 return true1221 }1222 case EndTagToken:1223 switch p.tok.DataAtom {1224 case a.Table:1225 if p.popUntil(tableScope, a.Table) {1226 p.resetInsertionMode()1227 return true1228 }1229 // Ignore the token.1230 return true1231 case a.Body, a.Caption, a.Col, a.Colgroup, a.Html, a.Tbody, a.Td, a.Tfoot, a.Th, a.Thead, a.Tr:1232 // Ignore the token.1233 return true1234 }1235 case CommentToken:1236 p.addChild(&Node{1237 Type: CommentNode,1238 Data: p.tok.Data,1239 })1240 return true1241 case DoctypeToken:1242 // Ignore the token.1243 return true1244 }1245 p.fosterParenting = true1246 defer func() { p.fosterParenting = false }()1247 return inBodyIM(p)1248}1249// Section 12.2.5.4.11.1250func inCaptionIM(p *parser) bool {1251 switch p.tok.Type {1252 case StartTagToken:1253 switch p.tok.DataAtom {1254 case a.Caption, a.Col, a.Colgroup, a.Tbody, a.Td, a.Tfoot, a.Thead, a.Tr:1255 if p.popUntil(tableScope, a.Caption) {1256 p.clearActiveFormattingElements()1257 p.im = inTableIM1258 return false1259 } else {1260 // Ignore the token.1261 return true1262 }1263 case a.Select:1264 p.reconstructActiveFormattingElements()1265 p.addElement()1266 p.framesetOK = false1267 p.im = inSelectInTableIM1268 return true1269 }1270 case EndTagToken:1271 switch p.tok.DataAtom {1272 case a.Caption:1273 if p.popUntil(tableScope, a.Caption) {1274 p.clearActiveFormattingElements()1275 p.im = inTableIM1276 }1277 return true1278 case a.Table:1279 if p.popUntil(tableScope, a.Caption) {1280 p.clearActiveFormattingElements()1281 p.im = inTableIM1282 return false1283 } else {1284 // Ignore the token.1285 return true1286 }1287 case a.Body, a.Col, a.Colgroup, a.Html, a.Tbody, a.Td, a.Tfoot, a.Th, a.Thead, a.Tr:1288 // Ignore the token.1289 return true1290 }1291 }1292 return inBodyIM(p)1293}1294// Section 12.2.5.4.12.1295func inColumnGroupIM(p *parser) bool {1296 switch p.tok.Type {1297 case TextToken:1298 s := strings.TrimLeft(p.tok.Data, whitespace)1299 if len(s) < len(p.tok.Data) {1300 // Add the initial whitespace to the current node.1301 p.addText(p.tok.Data[:len(p.tok.Data)-len(s)])1302 if s == "" {1303 return true1304 }1305 p.tok.Data = s1306 }1307 case CommentToken:1308 p.addChild(&Node{1309 Type: CommentNode,1310 Data: p.tok.Data,1311 })1312 return true1313 case DoctypeToken:1314 // Ignore the token.1315 return true1316 case StartTagToken:1317 switch p.tok.DataAtom {1318 case a.Html:1319 return inBodyIM(p)1320 case a.Col:1321 p.addElement()1322 p.oe.pop()1323 p.acknowledgeSelfClosingTag()1324 return true1325 }1326 case EndTagToken:1327 switch p.tok.DataAtom {1328 case a.Colgroup:1329 if p.oe.top().DataAtom != a.Html {1330 p.oe.pop()1331 p.im = inTableIM1332 }1333 return true1334 case a.Col:1335 // Ignore the token.1336 return true1337 }1338 }1339 if p.oe.top().DataAtom != a.Html {1340 p.oe.pop()1341 p.im = inTableIM1342 return false1343 }1344 return true1345}1346// Section 12.2.5.4.13.1347func inTableBodyIM(p *parser) bool {1348 switch p.tok.Type {1349 case StartTagToken:1350 switch p.tok.DataAtom {1351 case a.Tr:1352 p.clearStackToContext(tableBodyScope)1353 p.addElement()1354 p.im = inRowIM1355 return true1356 case a.Td, a.Th:1357 p.parseImpliedToken(StartTagToken, a.Tr, a.Tr.String())1358 return false1359 case a.Caption, a.Col, a.Colgroup, a.Tbody, a.Tfoot, a.Thead:1360 if p.popUntil(tableScope, a.Tbody, a.Thead, a.Tfoot) {1361 p.im = inTableIM1362 return false1363 }1364 // Ignore the token.1365 return true1366 }1367 case EndTagToken:1368 switch p.tok.DataAtom {1369 case a.Tbody, a.Tfoot, a.Thead:1370 if p.elementInScope(tableScope, p.tok.DataAtom) {1371 p.clearStackToContext(tableBodyScope)1372 p.oe.pop()1373 p.im = inTableIM1374 }1375 return true1376 case a.Table:1377 if p.popUntil(tableScope, a.Tbody, a.Thead, a.Tfoot) {1378 p.im = inTableIM1379 return false1380 }1381 // Ignore the token.1382 return true1383 case a.Body, a.Caption, a.Col, a.Colgroup, a.Html, a.Td, a.Th, a.Tr:1384 // Ignore the token.1385 return true1386 }1387 case CommentToken:1388 p.addChild(&Node{1389 Type: CommentNode,1390 Data: p.tok.Data,1391 })1392 return true1393 }1394 return inTableIM(p)1395}1396// Section 12.2.5.4.14.1397func inRowIM(p *parser) bool {1398 switch p.tok.Type {1399 case StartTagToken:1400 switch p.tok.DataAtom {1401 case a.Td, a.Th:1402 p.clearStackToContext(tableRowScope)1403 p.addElement()1404 p.afe = append(p.afe, &scopeMarker)1405 p.im = inCellIM1406 return true1407 case a.Caption, a.Col, a.Colgroup, a.Tbody, a.Tfoot, a.Thead, a.Tr:1408 if p.popUntil(tableScope, a.Tr) {1409 p.im = inTableBodyIM1410 return false1411 }1412 // Ignore the token.1413 return true1414 }1415 case EndTagToken:1416 switch p.tok.DataAtom {1417 case a.Tr:1418 if p.popUntil(tableScope, a.Tr) {1419 p.im = inTableBodyIM1420 return true1421 }1422 // Ignore the token.1423 return true1424 case a.Table:1425 if p.popUntil(tableScope, a.Tr) {1426 p.im = inTableBodyIM1427 return false1428 }1429 // Ignore the token.1430 return true1431 case a.Tbody, a.Tfoot, a.Thead:1432 if p.elementInScope(tableScope, p.tok.DataAtom) {1433 p.parseImpliedToken(EndTagToken, a.Tr, a.Tr.String())1434 return false1435 }1436 // Ignore the token.1437 return true1438 case a.Body, a.Caption, a.Col, a.Colgroup, a.Html, a.Td, a.Th:1439 // Ignore the token.1440 return true1441 }1442 }1443 return inTableIM(p)1444}1445// Section 12.2.5.4.15.1446func inCellIM(p *parser) bool {1447 switch p.tok.Type {1448 case StartTagToken:1449 switch p.tok.DataAtom {1450 case a.Caption, a.Col, a.Colgroup, a.Tbody, a.Td, a.Tfoot, a.Th, a.Thead, a.Tr:1451 if p.popUntil(tableScope, a.Td, a.Th) {1452 // Close the cell and reprocess.1453 p.clearActiveFormattingElements()1454 p.im = inRowIM1455 return false1456 }1457 // Ignore the token.1458 return true1459 case a.Select:1460 p.reconstructActiveFormattingElements()1461 p.addElement()1462 p.framesetOK = false1463 p.im = inSelectInTableIM1464 return true1465 }1466 case EndTagToken:1467 switch p.tok.DataAtom {1468 case a.Td, a.Th:1469 if !p.popUntil(tableScope, p.tok.DataAtom) {1470 // Ignore the token.1471 return true1472 }1473 p.clearActiveFormattingElements()1474 p.im = inRowIM1475 return true1476 case a.Body, a.Caption, a.Col, a.Colgroup, a.Html:1477 // Ignore the token.1478 return true1479 case a.Table, a.Tbody, a.Tfoot, a.Thead, a.Tr:1480 if !p.elementInScope(tableScope, p.tok.DataAtom) {1481 // Ignore the token.1482 return true1483 }1484 // Close the cell and reprocess.1485 p.popUntil(tableScope, a.Td, a.Th)1486 p.clearActiveFormattingElements()1487 p.im = inRowIM1488 return false1489 }1490 }1491 return inBodyIM(p)1492}1493// Section 12.2.5.4.16.1494func inSelectIM(p *parser) bool {1495 switch p.tok.Type {1496 case ErrorToken:1497 // Stop parsing.1498 return true1499 case TextToken:1500 p.addText(strings.Replace(p.tok.Data, "\x00", "", -1))1501 case StartTagToken:1502 switch p.tok.DataAtom {1503 case a.Html:1504 return inBodyIM(p)1505 case a.Option:1506 if p.top().DataAtom == a.Option {1507 p.oe.pop()1508 }1509 p.addElement()1510 case a.Optgroup:1511 if p.top().DataAtom == a.Option {1512 p.oe.pop()1513 }1514 if p.top().DataAtom == a.Optgroup {1515 p.oe.pop()1516 }1517 p.addElement()1518 case a.Select:1519 p.tok.Type = EndTagToken1520 return false1521 case a.Input, a.Keygen, a.Textarea:1522 if p.elementInScope(selectScope, a.Select) {1523 p.parseImpliedToken(EndTagToken, a.Select, a.Select.String())1524 return false1525 }1526 // In order to properly ignore <textarea>, we need to change the tokenizer mode.1527 p.tokenizer.NextIsNotRawText()1528 // Ignore the token.1529 return true1530 case a.Script:1531 return inHeadIM(p)1532 }1533 case EndTagToken:1534 switch p.tok.DataAtom {1535 case a.Option:1536 if p.top().DataAtom == a.Option {1537 p.oe.pop()1538 }1539 case a.Optgroup:1540 i := len(p.oe) - 11541 if p.oe[i].DataAtom == a.Option {1542 i--1543 }1544 if p.oe[i].DataAtom == a.Optgroup {1545 p.oe = p.oe[:i]1546 }1547 case a.Select:1548 if p.popUntil(selectScope, a.Select) {1549 p.resetInsertionMode()1550 }1551 }1552 case CommentToken:1553 p.addChild(&Node{1554 Type: CommentNode,1555 Data: p.tok.Data,1556 })1557 case DoctypeToken:1558 // Ignore the token.1559 return true1560 }1561 return true1562}1563// Section 12.2.5.4.17.1564func inSelectInTableIM(p *parser) bool {1565 switch p.tok.Type {1566 case StartTagToken, EndTagToken:1567 switch p.tok.DataAtom {1568 case a.Caption, a.Table, a.Tbody, a.Tfoot, a.Thead, a.Tr, a.Td, a.Th:1569 if p.tok.Type == StartTagToken || p.elementInScope(tableScope, p.tok.DataAtom) {1570 p.parseImpliedToken(EndTagToken, a.Select, a.Select.String())1571 return false1572 } else {1573 // Ignore the token.1574 return true1575 }1576 }1577 }1578 return inSelectIM(p)1579}1580// Section 12.2.5.4.18.1581func afterBodyIM(p *parser) bool {1582 switch p.tok.Type {1583 case ErrorToken:1584 // Stop parsing.1585 return true1586 case TextToken:1587 s := strings.TrimLeft(p.tok.Data, whitespace)1588 if len(s) == 0 {1589 // It was all whitespace.1590 return inBodyIM(p)1591 }1592 case StartTagToken:1593 if p.tok.DataAtom == a.Html {1594 return inBodyIM(p)1595 }1596 case EndTagToken:1597 if p.tok.DataAtom == a.Html {1598 if !p.fragment {1599 p.im = afterAfterBodyIM1600 }1601 return true1602 }1603 case CommentToken:1604 // The comment is attached to the <html> element.1605 if len(p.oe) < 1 || p.oe[0].DataAtom != a.Html {1606 panic("html: bad parser state: <html> element not found, in the after-body insertion mode")1607 }1608 p.oe[0].AppendChild(&Node{1609 Type: CommentNode,1610 Data: p.tok.Data,1611 })1612 return true1613 }1614 p.im = inBodyIM1615 return false1616}1617// Section 12.2.5.4.19.1618func inFramesetIM(p *parser) bool {1619 switch p.tok.Type {1620 case CommentToken:1621 p.addChild(&Node{1622 Type: CommentNode,1623 Data: p.tok.Data,1624 })1625 case TextToken:1626 // Ignore all text but whitespace.1627 s := strings.Map(func(c rune) rune {1628 switch c {1629 case ' ', '\t', '\n', '\f', '\r':1630 return c1631 }1632 return -11633 }, p.tok.Data)1634 if s != "" {1635 p.addText(s)1636 }1637 case StartTagToken:1638 switch p.tok.DataAtom {1639 case a.Html:1640 return inBodyIM(p)1641 case a.Frameset:1642 p.addElement()1643 case a.Frame:1644 p.addElement()1645 p.oe.pop()1646 p.acknowledgeSelfClosingTag()1647 case a.Noframes:1648 return inHeadIM(p)1649 }1650 case EndTagToken:1651 switch p.tok.DataAtom {1652 case a.Frameset:1653 if p.oe.top().DataAtom != a.Html {1654 p.oe.pop()1655 if p.oe.top().DataAtom != a.Frameset {1656 p.im = afterFramesetIM1657 return true1658 }1659 }1660 }1661 default:1662 // Ignore the token.1663 }1664 return true1665}1666// Section 12.2.5.4.20.1667func afterFramesetIM(p *parser) bool {1668 switch p.tok.Type {1669 case CommentToken:1670 p.addChild(&Node{1671 Type: CommentNode,1672 Data: p.tok.Data,1673 })1674 case TextToken:1675 // Ignore all text but whitespace.1676 s := strings.Map(func(c rune) rune {1677 switch c {1678 case ' ', '\t', '\n', '\f', '\r':1679 return c1680 }1681 return -11682 }, p.tok.Data)1683 if s != "" {1684 p.addText(s)1685 }1686 case StartTagToken:1687 switch p.tok.DataAtom {1688 case a.Html:1689 return inBodyIM(p)1690 case a.Noframes:1691 return inHeadIM(p)1692 }1693 case EndTagToken:1694 switch p.tok.DataAtom {1695 case a.Html:1696 p.im = afterAfterFramesetIM1697 return true1698 }1699 default:1700 // Ignore the token.1701 }1702 return true1703}1704// Section 12.2.5.4.21.1705func afterAfterBodyIM(p *parser) bool {1706 switch p.tok.Type {1707 case ErrorToken:1708 // Stop parsing.1709 return true1710 case TextToken:1711 s := strings.TrimLeft(p.tok.Data, whitespace)1712 if len(s) == 0 {1713 // It was all whitespace.1714 return inBodyIM(p)1715 }1716 case StartTagToken:1717 if p.tok.DataAtom == a.Html {1718 return inBodyIM(p)1719 }1720 case CommentToken:1721 p.doc.AppendChild(&Node{1722 Type: CommentNode,1723 Data: p.tok.Data,1724 })1725 return true1726 case DoctypeToken:1727 return inBodyIM(p)1728 }1729 p.im = inBodyIM1730 return false1731}1732// Section 12.2.5.4.22.1733func afterAfterFramesetIM(p *parser) bool {1734 switch p.tok.Type {1735 case CommentToken:1736 p.doc.AppendChild(&Node{1737 Type: CommentNode,1738 Data: p.tok.Data,1739 })1740 case TextToken:1741 // Ignore all text but whitespace.1742 s := strings.Map(func(c rune) rune {1743 switch c {1744 case ' ', '\t', '\n', '\f', '\r':1745 return c1746 }1747 return -11748 }, p.tok.Data)1749 if s != "" {1750 p.tok.Data = s1751 return inBodyIM(p)1752 }1753 case StartTagToken:1754 switch p.tok.DataAtom {1755 case a.Html:1756 return inBodyIM(p)1757 case a.Noframes:1758 return inHeadIM(p)1759 }1760 case DoctypeToken:1761 return inBodyIM(p)1762 default:1763 // Ignore the token.1764 }1765 return true1766}1767const whitespaceOrNUL = whitespace + "\x00"1768// Section 12.2.5.5.1769func parseForeignContent(p *parser) bool {1770 switch p.tok.Type {1771 case TextToken:1772 if p.framesetOK {1773 p.framesetOK = strings.TrimLeft(p.tok.Data, whitespaceOrNUL) == ""1774 }1775 p.tok.Data = strings.Replace(p.tok.Data, "\x00", "\ufffd", -1)1776 p.addText(p.tok.Data)1777 case CommentToken:1778 p.addChild(&Node{1779 Type: CommentNode,1780 Data: p.tok.Data,1781 })1782 case StartTagToken:1783 b := breakout[p.tok.Data]1784 if p.tok.DataAtom == a.Font {1785 loop:1786 for _, attr := range p.tok.Attr {1787 switch attr.Key {1788 case "color", "face", "size":1789 b = true1790 break loop1791 }1792 }1793 }1794 if b {1795 for i := len(p.oe) - 1; i >= 0; i-- {1796 n := p.oe[i]1797 if n.Namespace == "" || htmlIntegrationPoint(n) || mathMLTextIntegrationPoint(n) {1798 p.oe = p.oe[:i+1]1799 break1800 }1801 }1802 return false1803 }1804 switch p.top().Namespace {1805 case "math":1806 adjustAttributeNames(p.tok.Attr, mathMLAttributeAdjustments)1807 case "svg":1808 // Adjust SVG tag names. The tokenizer lower-cases tag names, but1809 // SVG wants e.g. "foreignObject" with a capital second "O".1810 if x := svgTagNameAdjustments[p.tok.Data]; x != "" {1811 p.tok.DataAtom = a.Lookup([]byte(x))1812 p.tok.Data = x1813 }1814 adjustAttributeNames(p.tok.Attr, svgAttributeAdjustments)1815 default:1816 panic("html: bad parser state: unexpected namespace")1817 }1818 adjustForeignAttributes(p.tok.Attr)1819 namespace := p.top().Namespace1820 p.addElement()1821 p.top().Namespace = namespace1822 if namespace != "" {1823 // Don't let the tokenizer go into raw text mode in foreign content1824 // (e.g. in an SVG <title> tag).1825 p.tokenizer.NextIsNotRawText()1826 }1827 if p.hasSelfClosingToken {1828 p.oe.pop()1829 p.acknowledgeSelfClosingTag()1830 }1831 case EndTagToken:1832 for i := len(p.oe) - 1; i >= 0; i-- {1833 if p.oe[i].Namespace == "" {1834 return p.im(p)1835 }1836 if strings.EqualFold(p.oe[i].Data, p.tok.Data) {1837 p.oe = p.oe[:i]1838 break1839 }1840 }1841 return true1842 default:1843 // Ignore the token.1844 }1845 return true1846}1847// Section 12.2.5.1848func (p *parser) inForeignContent() bool {1849 if len(p.oe) == 0 {1850 return false1851 }1852 n := p.oe[len(p.oe)-1]1853 if n.Namespace == "" {1854 return false1855 }1856 if mathMLTextIntegrationPoint(n) {1857 if p.tok.Type == StartTagToken && p.tok.DataAtom != a.Mglyph && p.tok.DataAtom != a.Malignmark {1858 return false1859 }1860 if p.tok.Type == TextToken {1861 return false1862 }1863 }1864 if n.Namespace == "math" && n.DataAtom == a.AnnotationXml && p.tok.Type == StartTagToken && p.tok.DataAtom == a.Svg {1865 return false1866 }1867 if htmlIntegrationPoint(n) && (p.tok.Type == StartTagToken || p.tok.Type == TextToken) {1868 return false1869 }1870 if p.tok.Type == ErrorToken {1871 return false1872 }1873 return true1874}1875// parseImpliedToken parses a token as though it had appeared in the parser's1876// input.1877func (p *parser) parseImpliedToken(t TokenType, dataAtom a.Atom, data string) {1878 realToken, selfClosing := p.tok, p.hasSelfClosingToken1879 p.tok = Token{1880 Type: t,1881 DataAtom: dataAtom,1882 Data: data,1883 }1884 p.hasSelfClosingToken = false1885 p.parseCurrentToken()1886 p.tok, p.hasSelfClosingToken = realToken, selfClosing1887}1888// parseCurrentToken runs the current token through the parsing routines1889// until it is consumed.1890func (p *parser) parseCurrentToken() {1891 if p.tok.Type == SelfClosingTagToken {1892 p.hasSelfClosingToken = true1893 p.tok.Type = StartTagToken1894 }1895 consumed := false1896 for !consumed {1897 if p.inForeignContent() {1898 consumed = parseForeignContent(p)1899 } else {1900 consumed = p.im(p)1901 }1902 }1903 if p.hasSelfClosingToken {1904 // This is a parse error, but ignore it.1905 p.hasSelfClosingToken = false1906 }1907}1908func (p *parser) parse() error {1909 // Iterate until EOF. Any other error will cause an early return.1910 var err error1911 for err != io.EOF {1912 // CDATA sections are allowed only in foreign content.1913 n := p.oe.top()1914 p.tokenizer.AllowCDATA(n != nil && n.Namespace != "")1915 // Read and parse the next token.1916 p.tokenizer.Next()1917 p.tok = p.tokenizer.Token()1918 if p.tok.Type == ErrorToken {1919 err = p.tokenizer.Err()1920 if err != nil && err != io.EOF {1921 return err1922 }1923 }1924 p.parseCurrentToken()1925 }1926 return nil1927}1928// Parse returns the parse tree for the HTML from the given Reader.1929// The input is assumed to be UTF-8 encoded.1930func Parse(r io.Reader) (*Node, error) {1931 p := &parser{1932 tokenizer: NewTokenizer(r),1933 doc: &Node{1934 Type: DocumentNode,1935 },1936 scripting: true,1937 framesetOK: true,1938 im: initialIM,1939 }1940 err := p.parse()1941 if err != nil {1942 return nil, err1943 }1944 return p.doc, nil1945}1946// ParseFragment parses a fragment of HTML and returns the nodes that were1947// found. If the fragment is the InnerHTML for an existing element, pass that1948// element in context.1949func ParseFragment(r io.Reader, context *Node) ([]*Node, error) {1950 contextTag := ""1951 if context != nil {1952 if context.Type != ElementNode {1953 return nil, errors.New("html: ParseFragment of non-element Node")1954 }1955 // The next check isn't just context.DataAtom.String() == context.Data because1956 // it is valid to pass an element whose tag isn't a known atom. For example,1957 // DataAtom == 0 and Data = "tagfromthefuture" is perfectly consistent.1958 if context.DataAtom != a.Lookup([]byte(context.Data)) {1959 return nil, fmt.Errorf("html: inconsistent Node: DataAtom=%q, Data=%q", context.DataAtom, context.Data)1960 }1961 contextTag = context.DataAtom.String()1962 }1963 p := &parser{1964 tokenizer: NewTokenizerFragment(r, contextTag),1965 doc: &Node{1966 Type: DocumentNode,1967 },1968 scripting: true,1969 fragment: true,1970 context: context,1971 }1972 root := &Node{1973 Type: ElementNode,1974 DataAtom: a.Html,1975 Data: a.Html.String(),1976 }1977 p.doc.AppendChild(root)1978 p.oe = nodeStack{root}1979 p.resetInsertionMode()1980 for n := context; n != nil; n = n.Parent {1981 if n.Type == ElementNode && n.DataAtom == a.Form {1982 p.form = n1983 break1984 }1985 }1986 err := p.parse()1987 if err != nil {1988 return nil, err1989 }1990 parent := p.doc1991 if context != nil {1992 parent = root1993 }1994 var result []*Node1995 for c := parent.FirstChild; c != nil; {1996 next := c.NextSibling1997 parent.RemoveChild(c)1998 result = append(result, c)1999 c = next2000 }2001 return result, nil2002}...
html.go
Source:html.go
...196//THEAD element197func (out *OutPrint) THEAD(a ...interface{}) {198 tHead(out, a...)199}200func startTFoot(out *OutPrint, props ...string) {201 StartElem(out, "tfoot", props...)202}203//StartTFOOT element204func (out *OutPrint) StartTFOOT(props ...string) {205 startTFoot(out, props...)206}207func endTFoot(out *OutPrint) {208 EndElem(out, "tfoot")209}210//EndTFOOT element211func (out *OutPrint) EndTFOOT() {212 endTFoot(out)213}214func tFoot(out *OutPrint, a ...interface{}) {215 Elem(out, "tfoot", a...)216}217//TFOOT element218func (out *OutPrint) TFOOT(a ...interface{}) {219 tFoot(out, a...)220}221func startTh(out *OutPrint, props ...string) {222 StartElem(out, "th", props...)223}224//StartTH element225func (out *OutPrint) StartTH(props ...string) {226 startTh(out, props...)...
TFoot
Using AI Code Generation
1import (2func main() {3 fmt.Println(html.TFoot)4}5import (6func main() {7 fmt.Println(html.THead)8}9import (10func main() {11 fmt.Println(html.THtml)12}13import (14func main() {15 fmt.Println(html.TBody)16}17import (18func main() {19 fmt.Println(html.Td)20}21import (22func main() {23 fmt.Println(html.Title)24}25import (26func main() {27 fmt.Println(html.Tr)28}29import (30func main() {31 fmt.Println(html.TdAttr)32}33import (34func main() {35 fmt.Println(html.Textarea)36}37import (38func main() {39 fmt.Println(html.TfootAttr)40}41import (42func main() {43 fmt.Println(html.TheadAttr)44}45import (46func main() {47 fmt.Println(html.TitleAttr)48}
TFoot
Using AI Code Generation
1import (2func main() {3 f, err := os.Create("TFoot.html")4 if err != nil {5 fmt.Println(err)6 }7 defer f.Close()8 io.WriteString(f, `<html>9}
TFoot
Using AI Code Generation
1import (2func main() {3 http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {4 fmt.Fprint(w, `<table>`)5 fmt.Fprint(w, `<tr><td>1</td><td>2</td></tr>`)6 fmt.Fprint(w, `<tr><td>3</td><td>4</td></tr>`)7 fmt.Fprint(w, `</table>`)8 })9 http.ListenAndServe(":8080", nil)10}
TFoot
Using AI Code Generation
1import (2func main() {3 if err != nil {4 panic(err)5 }6 defer res.Body.Close()7 if res.StatusCode != 200 {8 panic(res.StatusCode)9 }10 doc, err := goquery.NewDocumentFromReader(res.Body)11 if err != nil {12 panic(err)13 }14 doc.Find("table").Each(func(index int, item *goquery.Selection) {15 item.Find("tfoot").Each(func(i int, s *goquery.Selection) {16 fmt.Println(s.Text())17 })18 })19}
TFoot
Using AI Code Generation
1import (2func main() {3 file, err := os.Open("index.html")4 if err != nil {5 log.Fatal(err)6 }7 doc, err := html.Parse(file)8 if err != nil {9 log.Fatal(err)10 }11 forEachNode(doc, startElement, endElement)12}13func forEachNode(n *html.Node, pre, post func(n *html.Node)) {14 if pre != nil {15 pre(n)16 }17 fmt.Println(n.Data)18 for c := n.FirstChild; c != nil; c = c.NextSibling {19 forEachNode(c, pre, post)20 }21 if post != nil {22 post(n)23 }24}25func startElement(n *html.Node) {26 if n.Type == html.ElementNode {27 fmt.Printf("<%s", n.Data)28 for _, a := range n.Attr {29 fmt.Printf(" %s=%q", a.Key, a.Val)30 }31 fmt.Println(">")32 }33}34func endElement(n *html.Node) {35 if n.Type == html.ElementNode {36 fmt.Printf("</%s>", n.Data)37 }38}
TFoot
Using AI Code Generation
1import (2func main() {3 http.HandleFunc("/", handler)4 http.ListenAndServe(":8080", nil)5}6func handler(w http.ResponseWriter, r *http.Request) {7 fmt.Fprint(w, htm)8}
Learn to execute automation testing from scratch with LambdaTest Learning Hub. Right from setting up the prerequisites to run your first automation test, to following best practices and diving deeper into advanced test scenarios. LambdaTest Learning Hubs compile a list of step-by-step guides to help you be proficient with different test automation frameworks i.e. Selenium, Cypress, TestNG etc.
You could also refer to video tutorials over LambdaTest YouTube channel to get step by step demonstration from industry experts.
Get 100 minutes of automation test minutes FREE!!