Add a real-er web restart, doctor, workflow (#33786)

diff --git a/dev/automated_tests/pubspec.yaml b/dev/automated_tests/pubspec.yaml
index c9c2dc5..2fffc24 100644
--- a/dev/automated_tests/pubspec.yaml
+++ b/dev/automated_tests/pubspec.yaml
@@ -12,7 +12,7 @@
   test: 1.6.3
 
   analyzer: 0.36.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
-  args: 1.5.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
+  args: 1.5.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
   async: 2.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
   boolean_selector: 1.0.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
   charcode: 1.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
@@ -65,4 +65,4 @@
 flutter:
   uses-material-design: true
 
-# PUBSPEC CHECKSUM: 5cc4
+# PUBSPEC CHECKSUM: f3c5
diff --git a/dev/benchmarks/complex_layout/pubspec.yaml b/dev/benchmarks/complex_layout/pubspec.yaml
index becfef6..3b0c7e4 100644
--- a/dev/benchmarks/complex_layout/pubspec.yaml
+++ b/dev/benchmarks/complex_layout/pubspec.yaml
@@ -43,7 +43,7 @@
   test: 1.6.3
 
   analyzer: 0.36.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
-  args: 1.5.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
+  args: 1.5.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
   boolean_selector: 1.0.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
   csslib: 0.16.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
   front_end: 0.1.18 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
@@ -82,4 +82,4 @@
     - packages/flutter_gallery_assets/people/square/ali.png
     - packages/flutter_gallery_assets/places/india_chettinad_silk_maker.png
 
-# PUBSPEC CHECKSUM: f699
+# PUBSPEC CHECKSUM: f69a
diff --git a/dev/benchmarks/macrobenchmarks/pubspec.yaml b/dev/benchmarks/macrobenchmarks/pubspec.yaml
index f12d528..f7f56ec 100644
--- a/dev/benchmarks/macrobenchmarks/pubspec.yaml
+++ b/dev/benchmarks/macrobenchmarks/pubspec.yaml
@@ -43,7 +43,7 @@
   test: 1.6.3
 
   analyzer: 0.36.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
-  args: 1.5.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
+  args: 1.5.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
   boolean_selector: 1.0.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
   csslib: 0.16.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
   front_end: 0.1.18 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
@@ -79,4 +79,4 @@
 flutter:
   uses-material-design: true
 
-# PUBSPEC CHECKSUM: f699
+# PUBSPEC CHECKSUM: f69a
diff --git a/dev/benchmarks/microbenchmarks/pubspec.yaml b/dev/benchmarks/microbenchmarks/pubspec.yaml
index 64dfdf3..a18484d 100644
--- a/dev/benchmarks/microbenchmarks/pubspec.yaml
+++ b/dev/benchmarks/microbenchmarks/pubspec.yaml
@@ -16,7 +16,7 @@
   test: 1.6.3
 
   analyzer: 0.36.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
-  args: 1.5.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
+  args: 1.5.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
   async: 2.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
   boolean_selector: 1.0.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
   charcode: 1.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
@@ -73,4 +73,4 @@
 flutter:
   uses-material-design: true
 
-# PUBSPEC CHECKSUM: ce27
+# PUBSPEC CHECKSUM: 1128
diff --git a/dev/bots/pubspec.yaml b/dev/bots/pubspec.yaml
index 0fa8814..3b047a6 100644
--- a/dev/bots/pubspec.yaml
+++ b/dev/bots/pubspec.yaml
@@ -7,7 +7,7 @@
 
 dependencies:
   path: 1.6.2
-  args: 1.5.1
+  args: 1.5.2
   meta: 1.1.6
   process: 3.0.9
   platform: 2.2.0
@@ -64,7 +64,7 @@
   yaml: 2.1.15 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
 
 dev_dependencies:
-  mockito: 4.0.0
+  mockito: 4.1.0
   test_api: 0.2.5
 
-# PUBSPEC CHECKSUM: 7f9d
+# PUBSPEC CHECKSUM: ee9f
diff --git a/dev/devicelab/bin/tasks/linux_chrome_dev_mode.dart b/dev/devicelab/bin/tasks/linux_chrome_dev_mode.dart
new file mode 100644
index 0000000..a11b0ab
--- /dev/null
+++ b/dev/devicelab/bin/tasks/linux_chrome_dev_mode.dart
@@ -0,0 +1,10 @@
+// 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<void> main() async {
+  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
new file mode 100644
index 0000000..a11b0ab
--- /dev/null
+++ b/dev/devicelab/bin/tasks/macos_chrome_dev_mode.dart
@@ -0,0 +1,10 @@
+// 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<void> main() async {
+  await task(createWebDevModeTest());
+}
diff --git a/dev/devicelab/bin/tasks/windows_chrome_dev_mode.dart b/dev/devicelab/bin/tasks/windows_chrome_dev_mode.dart
new file mode 100644
index 0000000..a11b0ab
--- /dev/null
+++ b/dev/devicelab/bin/tasks/windows_chrome_dev_mode.dart
@@ -0,0 +1,10 @@
+// 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<void> main() async {
+  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
new file mode 100644
index 0000000..d5a7ef2
--- /dev/null
+++ b/dev/devicelab/lib/tasks/web_dev_mode_tests.dart
@@ -0,0 +1,142 @@
+// 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 'dart:async';
+import 'dart:convert';
+import 'dart:io';
+
+import 'package:path/path.dart' as path;
+
+import '../framework/framework.dart';
+import '../framework/utils.dart';
+
+final Directory _editedFlutterGalleryDir = dir(path.join(Directory.systemTemp.path, 'edited_flutter_gallery'));
+final Directory flutterGalleryDir = dir(path.join(flutterDirectory.path, 'examples/flutter_gallery'));
+
+TaskFunction createWebDevModeTest() {
+  return () async {
+    final List<String> options = <String>[
+      '--hot', '-d', 'web', '--verbose', '--resident', '--target=lib/main_web.dart',
+    ];
+    setLocalEngineOptionIfNecessary(options);
+    int hotRestartCount = 0;
+    await inDirectory<void>(flutterDirectory, () async {
+      rmTree(_editedFlutterGalleryDir);
+      mkdirs(_editedFlutterGalleryDir);
+      recursiveCopy(flutterGalleryDir, _editedFlutterGalleryDir);
+      await inDirectory<void>(_editedFlutterGalleryDir, () async {
+        {
+          final Process packagesGet = await startProcess(
+              path.join(flutterDirectory.path, 'bin', 'flutter'),
+              <String>['packages', 'get'],
+              environment: <String, String>{
+                'FLUTTER_WEB': 'true',
+              },
+          );
+          await packagesGet.exitCode;
+          final Process process = await startProcess(
+              path.join(flutterDirectory.path, 'bin', 'flutter'),
+              <String>['run']..addAll(options),
+              environment: <String, String>{
+                'FLUTTER_WEB': 'true',
+              },
+          );
+
+          final Completer<void> stdoutDone = Completer<void>();
+          final Completer<void> stderrDone = Completer<void>();
+          process.stdout
+              .transform<String>(utf8.decoder)
+              .transform<String>(const LineSplitter())
+              .listen((String line) {
+            if (line.contains('To hot restart')) {
+              process.stdin.write('R');
+            }
+            if (line.contains('Restarted')) {
+              if (hotRestartCount == 0) {
+                // Update the file and reload again.
+                final File appDartSource = file(path.join(
+                    _editedFlutterGalleryDir.path, 'lib/gallery/app.dart',
+                ));
+                appDartSource.writeAsStringSync(
+                    appDartSource.readAsStringSync().replaceFirst(
+                        "'Flutter Gallery'", "'Updated Flutter Gallery'",
+                    )
+                );
+                process.stdin.writeln('R');
+                ++hotRestartCount;
+              } else {
+                // Quit after second hot restart.
+                process.stdin.writeln('q');
+              }
+            }
+            print('stdout: $line');
+          }, onDone: () {
+            stdoutDone.complete();
+          });
+          process.stderr
+              .transform<String>(utf8.decoder)
+              .transform<String>(const LineSplitter())
+              .listen((String line) {
+            print('stderr: $line');
+          }, onDone: () {
+            stderrDone.complete();
+          });
+
+          await Future.wait<void>(<Future<void>>[
+            stdoutDone.future,
+            stderrDone.future,
+          ]);
+          await process.exitCode;
+
+        }
+
+        // Start `flutter run` again to make sure it loads from the previous
+        // state. dev compilers loads up from previously compiled JavaScript.
+        {
+          final Process process = await startProcess(
+              path.join(flutterDirectory.path, 'bin', 'flutter'),
+              <String>['run']..addAll(options),
+              environment: <String, String>{
+                'FLUTTER_WEB': 'true',
+              },
+          );
+          final Completer<void> stdoutDone = Completer<void>();
+          final Completer<void> stderrDone = Completer<void>();
+          process.stdout
+              .transform<String>(utf8.decoder)
+              .transform<String>(const LineSplitter())
+              .listen((String line) {
+            if (line.contains('To hot restart')) {
+              process.stdin.write('R');
+            }
+            if (line.contains('Restarted')) {
+              process.stdin.writeln('q');
+            }
+            print('stdout: $line');
+          }, onDone: () {
+            stdoutDone.complete();
+          });
+          process.stderr
+              .transform<String>(utf8.decoder)
+              .transform<String>(const LineSplitter())
+              .listen((String line) {
+            print('stderr: $line');
+          }, onDone: () {
+            stderrDone.complete();
+          });
+
+          await Future.wait<void>(<Future<void>>[
+            stdoutDone.future,
+            stderrDone.future,
+          ]);
+          await process.exitCode;
+        }
+      });
+    });
+    if (hotRestartCount != 1) {
+      return TaskResult.failure(null);
+    }
+    return TaskResult.success(null);
+  };
+}
diff --git a/dev/devicelab/manifest.yaml b/dev/devicelab/manifest.yaml
index 0c976c9..581bbeb 100644
--- a/dev/devicelab/manifest.yaml
+++ b/dev/devicelab/manifest.yaml
@@ -125,6 +125,13 @@
     stage: devicelab_win
     required_agent_capabilities: ["windows/android"]
 
