Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 51 additions & 33 deletions packages/devtools_app/lib/src/shared/framework/screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ enum ScreenMetaData {
'home',
iconAsset: 'icons/app_bar/devtools.png',
requiresConnection: false,
supportsWebServerDevice: true,
tutorialVideoTimestamp: '?t=0',
),
inspector(
Expand All @@ -40,6 +41,7 @@ enum ScreenMetaData {
iconAsset: 'icons/app_bar/inspector.png',
requiresFlutter: true,
requiresDebugBuild: true,
supportsWebServerDevice: true,
tutorialVideoTimestamp: '?t=172',
),
performance(
Expand Down Expand Up @@ -89,6 +91,7 @@ enum ScreenMetaData {
'logging',
title: 'Logging',
iconAsset: 'icons/app_bar/logging.png',
supportsWebServerDevice: true,
tutorialVideoTimestamp: '?t=558',
),
provider(
Expand Down Expand Up @@ -138,6 +141,7 @@ enum ScreenMetaData {
this.requiresFlutter = false,
this.requiresDebugBuild = false,
this.requiresAdvancedDeveloperMode = false,
this.supportsWebServerDevice = false,
this.worksWithOfflineData = false,
this.requiresLibrary,
this.tutorialVideoTimestamp,
Expand All @@ -155,6 +159,7 @@ enum ScreenMetaData {
final bool requiresFlutter;
final bool requiresDebugBuild;
final bool requiresAdvancedDeveloperMode;
final bool supportsWebServerDevice;
final bool worksWithOfflineData;
final String? requiresLibrary;

Expand Down Expand Up @@ -204,6 +209,7 @@ abstract class Screen {
this.requiresFlutter = false,
this.requiresDebugBuild = false,
this.requiresAdvancedDeveloperMode = false,
this.supportsWebServerDevice = false,
this.worksWithOfflineData = false,
this.showFloatingDebuggerControls = true,
}) : assert(
Expand All @@ -223,6 +229,7 @@ abstract class Screen {
bool requiresFlutter = false,
bool requiresDebugBuild = false,
bool requiresAdvancedDeveloperMode = false,
bool supportsWebServerDevice = false,
bool worksWithOfflineData = false,
bool Function(FlutterVersion? currentVersion)? shouldShowForFlutterVersion,
bool showFloatingDebuggerControls = true,
Expand All @@ -239,6 +246,7 @@ abstract class Screen {
requiresFlutter: requiresFlutter,
requiresDebugBuild: requiresDebugBuild,
requiresAdvancedDeveloperMode: requiresAdvancedDeveloperMode,
supportsWebServerDevice: supportsWebServerDevice,
worksWithOfflineData: worksWithOfflineData,
showFloatingDebuggerControls: showFloatingDebuggerControls,
title: title,
Expand All @@ -262,6 +270,7 @@ abstract class Screen {
requiresFlutter: metadata.requiresFlutter,
requiresDebugBuild: metadata.requiresDebugBuild,
requiresAdvancedDeveloperMode: metadata.requiresAdvancedDeveloperMode,
supportsWebServerDevice: metadata.supportsWebServerDevice,
worksWithOfflineData: metadata.worksWithOfflineData,
shouldShowForFlutterVersion: shouldShowForFlutterVersion,
showFloatingDebuggerControls: showFloatingDebuggerControls,
Expand Down Expand Up @@ -339,6 +348,10 @@ abstract class Screen {
/// is enabled.
final bool requiresAdvancedDeveloperMode;

/// Whether this screen should be included when the app is a web app without full debugging
/// support.
final bool supportsWebServerDevice;

/// Whether this screen works offline and should show in offline mode even if conditions are not met.
final bool worksWithOfflineData;

Expand Down Expand Up @@ -490,9 +503,11 @@ abstract class Screen {
}
}

final serviceManager = serviceConnection.serviceManager;
final connectedApp = serviceManager.connectedApp;
final serviceReady =
serviceConnection.serviceManager.isServiceAvailable &&
serviceConnection.serviceManager.connectedApp!.connectedAppInitialized;
serviceManager.isServiceAvailable &&
connectedApp!.connectedAppInitialized;
if (!serviceReady) {
if (!screen.requiresConnection) {
_log.finest('screen does not require connection: returning true');
Expand All @@ -509,42 +524,42 @@ abstract class Screen {
}
}

if (screen.requiresLibrary != null) {
if (serviceConnection.serviceManager.isolateManager.mainIsolate.value ==
null ||
!serviceConnection.serviceManager.libraryUriAvailableNow(
screen.requiresLibrary,
)) {
_log.finest(
'screen requires library ${screen.requiresLibrary}: returning false',
);
return (
show: false,
disabledReason: ScreenDisabledReason.requiresDartLibrary,
);
}
if (screen.requiresLibrary != null &&
(serviceManager.isolateManager.mainIsolate.value == null ||
!serviceManager.libraryUriAvailableNow(screen.requiresLibrary))) {
_log.finest(
'screen requires library ${screen.requiresLibrary}: returning false',
);
return (
show: false,
disabledReason: ScreenDisabledReason.requiresDartLibrary,
);
}
if (screen.requiresDartVm) {
if (serviceConnection.serviceManager.connectedApp!.isRunningOnDartVM !=
true) {
_log.finest('screen requires Dart VM: returning false');
return (show: false, disabledReason: ScreenDisabledReason.requiresDartVm);
}
if (screen.requiresDartVm && connectedApp.isRunningOnDartVM != true) {
_log.finest('screen requires Dart VM: returning false');
return (show: false, disabledReason: ScreenDisabledReason.requiresDartVm);
}
if (screen.requiresFlutter &&
serviceConnection.serviceManager.connectedApp!.isFlutterAppNow == false) {
if (screen.requiresFlutter && connectedApp.isFlutterAppNow == false) {
_log.finest('screen requires Flutter: returning false');
return (show: false, disabledReason: ScreenDisabledReason.requiresFlutter);
}
if (screen.requiresDebugBuild) {
if (serviceConnection.serviceManager.connectedApp!.isProfileBuildNow ==
true) {
_log.finest('screen requires debug build: returning false');
return (
show: false,
disabledReason: ScreenDisabledReason.requiresDebugBuild,
);
}
if (screen.requiresDebugBuild && connectedApp.isProfileBuildNow == true) {
_log.finest('screen requires debug build: returning false');
return (
show: false,
disabledReason: ScreenDisabledReason.requiresDebugBuild,
);
}
if (!screen.supportsWebServerDevice &&
connectedApp.isDartWebAppNow == true &&
!connectedApp.isDebuggableWebApp) {
_log.finest(
'screen requires a debuggable web application: returning false',
);
return (
show: false,
disabledReason: ScreenDisabledReason.requiresDebuggableWebApp,
);
}
_log.finest('${screen.screenId} screen supported: returning true');
return (show: true, disabledReason: null);
Expand Down Expand Up @@ -573,6 +588,9 @@ enum ScreenDisabledReason {
requiresAdvancedDeveloperMode(
'only works when Advanced Developer Mode is enabled',
),
requiresDebuggableWebApp(
'only works with web applications with full debugging support.',
),
serviceNotReady(
'requires a connected application, but there is no connection available.',
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ void main() {

void setupMockConnectedApp({
bool web = false,
bool debuggableWeb = true,
bool flutter = false,
bool debugMode = true,
SemanticVersion? flutterVersion,
Expand All @@ -55,6 +56,7 @@ void main() {
isFlutterApp: flutter,
isProfileBuild: !debugMode,
isWebApp: web,
isDebuggableWebApp: debuggableWeb,
);
if (flutter) {
fakeServiceConnection.serviceManager.availableLibraries.add(
Expand Down Expand Up @@ -114,6 +116,31 @@ void main() {
);
});

testWidgets('are correct for Dart Web app (DWDS websocket mode)', (
WidgetTester tester,
) async {
setupMockConnectedApp(web: true, debuggableWeb: false);

expect(
visibleScreenTypes,
equals([
HomeScreen,
// InspectorScreen,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove commented out code 😃 (here and below)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just confirming we actually want to do this, since I think these were intentionally added to make it clear which screens should be omitted. I'm happy to remove them though if you'd prefer 😄

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah didn't realize the test was already using this pattern. Fine by me to keep!

// LegacyPerformanceScreen,
// PerformanceScreen,
// ProfilerScreen,
// MemoryScreen,
// DebuggerScreen,
// NetworkScreen,
LoggingScreen,
// AppSizeScreen,
// DeepLinksScreen,
// VMDeveloperToolsScreen,
// DTDToolsScreen,
]),
);
});

testWidgets('are correct for Flutter (non-web) debug app', (
WidgetTester tester,
) async {
Expand Down Expand Up @@ -189,6 +216,31 @@ void main() {
);
});

testWidgets('are correct for Flutter web debug app (DWDS websocket mode)', (
WidgetTester tester,
) async {
setupMockConnectedApp(flutter: true, web: true, debuggableWeb: false);

expect(
visibleScreenTypes,
equals([
HomeScreen,
InspectorScreen,
// LegacyPerformanceScreen,
// PerformanceScreen,
// ProfilerScreen,
// MemoryScreen,
// DebuggerScreen,
// NetworkScreen,
LoggingScreen,
// AppSizeScreen,
// DeepLinksScreen,
// VMDeveloperToolsScreen,
// DTDToolsScreen,
]),
);
});

testWidgets('are correct for Flutter app on old Flutter version', (
WidgetTester tester,
) async {
Expand Down
28 changes: 10 additions & 18 deletions packages/devtools_app_shared/lib/src/service/connected_app.dart
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,16 @@ class ConnectedApp {

bool get isDebugFlutterAppNow => isFlutterAppNow! && !isProfileBuildNow!;

/// Returns true is the connected application's VM has the name
/// [dwdsChromeDebugProxyDeviceName], indicating that DWDS has an active
/// Chrome debugger connection with support for expression evaluation,
/// object inspection, and setting breakpoints.
///
/// If false, the connected application supports a reduced subset of the VM
/// service protocol.
bool get isDebuggableWebApp =>
serviceManager!.vm!.name == dwdsChromeDebugProxyDeviceName;

bool? get isRunningOnDartVM {
final name = serviceManager!.vm!.name;
// These are the two possible VM names returned by DWDS.
Expand Down Expand Up @@ -133,24 +143,6 @@ class ConnectedApp {
shouldLogError: false,
);
return !(value?.kind == 'Bool');

// TODO(terry): Disabled below code, it will hang if flutter run --start-paused
// see issue https://github.com/flutter/devtools/issues/2082.
// Currently, if eval (see above) doesn't work then we're
// running in Profile mode.
/*
assert(serviceConnectionManager.isServiceAvailable);
// Only flutter apps have profile and non-profile builds. If this changes in
// the future (flutter web), we can modify this check.
if (!isRunningOnDartVM || !await isFlutterApp) return false;

await serviceConnectionManager.manager.serviceExtensionManager.extensionStatesUpdated.future;

// The debugAllowBanner extension is only available in debug builds
final hasDebugExtension = serviceConnectionManager.manager.serviceExtensionManager
.isServiceExtensionAvailable(extensions.debugAllowBanner.extension);
return !hasDebugExtension;
*/
}

Future<void> initializeValues({void Function()? onComplete}) async {
Expand Down
4 changes: 4 additions & 0 deletions packages/devtools_test/lib/src/mocks/mocks.dart
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ void mockConnectedApp(
bool isFlutterApp = true,
bool isProfileBuild = false,
bool isWebApp = false,
bool isDebuggableWebApp = true,
String os = 'ios',
String flutterVersion = '2.10.0',
}) {
Expand All @@ -160,6 +161,9 @@ void mockConnectedApp(
when(
connectedApp.isFlutterNativeAppNow,
).thenReturn(isFlutterApp && !isWebApp);
when(
connectedApp.isDebuggableWebApp,
).thenReturn(isWebApp && isDebuggableWebApp);
if (isFlutterApp) {
when(connectedApp.flutterVersionNow).thenReturn(
FlutterVersion.parse({
Expand Down