| @@ -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 { | |||