content.go (2717B)
1 // Copyright 2011 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package template 6 7 import ( 8 "fmt" 9 htmltemplate "html/template" 10 "reflect" 11 ) 12 13 type contentType uint8 14 15 const ( 16 contentTypePlain contentType = iota 17 contentTypeCSS 18 contentTypeHTML 19 contentTypeHTMLAttr 20 contentTypeJS 21 contentTypeJSStr 22 contentTypeURL 23 contentTypeSrcset 24 // contentTypeUnsafe is used in attr.go for values that affect how 25 // embedded content and network messages are formed, vetted, 26 // or interpreted; or which credentials network messages carry. 27 contentTypeUnsafe 28 ) 29 30 // indirect returns the value, after dereferencing as many times 31 // as necessary to reach the base type (or nil). 32 func indirect(a any) any { 33 if a == nil { 34 return nil 35 } 36 if t := reflect.TypeOf(a); t.Kind() != reflect.Pointer { 37 // Avoid creating a reflect.Value if it's not a pointer. 38 return a 39 } 40 v := reflect.ValueOf(a) 41 for v.Kind() == reflect.Pointer && !v.IsNil() { 42 v = v.Elem() 43 } 44 return v.Interface() 45 } 46 47 var ( 48 errorType = reflect.TypeOf((*error)(nil)).Elem() 49 fmtStringerType = reflect.TypeOf((*fmt.Stringer)(nil)).Elem() 50 ) 51 52 // indirectToStringerOrError returns the value, after dereferencing as many times 53 // as necessary to reach the base type (or nil) or an implementation of fmt.Stringer 54 // or error, 55 func indirectToStringerOrError(a any) any { 56 if a == nil { 57 return nil 58 } 59 v := reflect.ValueOf(a) 60 for !v.Type().Implements(fmtStringerType) && !v.Type().Implements(errorType) && v.Kind() == reflect.Pointer && !v.IsNil() { 61 v = v.Elem() 62 } 63 return v.Interface() 64 } 65 66 // stringify converts its arguments to a string and the type of the content. 67 // All pointers are dereferenced, as in the text/template package. 68 func stringify(args ...any) (string, contentType) { 69 if len(args) == 1 { 70 switch s := indirect(args[0]).(type) { 71 case string: 72 return s, contentTypePlain 73 case htmltemplate.CSS: 74 return string(s), contentTypeCSS 75 case htmltemplate.HTML: 76 return string(s), contentTypeHTML 77 case htmltemplate.HTMLAttr: 78 return string(s), contentTypeHTMLAttr 79 case htmltemplate.JS: 80 return string(s), contentTypeJS 81 case htmltemplate.JSStr: 82 return string(s), contentTypeJSStr 83 case htmltemplate.URL: 84 return string(s), contentTypeURL 85 case htmltemplate.Srcset: 86 return string(s), contentTypeSrcset 87 } 88 } 89 i := 0 90 for _, arg := range args { 91 // We skip untyped nil arguments for backward compatibility. 92 // Without this they would be output as <nil>, escaped. 93 // See issue 25875. 94 if arg == nil { 95 continue 96 } 97 98 args[i] = indirectToStringerOrError(arg) 99 i++ 100 } 101 return fmt.Sprint(args[:i]...), contentTypePlain 102 }