diff options
Diffstat (limited to 'tpl/internal/go_templates/htmltemplate/content.go')
-rw-r--r-- | tpl/internal/go_templates/htmltemplate/content.go | 102 |
1 files changed, 102 insertions, 0 deletions
diff --git a/tpl/internal/go_templates/htmltemplate/content.go b/tpl/internal/go_templates/htmltemplate/content.go new file mode 100644 index 000000000..bc32dc813 --- /dev/null +++ b/tpl/internal/go_templates/htmltemplate/content.go @@ -0,0 +1,102 @@ +// Copyright 2011 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. + +package template + +import ( + "fmt" + htmltemplate "html/template" + "reflect" +) + +type contentType uint8 + +const ( + contentTypePlain contentType = iota + contentTypeCSS + contentTypeHTML + contentTypeHTMLAttr + contentTypeJS + contentTypeJSStr + contentTypeURL + contentTypeSrcset + // contentTypeUnsafe is used in attr.go for values that affect how + // embedded content and network messages are formed, vetted, + // or interpreted; or which credentials network messages carry. + contentTypeUnsafe +) + +// indirect returns the value, after dereferencing as many times +// as necessary to reach the base type (or nil). +func indirect(a interface{}) interface{} { + if a == nil { + return nil + } + if t := reflect.TypeOf(a); t.Kind() != reflect.Ptr { + // Avoid creating a reflect.Value if it's not a pointer. + return a + } + v := reflect.ValueOf(a) + for v.Kind() == reflect.Ptr && !v.IsNil() { + v = v.Elem() + } + return v.Interface() +} + +var ( + errorType = reflect.TypeOf((*error)(nil)).Elem() + fmtStringerType = reflect.TypeOf((*fmt.Stringer)(nil)).Elem() +) + +// indirectToStringerOrError returns the value, after dereferencing as many times +// as necessary to reach the base type (or nil) or an implementation of fmt.Stringer +// or error, +func indirectToStringerOrError(a interface{}) interface{} { + if a == nil { + return nil + } + v := reflect.ValueOf(a) + for !v.Type().Implements(fmtStringerType) && !v.Type().Implements(errorType) && v.Kind() == reflect.Ptr && !v.IsNil() { + v = v.Elem() + } + return v.Interface() +} + +// stringify converts its arguments to a string and the type of the content. +// All pointers are dereferenced, as in the text/template package. +func stringify(args ...interface{}) (string, contentType) { + if len(args) == 1 { + switch s := indirect(args[0]).(type) { + case string: + return s, contentTypePlain + case htmltemplate.CSS: + return string(s), contentTypeCSS + case htmltemplate.HTML: + return string(s), contentTypeHTML + case htmltemplate.HTMLAttr: + return string(s), contentTypeHTMLAttr + case htmltemplate.JS: + return string(s), contentTypeJS + case htmltemplate.JSStr: + return string(s), contentTypeJSStr + case htmltemplate.URL: + return string(s), contentTypeURL + case htmltemplate.Srcset: + return string(s), contentTypeSrcset + } + } + i := 0 + for _, arg := range args { + // We skip untyped nil arguments for backward compatibility. + // Without this they would be output as <nil>, escaped. + // See issue 25875. + if arg == nil { + continue + } + + args[i] = indirectToStringerOrError(arg) + i++ + } + return fmt.Sprint(args[:i]...), contentTypePlain +} |