From ce0079c4875178db81763c6f018b5e773b95b0d7 Mon Sep 17 00:00:00 2001 From: Ben Konyi Date: Thu, 28 Aug 2025 13:50:06 -0400 Subject: [PATCH 1/7] [ DWDS ] Serve DevTools from DDS by default In order to reduce the number of ways DevTools is served across the ecosystem, we're working on serving DevTools from DDS by default in all tools that spawn DDS. This change exposes some new DDS related configuration options that allow for specifying whether or not DevTools should be served via DDS and whether or not an existing DevTools server should be used. The `devToolsLauncher` parameter has been marked as deprecated as it will be removed in a future major release, but will continue to act as the default way to launch DevTools if it is provided. Other DDS related properties that have been merged into the new `DartDevelopmentServiceConfiguration` class are also marked as deprecated. --- dwds/CHANGELOG.md | 8 + .../debug_extension/tool/build_extension.dart | 5 +- .../web/data_serializers.g.dart | 32 +-- dwds/debug_extension/web/data_types.g.dart | 272 ++++++------------ dwds/lib/dart_web_debug_service.dart | 21 +- dwds/lib/dwds.dart | 1 + dwds/lib/src/config/tool_configuration.dart | 46 ++- .../lib/src/connections/debug_connection.dart | 5 +- dwds/lib/src/handlers/dev_handler.dart | 74 +++-- dwds/lib/src/injected/client.js | 2 +- dwds/lib/src/servers/devtools.dart | 2 + dwds/lib/src/services/app_debug_services.dart | 9 + dwds/lib/src/services/debug_service.dart | 24 +- dwds/lib/src/version.dart | 2 +- dwds/pubspec.yaml | 14 +- dwds/test/common/hot_restart_common.dart | 6 +- .../hot_restart_correctness_common.dart | 2 +- dwds/test/dds_port_test.dart | 30 +- dwds/test/debug_extension_test.dart | 10 +- dwds/test/debug_service_test.dart | 6 +- dwds/test/devtools_test.dart | 226 ++++++++------- dwds/test/events_test.dart | 2 +- dwds/test/fixtures/context.dart | 3 +- dwds/test/fixtures/utilities.dart | 53 ++-- dwds/test/handlers/injector_test.dart | 4 +- dwds/test/puppeteer/test_utils.dart | 4 +- 26 files changed, 449 insertions(+), 414 deletions(-) diff --git a/dwds/CHANGELOG.md b/dwds/CHANGELOG.md index 5e28fd57e..7208f3e20 100644 --- a/dwds/CHANGELOG.md +++ b/dwds/CHANGELOG.md @@ -1,3 +1,11 @@ +## 25.1.0 + +- Added `DartDevelopmentServiceConfiguration` to allow for configuring DDS behavior. +- Added support for serving DevTools via DDS. This will become the sole method of serving + DevTools from DWDS in a future major release. +- Deprecated `spawnDds`, `ddsPort`, and `devToolsLauncher` properties in `DebugSettings`. +- Added `ddsConfiguration` to `DebugSettings`. + ## 25.0.4 ### Bug Fixes: diff --git a/dwds/debug_extension/tool/build_extension.dart b/dwds/debug_extension/tool/build_extension.dart index 418c24567..e3fc9eb7a 100644 --- a/dwds/debug_extension/tool/build_extension.dart +++ b/dwds/debug_extension/tool/build_extension.dart @@ -20,8 +20,8 @@ import 'package:path/path.dart' as p; const _prodFlag = 'prod'; void main(List arguments) async { - final parser = - ArgParser()..addFlag(_prodFlag, negatable: true, defaultsTo: false); + final parser = ArgParser() + ..addFlag(_prodFlag, negatable: true, defaultsTo: false); final argResults = parser.parse(arguments); exitCode = await run(isProd: argResults[_prodFlag] as bool); @@ -41,6 +41,7 @@ Future run({required bool isProd}) async { '--output', 'build', '--release', + '--delete-conflicting-outputs' ]); final compileExitCode = await _handleProcess(compileStep); // Terminate early if compilation failed: diff --git a/dwds/debug_extension/web/data_serializers.g.dart b/dwds/debug_extension/web/data_serializers.g.dart index dd746a8d5..cd68e66d4 100644 --- a/dwds/debug_extension/web/data_serializers.g.dart +++ b/dwds/debug_extension/web/data_serializers.g.dart @@ -6,22 +6,20 @@ part of 'data_serializers.dart'; // BuiltValueGenerator // ************************************************************************** -Serializers _$serializers = - (new Serializers().toBuilder() - ..add(BatchedEvents.serializer) - ..add(ConnectFailure.serializer) - ..add(DebugInfo.serializer) - ..add(DebugStateChange.serializer) - ..add(DevToolsOpener.serializer) - ..add(DevToolsRequest.serializer) - ..add(DevToolsUrl.serializer) - ..add(ExtensionEvent.serializer) - ..add(ExtensionRequest.serializer) - ..add(ExtensionResponse.serializer) - ..addBuilderFactory( - const FullType(BuiltList, const [const FullType(ExtensionEvent)]), - () => new ListBuilder(), - )) - .build(); +Serializers _$serializers = (Serializers().toBuilder() + ..add(BatchedEvents.serializer) + ..add(ConnectFailure.serializer) + ..add(DebugInfo.serializer) + ..add(DebugStateChange.serializer) + ..add(DevToolsOpener.serializer) + ..add(DevToolsRequest.serializer) + ..add(DevToolsUrl.serializer) + ..add(ExtensionEvent.serializer) + ..add(ExtensionRequest.serializer) + ..add(ExtensionResponse.serializer) + ..addBuilderFactory( + const FullType(BuiltList, const [const FullType(ExtensionEvent)]), + () => ListBuilder())) + .build(); // ignore_for_file: deprecated_member_use_from_same_package,type=lint diff --git a/dwds/debug_extension/web/data_types.g.dart b/dwds/debug_extension/web/data_types.g.dart index 831d48344..00f5a0ddb 100644 --- a/dwds/debug_extension/web/data_types.g.dart +++ b/dwds/debug_extension/web/data_types.g.dart @@ -7,12 +7,12 @@ part of 'data_types.dart'; // ************************************************************************** Serializer _$connectFailureSerializer = - new _$ConnectFailureSerializer(); + _$ConnectFailureSerializer(); Serializer _$devToolsOpenerSerializer = - new _$DevToolsOpenerSerializer(); -Serializer _$devToolsUrlSerializer = new _$DevToolsUrlSerializer(); + _$DevToolsOpenerSerializer(); +Serializer _$devToolsUrlSerializer = _$DevToolsUrlSerializer(); Serializer _$debugStateChangeSerializer = - new _$DebugStateChangeSerializer(); + _$DebugStateChangeSerializer(); class _$ConnectFailureSerializer implements StructuredSerializer { @@ -22,11 +22,8 @@ class _$ConnectFailureSerializer final String wireName = 'ConnectFailure'; @override - Iterable serialize( - Serializers serializers, - ConnectFailure object, { - FullType specifiedType = FullType.unspecified, - }) { + Iterable serialize(Serializers serializers, ConnectFailure object, + {FullType specifiedType = FullType.unspecified}) { final result = [ 'tabId', serializers.serialize(object.tabId, specifiedType: const FullType(int)), @@ -36,20 +33,17 @@ class _$ConnectFailureSerializer if (value != null) { result ..add('reason') - ..add( - serializers.serialize(value, specifiedType: const FullType(String)), - ); + ..add(serializers.serialize(value, + specifiedType: const FullType(String))); } return result; } @override ConnectFailure deserialize( - Serializers serializers, - Iterable serialized, { - FullType specifiedType = FullType.unspecified, - }) { - final result = new ConnectFailureBuilder(); + Serializers serializers, Iterable serialized, + {FullType specifiedType = FullType.unspecified}) { + final result = ConnectFailureBuilder(); final iterator = serialized.iterator; while (iterator.moveNext()) { @@ -58,20 +52,12 @@ class _$ConnectFailureSerializer final Object? value = iterator.current; switch (key) { case 'tabId': - result.tabId = - serializers.deserialize( - value, - specifiedType: const FullType(int), - )! - as int; + result.tabId = serializers.deserialize(value, + specifiedType: const FullType(int))! as int; break; case 'reason': - result.reason = - serializers.deserialize( - value, - specifiedType: const FullType(String), - ) - as String?; + result.reason = serializers.deserialize(value, + specifiedType: const FullType(String)) as String?; break; } } @@ -88,17 +74,12 @@ class _$DevToolsOpenerSerializer final String wireName = 'DevToolsOpener'; @override - Iterable serialize( - Serializers serializers, - DevToolsOpener object, { - FullType specifiedType = FullType.unspecified, - }) { + Iterable serialize(Serializers serializers, DevToolsOpener object, + {FullType specifiedType = FullType.unspecified}) { final result = [ 'newWindow', - serializers.serialize( - object.newWindow, - specifiedType: const FullType(bool), - ), + serializers.serialize(object.newWindow, + specifiedType: const FullType(bool)), ]; return result; @@ -106,11 +87,9 @@ class _$DevToolsOpenerSerializer @override DevToolsOpener deserialize( - Serializers serializers, - Iterable serialized, { - FullType specifiedType = FullType.unspecified, - }) { - final result = new DevToolsOpenerBuilder(); + Serializers serializers, Iterable serialized, + {FullType specifiedType = FullType.unspecified}) { + final result = DevToolsOpenerBuilder(); final iterator = serialized.iterator; while (iterator.moveNext()) { @@ -119,12 +98,8 @@ class _$DevToolsOpenerSerializer final Object? value = iterator.current; switch (key) { case 'newWindow': - result.newWindow = - serializers.deserialize( - value, - specifiedType: const FullType(bool), - )! - as bool; + result.newWindow = serializers.deserialize(value, + specifiedType: const FullType(bool))! as bool; break; } } @@ -140,11 +115,8 @@ class _$DevToolsUrlSerializer implements StructuredSerializer { final String wireName = 'DevToolsUrl'; @override - Iterable serialize( - Serializers serializers, - DevToolsUrl object, { - FullType specifiedType = FullType.unspecified, - }) { + Iterable serialize(Serializers serializers, DevToolsUrl object, + {FullType specifiedType = FullType.unspecified}) { final result = [ 'tabId', serializers.serialize(object.tabId, specifiedType: const FullType(int)), @@ -156,12 +128,9 @@ class _$DevToolsUrlSerializer implements StructuredSerializer { } @override - DevToolsUrl deserialize( - Serializers serializers, - Iterable serialized, { - FullType specifiedType = FullType.unspecified, - }) { - final result = new DevToolsUrlBuilder(); + DevToolsUrl deserialize(Serializers serializers, Iterable serialized, + {FullType specifiedType = FullType.unspecified}) { + final result = DevToolsUrlBuilder(); final iterator = serialized.iterator; while (iterator.moveNext()) { @@ -170,20 +139,12 @@ class _$DevToolsUrlSerializer implements StructuredSerializer { final Object? value = iterator.current; switch (key) { case 'tabId': - result.tabId = - serializers.deserialize( - value, - specifiedType: const FullType(int), - )! - as int; + result.tabId = serializers.deserialize(value, + specifiedType: const FullType(int))! as int; break; case 'url': - result.url = - serializers.deserialize( - value, - specifiedType: const FullType(String), - )! - as String; + result.url = serializers.deserialize(value, + specifiedType: const FullType(String))! as String; break; } } @@ -200,39 +161,31 @@ class _$DebugStateChangeSerializer final String wireName = 'DebugStateChange'; @override - Iterable serialize( - Serializers serializers, - DebugStateChange object, { - FullType specifiedType = FullType.unspecified, - }) { + Iterable serialize(Serializers serializers, DebugStateChange object, + {FullType specifiedType = FullType.unspecified}) { final result = [ 'tabId', serializers.serialize(object.tabId, specifiedType: const FullType(int)), 'newState', - serializers.serialize( - object.newState, - specifiedType: const FullType(String), - ), + serializers.serialize(object.newState, + specifiedType: const FullType(String)), ]; Object? value; value = object.reason; if (value != null) { result ..add('reason') - ..add( - serializers.serialize(value, specifiedType: const FullType(String)), - ); + ..add(serializers.serialize(value, + specifiedType: const FullType(String))); } return result; } @override DebugStateChange deserialize( - Serializers serializers, - Iterable serialized, { - FullType specifiedType = FullType.unspecified, - }) { - final result = new DebugStateChangeBuilder(); + Serializers serializers, Iterable serialized, + {FullType specifiedType = FullType.unspecified}) { + final result = DebugStateChangeBuilder(); final iterator = serialized.iterator; while (iterator.moveNext()) { @@ -241,28 +194,16 @@ class _$DebugStateChangeSerializer final Object? value = iterator.current; switch (key) { case 'tabId': - result.tabId = - serializers.deserialize( - value, - specifiedType: const FullType(int), - )! - as int; + result.tabId = serializers.deserialize(value, + specifiedType: const FullType(int))! as int; break; case 'newState': - result.newState = - serializers.deserialize( - value, - specifiedType: const FullType(String), - )! - as String; + result.newState = serializers.deserialize(value, + specifiedType: const FullType(String))! as String; break; case 'reason': - result.reason = - serializers.deserialize( - value, - specifiedType: const FullType(String), - ) - as String?; + result.reason = serializers.deserialize(value, + specifiedType: const FullType(String)) as String?; break; } } @@ -278,19 +219,15 @@ class _$ConnectFailure extends ConnectFailure { final String? reason; factory _$ConnectFailure([void Function(ConnectFailureBuilder)? updates]) => - (new ConnectFailureBuilder()..update(updates))._build(); - - _$ConnectFailure._({required this.tabId, this.reason}) : super._() { - BuiltValueNullFieldError.checkNotNull(tabId, r'ConnectFailure', 'tabId'); - } + (ConnectFailureBuilder()..update(updates))._build(); + _$ConnectFailure._({required this.tabId, this.reason}) : super._(); @override ConnectFailure rebuild(void Function(ConnectFailureBuilder) updates) => (toBuilder()..update(updates)).build(); @override - ConnectFailureBuilder toBuilder() => - new ConnectFailureBuilder()..replace(this); + ConnectFailureBuilder toBuilder() => ConnectFailureBuilder()..replace(this); @override bool operator ==(Object other) { @@ -344,7 +281,6 @@ class ConnectFailureBuilder @override void replace(ConnectFailure other) { - ArgumentError.checkNotNull(other, 'other'); _$v = other as _$ConnectFailure; } @@ -357,14 +293,10 @@ class ConnectFailureBuilder ConnectFailure build() => _build(); _$ConnectFailure _build() { - final _$result = - _$v ?? - new _$ConnectFailure._( + final _$result = _$v ?? + _$ConnectFailure._( tabId: BuiltValueNullFieldError.checkNotNull( - tabId, - r'ConnectFailure', - 'tabId', - ), + tabId, r'ConnectFailure', 'tabId'), reason: reason, ); replace(_$result); @@ -377,23 +309,15 @@ class _$DevToolsOpener extends DevToolsOpener { final bool newWindow; factory _$DevToolsOpener([void Function(DevToolsOpenerBuilder)? updates]) => - (new DevToolsOpenerBuilder()..update(updates))._build(); - - _$DevToolsOpener._({required this.newWindow}) : super._() { - BuiltValueNullFieldError.checkNotNull( - newWindow, - r'DevToolsOpener', - 'newWindow', - ); - } + (DevToolsOpenerBuilder()..update(updates))._build(); + _$DevToolsOpener._({required this.newWindow}) : super._(); @override DevToolsOpener rebuild(void Function(DevToolsOpenerBuilder) updates) => (toBuilder()..update(updates)).build(); @override - DevToolsOpenerBuilder toBuilder() => - new DevToolsOpenerBuilder()..replace(this); + DevToolsOpenerBuilder toBuilder() => DevToolsOpenerBuilder()..replace(this); @override bool operator ==(Object other) { @@ -412,7 +336,8 @@ class _$DevToolsOpener extends DevToolsOpener { @override String toString() { return (newBuiltValueToStringHelper(r'DevToolsOpener') - ..add('newWindow', newWindow)).toString(); + ..add('newWindow', newWindow)) + .toString(); } } @@ -437,7 +362,6 @@ class DevToolsOpenerBuilder @override void replace(DevToolsOpener other) { - ArgumentError.checkNotNull(other, 'other'); _$v = other as _$DevToolsOpener; } @@ -450,14 +374,10 @@ class DevToolsOpenerBuilder DevToolsOpener build() => _build(); _$DevToolsOpener _build() { - final _$result = - _$v ?? - new _$DevToolsOpener._( + final _$result = _$v ?? + _$DevToolsOpener._( newWindow: BuiltValueNullFieldError.checkNotNull( - newWindow, - r'DevToolsOpener', - 'newWindow', - ), + newWindow, r'DevToolsOpener', 'newWindow'), ); replace(_$result); return _$result; @@ -471,19 +391,15 @@ class _$DevToolsUrl extends DevToolsUrl { final String url; factory _$DevToolsUrl([void Function(DevToolsUrlBuilder)? updates]) => - (new DevToolsUrlBuilder()..update(updates))._build(); - - _$DevToolsUrl._({required this.tabId, required this.url}) : super._() { - BuiltValueNullFieldError.checkNotNull(tabId, r'DevToolsUrl', 'tabId'); - BuiltValueNullFieldError.checkNotNull(url, r'DevToolsUrl', 'url'); - } + (DevToolsUrlBuilder()..update(updates))._build(); + _$DevToolsUrl._({required this.tabId, required this.url}) : super._(); @override DevToolsUrl rebuild(void Function(DevToolsUrlBuilder) updates) => (toBuilder()..update(updates)).build(); @override - DevToolsUrlBuilder toBuilder() => new DevToolsUrlBuilder()..replace(this); + DevToolsUrlBuilder toBuilder() => DevToolsUrlBuilder()..replace(this); @override bool operator ==(Object other) { @@ -534,7 +450,6 @@ class DevToolsUrlBuilder implements Builder { @override void replace(DevToolsUrl other) { - ArgumentError.checkNotNull(other, 'other'); _$v = other as _$DevToolsUrl; } @@ -547,19 +462,12 @@ class DevToolsUrlBuilder implements Builder { DevToolsUrl build() => _build(); _$DevToolsUrl _build() { - final _$result = - _$v ?? - new _$DevToolsUrl._( + final _$result = _$v ?? + _$DevToolsUrl._( tabId: BuiltValueNullFieldError.checkNotNull( - tabId, - r'DevToolsUrl', - 'tabId', - ), - url: BuiltValueNullFieldError.checkNotNull( - url, - r'DevToolsUrl', - 'url', - ), + tabId, r'DevToolsUrl', 'tabId'), + url: + BuiltValueNullFieldError.checkNotNull(url, r'DevToolsUrl', 'url'), ); replace(_$result); return _$result; @@ -574,30 +482,20 @@ class _$DebugStateChange extends DebugStateChange { @override final String? reason; - factory _$DebugStateChange([ - void Function(DebugStateChangeBuilder)? updates, - ]) => (new DebugStateChangeBuilder()..update(updates))._build(); - - _$DebugStateChange._({ - required this.tabId, - required this.newState, - this.reason, - }) : super._() { - BuiltValueNullFieldError.checkNotNull(tabId, r'DebugStateChange', 'tabId'); - BuiltValueNullFieldError.checkNotNull( - newState, - r'DebugStateChange', - 'newState', - ); - } + factory _$DebugStateChange( + [void Function(DebugStateChangeBuilder)? updates]) => + (DebugStateChangeBuilder()..update(updates))._build(); + _$DebugStateChange._( + {required this.tabId, required this.newState, this.reason}) + : super._(); @override DebugStateChange rebuild(void Function(DebugStateChangeBuilder) updates) => (toBuilder()..update(updates)).build(); @override DebugStateChangeBuilder toBuilder() => - new DebugStateChangeBuilder()..replace(this); + DebugStateChangeBuilder()..replace(this); @override bool operator ==(Object other) { @@ -659,7 +557,6 @@ class DebugStateChangeBuilder @override void replace(DebugStateChange other) { - ArgumentError.checkNotNull(other, 'other'); _$v = other as _$DebugStateChange; } @@ -672,19 +569,12 @@ class DebugStateChangeBuilder DebugStateChange build() => _build(); _$DebugStateChange _build() { - final _$result = - _$v ?? - new _$DebugStateChange._( + final _$result = _$v ?? + _$DebugStateChange._( tabId: BuiltValueNullFieldError.checkNotNull( - tabId, - r'DebugStateChange', - 'tabId', - ), + tabId, r'DebugStateChange', 'tabId'), newState: BuiltValueNullFieldError.checkNotNull( - newState, - r'DebugStateChange', - 'newState', - ), + newState, r'DebugStateChange', 'newState'), reason: reason, ); replace(_$result); diff --git a/dwds/lib/dart_web_debug_service.dart b/dwds/lib/dart_web_debug_service.dart index 693bcd8ce..c20c82a7a 100644 --- a/dwds/lib/dart_web_debug_service.dart +++ b/dwds/lib/dart_web_debug_service.dart @@ -115,15 +115,20 @@ class Dwds { } } + // TODO(bkonyi): only allow for serving DevTools via DDS. + // ignore: deprecated_member_use_from_same_package final devToolsLauncher = debugSettings.devToolsLauncher; - if (devToolsLauncher != null) { + Uri? launchedDevToolsUri; + if (devToolsLauncher != null && + // If DDS is configured to serve DevTools, use its instance. + !debugSettings.ddsConfiguration.serveDevTools) { devTools = await devToolsLauncher(appMetadata.hostname); - final uri = Uri( + launchedDevToolsUri = Uri( scheme: 'http', host: devTools.hostname, port: devTools.port, ); - _logger.info('Serving DevTools at $uri\n'); + _logger.info('Serving DevTools at $launchedDevToolsUri\n'); } final injected = DwdsInjector(extensionUri: extensionUri); @@ -140,8 +145,14 @@ class Dwds { debugSettings.useSseForInjectedClient, debugSettings.expressionCompiler, injected, - debugSettings.spawnDds, - debugSettings.ddsPort, + debugSettings.ddsConfiguration.copyWith( + // ignore: deprecated_member_use_from_same_package + enable: debugSettings.spawnDds, + // ignore: deprecated_member_use_from_same_package + port: debugSettings.ddsPort, + // TODO(bkonyi): only allow for DDS to serve DevTools. + devToolsServerAddress: launchedDevToolsUri, + ), debugSettings.launchDevToolsInNewWindow, useWebSocketConnection: useDwdsWebSocketConnection, ); diff --git a/dwds/lib/dwds.dart b/dwds/lib/dwds.dart index 2d008f38f..5610ceb0e 100644 --- a/dwds/lib/dwds.dart +++ b/dwds/lib/dwds.dart @@ -7,6 +7,7 @@ export 'src/config/tool_configuration.dart' show AppMetadata, UrlEncoder, + DartDevelopmentServiceConfiguration, DevToolsLauncher, DebugSettings, ToolConfiguration; diff --git a/dwds/lib/src/config/tool_configuration.dart b/dwds/lib/src/config/tool_configuration.dart index ed65e6576..5cd6352b1 100644 --- a/dwds/lib/src/config/tool_configuration.dart +++ b/dwds/lib/src/config/tool_configuration.dart @@ -51,6 +51,35 @@ typedef UrlEncoder = Future Function(String url); typedef DevToolsLauncher = Future Function(String hostname); +class DartDevelopmentServiceConfiguration { + const DartDevelopmentServiceConfiguration({ + this.enable = true, + this.port, + this.serveDevTools = true, + this.devToolsServerAddress, + }); + + DartDevelopmentServiceConfiguration copyWith({ + bool? enable, + int? port, + bool? serveDevTools, + Uri? devToolsServerAddress, + }) { + return DartDevelopmentServiceConfiguration( + enable: enable ?? this.enable, + port: port ?? this.port, + serveDevTools: serveDevTools ?? this.serveDevTools, + devToolsServerAddress: + devToolsServerAddress ?? this.devToolsServerAddress, + ); + } + + final bool enable; + final int? port; + final bool serveDevTools; + final Uri? devToolsServerAddress; +} + /// Debug settings for the connected app. /// /// These are set by the code runner and passed to DWDS on start up. @@ -60,14 +89,22 @@ class DebugSettings { final bool useSseForDebugProxy; final bool useSseForDebugBackend; final bool useSseForInjectedClient; + + @Deprecated('Use ddsConfiguration instead.') final bool spawnDds; + @Deprecated('Use ddsConfiguration instead.') final int? ddsPort; final bool enableDevToolsLaunch; final bool launchDevToolsInNewWindow; final bool emitDebugEvents; + @Deprecated( + 'Use ddsConfigurationInstead. DevTools will eventually only be ' + 'served via DDS.', + ) final DevToolsLauncher? devToolsLauncher; final ExpressionCompiler? expressionCompiler; final UrlEncoder? urlEncoder; + final DartDevelopmentServiceConfiguration ddsConfiguration; const DebugSettings({ this.enableDebugging = true, @@ -75,13 +112,18 @@ class DebugSettings { this.useSseForDebugProxy = true, this.useSseForDebugBackend = true, this.useSseForInjectedClient = true, - this.spawnDds = true, - this.ddsPort, + @Deprecated('Use ddsConfiguration instead.') this.spawnDds = true, + @Deprecated('Use ddsConfiguration instead.') this.ddsPort, this.enableDevToolsLaunch = true, this.launchDevToolsInNewWindow = true, this.emitDebugEvents = true, + @Deprecated( + 'Use ddsConfigurationInstead. DevTools will eventually only be ' + 'served via DDS.', + ) this.devToolsLauncher, this.expressionCompiler, this.urlEncoder, + this.ddsConfiguration = const DartDevelopmentServiceConfiguration(), }); } diff --git a/dwds/lib/src/connections/debug_connection.dart b/dwds/lib/src/connections/debug_connection.dart index 06efd932c..6a84cef89 100644 --- a/dwds/lib/src/connections/debug_connection.dart +++ b/dwds/lib/src/connections/debug_connection.dart @@ -35,9 +35,12 @@ class DebugConnection { /// The endpoint of the Dart VM Service. String get uri => _appDebugServices.debugService.uri; - // The endpoint of the Dart Development Service (DDS). + /// The endpoint of the Dart Development Service (DDS). String? get ddsUri => _appDebugServices.ddsUri?.toString(); + /// The endpoint of the Dart DevTools instance. + String? get devToolsUri => _appDebugServices.devToolsUri?.toString(); + /// A client of the Dart VM Service with DWDS specific extensions. VmService get vmService => _appDebugServices.dwdsVmClient.client; diff --git a/dwds/lib/src/handlers/dev_handler.dart b/dwds/lib/src/handlers/dev_handler.dart index 25b81982a..ec669f47a 100644 --- a/dwds/lib/src/handlers/dev_handler.dart +++ b/dwds/lib/src/handlers/dev_handler.dart @@ -6,6 +6,8 @@ import 'dart:async'; import 'dart:convert'; import 'dart:io'; +import 'package:collection/collection.dart'; +import 'package:dds/dds_launcher.dart'; import 'package:dwds/data/build_result.dart'; import 'package:dwds/data/connect_request.dart'; import 'package:dwds/data/debug_event.dart'; @@ -70,8 +72,7 @@ class DevHandler { final UrlEncoder? _urlEncoder; final bool _useSseForDebugProxy; final bool _useSseForInjectedClient; - final bool _spawnDds; - final int? _ddsPort; + final DartDevelopmentServiceConfiguration _ddsConfig; final bool _launchDevToolsInNewWindow; final ExpressionCompiler? _expressionCompiler; final DwdsInjector _injected; @@ -97,8 +98,8 @@ class DevHandler { this._useSseForInjectedClient, this._expressionCompiler, this._injected, - this._spawnDds, - this._ddsPort, + + this._ddsConfig, this._launchDevToolsInNewWindow, { this.useWebSocketConnection = false, }) { @@ -251,8 +252,7 @@ class DevHandler { // This will provide a websocket based service. useSse: false, expressionCompiler: _expressionCompiler, - spawnDds: _spawnDds, - ddsPort: _ddsPort, + ddsConfig: _ddsConfig, ); } @@ -451,7 +451,7 @@ class DevHandler { AppConnection appConnection, SocketConnection sseConnection, ) async { - if (_devTools == null) { + if (_devTools == null && !_ddsConfig.serveDevTools) { sseConnection.sink.add( jsonEncode( serializers.serialize( @@ -548,6 +548,7 @@ class DevHandler { await _launchDevTools( chromeProxy.remoteDebugger, _constructDevToolsUri( + appServices, appServices.debugService.uri, ideQueryParam: 'Dwds', ), @@ -853,21 +854,21 @@ class DevHandler { ChromeDebugService debugService, ) async { final dwdsStats = DwdsStats(); - Uri? ddsUri; - if (_spawnDds) { - final dds = await debugService.startDartDevelopmentService(); - ddsUri = dds.wsUri; + DartDevelopmentServiceLauncher? dds; + if (_ddsConfig.enable) { + dds = await debugService.startDartDevelopmentService(); } final vmClient = await ChromeDwdsVmClient.create( debugService, dwdsStats, - ddsUri, + dds?.wsUri, ); final appDebugService = ChromeAppDebugServices( debugService, vmClient, dwdsStats, - ddsUri, + dds?.wsUri, + dds?.devToolsUri, ); final encodedUri = await debugService.encodedUri; _logger.info('Debug service listening on $encodedUri\n'); @@ -985,8 +986,7 @@ class DevHandler { }, useSse: _useSseForDebugProxy, expressionCompiler: _expressionCompiler, - spawnDds: _spawnDds, - ddsPort: _ddsPort, + ddsConfig: _ddsConfig, ); appServices = await _createAppDebugServices(debugService); extensionDebugger.sendEvent('dwds.debugUri', debugService.uri); @@ -1026,27 +1026,17 @@ class DevHandler { emitEvent(DwdsEvent.devtoolsLaunch()); // Send the DevTools URI to the Dart Debug Extension so that it can open it: final devToolsUri = _constructDevToolsUri( + appServices, encodedUri, ideQueryParam: 'ChromeDevTools', ); return extensionDebugger.sendEvent('dwds.devtoolsUri', devToolsUri); } - DevTools _ensureDevTools() { - final devTools = _devTools; - if (devTools == null) { - throw StateError('DevHandler: DevTools is not available'); - } - return devTools; - } - Future _launchDevTools( RemoteDebugger remoteDebugger, String devToolsUri, ) async { - // TODO(annagrin): move checking whether devtools should be started - // and the creation of the uri logic here so it is easier to follow. - _ensureDevTools(); // TODO(grouma) - We may want to log the debugServiceUri if we don't launch // DevTools so that users can manually connect. emitEvent(DwdsEvent.devtoolsLaunch()); @@ -1057,20 +1047,28 @@ class DevHandler { } String _constructDevToolsUri( - String debugServiceUri, { + AppDebugServices appDebugServices, + String serviceUri, { String ideQueryParam = '', }) { - final devTools = _ensureDevTools(); - return Uri( - scheme: 'http', - host: devTools.hostname, - port: devTools.port, - path: 'debugger', - queryParameters: { - 'uri': debugServiceUri, - if (ideQueryParam.isNotEmpty) 'ide': ideQueryParam, - }, - ).toString(); + final devToolsUri = _devTools?.uri ?? appDebugServices.devToolsUri; + if (devToolsUri == null) { + throw StateError('DevHandler: DevTools is not available'); + } + return devToolsUri + .replace( + pathSegments: [ + // Strips any trailing slashes from the original path + ...devToolsUri.pathSegments.whereNot((e) => e.isEmpty), + 'debugger', + ], + queryParameters: { + ...devToolsUri.queryParameters, + 'uri': serviceUri, + if (ideQueryParam.isNotEmpty) 'ide': ideQueryParam, + }, + ) + .toString(); } static void _maybeEmitDwdsAttachEvent(DevToolsRequest request) { diff --git a/dwds/lib/src/injected/client.js b/dwds/lib/src/injected/client.js index cb3b4b4ba..4680e9b74 100644 --- a/dwds/lib/src/injected/client.js +++ b/dwds/lib/src/injected/client.js @@ -1,4 +1,4 @@ -// Generated by dart2js (, csp, intern-composite-values), the Dart to JavaScript compiler version: 3.9.0. +// Generated by dart2js (, csp, intern-composite-values), the Dart to JavaScript compiler version: 3.10.0-142.0.dev. // The code supports the following hooks: // dartPrint(message): // if this function is defined it is called instead of the Dart [print] diff --git a/dwds/lib/src/servers/devtools.dart b/dwds/lib/src/servers/devtools.dart index 579957dff..c52a07908 100644 --- a/dwds/lib/src/servers/devtools.dart +++ b/dwds/lib/src/servers/devtools.dart @@ -10,6 +10,8 @@ class DevTools { final int port; final HttpServer _server; + Uri get uri => Uri(scheme: 'http', host: hostname, port: port); + /// Null until [close] is called. /// /// All subsequent calls to [close] will return this future. diff --git a/dwds/lib/src/services/app_debug_services.dart b/dwds/lib/src/services/app_debug_services.dart index 4d262281b..474386f8e 100644 --- a/dwds/lib/src/services/app_debug_services.dart +++ b/dwds/lib/src/services/app_debug_services.dart @@ -13,6 +13,7 @@ abstract class AppDebugServices { DwdsVmClient get dwdsVmClient; DwdsStats? get dwdsStats; Uri? get ddsUri; + Uri? get devToolsUri; String? get connectedInstanceId; set connectedInstanceId(String? id); Future close(); @@ -25,6 +26,7 @@ class ChromeAppDebugServices implements AppDebugServices { final ChromeDwdsVmClient _dwdsVmClient; final DwdsStats _dwdsStats; final Uri? _ddsUri; + final Uri? _devToolsUri; Future? _closed; String? _connectedInstanceId; @@ -33,6 +35,7 @@ class ChromeAppDebugServices implements AppDebugServices { this._dwdsVmClient, this._dwdsStats, this._ddsUri, + this._devToolsUri, ); @override @@ -47,6 +50,9 @@ class ChromeAppDebugServices implements AppDebugServices { @override Uri? get ddsUri => _ddsUri; + @override + Uri? get devToolsUri => _devToolsUri; + @override String? get connectedInstanceId => _connectedInstanceId; @@ -81,7 +87,10 @@ class WebSocketAppDebugServices implements AppDebugServices { @override DwdsStats? get dwdsStats => null; @override + // TODO(bkonyi): DDS should still start in WebSocket mode. Uri? get ddsUri => null; + @override + Uri? get devToolsUri => null; @override ProxyService get proxyService => _debugService.webSocketProxyService; diff --git a/dwds/lib/src/services/debug_service.dart b/dwds/lib/src/services/debug_service.dart index 57cb01e43..ebd84257c 100644 --- a/dwds/lib/src/services/debug_service.dart +++ b/dwds/lib/src/services/debug_service.dart @@ -168,8 +168,7 @@ class ChromeDebugService implements DebugService { final String authToken; final HttpServer _server; final bool _useSse; - final bool _spawnDds; - final int? _ddsPort; + final DartDevelopmentServiceConfiguration _ddsConfig; final UrlEncoder? _urlEncoder; DartDevelopmentServiceLauncher? _dds; @@ -186,8 +185,7 @@ class ChromeDebugService implements DebugService { this.serviceExtensionRegistry, this._server, this._useSse, - this._spawnDds, - this._ddsPort, + this._ddsConfig, this._urlEncoder, ); @@ -208,7 +206,13 @@ class ChromeDebugService implements DebugService { port: port, path: authToken, ), - serviceUri: Uri(scheme: 'http', host: hostname, port: _ddsPort ?? 0), + serviceUri: Uri( + scheme: 'http', + host: hostname, + port: _ddsConfig.port ?? 0, + ), + devToolsServerAddress: _ddsConfig.devToolsServerAddress, + serveDevTools: _ddsConfig.serveDevTools, ); return _dds!; } @@ -216,7 +220,7 @@ class ChromeDebugService implements DebugService { @override String get uri { final dds = _dds; - if (_spawnDds && dds != null) { + if (_ddsConfig.enable && dds != null) { return (_useSse ? dds.sseUri : dds.wsUri).toString(); } return (_useSse @@ -263,8 +267,7 @@ class ChromeDebugService implements DebugService { UrlEncoder? urlEncoder, { void Function(Map)? onRequest, void Function(Map)? onResponse, - bool spawnDds = true, - int? ddsPort, + required DartDevelopmentServiceConfiguration ddsConfig, bool useSse = false, ExpressionCompiler? expressionCompiler, }) async { @@ -281,7 +284,7 @@ class ChromeDebugService implements DebugService { final serviceExtensionRegistry = ServiceExtensionRegistry(); Handler handler; // DDS will always connect to DWDS via web sockets. - if (useSse && !spawnDds) { + if (useSse && !ddsConfig.enable) { final sseHandler = SseHandler( Uri.parse('/$authToken/$_kSseHandlerPath'), keepAlive: const Duration(seconds: 5), @@ -332,8 +335,7 @@ class ChromeDebugService implements DebugService { serviceExtensionRegistry, server, useSse, - spawnDds, - ddsPort, + ddsConfig, urlEncoder, ); } diff --git a/dwds/lib/src/version.dart b/dwds/lib/src/version.dart index e04de9427..5309029da 100644 --- a/dwds/lib/src/version.dart +++ b/dwds/lib/src/version.dart @@ -1,2 +1,2 @@ // Generated code. Do not modify. -const packageVersion = '25.0.4'; +const packageVersion = '25.1.0'; diff --git a/dwds/pubspec.yaml b/dwds/pubspec.yaml index 5fdc6e599..061c8e2f8 100644 --- a/dwds/pubspec.yaml +++ b/dwds/pubspec.yaml @@ -1,6 +1,6 @@ name: dwds # Every time this changes you need to run `dart run build_runner build`. -version: 25.0.4 +version: 25.1.0 description: >- A service that proxies between the Chrome debug protocol and the Dart VM @@ -15,8 +15,8 @@ dependencies: built_value: ^8.3.0 collection: ^1.15.0 crypto: ^3.0.2 - dds: '>=4.2.5 <6.0.0' - file: '>=6.1.4 <8.0.0' + dds: ">=4.2.5 <6.0.0" + file: ">=6.1.4 <8.0.0" http: ^1.0.0 http_multi_server: ^3.2.0 logging: ^1.0.2 @@ -29,15 +29,15 @@ dependencies: shelf_packages_handler: ^3.0.0 shelf_proxy: ^1.0.4 shelf_static: ^1.1.0 - shelf_web_socket: '>=2.0.0 <4.0.0' + shelf_web_socket: ">=2.0.0 <4.0.0" source_maps: ^0.10.10 stack_trace: ^1.10.0 stream_channel: ^2.1.2 sse: ^4.1.2 uuid: ^4.0.0 - vm_service: '>=14.2.4 <16.0.0' + vm_service: ">=14.2.4 <16.0.0" vm_service_interface: ^2.0.1 - web_socket_channel: '>=2.2.0 <4.0.0' + web_socket_channel: ">=2.2.0 <4.0.0" web: ^1.1.0 webkit_inspection_protocol: ^1.0.1 @@ -55,7 +55,7 @@ dev_dependencies: frontend_server_common: path: ../frontend_server_common io: ^1.0.5 - js: '>=0.6.4 <0.8.0' + js: ">=0.6.4 <0.8.0" pubspec_parse: ^1.2.0 puppeteer: ^3.1.1 test: ^1.21.6 diff --git a/dwds/test/common/hot_restart_common.dart b/dwds/test/common/hot_restart_common.dart index de546ba08..980e4e30f 100644 --- a/dwds/test/common/hot_restart_common.dart +++ b/dwds/test/common/hot_restart_common.dart @@ -119,7 +119,7 @@ void runTests({ moduleFormat: provider.ddcModuleFormat, canaryFeatures: provider.canaryFeatures, ), - debugSettings: TestDebugSettings.noDevTools().copyWith( + debugSettings: TestDebugSettings.noDevToolsLaunch().copyWith( enableDebugging: false, ), ); @@ -147,7 +147,7 @@ void runTests({ moduleFormat: provider.ddcModuleFormat, canaryFeatures: provider.canaryFeatures, ), - debugSettings: TestDebugSettings.noDevTools().copyWith( + debugSettings: TestDebugSettings.noDevToolsLaunch().copyWith( enableDebugging: false, useSse: false, ), @@ -540,7 +540,7 @@ void runTests({ moduleFormat: provider.ddcModuleFormat, canaryFeatures: provider.canaryFeatures, ), - debugSettings: TestDebugSettings.noDevTools().copyWith( + debugSettings: TestDebugSettings.noDevToolsLaunch().copyWith( enableDebugging: false, ), ); diff --git a/dwds/test/common/hot_restart_correctness_common.dart b/dwds/test/common/hot_restart_correctness_common.dart index 36ac8e942..2b1bacc2e 100644 --- a/dwds/test/common/hot_restart_correctness_common.dart +++ b/dwds/test/common/hot_restart_correctness_common.dart @@ -180,7 +180,7 @@ void runTests({ moduleFormat: provider.ddcModuleFormat, canaryFeatures: provider.canaryFeatures, ), - debugSettings: TestDebugSettings.noDevTools().copyWith( + debugSettings: TestDebugSettings.noDevToolsLaunch().copyWith( enableDebugging: false, ), ); diff --git a/dwds/test/dds_port_test.dart b/dwds/test/dds_port_test.dart index 9d977064c..5d03b85e9 100644 --- a/dwds/test/dds_port_test.dart +++ b/dwds/test/dds_port_test.dart @@ -8,6 +8,7 @@ library; import 'dart:io'; +import 'package:dwds/dwds.dart'; import 'package:test/test.dart'; import 'package:test_common/test_sdk_configuration.dart'; @@ -16,29 +17,46 @@ import 'fixtures/project.dart'; import 'fixtures/utilities.dart'; void main() { - late final TestSdkConfigurationProvider provider; + late TestSdkConfigurationProvider provider; + late TestContext context; setUp(() { provider = TestSdkConfigurationProvider(); + context = TestContext(TestProject.test, provider); }); - tearDown(() { + tearDown(() async { + await context.tearDown(); provider.dispose(); }); - test('DWDS starts DDS with a specified port', () async { - final context = TestContext(TestProject.test, provider); - + test('DWDS starts DDS with a specified port (deprecated)', () async { // Find a unused port for the test. final server = await HttpServer.bind(InternetAddress.loopbackIPv4, 0); final expectedPort = server.port; await server.close(); await context.setUp( - debugSettings: TestDebugSettings.noDevTools().copyWith( + debugSettings: TestDebugSettings.noDevToolsLaunch().copyWith( ddsPort: expectedPort, ), ); expect(Uri.parse(context.debugConnection.ddsUri!).port, expectedPort); }); + + test('DWDS starts DDS with a specified port', () async { + // Find a unused port for the test. + final server = await HttpServer.bind(InternetAddress.loopbackIPv4, 0); + final expectedPort = server.port; + await server.close(); + + await context.setUp( + debugSettings: TestDebugSettings.noDevToolsLaunch().copyWith( + ddsConfiguration: DartDevelopmentServiceConfiguration( + port: expectedPort, + ), + ), + ); + expect(Uri.parse(context.debugConnection.ddsUri!).port, expectedPort); + }); } diff --git a/dwds/test/debug_extension_test.dart b/dwds/test/debug_extension_test.dart index 21129adcf..92707c0f4 100644 --- a/dwds/test/debug_extension_test.dart +++ b/dwds/test/debug_extension_test.dart @@ -62,7 +62,7 @@ void main() async { group('Without encoding', () { setUp(() async { await context.setUp( - debugSettings: TestDebugSettings.withDevTools( + debugSettings: TestDebugSettings.withDevToolsLaunch( context, ).copyWith(enableDebugExtension: true, useSse: useSse), ); @@ -125,7 +125,7 @@ void main() async { group('With a sharded Dart app', () { setUp(() async { await context.setUp( - debugSettings: TestDebugSettings.withDevTools( + debugSettings: TestDebugSettings.withDevToolsLaunch( context, ).copyWith(enableDebugExtension: true, useSse: useSse), ); @@ -161,7 +161,7 @@ void main() async { group('With an internal Dart app', () { setUp(() async { await context.setUp( - debugSettings: TestDebugSettings.withDevTools( + debugSettings: TestDebugSettings.withDevToolsLaunch( context, ).copyWith(enableDebugExtension: true, useSse: false), ); @@ -231,7 +231,7 @@ void main() async { group('With encoding', () { setUp(() async { await context.setUp( - debugSettings: TestDebugSettings.noDevTools().copyWith( + debugSettings: TestDebugSettings.noDevToolsLaunch().copyWith( enableDebugExtension: true, urlEncoder: (url) async => @@ -263,7 +263,7 @@ void main() async { setUp(() async { await context.setUp( appMetadata: TestAppMetadata.externalApp().copyWith(hostname: 'any'), - debugSettings: TestDebugSettings.noDevTools().copyWith( + debugSettings: TestDebugSettings.noDevToolsLaunch().copyWith( enableDebugExtension: true, ), ); diff --git a/dwds/test/debug_service_test.dart b/dwds/test/debug_service_test.dart index ae9c8db8d..67089ca82 100644 --- a/dwds/test/debug_service_test.dart +++ b/dwds/test/debug_service_test.dart @@ -8,6 +8,7 @@ library; import 'dart:io'; +import 'package:dwds/dwds.dart'; import 'package:test/test.dart'; import 'package:test_common/test_sdk_configuration.dart'; import 'package:vm_service/vm_service.dart'; @@ -26,7 +27,10 @@ void main() { setUpAll(() async { // Disable DDS as we're testing DWDS behavior. await context.setUp( - debugSettings: TestDebugSettings.noDevTools().copyWith(spawnDds: false), + debugSettings: TestDebugSettings.noDevToolsLaunch().copyWith( + spawnDds: false, + ddsConfiguration: DartDevelopmentServiceConfiguration(enable: false), + ), ); }); diff --git a/dwds/test/devtools_test.dart b/dwds/test/devtools_test.dart index 94eeb70fd..81764e8ab 100644 --- a/dwds/test/devtools_test.dart +++ b/dwds/test/devtools_test.dart @@ -8,6 +8,7 @@ library; import 'dart:io'; +import 'package:dwds/src/config/tool_configuration.dart'; import 'package:test/test.dart'; import 'package:test_common/test_sdk_configuration.dart'; import 'package:vm_service/vm_service.dart'; @@ -34,117 +35,143 @@ void main() { final context = TestContext(TestProject.test, provider); - group('Injected client', () { - setUp(() async { - await context.setUp( - debugSettings: TestDebugSettings.withDevTools(context), - ); - await context.webDriver.driver.keyboard.sendChord([Keyboard.alt, 'd']); - // Wait for DevTools to actually open. - await Future.delayed(const Duration(seconds: 2)); - }); - - tearDown(() async { - await context.tearDown(); - }); - - test('can launch devtools', () async { - final windows = await context.webDriver.windows.toList(); - await context.webDriver.driver.switchTo.window(windows.last); - expect(await context.webDriver.pageSource, contains('DevTools')); - expect(await context.webDriver.currentUrl, contains('ide=Dwds')); - // TODO(https://github.com/dart-lang/webdev/issues/1888): Re-enable. - }, skip: Platform.isWindows); - - test( - 'can not launch devtools for the same app in multiple tabs', - () async { - final appUrl = await context.webDriver.currentUrl; - // Open a new tab, select it, and navigate to the app - await context.webDriver.driver.execute( - "window.open('$appUrl', '_blank');", - [], - ); - await Future.delayed(const Duration(seconds: 2)); - final newAppWindow = await context.webDriver.windows.last; - await newAppWindow.setAsActive(); + for (final serveFromDds in [true, false]) { + group( + 'Injected client with DevTools served from ${serveFromDds ? 'DDS' : 'DevTools Launcher'}', + () { + setUp(() async { + await context.setUp( + debugSettings: TestDebugSettings.withDevToolsLaunch( + context, + serveFromDds: serveFromDds, + ), + ); + await context.webDriver.driver.keyboard.sendChord([ + Keyboard.alt, + 'd', + ]); + // Wait for DevTools to actually open. + await Future.delayed(const Duration(seconds: 2)); + }); - // Wait for the page to be ready before trying to open DevTools again. - await _waitForPageReady(context); + tearDown(() async { + await context.tearDown(); + }); - // Try to open devtools and check for the alert. - await context.webDriver.driver.keyboard.sendChord([Keyboard.alt, 'd']); - await Future.delayed(const Duration(seconds: 2)); - final alert = context.webDriver.driver.switchTo.alert; - expect(alert, isNotNull); - expect( - await alert.text, - contains('This app is already being debugged in a different tab'), + test( + 'can launch devtools', + () async { + final windows = await context.webDriver.windows.toList(); + await context.webDriver.driver.switchTo.window(windows.last); + expect(await context.webDriver.pageSource, contains('DevTools')); + expect(await context.webDriver.currentUrl, contains('ide=Dwds')); + // TODO(https://github.com/dart-lang/webdev/issues/1888): Re-enable. + }, + skip: Platform.isWindows, + retry: 0, ); - await alert.accept(); - - var windows = await context.webDriver.windows.toList(); - for (final window in windows) { - if (window.id != newAppWindow.id) { - await window.setAsActive(); - await window.close(); - } - } - await newAppWindow.setAsActive(); - await context.webDriver.driver.keyboard.sendChord([Keyboard.alt, 'd']); - await Future.delayed(const Duration(seconds: 2)); - windows = await context.webDriver.windows.toList(); - final devToolsWindow = windows.firstWhere( - (window) => window != newAppWindow, + test( + 'can not launch devtools for the same app in multiple tabs', + () async { + final appUrl = await context.webDriver.currentUrl; + // Open a new tab, select it, and navigate to the app + await context.webDriver.driver.execute( + "window.open('$appUrl', '_blank');", + [], + ); + await Future.delayed(const Duration(seconds: 2)); + final newAppWindow = await context.webDriver.windows.last; + await newAppWindow.setAsActive(); + + // Wait for the page to be ready before trying to open DevTools again. + await _waitForPageReady(context); + + // Try to open devtools and check for the alert. + await context.webDriver.driver.keyboard.sendChord([ + Keyboard.alt, + 'd', + ]); + await Future.delayed(const Duration(seconds: 2)); + final alert = context.webDriver.driver.switchTo.alert; + expect(alert, isNotNull); + expect( + await alert.text, + contains('This app is already being debugged in a different tab'), + ); + await alert.accept(); + + var windows = await context.webDriver.windows.toList(); + for (final window in windows) { + if (window.id != newAppWindow.id) { + await window.setAsActive(); + await window.close(); + } + } + + await newAppWindow.setAsActive(); + await context.webDriver.driver.keyboard.sendChord([ + Keyboard.alt, + 'd', + ]); + await Future.delayed(const Duration(seconds: 2)); + windows = await context.webDriver.windows.toList(); + final devToolsWindow = windows.firstWhere( + (window) => window != newAppWindow, + ); + await devToolsWindow.setAsActive(); + expect(await context.webDriver.pageSource, contains('DevTools')); + }, + skip: 'See https://github.com/dart-lang/webdev/issues/2462', ); - await devToolsWindow.setAsActive(); - expect(await context.webDriver.pageSource, contains('DevTools')); - }, - skip: 'See https://github.com/dart-lang/webdev/issues/2462', - ); - test( - 'destroys and recreates the isolate during a page refresh', - () async { - // This test is the same as one in reload_test, but runs here when there - // is a connected client (DevTools) since it can behave differently. - // https://github.com/dart-lang/webdev/pull/901#issuecomment-586438132 - final client = context.debugConnection.vmService; - await client.streamListen('Isolate'); - await context.makeEdits([ - ( - file: context.project.dartEntryFileName, - originalString: 'Hello World!', - newString: 'Bonjour le monde!', - ), - ]); - await context.waitForSuccessfulBuild(propagateToBrowser: true); - - final eventsDone = expectLater( - client.onIsolateEvent, - emitsThrough( - emitsInOrder([ - _hasKind(EventKind.kIsolateExit), - _hasKind(EventKind.kIsolateStart), - _hasKind(EventKind.kIsolateRunnable), - ]), - ), + test( + 'destroys and recreates the isolate during a page refresh', + () async { + // This test is the same as one in reload_test, but runs here when there + // is a connected client (DevTools) since it can behave differently. + // https://github.com/dart-lang/webdev/pull/901#issuecomment-586438132 + final client = context.debugConnection.vmService; + await client.streamListen('Isolate'); + await context.makeEdits([ + ( + file: context.project.dartEntryFileName, + originalString: 'Hello World!', + newString: 'Bonjour le monde!', + ), + ]); + await context.waitForSuccessfulBuild(propagateToBrowser: true); + + final eventsDone = expectLater( + client.onIsolateEvent, + emitsThrough( + emitsInOrder([ + _hasKind(EventKind.kIsolateExit), + _hasKind(EventKind.kIsolateStart), + _hasKind(EventKind.kIsolateRunnable), + ]), + ), + ); + + await context.webDriver.driver.refresh(); + + await eventsDone; + }, + skip: 'https://github.com/dart-lang/webdev/issues/1888', ); - - await context.webDriver.driver.refresh(); - - await eventsDone; }, - skip: 'https://github.com/dart-lang/webdev/issues/1888', + timeout: Timeout.factor(2), ); - }, timeout: Timeout.factor(2)); + } group('Injected client without a DevTools server', () { setUp(() async { await context.setUp( - debugSettings: TestDebugSettings.noDevTools().copyWith( + debugSettings: TestDebugSettings.noDevToolsLaunch().copyWith( enableDevToolsLaunch: true, + ddsConfiguration: DartDevelopmentServiceConfiguration( + serveDevTools: false, + ), ), ); }); @@ -169,8 +196,11 @@ void main() { () { setUp(() async { await context.setUp( - debugSettings: TestDebugSettings.noDevTools().copyWith( + debugSettings: TestDebugSettings.noDevToolsLaunch().copyWith( enableDebugExtension: true, + ddsConfiguration: DartDevelopmentServiceConfiguration( + serveDevTools: false, + ), ), ); }); diff --git a/dwds/test/events_test.dart b/dwds/test/events_test.dart index 5e90b22bd..cb8e5434f 100644 --- a/dwds/test/events_test.dart +++ b/dwds/test/events_test.dart @@ -141,7 +141,7 @@ void main() { ); await context.setUp( testSettings: TestSettings(enableExpressionEvaluation: true), - debugSettings: TestDebugSettings.withDevTools(context), + debugSettings: TestDebugSettings.withDevToolsLaunch(context), ); keyboard = context.webDriver.driver.keyboard; events = context.testServer.dwds.events; diff --git a/dwds/test/fixtures/context.dart b/dwds/test/fixtures/context.dart index e515f277a..095f66cef 100644 --- a/dwds/test/fixtures/context.dart +++ b/dwds/test/fixtures/context.dart @@ -134,7 +134,8 @@ class TestContext { Future setUp({ TestSettings testSettings = const TestSettings(), TestAppMetadata appMetadata = const TestAppMetadata.externalApp(), - TestDebugSettings debugSettings = const TestDebugSettings.noDevTools(), + TestDebugSettings debugSettings = + const TestDebugSettings.noDevToolsLaunch(), }) async { try { // Build settings to return from load strategy. diff --git a/dwds/test/fixtures/utilities.dart b/dwds/test/fixtures/utilities.dart index 6d779c1bc..2d0c86e49 100644 --- a/dwds/test/fixtures/utilities.dart +++ b/dwds/test/fixtures/utilities.dart @@ -101,23 +101,35 @@ Future retryFnAsync( } class TestDebugSettings extends DebugSettings { - TestDebugSettings.withDevTools(TestContext context) - : super( - devToolsLauncher: (hostname) async { - final server = await DevToolsServer().serveDevTools( - hostname: hostname, - enableStdinCommands: false, - customDevToolsPath: - context.sdkConfigurationProvider.sdkLayout.devToolsDirectory, - ); - if (server == null) { - throw StateError('DevTools server could not be started.'); - } - return DevTools(server.address.host, server.port, server); - }, - ); - - const TestDebugSettings.noDevTools() : super(enableDevToolsLaunch: false); + TestDebugSettings.withDevToolsLaunch( + TestContext context, { + bool serveFromDds = false, + }) : super( + devToolsLauncher: + serveFromDds + ? null + : (hostname) async { + final server = await DevToolsServer().serveDevTools( + hostname: hostname, + enableStdinCommands: false, + customDevToolsPath: + context + .sdkConfigurationProvider + .sdkLayout + .devToolsDirectory, + ); + if (server == null) { + throw StateError('DevTools server could not be started.'); + } + return DevTools(server.address.host, server.port, server); + }, + ddsConfiguration: DartDevelopmentServiceConfiguration( + serveDevTools: serveFromDds, + ), + ); + + const TestDebugSettings.noDevToolsLaunch() + : super(enableDevToolsLaunch: false); TestDebugSettings._({ required super.enableDebugging, @@ -133,6 +145,7 @@ class TestDebugSettings extends DebugSettings { required super.devToolsLauncher, required super.expressionCompiler, required super.urlEncoder, + required super.ddsConfiguration, }); TestDebugSettings copyWith({ @@ -147,6 +160,7 @@ class TestDebugSettings extends DebugSettings { DevToolsLauncher? devToolsLauncher, ExpressionCompiler? expressionCompiler, UrlEncoder? urlEncoder, + DartDevelopmentServiceConfiguration? ddsConfiguration, }) { return TestDebugSettings._( enableDebugging: enableDebugging ?? this.enableDebugging, @@ -163,6 +177,7 @@ class TestDebugSettings extends DebugSettings { devToolsLauncher: devToolsLauncher ?? this.devToolsLauncher, expressionCompiler: expressionCompiler ?? this.expressionCompiler, urlEncoder: urlEncoder ?? this.urlEncoder, + ddsConfiguration: ddsConfiguration ?? this.ddsConfiguration, ); } } @@ -194,14 +209,14 @@ class TestToolConfiguration extends ToolConfiguration { TestToolConfiguration.withDefaultLoadStrategy({ TestAppMetadata super.appMetadata = const TestAppMetadata.externalApp(), TestDebugSettings super.debugSettings = - const TestDebugSettings.noDevTools(), + const TestDebugSettings.noDevToolsLaunch(), TestBuildSettings buildSettings = const TestBuildSettings.dart(), }) : super(loadStrategy: TestStrategy(FakeAssetReader(), buildSettings)); TestToolConfiguration.withLoadStrategy({ TestAppMetadata super.appMetadata = const TestAppMetadata.externalApp(), TestDebugSettings super.debugSettings = - const TestDebugSettings.noDevTools(), + const TestDebugSettings.noDevToolsLaunch(), required super.loadStrategy, }); } diff --git a/dwds/test/handlers/injector_test.dart b/dwds/test/handlers/injector_test.dart index c51d35ae1..1d87d166d 100644 --- a/dwds/test/handlers/injector_test.dart +++ b/dwds/test/handlers/injector_test.dart @@ -311,7 +311,9 @@ void main() { late DwdsInjector injector; setUp(() async { final toolConfiguration = TestToolConfiguration.withDefaultLoadStrategy( - debugSettings: TestDebugSettings.noDevTools().copyWith(useSse: false), + debugSettings: TestDebugSettings.noDevToolsLaunch().copyWith( + useSse: false, + ), ); setGlobalsForTesting(toolConfiguration: toolConfiguration); injector = DwdsInjector(); diff --git a/dwds/test/puppeteer/test_utils.dart b/dwds/test/puppeteer/test_utils.dart index 16a659c4c..b95013a55 100644 --- a/dwds/test/puppeteer/test_utils.dart +++ b/dwds/test/puppeteer/test_utils.dart @@ -47,10 +47,10 @@ Future setUpExtensionTest( ), debugSettings: serveDevTools - ? TestDebugSettings.withDevTools( + ? TestDebugSettings.withDevToolsLaunch( context, ).copyWith(enableDebugExtension: true, useSse: useSse) - : TestDebugSettings.noDevTools().copyWith( + : TestDebugSettings.noDevToolsLaunch().copyWith( enableDebugExtension: true, useSse: useSse, ), From 0710ab210b9fc86c50ad5c9d0724b7dad02c029c Mon Sep 17 00:00:00 2001 From: Ben Konyi Date: Thu, 28 Aug 2025 13:59:37 -0400 Subject: [PATCH 2/7] Fix failures --- dwds/pubspec.yaml | 12 ++++++------ dwds/test/fixtures/utilities.dart | 4 ++++ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/dwds/pubspec.yaml b/dwds/pubspec.yaml index 061c8e2f8..7c2f9bc0b 100644 --- a/dwds/pubspec.yaml +++ b/dwds/pubspec.yaml @@ -15,8 +15,8 @@ dependencies: built_value: ^8.3.0 collection: ^1.15.0 crypto: ^3.0.2 - dds: ">=4.2.5 <6.0.0" - file: ">=6.1.4 <8.0.0" + dds: '>=4.2.5 <6.0.0' + file: '>=6.1.4 <8.0.0' http: ^1.0.0 http_multi_server: ^3.2.0 logging: ^1.0.2 @@ -29,15 +29,15 @@ dependencies: shelf_packages_handler: ^3.0.0 shelf_proxy: ^1.0.4 shelf_static: ^1.1.0 - shelf_web_socket: ">=2.0.0 <4.0.0" + shelf_web_socket: '>=2.0.0 <4.0.0' source_maps: ^0.10.10 stack_trace: ^1.10.0 stream_channel: ^2.1.2 sse: ^4.1.2 uuid: ^4.0.0 - vm_service: ">=14.2.4 <16.0.0" + vm_service: '>=14.2.4 <16.0.0' vm_service_interface: ^2.0.1 - web_socket_channel: ">=2.2.0 <4.0.0" + web_socket_channel: '>=2.2.0 <4.0.0' web: ^1.1.0 webkit_inspection_protocol: ^1.0.1 @@ -55,7 +55,7 @@ dev_dependencies: frontend_server_common: path: ../frontend_server_common io: ^1.0.5 - js: ">=0.6.4 <0.8.0" + js: '>=0.6.4 <0.8.0' pubspec_parse: ^1.2.0 puppeteer: ^3.1.1 test: ^1.21.6 diff --git a/dwds/test/fixtures/utilities.dart b/dwds/test/fixtures/utilities.dart index 2d0c86e49..a03156ff7 100644 --- a/dwds/test/fixtures/utilities.dart +++ b/dwds/test/fixtures/utilities.dart @@ -105,6 +105,7 @@ class TestDebugSettings extends DebugSettings { TestContext context, { bool serveFromDds = false, }) : super( + // ignore: deprecated_member_use_from_same_package devToolsLauncher: serveFromDds ? null @@ -168,12 +169,15 @@ class TestDebugSettings extends DebugSettings { useSseForDebugProxy: useSse ?? useSseForDebugProxy, useSseForDebugBackend: useSse ?? useSseForDebugBackend, useSseForInjectedClient: useSse ?? useSseForInjectedClient, + // ignore: deprecated_member_use_from_same_package spawnDds: spawnDds ?? this.spawnDds, + // ignore: deprecated_member_use_from_same_package ddsPort: ddsPort ?? this.ddsPort, enableDevToolsLaunch: enableDevToolsLaunch ?? this.enableDevToolsLaunch, launchDevToolsInNewWindow: launchDevToolsInNewWindow ?? this.launchDevToolsInNewWindow, emitDebugEvents: emitDebugEvents ?? this.emitDebugEvents, + // ignore: deprecated_member_use_from_same_package devToolsLauncher: devToolsLauncher ?? this.devToolsLauncher, expressionCompiler: expressionCompiler ?? this.expressionCompiler, urlEncoder: urlEncoder ?? this.urlEncoder, From 62ffa4c12cc7d4d379f82147d1334c96e2843df7 Mon Sep 17 00:00:00 2001 From: Ben Konyi Date: Thu, 28 Aug 2025 14:04:17 -0400 Subject: [PATCH 3/7] Remove unintended changes --- .../debug_extension/tool/build_extension.dart | 5 +- .../web/data_serializers.g.dart | 32 +- dwds/debug_extension/web/data_types.g.dart | 274 ++++++++++++------ 3 files changed, 211 insertions(+), 100 deletions(-) diff --git a/dwds/debug_extension/tool/build_extension.dart b/dwds/debug_extension/tool/build_extension.dart index e3fc9eb7a..418c24567 100644 --- a/dwds/debug_extension/tool/build_extension.dart +++ b/dwds/debug_extension/tool/build_extension.dart @@ -20,8 +20,8 @@ import 'package:path/path.dart' as p; const _prodFlag = 'prod'; void main(List arguments) async { - final parser = ArgParser() - ..addFlag(_prodFlag, negatable: true, defaultsTo: false); + final parser = + ArgParser()..addFlag(_prodFlag, negatable: true, defaultsTo: false); final argResults = parser.parse(arguments); exitCode = await run(isProd: argResults[_prodFlag] as bool); @@ -41,7 +41,6 @@ Future run({required bool isProd}) async { '--output', 'build', '--release', - '--delete-conflicting-outputs' ]); final compileExitCode = await _handleProcess(compileStep); // Terminate early if compilation failed: diff --git a/dwds/debug_extension/web/data_serializers.g.dart b/dwds/debug_extension/web/data_serializers.g.dart index cd68e66d4..dd746a8d5 100644 --- a/dwds/debug_extension/web/data_serializers.g.dart +++ b/dwds/debug_extension/web/data_serializers.g.dart @@ -6,20 +6,22 @@ part of 'data_serializers.dart'; // BuiltValueGenerator // ************************************************************************** -Serializers _$serializers = (Serializers().toBuilder() - ..add(BatchedEvents.serializer) - ..add(ConnectFailure.serializer) - ..add(DebugInfo.serializer) - ..add(DebugStateChange.serializer) - ..add(DevToolsOpener.serializer) - ..add(DevToolsRequest.serializer) - ..add(DevToolsUrl.serializer) - ..add(ExtensionEvent.serializer) - ..add(ExtensionRequest.serializer) - ..add(ExtensionResponse.serializer) - ..addBuilderFactory( - const FullType(BuiltList, const [const FullType(ExtensionEvent)]), - () => ListBuilder())) - .build(); +Serializers _$serializers = + (new Serializers().toBuilder() + ..add(BatchedEvents.serializer) + ..add(ConnectFailure.serializer) + ..add(DebugInfo.serializer) + ..add(DebugStateChange.serializer) + ..add(DevToolsOpener.serializer) + ..add(DevToolsRequest.serializer) + ..add(DevToolsUrl.serializer) + ..add(ExtensionEvent.serializer) + ..add(ExtensionRequest.serializer) + ..add(ExtensionResponse.serializer) + ..addBuilderFactory( + const FullType(BuiltList, const [const FullType(ExtensionEvent)]), + () => new ListBuilder(), + )) + .build(); // ignore_for_file: deprecated_member_use_from_same_package,type=lint diff --git a/dwds/debug_extension/web/data_types.g.dart b/dwds/debug_extension/web/data_types.g.dart index 00f5a0ddb..28fd163e9 100644 --- a/dwds/debug_extension/web/data_types.g.dart +++ b/dwds/debug_extension/web/data_types.g.dart @@ -7,12 +7,12 @@ part of 'data_types.dart'; // ************************************************************************** Serializer _$connectFailureSerializer = - _$ConnectFailureSerializer(); + new _$ConnectFailureSerializer(); Serializer _$devToolsOpenerSerializer = - _$DevToolsOpenerSerializer(); -Serializer _$devToolsUrlSerializer = _$DevToolsUrlSerializer(); + new _$DevToolsOpenerSerializer(); +Serializer _$devToolsUrlSerializer = new _$DevToolsUrlSerializer(); Serializer _$debugStateChangeSerializer = - _$DebugStateChangeSerializer(); + new _$DebugStateChangeSerializer(); class _$ConnectFailureSerializer implements StructuredSerializer { @@ -22,8 +22,11 @@ class _$ConnectFailureSerializer final String wireName = 'ConnectFailure'; @override - Iterable serialize(Serializers serializers, ConnectFailure object, - {FullType specifiedType = FullType.unspecified}) { + Iterable serialize( + Serializers serializers, + ConnectFailure object, { + FullType specifiedType = FullType.unspecified, + }) { final result = [ 'tabId', serializers.serialize(object.tabId, specifiedType: const FullType(int)), @@ -33,17 +36,20 @@ class _$ConnectFailureSerializer if (value != null) { result ..add('reason') - ..add(serializers.serialize(value, - specifiedType: const FullType(String))); + ..add( + serializers.serialize(value, specifiedType: const FullType(String)), + ); } return result; } @override ConnectFailure deserialize( - Serializers serializers, Iterable serialized, - {FullType specifiedType = FullType.unspecified}) { - final result = ConnectFailureBuilder(); + Serializers serializers, + Iterable serialized, { + FullType specifiedType = FullType.unspecified, + }) { + final result = new ConnectFailureBuilder(); final iterator = serialized.iterator; while (iterator.moveNext()) { @@ -52,12 +58,20 @@ class _$ConnectFailureSerializer final Object? value = iterator.current; switch (key) { case 'tabId': - result.tabId = serializers.deserialize(value, - specifiedType: const FullType(int))! as int; + result.tabId = + serializers.deserialize( + value, + specifiedType: const FullType(int), + )! + as int; break; case 'reason': - result.reason = serializers.deserialize(value, - specifiedType: const FullType(String)) as String?; + result.reason = + serializers.deserialize( + value, + specifiedType: const FullType(String), + ) + as String?; break; } } @@ -74,12 +88,17 @@ class _$DevToolsOpenerSerializer final String wireName = 'DevToolsOpener'; @override - Iterable serialize(Serializers serializers, DevToolsOpener object, - {FullType specifiedType = FullType.unspecified}) { + Iterable serialize( + Serializers serializers, + DevToolsOpener object, { + FullType specifiedType = FullType.unspecified, + }) { final result = [ 'newWindow', - serializers.serialize(object.newWindow, - specifiedType: const FullType(bool)), + serializers.serialize( + object.newWindow, + specifiedType: const FullType(bool), + ), ]; return result; @@ -87,9 +106,11 @@ class _$DevToolsOpenerSerializer @override DevToolsOpener deserialize( - Serializers serializers, Iterable serialized, - {FullType specifiedType = FullType.unspecified}) { - final result = DevToolsOpenerBuilder(); + Serializers serializers, + Iterable serialized, { + FullType specifiedType = FullType.unspecified, + }) { + final result = new DevToolsOpenerBuilder(); final iterator = serialized.iterator; while (iterator.moveNext()) { @@ -98,8 +119,12 @@ class _$DevToolsOpenerSerializer final Object? value = iterator.current; switch (key) { case 'newWindow': - result.newWindow = serializers.deserialize(value, - specifiedType: const FullType(bool))! as bool; + result.newWindow = + serializers.deserialize( + value, + specifiedType: const FullType(bool), + )! + as bool; break; } } @@ -115,8 +140,11 @@ class _$DevToolsUrlSerializer implements StructuredSerializer { final String wireName = 'DevToolsUrl'; @override - Iterable serialize(Serializers serializers, DevToolsUrl object, - {FullType specifiedType = FullType.unspecified}) { + Iterable serialize( + Serializers serializers, + DevToolsUrl object, { + FullType specifiedType = FullType.unspecified, + }) { final result = [ 'tabId', serializers.serialize(object.tabId, specifiedType: const FullType(int)), @@ -128,9 +156,12 @@ class _$DevToolsUrlSerializer implements StructuredSerializer { } @override - DevToolsUrl deserialize(Serializers serializers, Iterable serialized, - {FullType specifiedType = FullType.unspecified}) { - final result = DevToolsUrlBuilder(); + DevToolsUrl deserialize( + Serializers serializers, + Iterable serialized, { + FullType specifiedType = FullType.unspecified, + }) { + final result = new DevToolsUrlBuilder(); final iterator = serialized.iterator; while (iterator.moveNext()) { @@ -139,12 +170,20 @@ class _$DevToolsUrlSerializer implements StructuredSerializer { final Object? value = iterator.current; switch (key) { case 'tabId': - result.tabId = serializers.deserialize(value, - specifiedType: const FullType(int))! as int; + result.tabId = + serializers.deserialize( + value, + specifiedType: const FullType(int), + )! + as int; break; case 'url': - result.url = serializers.deserialize(value, - specifiedType: const FullType(String))! as String; + result.url = + serializers.deserialize( + value, + specifiedType: const FullType(String), + )! + as String; break; } } @@ -161,31 +200,39 @@ class _$DebugStateChangeSerializer final String wireName = 'DebugStateChange'; @override - Iterable serialize(Serializers serializers, DebugStateChange object, - {FullType specifiedType = FullType.unspecified}) { + Iterable serialize( + Serializers serializers, + DebugStateChange object, { + FullType specifiedType = FullType.unspecified, + }) { final result = [ 'tabId', serializers.serialize(object.tabId, specifiedType: const FullType(int)), 'newState', - serializers.serialize(object.newState, - specifiedType: const FullType(String)), + serializers.serialize( + object.newState, + specifiedType: const FullType(String), + ), ]; Object? value; value = object.reason; if (value != null) { result ..add('reason') - ..add(serializers.serialize(value, - specifiedType: const FullType(String))); + ..add( + serializers.serialize(value, specifiedType: const FullType(String)), + ); } return result; } @override DebugStateChange deserialize( - Serializers serializers, Iterable serialized, - {FullType specifiedType = FullType.unspecified}) { - final result = DebugStateChangeBuilder(); + Serializers serializers, + Iterable serialized, { + FullType specifiedType = FullType.unspecified, + }) { + final result = new DebugStateChangeBuilder(); final iterator = serialized.iterator; while (iterator.moveNext()) { @@ -194,16 +241,28 @@ class _$DebugStateChangeSerializer final Object? value = iterator.current; switch (key) { case 'tabId': - result.tabId = serializers.deserialize(value, - specifiedType: const FullType(int))! as int; + result.tabId = + serializers.deserialize( + value, + specifiedType: const FullType(int), + )! + as int; break; case 'newState': - result.newState = serializers.deserialize(value, - specifiedType: const FullType(String))! as String; + result.newState = + serializers.deserialize( + value, + specifiedType: const FullType(String), + )! + as String; break; case 'reason': - result.reason = serializers.deserialize(value, - specifiedType: const FullType(String)) as String?; + result.reason = + serializers.deserialize( + value, + specifiedType: const FullType(String), + ) + as String?; break; } } @@ -219,15 +278,19 @@ class _$ConnectFailure extends ConnectFailure { final String? reason; factory _$ConnectFailure([void Function(ConnectFailureBuilder)? updates]) => - (ConnectFailureBuilder()..update(updates))._build(); + (new ConnectFailureBuilder()..update(updates))._build(); + + _$ConnectFailure._({required this.tabId, this.reason}) : super._() { + BuiltValueNullFieldError.checkNotNull(tabId, r'ConnectFailure', 'tabId'); + } - _$ConnectFailure._({required this.tabId, this.reason}) : super._(); @override ConnectFailure rebuild(void Function(ConnectFailureBuilder) updates) => (toBuilder()..update(updates)).build(); @override - ConnectFailureBuilder toBuilder() => ConnectFailureBuilder()..replace(this); + ConnectFailureBuilder toBuilder() => + new ConnectFailureBuilder()..replace(this); @override bool operator ==(Object other) { @@ -281,6 +344,7 @@ class ConnectFailureBuilder @override void replace(ConnectFailure other) { + ArgumentError.checkNotNull(other, 'other'); _$v = other as _$ConnectFailure; } @@ -293,10 +357,14 @@ class ConnectFailureBuilder ConnectFailure build() => _build(); _$ConnectFailure _build() { - final _$result = _$v ?? - _$ConnectFailure._( + final _$result = + _$v ?? + new _$ConnectFailure._( tabId: BuiltValueNullFieldError.checkNotNull( - tabId, r'ConnectFailure', 'tabId'), + tabId, + r'ConnectFailure', + 'tabId', + ), reason: reason, ); replace(_$result); @@ -309,15 +377,23 @@ class _$DevToolsOpener extends DevToolsOpener { final bool newWindow; factory _$DevToolsOpener([void Function(DevToolsOpenerBuilder)? updates]) => - (DevToolsOpenerBuilder()..update(updates))._build(); + (new DevToolsOpenerBuilder()..update(updates))._build(); + + _$DevToolsOpener._({required this.newWindow}) : super._() { + BuiltValueNullFieldError.checkNotNull( + newWindow, + r'DevToolsOpener', + 'newWindow', + ); + } - _$DevToolsOpener._({required this.newWindow}) : super._(); @override DevToolsOpener rebuild(void Function(DevToolsOpenerBuilder) updates) => (toBuilder()..update(updates)).build(); @override - DevToolsOpenerBuilder toBuilder() => DevToolsOpenerBuilder()..replace(this); + DevToolsOpenerBuilder toBuilder() => + new DevToolsOpenerBuilder()..replace(this); @override bool operator ==(Object other) { @@ -336,8 +412,7 @@ class _$DevToolsOpener extends DevToolsOpener { @override String toString() { return (newBuiltValueToStringHelper(r'DevToolsOpener') - ..add('newWindow', newWindow)) - .toString(); + ..add('newWindow', newWindow)).toString(); } } @@ -362,6 +437,7 @@ class DevToolsOpenerBuilder @override void replace(DevToolsOpener other) { + ArgumentError.checkNotNull(other, 'other'); _$v = other as _$DevToolsOpener; } @@ -374,10 +450,14 @@ class DevToolsOpenerBuilder DevToolsOpener build() => _build(); _$DevToolsOpener _build() { - final _$result = _$v ?? - _$DevToolsOpener._( + final _$result = + _$v ?? + new _$DevToolsOpener._( newWindow: BuiltValueNullFieldError.checkNotNull( - newWindow, r'DevToolsOpener', 'newWindow'), + newWindow, + r'DevToolsOpener', + 'newWindow', + ), ); replace(_$result); return _$result; @@ -391,15 +471,19 @@ class _$DevToolsUrl extends DevToolsUrl { final String url; factory _$DevToolsUrl([void Function(DevToolsUrlBuilder)? updates]) => - (DevToolsUrlBuilder()..update(updates))._build(); + (new DevToolsUrlBuilder()..update(updates))._build(); + + _$DevToolsUrl._({required this.tabId, required this.url}) : super._() { + BuiltValueNullFieldError.checkNotNull(tabId, r'DevToolsUrl', 'tabId'); + BuiltValueNullFieldError.checkNotNull(url, r'DevToolsUrl', 'url'); + } - _$DevToolsUrl._({required this.tabId, required this.url}) : super._(); @override DevToolsUrl rebuild(void Function(DevToolsUrlBuilder) updates) => (toBuilder()..update(updates)).build(); @override - DevToolsUrlBuilder toBuilder() => DevToolsUrlBuilder()..replace(this); + DevToolsUrlBuilder toBuilder() => new DevToolsUrlBuilder()..replace(this); @override bool operator ==(Object other) { @@ -450,6 +534,7 @@ class DevToolsUrlBuilder implements Builder { @override void replace(DevToolsUrl other) { + ArgumentError.checkNotNull(other, 'other'); _$v = other as _$DevToolsUrl; } @@ -462,12 +547,19 @@ class DevToolsUrlBuilder implements Builder { DevToolsUrl build() => _build(); _$DevToolsUrl _build() { - final _$result = _$v ?? - _$DevToolsUrl._( + final _$result = + _$v ?? + new _$DevToolsUrl._( tabId: BuiltValueNullFieldError.checkNotNull( - tabId, r'DevToolsUrl', 'tabId'), - url: - BuiltValueNullFieldError.checkNotNull(url, r'DevToolsUrl', 'url'), + tabId, + r'DevToolsUrl', + 'tabId', + ), + url: BuiltValueNullFieldError.checkNotNull( + url, + r'DevToolsUrl', + 'url', + ), ); replace(_$result); return _$result; @@ -482,20 +574,30 @@ class _$DebugStateChange extends DebugStateChange { @override final String? reason; - factory _$DebugStateChange( - [void Function(DebugStateChangeBuilder)? updates]) => - (DebugStateChangeBuilder()..update(updates))._build(); + factory _$DebugStateChange([ + void Function(DebugStateChangeBuilder)? updates, + ]) => (new DebugStateChangeBuilder()..update(updates))._build(); + + _$DebugStateChange._({ + required this.tabId, + required this.newState, + this.reason, + }) : super._() { + BuiltValueNullFieldError.checkNotNull(tabId, r'DebugStateChange', 'tabId'); + BuiltValueNullFieldError.checkNotNull( + newState, + r'DebugStateChange', + 'newState', + ); + } - _$DebugStateChange._( - {required this.tabId, required this.newState, this.reason}) - : super._(); @override DebugStateChange rebuild(void Function(DebugStateChangeBuilder) updates) => (toBuilder()..update(updates)).build(); @override DebugStateChangeBuilder toBuilder() => - DebugStateChangeBuilder()..replace(this); + new DebugStateChangeBuilder()..replace(this); @override bool operator ==(Object other) { @@ -557,6 +659,7 @@ class DebugStateChangeBuilder @override void replace(DebugStateChange other) { + ArgumentError.checkNotNull(other, 'other'); _$v = other as _$DebugStateChange; } @@ -569,12 +672,19 @@ class DebugStateChangeBuilder DebugStateChange build() => _build(); _$DebugStateChange _build() { - final _$result = _$v ?? - _$DebugStateChange._( + final _$result = + _$v ?? + new _$DebugStateChange._( tabId: BuiltValueNullFieldError.checkNotNull( - tabId, r'DebugStateChange', 'tabId'), + tabId, + r'DebugStateChange', + 'tabId', + ), newState: BuiltValueNullFieldError.checkNotNull( - newState, r'DebugStateChange', 'newState'), + newState, + r'DebugStateChange', + 'newState', + ), reason: reason, ); replace(_$result); @@ -582,4 +692,4 @@ class DebugStateChangeBuilder } } -// ignore_for_file: deprecated_member_use_from_same_package,type=lint +// ignore_for_file: deprecated_member_use_from_same_package,type=lint \ No newline at end of file From 061219dfbf788a04ff3f3af795de3ef5337fe463 Mon Sep 17 00:00:00 2001 From: Ben Konyi Date: Thu, 28 Aug 2025 14:05:12 -0400 Subject: [PATCH 4/7] Newline --- dwds/debug_extension/web/data_types.g.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dwds/debug_extension/web/data_types.g.dart b/dwds/debug_extension/web/data_types.g.dart index 28fd163e9..831d48344 100644 --- a/dwds/debug_extension/web/data_types.g.dart +++ b/dwds/debug_extension/web/data_types.g.dart @@ -692,4 +692,4 @@ class DebugStateChangeBuilder } } -// ignore_for_file: deprecated_member_use_from_same_package,type=lint \ No newline at end of file +// ignore_for_file: deprecated_member_use_from_same_package,type=lint From d9e89ade9bacfe07e6f21007c164c51b85b11714 Mon Sep 17 00:00:00 2001 From: Ben Konyi Date: Thu, 28 Aug 2025 16:46:55 -0400 Subject: [PATCH 5/7] Remove copyWith --- dwds/lib/dart_web_debug_service.dart | 13 ++++++++----- dwds/lib/src/config/tool_configuration.dart | 15 --------------- 2 files changed, 8 insertions(+), 20 deletions(-) diff --git a/dwds/lib/dart_web_debug_service.dart b/dwds/lib/dart_web_debug_service.dart index c20c82a7a..1a6780dd4 100644 --- a/dwds/lib/dart_web_debug_service.dart +++ b/dwds/lib/dart_web_debug_service.dart @@ -145,13 +145,16 @@ class Dwds { debugSettings.useSseForInjectedClient, debugSettings.expressionCompiler, injected, - debugSettings.ddsConfiguration.copyWith( + DartDevelopmentServiceConfiguration( + // This technically isn't correct, but DartDevelopmentServiceConfiguration.enable + // is true by default, so this won't break unmigrated tools. // ignore: deprecated_member_use_from_same_package - enable: debugSettings.spawnDds, + enable: debugSettings.spawnDds && debugSettings.ddsConfiguration.enable, // ignore: deprecated_member_use_from_same_package - port: debugSettings.ddsPort, - // TODO(bkonyi): only allow for DDS to serve DevTools. - devToolsServerAddress: launchedDevToolsUri, + port: debugSettings.ddsPort ?? debugSettings.ddsConfiguration.port, + devToolsServerAddress: + launchedDevToolsUri ?? + debugSettings.ddsConfiguration.devToolsServerAddress, ), debugSettings.launchDevToolsInNewWindow, useWebSocketConnection: useDwdsWebSocketConnection, diff --git a/dwds/lib/src/config/tool_configuration.dart b/dwds/lib/src/config/tool_configuration.dart index 5cd6352b1..f5552b84e 100644 --- a/dwds/lib/src/config/tool_configuration.dart +++ b/dwds/lib/src/config/tool_configuration.dart @@ -59,21 +59,6 @@ class DartDevelopmentServiceConfiguration { this.devToolsServerAddress, }); - DartDevelopmentServiceConfiguration copyWith({ - bool? enable, - int? port, - bool? serveDevTools, - Uri? devToolsServerAddress, - }) { - return DartDevelopmentServiceConfiguration( - enable: enable ?? this.enable, - port: port ?? this.port, - serveDevTools: serveDevTools ?? this.serveDevTools, - devToolsServerAddress: - devToolsServerAddress ?? this.devToolsServerAddress, - ); - } - final bool enable; final int? port; final bool serveDevTools; From d6846d47b16a5645ea6ca1c08ac4520c235fbe57 Mon Sep 17 00:00:00 2001 From: Ben Konyi Date: Thu, 28 Aug 2025 17:35:31 -0400 Subject: [PATCH 6/7] Fix tests --- dwds/test/devtools_test.dart | 25 +++++++------------------ 1 file changed, 7 insertions(+), 18 deletions(-) diff --git a/dwds/test/devtools_test.dart b/dwds/test/devtools_test.dart index 81764e8ab..0d60611dc 100644 --- a/dwds/test/devtools_test.dart +++ b/dwds/test/devtools_test.dart @@ -58,18 +58,13 @@ void main() { await context.tearDown(); }); - test( - 'can launch devtools', - () async { - final windows = await context.webDriver.windows.toList(); - await context.webDriver.driver.switchTo.window(windows.last); - expect(await context.webDriver.pageSource, contains('DevTools')); - expect(await context.webDriver.currentUrl, contains('ide=Dwds')); - // TODO(https://github.com/dart-lang/webdev/issues/1888): Re-enable. - }, - skip: Platform.isWindows, - retry: 0, - ); + test('can launch devtools', () async { + final windows = await context.webDriver.windows.toList(); + await context.webDriver.driver.switchTo.window(windows.last); + expect(await context.webDriver.pageSource, contains('DevTools')); + expect(await context.webDriver.currentUrl, contains('ide=Dwds')); + // TODO(https://github.com/dart-lang/webdev/issues/1888): Re-enable. + }, skip: Platform.isWindows); test( 'can not launch devtools for the same app in multiple tabs', @@ -169,9 +164,6 @@ void main() { await context.setUp( debugSettings: TestDebugSettings.noDevToolsLaunch().copyWith( enableDevToolsLaunch: true, - ddsConfiguration: DartDevelopmentServiceConfiguration( - serveDevTools: false, - ), ), ); }); @@ -198,9 +190,6 @@ void main() { await context.setUp( debugSettings: TestDebugSettings.noDevToolsLaunch().copyWith( enableDebugExtension: true, - ddsConfiguration: DartDevelopmentServiceConfiguration( - serveDevTools: false, - ), ), ); }); From 4192dd246c199b8fce702bf7cf5cfaff6b50273a Mon Sep 17 00:00:00 2001 From: Ben Konyi Date: Fri, 29 Aug 2025 09:50:24 -0400 Subject: [PATCH 7/7] Update devtools_test.dart --- dwds/test/devtools_test.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/dwds/test/devtools_test.dart b/dwds/test/devtools_test.dart index 0d60611dc..eafc76885 100644 --- a/dwds/test/devtools_test.dart +++ b/dwds/test/devtools_test.dart @@ -8,7 +8,6 @@ library; import 'dart:io'; -import 'package:dwds/src/config/tool_configuration.dart'; import 'package:test/test.dart'; import 'package:test_common/test_sdk_configuration.dart'; import 'package:vm_service/vm_service.dart';