diff --git a/dev/devicelab/bin/tasks/linux_chrome_dev_mode.dart b/dev/devicelab/bin/tasks/linux_chrome_dev_mode.dart index d732a267f85..a11b0ab96e6 100644 --- a/dev/devicelab/bin/tasks/linux_chrome_dev_mode.dart +++ b/dev/devicelab/bin/tasks/linux_chrome_dev_mode.dart @@ -6,5 +6,5 @@ import 'package:flutter_devicelab/framework/framework.dart'; import 'package:flutter_devicelab/tasks/web_dev_mode_tests.dart'; Future main() async { - await task(createWebDevModeTest(WebDevice.webServer, false)); + await task(createWebDevModeTest()); } diff --git a/dev/devicelab/bin/tasks/macos_chrome_dev_mode.dart b/dev/devicelab/bin/tasks/macos_chrome_dev_mode.dart index d732a267f85..a11b0ab96e6 100644 --- a/dev/devicelab/bin/tasks/macos_chrome_dev_mode.dart +++ b/dev/devicelab/bin/tasks/macos_chrome_dev_mode.dart @@ -6,5 +6,5 @@ import 'package:flutter_devicelab/framework/framework.dart'; import 'package:flutter_devicelab/tasks/web_dev_mode_tests.dart'; Future main() async { - await task(createWebDevModeTest(WebDevice.webServer, false)); + await task(createWebDevModeTest()); } diff --git a/dev/devicelab/bin/tasks/web_incremental_test.dart b/dev/devicelab/bin/tasks/web_incremental_test.dart deleted file mode 100644 index 8e6724c3923..00000000000 --- a/dev/devicelab/bin/tasks/web_incremental_test.dart +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright 2019 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'package:flutter_devicelab/framework/framework.dart'; -import 'package:flutter_devicelab/tasks/web_dev_mode_tests.dart'; - -Future main() async { - await task(createWebDevModeTest(WebDevice.chrome, true)); -} diff --git a/dev/devicelab/bin/tasks/windows_chrome_dev_mode.dart b/dev/devicelab/bin/tasks/windows_chrome_dev_mode.dart index d732a267f85..a11b0ab96e6 100644 --- a/dev/devicelab/bin/tasks/windows_chrome_dev_mode.dart +++ b/dev/devicelab/bin/tasks/windows_chrome_dev_mode.dart @@ -6,5 +6,5 @@ import 'package:flutter_devicelab/framework/framework.dart'; import 'package:flutter_devicelab/tasks/web_dev_mode_tests.dart'; Future main() async { - await task(createWebDevModeTest(WebDevice.webServer, false)); + await task(createWebDevModeTest()); } diff --git a/dev/devicelab/lib/tasks/web_dev_mode_tests.dart b/dev/devicelab/lib/tasks/web_dev_mode_tests.dart index ecd29cbf1ce..6408b8586fc 100644 --- a/dev/devicelab/lib/tasks/web_dev_mode_tests.dart +++ b/dev/devicelab/lib/tasks/web_dev_mode_tests.dart @@ -20,21 +20,12 @@ const String kFirstRecompileTime = 'FirstRecompileTime'; const String kSecondStartupTime = 'SecondStartupTime'; const String kSecondRestartTime = 'SecondRestartTime'; - -abstract class WebDevice { - static const String chrome = 'chrome'; - static const String webServer = 'web-server'; -} - -TaskFunction createWebDevModeTest(String webDevice, bool enableIncrementalCompiler) { +TaskFunction createWebDevModeTest() { return () async { final List options = [ - '--hot', '-d', webDevice, '--verbose', '--resident', '--target=lib/main.dart', + '--hot', '-d', 'web-server', '--verbose', '--resident', '--target=lib/main.dart', ]; int hotRestartCount = 0; - final String expectedMessage = webDevice == WebDevice.webServer - ? 'Recompile complete' - : 'Reloaded application'; final Map measurements = {}; await inDirectory(flutterDirectory, () async { rmTree(_editedFlutterGalleryDir); @@ -47,8 +38,6 @@ TaskFunction createWebDevModeTest(String webDevice, bool enableIncrementalCompil ['packages', 'get'], environment: { 'FLUTTER_WEB': 'true', - if (enableIncrementalCompiler) - 'WEB_INCREMENTAL_COMPILER': 'true', }, ); await packagesGet.exitCode; @@ -57,26 +46,16 @@ TaskFunction createWebDevModeTest(String webDevice, bool enableIncrementalCompil flutterCommandArgs('run', options), environment: { 'FLUTTER_WEB': 'true', - if (enableIncrementalCompiler) - 'WEB_INCREMENTAL_COMPILER': 'true', }, ); final Completer stdoutDone = Completer(); final Completer stderrDone = Completer(); final Stopwatch sw = Stopwatch()..start(); - bool restarted = false; process.stdout .transform(utf8.decoder) .transform(const LineSplitter()) .listen((String line) { - // TODO(jonahwilliams): non-dwds builds do not know when the browser is loaded. - if (line.contains('Ignoring terminal input')) { - Future.delayed(const Duration(seconds: 1)).then((void _) { - process.stdin.write(restarted ? 'q' : 'r'); - }); - return; - } if (line.contains('To hot restart')) { // measure clean start-up time. sw.stop(); @@ -84,10 +63,9 @@ TaskFunction createWebDevModeTest(String webDevice, bool enableIncrementalCompil sw ..reset() ..start(); - process.stdin.write('r'); - return; + process.stdin.write('R'); } - if (line.contains(expectedMessage)) { + if (line.contains('Recompile complete')) { if (hotRestartCount == 0) { measurements[kFirstRestartTime] = sw.elapsedMilliseconds; // Update the file and reload again. @@ -102,10 +80,9 @@ TaskFunction createWebDevModeTest(String webDevice, bool enableIncrementalCompil sw ..reset() ..start(); - process.stdin.writeln('r'); + process.stdin.writeln('R'); ++hotRestartCount; } else { - restarted = true; measurements[kFirstRecompileTime] = sw.elapsedMilliseconds; // Quit after second hot restart. process.stdin.writeln('q'); @@ -142,35 +119,24 @@ TaskFunction createWebDevModeTest(String webDevice, bool enableIncrementalCompil flutterCommandArgs('run', options), environment: { 'FLUTTER_WEB': 'true', - if (enableIncrementalCompiler) - 'WEB_INCREMENTAL_COMPILER': 'true', }, ); final Completer stdoutDone = Completer(); final Completer stderrDone = Completer(); - bool restarted = false; process.stdout .transform(utf8.decoder) .transform(const LineSplitter()) .listen((String line) { - // TODO(jonahwilliams): non-dwds builds do not know when the browser is loaded. - if (line.contains('Ignoring terminal input')) { - Future.delayed(const Duration(seconds: 1)).then((void _) { - process.stdin.write(restarted ? 'q' : 'r'); - }); - return; - } + if (line.contains('To hot restart')) { measurements[kSecondStartupTime] = sw.elapsedMilliseconds; sw ..reset() ..start(); - process.stdin.write('r'); - return; + process.stdin.write('R'); } - if (line.contains(expectedMessage)) { - restarted = true; - measurements[kSecondRestartTime] = sw.elapsedMilliseconds; + if (line.contains('Recompile complete')) { + measurements[kSecondRestartTime] = sw.elapsedMilliseconds; process.stdin.writeln('q'); } print('stdout: $line'); diff --git a/dev/devicelab/manifest.yaml b/dev/devicelab/manifest.yaml index 7be503b0567..3aefda71bda 100644 --- a/dev/devicelab/manifest.yaml +++ b/dev/devicelab/manifest.yaml @@ -112,13 +112,6 @@ tasks: stage: devicelab required_agent_capabilities: ["linux/android"] - web_incremental_test: - description: > - Verify that the experimental frontend server support is functional. - stage: devicelab - required_agent_capabilities: ["linux/android"] - flaky: true - flutter_gallery_ios__compile: description: > Collects various performance metrics of compiling the Flutter diff --git a/packages/flutter_tools/lib/src/artifacts.dart b/packages/flutter_tools/lib/src/artifacts.dart index c27a067a183..ba0e235598c 100644 --- a/packages/flutter_tools/lib/src/artifacts.dart +++ b/packages/flutter_tools/lib/src/artifacts.dart @@ -28,7 +28,7 @@ enum Artifact { platformLibrariesJson, flutterPatchedSdkPath, frontendServerSnapshotForEngineDartSdk, - /// The root directory of the dart SDK. + /// The root directory of the dartk SDK. engineDartSdkPath, /// The dart binary used to execute any of the required snapshots. engineDartBinary, diff --git a/packages/flutter_tools/lib/src/build_runner/resident_web_runner.dart b/packages/flutter_tools/lib/src/build_runner/resident_web_runner.dart index 7b4dc3b5a76..b4016d52862 100644 --- a/packages/flutter_tools/lib/src/build_runner/resident_web_runner.dart +++ b/packages/flutter_tools/lib/src/build_runner/resident_web_runner.dart @@ -8,8 +8,7 @@ import 'package:build_daemon/client.dart'; import 'package:dwds/dwds.dart'; import 'package:meta/meta.dart'; import 'package:vm_service/vm_service.dart' as vmservice; -import 'package:webkit_inspection_protocol/webkit_inspection_protocol.dart' - hide StackTrace; +import 'package:webkit_inspection_protocol/webkit_inspection_protocol.dart' hide StackTrace; import '../application_package.dart'; import '../base/async_guard.dart'; @@ -17,21 +16,16 @@ import '../base/common.dart'; import '../base/file_system.dart'; import '../base/io.dart'; import '../base/logger.dart'; -import '../base/os.dart'; import '../base/terminal.dart'; import '../base/utils.dart'; import '../build_info.dart'; import '../convert.dart'; -import '../devfs.dart'; import '../device.dart'; -import '../features.dart'; import '../globals.dart'; import '../project.dart'; import '../reporting/reporting.dart'; import '../resident_runner.dart'; -import '../run_hot.dart'; import '../web/chrome.dart'; -import '../web/devfs_web.dart'; import '../web/web_device.dart'; import '../web/web_runner.dart'; import 'web_fs.dart'; @@ -40,24 +34,14 @@ import 'web_fs.dart'; class DwdsWebRunnerFactory extends WebRunnerFactory { @override ResidentRunner createWebRunner( - FlutterDevice device, { + Device device, { String target, @required bool stayResident, @required FlutterProject flutterProject, @required bool ipv6, @required DebuggingOptions debuggingOptions, }) { - if (featureFlags.isWebIncrementalCompilerEnabled) { - return _ExperimentalResidentWebRunner( - device, - target: target, - flutterProject: flutterProject, - debuggingOptions: debuggingOptions, - ipv6: ipv6, - stayResident: stayResident, - ); - } - return _DwdsResidentWebRunner( + return ResidentWebRunner( device, target: target, flutterProject: flutterProject, @@ -69,9 +53,8 @@ class DwdsWebRunnerFactory extends WebRunnerFactory { } /// A hot-runner which handles browser specific delegation. -abstract class ResidentWebRunner extends ResidentRunner { - ResidentWebRunner( - this.device, { +class ResidentWebRunner extends ResidentRunner { + ResidentWebRunner(this.device, { String target, @required this.flutterProject, @required bool ipv6, @@ -85,27 +68,22 @@ abstract class ResidentWebRunner extends ResidentRunner { stayResident: stayResident, ); - final FlutterDevice device; + final Device device; final FlutterProject flutterProject; - DateTime firstBuildTime; // Only the debug builds of the web support the service protocol. @override - bool get supportsServiceProtocol => - isRunningDebug && device.device is! WebServerDevice; + bool get supportsServiceProtocol => isRunningDebug && device is! WebServerDevice; @override - bool get debuggingEnabled => - isRunningDebug && device.device is! WebServerDevice; + bool get debuggingEnabled => isRunningDebug && device is! WebServerDevice; WebFs _webFs; ConnectionResult _connectionResult; StreamSubscription _stdOutSub; bool _exited = false; - WipConnection _wipConnection; - vmservice.VmService get _vmService => - _connectionResult?.debugConnection?.vmService; + vmservice.VmService get _vmService => _connectionResult?.debugConnection?.vmService; @override bool get canHotRestart { @@ -117,8 +95,7 @@ abstract class ResidentWebRunner extends ResidentRunner { String method, { Map params, }) async { - final vmservice.Response response = - await _vmService.callServiceExtension(method, args: params); + final vmservice.Response response = await _vmService.callServiceExtension(method, args: params); return response.toJson(); } @@ -138,7 +115,7 @@ abstract class ResidentWebRunner extends ResidentRunner { } await _stdOutSub?.cancel(); await _webFs?.stop(); - await device.device.stopApp(null); + await device.stopApp(null); if (ChromeLauncher.hasChromeInstance) { final Chrome chrome = await ChromeLauncher.connectedInstance; await chrome.close(); @@ -157,15 +134,13 @@ abstract class ResidentWebRunner extends ResidentRunner { return printHelpDetails(); } const String fire = '🔥'; - const String rawMessage = - ' To hot restart changes while running, press "r". ' - 'To hot restart (and refresh the browser), press "R".'; + const String rawMessage = ' To hot restart changes while running, press "r". ' + 'To hot restart (and refresh the browser), press "R".'; final String message = terminal.color( fire + terminal.bolden(rawMessage), TerminalColor.red, ); - printStatus( - 'Warning: Flutter\'s support for web development is not stable yet and hasn\'t'); + printStatus('Warning: Flutter\'s support for web development is not stable yet and hasn\'t'); printStatus('been thoroughly tested in production environments.'); printStatus('For more information see https://flutter.dev/web'); printStatus(''); @@ -174,202 +149,18 @@ abstract class ResidentWebRunner extends ResidentRunner { printStatus('For a more detailed help message, press "h". $quitMessage'); } - @override - Future debugDumpApp() async { - try { - await _vmService?.callServiceExtension( - 'ext.flutter.debugDumpApp', - ); - } on vmservice.RPCError { - return; - } - } - - @override - Future debugDumpRenderTree() async { - try { - await _vmService?.callServiceExtension( - 'ext.flutter.debugDumpRenderTree', - ); - } on vmservice.RPCError { - return; - } - } - - @override - Future debugDumpLayerTree() async { - try { - await _vmService?.callServiceExtension( - 'ext.flutter.debugDumpLayerTree', - ); - } on vmservice.RPCError { - return; - } - } - - @override - Future debugDumpSemanticsTreeInTraversalOrder() async { - try { - await _vmService?.callServiceExtension( - 'ext.flutter.debugDumpSemanticsTreeInTraversalOrder'); - } on vmservice.RPCError { - return; - } - } - - @override - Future debugTogglePlatform() async { - try { - final vmservice.Response response = await _vmService - ?.callServiceExtension('ext.flutter.platformOverride'); - final String currentPlatform = response.json['value']; - String nextPlatform; - switch (currentPlatform) { - case 'android': - nextPlatform = 'iOS'; - break; - case 'iOS': - nextPlatform = 'android'; - break; - } - if (nextPlatform == null) { - return; - } - await _vmService?.callServiceExtension('ext.flutter.platformOverride', - args: { - 'value': nextPlatform, - }); - printStatus('Switched operating system to $nextPlatform'); - } on vmservice.RPCError { - return; - } - } - - @override - Future debugDumpSemanticsTreeInInverseHitTestOrder() async { - try { - await _vmService?.callServiceExtension( - 'ext.flutter.debugDumpSemanticsTreeInInverseHitTestOrder'); - } on vmservice.RPCError { - return; - } - } - - @override - Future debugToggleDebugPaintSizeEnabled() async { - try { - final vmservice.Response response = - await _vmService?.callServiceExtension( - 'ext.flutter.debugPaint', - ); - await _vmService?.callServiceExtension( - 'ext.flutter.debugPaint', - args: { - 'enabled': !(response.json['enabled'] == 'true') - }, - ); - } on vmservice.RPCError { - return; - } - } - - @override - Future debugToggleDebugCheckElevationsEnabled() async { - try { - final vmservice.Response response = - await _vmService?.callServiceExtension( - 'ext.flutter.debugCheckElevationsEnabled', - ); - await _vmService?.callServiceExtension( - 'ext.flutter.debugCheckElevationsEnabled', - args: { - 'enabled': !(response.json['enabled'] == 'true') - }, - ); - } on vmservice.RPCError { - return; - } - } - - @override - Future debugTogglePerformanceOverlayOverride() async { - try { - final vmservice.Response response = await _vmService - ?.callServiceExtension('ext.flutter.showPerformanceOverlay'); - await _vmService?.callServiceExtension( - 'ext.flutter.showPerformanceOverlay', - args: { - 'enabled': !(response.json['enabled'] == 'true') - }, - ); - } on vmservice.RPCError { - return; - } - } - - @override - Future debugToggleWidgetInspector() async { - try { - final vmservice.Response response = await _vmService - ?.callServiceExtension('ext.flutter.debugToggleWidgetInspector'); - await _vmService?.callServiceExtension( - 'ext.flutter.debugToggleWidgetInspector', - args: { - 'enabled': !(response.json['enabled'] == 'true') - }, - ); - } on vmservice.RPCError { - return; - } - } - - @override - Future debugToggleProfileWidgetBuilds() async { - try { - final vmservice.Response response = await _vmService - ?.callServiceExtension('ext.flutter.profileWidgetBuilds'); - await _vmService?.callServiceExtension( - 'ext.flutter.profileWidgetBuilds', - args: { - 'enabled': !(response.json['enabled'] == 'true') - }, - ); - } on vmservice.RPCError { - return; - } - } -} - -class _ExperimentalResidentWebRunner extends ResidentWebRunner { - _ExperimentalResidentWebRunner( - FlutterDevice device, { - String target, - @required FlutterProject flutterProject, - @required bool ipv6, - @required DebuggingOptions debuggingOptions, - bool stayResident = true, - }) : super( - device, - flutterProject: flutterProject, - target: target ?? fs.path.join('lib', 'main.dart'), - debuggingOptions: debuggingOptions, - ipv6: ipv6, - stayResident: stayResident, - ); - @override Future run({ Completer connectionInfoCompleter, Completer appStartedCompleter, String route, }) async { - firstBuildTime = DateTime.now(); final ApplicationPackage package = await ApplicationPackageFactory.instance.getPackageForPlatform( TargetPlatform.web_javascript, applicationBinary: null, ); if (package == null) { - printError('This application is not configured to build on the web.'); + printError('No application found for TargetPlatform.web_javascript.'); printError('To add web support to a project, run `flutter create .`.'); return 1; } @@ -383,200 +174,7 @@ class _ExperimentalResidentWebRunner extends ResidentWebRunner { return 1; } final String modeName = debuggingOptions.buildInfo.friendlyModeName; - printStatus('Launching ${getDisplayPath(target)} on ${device.device.name} in $modeName mode...'); - final String effectiveHostname = debuggingOptions.hostname ?? 'localhost'; - final int hostPort = debuggingOptions.port == null - ? await os.findFreePort() - : int.tryParse(debuggingOptions.port); - device.devFS = WebDevFS( - effectiveHostname, - hostPort, - packagesFilePath, - ); - await device.devFS.create(); - await _updateDevFS(fullRestart: true); - device.generator.accept(); - await device.device.startApp( - package, - mainPath: target, - debuggingOptions: debuggingOptions, - platformArgs: { - 'uri': 'http://$effectiveHostname:$hostPort', - }, - ); - return attach( - connectionInfoCompleter: connectionInfoCompleter, - appStartedCompleter: appStartedCompleter, - ); - } - - @override - Future restart({ - bool fullRestart = false, - bool pauseAfterRestart = false, - String reason, - bool benchmarkMode = false, - }) async { - final Stopwatch timer = Stopwatch()..start(); - final Status status = logger.startProgress( - 'Performing hot restart...', - timeout: supportsServiceProtocol - ? timeoutConfiguration.fastOperation - : timeoutConfiguration.slowOperation, - progressId: 'hot.restart', - ); - - final UpdateFSReport report = await _updateDevFS(fullRestart: fullRestart); - if (report.success) { - device.generator.accept(); - } else { - await device.generator.reject(); - } - final String modules = report.invalidatedModules - .map((String module) => '"$module"') - .join(','); - - try { - if (fullRestart) { - await _wipConnection.sendCommand('Page.reload'); - } else { - await _wipConnection.debugger - .sendCommand('Runtime.evaluate', params: { - 'expression': 'window.\$hotReloadHook([$modules])', - 'awaitPromise': true, - 'returnByValue': true, - }); - } - } on WipError catch (err) { - printError(err.toString()); - return OperationResult(1, err.toString()); - } finally { - status.stop(); - } - final String verb = fullRestart ? 'Restarted' : 'Reloaded'; - printStatus('$verb application in ${getElapsedAsMilliseconds(timer.elapsed)}.'); - if (!fullRestart) { - flutterUsage.sendTiming('hot', 'web-incremental-restart', timer.elapsed); - } - HotEvent( - 'restart', - targetPlatform: getNameForTargetPlatform(TargetPlatform.web_javascript), - sdkName: await device.device.sdkNameAndVersion, - emulator: false, - fullRestart: true, - reason: reason, - ).send(); - return OperationResult.ok; - } - - Future _updateDevFS({bool fullRestart = false}) async { - final bool isFirstUpload = !assetBundle.wasBuiltOnce(); - final bool rebuildBundle = assetBundle.needsBuild(); - if (rebuildBundle) { - printTrace('Updating assets'); - final int result = await assetBundle.build(); - if (result != 0) { - return UpdateFSReport(success: false); - } - } - final List invalidatedFiles = - await ProjectFileInvalidator.findInvalidated( - lastCompiled: device.devFS.lastCompiled, - urisToMonitor: device.devFS.sources, - packagesPath: packagesFilePath, - ); - final Status devFSStatus = logger.startProgress( - 'Syncing files to device ${device.device.name}...', - timeout: timeoutConfiguration.fastOperation, - ); - final UpdateFSReport report = await device.devFS.update( - mainPath: mainPath, - target: target, - bundle: assetBundle, - firstBuildTime: firstBuildTime, - bundleFirstUpload: isFirstUpload, - generator: device.generator, - fullRestart: fullRestart, - dillOutputPath: dillOutputPath, - projectRootPath: projectRootPath, - pathToReload: getReloadPath(fullRestart: fullRestart), - invalidatedFiles: invalidatedFiles, - trackWidgetCreation: true, - ); - devFSStatus.stop(); - printTrace('Synced ${getSizeAsMB(report.syncedBytes)}.'); - return report; - } - - @override - Future attach({ - Completer connectionInfoCompleter, - Completer appStartedCompleter, - }) async { - final Chrome chrome = await ChromeLauncher.connectedInstance; - final ChromeTab chromeTab = - await chrome.chromeConnection.getTab((ChromeTab chromeTab) { - return chromeTab.url.contains(debuggingOptions.hostname); - }); - _wipConnection = await chromeTab.connect(); - appStartedCompleter?.complete(); - connectionInfoCompleter?.complete(); - if (stayResident) { - await waitForAppToFinish(); - } else { - await stopEchoingDeviceLog(); - await exitApp(); - } - await cleanupAtFinish(); - return 0; - } -} - -class _DwdsResidentWebRunner extends ResidentWebRunner { - _DwdsResidentWebRunner( - FlutterDevice device, { - String target, - @required FlutterProject flutterProject, - @required bool ipv6, - @required DebuggingOptions debuggingOptions, - bool stayResident = true, - }) : super( - device, - flutterProject: flutterProject, - target: target ?? fs.path.join('lib', 'main.dart'), - debuggingOptions: debuggingOptions, - ipv6: ipv6, - stayResident: stayResident, - ); - - @override - Future run({ - Completer connectionInfoCompleter, - Completer appStartedCompleter, - String route, - }) async { - firstBuildTime = DateTime.now(); - final ApplicationPackage package = await ApplicationPackageFactory.instance.getPackageForPlatform( - TargetPlatform.web_javascript, - applicationBinary: null, - ); - if (package == null) { - printError('This application is not configured to build on the web.'); - printError('To add web support to a project, run `flutter create .`.'); - return 1; - } - if (!fs.isFileSync(mainPath)) { - String message = 'Tried to run $mainPath, but that file does not exist.'; - if (target == null) { - message += - '\nConsider using the -t option to specify the Dart file to start.'; - } - printError(message); - return 1; - } - final String modeName = debuggingOptions.buildInfo.friendlyModeName; - printStatus( - 'Launching ${getDisplayPath(target)} on ${device.device.name} in $modeName mode...'); + printStatus('Launching ${getDisplayPath(target)} on ${device.name} in $modeName mode...'); Status buildStatus; bool statusActive = false; try { @@ -606,8 +204,7 @@ class _DwdsResidentWebRunner extends ResidentWebRunner { ); statusActive = true; } - await device.device.startApp( - package, + await device.startApp(package, mainPath: target, debuggingOptions: debuggingOptions, platformArgs: { @@ -632,38 +229,42 @@ class _DwdsResidentWebRunner extends ResidentWebRunner { } on VersionSkew { // Thrown if an older build daemon is already running. throwToolExit( - 'Another build daemon is already running with an older version.\n' - 'Try exiting other Flutter processes in this project and try again.'); + 'Another build daemon is already running with an older version.\n' + 'Try exiting other Flutter processes in this project and try again.' + ); } on OptionsSkew { // Thrown if a build daemon is already running with different configuration. throwToolExit( - 'Another build daemon is already running with different configuration.\n' - 'Exit other Flutter processes running in this project and try again.'); + 'Another build daemon is already running with different configuration.\n' + 'Try exiting other Flutter processes in this project and try again.' + ); } on WebSocketException { throwToolExit('Failed to connect to WebSocket.'); } on BuildException { throwToolExit('Failed to build application for the Web.'); } on ChromeDebugException catch (err, stackTrace) { throwToolExit( - 'Failed to establish connection with Chrome. Try running the application again.\n' - 'If this problem persists, please file an issue with the details below:\n$err\n$stackTrace'); + 'Failed to establish connection with Chrome. Try running the application again.\n' + 'If this problem persists, please file an issue with the details below:\n$err\n$stackTrace'); } on AppConnectionException { throwToolExit( - 'Failed to establish connection with the application instance in Chrome.\n' - 'This can happen if the websocket connection used by the web tooling is ' - 'unabled to correctly establish a connection, for example due to a firewall.'); - } on MissingPortFile { + 'Failed to establish connection with the application instance in Chrome.\n' + 'This can happen if the websocket connection used by the web tooling is ' + 'unabled to correctly establish a connection, for example due to a firewall.' + ); + } on MissingPortFile { throwToolExit( - 'Failed to connect to build daemon.\nThe daemon either failed to ' - 'start or was killed by another process.'); + 'Failed to connect to build daemon.\nThe daemon either failed to ' + 'start or was killed by another process.'); } on SocketException catch (err) { throwToolExit(err.toString()); } on StateError catch (err) { final String message = err.toString(); if (message.contains('Unable to start build daemon')) { - throwToolExit('Failed to start build daemon. The process might have ' - 'exited unexpectedly during startup. Try running the application ' - 'again.'); + throwToolExit( + 'Failed to start build daemon. The process might have ' + 'exited unexpectedly during startup. Try running the application ' + 'again.'); } rethrow; } finally { @@ -674,6 +275,61 @@ class _DwdsResidentWebRunner extends ResidentWebRunner { return 1; } + @override + Future attach({ + Completer connectionInfoCompleter, + Completer appStartedCompleter, + }) async { + Uri websocketUri; + if (supportsServiceProtocol) { + // Cleanup old subscriptions. These will throw if there isn't anything + // listening, which is fine because that is what we want to ensure. + try { + await _vmService.streamCancel('Stdout'); + } on vmservice.RPCError { + // Ignore this specific error. + } + try { + await _vmService.streamListen('Stdout'); + } on vmservice.RPCError { + // Ignore this specific error. + } + _stdOutSub = _vmService.onStdoutEvent.listen((vmservice.Event log) { + final String message = utf8.decode(base64.decode(log.bytes)).trim(); + printStatus(message); + }); + unawaited(_vmService.registerService('reloadSources', 'FlutterTools')); + websocketUri = Uri.parse(_connectionResult.debugConnection.uri); + // Always run main after connecting because start paused doesn't work yet. + if (!debuggingOptions.startPaused || !supportsServiceProtocol) { + _connectionResult.appConnection.runMain(); + } else { + StreamSubscription resumeSub; + resumeSub = _connectionResult.debugConnection.vmService.onDebugEvent.listen((vmservice.Event event) { + if (event.type == vmservice.EventKind.kResume) { + _connectionResult.appConnection.runMain(); + resumeSub.cancel(); + } + }); + } + } + if (websocketUri != null) { + printStatus('Debug service listening on $websocketUri'); + } + connectionInfoCompleter?.complete( + DebugConnectionInfo(wsUri: websocketUri) + ); + + if (stayResident) { + await waitForAppToFinish(); + } else { + await stopEchoingDeviceLog(); + await exitApp(); + } + await cleanupAtFinish(); + return 0; + } + @override Future restart({ bool fullRestart = false, @@ -700,11 +356,10 @@ class _DwdsResidentWebRunner extends ResidentWebRunner { flutterUsage.sendTiming('hot', 'web-recompile', recompileDuration); try { final vmservice.Response reloadResponse = fullRestart - ? await _vmService.callServiceExtension('fullReload') - : await _vmService.callServiceExtension('hotRestart'); + ? await _vmService.callServiceExtension('fullReload') + : await _vmService.callServiceExtension('hotRestart'); final String verb = fullRestart ? 'Restarted' : 'Reloaded'; - printStatus( - '$verb application in ${getElapsedAsMilliseconds(timer.elapsed)}.'); + printStatus('$verb application in ${getElapsedAsMilliseconds(timer.elapsed)}.'); // Send timing analytics for full restart and for refresh. final bool wasSuccessful = reloadResponse.type == 'Success'; @@ -720,10 +375,9 @@ class _DwdsResidentWebRunner extends ResidentWebRunner { return OperationResult(1, 'Page requires refresh.'); } finally { status.stop(); - HotEvent( - 'restart', + HotEvent('restart', targetPlatform: getNameForTargetPlatform(TargetPlatform.web_javascript), - sdkName: await device.device.sdkNameAndVersion, + sdkName: await device.sdkNameAndVersion, emulator: false, fullRestart: true, reason: reason, @@ -751,58 +405,159 @@ class _DwdsResidentWebRunner extends ResidentWebRunner { } @override - Future attach({ - Completer connectionInfoCompleter, - Completer appStartedCompleter, - }) async { - Uri websocketUri; - if (supportsServiceProtocol) { - // Cleanup old subscriptions. These will throw if there isn't anything - // listening, which is fine because that is what we want to ensure. - try { - await _vmService.streamCancel('Stdout'); - } on vmservice.RPCError { - // It is safe to ignore this error because we expect an error to be - // thrown if we're not already subscribed. - } - try { - await _vmService.streamListen('Stdout'); - } on vmservice.RPCError { - // It is safe to ignore this error because we expect an error to be - // thrown if we're not already subscribed. - } - _stdOutSub = _vmService.onStdoutEvent.listen((vmservice.Event log) { - final String message = utf8.decode(base64.decode(log.bytes)).trim(); - printStatus(message); - }); - unawaited(_vmService.registerService('reloadSources', 'FlutterTools')); - websocketUri = Uri.parse(_connectionResult.debugConnection.uri); - // Always run main after connecting because start paused doesn't work yet. - if (!debuggingOptions.startPaused || !supportsServiceProtocol) { - _connectionResult.appConnection.runMain(); - } else { - StreamSubscription resumeSub; - resumeSub = _connectionResult.debugConnection.vmService.onDebugEvent - .listen((vmservice.Event event) { - if (event.type == vmservice.EventKind.kResume) { - _connectionResult.appConnection.runMain(); - resumeSub.cancel(); - } - }); - } + Future debugDumpApp() async { + try { + await _vmService.callServiceExtension( + 'ext.flutter.debugDumpApp', + ); + } on vmservice.RPCError { + return; } - if (websocketUri != null) { - printStatus('Debug service listening on $websocketUri'); - } - connectionInfoCompleter?.complete(DebugConnectionInfo(wsUri: websocketUri)); + } - if (stayResident) { - await waitForAppToFinish(); - } else { - await stopEchoingDeviceLog(); - await exitApp(); + @override + Future debugDumpRenderTree() async { + try { + await _vmService.callServiceExtension( + 'ext.flutter.debugDumpRenderTree', + ); + } on vmservice.RPCError { + return; + } + } + + @override + Future debugDumpLayerTree() async { + try { + await _vmService.callServiceExtension( + 'ext.flutter.debugDumpLayerTree', + ); + } on vmservice.RPCError { + return; + } + } + + @override + Future debugDumpSemanticsTreeInTraversalOrder() async { + try { + await _vmService.callServiceExtension( + 'ext.flutter.debugDumpSemanticsTreeInTraversalOrder'); + } on vmservice.RPCError { + return; + } + } + + @override + Future debugTogglePlatform() async { + try { + final vmservice.Response response = await _vmService.callServiceExtension( + 'ext.flutter.platformOverride'); + final String currentPlatform = response.json['value']; + String nextPlatform; + switch (currentPlatform) { + case 'android': + nextPlatform = 'iOS'; + break; + case 'iOS': + nextPlatform = 'android'; + break; + } + if (nextPlatform == null) { + return; + } + await _vmService.callServiceExtension( + 'ext.flutter.platformOverride', args: { + 'value': nextPlatform, + }); + printStatus('Switched operating system to $nextPlatform'); + } on vmservice.RPCError { + return; + } + } + + @override + Future debugDumpSemanticsTreeInInverseHitTestOrder() async { + try { + await _vmService.callServiceExtension( + 'ext.flutter.debugDumpSemanticsTreeInInverseHitTestOrder'); + } on vmservice.RPCError { + return; + } + } + + + @override + Future debugToggleDebugPaintSizeEnabled() async { + try { + final vmservice.Response response = await _vmService.callServiceExtension( + 'ext.flutter.debugPaint', + ); + await _vmService.callServiceExtension( + 'ext.flutter.debugPaint', + args: {'enabled': !(response.json['enabled'] == 'true')}, + ); + } on vmservice.RPCError { + return; + } + } + + @override + Future debugToggleDebugCheckElevationsEnabled() async { + try { + final vmservice.Response response = await _vmService.callServiceExtension( + 'ext.flutter.debugCheckElevationsEnabled', + ); + await _vmService.callServiceExtension( + 'ext.flutter.debugCheckElevationsEnabled', + args: {'enabled': !(response.json['enabled'] == 'true')}, + ); + } on vmservice.RPCError { + return; + } + } + + @override + Future debugTogglePerformanceOverlayOverride() async { + try { + final vmservice.Response response = await _vmService.callServiceExtension( + 'ext.flutter.showPerformanceOverlay' + ); + await _vmService.callServiceExtension( + 'ext.flutter.showPerformanceOverlay', + args: {'enabled': !(response.json['enabled'] == 'true')}, + ); + } on vmservice.RPCError { + return; + } + } + + @override + Future debugToggleWidgetInspector() async { + try { + final vmservice.Response response = await _vmService.callServiceExtension( + 'ext.flutter.debugToggleWidgetInspector' + ); + await _vmService.callServiceExtension( + 'ext.flutter.debugToggleWidgetInspector', + args: {'enabled': !(response.json['enabled'] == 'true')}, + ); + } on vmservice.RPCError { + return; + } + } + + @override + Future debugToggleProfileWidgetBuilds() async { + try { + final vmservice.Response response = await _vmService.callServiceExtension( + 'ext.flutter.profileWidgetBuilds' + ); + await _vmService.callServiceExtension( + 'ext.flutter.profileWidgetBuilds', + args: {'enabled': !(response.json['enabled'] == 'true')}, + ); + } on vmservice.RPCError { + return; } - await cleanupAtFinish(); - return 0; } } diff --git a/packages/flutter_tools/lib/src/commands/daemon.dart b/packages/flutter_tools/lib/src/commands/daemon.dart index d37bfe03225..7c4286dbf63 100644 --- a/packages/flutter_tools/lib/src/commands/daemon.dart +++ b/packages/flutter_tools/lib/src/commands/daemon.dart @@ -430,7 +430,7 @@ class AppDomain extends Domain { if (await device.targetPlatform == TargetPlatform.web_javascript) { runner = webRunnerFactory.createWebRunner( - flutterDevice, + device, flutterProject: flutterProject, target: target, debuggingOptions: options, diff --git a/packages/flutter_tools/lib/src/commands/run.dart b/packages/flutter_tools/lib/src/commands/run.dart index b410da213ce..6bd40dfe13a 100644 --- a/packages/flutter_tools/lib/src/commands/run.dart +++ b/packages/flutter_tools/lib/src/commands/run.dart @@ -453,7 +453,7 @@ class RunCommand extends RunCommandBase { ); } else if (webMode) { runner = webRunnerFactory.createWebRunner( - flutterDevices.single, + devices.single, target: targetFile, flutterProject: flutterProject, ipv6: ipv6, diff --git a/packages/flutter_tools/lib/src/compile.dart b/packages/flutter_tools/lib/src/compile.dart index 4dfeba8ef91..49a66aeed62 100644 --- a/packages/flutter_tools/lib/src/compile.dart +++ b/packages/flutter_tools/lib/src/compile.dart @@ -201,6 +201,7 @@ class PackageUriMapper { PackageUriMapper(String scriptPath, String packagesPath, String fileSystemScheme, List fileSystemRoots) { final Map packageMap = PackageMap(fs.path.absolute(packagesPath)).map; final String scriptUri = Uri.file(scriptPath, windows: platform.isWindows).toString(); + for (String packageName in packageMap.keys) { final String prefix = packageMap[packageName].toString(); // Only perform a multi-root mapping if there are multiple roots. diff --git a/packages/flutter_tools/lib/src/devfs.dart b/packages/flutter_tools/lib/src/devfs.dart index 99b467deff3..366c698f65f 100644 --- a/packages/flutter_tools/lib/src/devfs.dart +++ b/packages/flutter_tools/lib/src/devfs.dart @@ -349,19 +349,12 @@ class UpdateFSReport { int get invalidatedSourcesCount => _invalidatedSourcesCount; int get syncedBytes => _syncedBytes; - /// JavaScript modules produced by the incremental compiler in `dartdevc` - /// mode. - /// - /// Only used for JavaScript compilation. - List invalidatedModules; - void incorporateResults(UpdateFSReport report) { if (!report._success) { _success = false; } _invalidatedSourcesCount += report._invalidatedSourcesCount; _syncedBytes += report._syncedBytes; - invalidatedModules ??= report.invalidatedModules; } bool _success; diff --git a/packages/flutter_tools/lib/src/features.dart b/packages/flutter_tools/lib/src/features.dart index bf1dba91948..9822ac9926a 100644 --- a/packages/flutter_tools/lib/src/features.dart +++ b/packages/flutter_tools/lib/src/features.dart @@ -151,15 +151,10 @@ const Feature flutterAndroidEmbeddingV2Feature = Feature( const Feature flutterWebIncrementalCompiler = Feature( name: 'Enable the incremental compiler for web builds', configSetting: 'enable-web-incremental-compiler', - environmentOverride: 'WEB_INCREMENTAL_COMPILER', master: FeatureChannelSetting( available: true, enabledByDefault: false, ), - dev: FeatureChannelSetting( - available: true, - enabledByDefault: false, - ), ); /// A [Feature] is a process for conditionally enabling tool features. diff --git a/packages/flutter_tools/lib/src/web/bootstrap.dart b/packages/flutter_tools/lib/src/web/bootstrap.dart index 877444d93fd..247e56d7207 100644 --- a/packages/flutter_tools/lib/src/web/bootstrap.dart +++ b/packages/flutter_tools/lib/src/web/bootstrap.dart @@ -75,25 +75,21 @@ define("main_module", ["$entrypoint", "dart_sdk"], function(app, dart_sdk) { dart_sdk.dart.setStartAsyncSynchronously(true); dart_sdk._isolate_helper.startRootIsolate(() => {}, []); dart_sdk._debugger.registerDevtoolsFormatter(); - let voidToNull = () => (voidToNull = dart_sdk.dart.constFn(dart_sdk.dart.fnType(dart_sdk.core.Null, [dart_sdk.dart.void])))(); + dart_sdk.ui.webOnlyInitializePlatform(); // Attach the main entrypoint and hot reload functionality to the window. window.\$mainEntrypoint = app.main.main; if (window.\$hotReload == null) { window.\$hotReload = function(cb) { - dart_sdk.developer.invokeExtension("ext.flutter.disassemble", "{}").then((_) => { - dart_sdk.dart.hotRestart(); - dart_sdk.ui.webOnlyInitializePlatform().then(dart_sdk.core.Null, dart_sdk.dart.fn(_ => { - window.\$mainEntrypoint(); - window.requestAnimationFrame(cb); - }, voidToNull())); - }); + dart_sdk.developer.invokeExtension("ext.flutter.disassemble", "{}"); + dart_sdk.dart.hotRestart(); + window.\$mainEntrypoint(); + if (cb != null) { + cb(); + } } } - - dart_sdk.ui.webOnlyInitializePlatform().then(dart_sdk.core.Null, dart_sdk.dart.fn(_ => { - app.main.main(); - }, voidToNull())); + app.main.main(); }); // Require JS configuration. diff --git a/packages/flutter_tools/lib/src/web/chrome.dart b/packages/flutter_tools/lib/src/web/chrome.dart index 3e58930cbc1..18c62556597 100644 --- a/packages/flutter_tools/lib/src/web/chrome.dart +++ b/packages/flutter_tools/lib/src/web/chrome.dart @@ -71,11 +71,6 @@ void resetChromeForTesting() { ChromeLauncher._currentCompleter = Completer(); } -@visibleForTesting -void launchChromeInstance(Chrome chrome) { - ChromeLauncher._currentCompleter.complete(chrome); -} - /// Responsible for launching chrome with devtools configured. class ChromeLauncher { const ChromeLauncher(); diff --git a/packages/flutter_tools/lib/src/web/devfs_web.dart b/packages/flutter_tools/lib/src/web/devfs_web.dart index a3348d8b4b7..a28682fd4c7 100644 --- a/packages/flutter_tools/lib/src/web/devfs_web.dart +++ b/packages/flutter_tools/lib/src/web/devfs_web.dart @@ -7,18 +7,12 @@ import 'dart:typed_data'; import 'package:meta/meta.dart'; import 'package:mime/mime.dart' as mime; -import '../artifacts.dart'; -import '../asset.dart'; import '../base/common.dart'; import '../base/file_system.dart'; import '../base/io.dart'; import '../build_info.dart'; -import '../bundle.dart'; -import '../compile.dart'; import '../convert.dart'; -import '../devfs.dart'; import '../globals.dart'; -import 'bootstrap.dart'; /// A web server which handles serving JavaScript and assets. /// @@ -55,10 +49,7 @@ class WebAssetServer { } final HttpServer _httpServer; - // If holding these in memory is too much overhead, this can be switched to a - // RandomAccessFile and read on demand. final Map _files = {}; - final Map _sourcemaps = {}; // handle requests for JavaScript source, dart sources maps, or asset files. Future _handleRequest(HttpRequest request) async { @@ -80,7 +71,8 @@ class WebAssetServer { } // If this is a JavaScript file, it must be in the in-memory cache. - // Attempt to look up the file by URI. + // Attempt to look up the file by URI, returning a 404 if it is not + // found. if (_files.containsKey(request.uri.path)) { final List bytes = _files[request.uri.path]; response.headers @@ -90,18 +82,6 @@ class WebAssetServer { await response.close(); return; } - // If this is a sourcemap file, then it might be in the in-memory cache. - // Attempt to lookup the file by URI. - if (_sourcemaps.containsKey(request.uri.path)) { - final List bytes = _sourcemaps[request.uri.path]; - response.headers - ..add('Content-Length', bytes.length) - ..add('Content-Type', 'application/json'); - response.add(bytes); - await response.close(); - return; - } - // If this is a dart file, it must be on the local file system and is // likely coming from a source map request. Attempt to look in the // local filesystem for it, and return a 404 if it is not found. The tool @@ -115,18 +95,6 @@ class WebAssetServer { file = fs.file(fs.path.join(getAssetBuildDirectory(), fs.path.relative(assetPath))); } - // If it isn't a project source or an asset, it must be a dart SDK source. - // or a flutter web SDK source. - if (!file.existsSync()) { - final Directory dartSdkParent = fs.directory(artifacts.getArtifactPath(Artifact.engineDartSdkPath)).parent; - file = fs.file(fs.path.joinAll([dartSdkParent.path, ...request.uri.pathSegments])); - } - - if (!file.existsSync()) { - final String flutterWebSdk = artifacts.getArtifactPath(Artifact.flutterWebSdk); - file = fs.file(fs.path.joinAll([flutterWebSdk, ...request.uri.pathSegments])); - } - if (!file.existsSync()) { response.statusCode = HttpStatus.notFound; await response.close(); @@ -163,186 +131,30 @@ class WebAssetServer { /// Update the in-memory asset server with the provided source and manifest files. /// /// Returns a list of updated modules. - List write(File codeFile, File manifestFile, File sourcemapFile) { + List write(File sourceFile, File manifestFile) { final List modules = []; - final Uint8List codeBytes = codeFile.readAsBytesSync(); - final Uint8List sourcemapBytes = sourcemapFile.readAsBytesSync(); + final Uint8List bytes = sourceFile.readAsBytesSync(); final Map manifest = json.decode(manifestFile.readAsStringSync()); for (String filePath in manifest.keys) { if (filePath == null) { printTrace('Invalid manfiest file: $filePath'); continue; } - final Map offsets = manifest[filePath]; - final List codeOffsets = offsets['code']; - final List sourcemapOffsets = offsets['sourcemap']; - if (codeOffsets.length != 2 || sourcemapOffsets.length != 2) { + final List offsets = manifest[filePath]; + if (offsets.length != 2) { printTrace('Invalid manifest byte offsets: $offsets'); continue; } - - final int codeStart = codeOffsets[0]; - final int codeEnd = codeOffsets[1]; - if (codeStart < 0 || codeEnd > codeBytes.lengthInBytes) { - printTrace('Invalid byte index: [$codeStart, $codeEnd]'); + final int start = offsets[0]; + final int end = offsets[1]; + if (start < 0 || end > bytes.lengthInBytes) { + printTrace('Invalid byte index: [$start, $end]'); continue; } - final Uint8List byteView = Uint8List.view( - codeBytes.buffer, - codeStart, - codeEnd - codeStart, - ); + final Uint8List byteView = Uint8List.view(bytes.buffer, start, end - start); _files[filePath] = byteView; - - final int sourcemapStart = sourcemapOffsets[0]; - final int sourcemapEnd = sourcemapOffsets[1]; - if (sourcemapStart < 0 || sourcemapEnd > sourcemapBytes.lengthInBytes) { - printTrace('Invalid byte index: [$sourcemapStart, $sourcemapEnd]'); - continue; - } - final Uint8List sourcemapView = Uint8List.view( - sourcemapBytes.buffer, - sourcemapStart, - sourcemapEnd - sourcemapStart , - ); - _sourcemaps['$filePath.map'] = sourcemapView; - modules.add(filePath); } return modules; } } - -class WebDevFS implements DevFS { - WebDevFS(this.hostname, this.port, this._packagesFilePath); - - final String hostname; - final int port; - final String _packagesFilePath; - WebAssetServer _webAssetServer; - - @override - List sources = []; - - @override - DateTime lastCompiled; - - // We do not evict assets on the web. - @override - Set get assetPathsToEvict => const {}; - - @override - Uri get baseUri => null; - - @override - Future create() async { - _webAssetServer = await WebAssetServer.start(hostname, port); - return Uri.base; - } - - @override - Future destroy() async { - await _webAssetServer.dispose(); - } - - @override - Uri deviceUriToHostUri(Uri deviceUri) { - return deviceUri; - } - - @override - String get fsName => 'web_asset'; - - @override - Directory get rootDirectory => null; - - @override - Future update({ - String mainPath, - String target, - AssetBundle bundle, - DateTime firstBuildTime, - bool bundleFirstUpload = false, - @required ResidentCompiler generator, - String dillOutputPath, - @required bool trackWidgetCreation, - bool fullRestart = false, - String projectRootPath, - String pathToReload, - List invalidatedFiles, - }) async { - assert(trackWidgetCreation != null); - assert(generator != null); - if (bundleFirstUpload) { - final File requireJS = fs.file(fs.path.join( - artifacts.getArtifactPath(Artifact.engineDartSdkPath), - 'lib', - 'dev_compiler', - 'kernel', - 'amd', - 'require.js', - )); - final File dartSdk = fs.file(fs.path.join( - artifacts.getArtifactPath(Artifact.flutterWebSdk), - 'kernel', - 'amd', - 'dart_sdk.js', - )); - final File dartSdkSourcemap = fs.file(fs.path.join( - artifacts.getArtifactPath(Artifact.flutterWebSdk), - 'kernel', - 'amd', - 'dart_sdk.js.map', - )); - final File stackTraceMapper = fs.file(fs.path.join( - artifacts.getArtifactPath(Artifact.engineDartSdkPath), - 'lib', - 'dev_compiler', - 'web', - 'dart_stack_trace_mapper.js', - )); - _webAssetServer.writeFile('/main.dart.js', generateBootstrapScript( - requireUrl: requireJS.path, - mapperUrl: stackTraceMapper.path, - entrypoint: '$mainPath.js', - )); - _webAssetServer.writeFile('/main_module.js', generateMainModule( - entrypoint: '$mainPath.js', - )); - _webAssetServer.writeFile('/dart_sdk.js', dartSdk.readAsStringSync()); - _webAssetServer.writeFile('/dart_sdk.js.map', dartSdkSourcemap.readAsStringSync()); - } - final DateTime candidateCompileTime = DateTime.now(); - if (fullRestart) { - generator.reset(); - } - final CompilerOutput compilerOutput = await generator.recompile( - mainPath, - invalidatedFiles, - outputPath: dillOutputPath ?? getDefaultApplicationKernelPath(trackWidgetCreation: trackWidgetCreation), - packagesFilePath : _packagesFilePath, - ); - if (compilerOutput == null || compilerOutput.errorCount > 0) { - return UpdateFSReport(success: false); - } - // Only update the last compiled time if we successfully compiled. - lastCompiled = candidateCompileTime; - // list of sources that needs to be monitored are in [compilerOutput.sources] - sources = compilerOutput.sources; - File codeFile; - File manifestFile; - File sourcemapFile; - List modules; - try { - codeFile = fs.file('${compilerOutput.outputFilename}.sources'); - manifestFile = fs.file('${compilerOutput.outputFilename}.json'); - sourcemapFile = fs.file('${compilerOutput.outputFilename}.map'); - modules = _webAssetServer.write(codeFile, manifestFile, sourcemapFile); - } on FileSystemException catch (err) { - throwToolExit('Failed to load recompiled sources:\n$err'); - } - return UpdateFSReport(success: true, syncedBytes: codeFile.lengthSync(), - invalidatedSourcesCount: invalidatedFiles.length) - ..invalidatedModules = modules; - } -} diff --git a/packages/flutter_tools/lib/src/web/web_runner.dart b/packages/flutter_tools/lib/src/web/web_runner.dart index 09402a3fcea..b87db3083bd 100644 --- a/packages/flutter_tools/lib/src/web/web_runner.dart +++ b/packages/flutter_tools/lib/src/web/web_runner.dart @@ -17,7 +17,7 @@ abstract class WebRunnerFactory { /// Create a [ResidentRunner] for the web. ResidentRunner createWebRunner( - FlutterDevice device, { + Device device, { String target, @required bool stayResident, @required FlutterProject flutterProject, diff --git a/packages/flutter_tools/test/commands.shard/hermetic/build_web_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/build_web_test.dart index b11f78ed2ac..883072fd6e4 100644 --- a/packages/flutter_tools/test/commands.shard/hermetic/build_web_test.dart +++ b/packages/flutter_tools/test/commands.shard/hermetic/build_web_test.dart @@ -63,12 +63,11 @@ void main() { test('Refuses to build using runner when missing index.html', () => testbed.run(() async { fs.file(fs.path.join('web', 'index.html')).deleteSync(); - final ResidentWebRunner runner = DwdsWebRunnerFactory().createWebRunner( + final ResidentWebRunner runner = ResidentWebRunner( null, flutterProject: FlutterProject.current(), ipv6: false, debuggingOptions: DebuggingOptions.enabled(BuildInfo.debug), - stayResident: true, ); expect(await runner.run(), 1); })); diff --git a/packages/flutter_tools/test/general.shard/resident_web_runner_cold_test.dart b/packages/flutter_tools/test/general.shard/resident_web_runner_cold_test.dart index 603fee855c3..216eefc65c6 100644 --- a/packages/flutter_tools/test/general.shard/resident_web_runner_cold_test.dart +++ b/packages/flutter_tools/test/general.shard/resident_web_runner_cold_test.dart @@ -26,21 +26,17 @@ void main() { Testbed testbed; MockFlutterWebFs mockWebFs; ResidentWebRunner residentWebRunner; - MockFlutterDevice mockFlutterDevice; setUp(() { mockWebFs = MockFlutterWebFs(); final MockWebDevice mockWebDevice = MockWebDevice(); - mockFlutterDevice = MockFlutterDevice(); - when(mockFlutterDevice.device).thenReturn(mockWebDevice); testbed = Testbed( setup: () { - residentWebRunner = residentWebRunner = DwdsWebRunnerFactory().createWebRunner( - mockFlutterDevice, + residentWebRunner = ResidentWebRunner( + mockWebDevice, flutterProject: FlutterProject.current(), debuggingOptions: DebuggingOptions.disabled(BuildInfo.release), ipv6: true, - stayResident: true, ); }, overrides: { @@ -122,4 +118,4 @@ class MockFlutterWebFs extends Mock implements WebFs {} class MockDebugConnection extends Mock implements DebugConnection {} class MockVmService extends Mock implements VmService {} class MockStatus extends Mock implements Status {} -class MockFlutterDevice extends Mock implements FlutterDevice {} + 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 05753d3dcf1..70a4d1de12a 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 @@ -12,22 +12,17 @@ import 'package:flutter_tools/src/base/file_system.dart'; import 'package:flutter_tools/src/base/io.dart'; import 'package:flutter_tools/src/base/logger.dart'; import 'package:flutter_tools/src/build_info.dart'; -import 'package:flutter_tools/src/compile.dart'; -import 'package:flutter_tools/src/devfs.dart'; import 'package:flutter_tools/src/device.dart'; -import 'package:flutter_tools/src/features.dart'; import 'package:flutter_tools/src/globals.dart'; import 'package:flutter_tools/src/project.dart'; import 'package:flutter_tools/src/reporting/reporting.dart'; import 'package:flutter_tools/src/resident_runner.dart'; import 'package:flutter_tools/src/build_runner/resident_web_runner.dart'; import 'package:flutter_tools/src/build_runner/web_fs.dart'; -import 'package:flutter_tools/src/web/chrome.dart'; import 'package:flutter_tools/src/web/web_device.dart'; import 'package:meta/meta.dart'; import 'package:mockito/mockito.dart'; import 'package:vm_service/vm_service.dart'; -import 'package:webkit_inspection_protocol/webkit_inspection_protocol.dart'; import '../src/common.dart'; import '../src/testbed.dart'; @@ -38,41 +33,22 @@ void main() { ResidentWebRunner residentWebRunner; MockDebugConnection mockDebugConnection; MockVmService mockVmService; - MockChromeDevice mockChromeDevice; + MockWebDevice mockWebDevice; MockAppConnection mockAppConnection; - MockFlutterDevice mockFlutterDevice; - MockWebDevFS mockWebDevFS; - MockResidentCompiler mockResidentCompiler; - MockChrome mockChrome; - MockChromeConnection mockChromeConnection; - MockChromeTab mockChromeTab; - MockWipConnection mockWipConnection; - MockWipDebugger mockWipDebugger; setUp(() { - resetChromeForTesting(); mockWebFs = MockFlutterWebFs(); mockDebugConnection = MockDebugConnection(); mockVmService = MockVmService(); - mockChromeDevice = MockChromeDevice(); + mockWebDevice = MockWebDevice(); mockAppConnection = MockAppConnection(); - mockFlutterDevice = MockFlutterDevice(); - mockWebDevFS = MockWebDevFS(); - mockResidentCompiler = MockResidentCompiler(); - mockChrome = MockChrome(); - mockChromeConnection = MockChromeConnection(); - mockChromeTab = MockChromeTab(); - mockWipConnection = MockWipConnection(); - mockWipDebugger = MockWipDebugger(); - when(mockFlutterDevice.device).thenReturn(mockChromeDevice); testbed = Testbed( setup: () { - residentWebRunner = DwdsWebRunnerFactory().createWebRunner( - mockFlutterDevice, + residentWebRunner = ResidentWebRunner( + mockWebDevice, flutterProject: FlutterProject.current(), debuggingOptions: DebuggingOptions.enabled(BuildInfo.debug), ipv6: true, - stayResident: true, ); }, overrides: { @@ -112,44 +88,26 @@ void main() { return const Stream.empty(); }); when(mockDebugConnection.uri).thenReturn('ws://127.0.0.1/abcd/'); - when(mockFlutterDevice.devFS).thenReturn(mockWebDevFS); - when(mockWebDevFS.sources).thenReturn([]); - when(mockFlutterDevice.generator).thenReturn(mockResidentCompiler); - when(mockChrome.chromeConnection).thenReturn(mockChromeConnection); - when(mockChromeConnection.getTab(any)).thenAnswer((Invocation invocation) async { - return mockChromeTab; - }); - when(mockChromeTab.connect()).thenAnswer((Invocation invocation) async { - return mockWipConnection; - }); - when(mockWipConnection.debugger).thenReturn(mockWipDebugger); } test('runner with web server device does not support debugging', () => testbed.run(() { - when(mockFlutterDevice.device).thenReturn(WebServerDevice()); - final ResidentRunner profileResidentWebRunner = residentWebRunner = DwdsWebRunnerFactory().createWebRunner( - mockFlutterDevice, + final ResidentRunner profileResidentWebRunner = ResidentWebRunner( + WebServerDevice(), flutterProject: FlutterProject.current(), debuggingOptions: DebuggingOptions.enabled(BuildInfo.debug), ipv6: true, - stayResident: true, ); expect(profileResidentWebRunner.debuggingEnabled, false); - - when(mockFlutterDevice.device).thenReturn(MockChromeDevice()); - expect(residentWebRunner.debuggingEnabled, true); })); test('profile does not supportsServiceProtocol', () => testbed.run(() { - when(mockFlutterDevice.device).thenReturn(mockChromeDevice); - final ResidentRunner profileResidentWebRunner = DwdsWebRunnerFactory().createWebRunner( - mockFlutterDevice, + final ResidentRunner profileResidentWebRunner = ResidentWebRunner( + MockWebDevice(), flutterProject: FlutterProject.current(), debuggingOptions: DebuggingOptions.enabled(BuildInfo.profile), ipv6: true, - stayResident: true, ); expect(profileResidentWebRunner.supportsServiceProtocol, false); @@ -161,7 +119,7 @@ void main() { final BufferLogger bufferLogger = logger; expect(await residentWebRunner.run(), 1); - expect(bufferLogger.errorText, contains('This application is not configured to build on the web')); + expect(bufferLogger.errorText, contains('No application found for TargetPlatform.web_javascript')); })); test('Exits on run if target file does not exist', () => testbed.run(() async { @@ -198,8 +156,8 @@ void main() { test('Can successfully run and disconnect with --no-resident', () => testbed.run(() async { _setupMocks(); - residentWebRunner = DwdsWebRunnerFactory().createWebRunner( - mockFlutterDevice, + residentWebRunner = ResidentWebRunner( + mockWebDevice, flutterProject: FlutterProject.current(), debuggingOptions: DebuggingOptions.enabled(BuildInfo.debug), ipv6: true, @@ -234,12 +192,11 @@ void main() { })); test('Does not run main with --start-paused', () => testbed.run(() async { - residentWebRunner = DwdsWebRunnerFactory().createWebRunner( - mockFlutterDevice, + residentWebRunner = ResidentWebRunner( + mockWebDevice, flutterProject: FlutterProject.current(), debuggingOptions: DebuggingOptions.enabled(BuildInfo.debug, startPaused: true), ipv6: true, - stayResident: true, ); _setupMocks(); final Completer connectionInfoCompleter = Completer(); @@ -287,94 +244,6 @@ void main() { Usage: () => MockFlutterUsage(), })); - test('Can hot reload after attaching - experimental', () => testbed.run(() async { - _setupMocks(); - launchChromeInstance(mockChrome); - when(mockWebDevFS.update( - mainPath: anyNamed('mainPath'), - target: anyNamed('target'), - bundle: anyNamed('bundle'), - firstBuildTime: anyNamed('firstBuildTime'), - bundleFirstUpload: anyNamed('bundleFirstUpload'), - generator: anyNamed('generator'), - fullRestart: anyNamed('fullRestart'), - dillOutputPath: anyNamed('dillOutputPath'), - trackWidgetCreation: anyNamed('trackWidgetCreation'), - projectRootPath: anyNamed('projectRootPath'), - pathToReload: anyNamed('pathToReload'), - invalidatedFiles: anyNamed('invalidatedFiles'), - )).thenAnswer((Invocation invocation) async { - return UpdateFSReport(success: true) - ..invalidatedModules = ['example']; - }); - final BufferLogger bufferLogger = logger; - final Completer connectionInfoCompleter = Completer(); - unawaited(residentWebRunner.run( - connectionInfoCompleter: connectionInfoCompleter, - )); - await connectionInfoCompleter.future; - final OperationResult result = await residentWebRunner.restart(fullRestart: false); - - expect(bufferLogger.statusText, contains('Reloaded application in')); - expect(result.code, 0); - verify(mockResidentCompiler.accept()).called(2); - // ensure that analytics are sent. - verify(Usage.instance.sendEvent('hot', 'restart', parameters: { - 'cd27': 'web-javascript', - 'cd28': null, - 'cd29': 'false', - 'cd30': 'true', - })).called(1); - verify(Usage.instance.sendTiming('hot', 'web-incremental-restart', any)).called(1); - }, overrides: { - Usage: () => MockFlutterUsage(), - FeatureFlags: () => TestFeatureFlags(isWebIncrementalCompilerEnabled: true), - })); - - test('Can hot restart after attaching - experimental', () => testbed.run(() async { - _setupMocks(); - launchChromeInstance(mockChrome); - when(mockWebDevFS.update( - mainPath: anyNamed('mainPath'), - target: anyNamed('target'), - bundle: anyNamed('bundle'), - firstBuildTime: anyNamed('firstBuildTime'), - bundleFirstUpload: anyNamed('bundleFirstUpload'), - generator: anyNamed('generator'), - fullRestart: anyNamed('fullRestart'), - dillOutputPath: anyNamed('dillOutputPath'), - trackWidgetCreation: anyNamed('trackWidgetCreation'), - projectRootPath: anyNamed('projectRootPath'), - pathToReload: anyNamed('pathToReload'), - invalidatedFiles: anyNamed('invalidatedFiles'), - )).thenAnswer((Invocation invocation) async { - return UpdateFSReport(success: true) - ..invalidatedModules = ['example']; - }); - final BufferLogger bufferLogger = logger; - final Completer connectionInfoCompleter = Completer(); - unawaited(residentWebRunner.run( - connectionInfoCompleter: connectionInfoCompleter, - )); - await connectionInfoCompleter.future; - final OperationResult result = await residentWebRunner.restart(fullRestart: true); - - expect(bufferLogger.statusText, contains('Restarted application in')); - expect(result.code, 0); - verify(mockResidentCompiler.accept()).called(2); - // ensure that analytics are sent. - verify(Usage.instance.sendEvent('hot', 'restart', parameters: { - 'cd27': 'web-javascript', - 'cd28': null, - 'cd29': 'false', - 'cd30': 'true', - })).called(1); - verifyNever(Usage.instance.sendTiming('hot', 'web-incremental-restart', any)); - }, overrides: { - Usage: () => MockFlutterUsage(), - FeatureFlags: () => TestFeatureFlags(isWebIncrementalCompilerEnabled: true), - })); - test('Can hot restart after attaching', () => testbed.run(() async { _setupMocks(); final BufferLogger bufferLogger = logger; @@ -657,7 +526,7 @@ void main() { test('cleanup of resources is safe to call multiple times', () => testbed.run(() async { _setupMocks(); bool debugClosed = false; - when(mockChromeDevice.stopApp(any)).thenAnswer((Invocation invocation) async { + when(mockWebDevice.stopApp(any)).thenAnswer((Invocation invocation) async { if (debugClosed) { throw StateError('debug connection closed twice'); } @@ -695,7 +564,7 @@ void main() { test('Prints target and device name on run', () => testbed.run(() async { _setupMocks(); - when(mockChromeDevice.name).thenReturn('Chromez'); + when(mockWebDevice.name).thenReturn('Chromez'); final Completer connectionInfoCompleter = Completer(); unawaited(residentWebRunner.run( connectionInfoCompleter: connectionInfoCompleter, @@ -887,18 +756,10 @@ void main() { } class MockFlutterUsage extends Mock implements Usage {} -class MockChromeDevice extends Mock implements ChromeDevice {} +class MockWebDevice extends Mock implements ChromeDevice {} class MockBuildDaemonCreator extends Mock implements BuildDaemonCreator {} class MockFlutterWebFs extends Mock implements WebFs {} class MockDebugConnection extends Mock implements DebugConnection {} class MockAppConnection extends Mock implements AppConnection {} class MockVmService extends Mock implements VmService {} class MockStatus extends Mock implements Status {} -class MockFlutterDevice extends Mock implements FlutterDevice {} -class MockWebDevFS extends Mock implements DevFS {} -class MockResidentCompiler extends Mock implements ResidentCompiler {} -class MockChrome extends Mock implements Chrome {} -class MockChromeConnection extends Mock implements ChromeConnection {} -class MockChromeTab extends Mock implements ChromeTab {} -class MockWipConnection extends Mock implements WipConnection {} -class MockWipDebugger extends Mock implements WipDebugger {} diff --git a/packages/flutter_tools/test/general.shard/web/devfs_web_test.dart b/packages/flutter_tools/test/general.shard/web/devfs_web_test.dart index df425f9197c..44961c9c645 100644 --- a/packages/flutter_tools/test/general.shard/web/devfs_web_test.dart +++ b/packages/flutter_tools/test/general.shard/web/devfs_web_test.dart @@ -89,36 +89,28 @@ void main() { test('Handles against malformed manifest', () => testbed.run(() async { final File source = fs.file('source') ..writeAsStringSync('main() {}'); - final File sourcemap = fs.file('sourcemap') - ..writeAsStringSync('{}'); // Missing ending offset. final File manifestMissingOffset = fs.file('manifestA') - ..writeAsStringSync(json.encode({'/foo.js': { - 'code': [0], - 'sourcemap': [0], - }})); - final File manifestOutOfBounds = fs.file('manifest') - ..writeAsStringSync(json.encode({'/foo.js': { - 'code': [0, 100], - 'sourcemap': [0], - }})); + ..writeAsStringSync(json.encode({'/foo.js': [0]})); + // Non-file URI. + final File manifestNonFileScheme = fs.file('manifestA') + ..writeAsStringSync(json.encode({'/foo.js': [0, 10]})); - expect(webAssetServer.write(source, manifestMissingOffset, sourcemap), isEmpty); - expect(webAssetServer.write(source, manifestOutOfBounds, sourcemap), isEmpty); + final File manifestOutOfBounds = fs.file('manifest') + ..writeAsStringSync(json.encode({'/foo.js': [0, 100]})); + + expect(webAssetServer.write(source, manifestMissingOffset), isEmpty); + expect(webAssetServer.write(source, manifestNonFileScheme), isEmpty); + expect(webAssetServer.write(source, manifestOutOfBounds), isEmpty); })); test('serves JavaScript files from in memory cache', () => testbed.run(() async { final File source = fs.file('source') ..writeAsStringSync('main() {}'); - final File sourcemap = fs.file('sourcemap') - ..writeAsStringSync('{}'); final File manifest = fs.file('manifest') - ..writeAsStringSync(json.encode({'/foo.js': { - 'code': [0, source.lengthSync()], - 'sourcemap': [0, 2], - }})); - webAssetServer.write(source, manifest, sourcemap); + ..writeAsStringSync(json.encode({'/foo.js': [0, source.lengthSync()]})); + webAssetServer.write(source, manifest); when(request.uri).thenReturn(Uri.parse('http://foobar/foo.js')); requestController.add(request); @@ -144,14 +136,9 @@ void main() { test('handles missing JavaScript files from in memory cache', () => testbed.run(() async { final File source = fs.file('source') ..writeAsStringSync('main() {}'); - final File sourcemap = fs.file('sourcemap') - ..writeAsStringSync('{}'); final File manifest = fs.file('manifest') - ..writeAsStringSync(json.encode({'/foo.js': { - 'code': [0, source.lengthSync()], - 'sourcemap': [0, 2], - }})); - webAssetServer.write(source, manifest, sourcemap); + ..writeAsStringSync(json.encode({'/foo.js': [0, source.lengthSync()]})); + webAssetServer.write(source, manifest); when(request.uri).thenReturn(Uri.parse('http://foobar/bar.js')); requestController.add(request);