github.com/mdaxf/iac@v0.0.0-20240519030858-58a061660378/vendor_skip/nhooyr.io/websocket/internal/wsjs/wsjs_js.go (about)

     1  // +build js
     2  
     3  // Package wsjs implements typed access to the browser javascript WebSocket API.
     4  //
     5  // https://developer.mozilla.org/en-US/docs/Web/API/WebSocket
     6  package wsjs
     7  
     8  import (
     9  	"syscall/js"
    10  )
    11  
    12  func handleJSError(err *error, onErr func()) {
    13  	r := recover()
    14  
    15  	if jsErr, ok := r.(js.Error); ok {
    16  		*err = jsErr
    17  
    18  		if onErr != nil {
    19  			onErr()
    20  		}
    21  		return
    22  	}
    23  
    24  	if r != nil {
    25  		panic(r)
    26  	}
    27  }
    28  
    29  // New is a wrapper around the javascript WebSocket constructor.
    30  func New(url string, protocols []string) (c WebSocket, err error) {
    31  	defer handleJSError(&err, func() {
    32  		c = WebSocket{}
    33  	})
    34  
    35  	jsProtocols := make([]interface{}, len(protocols))
    36  	for i, p := range protocols {
    37  		jsProtocols[i] = p
    38  	}
    39  
    40  	c = WebSocket{
    41  		v: js.Global().Get("WebSocket").New(url, jsProtocols),
    42  	}
    43  
    44  	c.setBinaryType("arraybuffer")
    45  
    46  	return c, nil
    47  }
    48  
    49  // WebSocket is a wrapper around a javascript WebSocket object.
    50  type WebSocket struct {
    51  	v js.Value
    52  }
    53  
    54  func (c WebSocket) setBinaryType(typ string) {
    55  	c.v.Set("binaryType", string(typ))
    56  }
    57  
    58  func (c WebSocket) addEventListener(eventType string, fn func(e js.Value)) func() {
    59  	f := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
    60  		fn(args[0])
    61  		return nil
    62  	})
    63  	c.v.Call("addEventListener", eventType, f)
    64  
    65  	return func() {
    66  		c.v.Call("removeEventListener", eventType, f)
    67  		f.Release()
    68  	}
    69  }
    70  
    71  // CloseEvent is the type passed to a WebSocket close handler.
    72  type CloseEvent struct {
    73  	Code     uint16
    74  	Reason   string
    75  	WasClean bool
    76  }
    77  
    78  // OnClose registers a function to be called when the WebSocket is closed.
    79  func (c WebSocket) OnClose(fn func(CloseEvent)) (remove func()) {
    80  	return c.addEventListener("close", func(e js.Value) {
    81  		ce := CloseEvent{
    82  			Code:     uint16(e.Get("code").Int()),
    83  			Reason:   e.Get("reason").String(),
    84  			WasClean: e.Get("wasClean").Bool(),
    85  		}
    86  		fn(ce)
    87  	})
    88  }
    89  
    90  // OnError registers a function to be called when there is an error
    91  // with the WebSocket.
    92  func (c WebSocket) OnError(fn func(e js.Value)) (remove func()) {
    93  	return c.addEventListener("error", fn)
    94  }
    95  
    96  // MessageEvent is the type passed to a message handler.
    97  type MessageEvent struct {
    98  	// string or []byte.
    99  	Data interface{}
   100  
   101  	// There are more fields to the interface but we don't use them.
   102  	// See https://developer.mozilla.org/en-US/docs/Web/API/MessageEvent
   103  }
   104  
   105  // OnMessage registers a function to be called when the WebSocket receives a message.
   106  func (c WebSocket) OnMessage(fn func(m MessageEvent)) (remove func()) {
   107  	return c.addEventListener("message", func(e js.Value) {
   108  		var data interface{}
   109  
   110  		arrayBuffer := e.Get("data")
   111  		if arrayBuffer.Type() == js.TypeString {
   112  			data = arrayBuffer.String()
   113  		} else {
   114  			data = extractArrayBuffer(arrayBuffer)
   115  		}
   116  
   117  		me := MessageEvent{
   118  			Data: data,
   119  		}
   120  		fn(me)
   121  
   122  		return
   123  	})
   124  }
   125  
   126  // Subprotocol returns the WebSocket subprotocol in use.
   127  func (c WebSocket) Subprotocol() string {
   128  	return c.v.Get("protocol").String()
   129  }
   130  
   131  // OnOpen registers a function to be called when the WebSocket is opened.
   132  func (c WebSocket) OnOpen(fn func(e js.Value)) (remove func()) {
   133  	return c.addEventListener("open", fn)
   134  }
   135  
   136  // Close closes the WebSocket with the given code and reason.
   137  func (c WebSocket) Close(code int, reason string) (err error) {
   138  	defer handleJSError(&err, nil)
   139  	c.v.Call("close", code, reason)
   140  	return err
   141  }
   142  
   143  // SendText sends the given string as a text message
   144  // on the WebSocket.
   145  func (c WebSocket) SendText(v string) (err error) {
   146  	defer handleJSError(&err, nil)
   147  	c.v.Call("send", v)
   148  	return err
   149  }
   150  
   151  // SendBytes sends the given message as a binary message
   152  // on the WebSocket.
   153  func (c WebSocket) SendBytes(v []byte) (err error) {
   154  	defer handleJSError(&err, nil)
   155  	c.v.Call("send", uint8Array(v))
   156  	return err
   157  }
   158  
   159  func extractArrayBuffer(arrayBuffer js.Value) []byte {
   160  	uint8Array := js.Global().Get("Uint8Array").New(arrayBuffer)
   161  	dst := make([]byte, uint8Array.Length())
   162  	js.CopyBytesToGo(dst, uint8Array)
   163  	return dst
   164  }
   165  
   166  func uint8Array(src []byte) js.Value {
   167  	uint8Array := js.Global().Get("Uint8Array").New(len(src))
   168  	js.CopyBytesToJS(uint8Array, src)
   169  	return uint8Array
   170  }