Skip to content

Commit bd0bcb8

Browse files
authored
Support running DWDS without a Chrome Debug Port (web-socket-based) (#2639)
* implemented socket-based dwds * fix issue with run main at start * updated changelog * simulate debugExtension in websocket-based execution flow * added multiwindow support * refactoring and removing unused method * support page refresh from vsCode * refactor createisolate and resume method * implemented pause/resume logic * improve canReuseConnection logic * comments cleanup * fixed canReuseConnection logic * implemented _handleConnectionClosed and fix issue with getVM * fix issue with handling isolate cleanup * addressed comments regarding closing remoteDebugger * code cleanup * created common interface for debugService and webSocketDebugService * remove use of _acceptNewConnections in WebSocketDebugService * created proxy_service.dart and consolidated all duplicated code * consolidate webSocketAppDebugService and AppDebugServices * consolidate dwdsVmClient and WebSocketDwdsVmClient * refactored can remove duplicate methods * remove use of dynamic * fix dev_handler error * consolidate handleChromeMessages and handleWebSocketMessages created default createIsolate and destroyIsolate in ProxyService * updated logg message in _sendRequestToClients * updated dev_handler to throw error if proxy_service is not the right type * addressing analyzer issues * removed unused variable * updated port to 44456 * refactored classes to and interface names to be more clear * fix variable name error * updated variable to be type * prepare version 24.4.1 for release
1 parent 84ebf86 commit bd0bcb8

23 files changed

+4023
-1222
lines changed

dwds/CHANGELOG.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
1-
## 24.4.1-wip
1+
## 24.4.1
22

3+
- Implemented a WebSocket-based communication protocol that provides essential developer tooling (hot reload, service extensions) when Chrome debugger access is unavailable. - [#2605](https://github.com/dart-lang/webdev/issues/2605)
4+
- Added WebSocket-based hot reload and service extension support via new `WebSocketProxyService` class that implements VM service protocol over WebSockets.
5+
- Enhanced `DevHandler` with `useWebSocketConnection` flag to toggle between Chrome-based and WebSocket-based communication protocols.
36
- Fixed an issue where we didn't wait until all scripts were parsed before
47
recomputing metadata on a hot reload.
58

dwds/lib/dart_web_debug_service.dart

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,15 @@ class Dwds {
3131
final DevHandler _devHandler;
3232
final AssetReader _assetReader;
3333
final bool _enableDebugging;
34+
final bool _useDwdsWebSocketConnection;
3435

3536
Dwds._(
3637
this.middleware,
3738
this._devTools,
3839
this._devHandler,
3940
this._assetReader,
4041
this._enableDebugging,
42+
this._useDwdsWebSocketConnection,
4143
) : handler = _devHandler.handler;
4244

4345
Stream<AppConnection> get connectedApps => _devHandler.connectedApps;
@@ -53,12 +55,18 @@ class Dwds {
5355
await _assetReader.close();
5456
}
5557

58+
/// Creates a debug connection for the given app connection.
59+
///
60+
/// Returns a [DebugConnection] that wraps the appropriate debug services
61+
/// based on the connection type (WebSocket or Chrome-based).
5662
Future<DebugConnection> debugConnection(AppConnection appConnection) async {
5763
if (!_enableDebugging) throw StateError('Debugging is not enabled.');
58-
final appDebugServices = await _devHandler.loadAppServices(appConnection);
59-
final chromeProxyService = appDebugServices.chromeProxyService;
60-
await chromeProxyService.isInitialized;
61-
return DebugConnection(appDebugServices);
64+
65+
if (_useDwdsWebSocketConnection) {
66+
return await _devHandler.createDebugConnectionForWebSocket(appConnection);
67+
} else {
68+
return await _devHandler.createDebugConnectionForChrome(appConnection);
69+
}
6270
}
6371

6472
static Future<Dwds> start({
@@ -123,10 +131,7 @@ class Dwds {
123131
_logger.info('Serving DevTools at $uri\n');
124132
}
125133

126-
final injected = DwdsInjector(
127-
extensionUri: extensionUri,
128-
useDwdsWebSocketConnection: useDwdsWebSocketConnection,
129-
);
134+
final injected = DwdsInjector(extensionUri: extensionUri);
130135

131136
final devHandler = DevHandler(
132137
chromeConnection,
@@ -143,6 +148,7 @@ class Dwds {
143148
debugSettings.spawnDds,
144149
debugSettings.ddsPort,
145150
debugSettings.launchDevToolsInNewWindow,
151+
useWebSocketConnection: useDwdsWebSocketConnection,
146152
);
147153

148154
return Dwds._(
@@ -151,6 +157,7 @@ class Dwds {
151157
devHandler,
152158
assetReader,
153159
debugSettings.enableDebugging,
160+
useDwdsWebSocketConnection,
154161
);
155162
}
156163
}

dwds/lib/data/serializers.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ import 'error_response.dart';
1414
import 'extension_request.dart';
1515
import 'hot_reload_request.dart';
1616
import 'hot_reload_response.dart';
17+
import 'service_extension_request.dart';
18+
import 'service_extension_response.dart';
1719
import 'isolate_events.dart';
1820
import 'register_event.dart';
1921
import 'run_request.dart';
@@ -40,5 +42,7 @@ part 'serializers.g.dart';
4042
ErrorResponse,
4143
RegisterEvent,
4244
RunRequest,
45+
ServiceExtensionRequest,
46+
ServiceExtensionResponse,
4347
])
4448
final Serializers serializers = _$serializers;

dwds/lib/data/serializers.g.dart

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// Copyright (c) 2025, the Dart project authors. All rights reserved.
2+
// Defines the request for service extension calls over WebSocket.
3+
4+
import 'dart:convert';
5+
import 'package:built_value/built_value.dart';
6+
import 'package:built_value/serializer.dart';
7+
8+
part 'service_extension_request.g.dart';
9+
10+
abstract class ServiceExtensionRequest
11+
implements Built<ServiceExtensionRequest, ServiceExtensionRequestBuilder> {
12+
String get id;
13+
String get method;
14+
String
15+
get argsJson; // Store args as JSON string for built_value compatibility
16+
17+
// Helper method to get args as Map<String, dynamic>
18+
Map<String, dynamic> get args =>
19+
argsJson.isEmpty
20+
? <String, dynamic>{}
21+
: json.decode(argsJson) as Map<String, dynamic>;
22+
23+
ServiceExtensionRequest._();
24+
factory ServiceExtensionRequest([
25+
void Function(ServiceExtensionRequestBuilder) updates,
26+
]) = _$ServiceExtensionRequest;
27+
28+
// Convenient factory method to create with args Map
29+
factory ServiceExtensionRequest.fromArgs({
30+
required String id,
31+
required String method,
32+
required Map<String, dynamic> args,
33+
}) => ServiceExtensionRequest(
34+
(b) =>
35+
b
36+
..id = id
37+
..method = method
38+
..argsJson = json.encode(args),
39+
);
40+
41+
static Serializer<ServiceExtensionRequest> get serializer =>
42+
_$serviceExtensionRequestSerializer;
43+
}

dwds/lib/data/service_extension_request.g.dart

Lines changed: 228 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)