func (c *ChromeTarget) SendDefaultRequest(ctx context.Context, paramRequest *gcdmessage.ParamRequest) (*gcdmessage.ChromeResponse, error) {
	data, err := json.Marshal(&gcdmessage.ChromeRequest{Id: paramRequest.Id, Method: paramRequest.Method, Params: paramRequest.Params})
	if err != nil {
		return nil, err
	}
	c.messageObserver.Request(paramRequest.Id, paramRequest.Method, data)
	response, err := c.sendData(ctx, paramRequest.Id, data)
	c.messageObserver.Response(paramRequest.Id, paramRequest.Method, observer.DigResponseData(response), err)
	if err != nil {
		return nil, err
	}
	cerr := &gcdmessage.ChromeErrorResponse{}
	json.Unmarshal(response.Data, cerr)
	if cerr != nil && cerr.Error != nil {
		return nil, &gcdmessage.ChromeRequestErr{Resp: cerr}
	}
	chromeResponse := &gcdmessage.ChromeResponse{}
	err = json.Unmarshal(response.Data, chromeResponse)
	if err != nil {
		return nil, err
	}
	return chromeResponse, nil
}
SendDefaultRequest sends a generic request that gets back a generic response, or error. This returns a ChromeResponse
object.
package gcd
import (
	"context"
	"sync"
	"sync/atomic"
	"time"
	"github.com/wirepair/gcd/v2/observer"
	"github.com/wirepair/gcd/v2/gcdapi"
	"github.com/wirepair/gcd/v2/gcdmessage"
)
type TargetInfo struct {
	Description          string `json:"description"`
	DevtoolsFrontendUrl  string `json:"devtoolsFrontendUrl"`
	FaviconUrl           string `json:"faviconUrl"`
	Id                   string `json:"id"`
	Title                string `json:"title"`
	Type                 string `json:"type"`
	Url                  string `json:"url"`
	WebSocketDebuggerUrl string `json:"webSocketDebuggerUrl"`
}
type devtoolsEventResponse struct {
	Method string `json:"method"`
	Msg    []byte `json:"msg"`
}
type ChromeTarget struct {
	ctx    context.Context
	sendId int64  
	replyLock       sync.RWMutex                            
	replyDispatcher map[int64]chan *gcdmessage.Message      
	eventLock       sync.RWMutex                            
	eventDispatcher map[string]func(*ChromeTarget, []byte)  
	conn            *WebSocket                              
	Accessibility        *gcdapi.Accessibility
	Animation            *gcdapi.Animation
	ApplicationCache     *gcdapi.ApplicationCache  
	Audits               *gcdapi.Audits
	BackgroundService    *gcdapi.BackgroundService
	Browser              *gcdapi.Browser
	CacheStorage         *gcdapi.CacheStorage
	Cast                 *gcdapi.Cast
	Console              *gcdapi.Console            
	CSS                  *gcdapi.CSS                
	Database             *gcdapi.Database           
	Debugger             *gcdapi.Debugger           
	DeviceOrientation    *gcdapi.DeviceOrientation  
	DOM                  *gcdapi.DOM                
	DOMDebugger          *gcdapi.DOMDebugger        
	DOMSnapshot          *gcdapi.DOMSnapshot
	DOMStorage           *gcdapi.DOMStorage  
	Emulation            *gcdapi.Emulation
	Fetch                *gcdapi.Fetch
	HeadlessExperimental *gcdapi.HeadlessExperimental
	HeapProfiler         *gcdapi.HeapProfiler  
	IndexedDB            *gcdapi.IndexedDB     
	Input                *gcdapi.Input         
	Inspector            *gcdapi.Inspector
	IO                   *gcdapi.IO
	LayerTree            *gcdapi.LayerTree
	Log                  *gcdapi.Log
	Memory               *gcdapi.Memory
	Network              *gcdapi.Network
	Overlay              *gcdapi.Overlay
	Page                 *gcdapi.Page
	Performance          *gcdapi.Performance  
	PerformanceTimeline  *gcdapi.PerformanceTimeline
	Profiler             *gcdapi.Profiler
	Runtime              *gcdapi.Runtime
	Schema               *gcdapi.Schema
	Security             *gcdapi.Security
	ServiceWorker        *gcdapi.ServiceWorker
	Storage              *gcdapi.Storage
	SystemInfo           *gcdapi.SystemInfo
	TargetApi            *gcdapi.Target  
	Tracing              *gcdapi.Tracing
	Tethering            *gcdapi.Tethering
	Media                *gcdapi.Media
	WebAudio             *gcdapi.WebAudio
	WebAuthn             *gcdapi.WebAuthn
	Target          *TargetInfo                  
	sendCh          chan *gcdmessage.Message     
	eventCh         chan *devtoolsEventResponse  
	doneCh          chan struct{}                
	apiTimeout      time.Duration                
	logger          Log
	debugger        *Gcd
	stopped         bool  
	messageObserver observer.MessageObserver
}
func openChromeTarget(debugger *Gcd, target *TargetInfo, observer observer.MessageObserver) (*ChromeTarget, error) {
	conn, err := wsConnection(debugger.ctx, target.WebSocketDebuggerUrl)
	if err != nil {
		return nil, err
	}
	chromeTarget := &ChromeTarget{
		conn:            conn,
		ctx:             debugger.ctx,
		Target:          target,
		apiTimeout:      120 * time.Second,  
		sendCh:          make(chan *gcdmessage.Message),
		replyDispatcher: make(map[int64]chan *gcdmessage.Message),
		eventDispatcher: make(map[string]func(*ChromeTarget, []byte)),
		eventCh:         make(chan *devtoolsEventResponse, debugger.eventQueueSize),  
		doneCh:          make(chan struct{}),
		logger:          debugger.logger,
		debugger:        debugger,
		sendId:          0,
		messageObserver: observer,
	}
	chromeTarget.Init()
	chromeTarget.listen()
	return chromeTarget, nil
}
func (c *ChromeTarget) Init() {
	c.Accessibility = gcdapi.NewAccessibility(c)
	c.Animation = gcdapi.NewAnimation(c)
	c.ApplicationCache = gcdapi.NewApplicationCache(c)
	c.Audits = gcdapi.NewAudits(c)
	c.Browser = gcdapi.NewBrowser(c)
	c.BackgroundService = gcdapi.NewBackgroundService(c)
	c.CacheStorage = gcdapi.NewCacheStorage(c)
	c.Cast = gcdapi.NewCast(c)
	c.Console = gcdapi.NewConsole(c)
	c.CSS = gcdapi.NewCSS(c)
	c.Database = gcdapi.NewDatabase(c)
	c.Debugger = gcdapi.NewDebugger(c)
	c.DeviceOrientation = gcdapi.NewDeviceOrientation(c)
	c.DOMDebugger = gcdapi.NewDOMDebugger(c)
	c.DOM = gcdapi.NewDOM(c)
	c.DOMSnapshot = gcdapi.NewDOMSnapshot(c)
	c.DOMStorage = gcdapi.NewDOMStorage(c)
	c.Emulation = gcdapi.NewEmulation(c)
	c.HeapProfiler = gcdapi.NewHeapProfiler(c)
	c.IndexedDB = gcdapi.NewIndexedDB(c)
	c.Input = gcdapi.NewInput(c)
	c.Inspector = gcdapi.NewInspector(c)
	c.IO = gcdapi.NewIO(c)
	c.LayerTree = gcdapi.NewLayerTree(c)
	c.Memory = gcdapi.NewMemory(c)
	c.Log = gcdapi.NewLog(c)
	c.Network = gcdapi.NewNetwork(c)
	c.Overlay = gcdapi.NewOverlay(c)
	c.Page = gcdapi.NewPage(c)
	c.Profiler = gcdapi.NewProfiler(c)
	c.Runtime = gcdapi.NewRuntime(c)
	c.Schema = gcdapi.NewSchema(c)
	c.Security = gcdapi.NewSecurity(c)
	c.SystemInfo = gcdapi.NewSystemInfo(c)
	c.ServiceWorker = gcdapi.NewServiceWorker(c)
	c.TargetApi = gcdapi.NewTarget(c)
	c.Tracing = gcdapi.NewTracing(c)
	c.Tethering = gcdapi.NewTethering(c)
	c.HeadlessExperimental = gcdapi.NewHeadlessExperimental(c)
	c.Performance = gcdapi.NewPerformance(c)
	c.PerformanceTimeline = gcdapi.NewPerformanceTimeline(c)
	c.Fetch = gcdapi.NewFetch(c)
	c.Cast = gcdapi.NewCast(c)
	c.Media = gcdapi.NewMedia(c)
	c.WebAudio = gcdapi.NewWebAudio(c)
	c.WebAuthn = gcdapi.NewWebAuthn(c)
	c.BackgroundService = gcdapi.NewBackgroundService(c)
}
func (c *ChromeTarget) shutdown() {
	if c.stopped == true {
		return
	}
	c.stopped = true
	close(c.doneCh)
	c.conn.close()
}
func (c *ChromeTarget) SetApiTimeout(timeout time.Duration) {
	c.apiTimeout = timeout
}
func (c *ChromeTarget) GetApiTimeout() time.Duration {
	return c.apiTimeout
}
func (c *ChromeTarget) Subscribe(method string, callback func(*ChromeTarget, []byte)) {
	c.eventLock.Lock()
	c.eventDispatcher[method] = func(chromeTarget *ChromeTarget, data []byte) {
		c.messageObserver.Event(method, data)
		callback(chromeTarget, data)
	}
	c.eventLock.Unlock()
}
func (c *ChromeTarget) Unsubscribe(method string) {
	c.eventLock.Lock()
	delete(c.eventDispatcher, method)
	c.eventLock.Unlock()
}
func (c *ChromeTarget) listen() {
	go c.listenRead()
	go c.listenWrite()
	go c.dispatchEvents()
}
func (c *ChromeTarget) listenWrite() {
	for {
		select {
		case msg := <-c.sendCh:
			c.replyLock.Lock()
			c.replyDispatcher[msg.Id] = msg.ReplyCh
			c.replyLock.Unlock()
			c.logger.Println(msg.Id, " sending to chrome: ", string(msg.Data))
			err := c.conn.Send(msg.Data)
			if err != nil {
				c.logger.Println("error sending websocket message: ", err)
				return
			}
		case <-c.doneCh:
			return
		case <-c.ctx.Done():
			return
		}
	}
}
func (c *ChromeTarget) listenRead() {
	for {
		msg, err := c.conn.Read()
		if err != nil {
			c.shutdown()
			return
		}
		c.dispatchResponse(msg)
	}
}
type responseHeader struct {
	Id     int64  `json:"id"`
	Method string `json:"method"`
}
func (c *ChromeTarget) dispatchResponse(msg []byte) {
	f := &responseHeader{}
	err := json.Unmarshal(msg, f)
	if err != nil {
		c.logger.Println("error reading response data from chrome target", err)
	}
	c.replyLock.Lock()
	if r, ok := c.replyDispatcher[f.Id]; ok {
		delete(c.replyDispatcher, f.Id)
		c.replyLock.Unlock()
		c.logDebug("dispatching response id:", f.Id)
		go c.dispatchWithTimeout(r, f.Id, msg)
		return
	}
	c.replyLock.Unlock()
	c.checkTargetDisconnected(f.Method)
	c.eventLock.RLock()
	_, ok := c.eventDispatcher[f.Method]
	c.eventLock.RUnlock()
	if ok {
		c.eventCh <- &devtoolsEventResponse{Method: f.Method, Msg: msg}
		return
	}
	if c.debugger.debugEvents {
		c.logger.Println("no event reciever bound: ", f.Method, " data: ", string(msg))
	}
}
func (c *ChromeTarget) dispatchEvents() {
	for {
		select {
		case <-c.doneCh:
			return
		case <-c.ctx.Done():
			return
		case m := <-c.eventCh:
			c.logDebug("dispatching", m.Method, "event: ", string(m.Msg))
			c.eventLock.Lock()
			cb, ok := c.eventDispatcher[m.Method]
			c.eventLock.Unlock()
			if !ok {
				break
			}
			cb(c, m.Msg)
		}
	}
}
func (c *ChromeTarget) dispatchWithTimeout(r chan<- *gcdmessage.Message, id int64, msg []byte) {
	timeout := time.NewTimer(c.GetApiTimeout())
	defer timeout.Stop()
	select {
	case r <- &gcdmessage.Message{Id: id, Data: msg}:
		timeout.Stop()
	case <-timeout.C:
		c.logDebug("timed out dispatching request id: ", id, " of msg: ", msg)
		close(r)
		return
	}
	return
}
func (c *ChromeTarget) checkTargetDisconnected(method string) {
	switch method {
	case "Inspector.targetCrashed", "Inspector.detached":
		c.replyLock.Lock()
		for _, replyCh := range c.replyDispatcher {
			close(replyCh)
		}
		c.replyDispatcher = make(map[int64]chan *gcdmessage.Message)
		c.replyLock.Unlock()
	}
}
func wsConnection(ctx context.Context, url string) (*WebSocket, error) {
	client := &WebSocket{}
	if err := client.Connect(ctx, url, nil); err != nil {
		return nil, err
	}
	return client, nil
}
func (c *ChromeTarget) GetId() int64 {
	return atomic.AddInt64(&c.sendId, 1)
}
func (c *ChromeTarget) GetSendCh() chan *gcdmessage.Message {
	return c.sendCh
}
func (c *ChromeTarget) GetDoneCh() chan struct{} {
	return c.doneCh
}
func (c *ChromeTarget) logDebug(args ...interface{}) {
	if c.debugger.debug {
		c.logger.Println(args...)
	}
}
func (c *ChromeTarget) SendCustomReturn(ctx context.Context, paramRequest *gcdmessage.ParamRequest) (*gcdmessage.Message, error) {
	data, err := json.Marshal(paramRequest)
	if err != nil {
		return nil, err
	}
	c.messageObserver.Request(paramRequest.Id, paramRequest.Method, data)
	response, err := c.sendData(ctx, paramRequest.Id, data)
	c.messageObserver.Response(paramRequest.Id, paramRequest.Method, observer.DigResponseData(response), err)
	return response, err
}