+  windows_chrome_dev_mode:
+    description: >
+      Run flutter web on the devicelab and hot restart.
+    stage: devicelab_win
+    required_agent_capabilities: ["windows/android"]
+    flaky: true
+
   # Android on-device tests
 
   complex_layout_scroll_perf__timeline_summary:
@@ -324,6 +331,13 @@
     stage: devicelab
     required_agent_capabilities: ["linux/android"]
 
+  linux_chrome_dev_mode:
+    description: >
+      Run flutter web on the devicelab and hot restart.
+    stage: devicelab
+    required_agent_capabilities: ["linux/android"]
+    flaky: true
+
   # iOS on-device tests
 
   flavors_test_ios:
@@ -421,6 +435,13 @@
     stage: devicelab_ios
     required_agent_capabilities: ["mac/ios"]
 
+  macos_chrome_dev_mode:
+    description: >
+      Run flutter web on the devicelab and hot restart.
+    stage: devicelab_ios
+    required_agent_capabilities: ["mac/ios"]
+    flaky: true
+
   # Tests running on Windows host
 
   flavors_test_win:
diff --git a/dev/devicelab/pubspec.yaml b/dev/devicelab/pubspec.yaml
index db2cd55..2b1e441 100644
--- a/dev/devicelab/pubspec.yaml
+++ b/dev/devicelab/pubspec.yaml
@@ -8,9 +8,9 @@
   sdk: ">=2.0.0-dev.68.0 <3.0.0"
 
 dependencies:
-  args: 1.5.1
+  args: 1.5.2
   file: 5.0.8
-  image: 2.1.3
+  image: 2.1.4
   meta: 1.1.6
   path: 1.6.2
   platform: 2.2.0
@@ -73,4 +73,4 @@
   watcher: 0.9.7+10 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
   yaml: 2.1.15 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
 
-# PUBSPEC CHECKSUM: b018
+# PUBSPEC CHECKSUM: 491a
diff --git a/dev/integration_tests/android_semantics_testing/pubspec.yaml b/dev/integration_tests/android_semantics_testing/pubspec.yaml
index 571f789..dd9f6ac 100644
--- a/dev/integration_tests/android_semantics_testing/pubspec.yaml
+++ b/dev/integration_tests/android_semantics_testing/pubspec.yaml
@@ -9,7 +9,7 @@
   test: 1.6.3
 
   analyzer: 0.36.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
-  args: 1.5.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
+  args: 1.5.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
   async: 2.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
   boolean_selector: 1.0.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
   charcode: 1.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
@@ -63,4 +63,4 @@
 flutter:
   uses-material-design: true
 
-# PUBSPEC CHECKSUM: d60e
+# PUBSPEC CHECKSUM: 820f
diff --git a/dev/integration_tests/android_views/pubspec.yaml b/dev/integration_tests/android_views/pubspec.yaml
index bf9e305..74356bd 100644
--- a/dev/integration_tests/android_views/pubspec.yaml
+++ b/dev/integration_tests/android_views/pubspec.yaml
@@ -40,7 +40,7 @@
   test: 1.6.3
 
   analyzer: 0.36.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
-  args: 1.5.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
+  args: 1.5.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
   boolean_selector: 1.0.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
   csslib: 0.16.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
   front_end: 0.1.18 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
@@ -76,4 +76,4 @@
 flutter:
   uses-material-design: true
 
-# PUBSPEC CHECKSUM: 10be
+# PUBSPEC CHECKSUM: fdbf
diff --git a/dev/integration_tests/channels/pubspec.yaml b/dev/integration_tests/channels/pubspec.yaml
index 150a768..a0a74cd 100644
--- a/dev/integration_tests/channels/pubspec.yaml
+++ b/dev/integration_tests/channels/pubspec.yaml
@@ -13,7 +13,7 @@
   test: 1.6.3
 
   analyzer: 0.36.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
-  args: 1.5.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
+  args: 1.5.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
   async: 2.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
   boolean_selector: 1.0.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
   charcode: 1.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
@@ -67,4 +67,4 @@
 flutter:
   uses-material-design: true
 
-# PUBSPEC CHECKSUM: d60e
+# PUBSPEC CHECKSUM: 820f
diff --git a/dev/integration_tests/codegen/pubspec.yaml b/dev/integration_tests/codegen/pubspec.yaml
index 229c3e4..baca4ae 100644
--- a/dev/integration_tests/codegen/pubspec.yaml
+++ b/dev/integration_tests/codegen/pubspec.yaml
@@ -35,7 +35,7 @@
   test: 1.6.3
 
   analyzer: 0.36.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
-  args: 1.5.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
+  args: 1.5.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
   boolean_selector: 1.0.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
   csslib: 0.16.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
   front_end: 0.1.18 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
@@ -74,4 +74,4 @@
 flutter:
   uses-material-design: true
 
-# PUBSPEC CHECKSUM: d60e
+# PUBSPEC CHECKSUM: 820f
diff --git a/dev/integration_tests/external_ui/pubspec.yaml b/dev/integration_tests/external_ui/pubspec.yaml
index 17e4739..8827df8 100644
--- a/dev/integration_tests/external_ui/pubspec.yaml
+++ b/dev/integration_tests/external_ui/pubspec.yaml
@@ -13,7 +13,7 @@
   test: 1.6.3
 
   analyzer: 0.36.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
-  args: 1.5.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
+  args: 1.5.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
   async: 2.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
   boolean_selector: 1.0.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
   charcode: 1.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
@@ -67,4 +67,4 @@
 flutter:
   uses-material-design: true
 
-# PUBSPEC CHECKSUM: d60e
+# PUBSPEC CHECKSUM: 820f
diff --git a/dev/integration_tests/flavors/pubspec.yaml b/dev/integration_tests/flavors/pubspec.yaml
index 8ef036a..d6a9ada 100644
--- a/dev/integration_tests/flavors/pubspec.yaml
+++ b/dev/integration_tests/flavors/pubspec.yaml
@@ -13,7 +13,7 @@
   test: 1.6.3
 
   analyzer: 0.36.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
-  args: 1.5.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
+  args: 1.5.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
   async: 2.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
   boolean_selector: 1.0.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
   charcode: 1.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
@@ -67,4 +67,4 @@
 flutter:
   uses-material-design: true
 
-# PUBSPEC CHECKSUM: d60e
+# PUBSPEC CHECKSUM: 820f
diff --git a/dev/integration_tests/image_loading/pubspec.yaml b/dev/integration_tests/image_loading/pubspec.yaml
index 09b3790..9d2f5c6 100644
--- a/dev/integration_tests/image_loading/pubspec.yaml
+++ b/dev/integration_tests/image_loading/pubspec.yaml
@@ -5,10 +5,10 @@
   flutter:
     sdk: flutter
   test: 1.6.3
-  mockito: 4.0.0
+  mockito: 4.1.0
 
   analyzer: 0.36.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
-  args: 1.5.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
+  args: 1.5.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
   async: 2.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
   boolean_selector: 1.0.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
   charcode: 1.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
@@ -60,4 +60,4 @@
 flutter:
   uses-material-design: true
 
-# PUBSPEC CHECKSUM: bf1e
+# PUBSPEC CHECKSUM: 8420
diff --git a/dev/integration_tests/platform_interaction/pubspec.yaml b/dev/integration_tests/platform_interaction/pubspec.yaml
index 8171255..9e483e4 100644
--- a/dev/integration_tests/platform_interaction/pubspec.yaml
+++ b/dev/integration_tests/platform_interaction/pubspec.yaml
@@ -13,7 +13,7 @@
   test: 1.6.3
 
   analyzer: 0.36.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
-  args: 1.5.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
+  args: 1.5.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
   async: 2.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
   boolean_selector: 1.0.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
   charcode: 1.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
