forked from chromedp/chromedp
-
Notifications
You must be signed in to change notification settings - Fork 0
/
eval.go
145 lines (127 loc) · 4.5 KB
/
eval.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
package chromedp
import (
"context"
"encoding/json"
"reflect"
"github.com/chromedp/cdproto/runtime"
)
// EvaluateAction are actions that evaluate JavaScript expressions using
// runtime.Evaluate.
type EvaluateAction Action
// Evaluate is an action to evaluate the JavaScript expression, unmarshaling
// the result of the script evaluation to res.
//
// When res is nil, the script result will be ignored.
//
// When res is a *[]byte, the raw JSON-encoded value of the script
// result will be placed in res.
//
// When res is a **runtime.RemoteObject, res will be set to the low-level
// protocol type, and no attempt will be made to convert the result.
// The original objects could be maintained in memory until the page is
// navigated or closed. `runtime.ReleaseObject` or `runtime.ReleaseObjectGroup`
// can be used to ask the browser to release the original objects.
//
// For all other cases, the result of the script will be returned "by value" (i.e.,
// JSON-encoded), and subsequently an attempt will be made to json.Unmarshal
// the script result to res. When the script result is "undefined" or "null",
// and the value that res points to can not be nil (only the value of a chan,
// func, interface, map, pointer, or slice can be nil), it returns [ErrJSUndefined]
// or [ErrJSNull] respectively.
func Evaluate(expression string, res interface{}, opts ...EvaluateOption) EvaluateAction {
return ActionFunc(func(ctx context.Context) error {
// set up parameters
p := runtime.Evaluate(expression)
switch res.(type) {
case **runtime.RemoteObject:
default:
p = p.WithReturnByValue(true)
}
// apply opts
for _, o := range opts {
p = o(p)
}
// evaluate
v, exp, err := p.Do(ctx)
if err != nil {
return err
}
if exp != nil {
return exp
}
return parseRemoteObject(v, res)
})
}
func parseRemoteObject(v *runtime.RemoteObject, res interface{}) (err error) {
if res == nil {
return
}
switch x := res.(type) {
case **runtime.RemoteObject:
*x = v
return
case *[]byte:
*x = v.Value
return
}
value := v.Value
if value == nil {
rv := reflect.ValueOf(res)
if rv.Kind() == reflect.Ptr {
switch rv.Elem().Kind() {
// Common kinds that can be nil.
case reflect.Ptr, reflect.Map, reflect.Slice:
// It's weird that res is a pointer to the following kinds,
// but they can be nil too.
case reflect.Chan, reflect.Func, reflect.Interface:
default:
// When the value that `res` points to can not be set to nil,
// return [ErrJSUndefined] or [ErrJSNull] respectively.
if v.Type == "undefined" {
return ErrJSUndefined
}
return ErrJSNull
}
}
// Change the value to the json literal null to make json.Unmarshal happy.
value = []byte("null")
}
return json.Unmarshal(value, res)
}
// EvaluateAsDevTools is an action that evaluates a JavaScript expression as
// Chrome DevTools would, evaluating the expression in the "console" context,
// and making the Command Line API available to the script.
//
// See [Evaluate] for more information on how script expressions are evaluated.
//
// Note: this should not be used with untrusted JavaScript.
func EvaluateAsDevTools(expression string, res interface{}, opts ...EvaluateOption) EvaluateAction {
return Evaluate(expression, res, append(opts, EvalObjectGroup("console"), EvalWithCommandLineAPI)...)
}
// EvaluateOption is the type for JavaScript evaluation options.
type EvaluateOption = func(*runtime.EvaluateParams) *runtime.EvaluateParams
// EvalObjectGroup is an evaluate option to set the object group.
func EvalObjectGroup(objectGroup string) EvaluateOption {
return func(p *runtime.EvaluateParams) *runtime.EvaluateParams {
return p.WithObjectGroup(objectGroup)
}
}
// EvalWithCommandLineAPI is an evaluate option to make the DevTools Command
// Line API available to the evaluated script.
//
// See [Evaluate] for more information on how evaluate actions work.
//
// Note: this should not be used with untrusted JavaScript.
func EvalWithCommandLineAPI(p *runtime.EvaluateParams) *runtime.EvaluateParams {
return p.WithIncludeCommandLineAPI(true)
}
// EvalIgnoreExceptions is an evaluate option that will cause JavaScript
// evaluation to ignore exceptions.
func EvalIgnoreExceptions(p *runtime.EvaluateParams) *runtime.EvaluateParams {
return p.WithSilent(true)
}
// EvalAsValue is an evaluate option that will cause the evaluated JavaScript
// expression to encode the result of the expression as a JSON-encoded value.
func EvalAsValue(p *runtime.EvaluateParams) *runtime.EvaluateParams {
return p.WithReturnByValue(true)
}