[flutter_tools] use existing service implementations for web (#78995)
This commit is contained in:
parent
12880f7751
commit
430626d0f3
@ -8,3 +8,4 @@ linter:
|
||||
unawaited_futures: true
|
||||
curly_braces_in_flow_control_structures: true
|
||||
avoid_catches_without_on_clauses: true
|
||||
prefer_relative_imports: true
|
||||
|
@ -138,11 +138,6 @@ class CommandHelp {
|
||||
'debugDumpRenderTree',
|
||||
);
|
||||
|
||||
late final CommandHelpOption v = _makeOption(
|
||||
'v',
|
||||
'Launch DevTools.',
|
||||
);
|
||||
|
||||
late final CommandHelpOption w = _makeOption(
|
||||
'w',
|
||||
'Dump widget hierarchy to the console.',
|
||||
|
@ -231,14 +231,15 @@ class ResidentWebRunner extends ResidentRunner {
|
||||
|
||||
@override
|
||||
Future<bool> debugDumpApp() async {
|
||||
if (!supportsServiceProtocol) {
|
||||
if (!supportsServiceProtocol || _vmService == null) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
await _vmService
|
||||
?.flutterDebugDumpApp(
|
||||
final String data = await _vmService
|
||||
.flutterDebugDumpApp(
|
||||
isolateId: null,
|
||||
);
|
||||
_logger.printStatus(data);
|
||||
} on vmservice.RPCError {
|
||||
// do nothing.
|
||||
}
|
||||
@ -247,14 +248,15 @@ class ResidentWebRunner extends ResidentRunner {
|
||||
|
||||
@override
|
||||
Future<bool> debugDumpRenderTree() async {
|
||||
if (!supportsServiceProtocol) {
|
||||
if (!supportsServiceProtocol || _vmService == null) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
await _vmService
|
||||
?.flutterDebugDumpRenderTree(
|
||||
final String data = await _vmService
|
||||
.flutterDebugDumpRenderTree(
|
||||
isolateId: null,
|
||||
);
|
||||
_logger.printStatus(data);
|
||||
} on vmservice.RPCError {
|
||||
// do nothing.
|
||||
}
|
||||
@ -263,14 +265,15 @@ class ResidentWebRunner extends ResidentRunner {
|
||||
|
||||
@override
|
||||
Future<bool> debugDumpLayerTree() async {
|
||||
if (!supportsServiceProtocol) {
|
||||
if (!supportsServiceProtocol || _vmService == null) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
await _vmService
|
||||
?.flutterDebugDumpLayerTree(
|
||||
final String data = await _vmService
|
||||
.flutterDebugDumpLayerTree(
|
||||
isolateId: null,
|
||||
);
|
||||
_logger.printStatus(data);
|
||||
} on vmservice.RPCError {
|
||||
// do nothing.
|
||||
}
|
||||
@ -788,8 +791,6 @@ class ResidentWebRunner extends ResidentRunner {
|
||||
|
||||
_stdOutSub = _vmService.service.onStdoutEvent.listen(onLogEvent);
|
||||
_stdErrSub = _vmService.service.onStderrEvent.listen(onLogEvent);
|
||||
_extensionEventSub =
|
||||
_vmService.service.onExtensionEvent.listen(printStructuredErrorLog);
|
||||
try {
|
||||
await _vmService.service.streamListen(vmservice.EventStreams.kStdout);
|
||||
} on vmservice.RPCError {
|
||||
@ -808,18 +809,21 @@ class ResidentWebRunner extends ResidentRunner {
|
||||
// It is safe to ignore this error because we expect an error to be
|
||||
// thrown if we're not already subscribed.
|
||||
}
|
||||
try {
|
||||
await _vmService.service.streamListen(vmservice.EventStreams.kExtension);
|
||||
} on vmservice.RPCError {
|
||||
// It is safe to ignore this error because we expect an error to be
|
||||
// thrown if we're not already subscribed.
|
||||
}
|
||||
unawaited(_vmService.service.registerService('reloadSources', 'FlutterTools'));
|
||||
_vmService.service.registerServiceCallback('reloadSources', (Map<String, Object> params) async {
|
||||
final bool pause = params['pause'] as bool ?? false;
|
||||
await restart(benchmarkMode: false, pause: pause, fullRestart: false);
|
||||
return <String, Object>{'type': 'Success'};
|
||||
});
|
||||
await setUpVmService(
|
||||
(String isolateId, {
|
||||
bool force,
|
||||
bool pause,
|
||||
}) async {
|
||||
await restart(benchmarkMode: false, pause: pause, fullRestart: false);
|
||||
},
|
||||
null,
|
||||
null,
|
||||
device.device,
|
||||
null,
|
||||
printStructuredErrorLog,
|
||||
_vmService.service,
|
||||
);
|
||||
|
||||
|
||||
websocketUri = Uri.parse(_connectionResult.debugConnection.uri);
|
||||
device.vmService = _vmService;
|
||||
|
@ -1403,7 +1403,6 @@ abstract class ResidentRunner {
|
||||
if (supportsWriteSkSL) {
|
||||
commandHelp.M.print();
|
||||
}
|
||||
commandHelp.v.print();
|
||||
// `P` should precede `a`
|
||||
commandHelp.P.print();
|
||||
commandHelp.a.print();
|
||||
|
@ -10,6 +10,7 @@ import 'package:file/file.dart';
|
||||
import 'package:meta/meta.dart' show required;
|
||||
import 'package:vm_service/vm_service.dart' as vm_service;
|
||||
|
||||
import 'base/common.dart';
|
||||
import 'base/context.dart';
|
||||
import 'base/io.dart' as io;
|
||||
import 'base/logger.dart';
|
||||
@ -158,8 +159,11 @@ typedef VMServiceConnector = Future<FlutterVmService> Function(Uri httpUri, {
|
||||
Device device,
|
||||
});
|
||||
|
||||
/// A connection to the Dart VM Service.
|
||||
vm_service.VmService setUpVmService(
|
||||
/// Set up the VM Service client by attaching services for each of the provided
|
||||
/// callbacks.
|
||||
///
|
||||
/// All parameters besides [vmService] may be null.
|
||||
Future<vm_service.VmService> setUpVmService(
|
||||
ReloadSources reloadSources,
|
||||
Restart restart,
|
||||
CompileExpression compileExpression,
|
||||
@ -167,7 +171,11 @@ vm_service.VmService setUpVmService(
|
||||
GetSkSLMethod skSLMethod,
|
||||
PrintStructuredErrorLogMethod printStructuredErrorLogMethod,
|
||||
vm_service.VmService vmService
|
||||
) {
|
||||
) async {
|
||||
// Each service registration requires a request to the attached VM service. Since the
|
||||
// order of these requests does not mattter, store each future in a list and await
|
||||
// all at the end of this method.
|
||||
final List<Future<vm_service.Success>> registrationRequests = <Future<vm_service.Success>>[];
|
||||
if (reloadSources != null) {
|
||||
vmService.registerServiceCallback('reloadSources', (Map<String, dynamic> params) async {
|
||||
final String isolateId = _validateRpcStringParam('reloadSources', params, 'isolateId');
|
||||
@ -182,7 +190,7 @@ vm_service.VmService setUpVmService(
|
||||
}
|
||||
};
|
||||
});
|
||||
vmService.registerService('reloadSources', 'Flutter Tools');
|
||||
registrationRequests.add(vmService.registerService('reloadSources', 'Flutter Tools'));
|
||||
}
|
||||
|
||||
if (restart != null) {
|
||||
@ -195,7 +203,7 @@ vm_service.VmService setUpVmService(
|
||||
}
|
||||
};
|
||||
});
|
||||
vmService.registerService('hotRestart', 'Flutter Tools');
|
||||
registrationRequests.add(vmService.registerService('hotRestart', 'Flutter Tools'));
|
||||
}
|
||||
|
||||
vmService.registerServiceCallback('flutterVersion', (Map<String, dynamic> params) async {
|
||||
@ -210,7 +218,7 @@ vm_service.VmService setUpVmService(
|
||||
}
|
||||
};
|
||||
});
|
||||
vmService.registerService('flutterVersion', 'Flutter Tools');
|
||||
registrationRequests.add(vmService.registerService('flutterVersion', 'Flutter Tools'));
|
||||
|
||||
if (compileExpression != null) {
|
||||
vmService.registerServiceCallback('compileExpression', (Map<String, dynamic> params) async {
|
||||
@ -230,7 +238,7 @@ vm_service.VmService setUpVmService(
|
||||
'result': <String, dynamic>{'kernelBytes': kernelBytesBase64},
|
||||
};
|
||||
});
|
||||
vmService.registerService('compileExpression', 'Flutter Tools');
|
||||
registrationRequests.add(vmService.registerService('compileExpression', 'Flutter Tools'));
|
||||
}
|
||||
if (device != null) {
|
||||
vmService.registerServiceCallback('flutterMemoryInfo', (Map<String, dynamic> params) async {
|
||||
@ -242,7 +250,7 @@ vm_service.VmService setUpVmService(
|
||||
}
|
||||
};
|
||||
});
|
||||
vmService.registerService('flutterMemoryInfo', 'Flutter Tools');
|
||||
registrationRequests.add(vmService.registerService('flutterMemoryInfo', 'Flutter Tools'));
|
||||
}
|
||||
if (skSLMethod != null) {
|
||||
vmService.registerServiceCallback('flutterGetSkSL', (Map<String, dynamic> params) async {
|
||||
@ -254,16 +262,22 @@ vm_service.VmService setUpVmService(
|
||||
}
|
||||
};
|
||||
});
|
||||
vmService.registerService('flutterGetSkSL', 'Flutter Tools');
|
||||
registrationRequests.add(vmService.registerService('flutterGetSkSL', 'Flutter Tools'));
|
||||
}
|
||||
if (printStructuredErrorLogMethod != null) {
|
||||
try {
|
||||
vmService.streamListen(vm_service.EventStreams.kExtension);
|
||||
} on vm_service.RPCError {
|
||||
// It is safe to ignore this error because we expect an error to be
|
||||
// thrown if we're already subscribed.
|
||||
}
|
||||
vmService.onExtensionEvent.listen(printStructuredErrorLogMethod);
|
||||
// It is safe to ignore this error because we expect an error to be
|
||||
// thrown if we're already subscribed.
|
||||
registrationRequests.add(vmService
|
||||
.streamListen(vm_service.EventStreams.kExtension)
|
||||
.catchError((dynamic error) {}, test: (dynamic error) => error is vm_service.RPCError)
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
await Future.wait(registrationRequests);
|
||||
} on vm_service.RPCError catch (e) {
|
||||
throwToolExit('Failed to register service methods on attached VM Service: $e');
|
||||
}
|
||||
return vmService;
|
||||
}
|
||||
@ -319,7 +333,7 @@ Future<FlutterVmService> _connect(
|
||||
},
|
||||
);
|
||||
|
||||
final vm_service.VmService service = setUpVmService(
|
||||
final vm_service.VmService service = await setUpVmService(
|
||||
reloadSources,
|
||||
restart,
|
||||
compileExpression,
|
||||
|
@ -73,7 +73,6 @@ void _testMessageLength({
|
||||
expect(commandHelp.r.toString().length, lessThanOrEqualTo(expectedWidth));
|
||||
expect(commandHelp.s.toString().length, lessThanOrEqualTo(expectedWidth));
|
||||
expect(commandHelp.t.toString().length, lessThanOrEqualTo(expectedWidth));
|
||||
expect(commandHelp.v.toString().length, lessThanOrEqualTo(expectedWidth));
|
||||
expect(commandHelp.w.toString().length, lessThanOrEqualTo(expectedWidth));
|
||||
expect(commandHelp.z.toString().length, lessThanOrEqualTo(expectedWidth));
|
||||
}
|
||||
@ -119,7 +118,6 @@ void main() {
|
||||
expect(commandHelp.r.toString(), startsWith('\x1B[1mr\x1B[22m'));
|
||||
expect(commandHelp.s.toString(), startsWith('\x1B[1ms\x1B[22m'));
|
||||
expect(commandHelp.t.toString(), startsWith('\x1B[1mt\x1B[22m'));
|
||||
expect(commandHelp.v.toString(), startsWith('\x1B[1mv\x1B[22m'));
|
||||
expect(commandHelp.w.toString(), startsWith('\x1B[1mw\x1B[22m'));
|
||||
expect(commandHelp.z.toString(), startsWith('\x1B[1mz\x1B[22m'));
|
||||
});
|
||||
@ -196,7 +194,6 @@ void main() {
|
||||
expect(commandHelp.r.toString(), equals('\x1B[1mr\x1B[22m Hot reload. $fire$fire$fire'));
|
||||
expect(commandHelp.s.toString(), equals('\x1B[1ms\x1B[22m Save a screenshot to flutter.png.'));
|
||||
expect(commandHelp.t.toString(), equals('\x1B[1mt\x1B[22m Dump rendering tree to the console. \x1B[90m(debugDumpRenderTree)\x1B[39m\x1b[22m'));
|
||||
expect(commandHelp.v.toString(), equals('\x1B[1mv\x1B[22m Launch DevTools.'));
|
||||
expect(commandHelp.w.toString(), equals('\x1B[1mw\x1B[22m Dump widget hierarchy to the console. \x1B[90m(debugDumpApp)\x1B[39m\x1b[22m'));
|
||||
expect(commandHelp.z.toString(), equals('\x1B[1mz\x1B[22m Toggle elevation checker.'));
|
||||
});
|
||||
@ -223,7 +220,6 @@ void main() {
|
||||
expect(commandHelp.r.toString(), equals('r Hot reload. $fire$fire$fire'));
|
||||
expect(commandHelp.s.toString(), equals('s Save a screenshot to flutter.png.'));
|
||||
expect(commandHelp.t.toString(), equals('t Dump rendering tree to the console. (debugDumpRenderTree)'));
|
||||
expect(commandHelp.v.toString(), equals('v Launch DevTools.'));
|
||||
expect(commandHelp.w.toString(), equals('w Dump widget hierarchy to the console. (debugDumpApp)'));
|
||||
expect(commandHelp.z.toString(), equals('z Toggle elevation checker.'));
|
||||
});
|
||||
|
@ -1501,7 +1501,6 @@ void main() {
|
||||
commandHelp.z,
|
||||
commandHelp.g,
|
||||
commandHelp.M,
|
||||
commandHelp.v,
|
||||
commandHelp.P,
|
||||
commandHelp.a,
|
||||
'',
|
||||
|
@ -60,19 +60,33 @@ const List<VmServiceExpectation> kAttachIsolateExpectations = <VmServiceExpectat
|
||||
'streamId': 'Isolate'
|
||||
}
|
||||
),
|
||||
FakeVmServiceRequest(
|
||||
method: 'registerService',
|
||||
args: <String, Object>{
|
||||
'service': 'reloadSources',
|
||||
'alias': 'Flutter Tools',
|
||||
}
|
||||
),
|
||||
FakeVmServiceRequest(
|
||||
method: 'registerService',
|
||||
args: <String, Object>{
|
||||
'service': 'flutterVersion',
|
||||
'alias': 'Flutter Tools',
|
||||
}
|
||||
),
|
||||
FakeVmServiceRequest(
|
||||
method: 'registerService',
|
||||
args: <String, Object>{
|
||||
'service': 'flutterMemoryInfo',
|
||||
'alias': 'Flutter Tools',
|
||||
}
|
||||
),
|
||||
FakeVmServiceRequest(
|
||||
method: 'streamListen',
|
||||
args: <String, Object>{
|
||||
'streamId': 'Extension',
|
||||
},
|
||||
),
|
||||
FakeVmServiceRequest(
|
||||
method: 'registerService',
|
||||
args: <String, Object>{
|
||||
'service': 'reloadSources',
|
||||
'alias': 'FlutterTools',
|
||||
}
|
||||
)
|
||||
];
|
||||
|
||||
const List<VmServiceExpectation> kAttachExpectations = <VmServiceExpectation>[
|
||||
@ -480,11 +494,6 @@ void main() {
|
||||
connectionInfoCompleter: connectionInfoCompleter,
|
||||
));
|
||||
await connectionInfoCompleter.future;
|
||||
|
||||
// Need these to run events, otherwise expect statements below run before
|
||||
// structured errors are processed.
|
||||
await null;
|
||||
await null;
|
||||
await null;
|
||||
|
||||
expect(testLogger.statusText, contains('\nerror text'));
|
||||
|
@ -9,9 +9,8 @@ import 'dart:async';
|
||||
import 'package:flutter_tools/src/base/common.dart';
|
||||
import 'package:flutter_tools/src/base/io.dart' as io;
|
||||
import 'package:flutter_tools/src/convert.dart';
|
||||
import 'package:test/fake.dart';
|
||||
import 'package:vm_service/vm_service.dart' as vm_service;
|
||||
import 'package:mockito/mockito.dart';
|
||||
import 'package:flutter_tools/src/base/logger.dart';
|
||||
import 'package:flutter_tools/src/device.dart';
|
||||
import 'package:flutter_tools/src/version.dart';
|
||||
import 'package:flutter_tools/src/vmservice.dart';
|
||||
@ -90,11 +89,11 @@ final FakeVmServiceRequest listViewsRequest = FakeVmServiceRequest(
|
||||
typedef ServiceCallback = Future<Map<String, dynamic>> Function(Map<String, Object>);
|
||||
|
||||
void main() {
|
||||
testUsingContext('VmService registers reloadSources', () async {
|
||||
testWithoutContext('VmService registers reloadSources', () async {
|
||||
Future<void> reloadSources(String isolateId, { bool pause, bool force}) async {}
|
||||
|
||||
final MockVMService mockVMService = MockVMService();
|
||||
setUpVmService(
|
||||
await setUpVmService(
|
||||
reloadSources,
|
||||
null,
|
||||
null,
|
||||
@ -104,16 +103,14 @@ void main() {
|
||||
mockVMService,
|
||||
);
|
||||
|
||||
verify(mockVMService.registerService('reloadSources', 'Flutter Tools')).called(1);
|
||||
}, overrides: <Type, Generator>{
|
||||
Logger: () => BufferLogger.test()
|
||||
expect(mockVMService.services, containsPair('reloadSources', 'Flutter Tools'));
|
||||
});
|
||||
|
||||
testUsingContext('VmService registers flutterMemoryInfo service', () async {
|
||||
testWithoutContext('VmService registers flutterMemoryInfo service', () async {
|
||||
final FakeDevice mockDevice = FakeDevice();
|
||||
|
||||
final MockVMService mockVMService = MockVMService();
|
||||
setUpVmService(
|
||||
await setUpVmService(
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
@ -123,14 +120,12 @@ void main() {
|
||||
mockVMService,
|
||||
);
|
||||
|
||||
verify(mockVMService.registerService('flutterMemoryInfo', 'Flutter Tools')).called(1);
|
||||
}, overrides: <Type, Generator>{
|
||||
Logger: () => BufferLogger.test()
|
||||
expect(mockVMService.services, containsPair('flutterMemoryInfo', 'Flutter Tools'));
|
||||
});
|
||||
|
||||
testUsingContext('VmService registers flutterGetSkSL service', () async {
|
||||
testWithoutContext('VmService registers flutterGetSkSL service', () async {
|
||||
final MockVMService mockVMService = MockVMService();
|
||||
setUpVmService(
|
||||
await setUpVmService(
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
@ -140,17 +135,42 @@ void main() {
|
||||
mockVMService,
|
||||
);
|
||||
|
||||
verify(mockVMService.registerService('flutterGetSkSL', 'Flutter Tools')).called(1);
|
||||
}, overrides: <Type, Generator>{
|
||||
Logger: () => BufferLogger.test()
|
||||
expect(mockVMService.services, containsPair('flutterGetSkSL', 'Flutter Tools'));
|
||||
});
|
||||
|
||||
testUsingContext('VmService registers flutterPrintStructuredErrorLogMethod', () async {
|
||||
testWithoutContext('VmService throws tool exit on service registration failure.', () async {
|
||||
final MockVMService mockVMService = MockVMService()
|
||||
..errorOnRegisterService = true;
|
||||
|
||||
await expectLater(() async => setUpVmService(
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
() async => 'hello',
|
||||
null,
|
||||
mockVMService,
|
||||
), throwsToolExit());
|
||||
});
|
||||
|
||||
testWithoutContext('VmService throws tool exit on service registration failure with awaited future.', () async {
|
||||
final MockVMService mockVMService = MockVMService()
|
||||
..errorOnRegisterService = true;
|
||||
|
||||
await expectLater(() async => setUpVmService(
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
() async => 'hello',
|
||||
(vm_service.Event event) { },
|
||||
mockVMService,
|
||||
), throwsToolExit());
|
||||
});
|
||||
|
||||
testWithoutContext('VmService registers flutterPrintStructuredErrorLogMethod', () async {
|
||||
final MockVMService mockVMService = MockVMService();
|
||||
when(mockVMService.onExtensionEvent).thenAnswer((Invocation invocation) {
|
||||
return const Stream<vm_service.Event>.empty();
|
||||
});
|
||||
setUpVmService(
|
||||
await setUpVmService(
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
@ -159,14 +179,12 @@ void main() {
|
||||
(vm_service.Event event) async => 'hello',
|
||||
mockVMService,
|
||||
);
|
||||
verify(mockVMService.streamListen(vm_service.EventStreams.kExtension)).called(1);
|
||||
}, overrides: <Type, Generator>{
|
||||
Logger: () => BufferLogger.test()
|
||||
expect(mockVMService.listenedStreams, contains(vm_service.EventStreams.kExtension));
|
||||
});
|
||||
|
||||
testUsingContext('VMService returns correct FlutterVersion', () async {
|
||||
testWithoutContext('VMService returns correct FlutterVersion', () async {
|
||||
final MockVMService mockVMService = MockVMService();
|
||||
setUpVmService(
|
||||
await setUpVmService(
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
@ -176,9 +194,7 @@ void main() {
|
||||
mockVMService,
|
||||
);
|
||||
|
||||
verify(mockVMService.registerService('flutterVersion', 'Flutter Tools')).called(1);
|
||||
}, overrides: <Type, Generator>{
|
||||
FlutterVersion: () => FakeFlutterVersion(),
|
||||
expect(mockVMService.services, containsPair('flutterVersion', 'Flutter Tools'));
|
||||
});
|
||||
|
||||
testUsingContext('VMService prints messages for connection failures', () {
|
||||
@ -629,8 +645,35 @@ void main() {
|
||||
});
|
||||
}
|
||||
|
||||
class MockVMService extends Mock implements vm_service.VmService {}
|
||||
class MockVMService extends Fake implements vm_service.VmService {
|
||||
final Map<String, String> services = <String, String>{};
|
||||
final Set<String> listenedStreams = <String>{};
|
||||
bool errorOnRegisterService = false;
|
||||
|
||||
@override
|
||||
void registerServiceCallback(String service, vm_service.ServiceCallback cb) {}
|
||||
|
||||
@override
|
||||
Future<vm_service.Success> registerService(String service, String alias) async {
|
||||
services[service] = alias;
|
||||
if (errorOnRegisterService) {
|
||||
throw vm_service.RPCError('registerService', 1234, 'error');
|
||||
}
|
||||
return vm_service.Success();
|
||||
}
|
||||
|
||||
@override
|
||||
Stream<vm_service.Event> get onExtensionEvent => const Stream<vm_service.Event>.empty();
|
||||
|
||||
@override
|
||||
Future<vm_service.Success> streamListen(String streamId) async {
|
||||
listenedStreams.add(streamId);
|
||||
return vm_service.Success();
|
||||
}
|
||||
}
|
||||
|
||||
class FakeDevice extends Fake implements Device {}
|
||||
|
||||
class FakeFlutterVersion extends Fake implements FlutterVersion {
|
||||
@override
|
||||
Map<String, Object> toJson() => const <String, Object>{'Fake': 'Version'};
|
||||
|
@ -572,7 +572,6 @@ void main() {
|
||||
'z Toggle elevation checker.',
|
||||
'g Run source code generators.',
|
||||
'M Write SkSL shaders to a unique file in the project directory.',
|
||||
'v Launch DevTools.', // TODO(ianh): hide this when devtools isn't available
|
||||
'P Toggle performance overlay. (WidgetsApp.showPerformanceOverlay)',
|
||||
'a Toggle timeline events for all widget build methods. (debugProfileWidgetBuilds)',
|
||||
'',
|
||||
|
Loading…
x
Reference in New Issue
Block a user