Skip to content

Commit e666ea8

Browse files
[flutter_tools] cleanups to web runner functionality (flutter#61178)
Skip unnecessary parsing of chrome URI. Ensure stack traces are initialized in web server. Disclaimer on web server that it does not support debugging and remove help message. Fix generated entrypoint to check for main(List<String> args) - Fixes flutter#59643 - Fixes flutter#55084 - Fixes flutter#60417
1 parent a1097ea commit e666ea8

File tree

10 files changed

+122
-75
lines changed

10 files changed

+122
-75
lines changed

packages/flutter_tools/lib/src/build_runner/resident_web_runner.dart

+10-2
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,9 @@ abstract class ResidentWebRunner extends ResidentRunner {
189189
globals.printStatus('');
190190
globals.printStatus(message);
191191
const String quitMessage = 'To quit, press "q".';
192-
globals.printStatus('For a more detailed help message, press "h". $quitMessage');
192+
if (device.device is! WebServerDevice) {
193+
globals.printStatus('For a more detailed help message, press "h". $quitMessage');
194+
}
193195
}
194196

195197
@override
@@ -636,18 +638,24 @@ class _ResidentWebRunner extends ResidentWebRunner {
636638
'// Flutter web bootstrap script for $importedEntrypoint.',
637639
'',
638640
"import 'dart:ui' as ui;",
641+
"import 'dart:async';",
639642
'',
640643
"import '$importedEntrypoint' as entrypoint;",
641644
if (hasWebPlugins)
642645
"import 'package:flutter_web_plugins/flutter_web_plugins.dart';",
643646
if (hasWebPlugins)
644647
"import '$generatedImport';",
645648
'',
649+
'typedef _UnaryFunction = dynamic Function(List<String> args);',
650+
'typedef _NullaryFunction = dynamic Function();',
646651
'Future<void> main() async {',
647652
if (hasWebPlugins)
648653
' registerPlugins(webPluginRegistry);',
649654
' await ui.webOnlyInitializePlatform();',
650-
' entrypoint.main();',
655+
' if (entrypoint.main is _UnaryFunction) {',
656+
' return (entrypoint.main as _UnaryFunction)(<String>[]);',
657+
' }',
658+
' return (entrypoint.main as _NullaryFunction)();',
651659
'}',
652660
'',
653661
].join('\n');

packages/flutter_tools/lib/src/doctor.dart

+1-1
Original file line numberDiff line numberDiff line change
@@ -88,10 +88,10 @@ class _DefaultDoctorValidatorsProvider implements DoctorValidatorsProvider {
8888
chromiumLauncher: ChromiumLauncher(
8989
browserFinder: findChromeExecutable,
9090
fileSystem: globals.fs,
91-
logger: globals.logger,
9291
operatingSystemUtils: globals.os,
9392
platform: globals.platform,
9493
processManager: globals.processManager,
94+
logger: globals.logger,
9595
),
9696
platform: globals.platform,
9797
),

packages/flutter_tools/lib/src/test/flutter_web_platform.dart

+2-2
Original file line numberDiff line numberDiff line change
@@ -628,9 +628,9 @@ class BrowserManager {
628628
browserFinder: findChromeExecutable,
629629
fileSystem: globals.fs,
630630
operatingSystemUtils: globals.os,
631-
logger: globals.logger,
632631
platform: globals.platform,
633632
processManager: globals.processManager,
633+
logger: globals.logger,
634634
);
635635
final Chromium chrome =
636636
await chromiumLauncher.launch(url.toString(), headless: headless);
@@ -668,7 +668,7 @@ class BrowserManager {
668668
/// Loads [_BrowserEnvironment].
669669
Future<_BrowserEnvironment> _loadBrowserEnvironment() async {
670670
return _BrowserEnvironment(
671-
this, null, _browser.remoteDebuggerUri, _onRestartController.stream);
671+
this, null, _browser.chromeConnection.url, _onRestartController.stream);
672672
}
673673

674674
/// Tells the browser to load a test suite from the URL [url].

packages/flutter_tools/lib/src/web/bootstrap.dart

+13-13
Original file line numberDiff line numberDiff line change
@@ -66,19 +66,19 @@ define("main_module.bootstrap", ["$entrypoint", "dart_sdk"], function(app, dart_
6666
window.\$dartLoader.rootDirectories = [];
6767
if (window.\$requireLoader) {
6868
window.\$requireLoader.getModuleLibraries = dart_sdk.dart.getModuleLibraries;
69-
if (window.\$dartStackTraceUtility && !window.\$dartStackTraceUtility.ready) {
70-
window.\$dartStackTraceUtility.ready = true;
71-
let dart = dart_sdk.dart;
72-
window.\$dartStackTraceUtility.setSourceMapProvider(function(url) {
73-
var baseUrl = window.location.protocol + '//' + window.location.host;
74-
url = url.replace(baseUrl + '/', '');
75-
if (url == 'dart_sdk.js') {
76-
return dart.getSourceMap('dart_sdk');
77-
}
78-
url = url.replace(".lib.js", "");
79-
return dart.getSourceMap(url);
80-
});
81-
}
69+
}
70+
if (window.\$dartStackTraceUtility && !window.\$dartStackTraceUtility.ready) {
71+
window.\$dartStackTraceUtility.ready = true;
72+
let dart = dart_sdk.dart;
73+
window.\$dartStackTraceUtility.setSourceMapProvider(function(url) {
74+
var baseUrl = window.location.protocol + '//' + window.location.host;
75+
url = url.replace(baseUrl + '/', '');
76+
if (url == 'dart_sdk.js') {
77+
return dart.getSourceMap('dart_sdk');
78+
}
79+
url = url.replace(".lib.js", "");
80+
return dart.getSourceMap(url);
81+
});
8282
}
8383
});
8484
''';

packages/flutter_tools/lib/src/web/chrome.dart

+12-39
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ import '../base/logger.dart';
1515
import '../base/os.dart';
1616
import '../base/platform.dart';
1717
import '../convert.dart';
18-
import '../globals.dart' as globals;
1918

2019
/// An environment variable used to override the location of Google Chrome.
2120
const String kChromeEnvironment = 'CHROME_EXECUTABLE';
@@ -105,14 +104,14 @@ class ChromiumLauncher {
105104
@required Platform platform,
106105
@required ProcessManager processManager,
107106
@required OperatingSystemUtils operatingSystemUtils,
108-
@required Logger logger,
109107
@required BrowserFinder browserFinder,
108+
@required Logger logger,
110109
}) : _fileSystem = fileSystem,
111110
_platform = platform,
112111
_processManager = processManager,
113112
_operatingSystemUtils = operatingSystemUtils,
114-
_logger = logger,
115113
_browserFinder = browserFinder,
114+
_logger = logger,
116115
_fileSystemUtils = FileSystemUtils(
117116
fileSystem: fileSystem,
118117
platform: platform,
@@ -122,9 +121,9 @@ class ChromiumLauncher {
122121
final Platform _platform;
123122
final ProcessManager _processManager;
124123
final OperatingSystemUtils _operatingSystemUtils;
125-
Logger _logger;
126124
final BrowserFinder _browserFinder;
127125
final FileSystemUtils _fileSystemUtils;
126+
final Logger _logger;
128127

129128
bool get hasChromeInstance => _currentCompleter.isCompleted;
130129

@@ -163,7 +162,6 @@ class ChromiumLauncher {
163162
bool skipCheck = false,
164163
Directory cacheDir,
165164
}) async {
166-
_logger ??= globals.logger;
167165
if (_currentCompleter.isCompleted) {
168166
throwToolExit('Only one instance of chrome can be started.');
169167
}
@@ -207,21 +205,15 @@ class ChromiumLauncher {
207205

208206
final Process process = await _processManager.start(args);
209207

210-
// When the process exits, copy the user settings back to the provided data-dir.
211-
if (cacheDir != null) {
212-
unawaited(process.exitCode.whenComplete(() {
213-
_cacheUserSessionInformation(userDataDir, cacheDir);
214-
}));
215-
}
216-
217208
process.stdout
218209
.transform(utf8.decoder)
219210
.transform(const LineSplitter())
220211
.listen((String line) {
221212
_logger.printTrace('[CHROME]: $line');
222213
});
223214

224-
// Wait until the DevTools are listening before trying to connect.
215+
// Wait until the DevTools are listening before trying to connect. This is
216+
// only required for flutter_test --platform=chrome and not flutter run.
225217
await process.stderr
226218
.transform(utf8.decoder)
227219
.transform(const LineSplitter())
@@ -232,13 +224,18 @@ class ChromiumLauncher {
232224
.firstWhere((String line) => line.startsWith('DevTools listening'), orElse: () {
233225
return 'Failed to spawn stderr';
234226
});
235-
final Uri remoteDebuggerUri = await _getRemoteDebuggerUrl(Uri.parse('http://localhost:$port'));
227+
228+
// When the process exits, copy the user settings back to the provided data-dir.
229+
if (cacheDir != null) {
230+
unawaited(process.exitCode.whenComplete(() {
231+
_cacheUserSessionInformation(userDataDir, cacheDir);
232+
}));
233+
}
236234
return _connect(Chromium._(
237235
port,
238236
ChromeConnection('localhost', port),
239237
url: url,
240238
process: process,
241-
remoteDebuggerUri: remoteDebuggerUri,
242239
chromiumLauncher: this,
243240
), skipCheck);
244241
}
@@ -311,28 +308,6 @@ class ChromiumLauncher {
311308
}
312309

313310
Future<Chromium> get connectedInstance => _currentCompleter.future;
314-
315-
/// Returns the full URL of the Chrome remote debugger for the main page.
316-
///
317-
/// This takes the [base] remote debugger URL (which points to a browser-wide
318-
/// page) and uses its JSON API to find the resolved URL for debugging the host
319-
/// page.
320-
Future<Uri> _getRemoteDebuggerUrl(Uri base) async {
321-
try {
322-
final HttpClient client = HttpClient();
323-
final HttpClientRequest request = await client.getUrl(base.resolve('/json/list'));
324-
final HttpClientResponse response = await request.close();
325-
final List<dynamic> jsonObject = await json.fuse(utf8).decoder.bind(response).single as List<dynamic>;
326-
if (jsonObject == null || jsonObject.isEmpty) {
327-
return base;
328-
}
329-
return base.resolve(jsonObject.first['devtoolsFrontendUrl'] as String);
330-
} on Exception {
331-
// If we fail to talk to the remote debugger protocol, give up and return
332-
// the raw URL rather than crashing.
333-
return base;
334-
}
335-
}
336311
}
337312

338313
/// A class for managing an instance of a Chromium browser.
@@ -342,7 +317,6 @@ class Chromium {
342317
this.chromeConnection, {
343318
this.url,
344319
Process process,
345-
this.remoteDebuggerUri,
346320
@required ChromiumLauncher chromiumLauncher,
347321
}) : _process = process,
348322
_chromiumLauncher = chromiumLauncher;
@@ -351,7 +325,6 @@ class Chromium {
351325
final int debugPort;
352326
final Process _process;
353327
final ChromeConnection chromeConnection;
354-
final Uri remoteDebuggerUri;
355328
final ChromiumLauncher _chromiumLauncher;
356329

357330
Future<int> get onExit => _process.exitCode;

packages/flutter_tools/lib/src/web/web_device.dart

+6-2
Original file line numberDiff line numberDiff line change
@@ -305,21 +305,21 @@ class WebDevices extends PollingDeviceDiscovery {
305305
chromiumLauncher: ChromiumLauncher(
306306
browserFinder: findChromeExecutable,
307307
fileSystem: fileSystem,
308-
logger: logger,
309308
platform: platform,
310309
processManager: processManager,
311310
operatingSystemUtils: operatingSystemUtils,
311+
logger: logger,
312312
),
313313
);
314314
if (platform.isWindows) {
315315
_edgeDevice = MicrosoftEdgeDevice(
316316
chromiumLauncher: ChromiumLauncher(
317317
browserFinder: findEdgeExecutable,
318318
fileSystem: fileSystem,
319-
logger: logger,
320319
platform: platform,
321320
processManager: processManager,
322321
operatingSystemUtils: operatingSystemUtils,
322+
logger: logger,
323323
),
324324
processManager: processManager,
325325
logger: logger,
@@ -450,6 +450,10 @@ class WebServerDevice extends Device {
450450
} else {
451451
_logger.printStatus('$mainPath is being served at $url', emphasis: true);
452452
}
453+
_logger.printStatus(
454+
'The web-server device does not support debugging. Consider using '
455+
'the Chrome or Edge devices for an improved development workflow.'
456+
);
453457
_logger.sendEvent('app.webLaunchUrl', <String, dynamic>{'url': url, 'launched': false});
454458
return LaunchResult.succeeded(observatoryUri: url != null ? Uri.parse(url): null);
455459
}

packages/flutter_tools/test/general.shard/web/chrome_test.dart

+3-15
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,8 @@ void main() {
3535
Platform platform;
3636
FakeProcessManager processManager;
3737
OperatingSystemUtils operatingSystemUtils;
38-
Logger logger;
3938

4039
setUp(() {
41-
logger = BufferLogger.test();
4240
operatingSystemUtils = MockOperatingSystemUtils();
4341
when(operatingSystemUtils.findFreePort())
4442
.thenAnswer((Invocation invocation) async {
@@ -54,8 +52,8 @@ void main() {
5452
platform: platform,
5553
processManager: processManager,
5654
operatingSystemUtils: operatingSystemUtils,
57-
logger: logger,
5855
browserFinder: findChromeExecutable,
56+
logger: BufferLogger.test(),
5957
);
6058
});
6159

@@ -162,7 +160,7 @@ void main() {
162160
.childFile('preferences');
163161
preferencesFile
164162
..createSync(recursive: true)
165-
..writeAsStringSync('example');
163+
..writeAsStringSync('"exit_type":"Crashed"');
166164

167165
final Directory localStorageContentsDirectory = dataDir
168166
.childDirectory('Default')
@@ -186,18 +184,8 @@ void main() {
186184
cacheDir: dataDir,
187185
);
188186

189-
// validate preferences
190-
final File tempFile = fileSystem
191-
.directory('.tmp_rand0/flutter_tools_chrome_device.rand0')
192-
.childDirectory('Default')
193-
.childFile('preferences');
194-
195-
expect(tempFile.existsSync(), true);
196-
expect(tempFile.readAsStringSync(), 'example');
197-
198-
// write crash to file:
199-
tempFile.writeAsStringSync('"exit_type":"Crashed"');
200187
exitCompleter.complete();
188+
await Future<void>.delayed(const Duration(microseconds: 1));
201189

202190
// writes non-crash back to dart_tool
203191
expect(preferencesFile.readAsStringSync(), '"exit_type":"Normal"');

packages/flutter_tools/test/general.shard/web/web_validator_test.dart

+1-1
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,8 @@ void main() {
3434
platform: platform,
3535
processManager: processManager,
3636
operatingSystemUtils: null,
37-
logger: BufferLogger.test(),
3837
browserFinder: findChromeExecutable,
38+
logger: BufferLogger.test(),
3939
);
4040
webValidator = webValidator = ChromeValidator(
4141
platform: platform,

packages/flutter_tools/test/integration.shard/test_data/basic_project.dart

+43
Original file line numberDiff line numberDiff line change
@@ -52,3 +52,46 @@ class BasicProject extends Project {
5252
Uri get topLevelFunctionBreakpointUri => mainDart;
5353
int get topLevelFunctionBreakpointLine => lineContaining(main, '// TOP LEVEL BREAKPOINT');
5454
}
55+
56+
class BasicProjectWithUnaryMain extends Project {
57+
58+
@override
59+
final String pubspec = '''
60+
name: test
61+
environment:
62+
sdk: ">=2.0.0-dev.68.0 <3.0.0"
63+
64+
dependencies:
65+
flutter:
66+
sdk: flutter
67+
''';
68+
69+
@override
70+
final String main = r'''
71+
import 'dart:async';
72+
73+
import 'package:flutter/material.dart';
74+
75+
Future<void> main(List<String> args) async {
76+
while (true) {
77+
runApp(new MyApp());
78+
await Future.delayed(const Duration(milliseconds: 50));
79+
}
80+
}
81+
82+
class MyApp extends StatelessWidget {
83+
@override
84+
Widget build(BuildContext context) {
85+
topLevelFunction();
86+
return new MaterialApp( // BUILD BREAKPOINT
87+
title: 'Flutter Demo',
88+
home: new Container(),
89+
);
90+
}
91+
}
92+
93+
topLevelFunction() {
94+
print("topLevelFunction"); // TOP LEVEL BREAKPOINT
95+
}
96+
''';
97+
}

0 commit comments

Comments
 (0)