1 package playwright
2
3 import (
4 "fmt"
5 "log"
6 "reflect"
7 "sync"
8
9 "github.com/go-stack/stack"
10 )
11
12 type callback struct {
13 Data interface{}
14 Error error
15 }
16
17 type connection struct {
18 waitingForRemoteObjectsLock sync.Mutex
19 waitingForRemoteObjects map[string]chan interface{}
20 objects map[string]*channelOwner
21 lastID int
22 lastIDLock sync.Mutex
23 rootObject *rootChannelOwner
24 callbacks sync.Map
25 onClose func() error
26 onmessage func(map[string]interface{}) error
27 isRemote bool
28 }
29
30 func (c *connection) Start() *Playwright {
31 playwright := make(chan *Playwright, 1)
32 go func() {
33 pw, err := c.rootObject.initialize()
34 if err != nil {
35 log.Fatal(err)
36 return
37 }
38 playwright <- pw
39 }()
40 return <-playwright
41 }
42
43 func (c *connection) Stop() error {
44 return c.onClose()
45 }
46
47 func (c *connection) Dispatch(msg *message) {
48 method := msg.Method
49 if msg.ID != 0 {
50 cb, _ := c.callbacks.Load(msg.ID)
51 if msg.Error != nil {
52 cb.(chan callback) <- callback{
53 Error: parseError(msg.Error.Error),
54 }
55 } else {
56 cb.(chan callback) <- callback{
57 Data: c.replaceGuidsWithChannels(msg.Result),
58 }
59 }
60 return
61 }
62 object := c.objects[msg.GUID]
63 if method == "__create__" {
64 c.createRemoteObject(
65 object, msg.Params["type"].(string), msg.Params["guid"].(string), msg.Params["initializer"],
66 )
67 return
68 }
69 if object == nil {
70 return
71 }
72 if method == "__dispose__" {
73 object.dispose()
74 return
75 }
76 if object.objectType == "JsonPipe" {
77 object.channel.Emit(method, msg.Params)
78 } else {
79 object.channel.Emit(method, c.replaceGuidsWithChannels(msg.Params))
80 }
81 }
82
83 func (c *connection) createRemoteObject(parent *channelOwner, objectType string, guid string, initializer interface{}) interface{} {
84 initializer = c.replaceGuidsWithChannels(initializer)
85 result := createObjectFactory(parent, objectType, guid, initializer.(map[string]interface{}))
86 c.waitingForRemoteObjectsLock.Lock()
87 if _, ok := c.waitingForRemoteObjects[guid]; ok {
88 c.waitingForRemoteObjects[guid] <- result
89 delete(c.waitingForRemoteObjects, guid)
90 }
91 c.waitingForRemoteObjectsLock.Unlock()
92 return result
93 }
94
95 func (c *connection) replaceChannelsWithGuids(payload interface{}) interface{} {
96 if payload == nil {
97 return nil
98 }
99 if channel, isChannel := payload.(*channel); isChannel {
100 return map[string]string{
101 "guid": channel.guid,
102 }
103 }
104 v := reflect.ValueOf(payload)
105 if v.Kind() == reflect.Slice {
106 listV := make([]interface{}, 0)
107 for i := 0; i < v.Len(); i++ {
108 listV = append(listV, c.replaceChannelsWithGuids(v.Index(i).Interface()))
109 }
110 return listV
111 }
112 if v.Kind() == reflect.Map {
113 mapV := make(map[string]interface{})
114 for _, key := range v.MapKeys() {
115 mapV[key.String()] = c.replaceChannelsWithGuids(v.MapIndex(key).Interface())
116 }
117 return mapV
118 }
119 return payload
120 }
121
122 func (c *connection) replaceGuidsWithChannels(payload interface{}) interface{} {
123 if payload == nil {
124 return nil
125 }
126 v := reflect.ValueOf(payload)
127 if v.Kind() == reflect.Slice {
128 listV := payload.([]interface{})
129 for i := 0; i < len(listV); i++ {
130 listV[i] = c.replaceGuidsWithChannels(listV[i])
131 }
132 return listV
133 }
134 if v.Kind() == reflect.Map {
135 mapV := payload.(map[string]interface{})
136 if guid, hasGUID := mapV["guid"]; hasGUID {
137 if channelOwner, ok := c.objects[guid.(string)]; ok {
138 return channelOwner.channel
139 }
140 }
141 for key := range mapV {
142 mapV[key] = c.replaceGuidsWithChannels(mapV[key])
143 }
144 return mapV
145 }
146 return payload
147 }
148
149 func (c *connection) SendMessageToServer(guid string, method string, params interface{}) (interface{}, error) {
150 c.lastIDLock.Lock()
151 c.lastID++
152 id := c.lastID
153 c.lastIDLock.Unlock()
154 stack := serializeCallStack(stack.Trace())
155 metadata := make(map[string]interface{})
156 metadata["stack"] = stack
157 metadata["apiName"] = ""
158 message := map[string]interface{}{
159 "id": id,
160 "guid": guid,
161 "method": method,
162 "params": c.replaceChannelsWithGuids(params),
163 "metadata": metadata,
164 }
165 cb, _ := c.callbacks.LoadOrStore(id, make(chan callback))
166 if err := c.onmessage(message); err != nil {
167 return nil, fmt.Errorf("could not send message: %w", err)
168 }
169 result := <-cb.(chan callback)
170 c.callbacks.Delete(id)
171 if result.Error != nil {
172 return nil, result.Error
173 }
174 return result.Data, nil
175 }
176
177 func serializeCallStack(stack stack.CallStack) []map[string]interface{} {
178 callStack := make([]map[string]interface{}, 0)
179 for _, s := range stack {
180 callStack = append(callStack, map[string]interface{}{
181 "file": s.Frame().File,
182 "line": s.Frame().Line,
183 "function": s.Frame().Function,
184 })
185 }
186 return callStack
187 }
188
189 func newConnection(onClose func() error) *connection {
190 connection := &connection{
191 waitingForRemoteObjects: make(map[string]chan interface{}),
192 objects: make(map[string]*channelOwner),
193 onClose: onClose,
194 isRemote: false,
195 }
196 connection.rootObject = newRootChannelOwner(connection)
197 return connection
198 }
199
200 func fromChannel(v interface{}) interface{} {
201 return v.(*channel).object
202 }
203
204 func fromNullableChannel(v interface{}) interface{} {
205 if v == nil {
206 return nil
207 }
208 return fromChannel(v)
209 }
210
View as plain text