@@ -67,4 +67,4 @@
 flutter:
   uses-material-design: true
 
-# PUBSPEC CHECKSUM: d60e
+# PUBSPEC CHECKSUM: 820f
diff --git a/dev/integration_tests/simple_codegen/pubspec.yaml b/dev/integration_tests/simple_codegen/pubspec.yaml
index 3f13bf2..024082e 100644
--- a/dev/integration_tests/simple_codegen/pubspec.yaml
+++ b/dev/integration_tests/simple_codegen/pubspec.yaml
@@ -5,7 +5,7 @@
   build: 1.1.4
 
   analyzer: 0.36.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
-  args: 1.5.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
+  args: 1.5.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
   async: 2.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
   charcode: 1.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
   collection: 1.14.11 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
@@ -32,4 +32,4 @@
   # The pub client defaults to an <2.0.0 sdk constraint which we need to explicitly overwrite.
   sdk: ">=2.0.0-dev.68.0 <3.0.0"
 
-# PUBSPEC CHECKSUM: 669a
+# PUBSPEC CHECKSUM: 069b
diff --git a/dev/integration_tests/ui/pubspec.yaml b/dev/integration_tests/ui/pubspec.yaml
index 3454f9d..64ccf84 100644
--- a/dev/integration_tests/ui/pubspec.yaml
+++ b/dev/integration_tests/ui/pubspec.yaml
@@ -6,7 +6,7 @@
   sdk: ">=2.0.0-dev.68.0 <3.0.0"
 
 dependencies:
-  image: 2.1.3
+  image: 2.1.4
   flutter:
     sdk: flutter
   flutter_driver:
@@ -15,7 +15,7 @@
 
   analyzer: 0.36.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
   archive: 2.0.9 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
-  args: 1.5.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
+  args: 1.5.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
   async: 2.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
   boolean_selector: 1.0.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
   charcode: 1.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
@@ -77,4 +77,4 @@
 flutter:
   uses-material-design: true
 
-# PUBSPEC CHECKSUM: 1b28
+# PUBSPEC CHECKSUM: 772a
diff --git a/dev/manual_tests/pubspec.yaml b/dev/manual_tests/pubspec.yaml
index 703c255..4ceeef2 100644
--- a/dev/manual_tests/pubspec.yaml
+++ b/dev/manual_tests/pubspec.yaml
@@ -17,7 +17,7 @@
   flutter_test:
     sdk: flutter
 
-  mockito: 4.0.0
+  mockito: 4.1.0
 
   async: 2.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
   boolean_selector: 1.0.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
@@ -36,4 +36,4 @@
 flutter:
   uses-material-design: true
 
-# PUBSPEC CHECKSUM: 949f
+# PUBSPEC CHECKSUM: 0ba0
diff --git a/dev/snippets/pubspec.yaml b/dev/snippets/pubspec.yaml
index f050291..9422e4a 100644
--- a/dev/snippets/pubspec.yaml
+++ b/dev/snippets/pubspec.yaml
@@ -13,7 +13,7 @@
   nodoc: true
 
 dependencies:
-  args: 1.5.1
+  args: 1.5.2
   dart_style: 1.2.7
   meta: 1.1.6
   platform: 2.2.0
@@ -98,4 +98,4 @@
   vm_service_client: 0.2.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
   web_socket_channel: 1.0.9 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
 
-# PUBSPEC CHECKSUM: 6b2c
+# PUBSPEC CHECKSUM: 052d
diff --git a/dev/tools/gen_keycodes/pubspec.yaml b/dev/tools/gen_keycodes/pubspec.yaml
index 0b1f935..cabc938 100644
--- a/dev/tools/gen_keycodes/pubspec.yaml
+++ b/dev/tools/gen_keycodes/pubspec.yaml
@@ -6,7 +6,7 @@
   sdk: ">=2.0.0-dev.68.0 <3.0.0"
 
 dependencies:
-  args: 1.5.1
+  args: 1.5.2
   http: 0.12.0+2
   path: 1.6.2
   platform: 2.2.0
@@ -22,4 +22,4 @@
   term_glyph: 1.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
   typed_data: 1.1.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
 
-# PUBSPEC CHECKSUM: 8bd3
+# PUBSPEC CHECKSUM: 24d4
diff --git a/dev/tools/pubspec.yaml b/dev/tools/pubspec.yaml
index bf6f713..be0a7f7 100644
--- a/dev/tools/pubspec.yaml
+++ b/dev/tools/pubspec.yaml
@@ -7,7 +7,7 @@
 
 dependencies:
   archive: 2.0.9
-  args: 1.5.1
+  args: 1.5.2
   http: 0.12.0+2
   intl: 0.15.8
   meta: 1.1.6
@@ -30,11 +30,11 @@
 
 dev_dependencies:
   test_api: 0.2.5
-  mockito: 4.0.0
+  mockito: 4.1.0
 
   boolean_selector: 1.0.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
   matcher: 0.12.5 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
   stack_trace: 1.9.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
   stream_channel: 2.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
 
-# PUBSPEC CHECKSUM: 470f
+# PUBSPEC CHECKSUM: a711
diff --git a/dev/tools/vitool/pubspec.yaml b/dev/tools/vitool/pubspec.yaml
index c9c6437..e7fc5a7 100644
--- a/dev/tools/vitool/pubspec.yaml
+++ b/dev/tools/vitool/pubspec.yaml
@@ -9,7 +9,7 @@
   sdk: ">=2.0.0-dev.68.0 <3.0.0"
 
 dependencies:
-  args: 1.5.1
+  args: 1.5.2
   vector_math: 2.0.8
   xml: 3.5.0
 
@@ -37,4 +37,4 @@
   term_glyph: 1.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
   test_api: 0.2.5 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
 
-# PUBSPEC CHECKSUM: b74d
+# PUBSPEC CHECKSUM: 3d4e
diff --git a/examples/catalog/pubspec.yaml b/examples/catalog/pubspec.yaml
index f5f77ec..70e2c7e 100644
--- a/examples/catalog/pubspec.yaml
+++ b/examples/catalog/pubspec.yaml
@@ -23,7 +23,7 @@
   test: 1.6.3
 
   analyzer: 0.36.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
-  args: 1.5.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
+  args: 1.5.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
   async: 2.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
   boolean_selector: 1.0.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
   charcode: 1.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
@@ -73,4 +73,4 @@
 flutter:
   uses-material-design: true
 
-# PUBSPEC CHECKSUM: 5ff8
+# PUBSPEC CHECKSUM: 25f9
diff --git a/examples/flutter_gallery/pubspec.yaml b/examples/flutter_gallery/pubspec.yaml
index 780e46c..c5aab9f 100644
--- a/examples/flutter_gallery/pubspec.yaml
+++ b/examples/flutter_gallery/pubspec.yaml
@@ -12,9 +12,9 @@
   intl: 0.15.8
   connectivity: 0.4.3+2
   string_scanner: 1.0.4
-  url_launcher: 5.0.2
+  url_launcher: 5.0.3
   cupertino_icons: 0.1.2
-  video_player: 0.10.1+2
+  video_player: 0.10.1+3
   scoped_model: 1.0.1
   shrine_images: 1.1.2
 
@@ -39,7 +39,7 @@
   test: 1.6.3
 
   analyzer: 0.36.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
-  args: 1.5.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
+  args: 1.5.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
   async: 2.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
   boolean_selector: 1.0.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
   convert: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
@@ -253,4 +253,4 @@
       - asset: packages/flutter_gallery_assets/fonts/merriweather/Merriweather-Regular.ttf
       - asset: packages/flutter_gallery_assets/fonts/merriweather/Merriweather-Light.ttf
 
-# PUBSPEC CHECKSUM: 8760
+# PUBSPEC CHECKSUM: 7863
diff --git a/examples/platform_channel/pubspec.yaml b/examples/platform_channel/pubspec.yaml
index 9433dfa..50bc7d5 100644
--- a/examples/platform_channel/pubspec.yaml
+++ b/examples/platform_channel/pubspec.yaml
@@ -21,7 +21,7 @@
   test: 1.6.3
 
   analyzer: 0.36.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
-  args: 1.5.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
+  args: 1.5.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
   async: 2.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
   boolean_selector: 1.0.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
   charcode: 1.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
@@ -72,4 +72,4 @@
 flutter:
   uses-material-design: true
 
-# PUBSPEC CHECKSUM: 5ff8
+# PUBSPEC CHECKSUM: 25f9
diff --git a/examples/stocks/pubspec.yaml b/examples/stocks/pubspec.yaml
index e9aea00..f10e89a 100644
--- a/examples/stocks/pubspec.yaml
+++ b/examples/stocks/pubspec.yaml
@@ -15,7 +15,7 @@
   isolate: 2.0.2
 
   analyzer: 0.36.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
-  args: 1.5.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
+  args: 1.5.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
   async: 2.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
   charcode: 1.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
   collection: 1.14.11 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
