@@ -746,7 +746,8 @@ | |||
revision = "12dd70caea0268ac0d6c2707d0611ef601e7c64e" | |||
[[projects]] | |||
digest = "1:47ea747d07fae720d749d06ac5dc5ded0df70c57e328b6549cf2d9c64698757e" | |||
branch = "master" | |||
digest = "1:6d5ed712653ea5321fe3e3475ab2188cf362a4e0d31e9fd3acbd4dfbbca0d680" | |||
name = "golang.org/x/net" | |||
packages = [ | |||
"context", | |||
@@ -755,7 +756,7 @@ | |||
"html/charset", | |||
] | |||
pruneopts = "NUT" | |||
revision = "f2499483f923065a842d38eb4c7f1927e6fc6e6d" | |||
revision = "9b4f9f5ad5197c79fd623a3638e70d8b26cef344" | |||
[[projects]] | |||
digest = "1:8159a9cda4b8810aaaeb0d60e2fa68e2fd86d8af4ec8f5059830839e3c8d93d5" |
@@ -27,7 +27,7 @@ ignored = ["google.golang.org/appengine*"] | |||
name = "golang.org/x/text" | |||
[[constraint]] | |||
revision = "f2499483f923065a842d38eb4c7f1927e6fc6e6d" | |||
branch = "master" | |||
name = "golang.org/x/net" | |||
[[override]] |
@@ -5,9 +5,11 @@ | |||
// Package context defines the Context type, which carries deadlines, | |||
// cancelation signals, and other request-scoped values across API boundaries | |||
// and between processes. | |||
// As of Go 1.7 this package is available in the standard library under the | |||
// name context. https://golang.org/pkg/context. | |||
// | |||
// Incoming requests to a server should create a Context, and outgoing calls to | |||
// servers should accept a Context. The chain of function calls between must | |||
// servers should accept a Context. The chain of function calls between must | |||
// propagate the Context, optionally replacing it with a modified copy created | |||
// using WithDeadline, WithTimeout, WithCancel, or WithValue. | |||
// | |||
@@ -16,14 +18,14 @@ | |||
// propagation: | |||
// | |||
// Do not store Contexts inside a struct type; instead, pass a Context | |||
// explicitly to each function that needs it. The Context should be the first | |||
// explicitly to each function that needs it. The Context should be the first | |||
// parameter, typically named ctx: | |||
// | |||
// func DoSomething(ctx context.Context, arg Arg) error { | |||
// // ... use ctx ... | |||
// } | |||
// | |||
// Do not pass a nil Context, even if a function permits it. Pass context.TODO | |||
// Do not pass a nil Context, even if a function permits it. Pass context.TODO | |||
// if you are unsure about which Context to use. | |||
// | |||
// Use context Values only for request-scoped data that transits processes and | |||
@@ -36,112 +38,15 @@ | |||
// Contexts. | |||
package context // import "golang.org/x/net/context" | |||
import "time" | |||
// A Context carries a deadline, a cancelation signal, and other values across | |||
// API boundaries. | |||
// | |||
// Context's methods may be called by multiple goroutines simultaneously. | |||
type Context interface { | |||
// Deadline returns the time when work done on behalf of this context | |||
// should be canceled. Deadline returns ok==false when no deadline is | |||
// set. Successive calls to Deadline return the same results. | |||
Deadline() (deadline time.Time, ok bool) | |||
// Done returns a channel that's closed when work done on behalf of this | |||
// context should be canceled. Done may return nil if this context can | |||
// never be canceled. Successive calls to Done return the same value. | |||
// | |||
// WithCancel arranges for Done to be closed when cancel is called; | |||
// WithDeadline arranges for Done to be closed when the deadline | |||
// expires; WithTimeout arranges for Done to be closed when the timeout | |||
// elapses. | |||
// | |||
// Done is provided for use in select statements: | |||
// | |||
// // Stream generates values with DoSomething and sends them to out | |||
// // until DoSomething returns an error or ctx.Done is closed. | |||
// func Stream(ctx context.Context, out chan<- Value) error { | |||
// for { | |||
// v, err := DoSomething(ctx) | |||
// if err != nil { | |||
// return err | |||
// } | |||
// select { | |||
// case <-ctx.Done(): | |||
// return ctx.Err() | |||
// case out <- v: | |||
// } | |||
// } | |||
// } | |||
// | |||
// See http://blog.golang.org/pipelines for more examples of how to use | |||
// a Done channel for cancelation. | |||
Done() <-chan struct{} | |||
// Err returns a non-nil error value after Done is closed. Err returns | |||
// Canceled if the context was canceled or DeadlineExceeded if the | |||
// context's deadline passed. No other values for Err are defined. | |||
// After Done is closed, successive calls to Err return the same value. | |||
Err() error | |||
// Value returns the value associated with this context for key, or nil | |||
// if no value is associated with key. Successive calls to Value with | |||
// the same key returns the same result. | |||
// | |||
// Use context values only for request-scoped data that transits | |||
// processes and API boundaries, not for passing optional parameters to | |||
// functions. | |||
// | |||
// A key identifies a specific value in a Context. Functions that wish | |||
// to store values in Context typically allocate a key in a global | |||
// variable then use that key as the argument to context.WithValue and | |||
// Context.Value. A key can be any type that supports equality; | |||
// packages should define keys as an unexported type to avoid | |||
// collisions. | |||
// | |||
// Packages that define a Context key should provide type-safe accessors | |||
// for the values stores using that key: | |||
// | |||
// // Package user defines a User type that's stored in Contexts. | |||
// package user | |||
// | |||
// import "golang.org/x/net/context" | |||
// | |||
// // User is the type of value stored in the Contexts. | |||
// type User struct {...} | |||
// | |||
// // key is an unexported type for keys defined in this package. | |||
// // This prevents collisions with keys defined in other packages. | |||
// type key int | |||
// | |||
// // userKey is the key for user.User values in Contexts. It is | |||
// // unexported; clients use user.NewContext and user.FromContext | |||
// // instead of using this key directly. | |||
// var userKey key = 0 | |||
// | |||
// // NewContext returns a new Context that carries value u. | |||
// func NewContext(ctx context.Context, u *User) context.Context { | |||
// return context.WithValue(ctx, userKey, u) | |||
// } | |||
// | |||
// // FromContext returns the User value stored in ctx, if any. | |||
// func FromContext(ctx context.Context) (*User, bool) { | |||
// u, ok := ctx.Value(userKey).(*User) | |||
// return u, ok | |||
// } | |||
Value(key interface{}) interface{} | |||
} | |||
// Background returns a non-nil, empty Context. It is never canceled, has no | |||
// values, and has no deadline. It is typically used by the main function, | |||
// values, and has no deadline. It is typically used by the main function, | |||
// initialization, and tests, and as the top-level Context for incoming | |||
// requests. | |||
func Background() Context { | |||
return background | |||
} | |||
// TODO returns a non-nil, empty Context. Code should use context.TODO when | |||
// TODO returns a non-nil, empty Context. Code should use context.TODO when | |||
// it's unclear which Context to use or it is not yet available (because the | |||
// surrounding function has not yet been extended to accept a Context | |||
// parameter). TODO is recognized by static analysis tools that determine | |||
@@ -149,8 +54,3 @@ func Background() Context { | |||
func TODO() Context { | |||
return todo | |||
} | |||
// A CancelFunc tells an operation to abandon its work. | |||
// A CancelFunc does not wait for the work to stop. | |||
// After the first call, subsequent calls to a CancelFunc do nothing. | |||
type CancelFunc func() |
@@ -35,8 +35,8 @@ func WithCancel(parent Context) (ctx Context, cancel CancelFunc) { | |||
} | |||
// WithDeadline returns a copy of the parent context with the deadline adjusted | |||
// to be no later than d. If the parent's deadline is already earlier than d, | |||
// WithDeadline(parent, d) is semantically equivalent to parent. The returned | |||
// to be no later than d. If the parent's deadline is already earlier than d, | |||
// WithDeadline(parent, d) is semantically equivalent to parent. The returned | |||
// context's Done channel is closed when the deadline expires, when the returned | |||
// cancel function is called, or when the parent context's Done channel is | |||
// closed, whichever happens first. |
@@ -0,0 +1,20 @@ | |||
// Copyright 2017 The Go Authors. All rights reserved. | |||
// Use of this source code is governed by a BSD-style | |||
// license that can be found in the LICENSE file. | |||
// +build go1.9 | |||
package context | |||
import "context" // standard library's context, as of Go 1.7 | |||
// A Context carries a deadline, a cancelation signal, and other values across | |||
// API boundaries. | |||
// | |||
// Context's methods may be called by multiple goroutines simultaneously. | |||
type Context = context.Context | |||
// A CancelFunc tells an operation to abandon its work. | |||
// A CancelFunc does not wait for the work to stop. | |||
// After the first call, subsequent calls to a CancelFunc do nothing. | |||
type CancelFunc = context.CancelFunc |
@@ -13,7 +13,7 @@ import ( | |||
"time" | |||
) | |||
// An emptyCtx is never canceled, has no values, and has no deadline. It is not | |||
// An emptyCtx is never canceled, has no values, and has no deadline. It is not | |||
// struct{}, since vars of this type must have distinct addresses. | |||
type emptyCtx int | |||
@@ -104,7 +104,7 @@ func propagateCancel(parent Context, child canceler) { | |||
} | |||
// parentCancelCtx follows a chain of parent references until it finds a | |||
// *cancelCtx. This function understands how each of the concrete types in this | |||
// *cancelCtx. This function understands how each of the concrete types in this | |||
// package represents its parent. | |||
func parentCancelCtx(parent Context) (*cancelCtx, bool) { | |||
for { | |||
@@ -134,14 +134,14 @@ func removeChild(parent Context, child canceler) { | |||
p.mu.Unlock() | |||
} | |||
// A canceler is a context type that can be canceled directly. The | |||
// A canceler is a context type that can be canceled directly. The | |||
// implementations are *cancelCtx and *timerCtx. | |||
type canceler interface { | |||
cancel(removeFromParent bool, err error) | |||
Done() <-chan struct{} | |||
} | |||
// A cancelCtx can be canceled. When canceled, it also cancels any children | |||
// A cancelCtx can be canceled. When canceled, it also cancels any children | |||
// that implement canceler. | |||
type cancelCtx struct { | |||
Context | |||
@@ -193,8 +193,8 @@ func (c *cancelCtx) cancel(removeFromParent bool, err error) { | |||
} | |||
// WithDeadline returns a copy of the parent context with the deadline adjusted | |||
// to be no later than d. If the parent's deadline is already earlier than d, | |||
// WithDeadline(parent, d) is semantically equivalent to parent. The returned | |||
// to be no later than d. If the parent's deadline is already earlier than d, | |||
// WithDeadline(parent, d) is semantically equivalent to parent. The returned | |||
// context's Done channel is closed when the deadline expires, when the returned | |||
// cancel function is called, or when the parent context's Done channel is | |||
// closed, whichever happens first. | |||
@@ -226,8 +226,8 @@ func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc) { | |||
return c, func() { c.cancel(true, Canceled) } | |||
} | |||
// A timerCtx carries a timer and a deadline. It embeds a cancelCtx to | |||
// implement Done and Err. It implements cancel by stopping its timer then | |||
// A timerCtx carries a timer and a deadline. It embeds a cancelCtx to | |||
// implement Done and Err. It implements cancel by stopping its timer then | |||
// delegating to cancelCtx.cancel. | |||
type timerCtx struct { | |||
*cancelCtx | |||
@@ -281,7 +281,7 @@ func WithValue(parent Context, key interface{}, val interface{}) Context { | |||
return &valueCtx{parent, key, val} | |||
} | |||
// A valueCtx carries a key-value pair. It implements Value for that key and | |||
// A valueCtx carries a key-value pair. It implements Value for that key and | |||
// delegates all other calls to the embedded Context. | |||
type valueCtx struct { | |||
Context |
@@ -0,0 +1,109 @@ | |||
// Copyright 2014 The Go Authors. All rights reserved. | |||
// Use of this source code is governed by a BSD-style | |||
// license that can be found in the LICENSE file. | |||
// +build !go1.9 | |||
package context | |||
import "time" | |||
// A Context carries a deadline, a cancelation signal, and other values across | |||
// API boundaries. | |||
// | |||
// Context's methods may be called by multiple goroutines simultaneously. | |||
type Context interface { | |||
// Deadline returns the time when work done on behalf of this context | |||
// should be canceled. Deadline returns ok==false when no deadline is | |||
// set. Successive calls to Deadline return the same results. | |||
Deadline() (deadline time.Time, ok bool) | |||
// Done returns a channel that's closed when work done on behalf of this | |||
// context should be canceled. Done may return nil if this context can | |||
// never be canceled. Successive calls to Done return the same value. | |||
// | |||
// WithCancel arranges for Done to be closed when cancel is called; | |||
// WithDeadline arranges for Done to be closed when the deadline | |||
// expires; WithTimeout arranges for Done to be closed when the timeout | |||
// elapses. | |||
// | |||
// Done is provided for use in select statements: | |||
// | |||
// // Stream generates values with DoSomething and sends them to out | |||
// // until DoSomething returns an error or ctx.Done is closed. | |||
// func Stream(ctx context.Context, out chan<- Value) error { | |||
// for { | |||
// v, err := DoSomething(ctx) | |||
// if err != nil { | |||
// return err | |||
// } | |||
// select { | |||
// case <-ctx.Done(): | |||
// return ctx.Err() | |||
// case out <- v: | |||
// } | |||
// } | |||
// } | |||
// | |||
// See http://blog.golang.org/pipelines for more examples of how to use | |||
// a Done channel for cancelation. | |||
Done() <-chan struct{} | |||
// Err returns a non-nil error value after Done is closed. Err returns | |||
// Canceled if the context was canceled or DeadlineExceeded if the | |||
// context's deadline passed. No other values for Err are defined. | |||
// After Done is closed, successive calls to Err return the same value. | |||
Err() error | |||
// Value returns the value associated with this context for key, or nil | |||
// if no value is associated with key. Successive calls to Value with | |||
// the same key returns the same result. | |||
// | |||
// Use context values only for request-scoped data that transits | |||
// processes and API boundaries, not for passing optional parameters to | |||
// functions. | |||
// | |||
// A key identifies a specific value in a Context. Functions that wish | |||
// to store values in Context typically allocate a key in a global | |||
// variable then use that key as the argument to context.WithValue and | |||
// Context.Value. A key can be any type that supports equality; | |||
// packages should define keys as an unexported type to avoid | |||
// collisions. | |||
// | |||
// Packages that define a Context key should provide type-safe accessors | |||
// for the values stores using that key: | |||
// | |||
// // Package user defines a User type that's stored in Contexts. | |||
// package user | |||
// | |||
// import "golang.org/x/net/context" | |||
// | |||
// // User is the type of value stored in the Contexts. | |||
// type User struct {...} | |||
// | |||
// // key is an unexported type for keys defined in this package. | |||
// // This prevents collisions with keys defined in other packages. | |||
// type key int | |||
// | |||
// // userKey is the key for user.User values in Contexts. It is | |||
// // unexported; clients use user.NewContext and user.FromContext | |||
// // instead of using this key directly. | |||
// var userKey key = 0 | |||
// | |||
// // NewContext returns a new Context that carries value u. | |||
// func NewContext(ctx context.Context, u *User) context.Context { | |||
// return context.WithValue(ctx, userKey, u) | |||
// } | |||
// | |||
// // FromContext returns the User value stored in ctx, if any. | |||
// func FromContext(ctx context.Context) (*User, bool) { | |||
// u, ok := ctx.Value(userKey).(*User) | |||
// return u, ok | |||
// } | |||
Value(key interface{}) interface{} | |||
} | |||
// A CancelFunc tells an operation to abandon its work. | |||
// A CancelFunc does not wait for the work to stop. | |||
// After the first call, subsequent calls to a CancelFunc do nothing. | |||
type CancelFunc func() |
@@ -4,17 +4,17 @@ | |||
// +build ignore | |||
package main | |||
//go:generate go run gen.go | |||
//go:generate go run gen.go -test | |||
// This program generates table.go and table_test.go. | |||
// Invoke as | |||
// | |||
// go run gen.go |gofmt >table.go | |||
// go run gen.go -test |gofmt >table_test.go | |||
package main | |||
import ( | |||
"bytes" | |||
"flag" | |||
"fmt" | |||
"go/format" | |||
"io/ioutil" | |||
"math/rand" | |||
"os" | |||
"sort" | |||
@@ -42,6 +42,18 @@ func identifier(s string) string { | |||
var test = flag.Bool("test", false, "generate table_test.go") | |||
func genFile(name string, buf *bytes.Buffer) { | |||
b, err := format.Source(buf.Bytes()) | |||
if err != nil { | |||
fmt.Fprintln(os.Stderr, err) | |||
os.Exit(1) | |||
} | |||
if err := ioutil.WriteFile(name, b, 0644); err != nil { | |||
fmt.Fprintln(os.Stderr, err) | |||
os.Exit(1) | |||
} | |||
} | |||
func main() { | |||
flag.Parse() | |||
@@ -52,32 +64,31 @@ func main() { | |||
all = append(all, extra...) | |||
sort.Strings(all) | |||
if *test { | |||
fmt.Printf("// generated by go run gen.go -test; DO NOT EDIT\n\n") | |||
fmt.Printf("package atom\n\n") | |||
fmt.Printf("var testAtomList = []string{\n") | |||
for _, s := range all { | |||
fmt.Printf("\t%q,\n", s) | |||
} | |||
fmt.Printf("}\n") | |||
return | |||
} | |||
// uniq - lists have dups | |||
// compute max len too | |||
maxLen := 0 | |||
w := 0 | |||
for _, s := range all { | |||
if w == 0 || all[w-1] != s { | |||
if maxLen < len(s) { | |||
maxLen = len(s) | |||
} | |||
all[w] = s | |||
w++ | |||
} | |||
} | |||
all = all[:w] | |||
if *test { | |||
var buf bytes.Buffer | |||
fmt.Fprintln(&buf, "// Code generated by go generate gen.go; DO NOT EDIT.\n") | |||
fmt.Fprintln(&buf, "//go:generate go run gen.go -test\n") | |||
fmt.Fprintln(&buf, "package atom\n") | |||
fmt.Fprintln(&buf, "var testAtomList = []string{") | |||
for _, s := range all { | |||
fmt.Fprintf(&buf, "\t%q,\n", s) | |||
} | |||
fmt.Fprintln(&buf, "}") | |||
genFile("table_test.go", &buf) | |||
return | |||
} | |||
// Find hash that minimizes table size. | |||
var best *table | |||
for i := 0; i < 1000000; i++ { | |||
@@ -163,36 +174,46 @@ func main() { | |||
atom[s] = uint32(off<<8 | len(s)) | |||
} | |||
var buf bytes.Buffer | |||
// Generate the Go code. | |||
fmt.Printf("// generated by go run gen.go; DO NOT EDIT\n\n") | |||
fmt.Printf("package atom\n\nconst (\n") | |||
fmt.Fprintln(&buf, "// Code generated by go generate gen.go; DO NOT EDIT.\n") | |||
fmt.Fprintln(&buf, "//go:generate go run gen.go\n") | |||
fmt.Fprintln(&buf, "package atom\n\nconst (") | |||
// compute max len | |||
maxLen := 0 | |||
for _, s := range all { | |||
fmt.Printf("\t%s Atom = %#x\n", identifier(s), atom[s]) | |||
if maxLen < len(s) { | |||
maxLen = len(s) | |||
} | |||
fmt.Fprintf(&buf, "\t%s Atom = %#x\n", identifier(s), atom[s]) | |||
} | |||
fmt.Printf(")\n\n") | |||
fmt.Fprintln(&buf, ")\n") | |||
fmt.Printf("const hash0 = %#x\n\n", best.h0) | |||
fmt.Printf("const maxAtomLen = %d\n\n", maxLen) | |||
fmt.Fprintf(&buf, "const hash0 = %#x\n\n", best.h0) | |||
fmt.Fprintf(&buf, "const maxAtomLen = %d\n\n", maxLen) | |||
fmt.Printf("var table = [1<<%d]Atom{\n", best.k) | |||
fmt.Fprintf(&buf, "var table = [1<<%d]Atom{\n", best.k) | |||
for i, s := range best.tab { | |||
if s == "" { | |||
continue | |||
} | |||
fmt.Printf("\t%#x: %#x, // %s\n", i, atom[s], s) | |||
fmt.Fprintf(&buf, "\t%#x: %#x, // %s\n", i, atom[s], s) | |||
} | |||
fmt.Printf("}\n") | |||
fmt.Fprintf(&buf, "}\n") | |||
datasize := (1 << best.k) * 4 | |||
fmt.Printf("const atomText =\n") | |||
fmt.Fprintln(&buf, "const atomText =") | |||
textsize := len(text) | |||
for len(text) > 60 { | |||
fmt.Printf("\t%q +\n", text[:60]) | |||
fmt.Fprintf(&buf, "\t%q +\n", text[:60]) | |||
text = text[60:] | |||
} | |||
fmt.Printf("\t%q\n\n", text) | |||
fmt.Fprintf(&buf, "\t%q\n\n", text) | |||
genFile("table.go", &buf) | |||
fmt.Fprintf(os.Stderr, "%d atoms; %d string bytes + %d tables = %d total data\n", len(all), textsize, datasize, textsize+datasize) | |||
fmt.Fprintf(os.Stdout, "%d atoms; %d string bytes + %d tables = %d total data\n", len(all), textsize, datasize, textsize+datasize) | |||
} | |||
type byLen []string | |||
@@ -285,8 +306,10 @@ func (t *table) push(i uint32, depth int) bool { | |||
// The lists of element names and attribute keys were taken from | |||
// https://html.spec.whatwg.org/multipage/indices.html#index | |||
// as of the "HTML Living Standard - Last Updated 21 February 2015" version. | |||
// as of the "HTML Living Standard - Last Updated 16 April 2018" version. | |||
// "command", "keygen" and "menuitem" have been removed from the spec, | |||
// but are kept here for backwards compatibility. | |||
var elements = []string{ | |||
"a", | |||
"abbr", | |||
@@ -349,6 +372,7 @@ var elements = []string{ | |||
"legend", | |||
"li", | |||
"link", | |||
"main", | |||
"map", | |||
"mark", | |||
"menu", | |||
@@ -364,6 +388,7 @@ var elements = []string{ | |||
"output", | |||
"p", | |||
"param", | |||
"picture", | |||
"pre", | |||
"progress", | |||
"q", | |||
@@ -375,6 +400,7 @@ var elements = []string{ | |||
"script", | |||
"section", | |||
"select", | |||
"slot", | |||
"small", | |||
"source", | |||
"span", | |||
@@ -403,14 +429,21 @@ var elements = []string{ | |||
} | |||
// https://html.spec.whatwg.org/multipage/indices.html#attributes-3 | |||
// | |||
// "challenge", "command", "contextmenu", "dropzone", "icon", "keytype", "mediagroup", | |||
// "radiogroup", "spellcheck", "scoped", "seamless", "sortable" and "sorted" have been removed from the spec, | |||
// but are kept here for backwards compatibility. | |||
var attributes = []string{ | |||
"abbr", | |||
"accept", | |||
"accept-charset", | |||
"accesskey", | |||
"action", | |||
"allowfullscreen", | |||
"allowpaymentrequest", | |||
"allowusermedia", | |||
"alt", | |||
"as", | |||
"async", | |||
"autocomplete", | |||
"autofocus", | |||
@@ -420,6 +453,7 @@ var attributes = []string{ | |||
"checked", | |||
"cite", | |||
"class", | |||
"color", | |||
"cols", | |||
"colspan", | |||
"command", | |||
@@ -457,6 +491,8 @@ var attributes = []string{ | |||
"icon", | |||
"id", | |||
"inputmode", | |||
"integrity", | |||
"is", | |||
"ismap", | |||
"itemid", | |||
"itemprop", | |||
@@ -481,16 +517,20 @@ var attributes = []string{ | |||
"multiple", | |||
"muted", | |||
"name", | |||
"nomodule", | |||
"nonce", | |||
"novalidate", | |||
"open", | |||
"optimum", | |||
"pattern", | |||
"ping", | |||
"placeholder", | |||
"playsinline", | |||
"poster", | |||
"preload", | |||
"radiogroup", | |||
"readonly", | |||
"referrerpolicy", | |||
"rel", | |||
"required", | |||
"reversed", | |||
@@ -507,10 +547,13 @@ var attributes = []string{ | |||
"sizes", | |||
"sortable", | |||
"sorted", | |||
"slot", | |||
"span", | |||
"spellcheck", | |||
"src", | |||
"srcdoc", | |||
"srclang", | |||
"srcset", | |||
"start", | |||
"step", | |||
"style", | |||
@@ -520,16 +563,22 @@ var attributes = []string{ | |||
"translate", | |||
"type", | |||
"typemustmatch", | |||
"updateviacache", | |||
"usemap", | |||
"value", | |||
"width", | |||
"workertype", | |||
"wrap", | |||
} | |||
// "onautocomplete", "onautocompleteerror", "onmousewheel", | |||
// "onshow" and "onsort" have been removed from the spec, | |||
// but are kept here for backwards compatibility. | |||
var eventHandlers = []string{ | |||
"onabort", | |||
"onautocomplete", | |||
"onautocompleteerror", | |||
"onauxclick", | |||
"onafterprint", | |||
"onbeforeprint", | |||
"onbeforeunload", | |||
@@ -541,11 +590,14 @@ var eventHandlers = []string{ | |||
"onclick", | |||
"onclose", | |||
"oncontextmenu", | |||
"oncopy", | |||
"oncuechange", | |||
"oncut", | |||
"ondblclick", | |||
"ondrag", | |||
"ondragend", | |||
"ondragenter", | |||
"ondragexit", | |||
"ondragleave", | |||
"ondragover", | |||
"ondragstart", | |||
@@ -565,18 +617,24 @@ var eventHandlers = []string{ | |||
"onload", | |||
"onloadeddata", | |||
"onloadedmetadata", | |||
"onloadend", | |||
"onloadstart", | |||
"onmessage", | |||
"onmessageerror", | |||
"onmousedown", | |||
"onmouseenter", | |||
"onmouseleave", | |||
"onmousemove", | |||
"onmouseout", | |||
"onmouseover", | |||
"onmouseup", | |||
"onmousewheel", | |||
"onwheel", | |||
"onoffline", | |||
"ononline", | |||
"onpagehide", | |||
"onpageshow", | |||
"onpaste", | |||
"onpause", | |||
"onplay", | |||
"onplaying", | |||
@@ -585,7 +643,9 @@ var eventHandlers = []string{ | |||
"onratechange", | |||
"onreset", | |||
"onresize", | |||
"onrejectionhandled", | |||
"onscroll", | |||
"onsecuritypolicyviolation", | |||
"onseeked", | |||
"onseeking", | |||
"onselect", | |||
@@ -597,6 +657,7 @@ var eventHandlers = []string{ | |||
"onsuspend", | |||
"ontimeupdate", | |||
"ontoggle", | |||
"onunhandledrejection", | |||
"onunload", | |||
"onvolumechange", | |||
"onwaiting", | |||
@@ -604,6 +665,7 @@ var eventHandlers = []string{ | |||
// extra are ad-hoc values not covered by any of the lists above. | |||
var extra = []string{ | |||
"acronym", | |||
"align", | |||
"annotation", | |||
"annotation-xml", | |||
@@ -639,6 +701,8 @@ var extra = []string{ | |||
"plaintext", | |||
"prompt", | |||
"public", | |||
"rb", | |||
"rtc", | |||
"spacer", | |||
"strike", | |||
"svg", |
@@ -4,7 +4,7 @@ | |||
package html | |||
// Section 12.2.3.2 of the HTML5 specification says "The following elements | |||
// Section 12.2.4.2 of the HTML5 specification says "The following elements | |||
// have varying levels of special parsing rules". | |||
// https://html.spec.whatwg.org/multipage/syntax.html#the-stack-of-open-elements | |||
var isSpecialElementMap = map[string]bool{ | |||
@@ -52,10 +52,12 @@ var isSpecialElementMap = map[string]bool{ | |||
"iframe": true, | |||
"img": true, | |||
"input": true, | |||
"isindex": true, | |||
"isindex": true, // The 'isindex' element has been removed, but keep it for backwards compatibility. | |||
"keygen": true, | |||
"li": true, | |||
"link": true, | |||
"listing": true, | |||
"main": true, | |||
"marquee": true, | |||
"menu": true, | |||
"meta": true, | |||
@@ -95,8 +97,16 @@ func isSpecialElement(element *Node) bool { | |||
switch element.Namespace { | |||
case "", "html": | |||
return isSpecialElementMap[element.Data] | |||
case "math": | |||
switch element.Data { | |||
case "mi", "mo", "mn", "ms", "mtext", "annotation-xml": | |||
return true | |||
} | |||
case "svg": | |||
return element.Data == "foreignObject" | |||
switch element.Data { | |||
case "foreignObject", "desc", "title": | |||
return true | |||
} | |||
} | |||
return false | |||
} |
@@ -49,18 +49,18 @@ call to Next. For example, to extract an HTML page's anchor text: | |||
for { | |||
tt := z.Next() | |||
switch tt { | |||
case ErrorToken: | |||
case html.ErrorToken: | |||
return z.Err() | |||
case TextToken: | |||
case html.TextToken: | |||
if depth > 0 { | |||
// emitBytes should copy the []byte it receives, | |||
// if it doesn't process it immediately. | |||
emitBytes(z.Text()) | |||
} | |||
case StartTagToken, EndTagToken: | |||
case html.StartTagToken, html.EndTagToken: | |||
tn, _ := z.TagName() | |||
if len(tn) == 1 && tn[0] == 'a' { | |||
if tt == StartTagToken { | |||
if tt == html.StartTagToken { | |||
depth++ | |||
} else { | |||
depth-- |
@@ -67,7 +67,7 @@ func mathMLTextIntegrationPoint(n *Node) bool { | |||
return false | |||
} | |||
// Section 12.2.5.5. | |||
// Section 12.2.6.5. | |||
var breakout = map[string]bool{ | |||
"b": true, | |||
"big": true, | |||
@@ -115,7 +115,7 @@ var breakout = map[string]bool{ | |||
"var": true, | |||
} | |||
// Section 12.2.5.5. | |||
// Section 12.2.6.5. | |||
var svgTagNameAdjustments = map[string]string{ | |||
"altglyph": "altGlyph", | |||
"altglyphdef": "altGlyphDef", | |||
@@ -155,7 +155,7 @@ var svgTagNameAdjustments = map[string]string{ | |||
"textpath": "textPath", | |||
} | |||
// Section 12.2.5.1 | |||
// Section 12.2.6.1 | |||
var mathMLAttributeAdjustments = map[string]string{ | |||
"definitionurl": "definitionURL", | |||
} |
@@ -21,9 +21,10 @@ const ( | |||
scopeMarkerNode | |||
) | |||
// Section 12.2.3.3 says "scope markers are inserted when entering applet | |||
// elements, buttons, object elements, marquees, table cells, and table | |||
// captions, and are used to prevent formatting from 'leaking'". | |||
// Section 12.2.4.3 says "The markers are inserted when entering applet, | |||
// object, marquee, template, td, th, and caption elements, and are used | |||
// to prevent formatting from "leaking" into applet, object, marquee, | |||
// template, td, th, and caption elements". | |||
var scopeMarker = Node{Type: scopeMarkerNode} | |||
// A Node consists of a NodeType and some Data (tag name for element nodes, | |||
@@ -173,6 +174,16 @@ func (s *nodeStack) index(n *Node) int { | |||
return -1 | |||
} | |||
// contains returns whether a is within s. | |||
func (s *nodeStack) contains(a atom.Atom) bool { | |||
for _, n := range *s { | |||
if n.DataAtom == a { | |||
return true | |||
} | |||
} | |||
return false | |||
} | |||
// insert inserts a node at the given index. | |||
func (s *nodeStack) insert(i int, n *Node) { | |||
(*s) = append(*s, nil) | |||
@@ -191,3 +202,19 @@ func (s *nodeStack) remove(n *Node) { | |||
(*s)[j] = nil | |||
*s = (*s)[:j] | |||
} | |||
type insertionModeStack []insertionMode | |||
func (s *insertionModeStack) pop() (im insertionMode) { | |||
i := len(*s) | |||
im = (*s)[i-1] | |||
*s = (*s)[:i-1] | |||
return im | |||
} | |||
func (s *insertionModeStack) top() insertionMode { | |||
if i := len(*s); i > 0 { | |||
return (*s)[i-1] | |||
} | |||
return nil | |||
} |
@@ -25,20 +25,22 @@ type parser struct { | |||
hasSelfClosingToken bool | |||
// doc is the document root element. | |||
doc *Node | |||
// The stack of open elements (section 12.2.3.2) and active formatting | |||
// elements (section 12.2.3.3). | |||
// The stack of open elements (section 12.2.4.2) and active formatting | |||
// elements (section 12.2.4.3). | |||
oe, afe nodeStack | |||
// Element pointers (section 12.2.3.4). | |||
// Element pointers (section 12.2.4.4). | |||
head, form *Node | |||
// Other parsing state flags (section 12.2.3.5). | |||
// Other parsing state flags (section 12.2.4.5). | |||
scripting, framesetOK bool | |||
// The stack of template insertion modes | |||
templateStack insertionModeStack | |||
// im is the current insertion mode. | |||
im insertionMode | |||
// originalIM is the insertion mode to go back to after completing a text | |||
// or inTableText insertion mode. | |||
originalIM insertionMode | |||
// fosterParenting is whether new elements should be inserted according to | |||
// the foster parenting rules (section 12.2.5.3). | |||
// the foster parenting rules (section 12.2.6.1). | |||
fosterParenting bool | |||
// quirks is whether the parser is operating in "quirks mode." | |||
quirks bool | |||
@@ -56,7 +58,7 @@ func (p *parser) top() *Node { | |||
return p.doc | |||
} | |||
// Stop tags for use in popUntil. These come from section 12.2.3.2. | |||
// Stop tags for use in popUntil. These come from section 12.2.4.2. | |||
var ( | |||
defaultScopeStopTags = map[string][]a.Atom{ | |||
"": {a.Applet, a.Caption, a.Html, a.Table, a.Td, a.Th, a.Marquee, a.Object, a.Template}, | |||
@@ -79,7 +81,7 @@ const ( | |||
// popUntil pops the stack of open elements at the highest element whose tag | |||
// is in matchTags, provided there is no higher element in the scope's stop | |||
// tags (as defined in section 12.2.3.2). It returns whether or not there was | |||
// tags (as defined in section 12.2.4.2). It returns whether or not there was | |||
// such an element. If there was not, popUntil leaves the stack unchanged. | |||
// | |||
// For example, the set of stop tags for table scope is: "html", "table". If | |||
@@ -126,7 +128,7 @@ func (p *parser) indexOfElementInScope(s scope, matchTags ...a.Atom) int { | |||
return -1 | |||
} | |||
case tableScope: | |||
if tagAtom == a.Html || tagAtom == a.Table { | |||
if tagAtom == a.Html || tagAtom == a.Table || tagAtom == a.Template { | |||
return -1 | |||
} | |||
case selectScope: | |||
@@ -162,17 +164,17 @@ func (p *parser) clearStackToContext(s scope) { | |||
tagAtom := p.oe[i].DataAtom | |||
switch s { | |||
case tableScope: | |||
if tagAtom == a.Html || tagAtom == a.Table { | |||
if tagAtom == a.Html || tagAtom == a.Table || tagAtom == a.Template { | |||
p.oe = p.oe[:i+1] | |||
return | |||
} | |||
case tableRowScope: | |||
if tagAtom == a.Html || tagAtom == a.Tr { | |||
if tagAtom == a.Html || tagAtom == a.Tr || tagAtom == a.Template { | |||
p.oe = p.oe[:i+1] | |||
return | |||
} | |||
case tableBodyScope: | |||
if tagAtom == a.Html || tagAtom == a.Tbody || tagAtom == a.Tfoot || tagAtom == a.Thead { | |||
if tagAtom == a.Html || tagAtom == a.Tbody || tagAtom == a.Tfoot || tagAtom == a.Thead || tagAtom == a.Template { | |||
p.oe = p.oe[:i+1] | |||
return | |||
} | |||
@@ -183,7 +185,7 @@ func (p *parser) clearStackToContext(s scope) { | |||
} | |||
// generateImpliedEndTags pops nodes off the stack of open elements as long as | |||
// the top node has a tag name of dd, dt, li, option, optgroup, p, rp, or rt. | |||
// the top node has a tag name of dd, dt, li, optgroup, option, p, rb, rp, rt or rtc. | |||
// If exceptions are specified, nodes with that name will not be popped off. | |||
func (p *parser) generateImpliedEndTags(exceptions ...string) { | |||
var i int | |||
@@ -192,7 +194,7 @@ loop: | |||
n := p.oe[i] | |||
if n.Type == ElementNode { | |||
switch n.DataAtom { | |||
case a.Dd, a.Dt, a.Li, a.Option, a.Optgroup, a.P, a.Rp, a.Rt: | |||
case a.Dd, a.Dt, a.Li, a.Optgroup, a.Option, a.P, a.Rb, a.Rp, a.Rt, a.Rtc: | |||
for _, except := range exceptions { | |||
if n.Data == except { | |||
break loop | |||
@@ -234,9 +236,9 @@ func (p *parser) shouldFosterParent() bool { | |||
} | |||
// fosterParent adds a child node according to the foster parenting rules. | |||
// Section 12.2.5.3, "foster parenting". | |||
// Section 12.2.6.1, "foster parenting". | |||
func (p *parser) fosterParent(n *Node) { | |||
var table, parent, prev *Node | |||
var table, parent, prev, template *Node | |||
var i int | |||
for i = len(p.oe) - 1; i >= 0; i-- { | |||
if p.oe[i].DataAtom == a.Table { | |||
@@ -245,6 +247,19 @@ func (p *parser) fosterParent(n *Node) { | |||
} | |||
} | |||
var j int | |||
for j = len(p.oe) - 1; j >= 0; j-- { | |||
if p.oe[j].DataAtom == a.Template { | |||
template = p.oe[j] | |||
break | |||
} | |||
} | |||
if template != nil && (table == nil || j > i) { | |||
template.AppendChild(n) | |||
return | |||
} | |||
if table == nil { | |||
// The foster parent is the html element. | |||
parent = p.oe[0] | |||
@@ -304,7 +319,7 @@ func (p *parser) addElement() { | |||
}) | |||
} | |||
// Section 12.2.3.3. | |||
// Section 12.2.4.3. | |||
func (p *parser) addFormattingElement() { | |||
tagAtom, attr := p.tok.DataAtom, p.tok.Attr | |||
p.addElement() | |||
@@ -351,7 +366,7 @@ findIdenticalElements: | |||
p.afe = append(p.afe, p.top()) | |||
} | |||
// Section 12.2.3.3. | |||
// Section 12.2.4.3. | |||
func (p *parser) clearActiveFormattingElements() { | |||
for { | |||
n := p.afe.pop() | |||
@@ -361,7 +376,7 @@ func (p *parser) clearActiveFormattingElements() { | |||
} | |||
} | |||
// Section 12.2.3.3. | |||
// Section 12.2.4.3. | |||
func (p *parser) reconstructActiveFormattingElements() { | |||
n := p.afe.top() | |||
if n == nil { | |||
@@ -390,12 +405,12 @@ func (p *parser) reconstructActiveFormattingElements() { | |||
} | |||
} | |||
// Section 12.2.4. | |||
// Section 12.2.5. | |||
func (p *parser) acknowledgeSelfClosingTag() { | |||
p.hasSelfClosingToken = false | |||
} | |||
// An insertion mode (section 12.2.3.1) is the state transition function from | |||
// An insertion mode (section 12.2.4.1) is the state transition function from | |||
// a particular state in the HTML5 parser's state machine. It updates the | |||
// parser's fields depending on parser.tok (where ErrorToken means EOF). | |||
// It returns whether the token was consumed. | |||
@@ -403,7 +418,7 @@ type insertionMode func(*parser) bool | |||
// setOriginalIM sets the insertion mode to return to after completing a text or | |||
// inTableText insertion mode. | |||
// Section 12.2.3.1, "using the rules for". | |||
// Section 12.2.4.1, "using the rules for". | |||
func (p *parser) setOriginalIM() { | |||
if p.originalIM != nil { | |||
panic("html: bad parser state: originalIM was set twice") | |||
@@ -411,18 +426,38 @@ func (p *parser) setOriginalIM() { | |||
p.originalIM = p.im | |||
} | |||
// Section 12.2.3.1, "reset the insertion mode". | |||
// Section 12.2.4.1, "reset the insertion mode". | |||
func (p *parser) resetInsertionMode() { | |||
for i := len(p.oe) - 1; i >= 0; i-- { | |||
n := p.oe[i] | |||
if i == 0 && p.context != nil { | |||
last := i == 0 | |||
if last && p.context != nil { | |||
n = p.context | |||
} | |||
switch n.DataAtom { | |||
case a.Select: | |||
if !last { | |||
for ancestor, first := n, p.oe[0]; ancestor != first; { | |||
if ancestor == first { | |||
break | |||
} | |||
ancestor = p.oe[p.oe.index(ancestor)-1] | |||
switch ancestor.DataAtom { | |||
case a.Template: | |||
p.im = inSelectIM | |||
return | |||
case a.Table: | |||
p.im = inSelectInTableIM | |||
return | |||
} | |||
} | |||
} | |||
p.im = inSelectIM | |||
case a.Td, a.Th: | |||
// TODO: remove this divergence from the HTML5 spec. | |||
// | |||
// See https://bugs.chromium.org/p/chromium/issues/detail?id=829668 | |||
p.im = inCellIM | |||
case a.Tr: | |||
p.im = inRowIM | |||
@@ -434,25 +469,41 @@ func (p *parser) resetInsertionMode() { | |||
p.im = inColumnGroupIM | |||
case a.Table: | |||
p.im = inTableIM | |||
case a.Template: | |||
// TODO: remove this divergence from the HTML5 spec. | |||
if n.Namespace != "" { | |||
continue | |||
} | |||
p.im = p.templateStack.top() | |||
case a.Head: | |||
p.im = inBodyIM | |||
// TODO: remove this divergence from the HTML5 spec. | |||
// | |||
// See https://bugs.chromium.org/p/chromium/issues/detail?id=829668 | |||
p.im = inHeadIM | |||
case a.Body: | |||
p.im = inBodyIM | |||
case a.Frameset: | |||
p.im = inFramesetIM | |||
case a.Html: | |||
p.im = beforeHeadIM | |||
if p.head == nil { | |||
p.im = beforeHeadIM | |||
} else { | |||
p.im = afterHeadIM | |||
} | |||
default: | |||
if last { | |||
p.im = inBodyIM | |||
return | |||
} | |||
continue | |||
} | |||
return | |||
} | |||
p.im = inBodyIM | |||
} | |||
const whitespace = " \t\r\n\f" | |||
// Section 12.2.5.4.1. | |||
// Section 12.2.6.4.1. | |||
func initialIM(p *parser) bool { | |||
switch p.tok.Type { | |||
case TextToken: | |||
@@ -479,7 +530,7 @@ func initialIM(p *parser) bool { | |||
return false | |||
} | |||
// Section 12.2.5.4.2. | |||
// Section 12.2.6.4.2. | |||
func beforeHTMLIM(p *parser) bool { | |||
switch p.tok.Type { | |||
case DoctypeToken: | |||
@@ -517,7 +568,7 @@ func beforeHTMLIM(p *parser) bool { | |||
return false | |||
} | |||
// Section 12.2.5.4.3. | |||
// Section 12.2.6.4.3. | |||
func beforeHeadIM(p *parser) bool { | |||
switch p.tok.Type { | |||
case TextToken: | |||
@@ -560,7 +611,7 @@ func beforeHeadIM(p *parser) bool { | |||
return false | |||
} | |||
// Section 12.2.5.4.4. | |||
// Section 12.2.6.4.4. | |||
func inHeadIM(p *parser) bool { | |||
switch p.tok.Type { | |||
case TextToken: | |||
@@ -590,19 +641,41 @@ func inHeadIM(p *parser) bool { | |||
case a.Head: | |||
// Ignore the token. | |||
return true | |||
case a.Template: | |||
p.addElement() | |||
p.afe = append(p.afe, &scopeMarker) | |||
p.framesetOK = false | |||
p.im = inTemplateIM | |||
p.templateStack = append(p.templateStack, inTemplateIM) | |||
return true | |||
} | |||
case EndTagToken: | |||
switch p.tok.DataAtom { | |||
case a.Head: | |||
n := p.oe.pop() | |||
if n.DataAtom != a.Head { | |||
panic("html: bad parser state: <head> element not found, in the in-head insertion mode") | |||
} | |||
p.oe.pop() | |||
p.im = afterHeadIM | |||
return true | |||
case a.Body, a.Html, a.Br: | |||
p.parseImpliedToken(EndTagToken, a.Head, a.Head.String()) | |||
return false | |||
case a.Template: | |||
if !p.oe.contains(a.Template) { | |||
return true | |||
} | |||
// TODO: remove this divergence from the HTML5 spec. | |||
// | |||
// See https://bugs.chromium.org/p/chromium/issues/detail?id=829668 | |||
p.generateImpliedEndTags() | |||
for i := len(p.oe) - 1; i >= 0; i-- { | |||
if n := p.oe[i]; n.Namespace == "" && n.DataAtom == a.Template { | |||
p.oe = p.oe[:i] | |||
break | |||
} | |||
} | |||
p.clearActiveFormattingElements() | |||
p.templateStack.pop() | |||
p.resetInsertionMode() | |||
return true | |||
default: | |||
// Ignore the token. | |||
return true | |||
@@ -622,7 +695,7 @@ func inHeadIM(p *parser) bool { | |||
return false | |||
} | |||
// Section 12.2.5.4.6. | |||
// Section 12.2.6.4.6. | |||
func afterHeadIM(p *parser) bool { | |||
switch p.tok.Type { | |||
case TextToken: | |||
@@ -648,7 +721,7 @@ func afterHeadIM(p *parser) bool { | |||
p.addElement() | |||
p.im = inFramesetIM | |||
return true | |||
case a.Base, a.Basefont, a.Bgsound, a.Link, a.Meta, a.Noframes, a.Script, a.Style, a.Title: | |||
case a.Base, a.Basefont, a.Bgsound, a.Link, a.Meta, a.Noframes, a.Script, a.Style, a.Template, a.Title: | |||
p.oe = append(p.oe, p.head) | |||
defer p.oe.remove(p.head) | |||
return inHeadIM(p) | |||
@@ -660,6 +733,8 @@ func afterHeadIM(p *parser) bool { | |||
switch p.tok.DataAtom { | |||
case a.Body, a.Html, a.Br: | |||
// Drop down to creating an implied <body> tag. | |||
case a.Template: | |||
return inHeadIM(p) | |||
default: | |||
// Ignore the token. | |||
return true | |||
@@ -697,7 +772,7 @@ func copyAttributes(dst *Node, src Token) { | |||
} | |||
} | |||
// Section 12.2.5.4.7. | |||
// Section 12.2.6.4.7. | |||
func inBodyIM(p *parser) bool { | |||
switch p.tok.Type { | |||
case TextToken: | |||
@@ -727,10 +802,16 @@ func inBodyIM(p *parser) bool { | |||
case StartTagToken: | |||
switch p.tok.DataAtom { | |||
case a.Html: | |||
if p.oe.contains(a.Template) { | |||
return true | |||
} | |||
copyAttributes(p.oe[0], p.tok) | |||
case a.Base, a.Basefont, a.Bgsound, a.Command, a.Link, a.Meta, a.Noframes, a.Script, a.Style, a.Title: | |||
case a.Base, a.Basefont, a.Bgsound, a.Command, a.Link, a.Meta, a.Noframes, a.Script, a.Style, a.Template, a.Title: | |||
return inHeadIM(p) | |||
case a.Body: | |||
if p.oe.contains(a.Template) { | |||
return true | |||
} | |||
if len(p.oe) >= 2 { | |||
body := p.oe[1] | |||
if body.Type == ElementNode && body.DataAtom == a.Body { | |||
@@ -767,9 +848,13 @@ func inBodyIM(p *parser) bool { | |||
// The newline, if any, will be dealt with by the TextToken case. | |||
p.framesetOK = false | |||
case a.Form: | |||
if p.form == nil { | |||
p.popUntil(buttonScope, a.P) | |||
p.addElement() | |||
if p.form != nil && !p.oe.contains(a.Template) { | |||
// Ignore the token | |||
return true | |||
} | |||
p.popUntil(buttonScope, a.P) | |||
p.addElement() | |||
if !p.oe.contains(a.Template) { | |||
p.form = p.top() | |||
} | |||
case a.Li: | |||
@@ -903,6 +988,14 @@ func inBodyIM(p *parser) bool { | |||
p.acknowledgeSelfClosingTag() | |||
p.popUntil(buttonScope, a.P) | |||
p.parseImpliedToken(StartTagToken, a.Form, a.Form.String()) | |||
if p.form == nil { | |||
// NOTE: The 'isindex' element has been removed, | |||
// and the 'template' element has not been designed to be | |||
// collaborative with the index element. | |||
// | |||
// Ignore the token. | |||
return true | |||
} | |||
if action != "" { | |||
p.form.Attr = []Attribute{{Key: "action", Val: action}} | |||
} | |||
@@ -952,11 +1045,16 @@ func inBodyIM(p *parser) bool { | |||
} | |||
p.reconstructActiveFormattingElements() | |||
p.addElement() | |||
case a.Rp, a.Rt: | |||
case a.Rb, a.Rtc: | |||
if p.elementInScope(defaultScope, a.Ruby) { | |||
p.generateImpliedEndTags() | |||
} | |||
p.addElement() | |||
case a.Rp, a.Rt: | |||
if p.elementInScope(defaultScope, a.Ruby) { | |||
p.generateImpliedEndTags("rtc") | |||
} | |||
p.addElement() | |||
case a.Math, a.Svg: | |||
p.reconstructActiveFormattingElements() | |||
if p.tok.DataAtom == a.Math { | |||
@@ -993,15 +1091,29 @@ func inBodyIM(p *parser) bool { | |||
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: | |||
p.popUntil(defaultScope, p.tok.DataAtom) | |||
case a.Form: | |||
node := p.form | |||
p.form = nil | |||
i := p.indexOfElementInScope(defaultScope, a.Form) | |||
if node == nil || i == -1 || p.oe[i] != node { | |||
// Ignore the token. | |||
return true | |||
if p.oe.contains(a.Template) { | |||
i := p.indexOfElementInScope(defaultScope, a.Form) | |||
if i == -1 { | |||
// Ignore the token. | |||
return true | |||
} | |||
p.generateImpliedEndTags() | |||
if p.oe[i].DataAtom != a.Form { | |||
// Ignore the token. | |||
return true | |||
} | |||
p.popUntil(defaultScope, a.Form) | |||
} else { | |||
node := p.form | |||
p.form = nil | |||
i := p.indexOfElementInScope(defaultScope, a.Form) | |||
if node == nil || i == -1 || p.oe[i] != node { | |||
// Ignore the token. | |||
return true | |||
} | |||
p.generateImpliedEndTags() | |||
p.oe.remove(node) | |||
} | |||
p.generateImpliedEndTags() | |||
p.oe.remove(node) | |||
case a.P: | |||
if !p.elementInScope(buttonScope, a.P) { | |||
p.parseImpliedToken(StartTagToken, a.P, a.P.String()) | |||
@@ -1022,6 +1134,8 @@ func inBodyIM(p *parser) bool { | |||
case a.Br: | |||
p.tok.Type = StartTagToken | |||
return false | |||
case a.Template: | |||
return inHeadIM(p) | |||
default: | |||
p.inBodyEndTagOther(p.tok.DataAtom) | |||
} | |||
@@ -1030,6 +1144,21 @@ func inBodyIM(p *parser) bool { | |||
Type: CommentNode, | |||
Data: p.tok.Data, | |||
}) | |||
case ErrorToken: | |||
// TODO: remove this divergence from the HTML5 spec. | |||
if len(p.templateStack) > 0 { | |||
p.im = inTemplateIM | |||
return false | |||
} else { | |||
for _, e := range p.oe { | |||
switch e.DataAtom { | |||
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, | |||
a.Thead, a.Tr, a.Body, a.Html: | |||
default: | |||
return true | |||
} | |||
} | |||
} | |||
} | |||
return true | |||
@@ -1160,7 +1289,7 @@ func (p *parser) inBodyEndTagFormatting(tagAtom a.Atom) { | |||
} | |||
// inBodyEndTagOther performs the "any other end tag" algorithm for inBodyIM. | |||
// "Any other end tag" handling from 12.2.5.5 The rules for parsing tokens in foreign content | |||
// "Any other end tag" handling from 12.2.6.5 The rules for parsing tokens in foreign content | |||
// https://html.spec.whatwg.org/multipage/syntax.html#parsing-main-inforeign | |||
func (p *parser) inBodyEndTagOther(tagAtom a.Atom) { | |||
for i := len(p.oe) - 1; i >= 0; i-- { | |||
@@ -1174,7 +1303,7 @@ func (p *parser) inBodyEndTagOther(tagAtom a.Atom) { | |||
} | |||
} | |||
// Section 12.2.5.4.8. | |||
// Section 12.2.6.4.8. | |||
func textIM(p *parser) bool { | |||
switch p.tok.Type { | |||
case ErrorToken: | |||
@@ -1203,12 +1332,9 @@ func textIM(p *parser) bool { | |||
return p.tok.Type == EndTagToken | |||
} | |||
// Section 12.2.5.4.9. | |||
// Section 12.2.6.4.9. | |||
func inTableIM(p *parser) bool { | |||
switch p.tok.Type { | |||
case ErrorToken: | |||
// Stop parsing. | |||
return true | |||
case TextToken: | |||
p.tok.Data = strings.Replace(p.tok.Data, "\x00", "", -1) | |||
switch p.oe.top().DataAtom { | |||
@@ -1249,7 +1375,7 @@ func inTableIM(p *parser) bool { | |||
} | |||
// Ignore the token. | |||
return true | |||
case a.Style, a.Script: | |||
case a.Style, a.Script, a.Template: | |||
return inHeadIM(p) | |||
case a.Input: | |||
for _, t := range p.tok.Attr { | |||
@@ -1261,7 +1387,7 @@ func inTableIM(p *parser) bool { | |||
} | |||
// Otherwise drop down to the default action. | |||
case a.Form: | |||
if p.form != nil { | |||
if p.oe.contains(a.Template) || p.form != nil { | |||
// Ignore the token. | |||
return true | |||
} | |||
@@ -1291,6 +1417,8 @@ func inTableIM(p *parser) bool { | |||
case a.Body, a.Caption, a.Col, a.Colgroup, a.Html, a.Tbody, a.Td, a.Tfoot, a.Th, a.Thead, a.Tr: | |||
// Ignore the token. | |||
return true | |||
case a.Template: | |||
return inHeadIM(p) | |||
} | |||
case CommentToken: | |||
p.addChild(&Node{ | |||
@@ -1301,6 +1429,8 @@ func inTableIM(p *parser) bool { | |||
case DoctypeToken: | |||
// Ignore the token. | |||
return true | |||
case ErrorToken: | |||
return inBodyIM(p) | |||
} | |||
p.fosterParenting = true | |||
@@ -1309,7 +1439,7 @@ func inTableIM(p *parser) bool { | |||
return inBodyIM(p) | |||
} | |||
// Section 12.2.5.4.11. | |||
// Section 12.2.6.4.11. | |||
func inCaptionIM(p *parser) bool { | |||
switch p.tok.Type { | |||
case StartTagToken: | |||
@@ -1355,7 +1485,7 @@ func inCaptionIM(p *parser) bool { | |||
return inBodyIM(p) | |||
} | |||
// Section 12.2.5.4.12. | |||
// Section 12.2.6.4.12. | |||
func inColumnGroupIM(p *parser) bool { | |||
switch p.tok.Type { | |||
case TextToken: | |||
@@ -1386,11 +1516,13 @@ func inColumnGroupIM(p *parser) bool { | |||
p.oe.pop() | |||
p.acknowledgeSelfClosingTag() | |||
return true | |||
case a.Template: | |||
return inHeadIM(p) | |||
} | |||
case EndTagToken: | |||
switch p.tok.DataAtom { | |||
case a.Colgroup: | |||
if p.oe.top().DataAtom != a.Html { | |||
if p.oe.top().DataAtom == a.Colgroup { | |||
p.oe.pop() | |||
p.im = inTableIM | |||
} | |||
@@ -1398,17 +1530,21 @@ func inColumnGroupIM(p *parser) bool { | |||
case a.Col: | |||
// Ignore the token. | |||
return true | |||
case a.Template: | |||
return inHeadIM(p) | |||
} | |||
case ErrorToken: | |||
return inBodyIM(p) | |||
} | |||
if p.oe.top().DataAtom != a.Html { | |||
p.oe.pop() | |||
p.im = inTableIM | |||
return false | |||
if p.oe.top().DataAtom != a.Colgroup { | |||
return true | |||
} | |||
return true | |||
p.oe.pop() | |||
p.im = inTableIM | |||
return false | |||
} | |||
// Section 12.2.5.4.13. | |||
// Section 12.2.6.4.13. | |||
func inTableBodyIM(p *parser) bool { | |||
switch p.tok.Type { | |||
case StartTagToken: | |||
@@ -1460,7 +1596,7 @@ func inTableBodyIM(p *parser) bool { | |||
return inTableIM(p) | |||
} | |||
// Section 12.2.5.4.14. | |||
// Section 12.2.6.4.14. | |||
func inRowIM(p *parser) bool { | |||
switch p.tok.Type { | |||
case StartTagToken: | |||
@@ -1511,7 +1647,7 @@ func inRowIM(p *parser) bool { | |||
return inTableIM(p) | |||
} | |||
// Section 12.2.5.4.15. | |||
// Section 12.2.6.4.15. | |||
func inCellIM(p *parser) bool { | |||
switch p.tok.Type { | |||
case StartTagToken: | |||
@@ -1560,12 +1696,9 @@ func inCellIM(p *parser) bool { | |||
return inBodyIM(p) | |||
} | |||
// Section 12.2.5.4.16. | |||
// Section 12.2.6.4.16. | |||
func inSelectIM(p *parser) bool { | |||
switch p.tok.Type { | |||
case ErrorToken: | |||
// Stop parsing. | |||
return true | |||
case TextToken: | |||
p.addText(strings.Replace(p.tok.Data, "\x00", "", -1)) | |||
case StartTagToken: | |||
@@ -1597,7 +1730,7 @@ func inSelectIM(p *parser) bool { | |||
p.tokenizer.NextIsNotRawText() | |||
// Ignore the token. | |||
return true | |||
case a.Script: | |||
case a.Script, a.Template: | |||
return inHeadIM(p) | |||
} | |||
case EndTagToken: | |||
@@ -1618,6 +1751,8 @@ func inSelectIM(p *parser) bool { | |||
if p.popUntil(selectScope, a.Select) { | |||
p.resetInsertionMode() | |||
} | |||
case a.Template: | |||
return inHeadIM(p) | |||
} | |||
case CommentToken: | |||
p.addChild(&Node{ | |||
@@ -1627,12 +1762,14 @@ func inSelectIM(p *parser) bool { | |||
case DoctypeToken: | |||
// Ignore the token. | |||
return true | |||
case ErrorToken: | |||
return inBodyIM(p) | |||
} | |||
return true | |||
} | |||
// Section 12.2.5.4.17. | |||
// Section 12.2.6.4.17. | |||
func inSelectInTableIM(p *parser) bool { | |||
switch p.tok.Type { | |||
case StartTagToken, EndTagToken: | |||
@@ -1650,7 +1787,73 @@ func inSelectInTableIM(p *parser) bool { | |||
return inSelectIM(p) | |||
} | |||
// Section 12.2.5.4.18. | |||
// Section 12.2.6.4.18. | |||
func inTemplateIM(p *parser) bool { | |||
switch p.tok.Type { | |||
case TextToken, CommentToken, DoctypeToken: | |||
return inBodyIM(p) | |||
case StartTagToken: | |||
switch p.tok.DataAtom { | |||
case a.Base, a.Basefont, a.Bgsound, a.Link, a.Meta, a.Noframes, a.Script, a.Style, a.Template, a.Title: | |||
return inHeadIM(p) | |||
case a.Caption, a.Colgroup, a.Tbody, a.Tfoot, a.Thead: | |||
p.templateStack.pop() | |||
p.templateStack = append(p.templateStack, inTableIM) | |||
p.im = inTableIM | |||
return false | |||
case a.Col: | |||
p.templateStack.pop() | |||
p.templateStack = append(p.templateStack, inColumnGroupIM) | |||
p.im = inColumnGroupIM | |||
return false | |||
case a.Tr: | |||
p.templateStack.pop() | |||
p.templateStack = append(p.templateStack, inTableBodyIM) | |||
p.im = inTableBodyIM | |||
return false | |||
case a.Td, a.Th: | |||
p.templateStack.pop() | |||
p.templateStack = append(p.templateStack, inRowIM) | |||
p.im = inRowIM | |||
return false | |||
default: | |||
p.templateStack.pop() | |||
p.templateStack = append(p.templateStack, inBodyIM) | |||
p.im = inBodyIM | |||
return false | |||
} | |||
case EndTagToken: | |||
switch p.tok.DataAtom { | |||
case a.Template: | |||
return inHeadIM(p) | |||
default: | |||
// Ignore the token. | |||
return true | |||
} | |||
case ErrorToken: | |||
if !p.oe.contains(a.Template) { | |||
// Ignore the token. | |||
return true | |||
} | |||
// TODO: remove this divergence from the HTML5 spec. | |||
// | |||
// See https://bugs.chromium.org/p/chromium/issues/detail?id=829668 | |||
p.generateImpliedEndTags() | |||
for i := len(p.oe) - 1; i >= 0; i-- { | |||
if n := p.oe[i]; n.Namespace == "" && n.DataAtom == a.Template { | |||
p.oe = p.oe[:i] | |||
break | |||
} | |||
} | |||
p.clearActiveFormattingElements() | |||
p.templateStack.pop() | |||
p.resetInsertionMode() | |||
return false | |||
} | |||
return false | |||
} | |||
// Section 12.2.6.4.19. | |||
func afterBodyIM(p *parser) bool { | |||
switch p.tok.Type { | |||
case ErrorToken: | |||
@@ -1688,7 +1891,7 @@ func afterBodyIM(p *parser) bool { | |||
return false | |||
} | |||
// Section 12.2.5.4.19. | |||
// Section 12.2.6.4.20. | |||
func inFramesetIM(p *parser) bool { | |||
switch p.tok.Type { | |||
case CommentToken: | |||
@@ -1738,7 +1941,7 @@ func inFramesetIM(p *parser) bool { | |||
return true | |||
} | |||
// Section 12.2.5.4.20. | |||
// Section 12.2.6.4.21. | |||
func afterFramesetIM(p *parser) bool { | |||
switch p.tok.Type { | |||
case CommentToken: | |||
@@ -1777,7 +1980,7 @@ func afterFramesetIM(p *parser) bool { | |||
return true | |||
} | |||
// Section 12.2.5.4.21. | |||
// Section 12.2.6.4.22. | |||
func afterAfterBodyIM(p *parser) bool { | |||
switch p.tok.Type { | |||
case ErrorToken: | |||
@@ -1806,7 +2009,7 @@ func afterAfterBodyIM(p *parser) bool { | |||
return false | |||
} | |||
// Section 12.2.5.4.22. | |||
// Section 12.2.6.4.23. | |||
func afterAfterFramesetIM(p *parser) bool { | |||
switch p.tok.Type { | |||
case CommentToken: | |||
@@ -1844,7 +2047,7 @@ func afterAfterFramesetIM(p *parser) bool { | |||
const whitespaceOrNUL = whitespace + "\x00" | |||
// Section 12.2.5.5. | |||
// Section 12.2.6.5 | |||
func parseForeignContent(p *parser) bool { | |||
switch p.tok.Type { | |||
case TextToken: | |||
@@ -1924,7 +2127,7 @@ func parseForeignContent(p *parser) bool { | |||
return true | |||
} | |||
// Section 12.2.5. | |||
// Section 12.2.6. | |||
func (p *parser) inForeignContent() bool { | |||
if len(p.oe) == 0 { | |||
return false | |||
@@ -2012,6 +2215,15 @@ func (p *parser) parse() error { | |||
} | |||
// Parse returns the parse tree for the HTML from the given Reader. | |||
// | |||
// It implements the HTML5 parsing algorithm | |||
// (https://html.spec.whatwg.org/multipage/syntax.html#tree-construction), | |||
// which is very complicated. The resultant tree can contain implicitly created | |||
// nodes that have no explicit <tag> listed in r's data, and nodes' parents can | |||
// differ from the nesting implied by a naive processing of start and end | |||
// <tag>s. Conversely, explicit <tag>s in r's data can be silently dropped, | |||
// with no corresponding node in the resulting tree. | |||
// | |||
// The input is assumed to be UTF-8 encoded. | |||
func Parse(r io.Reader) (*Node, error) { | |||
p := &parser{ | |||
@@ -2033,6 +2245,8 @@ func Parse(r io.Reader) (*Node, error) { | |||
// ParseFragment parses a fragment of HTML and returns the nodes that were | |||
// found. If the fragment is the InnerHTML for an existing element, pass that | |||
// element in context. | |||
// | |||
// It has the same intricacies as Parse. | |||
func ParseFragment(r io.Reader, context *Node) ([]*Node, error) { | |||
contextTag := "" | |||
if context != nil { | |||
@@ -2064,6 +2278,9 @@ func ParseFragment(r io.Reader, context *Node) ([]*Node, error) { | |||
} | |||
p.doc.AppendChild(root) | |||
p.oe = nodeStack{root} | |||
if context != nil && context.DataAtom == a.Template { | |||
p.templateStack = append(p.templateStack, inTemplateIM) | |||
} | |||
p.resetInsertionMode() | |||
for n := context; n != nil; n = n.Parent { |
@@ -1161,8 +1161,8 @@ func (z *Tokenizer) TagAttr() (key, val []byte, moreAttr bool) { | |||
return nil, nil, false | |||
} | |||
// Token returns the next Token. The result's Data and Attr values remain valid | |||
// after subsequent Next calls. | |||
// Token returns the current Token. The result's Data and Attr values remain | |||
// valid after subsequent Next calls. | |||
func (z *Tokenizer) Token() Token { | |||
t := Token{Type: z.tt} | |||
switch z.tt { |