~john-koepi/ubuntu/trusty/golang/default

« back to all changes in this revision

Viewing changes to src/pkg/html/doc.go

  • Committer: Bazaar Package Importer
  • Author(s): Ondřej Surý
  • Date: 2011-04-20 17:36:48 UTC
  • Revision ID: james.westby@ubuntu.com-20110420173648-ifergoxyrm832trd
Tags: upstream-2011.03.07.1
Import upstream version 2011.03.07.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// Copyright 2010 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
/*
 
6
The html package implements an HTML5-compliant tokenizer and parser.
 
7
 
 
8
Tokenization is done by creating a Tokenizer for an io.Reader r. It is the
 
9
caller's responsibility to ensure that r provides UTF-8 encoded HTML.
 
10
 
 
11
        z := html.NewTokenizer(r)
 
12
 
 
13
Given a Tokenizer z, the HTML is tokenized by repeatedly calling z.Next(),
 
14
which parses the next token and returns its type, or an error:
 
15
 
 
16
        for {
 
17
                tt := z.Next()
 
18
                if tt == html.ErrorToken {
 
19
                        // ...
 
20
                        return ...
 
21
                }
 
22
                // Process the current token.
 
23
        }
 
24
 
 
25
There are two APIs for retrieving the current token. The high-level API is to
 
26
call Token; the low-level API is to call Text or TagName / TagAttr. Both APIs
 
27
allow optionally calling Raw after Next but before Token, Text, TagName, or
 
28
TagAttr. In EBNF notation, the valid call sequence per token is:
 
29
 
 
30
        Next {Raw} [ Token | Text | TagName {TagAttr} ]
 
31
 
 
32
Token returns an independent data structure that completely describes a token.
 
33
Entities (such as "<") are unescaped, tag names and attribute keys are
 
34
lower-cased, and attributes are collected into a []Attribute. For example:
 
35
 
 
36
        for {
 
37
                if z.Next() == html.ErrorToken {
 
38
                        // Returning os.EOF indicates success.
 
39
                        return z.Error()
 
40
                }
 
41
                emitToken(z.Token())
 
42
        }
 
43
 
 
44
The low-level API performs fewer allocations and copies, but the contents of
 
45
the []byte values returned by Text, TagName and TagAttr may change on the next
 
46
call to Next. For example, to extract an HTML page's anchor text:
 
47
 
 
48
        depth := 0
 
49
        for {
 
50
                tt := z.Next()
 
51
                switch tt {
 
52
                case ErrorToken:
 
53
                        return z.Error()
 
54
                case TextToken:
 
55
                        if depth > 0 {
 
56
                                // emitBytes should copy the []byte it receives,
 
57
                                // if it doesn't process it immediately.
 
58
                                emitBytes(z.Text())
 
59
                        }
 
60
                case StartTagToken, EndTagToken:
 
61
                        tn, _ := z.TagName()
 
62
                        if len(tn) == 1 && tn[0] == 'a' {
 
63
                                if tt == StartTag {
 
64
                                        depth++
 
65
                                } else {
 
66
                                        depth--
 
67
                                }
 
68
                        }
 
69
                }
 
70
        }
 
71
 
 
72
A Tokenizer typically skips over HTML comments. To return comment tokens, set
 
73
Tokenizer.ReturnComments to true before looping over calls to Next.
 
74
 
 
75
Parsing is done by calling Parse with an io.Reader, which returns the root of
 
76
the parse tree (the document element) as a *Node. It is the caller's
 
77
responsibility to ensure that the Reader provides UTF-8 encoded HTML. For
 
78
example, to process each anchor node in depth-first order:
 
79
 
 
80
        doc, err := html.Parse(r)
 
81
        if err != nil {
 
82
                // ...
 
83
        }
 
84
        var f func(*html.Node)
 
85
        f = func(n *html.Node) {
 
86
                if n.Type == html.ElementNode && n.Data == "a" {
 
87
                        // Do something with n...
 
88
                }
 
89
                for _, c := range n.Child {
 
90
                        f(c)
 
91
                }
 
92
        }
 
93
        f(doc)
 
94
 
 
95
The relevant specifications include:
 
96
http://www.whatwg.org/specs/web-apps/current-work/multipage/syntax.html and
 
97
http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html
 
98
*/
 
99
package html
 
100
 
 
101
// The tokenization algorithm implemented by this package is not a line-by-line
 
102
// transliteration of the relatively verbose state-machine in the WHATWG
 
103
// specification. A more direct approach is used instead, where the program
 
104
// counter implies the state, such as whether it is tokenizing a tag or a text
 
105
// node. Specification compliance is verified by checking expected and actual
 
106
// outputs over a test suite rather than aiming for algorithmic fidelity.
 
107
 
 
108
// TODO(nigeltao): Does a DOM API belong in this package or a separate one?
 
109
// TODO(nigeltao): How does parsing interact with a JavaScript engine?