Index: runtime/bin/vmservice/client/lib/service_common.dart |
=================================================================== |
--- runtime/bin/vmservice/client/lib/service_common.dart (revision 38598) |
+++ runtime/bin/vmservice/client/lib/service_common.dart (working copy) |
@@ -2,11 +2,10 @@ |
// 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. |
-library service_html; |
+library service_common; |
import 'dart:async'; |
import 'dart:convert'; |
-import 'dart:html'; |
import 'package:logging/logging.dart'; |
import 'package:observatory/service.dart'; |
@@ -60,10 +59,23 @@ |
: 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 { |
+/// Minimal common interface for 'WebSocket' in [dart:io] and [dart:html]. |
+abstract class CommonWebSocket { |
+ void connect(String address, |
+ void onOpen(), |
+ void onMessage(dynamic data), |
+ void onError(), |
+ void onClose()); |
+ bool get isOpen; |
+ void send(dynamic data); |
+ void close(); |
+} |
+ |
+/// A [CommonWebSocketVM] communicates with a Dart VM over a CommonWebSocket. |
+/// 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. |
+abstract class CommonWebSocketVM extends VM { |
final Completer _connected = new Completer(); |
final Completer _disconnected = new Completer(); |
final WebSocketVMTarget target; |
@@ -72,9 +84,11 @@ |
final Map<String, _WebSocketRequest> _pendingRequests = |
new Map<String, _WebSocketRequest>(); |
int _requestSerial = 0; |
- WebSocket _webSocket; |
+ bool _hasInitiatedConnect = false; |
- WebSocketVM(this.target) { |
+ CommonWebSocket _webSocket; |
+ |
+ CommonWebSocketVM(this.target, this._webSocket) { |
assert(target != null); |
} |
@@ -94,7 +108,7 @@ |
Future get onDisconnect => _disconnected.future; |
void disconnect() { |
- if (_webSocket != null) { |
+ if (_hasInitiatedConnect) { |
_webSocket.close(); |
} |
_cancelAllRequests(); |
@@ -102,24 +116,21 @@ |
} |
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); |
+ if (!_hasInitiatedConnect) { |
+ _hasInitiatedConnect = true; |
+ _webSocket.connect( |
+ target.networkAddress, _onOpen, _onMessage, _onError, _onClose); |
} |
return _makeRequest(id); |
} |
/// Add a request for [id] to pending requests. |
Future<String> _makeRequest(String id) { |
- assert(_webSocket != null); |
+ assert(_hasInitiatedConnect); |
// Create request. |
String serial = (_requestSerial++).toString(); |
var request = new _WebSocketRequest(id); |
- if (_webSocket.readyState == WebSocket.OPEN) { |
+ if (_webSocket.isOpen) { |
// Already connected, send request immediately. |
_sendRequest(serial, request); |
} else { |
@@ -129,27 +140,28 @@ |
return request.completer.future; |
} |
- void _onClose(CloseEvent event) { |
+ void _onClose() { |
_cancelAllRequests(); |
_notifyDisconnect(); |
} |
// WebSocket error event handler. |
- void _onError(Event) { |
+ void _onError() { |
_cancelAllRequests(); |
_notifyDisconnect(); |
} |
// WebSocket open event handler. |
- void _onOpen(Event) { |
+ void _onOpen() { |
target.lastConnectionTime = new DateTime.now().millisecondsSinceEpoch; |
_sendAllDelayedRequests(); |
_notifyConnect(); |
} |
// WebSocket message event handler. |
- void _onMessage(MessageEvent event) { |
- var map = JSON.decode(event.data); |
+ void _onMessage(dynamic data) { |
+ assert(data is String); // We don't handle binary data, yet. |
+ var map = JSON.decode(data); |
if (map == null) { |
Logger.root.severe('WebSocketVM got empty message'); |
return; |
@@ -214,7 +226,7 @@ |
/// Send all delayed requests. |
void _sendAllDelayedRequests() { |
- assert(_webSocket != null); |
+ assert(_webSocket.isOpen); |
if (_delayedRequests.length == 0) { |
return; |
} |
@@ -227,7 +239,7 @@ |
/// Send the request over WebSocket. |
void _sendRequest(String serial, _WebSocketRequest request) { |
- assert (_webSocket.readyState == WebSocket.OPEN); |
+ assert (_webSocket.isOpen); |
if (!request.id.endsWith('/profile/tag')) { |
Logger.root.info('GET ${request.id} from ${target.networkAddress}'); |
} |
@@ -252,46 +264,3 @@ |
_webSocket.send(message); |
} |
} |
- |
-// A VM that communicates with the service via posting messages from DevTools. |
-class PostMessageVM extends VM { |
- final Completer _connected = new Completer(); |
- final Completer _disconnected = new Completer(); |
- void disconnect() { /* nope */ } |
- Future get onConnect => _connected.future; |
- Future get onDisconnect => _disconnected.future; |
- final Map<String, Completer> _pendingRequests = |
- new Map<String, Completer>(); |
- int _requestSerial = 0; |
- |
- PostMessageVM() : super() { |
- window.onMessage.listen(_messageHandler); |
- _connected.complete(this); |
- } |
- |
- void _messageHandler(msg) { |
- var id = msg.data['id']; |
- var name = msg.data['name']; |
- var data = msg.data['data']; |
- if (name != 'observatoryData') { |
- return; |
- } |
- var completer = _pendingRequests[id]; |
- assert(completer != null); |
- _pendingRequests.remove(id); |
- completer.complete(data); |
- } |
- |
- Future<String> getString(String path) { |
- var idString = '$_requestSerial'; |
- Map message = {}; |
- message['id'] = idString; |
- message['method'] = 'observatoryQuery'; |
- message['query'] = '$path'; |
- _requestSerial++; |
- var completer = new Completer(); |
- _pendingRequests[idString] = completer; |
- window.parent.postMessage(JSON.encode(message), '*'); |
- return completer.future; |
- } |
-} |