347 lines
7.2 KiB
Go
347 lines
7.2 KiB
Go
package playwright
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"math"
|
|
"math/big"
|
|
"net/url"
|
|
"reflect"
|
|
"time"
|
|
)
|
|
|
|
type jsHandleImpl struct {
|
|
channelOwner
|
|
preview string
|
|
}
|
|
|
|
func (j *jsHandleImpl) Evaluate(expression string, options ...interface{}) (interface{}, error) {
|
|
var arg interface{}
|
|
if len(options) == 1 {
|
|
arg = options[0]
|
|
}
|
|
result, err := j.channel.Send("evaluateExpression", map[string]interface{}{
|
|
"expression": expression,
|
|
"arg": serializeArgument(arg),
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return parseResult(result), nil
|
|
}
|
|
|
|
func (j *jsHandleImpl) EvaluateHandle(expression string, options ...interface{}) (JSHandle, error) {
|
|
var arg interface{}
|
|
if len(options) == 1 {
|
|
arg = options[0]
|
|
}
|
|
result, err := j.channel.Send("evaluateExpressionHandle", map[string]interface{}{
|
|
"expression": expression,
|
|
"arg": serializeArgument(arg),
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
channelOwner := fromChannel(result)
|
|
if channelOwner == nil {
|
|
return nil, nil
|
|
}
|
|
return channelOwner.(*jsHandleImpl), nil
|
|
}
|
|
|
|
func (j *jsHandleImpl) GetProperty(name string) (JSHandle, error) {
|
|
channel, err := j.channel.Send("getProperty", map[string]interface{}{
|
|
"name": name,
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return fromChannel(channel).(*jsHandleImpl), nil
|
|
}
|
|
|
|
func (j *jsHandleImpl) GetProperties() (map[string]JSHandle, error) {
|
|
properties, err := j.channel.Send("getPropertyList")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
propertiesMap := make(map[string]JSHandle)
|
|
for _, property := range properties.([]interface{}) {
|
|
item := property.(map[string]interface{})
|
|
propertiesMap[item["name"].(string)] = fromChannel(item["value"]).(*jsHandleImpl)
|
|
}
|
|
return propertiesMap, nil
|
|
}
|
|
|
|
func (j *jsHandleImpl) AsElement() ElementHandle {
|
|
return nil
|
|
}
|
|
|
|
func (j *jsHandleImpl) Dispose() error {
|
|
_, err := j.channel.Send("dispose")
|
|
if errors.Is(err, ErrTargetClosed) {
|
|
return nil
|
|
}
|
|
return err
|
|
}
|
|
|
|
func (j *jsHandleImpl) String() string {
|
|
return j.preview
|
|
}
|
|
|
|
func (j *jsHandleImpl) JSONValue() (interface{}, error) {
|
|
v, err := j.channel.Send("jsonValue")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return parseResult(v), nil
|
|
}
|
|
|
|
func parseValue(result interface{}, refs map[float64]interface{}) interface{} {
|
|
vMap, ok := result.(map[string]interface{})
|
|
if !ok {
|
|
return result
|
|
}
|
|
if v, ok := vMap["n"]; ok {
|
|
if math.Ceil(v.(float64))-v.(float64) == 0 {
|
|
return int(v.(float64))
|
|
}
|
|
return v.(float64)
|
|
}
|
|
|
|
if v, ok := vMap["u"]; ok {
|
|
u, _ := url.Parse(v.(string))
|
|
return u
|
|
}
|
|
|
|
if v, ok := vMap["bi"]; ok {
|
|
n := new(big.Int)
|
|
n.SetString(v.(string), 0)
|
|
return n
|
|
}
|
|
|
|
if v, ok := vMap["ref"]; ok {
|
|
if vV, ok := refs[v.(float64)]; ok {
|
|
return vV
|
|
}
|
|
return nil
|
|
}
|
|
|
|
if v, ok := vMap["s"]; ok {
|
|
return v.(string)
|
|
}
|
|
if v, ok := vMap["b"]; ok {
|
|
return v.(bool)
|
|
}
|
|
if v, ok := vMap["v"]; ok {
|
|
if v == "undefined" || v == "null" {
|
|
return nil
|
|
}
|
|
if v == "NaN" {
|
|
return math.NaN()
|
|
}
|
|
if v == "Infinity" {
|
|
return math.Inf(1)
|
|
}
|
|
if v == "-Infinity" {
|
|
return math.Inf(-1)
|
|
}
|
|
if v == "-0" {
|
|
return math.Copysign(0, -1)
|
|
}
|
|
return v
|
|
}
|
|
if v, ok := vMap["d"]; ok {
|
|
t, _ := time.Parse(time.RFC3339Nano, v.(string))
|
|
return t
|
|
}
|
|
if v, ok := vMap["a"]; ok {
|
|
aV := v.([]interface{})
|
|
refs[vMap["id"].(float64)] = aV
|
|
for i := range aV {
|
|
aV[i] = parseValue(aV[i], refs)
|
|
}
|
|
return aV
|
|
}
|
|
if v, ok := vMap["o"]; ok {
|
|
aV := v.([]interface{})
|
|
out := map[string]interface{}{}
|
|
refs[vMap["id"].(float64)] = out
|
|
for key := range aV {
|
|
entry := aV[key].(map[string]interface{})
|
|
out[entry["k"].(string)] = parseValue(entry["v"], refs)
|
|
}
|
|
return out
|
|
}
|
|
|
|
if v, ok := vMap["e"]; ok {
|
|
return parseError(Error{
|
|
Name: v.(map[string]interface{})["n"].(string),
|
|
Message: v.(map[string]interface{})["m"].(string),
|
|
Stack: v.(map[string]interface{})["s"].(string),
|
|
})
|
|
}
|
|
panic(fmt.Errorf("Unexpected value: %v", vMap))
|
|
}
|
|
|
|
func serializeValue(value interface{}, handles *[]*channel, depth int) interface{} {
|
|
if handle, ok := value.(*elementHandleImpl); ok {
|
|
h := len(*handles)
|
|
*handles = append(*handles, handle.channel)
|
|
return map[string]interface{}{
|
|
"h": h,
|
|
}
|
|
}
|
|
if handle, ok := value.(*jsHandleImpl); ok {
|
|
h := len(*handles)
|
|
*handles = append(*handles, handle.channel)
|
|
return map[string]interface{}{
|
|
"h": h,
|
|
}
|
|
}
|
|
if u, ok := value.(*url.URL); ok {
|
|
return map[string]interface{}{
|
|
"u": u.String(),
|
|
}
|
|
}
|
|
|
|
if err, ok := value.(error); ok {
|
|
var e *Error
|
|
if errors.As(err, &e) {
|
|
return map[string]interface{}{
|
|
"e": map[string]interface{}{
|
|
"n": e.Name,
|
|
"m": e.Message,
|
|
"s": e.Stack,
|
|
},
|
|
}
|
|
}
|
|
return map[string]interface{}{
|
|
"e": map[string]interface{}{
|
|
"n": "",
|
|
"m": err.Error(),
|
|
"s": "",
|
|
},
|
|
}
|
|
}
|
|
|
|
if depth > 100 {
|
|
panic(errors.New("Maximum argument depth exceeded"))
|
|
}
|
|
if value == nil {
|
|
return map[string]interface{}{
|
|
"v": "undefined",
|
|
}
|
|
}
|
|
if n, ok := value.(*big.Int); ok {
|
|
return map[string]interface{}{
|
|
"bi": n.String(),
|
|
}
|
|
}
|
|
|
|
switch v := value.(type) {
|
|
case time.Time:
|
|
return map[string]interface{}{
|
|
"d": v.Format(time.RFC3339Nano),
|
|
}
|
|
case int:
|
|
return map[string]interface{}{
|
|
"n": v,
|
|
}
|
|
case string:
|
|
return map[string]interface{}{
|
|
"s": v,
|
|
}
|
|
case bool:
|
|
return map[string]interface{}{
|
|
"b": v,
|
|
}
|
|
}
|
|
|
|
refV := reflect.ValueOf(value)
|
|
|
|
switch refV.Kind() {
|
|
case reflect.Float32, reflect.Float64:
|
|
floatV := refV.Float()
|
|
if math.IsInf(floatV, 1) {
|
|
return map[string]interface{}{
|
|
"v": "Infinity",
|
|
}
|
|
}
|
|
if math.IsInf(floatV, -1) {
|
|
return map[string]interface{}{
|
|
"v": "-Infinity",
|
|
}
|
|
}
|
|
// https://github.com/golang/go/issues/2196
|
|
if floatV == math.Copysign(0, -1) {
|
|
return map[string]interface{}{
|
|
"v": "-0",
|
|
}
|
|
}
|
|
if math.IsNaN(floatV) {
|
|
return map[string]interface{}{
|
|
"v": "NaN",
|
|
}
|
|
}
|
|
return map[string]interface{}{
|
|
"n": floatV,
|
|
}
|
|
case reflect.Slice:
|
|
aV := make([]interface{}, refV.Len())
|
|
for i := 0; i < refV.Len(); i++ {
|
|
aV[i] = serializeValue(refV.Index(i).Interface(), handles, depth+1)
|
|
}
|
|
return map[string]interface{}{
|
|
"a": aV,
|
|
}
|
|
case reflect.Map:
|
|
out := []interface{}{}
|
|
vM := value.(map[string]interface{})
|
|
for key := range vM {
|
|
v := serializeValue(vM[key], handles, depth+1)
|
|
// had key, so convert "undefined" to "null"
|
|
if reflect.DeepEqual(v, map[string]interface{}{
|
|
"v": "undefined",
|
|
}) {
|
|
v = map[string]interface{}{
|
|
"v": "null",
|
|
}
|
|
}
|
|
out = append(out, map[string]interface{}{
|
|
"k": key,
|
|
"v": v,
|
|
})
|
|
}
|
|
return map[string]interface{}{
|
|
"o": out,
|
|
}
|
|
}
|
|
return map[string]interface{}{
|
|
"v": "undefined",
|
|
}
|
|
}
|
|
|
|
func parseResult(result interface{}) interface{} {
|
|
return parseValue(result, map[float64]interface{}{})
|
|
}
|
|
|
|
func serializeArgument(arg interface{}) interface{} {
|
|
handles := []*channel{}
|
|
value := serializeValue(arg, &handles, 0)
|
|
return map[string]interface{}{
|
|
"value": value,
|
|
"handles": handles,
|
|
}
|
|
}
|
|
|
|
func newJSHandle(parent *channelOwner, objectType string, guid string, initializer map[string]interface{}) *jsHandleImpl {
|
|
bt := &jsHandleImpl{
|
|
preview: initializer["preview"].(string),
|
|
}
|
|
bt.createChannelOwner(bt, parent, objectType, guid, initializer)
|
|
bt.channel.On("previewUpdated", func(ev map[string]interface{}) {
|
|
bt.preview = ev["preview"].(string)
|
|
})
|
|
return bt
|
|
}
|