| @@ -746,7 +746,8 @@ | |||||
| revision = "12dd70caea0268ac0d6c2707d0611ef601e7c64e" | revision = "12dd70caea0268ac0d6c2707d0611ef601e7c64e" | ||||
| [[projects]] | [[projects]] | ||||
| digest = "1:47ea747d07fae720d749d06ac5dc5ded0df70c57e328b6549cf2d9c64698757e" | |||||
| branch = "master" | |||||
| digest = "1:6d5ed712653ea5321fe3e3475ab2188cf362a4e0d31e9fd3acbd4dfbbca0d680" | |||||
| name = "golang.org/x/net" | name = "golang.org/x/net" | ||||
| packages = [ | packages = [ | ||||
| "context", | "context", | ||||
| @@ -755,7 +756,7 @@ | |||||
| "html/charset", | "html/charset", | ||||
| ] | ] | ||||
| pruneopts = "NUT" | pruneopts = "NUT" | ||||
| revision = "f2499483f923065a842d38eb4c7f1927e6fc6e6d" | |||||
| revision = "9b4f9f5ad5197c79fd623a3638e70d8b26cef344" | |||||
| [[projects]] | [[projects]] | ||||
| digest = "1:8159a9cda4b8810aaaeb0d60e2fa68e2fd86d8af4ec8f5059830839e3c8d93d5" | digest = "1:8159a9cda4b8810aaaeb0d60e2fa68e2fd86d8af4ec8f5059830839e3c8d93d5" | ||||
| @@ -27,7 +27,7 @@ ignored = ["google.golang.org/appengine*"] | |||||
| name = "golang.org/x/text" | name = "golang.org/x/text" | ||||
| [[constraint]] | [[constraint]] | ||||
| revision = "f2499483f923065a842d38eb4c7f1927e6fc6e6d" | |||||
| branch = "master" | |||||
| name = "golang.org/x/net" | name = "golang.org/x/net" | ||||
| [[override]] | [[override]] | ||||
| @@ -5,9 +5,11 @@ | |||||
| // Package context defines the Context type, which carries deadlines, | // Package context defines the Context type, which carries deadlines, | ||||
| // cancelation signals, and other request-scoped values across API boundaries | // cancelation signals, and other request-scoped values across API boundaries | ||||
| // and between processes. | // 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 | // 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 | // propagate the Context, optionally replacing it with a modified copy created | ||||
| // using WithDeadline, WithTimeout, WithCancel, or WithValue. | // using WithDeadline, WithTimeout, WithCancel, or WithValue. | ||||
| // | // | ||||
| @@ -16,14 +18,14 @@ | |||||
| // propagation: | // propagation: | ||||
| // | // | ||||
| // Do not store Contexts inside a struct type; instead, pass a Context | // 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: | // parameter, typically named ctx: | ||||
| // | // | ||||
| // func DoSomething(ctx context.Context, arg Arg) error { | // func DoSomething(ctx context.Context, arg Arg) error { | ||||
| // // ... use ctx ... | // // ... 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. | // if you are unsure about which Context to use. | ||||
| // | // | ||||
| // Use context Values only for request-scoped data that transits processes and | // Use context Values only for request-scoped data that transits processes and | ||||
| @@ -36,112 +38,15 @@ | |||||
| // Contexts. | // Contexts. | ||||
| package context // import "golang.org/x/net/context" | 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 | // 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 | // initialization, and tests, and as the top-level Context for incoming | ||||
| // requests. | // requests. | ||||
| func Background() Context { | func Background() Context { | ||||
| return background | 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 | // 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 | // surrounding function has not yet been extended to accept a Context | ||||
| // parameter). TODO is recognized by static analysis tools that determine | // parameter). TODO is recognized by static analysis tools that determine | ||||
| @@ -149,8 +54,3 @@ func Background() Context { | |||||
| func TODO() Context { | func TODO() Context { | ||||
| return todo | 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 | // 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 | // 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 | // cancel function is called, or when the parent context's Done channel is | ||||
| // closed, whichever happens first. | // 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" | "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. | // struct{}, since vars of this type must have distinct addresses. | ||||
| type emptyCtx int | type emptyCtx int | ||||
| @@ -104,7 +104,7 @@ func propagateCancel(parent Context, child canceler) { | |||||
| } | } | ||||
| // parentCancelCtx follows a chain of parent references until it finds a | // 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. | // package represents its parent. | ||||
| func parentCancelCtx(parent Context) (*cancelCtx, bool) { | func parentCancelCtx(parent Context) (*cancelCtx, bool) { | ||||
| for { | for { | ||||
| @@ -134,14 +134,14 @@ func removeChild(parent Context, child canceler) { | |||||
| p.mu.Unlock() | 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. | // implementations are *cancelCtx and *timerCtx. | ||||
| type canceler interface { | type canceler interface { | ||||
| cancel(removeFromParent bool, err error) | cancel(removeFromParent bool, err error) | ||||
| Done() <-chan struct{} | 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. | // that implement canceler. | ||||
| type cancelCtx struct { | type cancelCtx struct { | ||||
| Context | 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 | // 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 | // 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 | // cancel function is called, or when the parent context's Done channel is | ||||
| // closed, whichever happens first. | // closed, whichever happens first. | ||||
| @@ -226,8 +226,8 @@ func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc) { | |||||
| return c, func() { c.cancel(true, Canceled) } | 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. | // delegating to cancelCtx.cancel. | ||||
| type timerCtx struct { | type timerCtx struct { | ||||
| *cancelCtx | *cancelCtx | ||||
| @@ -281,7 +281,7 @@ func WithValue(parent Context, key interface{}, val interface{}) Context { | |||||
| return &valueCtx{parent, key, val} | 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. | // delegates all other calls to the embedded Context. | ||||
| type valueCtx struct { | type valueCtx struct { | ||||
| Context | 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 | // +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 ( | import ( | ||||
| "bytes" | |||||
| "flag" | "flag" | ||||
| "fmt" | "fmt" | ||||
| "go/format" | |||||
| "io/ioutil" | |||||
| "math/rand" | "math/rand" | ||||
| "os" | "os" | ||||
| "sort" | "sort" | ||||
| @@ -42,6 +42,18 @@ func identifier(s string) string { | |||||
| var test = flag.Bool("test", false, "generate table_test.go") | 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() { | func main() { | ||||
| flag.Parse() | flag.Parse() | ||||
| @@ -52,32 +64,31 @@ func main() { | |||||
| all = append(all, extra...) | all = append(all, extra...) | ||||
| sort.Strings(all) | 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 | // uniq - lists have dups | ||||
| // compute max len too | |||||
| maxLen := 0 | |||||
| w := 0 | w := 0 | ||||
| for _, s := range all { | for _, s := range all { | ||||
| if w == 0 || all[w-1] != s { | if w == 0 || all[w-1] != s { | ||||
| if maxLen < len(s) { | |||||
| maxLen = len(s) | |||||
| } | |||||
| all[w] = s | all[w] = s | ||||
| w++ | w++ | ||||
| } | } | ||||
| } | } | ||||
| all = all[: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. | // Find hash that minimizes table size. | ||||
| var best *table | var best *table | ||||
| for i := 0; i < 1000000; i++ { | for i := 0; i < 1000000; i++ { | ||||
| @@ -163,36 +174,46 @@ func main() { | |||||
| atom[s] = uint32(off<<8 | len(s)) | atom[s] = uint32(off<<8 | len(s)) | ||||
| } | } | ||||
| var buf bytes.Buffer | |||||
| // Generate the Go code. | // 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 { | 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 { | for i, s := range best.tab { | ||||
| if s == "" { | if s == "" { | ||||
| continue | 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 | datasize := (1 << best.k) * 4 | ||||
| fmt.Printf("const atomText =\n") | |||||
| fmt.Fprintln(&buf, "const atomText =") | |||||
| textsize := len(text) | textsize := len(text) | ||||
| for len(text) > 60 { | for len(text) > 60 { | ||||
| fmt.Printf("\t%q +\n", text[:60]) | |||||
| fmt.Fprintf(&buf, "\t%q +\n", text[:60]) | |||||
| text = 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 | 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 | // The lists of element names and attribute keys were taken from | ||||
| // https://html.spec.whatwg.org/multipage/indices.html#index | // 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{ | var elements = []string{ | ||||
| "a", | "a", | ||||
| "abbr", | "abbr", | ||||
| @@ -349,6 +372,7 @@ var elements = []string{ | |||||
| "legend", | "legend", | ||||
| "li", | "li", | ||||
| "link", | "link", | ||||
| "main", | |||||
| "map", | "map", | ||||
| "mark", | "mark", | ||||
| "menu", | "menu", | ||||
| @@ -364,6 +388,7 @@ var elements = []string{ | |||||
| "output", | "output", | ||||
| "p", | "p", | ||||
| "param", | "param", | ||||
| "picture", | |||||
| "pre", | "pre", | ||||
| "progress", | "progress", | ||||
| "q", | "q", | ||||
| @@ -375,6 +400,7 @@ var elements = []string{ | |||||
| "script", | "script", | ||||
| "section", | "section", | ||||
| "select", | "select", | ||||
| "slot", | |||||
| "small", | "small", | ||||
| "source", | "source", | ||||
| "span", | "span", | ||||
| @@ -403,14 +429,21 @@ var elements = []string{ | |||||
| } | } | ||||
| // https://html.spec.whatwg.org/multipage/indices.html#attributes-3 | // 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{ | var attributes = []string{ | ||||
| "abbr", | "abbr", | ||||
| "accept", | "accept", | ||||
| "accept-charset", | "accept-charset", | ||||
| "accesskey", | "accesskey", | ||||
| "action", | "action", | ||||
| "allowfullscreen", | |||||
| "allowpaymentrequest", | |||||
| "allowusermedia", | |||||
| "alt", | "alt", | ||||
| "as", | |||||
| "async", | "async", | ||||
| "autocomplete", | "autocomplete", | ||||
| "autofocus", | "autofocus", | ||||
| @@ -420,6 +453,7 @@ var attributes = []string{ | |||||
| "checked", | "checked", | ||||
| "cite", | "cite", | ||||
| "class", | "class", | ||||
| "color", | |||||
| "cols", | "cols", | ||||
| "colspan", | "colspan", | ||||
| "command", | "command", | ||||
| @@ -457,6 +491,8 @@ var attributes = []string{ | |||||
| "icon", | "icon", | ||||
| "id", | "id", | ||||
| "inputmode", | "inputmode", | ||||
| "integrity", | |||||
| "is", | |||||
| "ismap", | "ismap", | ||||
| "itemid", | "itemid", | ||||
| "itemprop", | "itemprop", | ||||
| @@ -481,16 +517,20 @@ var attributes = []string{ | |||||
| "multiple", | "multiple", | ||||
| "muted", | "muted", | ||||
| "name", | "name", | ||||
| "nomodule", | |||||
| "nonce", | |||||
| "novalidate", | "novalidate", | ||||
| "open", | "open", | ||||
| "optimum", | "optimum", | ||||
| "pattern", | "pattern", | ||||
| "ping", | "ping", | ||||
| "placeholder", | "placeholder", | ||||
| "playsinline", | |||||
| "poster", | "poster", | ||||
| "preload", | "preload", | ||||
| "radiogroup", | "radiogroup", | ||||
| "readonly", | "readonly", | ||||
| "referrerpolicy", | |||||
| "rel", | "rel", | ||||
| "required", | "required", | ||||
| "reversed", | "reversed", | ||||
| @@ -507,10 +547,13 @@ var attributes = []string{ | |||||
| "sizes", | "sizes", | ||||
| "sortable", | "sortable", | ||||
| "sorted", | "sorted", | ||||
| "slot", | |||||
| "span", | "span", | ||||
| "spellcheck", | |||||
| "src", | "src", | ||||
| "srcdoc", | "srcdoc", | ||||
| "srclang", | "srclang", | ||||
| "srcset", | |||||
| "start", | "start", | ||||
| "step", | "step", | ||||
| "style", | "style", | ||||
| @@ -520,16 +563,22 @@ var attributes = []string{ | |||||
| "translate", | "translate", | ||||
| "type", | "type", | ||||
| "typemustmatch", | "typemustmatch", | ||||
| "updateviacache", | |||||
| "usemap", | "usemap", | ||||
| "value", | "value", | ||||
| "width", | "width", | ||||
| "workertype", | |||||
| "wrap", | "wrap", | ||||
| } | } | ||||
| // "onautocomplete", "onautocompleteerror", "onmousewheel", | |||||
| // "onshow" and "onsort" have been removed from the spec, | |||||
| // but are kept here for backwards compatibility. | |||||
| var eventHandlers = []string{ | var eventHandlers = []string{ | ||||
| "onabort", | "onabort", | ||||
| "onautocomplete", | "onautocomplete", | ||||
| "onautocompleteerror", | "onautocompleteerror", | ||||
| "onauxclick", | |||||
| "onafterprint", | "onafterprint", | ||||
| "onbeforeprint", | "onbeforeprint", | ||||
| "onbeforeunload", | "onbeforeunload", | ||||
| @@ -541,11 +590,14 @@ var eventHandlers = []string{ | |||||
| "onclick", | "onclick", | ||||
| "onclose", | "onclose", | ||||
| "oncontextmenu", | "oncontextmenu", | ||||
| "oncopy", | |||||
| "oncuechange", | "oncuechange", | ||||
| "oncut", | |||||
| "ondblclick", | "ondblclick", | ||||
| "ondrag", | "ondrag", | ||||
| "ondragend", | "ondragend", | ||||
| "ondragenter", | "ondragenter", | ||||
| "ondragexit", | |||||
| "ondragleave", | "ondragleave", | ||||
| "ondragover", | "ondragover", | ||||
| "ondragstart", | "ondragstart", | ||||
| @@ -565,18 +617,24 @@ var eventHandlers = []string{ | |||||
| "onload", | "onload", | ||||
| "onloadeddata", | "onloadeddata", | ||||
| "onloadedmetadata", | "onloadedmetadata", | ||||
| "onloadend", | |||||
| "onloadstart", | "onloadstart", | ||||
| "onmessage", | "onmessage", | ||||
| "onmessageerror", | |||||
| "onmousedown", | "onmousedown", | ||||
| "onmouseenter", | |||||
| "onmouseleave", | |||||
| "onmousemove", | "onmousemove", | ||||
| "onmouseout", | "onmouseout", | ||||
| "onmouseover", | "onmouseover", | ||||
| "onmouseup", | "onmouseup", | ||||
| "onmousewheel", | "onmousewheel", | ||||
| "onwheel", | |||||
| "onoffline", | "onoffline", | ||||
| "ononline", | "ononline", | ||||
| "onpagehide", | "onpagehide", | ||||
| "onpageshow", | "onpageshow", | ||||
| "onpaste", | |||||
| "onpause", | "onpause", | ||||
| "onplay", | "onplay", | ||||
| "onplaying", | "onplaying", | ||||
| @@ -585,7 +643,9 @@ var eventHandlers = []string{ | |||||
| "onratechange", | "onratechange", | ||||
| "onreset", | "onreset", | ||||
| "onresize", | "onresize", | ||||
| "onrejectionhandled", | |||||
| "onscroll", | "onscroll", | ||||
| "onsecuritypolicyviolation", | |||||
| "onseeked", | "onseeked", | ||||
| "onseeking", | "onseeking", | ||||
| "onselect", | "onselect", | ||||
| @@ -597,6 +657,7 @@ var eventHandlers = []string{ | |||||
| "onsuspend", | "onsuspend", | ||||
| "ontimeupdate", | "ontimeupdate", | ||||
| "ontoggle", | "ontoggle", | ||||
| "onunhandledrejection", | |||||
| "onunload", | "onunload", | ||||
| "onvolumechange", | "onvolumechange", | ||||
| "onwaiting", | "onwaiting", | ||||
| @@ -604,6 +665,7 @@ var eventHandlers = []string{ | |||||
| // extra are ad-hoc values not covered by any of the lists above. | // extra are ad-hoc values not covered by any of the lists above. | ||||
| var extra = []string{ | var extra = []string{ | ||||
| "acronym", | |||||
| "align", | "align", | ||||
| "annotation", | "annotation", | ||||
| "annotation-xml", | "annotation-xml", | ||||
| @@ -639,6 +701,8 @@ var extra = []string{ | |||||
| "plaintext", | "plaintext", | ||||
| "prompt", | "prompt", | ||||
| "public", | "public", | ||||
| "rb", | |||||
| "rtc", | |||||
| "spacer", | "spacer", | ||||
| "strike", | "strike", | ||||
| "svg", | "svg", | ||||
| @@ -4,7 +4,7 @@ | |||||
| package html | 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". | // have varying levels of special parsing rules". | ||||
| // https://html.spec.whatwg.org/multipage/syntax.html#the-stack-of-open-elements | // https://html.spec.whatwg.org/multipage/syntax.html#the-stack-of-open-elements | ||||
| var isSpecialElementMap = map[string]bool{ | var isSpecialElementMap = map[string]bool{ | ||||
| @@ -52,10 +52,12 @@ var isSpecialElementMap = map[string]bool{ | |||||
| "iframe": true, | "iframe": true, | ||||
| "img": true, | "img": true, | ||||
| "input": true, | "input": true, | ||||
| "isindex": true, | |||||
| "isindex": true, // The 'isindex' element has been removed, but keep it for backwards compatibility. | |||||
| "keygen": true, | |||||
| "li": true, | "li": true, | ||||
| "link": true, | "link": true, | ||||
| "listing": true, | "listing": true, | ||||
| "main": true, | |||||
| "marquee": true, | "marquee": true, | ||||
| "menu": true, | "menu": true, | ||||
| "meta": true, | "meta": true, | ||||
| @@ -95,8 +97,16 @@ func isSpecialElement(element *Node) bool { | |||||
| switch element.Namespace { | switch element.Namespace { | ||||
| case "", "html": | case "", "html": | ||||
| return isSpecialElementMap[element.Data] | return isSpecialElementMap[element.Data] | ||||
| case "math": | |||||
| switch element.Data { | |||||
| case "mi", "mo", "mn", "ms", "mtext", "annotation-xml": | |||||
| return true | |||||
| } | |||||
| case "svg": | case "svg": | ||||
| return element.Data == "foreignObject" | |||||
| switch element.Data { | |||||
| case "foreignObject", "desc", "title": | |||||
| return true | |||||
| } | |||||
| } | } | ||||
| return false | return false | ||||
| } | } | ||||
| @@ -49,18 +49,18 @@ call to Next. For example, to extract an HTML page's anchor text: | |||||
| for { | for { | ||||
| tt := z.Next() | tt := z.Next() | ||||
| switch tt { | switch tt { | ||||
| case ErrorToken: | |||||
| case html.ErrorToken: | |||||
| return z.Err() | return z.Err() | ||||
| case TextToken: | |||||
| case html.TextToken: | |||||
| if depth > 0 { | if depth > 0 { | ||||
| // emitBytes should copy the []byte it receives, | // emitBytes should copy the []byte it receives, | ||||
| // if it doesn't process it immediately. | // if it doesn't process it immediately. | ||||
| emitBytes(z.Text()) | emitBytes(z.Text()) | ||||
| } | } | ||||
| case StartTagToken, EndTagToken: | |||||
| case html.StartTagToken, html.EndTagToken: | |||||
| tn, _ := z.TagName() | tn, _ := z.TagName() | ||||
| if len(tn) == 1 && tn[0] == 'a' { | if len(tn) == 1 && tn[0] == 'a' { | ||||
| if tt == StartTagToken { | |||||
| if tt == html.StartTagToken { | |||||
| depth++ | depth++ | ||||
| } else { | } else { | ||||
| depth-- | depth-- | ||||
| @@ -67,7 +67,7 @@ func mathMLTextIntegrationPoint(n *Node) bool { | |||||
| return false | return false | ||||
| } | } | ||||
| // Section 12.2.5.5. | |||||
| // Section 12.2.6.5. | |||||
| var breakout = map[string]bool{ | var breakout = map[string]bool{ | ||||
| "b": true, | "b": true, | ||||
| "big": true, | "big": true, | ||||
| @@ -115,7 +115,7 @@ var breakout = map[string]bool{ | |||||
| "var": true, | "var": true, | ||||
| } | } | ||||
| // Section 12.2.5.5. | |||||
| // Section 12.2.6.5. | |||||
| var svgTagNameAdjustments = map[string]string{ | var svgTagNameAdjustments = map[string]string{ | ||||
| "altglyph": "altGlyph", | "altglyph": "altGlyph", | ||||
| "altglyphdef": "altGlyphDef", | "altglyphdef": "altGlyphDef", | ||||
| @@ -155,7 +155,7 @@ var svgTagNameAdjustments = map[string]string{ | |||||
| "textpath": "textPath", | "textpath": "textPath", | ||||
| } | } | ||||
| // Section 12.2.5.1 | |||||
| // Section 12.2.6.1 | |||||
| var mathMLAttributeAdjustments = map[string]string{ | var mathMLAttributeAdjustments = map[string]string{ | ||||
| "definitionurl": "definitionURL", | "definitionurl": "definitionURL", | ||||
| } | } | ||||
| @@ -21,9 +21,10 @@ const ( | |||||
| scopeMarkerNode | 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} | var scopeMarker = Node{Type: scopeMarkerNode} | ||||
| // A Node consists of a NodeType and some Data (tag name for element nodes, | // 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 | 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. | // insert inserts a node at the given index. | ||||
| func (s *nodeStack) insert(i int, n *Node) { | func (s *nodeStack) insert(i int, n *Node) { | ||||
| (*s) = append(*s, nil) | (*s) = append(*s, nil) | ||||
| @@ -191,3 +202,19 @@ func (s *nodeStack) remove(n *Node) { | |||||
| (*s)[j] = nil | (*s)[j] = nil | ||||
| *s = (*s)[:j] | *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 | hasSelfClosingToken bool | ||||
| // doc is the document root element. | // doc is the document root element. | ||||
| doc *Node | 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 | oe, afe nodeStack | ||||
| // Element pointers (section 12.2.3.4). | |||||
| // Element pointers (section 12.2.4.4). | |||||
| head, form *Node | head, form *Node | ||||
| // Other parsing state flags (section 12.2.3.5). | |||||
| // Other parsing state flags (section 12.2.4.5). | |||||
| scripting, framesetOK bool | scripting, framesetOK bool | ||||
| // The stack of template insertion modes | |||||
| templateStack insertionModeStack | |||||
| // im is the current insertion mode. | // im is the current insertion mode. | ||||
| im insertionMode | im insertionMode | ||||
| // originalIM is the insertion mode to go back to after completing a text | // originalIM is the insertion mode to go back to after completing a text | ||||
| // or inTableText insertion mode. | // or inTableText insertion mode. | ||||
| originalIM insertionMode | originalIM insertionMode | ||||
| // fosterParenting is whether new elements should be inserted according to | // 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 | fosterParenting bool | ||||
| // quirks is whether the parser is operating in "quirks mode." | // quirks is whether the parser is operating in "quirks mode." | ||||
| quirks bool | quirks bool | ||||
| @@ -56,7 +58,7 @@ func (p *parser) top() *Node { | |||||
| return p.doc | 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 ( | var ( | ||||
| defaultScopeStopTags = map[string][]a.Atom{ | defaultScopeStopTags = map[string][]a.Atom{ | ||||
| "": {a.Applet, a.Caption, a.Html, a.Table, a.Td, a.Th, a.Marquee, a.Object, a.Template}, | "": {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 | // 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 | // 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. | // 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 | // 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 | return -1 | ||||
| } | } | ||||
| case tableScope: | case tableScope: | ||||
| if tagAtom == a.Html || tagAtom == a.Table { | |||||
| if tagAtom == a.Html || tagAtom == a.Table || tagAtom == a.Template { | |||||
| return -1 | return -1 | ||||
| } | } | ||||
| case selectScope: | case selectScope: | ||||
| @@ -162,17 +164,17 @@ func (p *parser) clearStackToContext(s scope) { | |||||
| tagAtom := p.oe[i].DataAtom | tagAtom := p.oe[i].DataAtom | ||||
| switch s { | switch s { | ||||
| case tableScope: | 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] | p.oe = p.oe[:i+1] | ||||
| return | return | ||||
| } | } | ||||
| case tableRowScope: | 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] | p.oe = p.oe[:i+1] | ||||
| return | return | ||||
| } | } | ||||
| case tableBodyScope: | 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] | p.oe = p.oe[:i+1] | ||||
| return | return | ||||
| } | } | ||||
| @@ -183,7 +185,7 @@ func (p *parser) clearStackToContext(s scope) { | |||||
| } | } | ||||
| // generateImpliedEndTags pops nodes off the stack of open elements as long as | // 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. | // If exceptions are specified, nodes with that name will not be popped off. | ||||
| func (p *parser) generateImpliedEndTags(exceptions ...string) { | func (p *parser) generateImpliedEndTags(exceptions ...string) { | ||||
| var i int | var i int | ||||
| @@ -192,7 +194,7 @@ loop: | |||||
| n := p.oe[i] | n := p.oe[i] | ||||
| if n.Type == ElementNode { | if n.Type == ElementNode { | ||||
| switch n.DataAtom { | 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 { | for _, except := range exceptions { | ||||
| if n.Data == except { | if n.Data == except { | ||||
| break loop | break loop | ||||
| @@ -234,9 +236,9 @@ func (p *parser) shouldFosterParent() bool { | |||||
| } | } | ||||
| // fosterParent adds a child node according to the foster parenting rules. | // 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) { | func (p *parser) fosterParent(n *Node) { | ||||
| var table, parent, prev *Node | |||||
| var table, parent, prev, template *Node | |||||
| var i int | var i int | ||||
| for i = len(p.oe) - 1; i >= 0; i-- { | for i = len(p.oe) - 1; i >= 0; i-- { | ||||
| if p.oe[i].DataAtom == a.Table { | 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 { | if table == nil { | ||||
| // The foster parent is the html element. | // The foster parent is the html element. | ||||
| parent = p.oe[0] | 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() { | func (p *parser) addFormattingElement() { | ||||
| tagAtom, attr := p.tok.DataAtom, p.tok.Attr | tagAtom, attr := p.tok.DataAtom, p.tok.Attr | ||||
| p.addElement() | p.addElement() | ||||
| @@ -351,7 +366,7 @@ findIdenticalElements: | |||||
| p.afe = append(p.afe, p.top()) | p.afe = append(p.afe, p.top()) | ||||
| } | } | ||||
| // Section 12.2.3.3. | |||||
| // Section 12.2.4.3. | |||||
| func (p *parser) clearActiveFormattingElements() { | func (p *parser) clearActiveFormattingElements() { | ||||
| for { | for { | ||||
| n := p.afe.pop() | 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() { | func (p *parser) reconstructActiveFormattingElements() { | ||||
| n := p.afe.top() | n := p.afe.top() | ||||
| if n == nil { | if n == nil { | ||||
| @@ -390,12 +405,12 @@ func (p *parser) reconstructActiveFormattingElements() { | |||||
| } | } | ||||
| } | } | ||||
| // Section 12.2.4. | |||||
| // Section 12.2.5. | |||||
| func (p *parser) acknowledgeSelfClosingTag() { | func (p *parser) acknowledgeSelfClosingTag() { | ||||
| p.hasSelfClosingToken = false | 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 | // a particular state in the HTML5 parser's state machine. It updates the | ||||
| // parser's fields depending on parser.tok (where ErrorToken means EOF). | // parser's fields depending on parser.tok (where ErrorToken means EOF). | ||||
| // It returns whether the token was consumed. | // 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 | // setOriginalIM sets the insertion mode to return to after completing a text or | ||||
| // inTableText insertion mode. | // 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() { | func (p *parser) setOriginalIM() { | ||||
| if p.originalIM != nil { | if p.originalIM != nil { | ||||
| panic("html: bad parser state: originalIM was set twice") | panic("html: bad parser state: originalIM was set twice") | ||||
| @@ -411,18 +426,38 @@ func (p *parser) setOriginalIM() { | |||||
| p.originalIM = p.im | 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() { | func (p *parser) resetInsertionMode() { | ||||
| for i := len(p.oe) - 1; i >= 0; i-- { | for i := len(p.oe) - 1; i >= 0; i-- { | ||||
| n := p.oe[i] | n := p.oe[i] | ||||
| if i == 0 && p.context != nil { | |||||
| last := i == 0 | |||||
| if last && p.context != nil { | |||||
| n = p.context | n = p.context | ||||
| } | } | ||||
| switch n.DataAtom { | switch n.DataAtom { | ||||
| case a.Select: | 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 | p.im = inSelectIM | ||||
| case a.Td, a.Th: | 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 | p.im = inCellIM | ||||
| case a.Tr: | case a.Tr: | ||||
| p.im = inRowIM | p.im = inRowIM | ||||
| @@ -434,25 +469,41 @@ func (p *parser) resetInsertionMode() { | |||||
| p.im = inColumnGroupIM | p.im = inColumnGroupIM | ||||
| case a.Table: | case a.Table: | ||||
| p.im = inTableIM | 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: | 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: | case a.Body: | ||||
| p.im = inBodyIM | p.im = inBodyIM | ||||
| case a.Frameset: | case a.Frameset: | ||||
| p.im = inFramesetIM | p.im = inFramesetIM | ||||
| case a.Html: | case a.Html: | ||||
| p.im = beforeHeadIM | |||||
| if p.head == nil { | |||||
| p.im = beforeHeadIM | |||||
| } else { | |||||
| p.im = afterHeadIM | |||||
| } | |||||
| default: | default: | ||||
| if last { | |||||
| p.im = inBodyIM | |||||
| return | |||||
| } | |||||
| continue | continue | ||||
| } | } | ||||
| return | return | ||||
| } | } | ||||
| p.im = inBodyIM | |||||
| } | } | ||||
| const whitespace = " \t\r\n\f" | const whitespace = " \t\r\n\f" | ||||
| // Section 12.2.5.4.1. | |||||
| // Section 12.2.6.4.1. | |||||
| func initialIM(p *parser) bool { | func initialIM(p *parser) bool { | ||||
| switch p.tok.Type { | switch p.tok.Type { | ||||
| case TextToken: | case TextToken: | ||||
| @@ -479,7 +530,7 @@ func initialIM(p *parser) bool { | |||||
| return false | return false | ||||
| } | } | ||||
| // Section 12.2.5.4.2. | |||||
| // Section 12.2.6.4.2. | |||||
| func beforeHTMLIM(p *parser) bool { | func beforeHTMLIM(p *parser) bool { | ||||
| switch p.tok.Type { | switch p.tok.Type { | ||||
| case DoctypeToken: | case DoctypeToken: | ||||
| @@ -517,7 +568,7 @@ func beforeHTMLIM(p *parser) bool { | |||||
| return false | return false | ||||
| } | } | ||||
| // Section 12.2.5.4.3. | |||||
| // Section 12.2.6.4.3. | |||||
| func beforeHeadIM(p *parser) bool { | func beforeHeadIM(p *parser) bool { | ||||
| switch p.tok.Type { | switch p.tok.Type { | ||||
| case TextToken: | case TextToken: | ||||
| @@ -560,7 +611,7 @@ func beforeHeadIM(p *parser) bool { | |||||
| return false | return false | ||||
| } | } | ||||
| // Section 12.2.5.4.4. | |||||
| // Section 12.2.6.4.4. | |||||
| func inHeadIM(p *parser) bool { | func inHeadIM(p *parser) bool { | ||||
| switch p.tok.Type { | switch p.tok.Type { | ||||
| case TextToken: | case TextToken: | ||||
| @@ -590,19 +641,41 @@ func inHeadIM(p *parser) bool { | |||||
| case a.Head: | case a.Head: | ||||
| // Ignore the token. | // Ignore the token. | ||||
| return true | 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: | case EndTagToken: | ||||
| switch p.tok.DataAtom { | switch p.tok.DataAtom { | ||||
| case a.Head: | 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 | p.im = afterHeadIM | ||||
| return true | return true | ||||
| case a.Body, a.Html, a.Br: | case a.Body, a.Html, a.Br: | ||||
| p.parseImpliedToken(EndTagToken, a.Head, a.Head.String()) | p.parseImpliedToken(EndTagToken, a.Head, a.Head.String()) | ||||
| return false | 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: | default: | ||||
| // Ignore the token. | // Ignore the token. | ||||
| return true | return true | ||||
| @@ -622,7 +695,7 @@ func inHeadIM(p *parser) bool { | |||||
| return false | return false | ||||
| } | } | ||||
| // Section 12.2.5.4.6. | |||||
| // Section 12.2.6.4.6. | |||||
| func afterHeadIM(p *parser) bool { | func afterHeadIM(p *parser) bool { | ||||
| switch p.tok.Type { | switch p.tok.Type { | ||||
| case TextToken: | case TextToken: | ||||
| @@ -648,7 +721,7 @@ func afterHeadIM(p *parser) bool { | |||||
| p.addElement() | p.addElement() | ||||
| p.im = inFramesetIM | p.im = inFramesetIM | ||||
| return true | 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) | p.oe = append(p.oe, p.head) | ||||
| defer p.oe.remove(p.head) | defer p.oe.remove(p.head) | ||||
| return inHeadIM(p) | return inHeadIM(p) | ||||
| @@ -660,6 +733,8 @@ func afterHeadIM(p *parser) bool { | |||||
| switch p.tok.DataAtom { | switch p.tok.DataAtom { | ||||
| case a.Body, a.Html, a.Br: | case a.Body, a.Html, a.Br: | ||||
| // Drop down to creating an implied <body> tag. | // Drop down to creating an implied <body> tag. | ||||
| case a.Template: | |||||
| return inHeadIM(p) | |||||
| default: | default: | ||||
| // Ignore the token. | // Ignore the token. | ||||
| return true | 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 { | func inBodyIM(p *parser) bool { | ||||
| switch p.tok.Type { | switch p.tok.Type { | ||||
| case TextToken: | case TextToken: | ||||
| @@ -727,10 +802,16 @@ func inBodyIM(p *parser) bool { | |||||
| case StartTagToken: | case StartTagToken: | ||||
| switch p.tok.DataAtom { | switch p.tok.DataAtom { | ||||
| case a.Html: | case a.Html: | ||||
| if p.oe.contains(a.Template) { | |||||
| return true | |||||
| } | |||||
| copyAttributes(p.oe[0], p.tok) | 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) | return inHeadIM(p) | ||||
| case a.Body: | case a.Body: | ||||
| if p.oe.contains(a.Template) { | |||||
| return true | |||||
| } | |||||
| if len(p.oe) >= 2 { | if len(p.oe) >= 2 { | ||||
| body := p.oe[1] | body := p.oe[1] | ||||
| if body.Type == ElementNode && body.DataAtom == a.Body { | 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. | // The newline, if any, will be dealt with by the TextToken case. | ||||
| p.framesetOK = false | p.framesetOK = false | ||||
| case a.Form: | 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() | p.form = p.top() | ||||
| } | } | ||||
| case a.Li: | case a.Li: | ||||
| @@ -903,6 +988,14 @@ func inBodyIM(p *parser) bool { | |||||
| p.acknowledgeSelfClosingTag() | p.acknowledgeSelfClosingTag() | ||||
| p.popUntil(buttonScope, a.P) | p.popUntil(buttonScope, a.P) | ||||
| p.parseImpliedToken(StartTagToken, a.Form, a.Form.String()) | 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 != "" { | if action != "" { | ||||
| p.form.Attr = []Attribute{{Key: "action", Val: action}} | p.form.Attr = []Attribute{{Key: "action", Val: action}} | ||||
| } | } | ||||
| @@ -952,11 +1045,16 @@ func inBodyIM(p *parser) bool { | |||||
| } | } | ||||
| p.reconstructActiveFormattingElements() | p.reconstructActiveFormattingElements() | ||||
| p.addElement() | p.addElement() | ||||
| case a.Rp, a.Rt: | |||||
| case a.Rb, a.Rtc: | |||||
| if p.elementInScope(defaultScope, a.Ruby) { | if p.elementInScope(defaultScope, a.Ruby) { | ||||
| p.generateImpliedEndTags() | p.generateImpliedEndTags() | ||||
| } | } | ||||
| p.addElement() | p.addElement() | ||||
| case a.Rp, a.Rt: | |||||
| if p.elementInScope(defaultScope, a.Ruby) { | |||||
| p.generateImpliedEndTags("rtc") | |||||
| } | |||||
| p.addElement() | |||||
| case a.Math, a.Svg: | case a.Math, a.Svg: | ||||
| p.reconstructActiveFormattingElements() | p.reconstructActiveFormattingElements() | ||||
| if p.tok.DataAtom == a.Math { | 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: | 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) | p.popUntil(defaultScope, p.tok.DataAtom) | ||||
| case a.Form: | 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: | case a.P: | ||||
| if !p.elementInScope(buttonScope, a.P) { | if !p.elementInScope(buttonScope, a.P) { | ||||
| p.parseImpliedToken(StartTagToken, a.P, a.P.String()) | p.parseImpliedToken(StartTagToken, a.P, a.P.String()) | ||||
| @@ -1022,6 +1134,8 @@ func inBodyIM(p *parser) bool { | |||||
| case a.Br: | case a.Br: | ||||
| p.tok.Type = StartTagToken | p.tok.Type = StartTagToken | ||||
| return false | return false | ||||
| case a.Template: | |||||
| return inHeadIM(p) | |||||
| default: | default: | ||||
| p.inBodyEndTagOther(p.tok.DataAtom) | p.inBodyEndTagOther(p.tok.DataAtom) | ||||
| } | } | ||||
| @@ -1030,6 +1144,21 @@ func inBodyIM(p *parser) bool { | |||||
| Type: CommentNode, | Type: CommentNode, | ||||
| Data: p.tok.Data, | 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 | return true | ||||
| @@ -1160,7 +1289,7 @@ func (p *parser) inBodyEndTagFormatting(tagAtom a.Atom) { | |||||
| } | } | ||||
| // inBodyEndTagOther performs the "any other end tag" algorithm for inBodyIM. | // 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 | // https://html.spec.whatwg.org/multipage/syntax.html#parsing-main-inforeign | ||||
| func (p *parser) inBodyEndTagOther(tagAtom a.Atom) { | func (p *parser) inBodyEndTagOther(tagAtom a.Atom) { | ||||
| for i := len(p.oe) - 1; i >= 0; i-- { | 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 { | func textIM(p *parser) bool { | ||||
| switch p.tok.Type { | switch p.tok.Type { | ||||
| case ErrorToken: | case ErrorToken: | ||||
| @@ -1203,12 +1332,9 @@ func textIM(p *parser) bool { | |||||
| return p.tok.Type == EndTagToken | return p.tok.Type == EndTagToken | ||||
| } | } | ||||
| // Section 12.2.5.4.9. | |||||
| // Section 12.2.6.4.9. | |||||
| func inTableIM(p *parser) bool { | func inTableIM(p *parser) bool { | ||||
| switch p.tok.Type { | switch p.tok.Type { | ||||
| case ErrorToken: | |||||
| // Stop parsing. | |||||
| return true | |||||
| case TextToken: | case TextToken: | ||||
| p.tok.Data = strings.Replace(p.tok.Data, "\x00", "", -1) | p.tok.Data = strings.Replace(p.tok.Data, "\x00", "", -1) | ||||
| switch p.oe.top().DataAtom { | switch p.oe.top().DataAtom { | ||||
| @@ -1249,7 +1375,7 @@ func inTableIM(p *parser) bool { | |||||
| } | } | ||||
| // Ignore the token. | // Ignore the token. | ||||
| return true | return true | ||||
| case a.Style, a.Script: | |||||
| case a.Style, a.Script, a.Template: | |||||
| return inHeadIM(p) | return inHeadIM(p) | ||||
| case a.Input: | case a.Input: | ||||
| for _, t := range p.tok.Attr { | for _, t := range p.tok.Attr { | ||||
| @@ -1261,7 +1387,7 @@ func inTableIM(p *parser) bool { | |||||
| } | } | ||||
| // Otherwise drop down to the default action. | // Otherwise drop down to the default action. | ||||
| case a.Form: | case a.Form: | ||||
| if p.form != nil { | |||||
| if p.oe.contains(a.Template) || p.form != nil { | |||||
| // Ignore the token. | // Ignore the token. | ||||
| return true | 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: | 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. | // Ignore the token. | ||||
| return true | return true | ||||
| case a.Template: | |||||
| return inHeadIM(p) | |||||
| } | } | ||||
| case CommentToken: | case CommentToken: | ||||
| p.addChild(&Node{ | p.addChild(&Node{ | ||||
| @@ -1301,6 +1429,8 @@ func inTableIM(p *parser) bool { | |||||
| case DoctypeToken: | case DoctypeToken: | ||||
| // Ignore the token. | // Ignore the token. | ||||
| return true | return true | ||||
| case ErrorToken: | |||||
| return inBodyIM(p) | |||||
| } | } | ||||
| p.fosterParenting = true | p.fosterParenting = true | ||||
| @@ -1309,7 +1439,7 @@ func inTableIM(p *parser) bool { | |||||
| return inBodyIM(p) | return inBodyIM(p) | ||||
| } | } | ||||
| // Section 12.2.5.4.11. | |||||
| // Section 12.2.6.4.11. | |||||
| func inCaptionIM(p *parser) bool { | func inCaptionIM(p *parser) bool { | ||||
| switch p.tok.Type { | switch p.tok.Type { | ||||
| case StartTagToken: | case StartTagToken: | ||||
| @@ -1355,7 +1485,7 @@ func inCaptionIM(p *parser) bool { | |||||
| return inBodyIM(p) | return inBodyIM(p) | ||||
| } | } | ||||
| // Section 12.2.5.4.12. | |||||
| // Section 12.2.6.4.12. | |||||
| func inColumnGroupIM(p *parser) bool { | func inColumnGroupIM(p *parser) bool { | ||||
| switch p.tok.Type { | switch p.tok.Type { | ||||
| case TextToken: | case TextToken: | ||||
| @@ -1386,11 +1516,13 @@ func inColumnGroupIM(p *parser) bool { | |||||
| p.oe.pop() | p.oe.pop() | ||||
| p.acknowledgeSelfClosingTag() | p.acknowledgeSelfClosingTag() | ||||
| return true | return true | ||||
| case a.Template: | |||||
| return inHeadIM(p) | |||||
| } | } | ||||
| case EndTagToken: | case EndTagToken: | ||||
| switch p.tok.DataAtom { | switch p.tok.DataAtom { | ||||
| case a.Colgroup: | case a.Colgroup: | ||||
| if p.oe.top().DataAtom != a.Html { | |||||
| if p.oe.top().DataAtom == a.Colgroup { | |||||
| p.oe.pop() | p.oe.pop() | ||||
| p.im = inTableIM | p.im = inTableIM | ||||
| } | } | ||||
| @@ -1398,17 +1530,21 @@ func inColumnGroupIM(p *parser) bool { | |||||
| case a.Col: | case a.Col: | ||||
| // Ignore the token. | // Ignore the token. | ||||
| return true | 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 { | func inTableBodyIM(p *parser) bool { | ||||
| switch p.tok.Type { | switch p.tok.Type { | ||||
| case StartTagToken: | case StartTagToken: | ||||
| @@ -1460,7 +1596,7 @@ func inTableBodyIM(p *parser) bool { | |||||
| return inTableIM(p) | return inTableIM(p) | ||||
| } | } | ||||
| // Section 12.2.5.4.14. | |||||
| // Section 12.2.6.4.14. | |||||
| func inRowIM(p *parser) bool { | func inRowIM(p *parser) bool { | ||||
| switch p.tok.Type { | switch p.tok.Type { | ||||
| case StartTagToken: | case StartTagToken: | ||||
| @@ -1511,7 +1647,7 @@ func inRowIM(p *parser) bool { | |||||
| return inTableIM(p) | return inTableIM(p) | ||||
| } | } | ||||
| // Section 12.2.5.4.15. | |||||
| // Section 12.2.6.4.15. | |||||
| func inCellIM(p *parser) bool { | func inCellIM(p *parser) bool { | ||||
| switch p.tok.Type { | switch p.tok.Type { | ||||
| case StartTagToken: | case StartTagToken: | ||||
| @@ -1560,12 +1696,9 @@ func inCellIM(p *parser) bool { | |||||
| return inBodyIM(p) | return inBodyIM(p) | ||||
| } | } | ||||
| // Section 12.2.5.4.16. | |||||
| // Section 12.2.6.4.16. | |||||
| func inSelectIM(p *parser) bool { | func inSelectIM(p *parser) bool { | ||||
| switch p.tok.Type { | switch p.tok.Type { | ||||
| case ErrorToken: | |||||
| // Stop parsing. | |||||
| return true | |||||
| case TextToken: | case TextToken: | ||||
| p.addText(strings.Replace(p.tok.Data, "\x00", "", -1)) | p.addText(strings.Replace(p.tok.Data, "\x00", "", -1)) | ||||
| case StartTagToken: | case StartTagToken: | ||||
| @@ -1597,7 +1730,7 @@ func inSelectIM(p *parser) bool { | |||||
| p.tokenizer.NextIsNotRawText() | p.tokenizer.NextIsNotRawText() | ||||
| // Ignore the token. | // Ignore the token. | ||||
| return true | return true | ||||
| case a.Script: | |||||
| case a.Script, a.Template: | |||||
| return inHeadIM(p) | return inHeadIM(p) | ||||
| } | } | ||||
| case EndTagToken: | case EndTagToken: | ||||
| @@ -1618,6 +1751,8 @@ func inSelectIM(p *parser) bool { | |||||
| if p.popUntil(selectScope, a.Select) { | if p.popUntil(selectScope, a.Select) { | ||||
| p.resetInsertionMode() | p.resetInsertionMode() | ||||
| } | } | ||||
| case a.Template: | |||||
| return inHeadIM(p) | |||||
| } | } | ||||
| case CommentToken: | case CommentToken: | ||||
| p.addChild(&Node{ | p.addChild(&Node{ | ||||
| @@ -1627,12 +1762,14 @@ func inSelectIM(p *parser) bool { | |||||
| case DoctypeToken: | case DoctypeToken: | ||||
| // Ignore the token. | // Ignore the token. | ||||
| return true | return true | ||||
| case ErrorToken: | |||||
| return inBodyIM(p) | |||||
| } | } | ||||
| return true | return true | ||||
| } | } | ||||
| // Section 12.2.5.4.17. | |||||
| // Section 12.2.6.4.17. | |||||
| func inSelectInTableIM(p *parser) bool { | func inSelectInTableIM(p *parser) bool { | ||||
| switch p.tok.Type { | switch p.tok.Type { | ||||
| case StartTagToken, EndTagToken: | case StartTagToken, EndTagToken: | ||||
| @@ -1650,7 +1787,73 @@ func inSelectInTableIM(p *parser) bool { | |||||
| return inSelectIM(p) | 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 { | func afterBodyIM(p *parser) bool { | ||||
| switch p.tok.Type { | switch p.tok.Type { | ||||
| case ErrorToken: | case ErrorToken: | ||||
| @@ -1688,7 +1891,7 @@ func afterBodyIM(p *parser) bool { | |||||
| return false | return false | ||||
| } | } | ||||
| // Section 12.2.5.4.19. | |||||
| // Section 12.2.6.4.20. | |||||
| func inFramesetIM(p *parser) bool { | func inFramesetIM(p *parser) bool { | ||||
| switch p.tok.Type { | switch p.tok.Type { | ||||
| case CommentToken: | case CommentToken: | ||||
| @@ -1738,7 +1941,7 @@ func inFramesetIM(p *parser) bool { | |||||
| return true | return true | ||||
| } | } | ||||
| // Section 12.2.5.4.20. | |||||
| // Section 12.2.6.4.21. | |||||
| func afterFramesetIM(p *parser) bool { | func afterFramesetIM(p *parser) bool { | ||||
| switch p.tok.Type { | switch p.tok.Type { | ||||
| case CommentToken: | case CommentToken: | ||||
| @@ -1777,7 +1980,7 @@ func afterFramesetIM(p *parser) bool { | |||||
| return true | return true | ||||
| } | } | ||||
| // Section 12.2.5.4.21. | |||||
| // Section 12.2.6.4.22. | |||||
| func afterAfterBodyIM(p *parser) bool { | func afterAfterBodyIM(p *parser) bool { | ||||
| switch p.tok.Type { | switch p.tok.Type { | ||||
| case ErrorToken: | case ErrorToken: | ||||
| @@ -1806,7 +2009,7 @@ func afterAfterBodyIM(p *parser) bool { | |||||
| return false | return false | ||||
| } | } | ||||
| // Section 12.2.5.4.22. | |||||
| // Section 12.2.6.4.23. | |||||
| func afterAfterFramesetIM(p *parser) bool { | func afterAfterFramesetIM(p *parser) bool { | ||||
| switch p.tok.Type { | switch p.tok.Type { | ||||
| case CommentToken: | case CommentToken: | ||||
| @@ -1844,7 +2047,7 @@ func afterAfterFramesetIM(p *parser) bool { | |||||
| const whitespaceOrNUL = whitespace + "\x00" | const whitespaceOrNUL = whitespace + "\x00" | ||||
| // Section 12.2.5.5. | |||||
| // Section 12.2.6.5 | |||||
| func parseForeignContent(p *parser) bool { | func parseForeignContent(p *parser) bool { | ||||
| switch p.tok.Type { | switch p.tok.Type { | ||||
| case TextToken: | case TextToken: | ||||
| @@ -1924,7 +2127,7 @@ func parseForeignContent(p *parser) bool { | |||||
| return true | return true | ||||
| } | } | ||||
| // Section 12.2.5. | |||||
| // Section 12.2.6. | |||||
| func (p *parser) inForeignContent() bool { | func (p *parser) inForeignContent() bool { | ||||
| if len(p.oe) == 0 { | if len(p.oe) == 0 { | ||||
| return false | return false | ||||
| @@ -2012,6 +2215,15 @@ func (p *parser) parse() error { | |||||
| } | } | ||||
| // Parse returns the parse tree for the HTML from the given Reader. | // 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. | // The input is assumed to be UTF-8 encoded. | ||||
| func Parse(r io.Reader) (*Node, error) { | func Parse(r io.Reader) (*Node, error) { | ||||
| p := &parser{ | 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 | // 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 | // found. If the fragment is the InnerHTML for an existing element, pass that | ||||
| // element in context. | // element in context. | ||||
| // | |||||
| // It has the same intricacies as Parse. | |||||
| func ParseFragment(r io.Reader, context *Node) ([]*Node, error) { | func ParseFragment(r io.Reader, context *Node) ([]*Node, error) { | ||||
| contextTag := "" | contextTag := "" | ||||
| if context != nil { | if context != nil { | ||||
| @@ -2064,6 +2278,9 @@ func ParseFragment(r io.Reader, context *Node) ([]*Node, error) { | |||||
| } | } | ||||
| p.doc.AppendChild(root) | p.doc.AppendChild(root) | ||||
| p.oe = nodeStack{root} | p.oe = nodeStack{root} | ||||
| if context != nil && context.DataAtom == a.Template { | |||||
| p.templateStack = append(p.templateStack, inTemplateIM) | |||||
| } | |||||
| p.resetInsertionMode() | p.resetInsertionMode() | ||||
| for n := context; n != nil; n = n.Parent { | 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 | 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 { | func (z *Tokenizer) Token() Token { | ||||
| t := Token{Type: z.tt} | t := Token{Type: z.tt} | ||||
| switch z.tt { | switch z.tt { | ||||