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 }