@@ -62,4 +62,4 @@
 flutter:
   uses-material-design: true
 
-# PUBSPEC CHECKSUM: 63b7
+# PUBSPEC CHECKSUM: 69b8
diff --git a/packages/flutter/pubspec.yaml b/packages/flutter/pubspec.yaml
index a035993..e4afaeb 100644
--- a/packages/flutter/pubspec.yaml
+++ b/packages/flutter/pubspec.yaml
@@ -22,7 +22,7 @@
     sdk: flutter
   flutter_goldens:
     sdk: flutter
-  mockito: 4.0.0
+  mockito: 4.1.0
 
   async: 2.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
   boolean_selector: 1.0.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
@@ -42,4 +42,4 @@
   term_glyph: 1.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
   test_api: 0.2.5 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
 
-# PUBSPEC CHECKSUM: 94dc
+# PUBSPEC CHECKSUM: 45dd
diff --git a/packages/flutter_driver/pubspec.yaml b/packages/flutter_driver/pubspec.yaml
index eb36b72..70c2679 100644
--- a/packages/flutter_driver/pubspec.yaml
+++ b/packages/flutter_driver/pubspec.yaml
@@ -43,7 +43,7 @@
   vector_math: 2.0.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
 
 dev_dependencies:
-  mockito: 4.0.0
+  mockito: 4.1.0
   quiver: 2.0.3
 
-# PUBSPEC CHECKSUM: b48d
+# PUBSPEC CHECKSUM: ef8e
diff --git a/packages/flutter_goldens/pubspec.yaml b/packages/flutter_goldens/pubspec.yaml
index 2a53afc..cf846a1 100644
--- a/packages/flutter_goldens/pubspec.yaml
+++ b/packages/flutter_goldens/pubspec.yaml
@@ -33,6 +33,6 @@
   vector_math: 2.0.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
 
 dev_dependencies:
-  mockito: 4.0.0
+  mockito: 4.1.0
 
-# PUBSPEC CHECKSUM: 046a
+# PUBSPEC CHECKSUM: 926b
diff --git a/packages/flutter_localizations/pubspec.yaml b/packages/flutter_localizations/pubspec.yaml
index d73ddef..7c50e3d 100644
--- a/packages/flutter_localizations/pubspec.yaml
+++ b/packages/flutter_localizations/pubspec.yaml
@@ -20,7 +20,7 @@
 dev_dependencies:
   flutter_test:
     sdk: flutter
-  mockito: 4.0.0
+  mockito: 4.1.0
 
   async: 2.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
   boolean_selector: 1.0.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
@@ -35,4 +35,4 @@
   term_glyph: 1.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
   test_api: 0.2.5 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
 
-# PUBSPEC CHECKSUM: 4ddd
+# PUBSPEC CHECKSUM: c3de
diff --git a/packages/flutter_tools/BUILD.gn b/packages/flutter_tools/BUILD.gn
index 48b481d..79988db 100644
--- a/packages/flutter_tools/BUILD.gn
+++ b/packages/flutter_tools/BUILD.gn
@@ -31,6 +31,8 @@
     "//third_party/dart-pkg/pub/json_schema",
     "//third_party/dart-pkg/pub/linter",
     "//third_party/dart-pkg/pub/meta",
+    "//third_party/dart-pkg/pub/mime",
+    "//third_party/dart-pkg/pub/multi_server_socket",
     "//third_party/dart-pkg/pub/multicast_dns",
     "//third_party/dart-pkg/pub/mustache",
     "//third_party/dart-pkg/pub/package_config",
@@ -38,12 +40,16 @@
     "//third_party/dart-pkg/pub/platform",
     "//third_party/dart-pkg/pub/process",
     "//third_party/dart-pkg/pub/quiver",
+    "//third_party/dart-pkg/pub/shelf_packages_handler",
+    "//third_party/dart-pkg/pub/shelf_static",
     "//third_party/dart-pkg/pub/stack_trace",
     "//third_party/dart-pkg/pub/test",
     "//third_party/dart-pkg/pub/usage",
     "//third_party/dart-pkg/pub/web_socket_channel",
+   "//third_party/dart-pkg/pub/webkit_inspection_protocol",
     "//third_party/dart-pkg/pub/xml",
     "//third_party/dart-pkg/pub/yaml",
+    "//third_party/dart-pkg/pub/node_preamble",
   ]
 }
 
diff --git a/packages/flutter_tools/lib/src/application_package.dart b/packages/flutter_tools/lib/src/application_package.dart
index 96bc9b5..60f0276 100644
--- a/packages/flutter_tools/lib/src/application_package.dart
+++ b/packages/flutter_tools/lib/src/application_package.dart
@@ -56,7 +56,7 @@
         return applicationBinary == null
             ? MacOSApp.fromMacOSProject(FlutterProject.current().macos)
             : MacOSApp.fromPrebuiltApp(applicationBinary);
-      case TargetPlatform.web:
+      case TargetPlatform.web_javascript:
         return WebApplicationPackage(FlutterProject.current());
       case TargetPlatform.linux_x64:
         return applicationBinary == null
@@ -411,7 +411,7 @@
       case TargetPlatform.linux_x64:
       case TargetPlatform.windows_x64:
       case TargetPlatform.tester:
-      case TargetPlatform.web:
+      case TargetPlatform.web_javascript:
         return null;
     }
     return null;
diff --git a/packages/flutter_tools/lib/src/artifacts.dart b/packages/flutter_tools/lib/src/artifacts.dart
index 259ba67..26f6ee7 100644
--- a/packages/flutter_tools/lib/src/artifacts.dart
+++ b/packages/flutter_tools/lib/src/artifacts.dart
@@ -136,7 +136,7 @@
       case TargetPlatform.windows_x64:
       case TargetPlatform.fuchsia:
       case TargetPlatform.tester:
-      case TargetPlatform.web:
+      case TargetPlatform.web_javascript:
         return _getHostArtifactPath(artifact, platform, mode);
     }
     assert(false, 'Invalid platform $platform.');
@@ -238,7 +238,7 @@
       case TargetPlatform.windows_x64:
       case TargetPlatform.fuchsia:
       case TargetPlatform.tester:
-      case TargetPlatform.web:
+      case TargetPlatform.web_javascript:
         assert(mode == null, 'Platform $platform does not support different build modes.');
         return fs.path.join(engineDir, platformName);
       case TargetPlatform.ios:
diff --git a/packages/flutter_tools/lib/src/build_info.dart b/packages/flutter_tools/lib/src/build_info.dart
index 8c8e94f..8379e81 100644
--- a/packages/flutter_tools/lib/src/build_info.dart
+++ b/packages/flutter_tools/lib/src/build_info.dart
@@ -243,7 +243,7 @@
   windows_x64,
   fuchsia,
   tester,
-  web,
+  web_javascript,
 }
 
 /// iOS target device architecture.
@@ -303,8 +303,8 @@
       return 'fuchsia';
     case TargetPlatform.tester:
       return 'flutter-tester';
-    case TargetPlatform.web:
-      return 'web';
+    case TargetPlatform.web_javascript:
+      return 'web-javascript';
   }
   assert(false);
   return null;
@@ -328,8 +328,8 @@
       return TargetPlatform.linux_x64;
     case 'windows-x64':
       return TargetPlatform.windows_x64;
-    case 'web':
-      return TargetPlatform.web;
+    case 'web-javascript':
+      return TargetPlatform.web_javascript;
   }
   assert(platform != null);
   return null;
diff --git a/packages/flutter_tools/lib/src/commands/run.dart b/packages/flutter_tools/lib/src/commands/run.dart
index 55292dc..50c609d 100644
--- a/packages/flutter_tools/lib/src/commands/run.dart
+++ b/packages/flutter_tools/lib/src/commands/run.dart
@@ -403,7 +403,7 @@
     // in a "hot mode".
     final bool webMode = !FlutterVersion.instance.isStable
       && devices.length == 1
-      && await devices.single.targetPlatform == TargetPlatform.web
+      && await devices.single.targetPlatform == TargetPlatform.web_javascript
       && hotMode;
 
     ResidentRunner runner;
