github.com/MontFerret/ferret@v0.18.0/pkg/drivers/cdp/eval/resolver.go (about) 1 package eval 2 3 import ( 4 "context" 5 "strconv" 6 7 "github.com/mafredri/cdp" 8 "github.com/mafredri/cdp/protocol/page" 9 "github.com/mafredri/cdp/protocol/runtime" 10 "github.com/pkg/errors" 11 12 "github.com/MontFerret/ferret/pkg/drivers" 13 "github.com/MontFerret/ferret/pkg/runtime/core" 14 "github.com/MontFerret/ferret/pkg/runtime/values" 15 ) 16 17 type ( 18 ValueLoader interface { 19 Load( 20 ctx context.Context, 21 frameID page.FrameID, 22 remoteType RemoteObjectType, 23 remoteClass RemoteClassName, 24 id runtime.RemoteObjectID, 25 ) (core.Value, error) 26 } 27 28 ValueLoaderFn func( 29 ctx context.Context, 30 frameID page.FrameID, 31 remoteType RemoteObjectType, 32 remoteClass RemoteClassName, 33 id runtime.RemoteObjectID, 34 ) (core.Value, error) 35 36 Resolver struct { 37 runtime cdp.Runtime 38 frameID page.FrameID 39 loader ValueLoader 40 } 41 ) 42 43 func (f ValueLoaderFn) Load( 44 ctx context.Context, 45 frameID page.FrameID, 46 remoteType RemoteObjectType, 47 remoteClass RemoteClassName, 48 id runtime.RemoteObjectID, 49 ) (core.Value, error) { 50 return f(ctx, frameID, remoteType, remoteClass, id) 51 } 52 53 func NewResolver(runtime cdp.Runtime, frameID page.FrameID) *Resolver { 54 return &Resolver{runtime, frameID, nil} 55 } 56 57 func (r *Resolver) SetLoader(loader ValueLoader) *Resolver { 58 r.loader = loader 59 60 return r 61 } 62 63 func (r *Resolver) ToValue(ctx context.Context, ref runtime.RemoteObject) (core.Value, error) { 64 // It's not an actual ref but rather a plain value 65 if ref.ObjectID == nil { 66 if ref.Value != nil { 67 return values.Unmarshal(ref.Value) 68 } 69 70 return values.None, nil 71 } 72 73 subtype := ToRemoteObjectType(ref) 74 75 switch subtype { 76 case NullObjectType, UndefinedObjectType: 77 return values.None, nil 78 case ArrayObjectType: 79 props, err := r.runtime.GetProperties(ctx, runtime.NewGetPropertiesArgs(*ref.ObjectID).SetOwnProperties(true)) 80 81 if err != nil { 82 return values.None, err 83 } 84 85 if props.ExceptionDetails != nil { 86 exception := *props.ExceptionDetails 87 88 return values.None, errors.New(exception.Text) 89 } 90 91 result := values.NewArray(len(props.Result)) 92 93 for _, descr := range props.Result { 94 if !descr.Enumerable { 95 continue 96 } 97 98 if descr.Value == nil { 99 continue 100 } 101 102 el, err := r.ToValue(ctx, *descr.Value) 103 104 if err != nil { 105 return values.None, err 106 } 107 108 result.Push(el) 109 } 110 111 return result, nil 112 case NodeObjectType: 113 // is it even possible? 114 if ref.ObjectID == nil { 115 return values.Unmarshal(ref.Value) 116 } 117 118 return r.loadValue(ctx, NodeObjectType, ToRemoteClassName(ref), *ref.ObjectID) 119 default: 120 switch ToRemoteType(ref) { 121 case StringType: 122 str, err := strconv.Unquote(string(ref.Value)) 123 124 if err != nil { 125 return values.None, err 126 } 127 128 return values.NewString(str), nil 129 case ObjectType: 130 if subtype == NullObjectType || subtype == UnknownObjectType { 131 return values.None, nil 132 } 133 134 return values.Unmarshal(ref.Value) 135 default: 136 return values.Unmarshal(ref.Value) 137 } 138 } 139 } 140 141 func (r *Resolver) ToElement(ctx context.Context, ref runtime.RemoteObject) (drivers.HTMLElement, error) { 142 if ref.ObjectID == nil { 143 return nil, core.Error(core.ErrInvalidArgument, "ref id") 144 } 145 146 val, err := r.loadValue(ctx, ToRemoteObjectType(ref), ToRemoteClassName(ref), *ref.ObjectID) 147 148 if err != nil { 149 return nil, err 150 } 151 152 return drivers.ToElement(val) 153 } 154 155 func (r *Resolver) ToProperty( 156 ctx context.Context, 157 id runtime.RemoteObjectID, 158 propName string, 159 ) (core.Value, error) { 160 res, err := r.runtime.GetProperties( 161 ctx, 162 runtime.NewGetPropertiesArgs(id), 163 ) 164 165 if err != nil { 166 return values.None, err 167 } 168 169 if err := parseRuntimeException(res.ExceptionDetails); err != nil { 170 return values.None, err 171 } 172 173 for _, prop := range res.Result { 174 if prop.Name == propName { 175 if prop.Value != nil { 176 return r.ToValue(ctx, *prop.Value) 177 } 178 179 return values.None, nil 180 } 181 } 182 183 return values.None, nil 184 } 185 186 func (r *Resolver) ToProperties( 187 ctx context.Context, 188 id runtime.RemoteObjectID, 189 ) (*values.Array, error) { 190 res, err := r.runtime.GetProperties( 191 ctx, 192 runtime.NewGetPropertiesArgs(id), 193 ) 194 195 if err != nil { 196 return values.EmptyArray(), err 197 } 198 199 if err := parseRuntimeException(res.ExceptionDetails); err != nil { 200 return values.EmptyArray(), err 201 } 202 203 arr := values.NewArray(len(res.Result)) 204 205 for _, prop := range res.Result { 206 if prop.Value != nil { 207 val, err := r.ToValue(ctx, *prop.Value) 208 209 if err != nil { 210 return values.EmptyArray(), err 211 } 212 213 arr.Push(val) 214 } 215 } 216 217 return arr, nil 218 } 219 220 func (r *Resolver) loadValue(ctx context.Context, remoteType RemoteObjectType, remoteClass RemoteClassName, id runtime.RemoteObjectID) (core.Value, error) { 221 if r.loader == nil { 222 return values.None, core.Error(core.ErrNotImplemented, "ValueLoader") 223 } 224 225 return r.loader.Load(ctx, r.frameID, remoteType, remoteClass, id) 226 }