Skip to content

Commit

Permalink
Add a DevTools server API to notify when a vm service connection chan…
Browse files Browse the repository at this point in the history
…ges (flutter#7291)
  • Loading branch information
kenzieschmoll authored Mar 12, 2024
1 parent 5c8553e commit 5310e08
Show file tree
Hide file tree
Showing 22 changed files with 720 additions and 27 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -273,8 +273,9 @@ class _ExtensionIFrameController extends DisposableController
}
break;
case DevToolsExtensionEventType.vmServiceConnection:
final service = serviceConnection.serviceManager.service;
updateVmServiceConnection(uri: service?.wsUri);
updateVmServiceConnection(
uri: serviceConnection.serviceManager.serviceUri,
);
break;
case DevToolsExtensionEventType.showNotification:
_handleShowNotification(event);
Expand Down
2 changes: 1 addition & 1 deletion packages/devtools_app/lib/src/framework/home_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,7 @@ class _ConnectInputState extends State<ConnectInput> with BlockingActionMixin {
await FrameworkCore.initVmService(serviceUriAsString: uri);
if (connected) {
final connectedUri =
Uri.parse(serviceConnection.serviceManager.service!.wsUri!);
Uri.parse(serviceConnection.serviceManager.serviceUri!);
routerDelegate.updateArgsIfChanged({'uri': '$connectedUri'});
final shortUri = connectedUri.replace(path: '');
notificationService.push('Successfully connected to $shortUri.');
Expand Down
17 changes: 17 additions & 0 deletions packages/devtools_app/lib/src/service/service_manager.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import '../shared/diagnostics/inspector_service.dart';
import '../shared/error_badge_manager.dart';
import '../shared/feature_flags.dart';
import '../shared/globals.dart';
import '../shared/server/server.dart' as server;
import '../shared/title.dart';
import '../shared/utils.dart';
import 'service_registrations.dart' as registrations;
Expand Down Expand Up @@ -130,6 +131,13 @@ class ServiceConnectionManager {
if (debugLogServiceProtocolEvents) {
serviceTrafficLogger = VmServiceTrafficLogger(service!);
}

unawaited(
server.notifyForVmServiceConnection(
vmServiceUri: serviceManager.serviceUri!,
connected: true,
),
);
}

void _beforeCloseVmService(VmServiceWrapper? service) {
Expand All @@ -140,6 +148,15 @@ class ServiceConnectionManager {
? OfflineConnectedApp.parse(serviceManager.connectedApp!.toJson())
: null;
offlineController.previousConnectedApp = previousConnectedApp;

// This must be called before we close the VM service so that
// [serviceManager.serviceUri] is not null.
unawaited(
server.notifyForVmServiceConnection(
vmServiceUri: serviceManager.serviceUri!,
connected: false,
),
);
}

void _afterCloseVmService(VmServiceWrapper? service) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,7 @@ class FrameworkController {
final connectionState =
serviceConnection.serviceManager.connectedState.value;
if (connectionState.connected) {
_connectedController.add(
serviceConnection.serviceManager.service!.wsUri!,
);
_connectedController.add(serviceConnection.serviceManager.serviceUri!);
} else {
_disconnectedController.add(null);
}
Expand Down
22 changes: 22 additions & 0 deletions packages/devtools_app/lib/src/shared/server/server.dart
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,26 @@ Future<DevToolsJsonFile?> requestFile({
return null;
}

Future<void> notifyForVmServiceConnection({
required String vmServiceUri,
required bool connected,
}) async {
if (isDevToolsServerAvailable) {
final uri = Uri(
path: apiNotifyForVmServiceConnection,
queryParameters: {
apiParameterValueKey: vmServiceUri,
apiParameterVmServiceConnected: connected.toString(),
},
);
final resp = await request(uri.toString());
final statusOk = resp?.statusOk ?? false;
if (!statusOk) {
logWarning(resp, apiNotifyForVmServiceConnection);
}
}
}

DevToolsJsonFile _devToolsJsonFileFromResponse(
Response resp,
String filePath,
Expand All @@ -100,4 +120,6 @@ void logWarning(Response? response, String apiType) {

extension ResponseExtension on Response {
bool get statusOk => statusCode == 200;
bool get statusForbidden => statusCode == 403;
bool get statusError => statusCode == 500;
}
2 changes: 1 addition & 1 deletion packages/devtools_app/lib/src/shared/utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ List<ConnectionDescription> generateDeviceDescription(
ConnectionDescription? vmServiceConnection;
if (includeVmServiceConnection &&
serviceConnection.serviceManager.service != null) {
final description = serviceConnection.serviceManager.service!.wsUri!;
final description = serviceConnection.serviceManager.serviceUri!;
vmServiceConnection = ConnectionDescription(
title: 'VM Service Connection',
description: description,
Expand Down
1 change: 1 addition & 0 deletions packages/devtools_app_shared/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
* Remove deprecated `background` and `onBackground` values for `lightColorScheme`
and `darkColorScheme`.
* Rename `Split` to `SplitPane`.
* Add `ServiceManager.serviceUri` field to store the connected VM service URI.

## 0.0.10
* Add `DTDManager` class and export from `service.dart`.
Expand Down
20 changes: 16 additions & 4 deletions packages/devtools_app_shared/lib/src/service/service_manager.dart
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,13 @@ class ServiceManager<T extends VmService> {
ConnectedApp? connectedApp;

T? service;

/// The URI of the most recent VM service connection [service].
///
/// We store this in a local variable so that we still have access to it when
/// the VM service closes.
String? serviceUri;

VM? vm;
String? sdkVersion;

Expand Down Expand Up @@ -216,6 +223,8 @@ class ServiceManager<T extends VmService> {
return;
}
this.service = service;
serviceUri = service.wsUri!;

if (_serviceAvailable.isCompleted) {
_serviceAvailable = Completer();
}
Expand Down Expand Up @@ -366,16 +375,19 @@ class ServiceManager<T extends VmService> {
void _closeVmServiceConnection() {
_serviceAvailable = Completer();
service = null;
serviceUri = null;
vm = null;
sdkVersion = null;
connectedApp = null;
}

Future<void> manuallyDisconnect() async {
await vmServiceClosed(
connectionState:
const ConnectedState(false, userInitiatedConnectionState: true),
);
if (hasConnection) {
await vmServiceClosed(
connectionState:
const ConnectedState(false, userInitiatedConnectionState: true),
);
}
}

Future<Response> callServiceOnMainIsolate(String name) async {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class VmServiceConnectionDisplay extends StatelessWidget {
disconnectedHint: '(e.g., http://127.0.0.1:60851/fH-kAEXc7MQ=/)',
onConnect: (value) => simController.updateVmServiceConnection(uri: value),
onDisconnect: () => simController.updateVmServiceConnection(uri: null),
currentConnection: () => serviceManager.service!.wsUri ?? '--',
currentConnection: () => serviceManager.serviceUri ?? '--',
help: const VmServiceHelp(),
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -138,8 +138,10 @@ class _DevToolsExtensionState extends State<DevToolsExtension>
void initState() {
super.initState();
_initGlobals();
extensionManager._init(
connectToVmService: widget.requiresRunningApplication,
unawaited(
extensionManager._init(
connectToVmService: widget.requiresRunningApplication,
),
);
for (final handler in widget.eventHandlers.entries) {
extensionManager.registerEventHandler(handler.key, handler.value);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ class ExtensionManager {
EventListener? _handleMessageListener;

// ignore: unused_element, false positive due to part files
void _init({required bool connectToVmService}) {
Future<void> _init({required bool connectToVmService}) async {
window.addEventListener(
'message',
_handleMessageListener = _handleMessage.toJS,
Expand All @@ -56,6 +56,11 @@ class ExtensionManager {
final themeValue = queryParams[ExtensionEventParameters.theme];
_setThemeForValue(themeValue);

final dtdUri = queryParams[_dtdQueryParameter];
if (dtdUri != null) {
await _connectToDtd(dtdUri);
}

final vmServiceUri = queryParams[_vmServiceQueryParameter];
if (connectToVmService) {
if (vmServiceUri == null) {
Expand All @@ -71,11 +76,6 @@ class ExtensionManager {
unawaited(_connectToVmService(vmServiceUri));
}
}

final dtdUri = queryParams[_dtdQueryParameter];
if (dtdUri != null) {
unawaited(_connectToDtd(dtdUri));
}
}

// ignore: unused_element, false positive due to part files
Expand Down Expand Up @@ -186,7 +186,7 @@ class ExtensionManager {
);
_updateQueryParameter(
_vmServiceQueryParameter,
serviceManager.service!.wsUri!,
serviceManager.serviceUri!,
);
} catch (e) {
final errorMessage =
Expand Down
5 changes: 5 additions & 0 deletions packages/devtools_shared/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
# 8.0.0
* **Breaking change:** rename `ServerApi.getCompleted` to `ServerApi.success` and make the
`value` parameter optional.
* **Breaking change:** remove the `String? dtdUri` parameter from `ServerApi.handle` and replace
it with a parameter `DTDConnectionInfo? dtd`.
* Introduce a new typedef `DTDConnectionInfo`.
* Add a new API `apiNotifyForVmServiceConnection` that DevTools will call when a
VM service connection is connected or disconnected from the client.
* Add a helper method `packageRootFromFileUriString`.
* Refactor yaml extension methods.

Expand Down
9 changes: 9 additions & 0 deletions packages/devtools_shared/lib/src/devtools_api.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,15 @@
/// All server APIs prefix:
const apiPrefix = 'api/';

/// Key used for any request or response to specify a value argument.
const apiParameterValueKey = 'value';

/// Notifies the DevTools server when a DevTools app client connects to a new
/// VM service.
const apiNotifyForVmServiceConnection =
'${apiPrefix}notifyForVmServiceConnection';
const apiParameterVmServiceConnected = 'connected';

/// Flutter GA properties APIs:
const apiGetFlutterGAEnabled = '${apiPrefix}getFlutterGAEnabled';
const apiGetFlutterGAClientId = '${apiPrefix}getFlutterGAClientId';
Expand Down
4 changes: 2 additions & 2 deletions packages/devtools_shared/lib/src/server/handlers/_dtd.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ part of '../server_api.dart';
abstract class _DtdApiHandler {
static shelf.Response handleGetDtdUri(
ServerApi api,
String? dtdUri,
DTDConnectionInfo? dtd,
) {
return ServerApi._encodeResponse(
{DtdApi.uriPropertyName: dtdUri},
{DtdApi.uriPropertyName: dtd?.uri},
api: api,
);
}
Expand Down
Loading

0 comments on commit 5310e08

Please sign in to comment.