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 }