...

Source file src/github.com/playwright-community/playwright-go/connection.go

Documentation: github.com/playwright-community/playwright-go

     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