diff --git a/packages/flutter_tools/lib/src/commands/update_packages.dart b/packages/flutter_tools/lib/src/commands/update_packages.dart
index 6f08ce3..9c80937 100644
--- a/packages/flutter_tools/lib/src/commands/update_packages.dart
+++ b/packages/flutter_tools/lib/src/commands/update_packages.dart
@@ -22,7 +22,10 @@
 const Map<String, String> _kManuallyPinnedDependencies = <String, String>{
   // Add pinned packages here.
   'flutter_gallery_assets': '0.1.8', // See //examples/flutter_gallery/pubspec.yaml
-  'build_daemon': '0.6.1',
+  'build_daemon': '0.6.1', // Crashes at 1.0
+  'test': '1.6.3',     //  | Tests are timing out at 1.6.4 https://github.com/flutter/flutter/issues/33823
+  'test_api': '0.2.5', //  |
+  'test_core': '0.2.5' //  |
 };
 
 class UpdatePackagesCommand extends FlutterCommand {
@@ -1106,7 +1109,16 @@
   overrides.writeln('dependency_overrides:');
   if (_kManuallyPinnedDependencies.isNotEmpty) {
     printStatus('WARNING: the following packages use hard-coded version constraints:');
+    final Set<String> allTransitive = <String>{
+      for (PubspecDependency dependency in dependencies)
+        dependency.name
+    };
     for (String package in _kManuallyPinnedDependencies.keys) {
+      // Don't add pinned dependency if it is not in the set of all transitive dependencies.
+      if (!allTransitive.contains(package)) {
+        printStatus('Skipping $package because it was not transitive');
+        continue;
+      }
       final String version = _kManuallyPinnedDependencies[package];
       result.writeln('  $package: $version');
       printStatus('  - $package: $version');
diff --git a/packages/flutter_tools/lib/src/context_runner.dart b/packages/flutter_tools/lib/src/context_runner.dart
index 182130a..8d7a0eb 100644
--- a/packages/flutter_tools/lib/src/context_runner.dart
+++ b/packages/flutter_tools/lib/src/context_runner.dart
@@ -43,8 +43,9 @@
 import 'run_hot.dart';
 import 'usage.dart';
 import 'version.dart';
+import 'web/chrome.dart';
 import 'web/compile.dart';
-import 'web/web_device.dart';
+import 'web/workflow.dart';
 import 'windows/windows_workflow.dart';
 
 Future<T> runInContext<T>(
@@ -100,6 +101,7 @@
       Usage: () => Usage(),
       UserMessages: () => UserMessages(),
       WebCompiler: () => const WebCompiler(),
+      WebWorkflow: () => const WebWorkflow(),
       WindowsWorkflow: () => const WindowsWorkflow(),
       Xcode: () => Xcode(),
       XcodeValidator: () => const XcodeValidator(),
diff --git a/packages/flutter_tools/lib/src/device.dart b/packages/flutter_tools/lib/src/device.dart
index fa0ae74..01aca67 100644
--- a/packages/flutter_tools/lib/src/device.dart
+++ b/packages/flutter_tools/lib/src/device.dart
@@ -23,6 +23,7 @@
 import 'project.dart';
 import 'tester/flutter_tester.dart';
 import 'web/web_device.dart';
+import 'web/workflow.dart';
 import 'windows/windows_device.dart';
 
 DeviceManager get deviceManager => context.get<DeviceManager>();
diff --git a/packages/flutter_tools/lib/src/doctor.dart b/packages/flutter_tools/lib/src/doctor.dart
index 1fff8d7..50cf645 100644
--- a/packages/flutter_tools/lib/src/doctor.dart
+++ b/packages/flutter_tools/lib/src/doctor.dart
@@ -33,6 +33,8 @@
 import 'tester/flutter_tester.dart';
 import 'version.dart';
 import 'vscode/vscode_validator.dart';
+import 'web/web_validator.dart';
+import 'web/workflow.dart';
 import 'windows/windows_workflow.dart';
 
 Doctor get doctor => context.get<Doctor>();
@@ -66,6 +68,9 @@
       if (iosWorkflow.appliesToHostPlatform)
         _validators.add(iosValidator);
 
+      if (webWorkflow.appliesToHostPlatform)
+        _validators.add(const WebValidator());
+
       final List<DoctorValidator> ideValidators = <DoctorValidator>[];
       ideValidators.addAll(AndroidStudioValidator.allValidators);
       ideValidators.addAll(IntelliJValidator.installedValidators);
@@ -278,6 +283,8 @@
 
 /// A series of tools and required install steps for a target platform (iOS or Android).
 abstract class Workflow {
+  const Workflow();
+
   /// Whether the workflow applies to this platform (as in, should we ever try and use it).
   bool get appliesToHostPlatform;
 
diff --git a/packages/flutter_tools/lib/src/project.dart b/packages/flutter_tools/lib/src/project.dart
index ed9ae19..6b51446 100644
--- a/packages/flutter_tools/lib/src/project.dart
+++ b/packages/flutter_tools/lib/src/project.dart
@@ -20,7 +20,7 @@
 import 'ios/xcodeproj.dart' as xcode;
 import 'plugins.dart';
 import 'template.dart';
-import 'web/web_device.dart';
+import 'web/workflow.dart';
 
 /// Represents the contents of a Flutter project at the specified [directory].
 ///
@@ -188,7 +188,7 @@
     if (flutterDesktopEnabled && macos.existsSync()) {
       await macos.ensureReadyForPlatformSpecificTooling();
     }
-    if (flutterWebEnabled) {
+    if (flutterWebEnabled && web.existsSync()) {
       await web.ensureReadyForPlatformSpecificTooling();
     }
     await injectPlugins(this, checkProjects: checkProjects);
diff --git a/packages/flutter_tools/lib/src/resident_web_runner.dart b/packages/flutter_tools/lib/src/resident_web_runner.dart
index 1652628..4777b21 100644
--- a/packages/flutter_tools/lib/src/resident_web_runner.dart
+++ b/packages/flutter_tools/lib/src/resident_web_runner.dart
@@ -5,11 +5,14 @@
 import 'dart:async';
 
 import 'package:meta/meta.dart';
+import 'package:webkit_inspection_protocol/webkit_inspection_protocol.dart';
 
 import 'asset.dart';
 import 'base/common.dart';
 import 'base/file_system.dart';
+import 'base/logger.dart';
 import 'base/terminal.dart';
+import 'base/utils.dart';
 import 'build_info.dart';
 import 'bundle.dart';
 import 'dart/package_map.dart';
@@ -19,8 +22,8 @@
 import 'resident_runner.dart';
 import 'run_hot.dart';
 import 'web/asset_server.dart';
+import 'web/chrome.dart';
 import 'web/compile.dart';
-import 'web/web_device.dart';
 
 /// A hot-runner which handles browser specific delegation.
 class ResidentWebRunner extends ResidentRunner {
@@ -44,6 +47,7 @@
   WebAssetServer _server;
   ProjectFileInvalidator projectFileInvalidator;
   DateTime _lastCompiled;
+  WipConnection _connection;
   final FlutterProject flutterProject;
 
   @override
@@ -58,12 +62,16 @@
   }
 
   @override
-  Future<void> cleanupAfterSignal() {
+  Future<void> cleanupAfterSignal() async {
+    await _connection.sendCommand('Browser.close');
+    _connection = null;
     return _server?.dispose();
   }
 
   @override
-  Future<void> cleanupAtFinish() {
+  Future<void> cleanupAtFinish() async {
+    await _connection?.sendCommand('Browser.close');
+    _connection = null;
     return _server?.dispose();
   }
 
@@ -128,7 +136,15 @@
     await _server.initialize();
 
     // Step 3: Spawn an instance of Chrome and direct it to the created server.
-    await chromeLauncher.launch('http:localhost:${_server.port}');
+    final String url = 'http://localhost:${_server.port}';
+    final Chrome chrome = await chromeLauncher.launch(url);
+    final ChromeTab chromeTab = await chrome.chromeConnection.getTab((ChromeTab chromeTab) {
+      return chromeTab.url.contains(url); // we don't care about trailing slashes or #
+    });
+    _connection = await chromeTab.connect();
+    _connection.onClose.listen((WipConnection connection) {
+      appFinished();
+    });
 
     // We don't support the debugging proxy yet.
     appStartedCompleter?.complete();
@@ -139,23 +155,39 @@
   }
 
   @override
-  Future<OperationResult> restart(
-      {bool fullRestart = false,
-      bool pauseAfterRestart = false,
-      String reason,
-      bool benchmarkMode = false}) async {
-    final List<Uri> invalidatedSources = ProjectFileInvalidator.findInvalidated(
-      lastCompiled: _lastCompiled,
-      urisToMonitor: <Uri>[
-        for (FileSystemEntity entity in flutterProject.directory
-            .childDirectory('lib')
-            .listSync(recursive: true))
-          if (entity is File && entity.path.endsWith('.dart')) entity.uri
-      ], // Add new class to track this for web.
-      packagesPath: PackageMap.globalPackagesPath,
+  Future<OperationResult> 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: timeoutConfiguration.fastOperation,
+      progressId: 'hot.restart',
     );
-    await webCompilationProxy.invalidate(inputs: invalidatedSources);
-    printStatus('Sources updated, refresh browser');
-    return OperationResult.ok;
+    OperationResult result = OperationResult.ok;
+    try {
+      final List<Uri> invalidatedSources = ProjectFileInvalidator.findInvalidated(
+        lastCompiled: _lastCompiled,
+        urisToMonitor: <Uri>[
+          for (FileSystemEntity entity in flutterProject.directory
+              .childDirectory('lib')
+              .listSync(recursive: true))
+            if (entity is File && entity.path.endsWith('.dart')) entity.uri
+        ], // Add new class to track this for web.
+        packagesPath: PackageMap.globalPackagesPath,
+      );
+      await webCompilationProxy.invalidate(inputs: invalidatedSources);
+      await _connection.sendCommand('Page.reload');
+      await Future<void>.delayed(const Duration(milliseconds: 150));
+    } catch (err) {
+      result = OperationResult(1, err.toString());
+    } finally {
+      printStatus('Restarted application in ${getElapsedAsMilliseconds(timer.elapsed)}.');
+      status.cancel();
+    }
+    return result;
   }
 }
diff --git a/packages/flutter_tools/lib/src/runner/flutter_command.dart b/packages/flutter_tools/lib/src/runner/flutter_command.dart
index bd00937..b39e674 100644
--- a/packages/flutter_tools/lib/src/runner/flutter_command.dart
+++ b/packages/flutter_tools/lib/src/runner/flutter_command.dart
@@ -670,7 +670,7 @@
     case TargetPlatform.android_x64:
     case TargetPlatform.android_x86:
       return DevelopmentArtifact.android;
-    case TargetPlatform.web:
+    case TargetPlatform.web_javascript:
       return DevelopmentArtifact.web;
     case TargetPlatform.ios:
       return DevelopmentArtifact.iOS;
diff --git a/packages/flutter_tools/lib/src/web/asset_server.dart b/packages/flutter_tools/lib/src/web/asset_server.dart
index d4ad143..730692c 100644
--- a/packages/flutter_tools/lib/src/web/asset_server.dart
+++ b/packages/flutter_tools/lib/src/web/asset_server.dart
@@ -170,7 +170,6 @@
 
   Future<void> _completeRequest(HttpRequest request, File file,
       [String contentType = 'text']) async {
-    printTrace('looking for ${request.uri} at ${file.path}');
     if (!file.existsSync()) {
       request.response.statusCode = HttpStatus.notFound;
       await request.response.close();
diff --git a/packages/flutter_tools/lib/src/web/chrome.dart b/packages/flutter_tools/lib/src/web/chrome.dart
new file mode 100644
index 0000000..40be47f
--- /dev/null
+++ b/packages/flutter_tools/lib/src/web/chrome.dart
@@ -0,0 +1,182 @@
+// 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 'dart:async';
+
+import 'package:webkit_inspection_protocol/webkit_inspection_protocol.dart';
+
+import '../base/common.dart';
+import '../base/context.dart';
+import '../base/file_system.dart';
+import '../base/io.dart';
+import '../base/os.dart';
+import '../base/platform.dart';
+import '../base/process_manager.dart';
+import '../convert.dart';
+import '../globals.dart';
+
+/// The [ChromeLauncher] instance.
+ChromeLauncher get chromeLauncher => context.get<ChromeLauncher>();
+
+/// An environment variable used to override the location of chrome.
+const String kChromeEnvironment = 'CHROME_EXECUTABLE';
+
+/// The expected executable name on linux.
+const String kLinuxExecutable = 'google-chrome';
+
+/// The expected executable name on macOS.
+const String kMacOSExecutable =
+    '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome';
+
+/// The expected executable name on Windows.
+const String kWindowsExecutable = r'Google\Chrome\Application\chrome.exe';
+
+/// The possible locations where the chrome executable can be located on windows.
+final List<String> kWindowsPrefixes = <String>[
+  platform.environment['LOCALAPPDATA'],
+  platform.environment['PROGRAMFILES'],
+  platform.environment['PROGRAMFILES(X86)']
+];
+
+/// Find the chrome executable on the current platform.
+///
+/// Does not verify whether the executable exists.
+String findChromeExecutable() {
+  if (platform.environment.containsKey(kChromeEnvironment)) {
+    return platform.environment[kChromeEnvironment];
+  }
+  if (platform.isLinux) {
+    return kLinuxExecutable;
+  }
+  if (platform.isMacOS) {
+    return kMacOSExecutable;
+  }
+  if (platform.isWindows) {
+    final String windowsPrefix = kWindowsPrefixes.firstWhere((String prefix) {
+      if (prefix == null) {
+        return false;
+      }
+      final String path = fs.path.join(prefix, kWindowsExecutable);
+      return fs.file(path).existsSync();
+    }, orElse: () => '.');
+    return fs.path.join(windowsPrefix, kWindowsExecutable);
+  }
+  throwToolExit('Platform ${platform.operatingSystem} is not supported.');
+  return null;
+}
+
+/// Responsible for launching chrome with devtools configured.
+class ChromeLauncher {
+  const ChromeLauncher();
+
+  static final Completer<Chrome> _currentCompleter = Completer<Chrome>();
+
+  /// Launch the chrome browser to a particular `host` page.
+  Future<Chrome> launch(String url) async {
+    final String chromeExecutable = findChromeExecutable();
+    final Directory dataDir = fs.systemTempDirectory.createTempSync();
+    final int port = await os.findFreePort();
+    final List<String> args = <String>[
+      chromeExecutable,
+      // Using a tmp directory ensures that a new instance of chrome launches
+      // allowing for the remote debug port to be enabled.
+      '--user-data-dir=${dataDir.path}',
+      '--remote-debugging-port=$port',
+      // When the DevTools has focus we don't want to slow down the application.
+      '--disable-background-timer-throttling',
+      // Since we are using a temp profile, disable features that slow the
+      // Chrome launch.
+      '--disable-extensions',
+      '--disable-popup-blocking',
+      '--bwsi',
+      '--no-first-run',
+      '--no-default-browser-check',
+      '--disable-default-apps',
+      '--disable-translate',
+      url,
+    ];
+    final Process process = await processManager.start(args);
+
+    // Wait until the DevTools are listening before trying to connect.
+    await process.stderr
+        .transform(utf8.decoder)
+        .transform(const LineSplitter())
+        .firstWhere((String line) => line.startsWith('DevTools listening'))
+        .timeout(const Duration(seconds: 60), onTimeout: () {
+          throwToolExit('Unable to connect to Chrome DevTools.');
+          return null;
+        });
+
+    return _connect(Chrome._(
+      port,
+      ChromeConnection('localhost', port),
+      process: process,
+      dataDir: dataDir,
+    ));
+  }
+
+  static Future<Chrome> _connect(Chrome chrome) async {
+    if (_currentCompleter.isCompleted) {
+      throwToolExit('Only one instance of chrome can be started.');
+    }
+    // The connection is lazy. Try a simple call to make sure the provided
+    // connection is valid.
+    try {
+      await chrome.chromeConnection.getTabs();
+    } catch (e) {
+      await chrome.close();
+      throwToolExit(
+          'Unable to connect to Chrome debug port: ${chrome.debugPort}\n $e');
+    }
+    _currentCompleter.complete(chrome);
+    return chrome;
+  }
+
+  /// Connects to an instance of Chrome with an open debug port.
+  static Future<Chrome> fromExisting(int port) async =>
+      _connect(Chrome._(port, ChromeConnection('localhost', port)));
+
+  static Future<Chrome> get connectedInstance => _currentCompleter.future;
+}
+
+/// A class for managing an instance of Chrome.
+class Chrome {
+  const Chrome._(
+    this.debugPort,
+    this.chromeConnection, {
+    Process process,
+    Directory dataDir,
+  })  : _process = process,
+        _dataDir = dataDir;
+
+  final int debugPort;
+  final Process _process;
+  final Directory _dataDir;
+  final ChromeConnection chromeConnection;
+
+  static Completer<Chrome> _currentCompleter = Completer<Chrome>();
+
+  Future<void> close() async {
+    if (_currentCompleter.isCompleted) {
+      _currentCompleter = Completer<Chrome>();
+    }
+    chromeConnection.close();
+    _process?.kill(ProcessSignal.SIGKILL);
+    await _process?.exitCode;
+    try {
+      // Chrome starts another process as soon as it dies that modifies the
+      // profile information. Give it some time before attempting to delete
+      // the directory.
+      await Future<void>.delayed(const Duration(milliseconds: 500));
+    } catch (_) {
+      // Silently fail if we can't clean up the profile information.
+    } finally {
+      try {
+        await _dataDir?.delete(recursive: true);
+      } on FileSystemException {
+        printError('failed to delete temporary profile at ${_dataDir.path}');
+      }
+    }
+  }
+}
diff --git a/packages/flutter_tools/lib/src/web/web_device.dart b/packages/flutter_tools/lib/src/web/web_device.dart
index d8ce9ce..b267ca4 100644
--- a/packages/flutter_tools/lib/src/web/web_device.dart
+++ b/packages/flutter_tools/lib/src/web/web_device.dart
@@ -5,30 +5,18 @@
 import '../application_package.dart';
 import '../asset.dart';
 import '../base/common.dart';
-import '../base/context.dart';
 import '../base/file_system.dart';
 import '../base/io.dart';
 import '../base/logger.dart';
-import '../base/platform.dart';
 import '../base/process_manager.dart';
 import '../build_info.dart';
 import '../bundle.dart';
 import '../device.dart';
 import '../globals.dart';
 import '../project.dart';
-import '../version.dart';
 import '../web/compile.dart';
-
-ChromeLauncher get chromeLauncher => context.get<ChromeLauncher>();
-
-/// Only launch or display web devices if `FLUTTER_WEB`
-/// environment variable is set to true.
-bool get flutterWebEnabled {
-  _flutterWebEnabled = platform.environment['FLUTTER_WEB']?.toLowerCase() == 'true';
-  return _flutterWebEnabled && !FlutterVersion.instance.isStable;
-}
-bool _flutterWebEnabled;
-
+import '../web/workflow.dart';
+import 'chrome.dart';
 
 class WebApplicationPackage extends ApplicationPackage {
   WebApplicationPackage(this._flutterProject) : super(id: _flutterProject.manifest.appName);
@@ -42,7 +30,6 @@
   Directory get webSourcePath => _flutterProject.directory.childDirectory('web');
 }
 
-
 class WebDevice extends Device {
   WebDevice() : super('web');
 
@@ -94,7 +81,17 @@
   DevicePortForwarder get portForwarder => const NoOpDevicePortForwarder();
 
   @override
-  Future<String> get sdkNameAndVersion async => 'web';
+  Future<String> get sdkNameAndVersion async {
+    final String chrome = findChromeExecutable();
+    final ProcessResult result = await processManager.run(<String>[
+      chrome,
+      '--version',
+    ]);
+    if (result.exitCode == 0) {
+      return result.stdout;
+    }
+    return 'unknown';
+  }
 
   @override
   Future<LaunchResult> startApp(
@@ -140,7 +137,7 @@
   }
 
   @override
-  Future<TargetPlatform> get targetPlatform async => TargetPlatform.web;
+  Future<TargetPlatform> get targetPlatform async => TargetPlatform.web_javascript;
 
   @override
   Future<bool> uninstallApp(ApplicationPackage app) async => true;
@@ -202,45 +199,3 @@
   @override
   bool get supportsPlatform => flutterWebEnabled;
 }
-
-const String _klinuxExecutable = 'google-chrome';
-const String _kMacOSExecutable = '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome';
-const String _kWindowsExecutable = r'Google\Chrome\Application\chrome.exe';
-final List<String> _kWindowsPrefixes = <String>[
-  platform.environment['LOCALAPPDATA'],
-  platform.environment['PROGRAMFILES'],
-  platform.environment['PROGRAMFILES(X86)'],
-];
-
-// Responsible for launching chrome with devtools configured.
-class ChromeLauncher {
-  const ChromeLauncher();
-
-  /// Launch the chrome browser to a particular `host` page.
-  Future<Process> launch(String host) async {
-    String executable;
-    if (platform.isMacOS) {
-      executable = _kMacOSExecutable;
-    } else if (platform.isLinux) {
-      executable = _klinuxExecutable;
-    } else if (platform.isWindows) {
-      final String filePath = _kWindowsPrefixes.firstWhere((String prefix) {
-        if (prefix == null) {
-          return false;
-        }
-        final String path = fs.path.join(prefix, _kWindowsExecutable);
-        return fs.file(path).existsSync();
-      }, orElse: () => '.');
-      executable = filePath;
-    } else {
-      throwToolExit('Platform ${platform.operatingSystem} is not supported.');
-    }
-    if (!fs.file(executable).existsSync()) {
-      throwToolExit('Chrome executable not found at $executable');
-    }
-    return processManager.start(<String>[
-      executable,
-      host,
-    ], mode: ProcessStartMode.detached);
-  }
-}
diff --git a/packages/flutter_tools/lib/src/web/web_validator.dart b/packages/flutter_tools/lib/src/web/web_validator.dart
new file mode 100644
index 0000000..51ec94e
--- /dev/null
+++ b/packages/flutter_tools/lib/src/web/web_validator.dart
@@ -0,0 +1,37 @@
+// 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 '../base/platform.dart';
+import '../doctor.dart';
+import 'chrome.dart';
+import 'workflow.dart';
+
+/// A validator that checks whether chrome is installed and can run.
+class WebValidator extends DoctorValidator {
+  const WebValidator() : super('Chrome - develop for the web');
+
+  @override
+  Future<ValidationResult> validate() async {
+    final String chrome = findChromeExecutable();
+    final bool canRunChrome = canFindChrome();
+    final List<ValidationMessage> messages = <ValidationMessage>[];
+    if (platform.environment.containsKey(kChromeEnvironment)) {
+      messages.add(ValidationMessage('$kChromeEnvironment = $chrome'));
+    } else {
+      messages.add(ValidationMessage('Chrome at $chrome'));
+      messages.add(ValidationMessage.hint('$kChromeEnvironment not set'));
+    }
+    if (!canRunChrome) {
+      return ValidationResult(
+        ValidationType.missing,
+        messages,
+        statusInfo: 'Cannot find chrome executable at $chrome',
+      );
+    }
+    return ValidationResult(
+      ValidationType.installed,
+      messages,
+    );
+  }
+}
diff --git a/packages/flutter_tools/lib/src/web/workflow.dart b/packages/flutter_tools/lib/src/web/workflow.dart
new file mode 100644
index 0000000..47c28f4
--- /dev/null
+++ b/packages/flutter_tools/lib/src/web/workflow.dart
@@ -0,0 +1,51 @@
+// 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 '../base/context.dart';
+import '../base/file_system.dart';
+import '../base/platform.dart';
+import '../base/process_manager.dart';
+import '../doctor.dart';
+import '../version.dart';
+import 'chrome.dart';
+
+/// Only launch or display web devices if `FLUTTER_WEB`
+/// environment variable is set to true.
+bool get flutterWebEnabled {
+  _flutterWebEnabled = platform.environment['FLUTTER_WEB']?.toLowerCase() == 'true';
+  return _flutterWebEnabled && !FlutterVersion.instance.isStable;
+}
+bool _flutterWebEnabled;
+
+/// The  web workflow instance.
+WebWorkflow get webWorkflow => context.get<WebWorkflow>();
+
+class WebWorkflow extends Workflow {
+  const WebWorkflow();
+
+  @override
+  bool get appliesToHostPlatform => flutterWebEnabled && (platform.isWindows || platform.isMacOS || platform.isLinux);
+
+  @override
+  bool get canLaunchDevices => flutterWebEnabled && canFindChrome();
+
+  @override
+  bool get canListDevices => flutterWebEnabled && canFindChrome();
+
+  @override
+  bool get canListEmulators => false;
+}
+
+/// Whether we can locate the chrome executable.
+bool canFindChrome() {
+  final String chrome = findChromeExecutable();
+  if (platform.isLinux) {
+    return processManager.canRun(chrome);
+  } else if (platform.isMacOS) {
+    return fs.file(chrome).existsSync();
+  } else if (platform.isWindows) {
+    return fs.file(chrome).existsSync();
+  }
+  return false;
+}
diff --git a/packages/flutter_tools/pubspec.yaml b/packages/flutter_tools/pubspec.yaml
index 8521ced..a4cca93 100644
--- a/packages/flutter_tools/pubspec.yaml
+++ b/packages/flutter_tools/pubspec.yaml
@@ -10,7 +10,7 @@
 dependencies:
   # To update these, use "flutter update-packages --force-upgrade".
   archive: 2.0.9
-  args: 1.5.1
+  args: 1.5.2
   bsdiff: 0.1.0
   completion: 0.2.1+1
   coverage: 0.12.4
@@ -33,6 +33,7 @@
   usage: 3.4.1
   vm_service_client: 0.2.6+2
   web_socket_channel: 1.0.13
+  webkit_inspection_protocol: 0.4.0
   xml: 3.5.0
   yaml: 2.1.15
   flutter_goldens_client:
@@ -60,7 +61,7 @@
   build_config: 0.4.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
   build_resolvers: 1.0.5 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
   built_collection: 4.2.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
-  built_value: 6.5.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
+  built_value: 6.6.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
   charcode: 1.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
   convert: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
   csslib: 0.16.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
@@ -100,13 +101,13 @@
 
 dev_dependencies:
   collection: 1.14.11
-  mockito: 4.0.0
+  mockito: 4.1.0
   file_testing: 2.1.0
-  vm_service_lib: 3.17.0
+  vm_service_lib: 3.17.0+1
   test: 1.6.3
   build_runner: 1.4.0
   build_vm_compilers: 1.0.0
-  build_test: 0.10.7+3
+  build_test: 0.10.8
 
   mime: 0.9.6+3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
   multi_server_socket: 1.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
@@ -118,4 +119,4 @@
   # Exclude this package from the hosted API docs.
   nodoc: true
 
-# PUBSPEC CHECKSUM: b34a
+# PUBSPEC CHECKSUM: ef94
diff --git a/packages/flutter_tools/test/commands/run_test.dart b/packages/flutter_tools/test/commands/run_test.dart
index aa8aecc..492c149 100644
--- a/packages/flutter_tools/test/commands/run_test.dart
+++ b/packages/flutter_tools/test/commands/run_test.dart
@@ -66,7 +66,7 @@
 
       when(mockDeviceManager.getDevices()).thenAnswer((Invocation invocation) {
         return Stream<Device>.fromIterable(<Device>[
-          MockDevice(TargetPlatform.web),
+          MockDevice(TargetPlatform.web_javascript),
         ]);
       });
 
diff --git a/packages/flutter_tools/test/web/devices_test.dart b/packages/flutter_tools/test/web/devices_test.dart
index 77dac14..7ab0856 100644
--- a/packages/flutter_tools/test/web/devices_test.dart
+++ b/packages/flutter_tools/test/web/devices_test.dart
@@ -5,6 +5,7 @@
 import 'package:flutter_tools/src/base/file_system.dart';
 import 'package:flutter_tools/src/base/platform.dart';
 import 'package:flutter_tools/src/project.dart';
+import 'package:flutter_tools/src/web/chrome.dart';
 import 'package:flutter_tools/src/web/compile.dart';
 import 'package:flutter_tools/src/web/web_device.dart';
 import 'package:mockito/mockito.dart';
diff --git a/packages/flutter_tools/test/web/web_validator_test.dart b/packages/flutter_tools/test/web/web_validator_test.dart
new file mode 100644
index 0000000..9270bf2
--- /dev/null
+++ b/packages/flutter_tools/test/web/web_validator_test.dart
@@ -0,0 +1,52 @@
+// 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_tools/src/base/file_system.dart';
+import 'package:flutter_tools/src/base/platform.dart';
+import 'package:flutter_tools/src/doctor.dart';
+import 'package:flutter_tools/src/web/chrome.dart';
+import 'package:flutter_tools/src/web/web_validator.dart';
+import 'package:mockito/mockito.dart';
+
+import '../src/common.dart';
+import '../src/testbed.dart';
+
+void main() {
+  group('WebValidator', () {
+    Testbed testbed;
+    WebValidator webValidator;
+    MockPlatform mockPlatform;
+
+    setUp(() {
+      testbed = Testbed(setup: () {
+        fs.file(kMacOSExecutable).createSync(recursive: true);
+        fs.file('chrome_foo').createSync();
+        return null;
+      }, overrides: <Type, Generator>{
+        Platform: () => mockPlatform,
+      });
+      webValidator = const WebValidator();
+      mockPlatform = MockPlatform();
+      when(mockPlatform.isMacOS).thenReturn(true);
+      when(mockPlatform.isWindows).thenReturn(false);
+      when(mockPlatform.isLinux).thenReturn(false);
+    });
+
+    test('Can find macOS executable ', () => testbed.run(() async {
+      final ValidationResult result = await webValidator.validate();
+      expect(result.type, ValidationType.installed);
+    }));
+
+    test('Can notice missing macOS executable ', () => testbed.run(() async {
+      fs.file(kMacOSExecutable).deleteSync();
+      final ValidationResult result = await webValidator.validate();
+      expect(result.type, ValidationType.missing);
+    }));
+  });
+}
+
+class MockPlatform extends Mock implements Platform  {
+  @override
+  Map<String, String> get environment => const <String, String>{};
+}
diff --git a/packages/flutter_tools/test/web/workflow_test.dart b/packages/flutter_tools/test/web/workflow_test.dart
new file mode 100644
index 0000000..d9660e0
--- /dev/null
+++ b/packages/flutter_tools/test/web/workflow_test.dart
@@ -0,0 +1,140 @@
+// 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_tools/src/base/file_system.dart';
+import 'package:flutter_tools/src/base/platform.dart';
+import 'package:flutter_tools/src/version.dart';
+import 'package:flutter_tools/src/web/chrome.dart';
+import 'package:flutter_tools/src/web/workflow.dart';
+import 'package:mockito/mockito.dart';
+import 'package:process/process.dart';
+
+import '../src/common.dart';
+import '../src/context.dart';
+import '../src/testbed.dart';
+
+void main() {
+  group('WebWorkflow', () {
+    Testbed testbed;
+    MockPlatform noEnvironment;
+    MockPlatform notSupported;
+    MockPlatform windows;
+    MockPlatform linux;
+    MockPlatform macos;
+    MockProcessManager mockProcessManager;
+    MockFlutterVersion unstable;
+    MockFlutterVersion stable;
+    WebWorkflow workflow;
+
+    setUpAll(() {
+      unstable = MockFlutterVersion(false);
+      stable = MockFlutterVersion(true);
+      noEnvironment = MockPlatform(environment: const <String, String>{});
+      notSupported = MockPlatform(linux: false, windows: false, macos: false);
+      windows = MockPlatform(windows: true);
+      linux = MockPlatform(linux: true);
+      macos = MockPlatform(macos: true);
+      workflow = const WebWorkflow();
+      mockProcessManager = MockProcessManager();
+      testbed = Testbed(setup: () async {
+        fs.file('chrome').createSync();
+        when(mockProcessManager.canRun('chrome')).thenReturn(true);
+      }, overrides: <Type, Generator>{
+        FlutterVersion: () => unstable,
+        ProcessManager: () => mockProcessManager,
+      });
+    });
+
+    test('does not apply if FLUTTER_WEB is not true', ()=> testbed.run(() {
+      expect(workflow.appliesToHostPlatform, false);
+      expect(workflow.canLaunchDevices, false);
+      expect(workflow.canListDevices, false);
+      expect(workflow.canListEmulators, false);
+    }, overrides: <Type, Generator>{
+      Platform: () => noEnvironment,
+    }));
+
+    test('Applies on Linux', () => testbed.run(() {
+      expect(workflow.appliesToHostPlatform, true);
+      expect(workflow.canLaunchDevices, true);
+      expect(workflow.canListDevices, true);
+      expect(workflow.canListEmulators, false);
+    }, overrides: <Type, Generator>{
+      Platform: () => linux,
+    }));
+
+    test('Applies on macOS', () => testbed.run(() {
+      expect(workflow.appliesToHostPlatform, true);
+      expect(workflow.canLaunchDevices, true);
+      expect(workflow.canListDevices, true);
+      expect(workflow.canListEmulators, false);
+    }, overrides: <Type, Generator>{
+      Platform: () => macos,
+    }));
+
+    test('Applies on Windows', () => testbed.run(() {
+      expect(workflow.appliesToHostPlatform, true);
+      expect(workflow.canLaunchDevices, true);
+      expect(workflow.canListDevices, true);
+      expect(workflow.canListEmulators, false);
+    }, overrides: <Type, Generator>{
+      Platform: () => windows,
+    }));
+
+    test('does not apply on other platforms', () => testbed.run(() {
+      expect(workflow.appliesToHostPlatform, false);
+      expect(workflow.canLaunchDevices, false);
+      expect(workflow.canListDevices, false);
+      expect(workflow.canListEmulators, false);
+    }, overrides: <Type, Generator>{
+      Platform: () => notSupported,
+    }));
+
+    test('does not apply on stable brnach', () => testbed.run(() {
+      expect(workflow.appliesToHostPlatform, false);
+      expect(workflow.canLaunchDevices, false);
+      expect(workflow.canListDevices, false);
+      expect(workflow.canListEmulators, false);
+    }, overrides: <Type, Generator>{
+      Platform: () => macos,
+      FlutterVersion: () => stable,
+    }));
+  });
+}
+
+class MockFlutterVersion extends Mock implements FlutterVersion {
+  MockFlutterVersion(this.isStable);
+
+  @override
+  final bool isStable;
+}
+
+class MockProcessManager extends Mock implements ProcessManager {}
+
+class MockPlatform extends Mock implements Platform {
+  MockPlatform(
+      {this.windows = false,
+      this.macos = false,
+      this.linux = false,
+      this.environment = const <String, String>{
+        'FLUTTER_WEB': 'true',
+        kChromeEnvironment: 'chrome',
+      }});
+
+  final bool windows;
+  final bool macos;
+  final bool linux;
+
+  @override
+  final Map<String, String> environment;
+
+  @override
+  bool get isLinux => linux;
+
+  @override
+  bool get isMacOS => macos;
+
+  @override
+  bool get isWindows => windows;
+}
diff --git a/packages/fuchsia_remote_debug_protocol/pubspec.yaml b/packages/fuchsia_remote_debug_protocol/pubspec.yaml
index 67b6225..c5cbdba 100644
--- a/packages/fuchsia_remote_debug_protocol/pubspec.yaml
+++ b/packages/fuchsia_remote_debug_protocol/pubspec.yaml
@@ -42,6 +42,6 @@
   vm_service_client: 0.2.6+2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
 
 dev_dependencies:
-  mockito: 4.0.0
+  mockito: 4.1.0
 
-# PUBSPEC CHECKSUM: b48d
+# PUBSPEC CHECKSUM: ef8e