diff --git a/CHANGELOG.md b/CHANGELOG.md index 02e4630..011eed1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,11 @@ ## 2.4.1-wip -- Bump minimum Dart version to 3.2.0 - Update the examples to use `WebSocketChannel.ready` and clarify that `WebSocketChannel.ready` should be awaited before sending data over the `WebSocketChannel`. - Mention `ready` in the docs for `connect`. +- Bump minimum Dart version to 3.2.0 +- Move to `pkg:web` to support WebAssembly compilation. ## 2.4.0 diff --git a/lib/html.dart b/lib/html.dart index 0cb7ffd..2263e6e 100644 --- a/lib/html.dart +++ b/lib/html.dart @@ -3,14 +3,16 @@ // BSD-style license that can be found in the LICENSE file. import 'dart:async'; -import 'dart:html'; +import 'dart:js_interop'; import 'dart:typed_data'; import 'package:async/async.dart'; import 'package:stream_channel/stream_channel.dart'; +import 'package:web/helpers.dart'; import 'src/channel.dart'; import 'src/exception.dart'; +import 'src/web_helpers.dart'; /// A [WebSocketChannel] that communicates using a `dart:html` [WebSocket]. class HtmlWebSocketChannel extends StreamChannelMixin @@ -73,8 +75,15 @@ class HtmlWebSocketChannel extends StreamChannelMixin /// [BinaryType.blob], they're delivered as [Blob]s instead. HtmlWebSocketChannel.connect(Object url, {Iterable? protocols, BinaryType? binaryType}) - : this(WebSocket(url.toString(), protocols) - ..binaryType = (binaryType ?? BinaryType.list).value); + : this( + WebSocket( + url.toString(), + (protocols?.toList() ?? const []) + .map((e) => e.toJS) + .toList() + .toJS, + )..binaryType = (binaryType ?? BinaryType.list).value, + ); /// Creates a channel wrapping [innerWebSocket]. HtmlWebSocketChannel(this.innerWebSocket) { @@ -109,11 +118,7 @@ class HtmlWebSocketChannel extends StreamChannelMixin _controller.local.sink.close(); }); - innerWebSocket.onMessage.listen((event) { - var data = event.data; - if (data is ByteBuffer) data = data.asUint8List(); - _controller.local.sink.add(data); - }); + innerWebSocket.onMessage.listen(_innerListen); // The socket API guarantees that only a single error event will be emitted, // and that once it is no other events will be emitted. @@ -124,16 +129,29 @@ class HtmlWebSocketChannel extends StreamChannelMixin }); } + void _innerListen(MessageEvent event) { + final eventData = event.data; + Object? data; + if (eventData.typeofEquals('object') && + (eventData as JSObject).instanceOfString('ArrayBuffer')) { + data = (eventData as JSArrayBuffer).toDart.asUint8List(); + } else { + data = event.data; + } + _controller.local.sink.add(data); + } + /// Pipes user events to [innerWebSocket]. void _listen() { - _controller.local.stream.listen(innerWebSocket.send, onDone: () { + _controller.local.stream.listen((obj) => innerWebSocket.send(obj!.jsify()!), + onDone: () { // On Chrome and possibly other browsers, `null` can't be passed as the // default here. The actual arity of the function call must be correct or // it will fail. if (_localCloseCode != null && _localCloseReason != null) { - innerWebSocket.close(_localCloseCode, _localCloseReason); + innerWebSocket.close(_localCloseCode!, _localCloseReason!); } else if (_localCloseCode != null) { - innerWebSocket.close(_localCloseCode); + innerWebSocket.close(_localCloseCode!); } else { innerWebSocket.close(); } diff --git a/lib/src/web_helpers.dart b/lib/src/web_helpers.dart new file mode 100644 index 0000000..7ef46e5 --- /dev/null +++ b/lib/src/web_helpers.dart @@ -0,0 +1,17 @@ +// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'package:web/helpers.dart'; + +// TODO(kevmoo): remove when https://github.com/dart-lang/web/commit/4cb5811ed06 +// is in a published release and the min constraint on pkg:web is updated +extension WebSocketEvents on WebSocket { + Stream get onOpen => EventStreamProviders.openEvent.forTarget(this); + Stream get onMessage => + EventStreamProviders.messageEvent.forTarget(this); + Stream get onClose => + EventStreamProviders.closeEvent.forTarget(this); + Stream get onError => + EventStreamProviders.errorEventSourceEvent.forTarget(this); +} diff --git a/pubspec.yaml b/pubspec.yaml index c772f4d..5c3b9cf 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -14,6 +14,7 @@ dependencies: async: ^2.5.0 crypto: ^3.0.0 stream_channel: ^2.1.0 + web: ^0.4.0 dev_dependencies: dart_flutter_team_lints: ^2.0.0 diff --git a/test/html_test.dart b/test/html_test.dart index d444d7c..9d91025 100644 --- a/test/html_test.dart +++ b/test/html_test.dart @@ -6,12 +6,14 @@ library; import 'dart:async'; -import 'dart:html'; +import 'dart:js_interop'; import 'dart:typed_data'; import 'package:async/async.dart'; import 'package:test/test.dart'; +import 'package:web/helpers.dart' hide BinaryType; import 'package:web_socket_channel/html.dart'; +import 'package:web_socket_channel/src/web_helpers.dart'; import 'package:web_socket_channel/web_socket_channel.dart'; void main() { @@ -176,6 +178,6 @@ void main() { Future> _decodeBlob(Blob blob) async { final reader = FileReader(); reader.readAsArrayBuffer(blob); - await reader.onLoad.first; - return reader.result as Uint8List; + await reader.onLoadEnd.first; + return (reader.result as JSArrayBuffer).toDart.asUint8List(); }