gitee.com/ks-custle/core-gm@v0.0.0-20230922171213-b83bdd97b62c/net/html/render.go (about) 1 // Copyright 2011 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package html 6 7 import ( 8 "bufio" 9 "errors" 10 "fmt" 11 "io" 12 "strings" 13 ) 14 15 type writer interface { 16 io.Writer 17 io.ByteWriter 18 WriteString(string) (int, error) 19 } 20 21 // Render renders the parse tree n to the given writer. 22 // 23 // Rendering is done on a 'best effort' basis: calling Parse on the output of 24 // Render will always result in something similar to the original tree, but it 25 // is not necessarily an exact clone unless the original tree was 'well-formed'. 26 // 'Well-formed' is not easily specified; the HTML5 specification is 27 // complicated. 28 // 29 // Calling Parse on arbitrary input typically results in a 'well-formed' parse 30 // tree. However, it is possible for Parse to yield a 'badly-formed' parse tree. 31 // For example, in a 'well-formed' parse tree, no <a> element is a child of 32 // another <a> element: parsing "<a><a>" results in two sibling elements. 33 // Similarly, in a 'well-formed' parse tree, no <a> element is a child of a 34 // <table> element: parsing "<p><table><a>" results in a <p> with two sibling 35 // children; the <a> is reparented to the <table>'s parent. However, calling 36 // Parse on "<a><table><a>" does not return an error, but the result has an <a> 37 // element with an <a> child, and is therefore not 'well-formed'. 38 // 39 // Programmatically constructed trees are typically also 'well-formed', but it 40 // is possible to construct a tree that looks innocuous but, when rendered and 41 // re-parsed, results in a different tree. A simple example is that a solitary 42 // text node would become a tree containing <html>, <head> and <body> elements. 43 // Another example is that the programmatic equivalent of "a<head>b</head>c" 44 // becomes "<html><head><head/><body>abc</body></html>". 45 func Render(w io.Writer, n *Node) error { 46 if x, ok := w.(writer); ok { 47 return render(x, n) 48 } 49 buf := bufio.NewWriter(w) 50 if err := render(buf, n); err != nil { 51 return err 52 } 53 return buf.Flush() 54 } 55 56 // plaintextAbort is returned from render1 when a <plaintext> element 57 // has been rendered. No more end tags should be rendered after that. 58 var plaintextAbort = errors.New("html: internal error (plaintext abort)") 59 60 func render(w writer, n *Node) error { 61 err := render1(w, n) 62 if err == plaintextAbort { 63 err = nil 64 } 65 return err 66 } 67 68 func render1(w writer, n *Node) error { 69 // Render non-element nodes; these are the easy cases. 70 switch n.Type { 71 case ErrorNode: 72 return errors.New("html: cannot render an ErrorNode node") 73 case TextNode: 74 return escape(w, n.Data) 75 case DocumentNode: 76 for c := n.FirstChild; c != nil; c = c.NextSibling { 77 if err := render1(w, c); err != nil { 78 return err 79 } 80 } 81 return nil 82 case ElementNode: 83 // No-op. 84 case CommentNode: 85 if _, err := w.WriteString("<!--"); err != nil { 86 return err 87 } 88 if _, err := w.WriteString(n.Data); err != nil { 89 return err 90 } 91 if _, err := w.WriteString("-->"); err != nil { 92 return err 93 } 94 return nil 95 case DoctypeNode: 96 if _, err := w.WriteString("<!DOCTYPE "); err != nil { 97 return err 98 } 99 if _, err := w.WriteString(n.Data); err != nil { 100 return err 101 } 102 if n.Attr != nil { 103 var p, s string 104 for _, a := range n.Attr { 105 switch a.Key { 106 case "public": 107 p = a.Val 108 case "system": 109 s = a.Val 110 } 111 } 112 if p != "" { 113 if _, err := w.WriteString(" PUBLIC "); err != nil { 114 return err 115 } 116 if err := writeQuoted(w, p); err != nil { 117 return err 118 } 119 if s != "" { 120 if err := w.WriteByte(' '); err != nil { 121 return err 122 } 123 if err := writeQuoted(w, s); err != nil { 124 return err 125 } 126 } 127 } else if s != "" { 128 if _, err := w.WriteString(" SYSTEM "); err != nil { 129 return err 130 } 131 if err := writeQuoted(w, s); err != nil { 132 return err 133 } 134 } 135 } 136 return w.WriteByte('>') 137 case RawNode: 138 _, err := w.WriteString(n.Data) 139 return err 140 default: 141 return errors.New("html: unknown node type") 142 } 143 144 // Render the <xxx> opening tag. 145 if err := w.WriteByte('<'); err != nil { 146 return err 147 } 148 if _, err := w.WriteString(n.Data); err != nil { 149 return err 150 } 151 for _, a := range n.Attr { 152 if err := w.WriteByte(' '); err != nil { 153 return err 154 } 155 if a.Namespace != "" { 156 if _, err := w.WriteString(a.Namespace); err != nil { 157 return err 158 } 159 if err := w.WriteByte(':'); err != nil { 160 return err 161 } 162 } 163 if _, err := w.WriteString(a.Key); err != nil { 164 return err 165 } 166 if _, err := w.WriteString(`="`); err != nil { 167 return err 168 } 169 if err := escape(w, a.Val); err != nil { 170 return err 171 } 172 if err := w.WriteByte('"'); err != nil { 173 return err 174 } 175 } 176 if voidElements[n.Data] { 177 if n.FirstChild != nil { 178 return fmt.Errorf("html: void element <%s> has child nodes", n.Data) 179 } 180 _, err := w.WriteString("/>") 181 return err 182 } 183 if err := w.WriteByte('>'); err != nil { 184 return err 185 } 186 187 // Add initial newline where there is danger of a newline beging ignored. 188 if c := n.FirstChild; c != nil && c.Type == TextNode && strings.HasPrefix(c.Data, "\n") { 189 switch n.Data { 190 case "pre", "listing", "textarea": 191 if err := w.WriteByte('\n'); err != nil { 192 return err 193 } 194 } 195 } 196 197 // Render any child nodes. 198 switch n.Data { 199 case "iframe", "noembed", "noframes", "noscript", "plaintext", "script", "style", "xmp": 200 for c := n.FirstChild; c != nil; c = c.NextSibling { 201 if c.Type == TextNode { 202 if _, err := w.WriteString(c.Data); err != nil { 203 return err 204 } 205 } else { 206 if err := render1(w, c); err != nil { 207 return err 208 } 209 } 210 } 211 if n.Data == "plaintext" { 212 // Don't render anything else. <plaintext> must be the 213 // last element in the file, with no closing tag. 214 return plaintextAbort 215 } 216 default: 217 for c := n.FirstChild; c != nil; c = c.NextSibling { 218 if err := render1(w, c); err != nil { 219 return err 220 } 221 } 222 } 223 224 // Render the </xxx> closing tag. 225 if _, err := w.WriteString("</"); err != nil { 226 return err 227 } 228 if _, err := w.WriteString(n.Data); err != nil { 229 return err 230 } 231 return w.WriteByte('>') 232 } 233 234 // writeQuoted writes s to w surrounded by quotes. Normally it will use double 235 // quotes, but if s contains a double quote, it will use single quotes. 236 // It is used for writing the identifiers in a doctype declaration. 237 // In valid HTML, they can't contain both types of quotes. 238 func writeQuoted(w writer, s string) error { 239 var q byte = '"' 240 if strings.Contains(s, `"`) { 241 q = '\'' 242 } 243 if err := w.WriteByte(q); err != nil { 244 return err 245 } 246 if _, err := w.WriteString(s); err != nil { 247 return err 248 } 249 if err := w.WriteByte(q); err != nil { 250 return err 251 } 252 return nil 253 } 254 255 // Section 12.1.2, "Elements", gives this list of void elements. Void elements 256 // are those that can't have any contents. 257 var voidElements = map[string]bool{ 258 "area": true, 259 "base": true, 260 "br": true, 261 "col": true, 262 "embed": true, 263 "hr": true, 264 "img": true, 265 "input": true, 266 "keygen": true, // "keygen" has been removed from the spec, but are kept here for backwards compatibility. 267 "link": true, 268 "meta": true, 269 "param": true, 270 "source": true, 271 "track": true, 272 "wbr": true, 273 }