Index: runtime/bin/vmservice/client/lib/service_html.dart |
=================================================================== |
--- runtime/bin/vmservice/client/lib/service_html.dart (revision 38654) |
+++ runtime/bin/vmservice/client/lib/service_html.dart (working copy) |
@@ -8,249 +8,42 @@ |
import 'dart:convert'; |
import 'dart:html'; |
-import 'package:logging/logging.dart'; |
-import 'package:observatory/service.dart'; |
+import 'package:observatory/service_common.dart'; |
// Export the service library. |
-export 'package:observatory/service.dart'; |
+export 'package:observatory/service_common.dart'; |
-/// Description of a VM target. |
-class WebSocketVMTarget { |
- // Last time this VM has been connected to. |
- int lastConnectionTime = 0; |
- bool get hasEverConnected => lastConnectionTime > 0; |
+class _HtmlWebSocket implements CommonWebSocket { |
+ WebSocket _webSocket; |
- // Chrome VM or standalone; |
- bool chrome = false; |
- bool get standalone => !chrome; |
- |
- // User defined name. |
- String name; |
- // Network address of VM. |
- String networkAddress; |
- |
- WebSocketVMTarget(this.networkAddress) { |
- name = networkAddress; |
+ void connect(String address, |
+ void onOpen(), |
+ void onMessage(dynamic data), |
+ void onError(), |
+ void onClose()) { |
+ _webSocket = new WebSocket(address); |
+ _webSocket.onClose.listen((CloseEvent) => onClose()); |
+ _webSocket.onError.listen((Event) => onError()); |
+ _webSocket.onOpen.listen((Event) => onOpen()); |
+ _webSocket.onMessage.listen((MessageEvent event) => onMessage(event.data)); |
} |
- |
- WebSocketVMTarget.fromMap(Map json) { |
- lastConnectionTime = json['lastConnectionTime']; |
- chrome = json['chrome']; |
- name = json['name']; |
- networkAddress = json['networkAddress']; |
- if (name == null) { |
- name = networkAddress; |
- } |
+ |
+ bool get isOpen => _webSocket.readyState == WebSocket.OPEN; |
+ |
+ void send(dynamic data) { |
+ _webSocket.send(data); |
} |
- |
- Map toJson() { |
- return { |
- 'lastConnectionTime': lastConnectionTime, |
- 'chrome': chrome, |
- 'name': name, |
- 'networkAddress': networkAddress, |
- }; |
+ |
+ void close() { |
+ _webSocket.close(); |
} |
} |
-class _WebSocketRequest { |
- final String id; |
- final Completer<String> completer; |
- _WebSocketRequest(this.id) |
- : completer = new Completer<String>(); |
-} |
- |
/// The [WebSocketVM] communicates with a Dart VM over WebSocket. The Dart VM |
/// can be embedded in Chromium or standalone. In the case of Chromium, we |
/// make the service requests via the Chrome Remote Debugging Protocol. |
-class WebSocketVM extends VM { |
- final Completer _connected = new Completer(); |
- final Completer _disconnected = new Completer(); |
- final WebSocketVMTarget target; |
- final Map<String, _WebSocketRequest> _delayedRequests = |
- new Map<String, _WebSocketRequest>(); |
- final Map<String, _WebSocketRequest> _pendingRequests = |
- new Map<String, _WebSocketRequest>(); |
- int _requestSerial = 0; |
- WebSocket _webSocket; |
- |
- WebSocketVM(this.target) { |
- assert(target != null); |
- } |
- |
- void _notifyConnect() { |
- if (!_connected.isCompleted) { |
- Logger.root.info('WebSocketVM connection opened: ${target.networkAddress}'); |
- _connected.complete(this); |
- } |
- } |
- Future get onConnect => _connected.future; |
- void _notifyDisconnect() { |
- if (!_disconnected.isCompleted) { |
- Logger.root.info('WebSocketVM connection error: ${target.networkAddress}'); |
- _disconnected.complete(this); |
- } |
- } |
- Future get onDisconnect => _disconnected.future; |
- |
- void disconnect() { |
- if (_webSocket != null) { |
- _webSocket.close(); |
- } |
- _cancelAllRequests(); |
- _notifyDisconnect(); |
- } |
- |
- Future<String> getString(String id) { |
- if (_webSocket == null) { |
- // Create a WebSocket. |
- _webSocket = new WebSocket(target.networkAddress); |
- _webSocket.onClose.listen(_onClose); |
- _webSocket.onError.listen(_onError); |
- _webSocket.onOpen.listen(_onOpen); |
- _webSocket.onMessage.listen(_onMessage); |
- } |
- return _makeRequest(id); |
- } |
- |
- /// Add a request for [id] to pending requests. |
- Future<String> _makeRequest(String id) { |
- assert(_webSocket != null); |
- // Create request. |
- String serial = (_requestSerial++).toString(); |
- var request = new _WebSocketRequest(id); |
- if (_webSocket.readyState == WebSocket.OPEN) { |
- // Already connected, send request immediately. |
- _sendRequest(serial, request); |
- } else { |
- // Not connected yet, add to delayed requests. |
- _delayedRequests[serial] = request; |
- } |
- return request.completer.future; |
- } |
- |
- void _onClose(CloseEvent event) { |
- _cancelAllRequests(); |
- _notifyDisconnect(); |
- } |
- |
- // WebSocket error event handler. |
- void _onError(Event) { |
- _cancelAllRequests(); |
- _notifyDisconnect(); |
- } |
- |
- // WebSocket open event handler. |
- void _onOpen(Event) { |
- target.lastConnectionTime = new DateTime.now().millisecondsSinceEpoch; |
- _sendAllDelayedRequests(); |
- _notifyConnect(); |
- } |
- |
- // WebSocket message event handler. |
- void _onMessage(MessageEvent event) { |
- var map = JSON.decode(event.data); |
- if (map == null) { |
- Logger.root.severe('WebSocketVM got empty message'); |
- return; |
- } |
- // Extract serial and response. |
- var serial; |
- var response; |
- if (target.chrome) { |
- if (map['method'] != 'Dart.observatoryData') { |
- // ignore devtools protocol spam. |
- return; |
- } |
- serial = map['params']['id'].toString(); |
- response = map['params']['data']; |
- } else { |
- serial = map['seq']; |
- response = map['response']; |
- } |
- if (serial == null) { |
- // Messages without sequence numbers are asynchronous events |
- // from the vm. |
- postEventMessage(response); |
- return; |
- } |
- // Complete request. |
- var request = _pendingRequests.remove(serial); |
- if (request == null) { |
- Logger.root.severe('Received unexpected message: ${map}'); |
- return; |
- } |
- request.completer.complete(response); |
- } |
- |
- String _generateNetworkError(String userMessage) { |
- return JSON.encode({ |
- 'type': 'ServiceException', |
- 'id': '', |
- 'kind': 'NetworkException', |
- 'message': userMessage |
- }); |
- } |
- |
- void _cancelRequests(Map<String, _WebSocketRequest> requests) { |
- requests.forEach((String serial, _WebSocketRequest request) { |
- request.completer.complete( |
- _generateNetworkError('WebSocket disconnected')); |
- }); |
- requests.clear(); |
- } |
- |
- /// Cancel all pending and delayed requests by completing them with an error. |
- void _cancelAllRequests() { |
- if (_pendingRequests.length > 0) { |
- Logger.root.info('Cancelling all pending requests.'); |
- _cancelRequests(_pendingRequests); |
- } |
- if (_delayedRequests.length > 0) { |
- Logger.root.info('Cancelling all delayed requests.'); |
- _cancelRequests(_delayedRequests); |
- } |
- } |
- |
- /// Send all delayed requests. |
- void _sendAllDelayedRequests() { |
- assert(_webSocket != null); |
- if (_delayedRequests.length == 0) { |
- return; |
- } |
- Logger.root.info('Sending all delayed requests.'); |
- // Send all delayed requests. |
- _delayedRequests.forEach(_sendRequest); |
- // Clear all delayed requests. |
- _delayedRequests.clear(); |
- } |
- |
- /// Send the request over WebSocket. |
- void _sendRequest(String serial, _WebSocketRequest request) { |
- assert (_webSocket.readyState == WebSocket.OPEN); |
- if (!request.id.endsWith('/profile/tag')) { |
- Logger.root.info('GET ${request.id} from ${target.networkAddress}'); |
- } |
- // Mark request as pending. |
- assert(_pendingRequests.containsKey(serial) == false); |
- _pendingRequests[serial] = request; |
- var message; |
- // Encode message. |
- if (target.chrome) { |
- message = JSON.encode({ |
- 'id': int.parse(serial), |
- 'method': 'Dart.observatoryQuery', |
- 'params': { |
- 'id': serial, |
- 'query': request.id |
- } |
- }); |
- } else { |
- message = JSON.encode({'seq': serial, 'request': request.id}); |
- } |
- // Send message. |
- _webSocket.send(message); |
- } |
+class WebSocketVM extends CommonWebSocketVM { |
+ WebSocketVM(WebSocketVMTarget target) : super(target, new _HtmlWebSocket()); |
} |
// A VM that communicates with the service via posting messages from DevTools. |