diff --git a/.github/dependabot.yaml b/.github/dependabot.yaml new file mode 100644 index 0000000..edbfd65 --- /dev/null +++ b/.github/dependabot.yaml
@@ -0,0 +1,9 @@ +# Dependabot configuration file - enable regular dependencies checks. +version: 2 +enable-beta-ecosystems: true + +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly"
diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml new file mode 100644 index 0000000..a6d5ca6 --- /dev/null +++ b/.github/workflows/build.yaml
@@ -0,0 +1,35 @@ +name: Dart + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + - uses: dart-lang/setup-dart@v1.3 + - uses: nanasess/setup-chromedriver@v1.0.7 + + - name: Install dependencies + run: dart pub get + + - name: Validate formatting + run: dart format --output=none --set-exit-if-changed . + + - name: Analyze source code + run: dart analyze + + # Disabled; tracked via #75. + # - name: Run tests + # run: | + # export DISPLAY=:99 + # chromedriver --port=4444 --url-base=/wd/hub & + # sudo Xvfb -ac :99 -screen 0 1280x1024x24 > /dev/null 2>&1 & + # dart test + # env: + # CHROMEDRIVER_ARGS: '--no-sandbox --headless'
diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 0dfdf99..0000000 --- a/.travis.yml +++ /dev/null
@@ -1,17 +0,0 @@ -language: dart - -dart: - - dev - -addons: - chrome: stable - -services: - - xvfb - -script: ./tool/travis.sh - -# Only building master means that we don't run two builds for each pull request. -branches: - only: - - master
diff --git a/CHANGELOG.md b/CHANGELOG.md index e05301b..c175eda 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md
@@ -1,4 +1,18 @@ -# webkit_inspection_protocol.dart +## 1.1.0-dev + +- Have `ChromeConnection.getTabs` return better exceptions where there's a + failure setting up the Chrome connection (#85). +- Introduce a new, optional `retryFor` parameter to `ChromeConnection.getTabs`. + This will re-try failed connections for a period of time; it can be useful to + mitigate some intermittent connection issues very early in Chrome's startup. + +## 1.0.1 +- Use `package:lints` for analysis. +- Populate the pubspec `repository` field. +- Enable the `avoid_dynamic_calls` lint. + +## 1.0.0 +- Migrate to null safety. ## 0.7.5 - Allow the latest `logging` package.
diff --git a/README.md b/README.md index ff209a4..0d381ec 100644 --- a/README.md +++ b/README.md
@@ -1,9 +1,6 @@ -# webkit_inspection_protocol.dart - -[![Build Status](https://travis-ci.org/google/webkit_inspection_protocol.dart.svg)](https://travis-ci.org/google/webkit_inspection_protocol.dart) +[![Dart](https://github.com/google/webkit_inspection_protocol.dart/actions/workflows/build.yaml/badge.svg)](https://github.com/google/webkit_inspection_protocol.dart/actions/workflows/build.yaml) [![pub package](https://img.shields.io/pub/v/webkit_inspection_protocol.svg)](https://pub.dartlang.org/packages/webkit_inspection_protocol) - -## What is it? +[![package publisher](https://img.shields.io/pub/publisher/webkit_inspection_protocol.svg)](https://pub.dev/packages/webkit_inspection_protocol/publisher) The `webkit_inspection_protocol` package is a client for the Chrome DevTools Protocol (previously called the Webkit Inspection Protocol). It's used to talk to Chrome DevTools
diff --git a/analysis_options.yaml b/analysis_options.yaml index ab3a9b3..8051c3b 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml
@@ -1,16 +1,9 @@ +include: package:lints/recommended.yaml + analyzer: errors: deprecated_member_use_from_same_package: ignore linter: rules: - - always_declare_return_types - - avoid_init_to_null - - directives_ordering - - slash_for_doc_comments - - prefer_const_constructors - - prefer_const_constructors_in_immutables - - prefer_const_declarations - - prefer_const_literals_to_create_immutables - - prefer_final_fields - - type_annotate_public_apis + - avoid_dynamic_calls
diff --git a/example/multiplex.dart b/example/multiplex.dart index 6ed123d..8fc6e97 100644 --- a/example/multiplex.dart +++ b/example/multiplex.dart
@@ -14,7 +14,7 @@ import 'multiplex_impl.dart' show Server; void main(List<String> argv) async { - var args = (new ArgParser() + var args = (ArgParser() ..addFlag('verbose', abbr: 'v', defaultsTo: false, negatable: false) ..addFlag('model_dom', defaultsTo: false, negatable: true) ..addOption('chrome_host', defaultsTo: 'localhost') @@ -34,8 +34,8 @@ stderr.writeln('${rec.level.name}: ${rec.time}: ${rec.message}'); }); - var cr = new ChromeConnection( + var cr = ChromeConnection( args['chrome_host'] as String, int.parse(args['chrome_port'] as String)); - new Server(int.parse(args['listen_port'] as String), cr, + Server(int.parse(args['listen_port'] as String), cr, modelDom: args['model_dom'] as bool); }
diff --git a/example/multiplex_impl.dart b/example/multiplex_impl.dart index c2e5d5a..c8eeb34 100644 --- a/example/multiplex_impl.dart +++ b/example/multiplex_impl.dart
@@ -19,9 +19,9 @@ show ChromeConnection, ChromeTab, WipConnection; class Server { - static final _log = new Logger('Server'); + static final _log = Logger('Server'); - Future<HttpServer> _server; + Future<HttpServer>? _server; final ChromeConnection chrome; final int port; final bool modelDom; @@ -29,13 +29,13 @@ final _connections = <String, Future<WipConnection>>{}; final _modelDoms = <String, WipDomModel>{}; - Server(this.port, this.chrome, {this.modelDom}) { + Server(this.port, this.chrome, {this.modelDom = false}) { _server = io.serve(_handler, InternetAddress.anyIPv4, port); } shelf.Handler get _handler => const shelf.Pipeline() .addMiddleware(shelf.logRequests(logger: _shelfLogger)) - .addHandler(new shelf.Cascade() + .addHandler(shelf.Cascade() .add(_webSocket) .add(_mainPage) .add(_json) @@ -55,14 +55,13 @@ if (path.isEmpty) { var resp = await _mainPageHtml(); _log.info('mainPage: $resp'); - return new shelf.Response.ok(resp, - headers: {'Content-Type': 'text/html'}); + return shelf.Response.ok(resp, headers: {'Content-Type': 'text/html'}); } - return new shelf.Response.notFound(null); + return shelf.Response.notFound(null); } Future<String> _mainPageHtml() async { - var html = new StringBuffer(r'''<!DOCTYPE html> + var html = StringBuffer(r'''<!DOCTYPE html> <html> <head> <title>Chrome Windows</title> @@ -81,12 +80,15 @@ ..write('/devtools/page/') ..write(tab.id) ..write('">'); - if (tab.title != null && tab.title.isNotEmpty) { + if (tab.title != null && tab.title!.isNotEmpty) { html.write(tab.title); } else { html.write(tab.url); } - html..write('</a></td><td>')..write(tab.description)..write('</td></tr>'); + html + ..write('</a></td><td>') + ..write(tab.description) + ..write('</td></tr>'); } html.write(r'''</tbody> </table> @@ -100,10 +102,10 @@ if (path.length == 1 && path[0] == 'json') { var resp = jsonEncode(await chrome.getTabs(), toEncodable: _jsonEncode); _log.info('json: $resp'); - return new shelf.Response.ok(resp, + return shelf.Response.ok(resp, headers: {'Content-Type': 'application/json'}); } - return new shelf.Response.notFound(null); + return shelf.Response.notFound(null); } Future<shelf.Response> _forward(shelf.Request request) async { @@ -111,33 +113,35 @@ var dtResp = await chrome.getUrl(request.url.path); if (dtResp.statusCode == 200) { - return new shelf.Response.ok(dtResp, + return shelf.Response.ok(dtResp, headers: {'Content-Type': dtResp.headers.contentType.toString()}); } _log.warning( 'Forwarded ${request.url} returned statusCode: ${dtResp.statusCode}'); - return new shelf.Response.notFound(null); + return shelf.Response.notFound(null); } Future<shelf.Response> _webSocket(shelf.Request request) async { var path = request.url.pathSegments; if (path.length != 3 || path[0] != 'devtools' || path[1] != 'page') { - return new shelf.Response.notFound(null); + return shelf.Response.notFound(null); } _log.info('connecting to websocket: ${request.url}'); + // TODO: The first arg of webSocketHandler is untyped; consider refactoring + // (in package:web_socket_channel) to use a typedef. return ws.webSocketHandler((WebSocketChannel webSocket) async { var debugger = await _connections.putIfAbsent(path[2], () async { - var tab = await chrome.getTab((tab) => tab.id == path[2]); + var tab = (await chrome.getTab((tab) => tab.id == path[2]))!; return WipConnection.connect(tab.webSocketDebuggerUrl); }); - WipDomModel dom; + WipDomModel? dom; if (modelDom) { - dom = await _modelDoms.putIfAbsent(path[2], () { - return new WipDomModel(debugger.dom); + dom = _modelDoms.putIfAbsent(path[2], () { + return WipDomModel(debugger.dom); }); } - var forwarder = new WipForwarder(debugger, webSocket.stream.cast(), + var forwarder = WipForwarder(debugger, webSocket.stream.cast(), sink: webSocket.sink, domModel: dom); debugger.onClose.listen((_) { _connections.remove(path[2]); @@ -149,12 +153,12 @@ Future close() async { if (_server != null) { - await (await _server).close(force: true); + await (await _server!).close(force: true); _server = null; } } - Object _jsonEncode(Object obj) { + Object? _jsonEncode(Object? obj) { if (obj is ChromeTab) { var json = <String, dynamic>{ 'description': obj.description,
diff --git a/lib/dom_model.dart b/lib/dom_model.dart index 72e70da..9bb5f05 100644 --- a/lib/dom_model.dart +++ b/lib/dom_model.dart
@@ -26,55 +26,55 @@ /// Implementation of WipDom that maintains and updates a model of the DOM /// based on incoming events. class WipDomModel implements WipDom { - static final _log = new Logger('WipDomModel'); + static final _log = Logger('WipDomModel'); final WipDom _dom; final Map<int, _Node> _nodeCache = {}; - Future<_Node> _root; + Future<_Node>? _root; - Stream<AttributeModifiedEvent> onAttributeModified; - Stream<AttributeRemovedEvent> onAttributeRemoved; - Stream<CharacterDataModifiedEvent> onCharacterDataModified; - Stream<ChildNodeCountUpdatedEvent> onChildNodeCountUpdated; - Stream<ChildNodeInsertedEvent> onChildNodeInserted; - Stream<ChildNodeRemovedEvent> onChildNodeRemoved; - Stream<DocumentUpdatedEvent> onDocumentUpdated; - Stream<SetChildNodesEvent> onSetChildNodes; + @override + late final Stream<AttributeModifiedEvent> onAttributeModified = + StreamTransformer.fromHandlers(handleData: _onAttributeModified) + .bind(_dom.onAttributeModified); + @override + late final Stream<AttributeRemovedEvent> onAttributeRemoved = + StreamTransformer.fromHandlers(handleData: _onAttributeRemoved) + .bind(_dom.onAttributeRemoved); + @override + late final Stream<CharacterDataModifiedEvent> onCharacterDataModified = + StreamTransformer.fromHandlers(handleData: _onCharacterDataModified) + .bind(_dom.onCharacterDataModified); + @override + late final Stream<ChildNodeCountUpdatedEvent> onChildNodeCountUpdated = + StreamTransformer.fromHandlers(handleData: _onChildNodeCountUpdated) + .bind(_dom.onChildNodeCountUpdated); + @override + late final Stream<ChildNodeInsertedEvent> onChildNodeInserted = + StreamTransformer.fromHandlers(handleData: _onChildNodeInserted) + .bind(_dom.onChildNodeInserted); + @override + late final Stream<ChildNodeRemovedEvent> onChildNodeRemoved = + StreamTransformer.fromHandlers(handleData: _onChildNodeRemoved) + .bind(_dom.onChildNodeRemoved); + @override + late final Stream<DocumentUpdatedEvent> onDocumentUpdated = + StreamTransformer.fromHandlers(handleData: _onDocumentUpdated) + .bind(_dom.onDocumentUpdated); + @override + late final Stream<SetChildNodesEvent> onSetChildNodes = + StreamTransformer.fromHandlers(handleData: _onSetChildNodes) + .bind(_dom.onSetChildNodes); WipDomModel(this._dom) { - onAttributeModified = - new StreamTransformer.fromHandlers(handleData: _onAttributeModified) - .bind(_dom.onAttributeModified) - ..listen(_logEvent); - onAttributeRemoved = - new StreamTransformer.fromHandlers(handleData: _onAttributeRemoved) - .bind(_dom.onAttributeRemoved) - ..listen(_logEvent); - onCharacterDataModified = - new StreamTransformer.fromHandlers(handleData: _onCharacterDataModified) - .bind(_dom.onCharacterDataModified) - ..listen(_logEvent); - onChildNodeCountUpdated = - new StreamTransformer.fromHandlers(handleData: _onChildNodeCountUpdated) - .bind(_dom.onChildNodeCountUpdated) - ..listen(_logEvent); - onChildNodeInserted = - new StreamTransformer.fromHandlers(handleData: _onChildNodeInserted) - .bind(_dom.onChildNodeInserted) - ..listen(_logEvent); - onChildNodeRemoved = - new StreamTransformer.fromHandlers(handleData: _onChildNodeRemoved) - .bind(_dom.onChildNodeRemoved) - ..listen(_logEvent); - onDocumentUpdated = - new StreamTransformer.fromHandlers(handleData: _onDocumentUpdated) - .bind(_dom.onDocumentUpdated) - ..listen(_logEvent); - onSetChildNodes = - new StreamTransformer.fromHandlers(handleData: _onSetChildNodes) - .bind(_dom.onSetChildNodes) - ..listen(_logEvent); + onAttributeModified.listen(_logEvent); + onAttributeRemoved.listen(_logEvent); + onCharacterDataModified.listen(_logEvent); + onChildNodeCountUpdated.listen(_logEvent); + onChildNodeInserted.listen(_logEvent); + onChildNodeRemoved.listen(_logEvent); + onDocumentUpdated.listen(_logEvent); + onSetChildNodes.listen(_logEvent); } void _logEvent(WipEvent event) { @@ -84,14 +84,14 @@ void _onAttributeModified( AttributeModifiedEvent event, EventSink<AttributeModifiedEvent> sink) { var node = _getOrCreateNode(event.nodeId); - node._attributes[event.name] = event.value; + node._attributes![event.name] = event.value; sink.add(event); } void _onAttributeRemoved( AttributeRemovedEvent event, EventSink<AttributeRemovedEvent> sink) { var node = _getOrCreateNode(event.nodeId); - node._attributes.remove(event.name); + node._attributes!.remove(event.name); sink.add(event); } @@ -112,14 +112,11 @@ void _onChildNodeInserted( ChildNodeInsertedEvent event, EventSink<ChildNodeInsertedEvent> sink) { var parent = _getOrCreateNode(event.parentNodeId); - int index = 0; - if (event.previousNodeId != null) { - index = - parent._children.indexOf(_getOrCreateNode(event.previousNodeId)) + 1; - } + int index = + parent._children!.indexOf(_getOrCreateNode(event.previousNodeId)) + 1; var node = _getOrCreateNodeFromNode(event.node); - parent._children.insert(index, node); - parent._childNodeCount = parent._children.length; + parent._children!.insert(index, node); + parent._childNodeCount = parent._children!.length; sink.add(event); } @@ -127,8 +124,8 @@ ChildNodeRemovedEvent event, EventSink<ChildNodeRemovedEvent> sink) { var parent = _getOrCreateNode(event.parentNodeId); var node = _nodeCache.remove(event.nodeId); - parent._children.remove(node); - parent._childNodeCount = parent._children.length; + parent._children!.remove(node); + parent._childNodeCount = parent._children!.length; sink.add(event); } @@ -144,7 +141,7 @@ var parent = _getOrCreateNode(event.nodeId); parent._children = event.nodes.map(_getOrCreateNodeFromNode).toList(growable: true); - parent._childNodeCount = parent._children.length; + parent._childNodeCount = parent._children!.length; sink.add(event); } @@ -152,7 +149,7 @@ Future<Map<String, String>> getAttributes(int nodeId) async { Map<String, String> attributes = await _dom.getAttributes(nodeId); var node = _getOrCreateNode(nodeId); - node._attributes = new Map.from(attributes); + node._attributes = Map.from(attributes); return attributes; } @@ -161,28 +158,26 @@ /// multiple times on the same page. @override Future<Node> getDocument() { - if (_root == null) { - _root = _dom.getDocument().then((n) => _getOrCreateNodeFromNode(n)); - } - return _root; + _root ??= _dom.getDocument().then((n) => _getOrCreateNodeFromNode(n)); + return _root!; } _Node _getOrCreateNode(int nodeId) => - _nodeCache.putIfAbsent(nodeId, () => new _Node(nodeId)); + _nodeCache.putIfAbsent(nodeId, () => _Node(nodeId)); _Node _getOrCreateNodeFromNode(Node src) { try { var node = _getOrCreateNode(src.nodeId); if (src.attributes != null) { - node._attributes = new Map.from(src.attributes); + node._attributes = Map.of(src.attributes!); } if (src.children != null) { node._children = - src.children.map(_getOrCreateNodeFromNode).toList(growable: true); + src.children!.map(_getOrCreateNodeFromNode).toList(growable: true); } - node._childNodeCount = src.childNodeCount; + node._childNodeCount = src.childNodeCount ?? 0; if (src.contentDocument != null) { - node._contentDocument = _getOrCreateNodeFromNode(src.contentDocument); + node._contentDocument = _getOrCreateNodeFromNode(src.contentDocument!); } node._documentUrl = src.documentUrl; node._internalSubset = src.internalSubset; @@ -202,94 +197,95 @@ } } + @override dynamic noSuchMethod(Invocation invocation) => reflect(_dom).delegate(invocation); } class _Node implements Node { - Map<String, String> _attributes; + Map<String, String>? _attributes; @override - Map<String, String> get attributes => - _attributes != null ? new UnmodifiableMapView(_attributes) : null; + Map<String, String>? get attributes => + _attributes != null ? UnmodifiableMapView(_attributes!) : null; - int _childNodeCount; + int? _childNodeCount; @override - int get childNodeCount => _childNodeCount; + int? get childNodeCount => _childNodeCount; - List<_Node> _children; + List<_Node>? _children; @override - List<Node> get children => - _children != null ? new UnmodifiableListView(_children) : null; + List<Node>? get children => + _children != null ? UnmodifiableListView(_children!) : null; - _Node _contentDocument; + _Node? _contentDocument; @override - Node get contentDocument => _contentDocument; + Node? get contentDocument => _contentDocument; - String _documentUrl; + String? _documentUrl; @override - String get documentUrl => _documentUrl; + String? get documentUrl => _documentUrl; - String _internalSubset; + String? _internalSubset; @override - String get internalSubset => _internalSubset; + String? get internalSubset => _internalSubset; - String _localName; + String? _localName; @override - String get localName => _localName; + String get localName => _localName!; - String _name; + String? _name; @override - String get name => _name; + String? get name => _name; @override final int nodeId; - String _nodeName; + String? _nodeName; @override - String get nodeName => _nodeName; + String get nodeName => _nodeName!; - int _nodeType; + int? _nodeType; @override - int get nodeType => _nodeType; + int get nodeType => _nodeType!; - String _nodeValue; + String? _nodeValue; @override - String get nodeValue => _nodeValue; + String get nodeValue => _nodeValue!; - String _publicId; + String? _publicId; @override - String get publicId => _publicId; + String? get publicId => _publicId; - String _systemId; + String? _systemId; @override - String get systemId => _systemId; + String? get systemId => _systemId; - String _value; + String? _value; @override - String get value => _value; + String? get value => _value; - String _xmlVersion; + String? _xmlVersion; @override - String get xmlVersion => _xmlVersion; + String? get xmlVersion => _xmlVersion; _Node(this.nodeId); - Map toJson() => _toJsonInternal(new Set()); + Map toJson() => _toJsonInternal({}); Map _toJsonInternal(Set visited) { var map = { @@ -300,44 +296,42 @@ 'nodeValue': nodeValue }; if (visited.add(nodeId)) { - if (attributes != null && attributes.isNotEmpty) { - map['attributes'] = flattenAttributesMap(attributes); + if (attributes != null && attributes!.isNotEmpty) { + map['attributes'] = flattenAttributesMap(attributes!); } if (childNodeCount != null) { - map['childNodeCount'] = childNodeCount; + map['childNodeCount'] = childNodeCount!; } - if (_children != null && _children.isNotEmpty) { + if (_children != null && _children!.isNotEmpty) { var newChildren = []; - _children.forEach((child) { - if (child != null) { - newChildren.add(child._toJsonInternal(visited)); - } - }); + for (var child in _children!) { + newChildren.add(child._toJsonInternal(visited)); + } map['children'] = newChildren; } if (_contentDocument != null) { - map['contentDocument'] = _contentDocument._toJsonInternal(visited); + map['contentDocument'] = _contentDocument!._toJsonInternal(visited); } if (documentUrl != null) { - map['documentUrl'] = documentUrl; + map['documentUrl'] = documentUrl!; } if (internalSubset != null) { - map['internalSubset'] = internalSubset; + map['internalSubset'] = internalSubset!; } if (name != null) { - map['name'] = name; + map['name'] = name!; } if (publicId != null) { - map['publicId'] = publicId; + map['publicId'] = publicId!; } if (systemId != null) { - map['systemId'] = systemId; + map['systemId'] = systemId!; } if (value != null) { - map['value'] = value; + map['value'] = value!; } if (xmlVersion != null) { - map['xmlVersion'] = xmlVersion; + map['xmlVersion'] = xmlVersion!; } } return map; @@ -347,9 +341,9 @@ List<String> flattenAttributesMap(Map<String, String> attributes) { var result = <String>[]; attributes.forEach((k, v) { - if (k != null) { - result..add(k)..add(v); - } + result + ..add(k) + ..add(v); }); return result; }
diff --git a/lib/forwarder.dart b/lib/forwarder.dart index 85f9f47..16a1ac0 100644 --- a/lib/forwarder.dart +++ b/lib/forwarder.dart
@@ -16,12 +16,12 @@ /// Forwards a [Stream] to a [WipConnection] and events /// from a [WipConnection] to a [StreamSink]. class WipForwarder { - static final _log = new Logger('ChromeForwarder'); + static final _log = Logger('ChromeForwarder'); final Stream<String> _in; final StreamSink _out; final WipConnection _debugger; - final WipDom domModel; + final WipDom? domModel; /// If false, no Debugger.paused events will be forwarded back to the client. /// This gets automatically set to true if a breakpoint is set by the client. @@ -29,15 +29,12 @@ final List<StreamSubscription> _subscriptions = <StreamSubscription>[]; - final StreamController<Null> _closedController = - new StreamController.broadcast(); + final StreamController<void> _closedController = StreamController.broadcast(); factory WipForwarder(WipConnection debugger, Stream<String> stream, - {StreamSink sink, WipDom domModel}) { - if (sink == null) { - sink = stream as StreamSink; - } - return new WipForwarder._(debugger, stream, sink, domModel); + {StreamSink? sink, WipDom? domModel}) { + sink ??= stream as StreamSink; + return WipForwarder._(debugger, stream, sink, domModel); } WipForwarder._(this._debugger, this._in, this._out, this.domModel) { @@ -48,7 +45,7 @@ } Future _onClientDataHandler(String data) async { - var json = jsonDecode(data); + var json = jsonDecode(data) as Map<String, dynamic>; var response = {'id': json['id']}; _log.info('Forwarding to debugger: $data'); try { @@ -63,12 +60,12 @@ if (domModel != null) { switch (method) { case 'DOM.getDocument': - response['result'] = {'root': (await domModel.getDocument())}; + response['result'] = {'root': (await domModel!.getDocument())}; processed = true; break; case 'DOM.getAttributes': var attributes = flattenAttributesMap( - await domModel.getAttributes(params['nodeId'] as int)); + await domModel!.getAttributes(params['nodeId'] as int)); response['result'] = {'attributes': attributes}; processed = true; break; @@ -125,21 +122,27 @@ void pause() { assert(_subscriptions.isNotEmpty); _log.info('Pausing forwarding'); - _subscriptions.forEach((s) => s.pause()); + for (var s in _subscriptions) { + s.pause(); + } _subscriptions.clear(); } void resume() { assert(_subscriptions.isNotEmpty); _log.info('Resuming forwarding'); - _subscriptions.forEach((s) => s.resume()); + for (var s in _subscriptions) { + s.resume(); + } _subscriptions.clear(); } Future stop() { assert(_subscriptions.isNotEmpty); _log.info('Stopping forwarding'); - _subscriptions.forEach((s) => s.cancel()); + for (var s in _subscriptions) { + s.cancel(); + } _subscriptions.clear(); _closedController.add(null); return Future.wait([_closedController.close(), _out.close()]);
diff --git a/lib/src/console.dart b/lib/src/console.dart index e1500d7..4864a9f 100644 --- a/lib/src/console.dart +++ b/lib/src/console.dart
@@ -17,33 +17,34 @@ Stream<ConsoleMessageEvent> get onMessage => eventStream( 'Console.messageAdded', - (WipEvent event) => new ConsoleMessageEvent(event.json)); + (WipEvent event) => ConsoleMessageEvent(event.json)); Stream<ConsoleClearedEvent> get onCleared => eventStream( 'Console.messagesCleared', - (WipEvent event) => new ConsoleClearedEvent(event.json)); + (WipEvent event) => ConsoleClearedEvent(event.json)); } class ConsoleMessageEvent extends WipEvent { ConsoleMessageEvent(Map<String, dynamic> json) : super(json); - Map get _message => params['message'] as Map; + Map get _message => params!['message'] as Map; String get text => _message['text'] as String; String get level => _message['level'] as String; - String get url => _message['url'] as String; + String? get url => _message['url'] as String?; Iterable<WipConsoleCallFrame> getStackTrace() { if (_message.containsKey('stackTrace')) { - return (params['stackTrace'] as List).map((frame) => - new WipConsoleCallFrame.fromMap(frame as Map<String, dynamic>)); + return (params!['stackTrace'] as List).map((frame) => + WipConsoleCallFrame.fromMap(frame as Map<String, dynamic>)); } else { return []; } } + @override String toString() => text; }
diff --git a/lib/src/debugger.dart b/lib/src/debugger.dart index a98f2c9..73bf2ca 100644 --- a/lib/src/debugger.dart +++ b/lib/src/debugger.dart
@@ -25,19 +25,19 @@ Future<String> getScriptSource(String scriptId) async { return (await sendCommand('Debugger.getScriptSource', params: {'scriptId': scriptId})) - .result['scriptSource'] as String; + .result!['scriptSource'] as String; } Future<WipResponse> pause() => sendCommand('Debugger.pause'); Future<WipResponse> resume() => sendCommand('Debugger.resume'); - Future<WipResponse> stepInto({Map<String, dynamic> params}) => + Future<WipResponse> stepInto({Map<String, dynamic>? params}) => sendCommand('Debugger.stepInto', params: params); Future<WipResponse> stepOut() => sendCommand('Debugger.stepOut'); - Future<WipResponse> stepOver({Map<String, dynamic> params}) => + Future<WipResponse> stepOver({Map<String, dynamic>? params}) => sendCommand('Debugger.stepOver', params: params); Future<WipResponse> setPauseOnExceptions(PauseState state) { @@ -53,7 +53,7 @@ /// evaluates to true. Future<SetBreakpointResponse> setBreakpoint( WipLocation location, { - String condition, + String? condition, }) async { Map<String, dynamic> params = { 'location': location.toJsonMap(), @@ -65,11 +65,11 @@ final WipResponse response = await sendCommand('Debugger.setBreakpoint', params: params); - if (response.result.containsKey('exceptionDetails')) { - throw new ExceptionDetails( - response.result['exceptionDetails'] as Map<String, dynamic>); + if (response.result!.containsKey('exceptionDetails')) { + throw ExceptionDetails( + response.result!['exceptionDetails'] as Map<String, dynamic>); } else { - return new SetBreakpointResponse(response.json); + return SetBreakpointResponse(response.json); } } @@ -88,7 +88,7 @@ Future<RemoteObject> evaluateOnCallFrame( String callFrameId, String expression, { - bool returnByValue, + bool? returnByValue, }) async { Map<String, dynamic> params = { 'callFrameId': callFrameId, @@ -101,12 +101,11 @@ final WipResponse response = await sendCommand('Debugger.evaluateOnCallFrame', params: params); - if (response.result.containsKey('exceptionDetails')) { - throw new ExceptionDetails( - response.result['exceptionDetails'] as Map<String, dynamic>); + if (response.result!.containsKey('exceptionDetails')) { + throw ExceptionDetails( + response.result!['exceptionDetails'] as Map<String, dynamic>); } else { - return new RemoteObject( - response.result['result'] as Map<String, dynamic>); + return RemoteObject(response.result!['result'] as Map<String, dynamic>); } } @@ -120,8 +119,8 @@ /// (non-nested) function as start. Future<List<WipBreakLocation>> getPossibleBreakpoints( WipLocation start, { - WipLocation end, - bool restrictToFunction, + WipLocation? end, + bool? restrictToFunction, }) async { Map<String, dynamic> params = { 'start': start.toJsonMap(), @@ -136,11 +135,11 @@ final WipResponse response = await sendCommand('Debugger.getPossibleBreakpoints', params: params); - if (response.result.containsKey('exceptionDetails')) { - throw new ExceptionDetails( - response.result['exceptionDetails'] as Map<String, dynamic>); + if (response.result!.containsKey('exceptionDetails')) { + throw ExceptionDetails( + response.result!['exceptionDetails'] as Map<String, dynamic>); } else { - List locations = response.result['locations']; + List locations = response.result!['locations']; return List.from(locations.map((map) => WipBreakLocation(map))); } } @@ -155,21 +154,21 @@ }); } - Stream<DebuggerPausedEvent> get onPaused => eventStream('Debugger.paused', - (WipEvent event) => new DebuggerPausedEvent(event.json)); + Stream<DebuggerPausedEvent> get onPaused => eventStream( + 'Debugger.paused', (WipEvent event) => DebuggerPausedEvent(event.json)); Stream<GlobalObjectClearedEvent> get onGlobalObjectCleared => eventStream( 'Debugger.globalObjectCleared', - (WipEvent event) => new GlobalObjectClearedEvent(event.json)); + (WipEvent event) => GlobalObjectClearedEvent(event.json)); - Stream<DebuggerResumedEvent> get onResumed => eventStream('Debugger.resumed', - (WipEvent event) => new DebuggerResumedEvent(event.json)); + Stream<DebuggerResumedEvent> get onResumed => eventStream( + 'Debugger.resumed', (WipEvent event) => DebuggerResumedEvent(event.json)); Stream<ScriptParsedEvent> get onScriptParsed => eventStream( 'Debugger.scriptParsed', - (WipEvent event) => new ScriptParsedEvent(event.json)); + (WipEvent event) => ScriptParsedEvent(event.json)); - Map<String, WipScript> get scripts => new UnmodifiableMapView(_scripts); + Map<String, WipScript> get scripts => UnmodifiableMapView(_scripts); } String _pauseStateToString(PauseState state) { @@ -181,7 +180,7 @@ case PauseState.uncaught: return 'uncaught'; default: - throw new ArgumentError('unknown state: $state'); + throw ArgumentError('unknown state: $state'); } } @@ -190,8 +189,9 @@ class ScriptParsedEvent extends WipEvent { ScriptParsedEvent(Map<String, dynamic> json) : super(json); - WipScript get script => new WipScript(params); + WipScript get script => WipScript(params!); + @override String toString() => script.toString(); } @@ -209,31 +209,32 @@ DebuggerPausedEvent(Map<String, dynamic> json) : super(json); /// Call stack the virtual machine stopped on. - List<WipCallFrame> getCallFrames() => (params['callFrames'] as List) - .map((frame) => new WipCallFrame(frame as Map<String, dynamic>)) + List<WipCallFrame> getCallFrames() => (params!['callFrames'] as List) + .map((frame) => WipCallFrame(frame as Map<String, dynamic>)) .toList(); /// Pause reason. /// /// Allowed Values: ambiguous, assert, debugCommand, DOM, EventListener, /// exception, instrumentation, OOM, other, promiseRejection, XHR. - String get reason => params['reason'] as String; + String get reason => params!['reason'] as String; /// Object containing break-specific auxiliary properties. - Object get data => params['data']; + Object? get data => params!['data']; /// Hit breakpoints IDs (optional). - List<String> get hitBreakpoints { - if (params['hitBreakpoints'] == null) return null; - return (params['hitBreakpoints'] as List).cast<String>(); + List<String>? get hitBreakpoints { + if (params!['hitBreakpoints'] == null) return null; + return (params!['hitBreakpoints'] as List).cast<String>(); } /// Async stack trace, if any. - StackTrace get asyncStackTrace => params['asyncStackTrace'] == null + StackTrace? get asyncStackTrace => params!['asyncStackTrace'] == null ? null - : StackTrace(params['asyncStackTrace']); + : StackTrace(params!['asyncStackTrace']); - String toString() => 'paused: ${reason}'; + @override + String toString() => 'paused: $reason'; } /// A debugger call frame. @@ -254,29 +255,30 @@ /// Location in the source code. WipLocation get location => - new WipLocation(json['location'] as Map<String, dynamic>); + WipLocation(json['location'] as Map<String, dynamic>); /// JavaScript script name or url. String get url => json['url'] as String; /// Scope chain for this call frame. Iterable<WipScope> getScopeChain() => (json['scopeChain'] as List) - .map((scope) => new WipScope(scope as Map<String, dynamic>)); + .map((scope) => WipScope(scope as Map<String, dynamic>)); /// `this` object for this call frame. RemoteObject get thisObject => - new RemoteObject(json['this'] as Map<String, dynamic>); + RemoteObject(json['this'] as Map<String, dynamic>); /// The value being returned, if the function is at return point. /// /// (optional) - RemoteObject get returnValue { + RemoteObject? get returnValue { return json.containsKey('returnValue') - ? new RemoteObject(json['returnValue'] as Map<String, dynamic>) + ? RemoteObject(json['returnValue'] as Map<String, dynamic>) : null; } - String toString() => '[${functionName}]'; + @override + String toString() => '[$functionName]'; } class WipLocation { @@ -284,7 +286,7 @@ WipLocation(this.json); - WipLocation.fromValues(String scriptId, int lineNumber, {int columnNumber}) + WipLocation.fromValues(String scriptId, int lineNumber, {int? columnNumber}) : json = {} { json['scriptId'] = scriptId; json['lineNumber'] = lineNumber; @@ -297,13 +299,14 @@ int get lineNumber => json['lineNumber']; - int get columnNumber => json['columnNumber']; + int? get columnNumber => json['columnNumber']; Map<String, dynamic> toJsonMap() { return json; } - String toString() => '[${scriptId}:${lineNumber}:${columnNumber}]'; + @override + String toString() => '[$scriptId:$lineNumber:$columnNumber]'; } class WipScript { @@ -323,11 +326,12 @@ int get endColumn => json['endColumn'] as int; - bool get isContentScript => json['isContentScript'] as bool; + bool? get isContentScript => json['isContentScript'] as bool?; - String get sourceMapURL => json['sourceMapURL'] as String; + String? get sourceMapURL => json['sourceMapURL'] as String?; - String toString() => '[script ${scriptId}: ${url}]'; + @override + String toString() => '[script $scriptId: $url]'; } class WipScope { @@ -339,20 +343,20 @@ String get scope => json['type'] as String; /// Name of the scope, null if unnamed closure or global scope - String get name => json['name'] as String; + String? get name => json['name'] as String?; /// Object representing the scope. For global and with scopes it represents /// the actual object; for the rest of the scopes, it is artificial transient /// object enumerating scope variables as its properties. RemoteObject get object => - new RemoteObject(json['object'] as Map<String, dynamic>); + RemoteObject(json['object'] as Map<String, dynamic>); } class WipBreakLocation extends WipLocation { WipBreakLocation(Map<String, dynamic> json) : super(json); WipBreakLocation.fromValues(String scriptId, int lineNumber, - {int columnNumber, String type}) + {int? columnNumber, String? type}) : super.fromValues(scriptId, lineNumber, columnNumber: columnNumber) { if (type != null) { json['type'] = type; @@ -360,14 +364,14 @@ } /// Allowed Values: `debuggerStatement`, `call`, `return`. - String get type => json['type']; + String? get type => json['type'] as String?; } /// The response from [WipDebugger.setBreakpoint]. class SetBreakpointResponse extends WipResponse { SetBreakpointResponse(Map<String, dynamic> json) : super(json); - String get breakpointId => result['breakpointId']; + String get breakpointId => result!['breakpointId']; - WipLocation get actualLocation => WipLocation(result['actualLocation']); + WipLocation get actualLocation => WipLocation(result!['actualLocation']); }
diff --git a/lib/src/dom.dart b/lib/src/dom.dart index d1fd1e5..7c5433f 100644 --- a/lib/src/dom.dart +++ b/lib/src/dom.dart
@@ -14,52 +14,43 @@ Future<Map<String, String>> getAttributes(int nodeId) async { WipResponse resp = await sendCommand('DOM.getAttributes', params: {'nodeId': nodeId}); - return _attributeListToMap((resp.result['attributes'] as List).cast()); + return _attributeListToMap((resp.result!['attributes'] as List).cast()); } Future<Node> getDocument() async => - new Node((await sendCommand('DOM.getDocument')).result['root'] + Node((await sendCommand('DOM.getDocument')).result!['root'] as Map<String, dynamic>); Future<String> getOuterHtml(int nodeId) async => (await sendCommand('DOM.getOuterHTML', params: {'nodeId': nodeId})) - .result['root'] as String; + .result!['root'] as String; - Future hideHighlight() => sendCommand('DOM.hideHighlight'); + Future<void> hideHighlight() => sendCommand('DOM.hideHighlight'); - Future highlightNode(int nodeId, - {Rgba borderColor, - Rgba contentColor, - Rgba marginColor, - Rgba paddingColor, - bool showInfo}) { - var params = <String, dynamic>{'nodeId': nodeId, 'highlightConfig': {}}; - - if (borderColor != null) { - params['highlightConfig']['borderColor'] = borderColor; - } - - if (contentColor != null) { - params['highlightConfig']['contentColor'] = contentColor; - } - - if (marginColor != null) { - params['highlightConfig']['marginColor'] = marginColor; - } - - if (paddingColor != null) { - params['highlightConfig']['paddingColor'] = paddingColor; - } - - if (showInfo != null) { - params['highlightConfig']['showInfo'] = showInfo; - } + Future<void> highlightNode( + int nodeId, { + Rgba? borderColor, + Rgba? contentColor, + Rgba? marginColor, + Rgba? paddingColor, + bool? showInfo, + }) { + var params = <String, dynamic>{ + 'nodeId': nodeId, + 'highlightConfig': <String, dynamic>{ + if (borderColor != null) 'borderColor': borderColor, + if (contentColor != null) 'contentColor': contentColor, + if (marginColor != null) 'marginColor': marginColor, + if (paddingColor != null) 'paddingColor': paddingColor, + if (showInfo != null) 'showInfo': showInfo, + }, + }; return sendCommand('DOM.highlightNode', params: params); } - Future highlightRect(int x, int y, int width, int height, - {Rgba color, Rgba outlineColor}) { + Future<void> highlightRect(int x, int y, int width, int height, + {Rgba? color, Rgba? outlineColor}) { var params = <String, dynamic>{ 'x': x, 'y': y, @@ -79,7 +70,7 @@ } Future<int> moveTo(int nodeId, int targetNodeId, - {int insertBeforeNodeId}) async { + {int? insertBeforeNodeId}) async { var params = {'nodeId': nodeId, 'targetNodeId': targetNodeId}; if (insertBeforeNodeId != null) { @@ -87,52 +78,52 @@ } var resp = await sendCommand('DOM.moveTo', params: params); - return resp.result['nodeId'] as int; + return resp.result!['nodeId'] as int; } Future<int> querySelector(int nodeId, String selector) async { var resp = await sendCommand('DOM.querySelector', params: {'nodeId': nodeId, 'selector': selector}); - return resp.result['nodeId'] as int; + return resp.result!['nodeId'] as int; } Future<List<int>> querySelectorAll(int nodeId, String selector) async { var resp = await sendCommand('DOM.querySelectorAll', params: {'nodeId': nodeId, 'selector': selector}); - return (resp.result['nodeIds'] as List).cast(); + return (resp.result!['nodeIds'] as List).cast(); } - Future removeAttribute(int nodeId, String name) => + Future<void> removeAttribute(int nodeId, String name) => sendCommand('DOM.removeAttribute', params: {'nodeId': nodeId, 'name': name}); - Future removeNode(int nodeId) => + Future<void> removeNode(int nodeId) => sendCommand('DOM.removeNode', params: {'nodeId': nodeId}); - Future requestChildNodes(int nodeId) => + Future<void> requestChildNodes(int nodeId) => sendCommand('DOM.requestChildNodes', params: {'nodeId': nodeId}); Future<int> requestNode(String objectId) async { var resp = await sendCommand('DOM.requestNode', params: {'objectId': objectId}); - return resp.result['nodeId'] as int; + return resp.result!['nodeId'] as int; } - Future<RemoteObject> resolveNode(int nodeId, {String objectGroup}) async { + Future<RemoteObject> resolveNode(int nodeId, {String? objectGroup}) async { var params = <String, dynamic>{'nodeId': nodeId}; if (objectGroup != null) { params['objectGroup'] = objectGroup; } var resp = await sendCommand('DOM.resolveNode', params: params); - return new RemoteObject(resp.result['object'] as Map<String, dynamic>); + return RemoteObject(resp.result!['object'] as Map<String, dynamic>); } - Future setAttributeValue(int nodeId, String name, String value) => + Future<void> setAttributeValue(int nodeId, String name, String value) => sendCommand('DOM.setAttributeValue', params: {'nodeId': nodeId, 'name': name, 'value': value}); - Future setAttributesAsText(int nodeId, String text, {String name}) { + Future<void> setAttributesAsText(int nodeId, String text, {String? name}) { var params = {'nodeId': nodeId, 'text': text}; if (name != null) { params['name'] = name; @@ -143,106 +134,99 @@ Future<int> setNodeName(int nodeId, String name) async { var resp = await sendCommand('DOM.setNodeName', params: {'nodeId': nodeId, 'name': name}); - return resp.result['nodeId'] as int; + return resp.result!['nodeId'] as int; } - Future setNodeValue(int nodeId, String value) => + Future<void> setNodeValue(int nodeId, String value) => sendCommand('DOM.setNodeValue', params: {'nodeId': nodeId, 'value': value}); - Future setOuterHtml(int nodeId, String outerHtml) => + Future<void> setOuterHtml(int nodeId, String outerHtml) => sendCommand('DOM.setOuterHTML', params: {'nodeId': nodeId, 'outerHtml': outerHtml}); Stream<AttributeModifiedEvent> get onAttributeModified => eventStream( 'DOM.attributeModified', - (WipEvent event) => new AttributeModifiedEvent(event.json)); + (WipEvent event) => AttributeModifiedEvent(event.json)); Stream<AttributeRemovedEvent> get onAttributeRemoved => eventStream( 'DOM.attributeRemoved', - (WipEvent event) => new AttributeRemovedEvent(event.json)); + (WipEvent event) => AttributeRemovedEvent(event.json)); Stream<CharacterDataModifiedEvent> get onCharacterDataModified => eventStream( 'DOM.characterDataModified', - (WipEvent event) => new CharacterDataModifiedEvent(event.json)); + (WipEvent event) => CharacterDataModifiedEvent(event.json)); Stream<ChildNodeCountUpdatedEvent> get onChildNodeCountUpdated => eventStream( 'DOM.childNodeCountUpdated', - (WipEvent event) => new ChildNodeCountUpdatedEvent(event.json)); + (WipEvent event) => ChildNodeCountUpdatedEvent(event.json)); Stream<ChildNodeInsertedEvent> get onChildNodeInserted => eventStream( 'DOM.childNodeInserted', - (WipEvent event) => new ChildNodeInsertedEvent(event.json)); + (WipEvent event) => ChildNodeInsertedEvent(event.json)); Stream<ChildNodeRemovedEvent> get onChildNodeRemoved => eventStream( 'DOM.childNodeRemoved', - (WipEvent event) => new ChildNodeRemovedEvent(event.json)); + (WipEvent event) => ChildNodeRemovedEvent(event.json)); Stream<DocumentUpdatedEvent> get onDocumentUpdated => eventStream( 'DOM.documentUpdated', - (WipEvent event) => new DocumentUpdatedEvent(event.json)); + (WipEvent event) => DocumentUpdatedEvent(event.json)); Stream<SetChildNodesEvent> get onSetChildNodes => eventStream( - 'DOM.setChildNodes', - (WipEvent event) => new SetChildNodesEvent(event.json)); + 'DOM.setChildNodes', (WipEvent event) => SetChildNodesEvent(event.json)); } class AttributeModifiedEvent extends WipEvent { AttributeModifiedEvent(Map<String, dynamic> json) : super(json); - int get nodeId => params['nodeId'] as int; + int get nodeId => params!['nodeId'] as int; - String get name => params['name'] as String; + String get name => params!['name'] as String; - String get value => params['value'] as String; + String get value => params!['value'] as String; } class AttributeRemovedEvent extends WipEvent { AttributeRemovedEvent(Map<String, dynamic> json) : super(json); - int get nodeId => params['nodeId'] as int; + int get nodeId => params!['nodeId'] as int; - String get name => params['name'] as String; + String get name => params!['name'] as String; } class CharacterDataModifiedEvent extends WipEvent { CharacterDataModifiedEvent(Map<String, dynamic> json) : super(json); - int get nodeId => params['nodeId'] as int; + int get nodeId => params!['nodeId'] as int; - String get characterData => params['characterData'] as String; + String get characterData => params!['characterData'] as String; } class ChildNodeCountUpdatedEvent extends WipEvent { ChildNodeCountUpdatedEvent(Map<String, dynamic> json) : super(json); - int get nodeId => params['nodeId'] as int; + int get nodeId => params!['nodeId'] as int; - int get childNodeCount => params['childNodeCount'] as int; + int get childNodeCount => params!['childNodeCount'] as int; } class ChildNodeInsertedEvent extends WipEvent { ChildNodeInsertedEvent(Map<String, dynamic> json) : super(json); - int get parentNodeId => params['parentNodeId'] as int; + int get parentNodeId => params!['parentNodeId'] as int; - int get previousNodeId => params['previousNodeId'] as int; - Node _node; + int get previousNodeId => params!['previousNodeId'] as int; - Node get node { - if (_node == null) { - _node = new Node(params['node'] as Map<String, dynamic>); - } - return _node; - } + late final node = Node(params!['node'] as Map<String, dynamic>); } class ChildNodeRemovedEvent extends WipEvent { ChildNodeRemovedEvent(Map<String, dynamic> json) : super(json); - int get parentNodeId => params['parentNodeId'] as int; + int get parentNodeId => params!['parentNodeId'] as int; - int get nodeId => params['nodeId'] as int; + int get nodeId => params!['nodeId'] as int; } class DocumentUpdatedEvent extends WipEvent { @@ -252,14 +236,15 @@ class SetChildNodesEvent extends WipEvent { SetChildNodesEvent(Map<String, dynamic> json) : super(json); - int get nodeId => params['parentId'] as int; + int get nodeId => params!['parentId'] as int; Iterable<Node> get nodes sync* { - for (Map node in params['nodes']) { - yield new Node(node as Map<String, dynamic>); + for (Map node in params!['nodes']) { + yield Node(node as Map<String, dynamic>); } } + @override String toString() => 'SetChildNodes $nodeId: $nodes'; } @@ -271,41 +256,31 @@ Node(this._map); - Map<String, String> _attributes; + late final Map<String, String>? attributes = _map.containsKey('attributes') + ? _attributeListToMap((_map['attributes'] as List).cast()) + : null; - Map<String, String> get attributes { - if (_attributes == null && _map.containsKey('attributes')) { - _attributes = _attributeListToMap((_map['attributes'] as List).cast()); - } - return _attributes; - } + int? get childNodeCount => _map['childNodeCount'] as int?; - int get childNodeCount => _map['childNodeCount'] as int; + late final List<Node>? children = _map.containsKey('children') + ? UnmodifiableListView((_map['children'] as List) + .map((c) => Node(c as Map<String, dynamic>))) + : null; - List<Node> _children; - - List<Node> get children { - if (_children == null && _map.containsKey('children')) { - _children = new UnmodifiableListView((_map['children'] as List) - .map((c) => new Node(c as Map<String, dynamic>))); - } - return _children; - } - - Node get contentDocument { + Node? get contentDocument { if (_map.containsKey('contentDocument')) { - return new Node(_map['contentDocument'] as Map<String, dynamic>); + return Node(_map['contentDocument'] as Map<String, dynamic>); } return null; } - String get documentUrl => _map['documentURL'] as String; + String? get documentUrl => _map['documentURL'] as String?; - String get internalSubset => _map['internalSubset'] as String; + String? get internalSubset => _map['internalSubset'] as String?; String get localName => _map['localName'] as String; - String get name => _map['name'] as String; + String? get name => _map['name'] as String?; int get nodeId => _map['nodeId'] as int; @@ -315,29 +290,30 @@ String get nodeValue => _map['nodeValue'] as String; - String get publicId => _map['publicId'] as String; + String? get publicId => _map['publicId'] as String?; - String get systemId => _map['systemId'] as String; + String? get systemId => _map['systemId'] as String?; - String get value => _map['value'] as String; + String? get value => _map['value'] as String?; - String get xmlVersion => _map['xmlVersion'] as String; + String? get xmlVersion => _map['xmlVersion'] as String?; + @override String toString() => '$nodeName: $nodeId $attributes'; } class Rgba { - final int a; + final int? a; final int b; final int r; final int g; Rgba(this.r, this.g, this.b, [this.a]); - Map toJson() { + Map<String, int> toJson() { var json = {'r': r, 'g': g, 'b': b}; if (a != null) { - json['a'] = a; + json['a'] = a!; } return json; } @@ -348,5 +324,5 @@ for (int i = 0; i < attrList.length; i += 2) { attributes[attrList[i]] = attrList[i + 1]; } - return new UnmodifiableMapView(attributes); + return UnmodifiableMapView(attributes); }
diff --git a/lib/src/log.dart b/lib/src/log.dart index 6dc5b46..9839472 100644 --- a/lib/src/log.dart +++ b/lib/src/log.dart
@@ -12,14 +12,14 @@ Future<WipResponse> disable() => sendCommand('Log.disable'); - Stream<LogEntry> get onEntryAdded => eventStream( - 'Log.entryAdded', (WipEvent event) => new LogEntry(event.json)); + Stream<LogEntry> get onEntryAdded => + eventStream('Log.entryAdded', (WipEvent event) => LogEntry(event.json)); } class LogEntry extends WipEvent { LogEntry(Map<String, dynamic> json) : super(json); - Map<String, dynamic> get _entry => params['entry'] as Map<String, dynamic>; + Map<String, dynamic> get _entry => params!['entry'] as Map<String, dynamic>; /// Log entry source. Allowed values: xml, javascript, network, storage, /// appcache, rendering, security, deprecation, worker, violation, @@ -34,10 +34,11 @@ /// URL of the resource if known. @optional - String get url => _entry['url'] as String; + String? get url => _entry['url'] as String?; /// Timestamp when this entry was added. num get timestamp => _entry['timestamp'] as num; + @override String toString() => text; }
diff --git a/lib/src/page.dart b/lib/src/page.dart index e8bf1e5..5b125d0 100644 --- a/lib/src/page.dart +++ b/lib/src/page.dart
@@ -16,7 +16,7 @@ sendCommand('Page.navigate', params: {'url': url}); Future<WipResponse> reload( - {bool ignoreCache, String scriptToEvaluateOnLoad}) { + {bool? ignoreCache, String? scriptToEvaluateOnLoad}) { var params = <String, dynamic>{}; if (ignoreCache != null) { params['ignoreCache'] = ignoreCache;
diff --git a/lib/src/runtime.dart b/lib/src/runtime.dart index da09256..ada5399 100644 --- a/lib/src/runtime.dart +++ b/lib/src/runtime.dart
@@ -28,9 +28,9 @@ /// return once awaited promise is resolved. Future<RemoteObject> evaluate( String expression, { - bool returnByValue, - int contextId, - bool awaitPromise, + bool? returnByValue, + int? contextId, + bool? awaitPromise, }) async { Map<String, dynamic> params = { 'expression': expression, @@ -48,12 +48,11 @@ final WipResponse response = await sendCommand('Runtime.evaluate', params: params); - if (response.result.containsKey('exceptionDetails')) { - throw new ExceptionDetails( - response.result['exceptionDetails'] as Map<String, dynamic>); + if (response.result!.containsKey('exceptionDetails')) { + throw ExceptionDetails( + response.result!['exceptionDetails'] as Map<String, dynamic>); } else { - return new RemoteObject( - response.result['result'] as Map<String, dynamic>); + return RemoteObject(response.result!['result'] as Map<String, dynamic>); } } @@ -64,10 +63,10 @@ /// object (int, String, double, bool). Future<RemoteObject> callFunctionOn( String functionDeclaration, { - String objectId, - List<dynamic> arguments, - bool returnByValue, - int executionContextId, + String? objectId, + List<dynamic>? arguments, + bool? returnByValue, + int? executionContextId, }) async { Map<String, dynamic> params = { 'functionDeclaration': functionDeclaration, @@ -95,12 +94,11 @@ final WipResponse response = await sendCommand('Runtime.callFunctionOn', params: params); - if (response.result.containsKey('exceptionDetails')) { - throw new ExceptionDetails( - response.result['exceptionDetails'] as Map<String, dynamic>); + if (response.result!.containsKey('exceptionDetails')) { + throw ExceptionDetails( + response.result!['exceptionDetails'] as Map<String, dynamic>); } else { - return new RemoteObject( - response.result['result'] as Map<String, dynamic>); + return RemoteObject(response.result!['result'] as Map<String, dynamic>); } } @@ -109,13 +107,13 @@ @experimental Future<HeapUsage> getHeapUsage() async { final WipResponse response = await sendCommand('Runtime.getHeapUsage'); - return HeapUsage(response.result); + return HeapUsage(response.result!); } /// Returns the isolate id. @experimental Future<String> getIsolateId() async { - return (await sendCommand('Runtime.getIsolateId')).result['id'] as String; + return (await sendCommand('Runtime.getIsolateId')).result!['id'] as String; } /// Returns properties of a given object. Object group of the result is @@ -127,7 +125,7 @@ /// itself, not to its prototype chain. Future<List<PropertyDescriptor>> getProperties( RemoteObject object, { - bool ownProperties, + bool? ownProperties, }) async { Map<String, dynamic> params = { 'objectId': object.objectId, @@ -139,34 +137,34 @@ final WipResponse response = await sendCommand('Runtime.getProperties', params: params); - if (response.result.containsKey('exceptionDetails')) { - throw new ExceptionDetails( - response.result['exceptionDetails'] as Map<String, dynamic>); + if (response.result!.containsKey('exceptionDetails')) { + throw ExceptionDetails( + response.result!['exceptionDetails'] as Map<String, dynamic>); } else { - List locations = response.result['result']; + List locations = response.result!['result']; return List.from(locations.map((map) => PropertyDescriptor(map))); } } Stream<ConsoleAPIEvent> get onConsoleAPICalled => eventStream( 'Runtime.consoleAPICalled', - (WipEvent event) => new ConsoleAPIEvent(event.json)); + (WipEvent event) => ConsoleAPIEvent(event.json)); Stream<ExceptionThrownEvent> get onExceptionThrown => eventStream( 'Runtime.exceptionThrown', - (WipEvent event) => new ExceptionThrownEvent(event.json)); + (WipEvent event) => ExceptionThrownEvent(event.json)); /// Issued when new execution context is created. Stream<ExecutionContextDescription> get onExecutionContextCreated => eventStream( 'Runtime.executionContextCreated', (WipEvent event) => - new ExecutionContextDescription(event.params['context'])); + ExecutionContextDescription(event.params!['context'])); /// Issued when execution context is destroyed. Stream<String> get onExecutionContextDestroyed => eventStream( 'Runtime.executionContextDestroyed', - (WipEvent event) => event.params['executionContextId']); + (WipEvent event) => event.params!['executionContextId']); /// Issued when all executionContexts were cleared in browser. Stream get onExecutionContextsCleared => eventStream( @@ -180,14 +178,14 @@ /// Type of the call. Allowed values: log, debug, info, error, warning, dir, /// dirxml, table, trace, clear, startGroup, startGroupCollapsed, endGroup, /// assert, profile, profileEnd. - String get type => params['type'] as String; + String get type => params!['type'] as String; /// Call timestamp. - num get timestamp => params['timestamp'] as num; + num get timestamp => params!['timestamp'] as num; /// Call arguments. - List<RemoteObject> get args => (params['args'] as List) - .map((m) => new RemoteObject(m as Map<String, dynamic>)) + List<RemoteObject> get args => (params!['args'] as List) + .map((m) => RemoteObject(m as Map<String, dynamic>)) .toList(); } @@ -212,10 +210,10 @@ ExceptionThrownEvent(Map<String, dynamic> json) : super(json); /// Timestamp of the exception. - int get timestamp => params['timestamp'] as int; + int get timestamp => params!['timestamp'] as int; ExceptionDetails get exceptionDetails => - new ExceptionDetails(params['exceptionDetails'] as Map<String, dynamic>); + ExceptionDetails(params!['exceptionDetails'] as Map<String, dynamic>); } class ExceptionDetails implements Exception { @@ -243,20 +241,21 @@ /// Script ID of the exception location. @optional - String get scriptId => json['scriptId'] as String; + String? get scriptId => json['scriptId'] as String?; /// JavaScript stack trace if available. @optional - StackTrace get stackTrace => json['stackTrace'] == null + StackTrace? get stackTrace => json['stackTrace'] == null ? null - : new StackTrace(json['stackTrace'] as Map<String, dynamic>); + : StackTrace(json['stackTrace'] as Map<String, dynamic>); /// Exception object if available. @optional - RemoteObject get exception => json['exception'] == null + RemoteObject? get exception => json['exception'] == null ? null - : new RemoteObject(json['exception'] as Map<String, dynamic>); + : RemoteObject(json['exception'] as Map<String, dynamic>); + @override String toString() => '$text, $url, $scriptId, $lineNumber, $exception'; } @@ -267,7 +266,7 @@ StackTrace(this.json); List<CallFrame> get callFrames => (json['callFrames'] as List) - .map((m) => new CallFrame(m as Map<String, dynamic>)) + .map((m) => CallFrame(m as Map<String, dynamic>)) .toList(); /// String label of this stack trace. For async traces this may be a name of @@ -278,7 +277,7 @@ /// Asynchronous JavaScript stack trace that preceded this stack, if /// available. @optional - StackTrace get parent { + StackTrace? get parent { return json['parent'] == null ? null : StackTrace(json['parent']); } @@ -290,11 +289,12 @@ }); return frames.map((CallFrame frame) { - return '${frame.functionName}()'.padRight(width + 2) + - ' ${frame.url} ${frame.lineNumber}:${frame.columnNumber}'; + var name = '${frame.functionName}()'.padRight(width + 2); + return '$name ${frame.url} ${frame.lineNumber}:${frame.columnNumber}'; }).toList(); } + @override String toString() => callFrames.map((f) => ' $f').join('\n'); } @@ -321,6 +321,7 @@ /// JavaScript script column number (0-based). int get columnNumber => json['columnNumber'] as int; + @override String toString() => '$functionName() ($url $lineNumber:$columnNumber)'; } @@ -341,22 +342,22 @@ /// Allowed Values: array, null, node, regexp, date, map, set, weakmap, /// weakset, iterator, generator, error, proxy, promise, typedarray, /// arraybuffer, dataview, i32, i64, f32, f64, v128, anyref. - String get subtype => json['subtype'] as String; + String? get subtype => json['subtype'] as String?; /// Object class (constructor) name. /// /// Specified for object type values only. - String get className => json['className'] as String; + String? get className => json['className'] as String?; /// Remote object value in case of primitive values or JSON values (if it was /// requested). (optional) - Object get value => json['value']; + Object? get value => json['value']; /// String representation of the object. (optional) - String get description => json['description'] as String; + String? get description => json['description'] as String?; /// Unique object identifier (for non-primitive values). (optional) - String get objectId => json['objectId'] as String; + String? get objectId => json['objectId'] as String?; @override String toString() => '$type $value'; @@ -389,24 +390,24 @@ String get name => json['name']; /// The value associated with the property. - RemoteObject get value => + RemoteObject? get value => json['value'] != null ? RemoteObject(json['value']) : null; /// True if the value associated with the property may be changed (data /// descriptors only). - bool get writable => json['writable']; + bool? get writable => json['writable'] as bool?; /// True if the type of this property descriptor may be changed and if the /// property may be deleted from the corresponding object. - bool get configurable => json['configurable']; + bool get configurable => json['configurable'] as bool; /// True if this property shows up during enumeration of the properties on the /// corresponding object. - bool get enumerable => json['enumerable']; + bool get enumerable => json['enumerable'] as bool; /// True if the result was thrown during the evaluation. - bool get wasThrown => json['wasThrown']; + bool? get wasThrown => json['wasThrown'] as bool?; /// True if the property is owned for the object. - bool get isOwn => json['isOwn']; + bool? get isOwn => json['isOwn'] as bool?; }
diff --git a/lib/src/target.dart b/lib/src/target.dart index 5b51382..41a04c1 100644 --- a/lib/src/target.dart +++ b/lib/src/target.dart
@@ -16,7 +16,7 @@ Future<String> createTarget(String url) async { WipResponse response = await sendCommand('Target.createTarget', params: {'url': url}); - return response.result['targetId'] as String; + return response.result!['targetId'] as String; } /// Activates (focuses) the target. @@ -29,7 +29,7 @@ Future<bool> closeTarget(String targetId) async { WipResponse response = await sendCommand('Target.closeTarget', params: {'targetId': targetId}); - return response.result['success'] as bool; + return response.result!['success'] as bool; } /// Inject object to the target's main frame that provides a communication @@ -43,7 +43,7 @@ @experimental Future<WipResponse> exposeDevToolsProtocol( String targetId, { - String bindingName, + String? bindingName, }) { final Map<String, dynamic> params = {'targetId': targetId}; if (bindingName != null) {
diff --git a/lib/webkit_inspection_protocol.dart b/lib/webkit_inspection_protocol.dart index 1a0330e..6f5f7df 100644 --- a/lib/webkit_inspection_protocol.dart +++ b/lib/webkit_inspection_protocol.dart
@@ -6,7 +6,7 @@ import 'dart:async'; import 'dart:convert'; -import 'dart:io' show HttpClient, HttpClientResponse, WebSocket; +import 'dart:io' show HttpClient, HttpClientResponse, IOException, WebSocket; import 'src/console.dart'; import 'src/debugger.dart'; @@ -29,24 +29,57 @@ /// This assumes the browser has been started with the `--remote-debugging-port` /// flag. The data is read from the `http://{host}:{port}/json` url. class ChromeConnection { - final HttpClient _client = new HttpClient(); + final HttpClient _client = HttpClient(); final Uri url; ChromeConnection(String host, [int port = 9222]) - : url = Uri.parse('http://${host}:${port}/'); + : url = Uri.parse('http://$host:$port/'); - // TODO(DrMarcII): consider changing this to return Stream<ChromeTab>. - Future<List<ChromeTab>> getTabs() async { + /// Return all the available tabs. + /// + /// This method can potentially throw a [ConnectionException] on some protocol + /// issues. + /// + /// An optional [retryFor] duration can be used to automatically re-try + /// connections for some period of time. Anecdotally, Chrome can return errors + /// when trying to list the available tabs very early in its startup sequence. + Future<List<ChromeTab>> getTabs({ + Duration? retryFor, + }) async { + final start = DateTime.now(); + DateTime? end = retryFor == null ? null : start.add(retryFor); + var response = await getUrl('/json'); - var respBody = await utf8.decodeStream(response.cast<List<int>>()); - return new List<ChromeTab>.from( - (jsonDecode(respBody) as List).map((m) => new ChromeTab(m as Map))); + var responseBody = await utf8.decodeStream(response.cast<List<int>>()); + + late List decoded; + while (true) { + try { + decoded = jsonDecode(responseBody); + return List<ChromeTab>.from(decoded.map((m) => ChromeTab(m as Map))); + } on FormatException catch (formatException) { + if (end != null && end.isBefore(DateTime.now())) { + // Delay for retryFor / 4 milliseconds. + await Future.delayed( + Duration(milliseconds: retryFor!.inMilliseconds ~/ 4), + ); + } else { + throw ConnectionException( + formatException: formatException, + responseStatus: '${response.statusCode} ${response.reasonPhrase}', + responseBody: responseBody, + ); + } + } + } } - Future<ChromeTab> getTab(bool accept(ChromeTab tab), - {Duration retryFor}) async { - var start = new DateTime.now(); + Future<ChromeTab?> getTab( + bool Function(ChromeTab tab) accept, { + Duration? retryFor, + }) async { + var start = DateTime.now(); var end = start; if (retryFor != null) { end = start.add(retryFor); @@ -59,15 +92,15 @@ return tab; } } - if (end.isBefore(new DateTime.now())) { + if (end.isBefore(DateTime.now())) { return null; } } catch (e) { - if (end.isBefore(new DateTime.now())) { + if (end.isBefore(DateTime.now())) { rethrow; } } - await new Future.delayed(const Duration(milliseconds: 25)); + await Future.delayed(const Duration(milliseconds: 25)); } } @@ -79,21 +112,54 @@ void close() => _client.close(force: true); } +/// An exception that can be thrown early in the connection sequence for a +/// [ChromeConnection]. +/// +/// This exception includes the underlying exception, as well as the http +/// response from the browser that we failed on. The [toString] implementation +/// includes a summary of the response. +class ConnectionException implements IOException { + final FormatException formatException; + final String responseStatus; + final String responseBody; + + ConnectionException({ + required this.formatException, + required this.responseStatus, + required this.responseBody, + }); + + @override + String toString() { + final buf = StringBuffer('${formatException.message}\n'); + buf.writeln('$responseStatus; body:'); + var lines = responseBody.split('\n'); + if (lines.length > 10) { + lines = [ + ...lines.take(10), + '...', + ]; + } + buf.writeAll(lines, '\n'); + return buf.toString(); + } +} + class ChromeTab { final Map _map; ChromeTab(this._map); - String get description => _map['description'] as String; + String? get description => _map['description'] as String?; - String get devtoolsFrontendUrl => _map['devtoolsFrontendUrl'] as String; + String? get devtoolsFrontendUrl => _map['devtoolsFrontendUrl'] as String?; - String get faviconUrl => _map['faviconUrl'] as String; + String? get faviconUrl => _map['faviconUrl'] as String?; /// Ex. `E1999E8A-EE27-0450-9900-5BFF4C69CA83`. String get id => _map['id'] as String; - String get title => _map['title'] as String; + String? get title => _map['title'] as String?; /// Ex. `background_page`, `page`. String get type => _map['type'] as String; @@ -112,6 +178,7 @@ Future<WipConnection> connect() => WipConnection.connect(webSocketDebuggerUrl); + @override String toString() => url; } @@ -124,59 +191,38 @@ int _nextId = 0; - WipConsole _console; // ignore: deprecated_member_use @Deprecated('This domain is deprecated - use Runtime or Log instead') - WipConsole get console => _console; + late final WipConsole console = WipConsole(this); - WipDebugger _debugger; + late final WipDebugger debugger = WipDebugger(this); - WipDebugger get debugger => _debugger; + late final WipDom dom = WipDom(this); - WipDom _dom; + late final WipPage page = WipPage(this); - WipDom get dom => _dom; + late final WipTarget target = WipTarget(this); - WipPage _page; + late final WipLog log = WipLog(this); - WipPage get page => _page; - - WipTarget _target; - - WipTarget get target => _target; - - WipLog _log; - - WipLog get log => _log; - - WipRuntime _runtime; - - WipRuntime get runtime => _runtime; + late final WipRuntime runtime = WipRuntime(this); final StreamController<String> _onSend = StreamController.broadcast(sync: true); final StreamController<String> _onReceive = StreamController.broadcast(sync: true); - final Map _completers = <int, Completer<WipResponse>>{}; + final Map<int, Completer<WipResponse>> _completers = {}; - final _closeController = new StreamController<WipConnection>.broadcast(); - final _notificationController = new StreamController<WipEvent>.broadcast(); + final _closeController = StreamController<WipConnection>.broadcast(); + final _notificationController = StreamController<WipEvent>.broadcast(); static Future<WipConnection> connect(String url) { return WebSocket.connect(url).then((socket) { - return new WipConnection._(url, socket); + return WipConnection._(url, socket); }); } WipConnection._(this.url, this._ws) { - _console = new WipConsole(this); // ignore: deprecated_member_use - _debugger = new WipDebugger(this); - _dom = new WipDom(this); - _page = new WipPage(this); - _target = new WipTarget(this); - _log = new WipLog(this); - _runtime = new WipRuntime(this); - _ws.listen((data) { var json = jsonDecode(data as String) as Map<String, dynamic>; _onReceive.add(data); @@ -195,16 +241,17 @@ Future close() => _ws.close(); + @override String toString() => url; Future<WipResponse> sendCommand(String method, - [Map<String, dynamic> params]) { - var completer = new Completer<WipResponse>(); + [Map<String, dynamic>? params]) { + var completer = Completer<WipResponse>(); var json = {'id': _nextId++, 'method': method}; if (params != null) { json['params'] = params; } - _completers[json['id']] = completer; + _completers[json['id'] as int] = completer; String message = jsonEncode(json); _ws.add(message); _onSend.add(message); @@ -212,16 +259,16 @@ } void _handleNotification(Map<String, dynamic> json) { - _notificationController.add(new WipEvent(json)); + _notificationController.add(WipEvent(json)); } void _handleResponse(Map<String, dynamic> event) { - var completer = _completers.remove(event['id']); + var completer = _completers.remove(event['id'])!; if (event.containsKey('error')) { - completer.completeError(new WipError(event)); + completer.completeError(WipError(event)); } else { - completer.complete(new WipResponse(event)); + completer.complete(WipResponse(event)); } } @@ -242,12 +289,13 @@ final Map<String, dynamic> json; final String method; - final Map<String, dynamic> params; + final Map<String, dynamic>? params; WipEvent(this.json) : method = json['method'] as String, - params = json['params'] as Map<String, dynamic>; + params = json['params'] as Map<String, dynamic>?; + @override String toString() => 'WipEvent: $method($params)'; } @@ -255,16 +303,17 @@ final Map<String, dynamic> json; final int id; - final dynamic error; + final Map<String, dynamic>? error; WipError(this.json) : id = json['id'] as int, - error = json['error']; + error = json['error'] as Map<String, dynamic>?; - int get code => error == null ? null : error['code']; + int? get code => error == null ? null : error!['code']; - String get message => error == null ? null : error['message']; + String? get message => error == null ? null : error!['message']; + @override String toString() => 'WipError $code $message'; } @@ -272,16 +321,17 @@ final Map<String, dynamic> json; final int id; - final Map<String, dynamic> result; + final Map<String, dynamic>? result; WipResponse(this.json) : id = json['id'] as int, - result = json['result'] as Map<String, dynamic>; + result = json['result'] as Map<String, dynamic>?; + @override String toString() => 'WipResponse $id: $result'; } -typedef T WipEventTransformer<T>(WipEvent event); +typedef WipEventTransformer<T> = T Function(WipEvent event); /// @optional const String optional = 'optional'; @@ -290,22 +340,19 @@ final Map<String, Stream> _eventStreams = {}; final WipConnection connection; - Stream<WipDomain> _onClosed; - Stream<WipDomain> get onClosed => _onClosed; + late final Stream<WipDomain> onClosed = StreamTransformer.fromHandlers( + handleData: (event, EventSink<WipDomain> sink) { + sink.add(this); + }).bind(connection.onClose); - WipDomain(WipConnection connection) : this.connection = connection { - this._onClosed = new StreamTransformer.fromHandlers( - handleData: (event, EventSink<WipDomain> sink) { - sink.add(this); - }).bind(connection.onClose); - } + WipDomain(this.connection); Stream<T> eventStream<T>(String method, WipEventTransformer<T> transformer) { return _eventStreams .putIfAbsent( method, - () => new StreamTransformer.fromHandlers( + () => StreamTransformer.fromHandlers( handleData: (WipEvent event, EventSink<T> sink) { if (event.method == method) { sink.add(transformer(event)); @@ -318,13 +365,14 @@ Future<WipResponse> sendCommand( String method, { - Map<String, dynamic> params, + Map<String, dynamic>? params, }) { return connection.sendCommand(method, params); } } -const _Experimental experimental = const _Experimental(); +// ignore: library_private_types_in_public_api +const _Experimental experimental = _Experimental(); class _Experimental { const _Experimental();
diff --git a/pubspec.yaml b/pubspec.yaml index f08319e..a36f2e6 100644 --- a/pubspec.yaml +++ b/pubspec.yaml
@@ -1,18 +1,22 @@ name: webkit_inspection_protocol -version: 0.7.5 -description: A client for the Chrome DevTools Protocol (previously called the Webkit Inspection Protocol). -homepage: https://github.com/google/webkit_inspection_protocol.dart +version: 1.1.0-dev +description: > + A client for the Chrome DevTools Protocol (previously called the Webkit + Inspection Protocol). +repository: https://github.com/google/webkit_inspection_protocol.dart environment: - sdk: '>=2.0.0 <3.0.0' + sdk: '>=2.12.0 <3.0.0' dependencies: - logging: '>=0.11.0 <2.0.0' + logging: ^1.0.0 dev_dependencies: - args: '>=0.13.0 <2.0.0' - shelf: ^0.7.0 - shelf_static: ^0.2.0 - shelf_web_socket: ^0.2.0 + args: ^2.0.0 + lints: ^2.0.0 + shelf_static: ^1.0.0 + shelf_web_socket: ^1.0.0 + shelf: ^1.0.0 test: ^1.16.0 - webdriver: ^2.0.0 + web_socket_channel: ^2.0.0 # used in example/ + webdriver: ^3.0.0
diff --git a/test/console_test.dart b/test/console_test.dart index 46c5abf..e4f68a0 100644 --- a/test/console_test.dart +++ b/test/console_test.dart
@@ -13,13 +13,13 @@ void main() { group('WipConsole', () { - WipConsole console; // ignore: deprecated_member_use + WipConsole? console; // ignore: deprecated_member_use List<ConsoleMessageEvent> events = []; - var subs = []; + var subs = <StreamSubscription>[]; Future checkMessages(int expectedCount) async { // make sure all messages have been delivered - await new Future.delayed(const Duration(seconds: 1)); + await Future.delayed(const Duration(seconds: 1)); expect(events, hasLength(expectedCount)); for (int i = 0; i < expectedCount; i++) { if (i == 0) { @@ -35,26 +35,28 @@ // ignore: deprecated_member_use console = (await wipConnection).console; events.clear(); - subs.add(console.onMessage.listen(events.add)); + subs.add(console!.onMessage.listen(events.add)); }); tearDown(() async { - await console.disable(); + await console?.disable(); console = null; await closeConnection(); - subs.forEach((s) => s.cancel()); + for (var s in subs) { + s.cancel(); + } subs.clear(); }); test('receives new console messages', () async { - await console.enable(); + await console!.enable(); await navigateToPage('console_test.html'); await checkMessages(4); }); test('receives old console messages', () async { await navigateToPage('console_test.html'); - await console.enable(); + await console!.enable(); await checkMessages(4); });
diff --git a/test/debugger_test.dart b/test/debugger_test.dart index 8c2c265..b9e691c 100644 --- a/test/debugger_test.dart +++ b/test/debugger_test.dart
@@ -13,7 +13,7 @@ void main() { group('WipDebugger', () { - WipDebugger debugger; + WipDebugger? debugger; List<StreamSubscription> subs = []; setUp(() async { @@ -21,19 +21,21 @@ }); tearDown(() async { - await debugger.disable(); + await debugger?.disable(); debugger = null; await closeConnection(); - subs.forEach((s) => s.cancel()); + for (var s in subs) { + s.cancel(); + } subs.clear(); }); test('gets script events', () async { final controller = StreamController<ScriptParsedEvent>(); - subs.add(debugger.onScriptParsed.listen(controller.add)); + subs.add(debugger!.onScriptParsed.listen(controller.add)); - await debugger.enable(); + await debugger!.enable(); await navigateToPage('debugger_test.html'); expect(controller.stream.first, isNotNull); @@ -41,24 +43,24 @@ test('getScriptSource', () async { final controller = StreamController<ScriptParsedEvent>(); - subs.add(debugger.onScriptParsed.listen(controller.add)); + subs.add(debugger!.onScriptParsed.listen(controller.add)); - await debugger.enable(); + await debugger!.enable(); await navigateToPage('debugger_test.html'); final event = await controller.stream .firstWhere((event) => event.script.url.endsWith('.html')); expect(event.script.scriptId, isNotEmpty); - final source = await debugger.getScriptSource(event.script.scriptId); + final source = await debugger!.getScriptSource(event.script.scriptId); expect(source, isNotEmpty); }); test('getPossibleBreakpoints', () async { final controller = StreamController<ScriptParsedEvent>(); - subs.add(debugger.onScriptParsed.listen(controller.add)); + subs.add(debugger!.onScriptParsed.listen(controller.add)); - await debugger.enable(); + await debugger!.enable(); await navigateToPage('debugger_test.html'); final event = await controller.stream @@ -67,7 +69,7 @@ final script = event.script; - final result = await debugger + final result = await debugger! .getPossibleBreakpoints(WipLocation.fromValues(script.scriptId, 0)); expect(result, isNotEmpty); expect(result.any((bp) => bp.lineNumber == 10), true); @@ -75,9 +77,9 @@ test('setBreakpoint / removeBreakpoint', () async { final controller = StreamController<ScriptParsedEvent>(); - subs.add(debugger.onScriptParsed.listen(controller.add)); + subs.add(debugger!.onScriptParsed.listen(controller.add)); - await debugger.enable(); + await debugger!.enable(); await navigateToPage('debugger_test.html'); final event = await controller.stream @@ -86,11 +88,11 @@ final script = event.script; - final bpResult = await debugger + final bpResult = await debugger! .setBreakpoint(WipLocation.fromValues(script.scriptId, 10)); expect(bpResult.breakpointId, isNotEmpty); - final result = await debugger.removeBreakpoint(bpResult.breakpointId); + final result = await debugger!.removeBreakpoint(bpResult.breakpointId); expect(result.result, isEmpty); }); });
diff --git a/test/dom_model_test.dart b/test/dom_model_test.dart index 762d128..5ac2b53 100644 --- a/test/dom_model_test.dart +++ b/test/dom_model_test.dart
@@ -12,10 +12,10 @@ void main() { group('WipDomModel', () { - WipDom dom; + WipDom? dom; setUp(() async { - dom = new WipDomModel((await navigateToPage('dom_model_test.html')).dom); + dom = WipDomModel((await navigateToPage('dom_model_test.html')).dom); }); tearDown(() async { @@ -24,46 +24,46 @@ }); test('maintains model across getDocument calls', () async { - var document1 = await dom.getDocument(); - var document2 = await dom.getDocument(); + var document1 = await dom!.getDocument(); + var document2 = await dom!.getDocument(); expect(document2.nodeId, document1.nodeId); }); test('requestChildNodes updates children', () async { - Node htmlNode = (await dom.getDocument()).children[1]; - for (var child in htmlNode.children) { + Node htmlNode = (await dom!.getDocument()).children![1]; + for (var child in htmlNode.children!) { expect(child.children, isNull); - await dom.requestChildNodes(child.nodeId); + await dom!.requestChildNodes(child.nodeId); } // wait for children to be updated - for (var child in htmlNode.children) { + for (var child in htmlNode.children!) { expect(child.children, isNotNull); } }); test('removing a node updates children', () async { - Node bodyNode = (await dom.getDocument()).children[1].children[1]; - await dom.requestChildNodes(bodyNode.nodeId); - var childCount = bodyNode.childNodeCount; - await dom.removeNode(bodyNode.children.first.nodeId); + Node bodyNode = (await dom!.getDocument()).children![1].children![1]; + await dom!.requestChildNodes(bodyNode.nodeId); + var childCount = bodyNode.childNodeCount!; + await dom!.removeNode(bodyNode.children!.first.nodeId); expect(bodyNode.children, hasLength(childCount - 1)); expect(bodyNode.childNodeCount, childCount - 1); }); test('Moving a node updates children', () async { - Node bodyNode = (await dom.getDocument()).children[1].children[1]; - await dom.requestChildNodes(bodyNode.nodeId); - Node div1 = bodyNode.children[0]; - Node div2 = bodyNode.children[1]; + Node bodyNode = (await dom!.getDocument()).children![1].children![1]; + await dom!.requestChildNodes(bodyNode.nodeId); + Node div1 = bodyNode.children![0]; + Node div2 = bodyNode.children![1]; expect(div1.childNodeCount, 1); expect(div2.childNodeCount, 0); - await dom.requestChildNodes(div1.nodeId); - await dom.requestChildNodes(div2.nodeId); + await dom!.requestChildNodes(div1.nodeId); + await dom!.requestChildNodes(div2.nodeId); - await dom.moveTo(div1.children.first.nodeId, div2.nodeId); + await dom!.moveTo(div1.children!.first.nodeId, div2.nodeId); expect(div1.childNodeCount, 0); expect(div2.childNodeCount, 1); @@ -71,56 +71,56 @@ }, skip: 'google/webkit_inspection_protocol.dart/issues/52'); test('Setting node value updates value', () async { - Node bodyNode = (await dom.getDocument()).children[1].children[1]; - await dom.requestChildNodes(bodyNode.nodeId); + Node bodyNode = (await dom!.getDocument()).children![1].children![1]; + await dom!.requestChildNodes(bodyNode.nodeId); - Node div1 = bodyNode.children[0]; - await dom.requestChildNodes(div1.nodeId); + Node div1 = bodyNode.children![0]; + await dom!.requestChildNodes(div1.nodeId); - Node h1 = div1.children[0]; - await dom.requestChildNodes(h1.nodeId); + Node h1 = div1.children![0]; + await dom!.requestChildNodes(h1.nodeId); - Node text = h1.children[0]; + Node text = h1.children![0]; expect(text.nodeValue, 'test'); - await dom.setNodeValue(text.nodeId, 'some new text'); + await dom!.setNodeValue(text.nodeId, 'some new text'); expect(text.nodeValue, 'some new text'); }); test('Adding attribute updates attributes', () async { - Node bodyNode = (await dom.getDocument()).children[1].children[1]; - expect(bodyNode.attributes.containsKey('my-attr'), isFalse); - await dom.setAttributeValue(bodyNode.nodeId, 'my-attr', 'my-value'); - expect(bodyNode.attributes['my-attr'], 'my-value'); + Node bodyNode = (await dom!.getDocument()).children![1].children![1]; + expect(bodyNode.attributes!.containsKey('my-attr'), isFalse); + await dom!.setAttributeValue(bodyNode.nodeId, 'my-attr', 'my-value'); + expect(bodyNode.attributes!['my-attr'], 'my-value'); }); test('Changing attribute updates attributes', () async { - Node bodyNode = (await dom.getDocument()).children[1].children[1]; - expect(bodyNode.attributes['test-attr'], 'test-attr-value'); - await dom.setAttributeValue(bodyNode.nodeId, 'test-attr', 'my-value'); - expect(bodyNode.attributes['test-attr'], 'my-value'); + Node bodyNode = (await dom!.getDocument()).children![1].children![1]; + expect(bodyNode.attributes!['test-attr'], 'test-attr-value'); + await dom!.setAttributeValue(bodyNode.nodeId, 'test-attr', 'my-value'); + expect(bodyNode.attributes!['test-attr'], 'my-value'); }); test('Removing attribute updates attributes', () async { - Node bodyNode = (await dom.getDocument()).children[1].children[1]; - expect(bodyNode.attributes['test-attr'], 'test-attr-value'); - await dom.removeAttribute(bodyNode.nodeId, 'test-attr'); - expect(bodyNode.attributes.containsKey('test-attr'), isFalse); + Node bodyNode = (await dom!.getDocument()).children![1].children![1]; + expect(bodyNode.attributes!['test-attr'], 'test-attr-value'); + await dom!.removeAttribute(bodyNode.nodeId, 'test-attr'); + expect(bodyNode.attributes!.containsKey('test-attr'), isFalse); }); test('refreshing resets document', () async { - var document1 = await dom.getDocument(); + var document1 = await dom!.getDocument(); await navigateToPage('dom_model_test.html'); - var document2 = await dom.getDocument(); + var document2 = await dom!.getDocument(); expect(document2.nodeId, isNot(document1.nodeId)); }); test('getting attributes works', () async { - Node bodyNode = (await dom.getDocument()).children[1].children[1]; + Node bodyNode = (await dom!.getDocument()).children![1].children![1]; var attributes = bodyNode.attributes; - var getAttributes = await dom.getAttributes(bodyNode.nodeId); + var getAttributes = await dom!.getAttributes(bodyNode.nodeId); expect(getAttributes, attributes); expect(bodyNode.attributes, attributes);
diff --git a/test/runtime_test.dart b/test/runtime_test.dart index 4f4b78a..33d61cf 100644 --- a/test/runtime_test.dart +++ b/test/runtime_test.dart
@@ -13,7 +13,7 @@ void main() { group('WipRuntime', () { - WipRuntime runtime; + WipRuntime? runtime; List<StreamSubscription> subs = []; setUp(() async { @@ -21,46 +21,48 @@ }); tearDown(() async { - await runtime.disable(); + await runtime?.disable(); runtime = null; await closeConnection(); - subs.forEach((s) => s.cancel()); + for (var s in subs) { + s.cancel(); + } subs.clear(); }); test('getIsolateId', () async { - await runtime.enable(); + await runtime!.enable(); await navigateToPage('runtime_test.html'); - expect(await runtime.getIsolateId(), isNotEmpty); + expect(await runtime!.getIsolateId(), isNotEmpty); }); test('getHeapUsage', () async { - await runtime.enable(); + await runtime!.enable(); await navigateToPage('runtime_test.html'); - HeapUsage usage = await runtime.getHeapUsage(); + HeapUsage usage = await runtime!.getHeapUsage(); expect(usage.usedSize, greaterThan(0)); expect(usage.totalSize, greaterThan(0)); }); test('evaluate', () async { - await runtime.enable(); + await runtime!.enable(); await navigateToPage('runtime_test.html'); - RemoteObject result = await runtime.evaluate('1+1'); + RemoteObject result = await runtime!.evaluate('1+1'); expect(result.type, 'number'); expect(result.value, 2); }); test('callFunctionOn', () async { - await runtime.enable(); + await runtime!.enable(); await navigateToPage('runtime_test.html'); - RemoteObject console = await runtime.evaluate('console'); - RemoteObject result = await runtime.callFunctionOn( + RemoteObject console = await runtime!.evaluate('console'); + RemoteObject result = await runtime!.callFunctionOn( ''' function(msg) { console.log(msg); @@ -77,12 +79,12 @@ }); test('getProperties', () async { - await runtime.enable(); + await runtime!.enable(); await navigateToPage('runtime_test.html'); - RemoteObject console = await runtime.evaluate('console'); + RemoteObject console = await runtime!.evaluate('console'); - List<PropertyDescriptor> properties = await runtime.getProperties( + List<PropertyDescriptor> properties = await runtime!.getProperties( console, ownProperties: true, );
diff --git a/test/test_setup.dart b/test/test_setup.dart index 5a5cfbe..857e445 100644 --- a/test/test_setup.dart +++ b/test/test_setup.dart
@@ -10,26 +10,24 @@ import 'package:webdriver/io.dart'; import 'package:webkit_inspection_protocol/webkit_inspection_protocol.dart'; -Future<WipConnection> _wipConnection; +Future<WipConnection>? _wipConnection; /// Returns a (cached) debugger connection to the first regular tab of /// the browser with remote debugger running at 'localhost:9222', Future<WipConnection> get wipConnection { - if (_wipConnection == null) { - _wipConnection = () async { - var debugPort = await _startWebDriver(await _startChromeDriver()); - var chrome = new ChromeConnection('localhost', debugPort); - var tab = await chrome - .getTab((tab) => !tab.isBackgroundPage && !tab.isChromeExtension); - var connection = await tab.connect(); - connection.onClose.listen((_) => _wipConnection = null); - return connection; - }(); - } - return _wipConnection; + _wipConnection ??= () async { + var debugPort = await _startWebDriver(await _startChromeDriver()); + var chrome = ChromeConnection('localhost', debugPort); + var tab = (await chrome + .getTab((tab) => !tab.isBackgroundPage && !tab.isChromeExtension))!; + var connection = await tab.connect(); + connection.onClose.listen((_) => _wipConnection = null); + return connection; + }(); + return _wipConnection!; } -Process _chromeDriver; +Process? _chromeDriver; /// Starts ChromeDriver and returns the listening port. Future<int> _startChromeDriver() async { @@ -39,12 +37,12 @@ await Future.delayed(const Duration(milliseconds: 25)); try { - var _exeExt = Platform.isWindows ? '.exe' : ''; - _chromeDriver = await Process.start('chromedriver$_exeExt', + var exeExt = Platform.isWindows ? '.exe' : ''; + _chromeDriver = await Process.start('chromedriver$exeExt', ['--port=$chromeDriverPort', '--url-base=wd/hub']); // On windows this takes a while to boot up, wait for the first line // of stdout as a signal that it is ready. - await _chromeDriver.stdout + await _chromeDriver!.stdout .transform(utf8.decoder) .transform(const LineSplitter()) .first; @@ -55,7 +53,7 @@ return chromeDriverPort; } -WebDriver _webDriver; +WebDriver? _webDriver; /// Starts WebDriver and returns the listening debug port. Future<int> _startWebDriver(int chromeDriverPort) async { @@ -93,24 +91,22 @@ return port; } -var _testServerUri; +Future<Uri>? _testServerUri; /// Ensures that an HTTP server serving files from 'test/data' has been /// started and navigates to to [page] using [wipConnection]. /// Return [wipConnection]. Future<WipConnection> navigateToPage(String page) async { - if (_testServerUri == null) { - _testServerUri = () async { - var receivePort = new ReceivePort(); - await Isolate.spawn(_startHttpServer, receivePort.sendPort); - var port = await receivePort.first; - return new Uri.http('localhost:$port', ''); - }(); - } + _testServerUri ??= () async { + var receivePort = ReceivePort(); + await Isolate.spawn(_startHttpServer, receivePort.sendPort); + var port = await receivePort.first; + return Uri.http('localhost:$port', ''); + }(); await (await wipConnection) .page - .navigate((await _testServerUri).resolve(page).toString()); - await new Future.delayed(const Duration(seconds: 1)); + .navigate((await _testServerUri)!.resolve(page).toString()); + await Future.delayed(const Duration(seconds: 1)); return wipConnection; }
diff --git a/tool/travis.sh b/tool/travis.sh deleted file mode 100755 index 04cf687..0000000 --- a/tool/travis.sh +++ /dev/null
@@ -1,25 +0,0 @@ -#!/bin/bash - -# Copyright (c) 2014, Google Inc. 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. - -# Fast fail the script on failures. -set -e - -# Verify that the libraries are error free. -pub global activate tuneup -pub global run tuneup check - -# Install CHROMEDRIVER -export CHROMEDRIVER_BINARY=/usr/bin/google-chrome -export CHROMEDRIVER_OS=linux64 -export CHROME_LATEST_VERSION=$("$CHROMEDRIVER_BINARY" --version | cut -d' ' -f3 | cut -d'.' -f1) -export CHROME_DRIVER_VERSION=$(wget -qO- https://chromedriver.storage.googleapis.com/LATEST_RELEASE_$CHROME_LATEST_VERSION) -wget https://chromedriver.storage.googleapis.com/$CHROME_DRIVER_VERSION/chromedriver_$CHROMEDRIVER_OS.zip -unzip "chromedriver_${CHROMEDRIVER_OS}.zip" -export CHROMEDRIVER_ARGS=--no-sandbox -export PATH=$PATH:$PWD - -# Run tests -pub run test -j 1