diff --git a/packages/flutter_tools/doc/daemon.md b/packages/flutter_tools/doc/daemon.md index b2627512a60..0e63815c7d3 100644 --- a/packages/flutter_tools/doc/daemon.md +++ b/packages/flutter_tools/doc/daemon.md @@ -60,7 +60,7 @@ The `version()` command responds with a String with the protocol version. The `shutdown()` command will terminate the flutter daemon. It is not necessary to call this before shutting down the daemon; it is perfectly acceptable to just kill the daemon process. -### daemon.getSupportedPlatforms +#### daemon.getSupportedPlatforms The `getSupportedPlatforms()` command will enumerate all platforms supported by the project located at the provided `projectRoot`. It returns a Map with the key 'platforms' containing a List of strings which describe the set of all possibly supported platforms. Possible values include: - android @@ -153,6 +153,10 @@ This is sent when an operation starts and again when it stops. When an operation This is sent when an app is stopped or detached from. The `params` field will be a map with the field `appId`. +#### app.webLaunchUrl + +This is sent once a web application is being served and available for the user to access. The `params` field will be a map with a string `url` field and a boolean `launched` indicating whether the application has already been launched in a browser (this will generally be true for a browser device unless `--no-web-browser-launch` was used, and false for the headless `web-server` device). + ### device domain #### device.getDevices diff --git a/packages/flutter_tools/lib/src/base/logger.dart b/packages/flutter_tools/lib/src/base/logger.dart index 25daa42da19..ee0782d2cb7 100644 --- a/packages/flutter_tools/lib/src/base/logger.dart +++ b/packages/flutter_tools/lib/src/base/logger.dart @@ -147,13 +147,11 @@ abstract class Logger { int progressIndicatorPadding = kDefaultStatusPadding, }); - /// Send a progress notification that is instant. + /// Send an event to be emitted. /// /// Only surfaces a value in machine modes, Loggers may ignore this message in - /// non-machine modes. Like [startProgress] but with a single event. - void sendNotification(String message, { - String progressId, - }); + /// non-machine modes. + void sendEvent(String name, [Map args]) { } } class StdoutLogger extends Logger { @@ -260,7 +258,7 @@ class StdoutLogger extends Logger { } @override - void sendNotification(String message, {String progressId}) { } + void sendEvent(String name, [Map args]) { } } /// A [StdoutLogger] which replaces Unicode characters that cannot be printed to @@ -354,7 +352,7 @@ class BufferLogger extends Logger { } @override - void sendNotification(String message, {String progressId}) { } + void sendEvent(String name, [Map args]) { } } class VerboseLogger extends Logger { @@ -469,7 +467,7 @@ class VerboseLogger extends Logger { } @override - void sendNotification(String message, {String progressId}) { } + void sendEvent(String name, [Map args]) { } } enum _LogType { error, status, trace } diff --git a/packages/flutter_tools/lib/src/commands/daemon.dart b/packages/flutter_tools/lib/src/commands/daemon.dart index fed2bd0b096..95203b0554a 100644 --- a/packages/flutter_tools/lib/src/commands/daemon.dart +++ b/packages/flutter_tools/lib/src/commands/daemon.dart @@ -900,7 +900,7 @@ class NotifyingLogger extends Logger { } @override - void sendNotification(String message, {String progressId}) { } + void sendEvent(String name, [Map args]) { } } /// A running application, started by this daemon. @@ -1113,13 +1113,12 @@ class _AppRunLogger extends Logger { } @override - void sendNotification(String message, {String progressId}) { - final int id = _nextProgressId++; - _sendProgressEvent({ - 'id': id.toString(), - 'progressId': progressId, - 'finished': true, - }); + void sendEvent(String name, [Map args]) { + if (domain == null) { + printStatus('event sent after app closed: $name'); + } else { + domain.sendEvent(name, args); + } } } diff --git a/packages/flutter_tools/lib/src/web/web_device.dart b/packages/flutter_tools/lib/src/web/web_device.dart index 501aadb4a6f..fb44abfd8d3 100644 --- a/packages/flutter_tools/lib/src/web/web_device.dart +++ b/packages/flutter_tools/lib/src/web/web_device.dart @@ -136,9 +136,10 @@ class ChromeDevice extends Device { dataDir: fs.currentDirectory .childDirectory('.dart_tool') .childDirectory('chrome-device')); + logger.sendEvent('app.webLaunchUrl', {'url': url, 'launched': true}); } else { printStatus('Waiting for connection from Dart debug extension at $url', emphasis: true); - logger.sendNotification(url, progressId: 'debugExtension'); + logger.sendEvent('app.webLaunchUrl', {'url': url, 'launched': false}); } return LaunchResult.succeeded(observatoryUri: null); } @@ -250,7 +251,7 @@ class WebServerDevice extends Device { }) async { final String url = platformArgs['uri']; printStatus('$mainPath is being served at $url', emphasis: true); - logger.sendNotification(url, progressId: 'debugExtension'); + logger.sendEvent('app.webLaunchUrl', {'url': url, 'launched': false}); return LaunchResult.succeeded(observatoryUri: null); } diff --git a/packages/flutter_tools/test/commands.shard/hermetic/attach_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/attach_test.dart index b4fb892c522..7a99e5b8ecc 100644 --- a/packages/flutter_tools/test/commands.shard/hermetic/attach_test.dart +++ b/packages/flutter_tools/test/commands.shard/hermetic/attach_test.dart @@ -554,7 +554,7 @@ class StreamLogger extends Logger { Stream get stream => _controller.stream; @override - void sendNotification(String message, {String progressId}) { } + void sendEvent(String name, [Map args]) { } } class LoggerInterrupted implements Exception { diff --git a/packages/flutter_tools/test/general.shard/resident_web_runner_test.dart b/packages/flutter_tools/test/general.shard/resident_web_runner_test.dart index 32f713c61a5..0b03316148f 100644 --- a/packages/flutter_tools/test/general.shard/resident_web_runner_test.dart +++ b/packages/flutter_tools/test/general.shard/resident_web_runner_test.dart @@ -105,6 +105,7 @@ void main() { when(mockWebFs.recompile()).thenAnswer((Invocation _) { return Future.value(false); }); + when(mockWebFs.uri).thenReturn('http://localhost:8765/app/'); when(mockDebugConnection.vmService).thenReturn(mockVmService); when(mockDebugConnection.onDone).thenAnswer((Invocation invocation) { return Completer().future; @@ -729,6 +730,75 @@ void main() { expect(bufferLogger.statusText, contains('Launching ${fs.path.join('lib', 'main.dart')} on Chromez in debug mode')); })); + test('Sends launched app.webLaunchUrl event for Chrome device', () => testbed.run(() async { + _setupMocks(); + when(mockFlutterDevice.device).thenReturn(ChromeDevice()); + + final DelegateLogger delegateLogger = logger; + final MockStatus mockStatus = MockStatus(); + delegateLogger.status = mockStatus; + final ResidentWebRunner runner = DwdsWebRunnerFactory().createWebRunner( + mockFlutterDevice, + flutterProject: FlutterProject.current(), + debuggingOptions: DebuggingOptions.enabled(BuildInfo.debug), + ipv6: true, + stayResident: true, + dartDefines: const [], + ); + + final Completer connectionInfoCompleter = Completer(); + unawaited(runner.run( + connectionInfoCompleter: connectionInfoCompleter, + )); + await connectionInfoCompleter.future; + + // Ensure we got the URL and that it was already launched. + verify(logger.sendEvent( + 'app.webLaunchUrl', + argThat(allOf( + containsPair('url', 'http://localhost:8765/app/'), + containsPair('launched', true), + )) + )); + }, overrides: { + Logger: () => DelegateLogger(MockLogger()), + ChromeLauncher: () => MockChromeLauncher(), + })); + + test('Sends unlaunched app.webLaunchUrl event for Web Server device', () => testbed.run(() async { + _setupMocks(); + when(mockFlutterDevice.device).thenReturn(WebServerDevice()); + + final DelegateLogger delegateLogger = logger; + final MockStatus mockStatus = MockStatus(); + delegateLogger.status = mockStatus; + final ResidentWebRunner runner = DwdsWebRunnerFactory().createWebRunner( + mockFlutterDevice, + flutterProject: FlutterProject.current(), + debuggingOptions: DebuggingOptions.enabled(BuildInfo.debug), + ipv6: true, + stayResident: true, + dartDefines: const [], + ); + + final Completer connectionInfoCompleter = Completer(); + unawaited(runner.run( + connectionInfoCompleter: connectionInfoCompleter, + )); + await connectionInfoCompleter.future; + + // Ensure we got the URL and that it was not already launched. + verify(logger.sendEvent( + 'app.webLaunchUrl', + argThat(allOf( + containsPair('url', 'http://localhost:8765/app/'), + containsPair('launched', false), + )) + )); + }, overrides: { + Logger: () => DelegateLogger(MockLogger()) + })); + test('Successfully turns WebSocketException into ToolExit', () => testbed.run(() async { _setupMocks(); final Completer connectionInfoCompleter = Completer(); @@ -908,6 +978,7 @@ void main() { })); } +class MockChromeLauncher extends Mock implements ChromeLauncher {} class MockFlutterUsage extends Mock implements Usage {} class MockChromeDevice extends Mock implements ChromeDevice {} class MockBuildDaemonCreator extends Mock implements BuildDaemonCreator {} @@ -924,3 +995,4 @@ class MockChromeConnection extends Mock implements ChromeConnection {} class MockChromeTab extends Mock implements ChromeTab {} class MockWipConnection extends Mock implements WipConnection {} class MockWipDebugger extends Mock implements WipDebugger {} +class MockLogger extends Mock implements Logger {} diff --git a/packages/flutter_tools/test/src/testbed.dart b/packages/flutter_tools/test/src/testbed.dart index 9d684b54302..978b79068d1 100644 --- a/packages/flutter_tools/test/src/testbed.dart +++ b/packages/flutter_tools/test/src/testbed.dart @@ -801,8 +801,8 @@ class DelegateLogger implements Logger { } @override - void sendNotification(String message, {String progressId}) { - delegate.sendNotification(message, progressId: progressId); + void sendEvent(String name, [Map args]) { + delegate.sendEvent(name, args); } @override