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.
15 type writer interface {
18 WriteString(string) (int, error)
21 // Render renders the parse tree n to the given writer.
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
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'.
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 {
49 buf := bufio.NewWriter(w)
50 if err := render(buf, n); err != nil {
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)")
60 func render(w writer, n *Node) error {
62 if err == plaintextAbort {
68 func render1(w writer, n *Node) error {
69 // Render non-element nodes; these are the easy cases.
72 return errors.New("html: cannot render an ErrorNode node")
74 return escape(w, n.Data)
76 for c := n.FirstChild; c != nil; c = c.NextSibling {
77 if err := render1(w, c); err != nil {
85 if _, err := w.WriteString("<!--"); err != nil {
88 if _, err := w.WriteString(n.Data); err != nil {
91 if _, err := w.WriteString("-->"); err != nil {
96 if _, err := w.WriteString("<!DOCTYPE "); err != nil {
99 if _, err := w.WriteString(n.Data); err != nil {
104 for _, a := range n.Attr {
113 if _, err := w.WriteString(" PUBLIC "); err != nil {
116 if err := writeQuoted(w, p); err != nil {
120 if err := w.WriteByte(' '); err != nil {
123 if err := writeQuoted(w, s); err != nil {
128 if _, err := w.WriteString(" SYSTEM "); err != nil {
131 if err := writeQuoted(w, s); err != nil {
136 return w.WriteByte('>')
138 return errors.New("html: unknown node type")
141 // Render the <xxx> opening tag.
142 if err := w.WriteByte('<'); err != nil {
145 if _, err := w.WriteString(n.Data); err != nil {
148 for _, a := range n.Attr {
149 if err := w.WriteByte(' '); err != nil {
152 if a.Namespace != "" {
153 if _, err := w.WriteString(a.Namespace); err != nil {
156 if err := w.WriteByte(':'); err != nil {
160 if _, err := w.WriteString(a.Key); err != nil {
163 if _, err := w.WriteString(`="`); err != nil {
166 if err := escape(w, a.Val); err != nil {
169 if err := w.WriteByte('"'); err != nil {
173 if voidElements[n.Data] {
174 if n.FirstChild != nil {
175 return fmt.Errorf("html: void element <%s> has child nodes", n.Data)
177 _, err := w.WriteString("/>")
180 if err := w.WriteByte('>'); err != nil {
184 // Add initial newline where there is danger of a newline beging ignored.
185 if c := n.FirstChild; c != nil && c.Type == TextNode && strings.HasPrefix(c.Data, "\n") {
187 case "pre", "listing", "textarea":
188 if err := w.WriteByte('\n'); err != nil {
194 // Render any child nodes.
196 case "iframe", "noembed", "noframes", "noscript", "plaintext", "script", "style", "xmp":
197 for c := n.FirstChild; c != nil; c = c.NextSibling {
198 if c.Type == TextNode {
199 if _, err := w.WriteString(c.Data); err != nil {
203 if err := render1(w, c); err != nil {
208 if n.Data == "plaintext" {
209 // Don't render anything else. <plaintext> must be the
210 // last element in the file, with no closing tag.
211 return plaintextAbort
214 for c := n.FirstChild; c != nil; c = c.NextSibling {
215 if err := render1(w, c); err != nil {
221 // Render the </xxx> closing tag.
222 if _, err := w.WriteString("</"); err != nil {
225 if _, err := w.WriteString(n.Data); err != nil {
228 return w.WriteByte('>')
231 // writeQuoted writes s to w surrounded by quotes. Normally it will use double
232 // quotes, but if s contains a double quote, it will use single quotes.
233 // It is used for writing the identifiers in a doctype declaration.
234 // In valid HTML, they can't contain both types of quotes.
235 func writeQuoted(w writer, s string) error {
237 if strings.Contains(s, `"`) {
240 if err := w.WriteByte(q); err != nil {
243 if _, err := w.WriteString(s); err != nil {
246 if err := w.WriteByte(q); err != nil {
252 // Section 12.1.2, "Elements", gives this list of void elements. Void elements
253 // are those that can't have any contents.
254 var voidElements = map[string]bool{