pass analysis
diff --git a/engine/analysis_options.yaml b/engine/analysis_options.yaml
index 60918b3..92e155d 100644
--- a/engine/analysis_options.yaml
+++ b/engine/analysis_options.yaml
@@ -1,11 +1,221 @@
-# Web-specific analysis options.
+# Specify analysis options.
 #
-# As of today the web code contains quite a few deviations from the repo-wide
-# analysis options due to having been migrated from google3. The ultimate goal
-# is to clean up our code and delete this file.
+# This file is a copy of analysis_options.yaml from flutter repo
+# as of 2021-03-19, but with:
+#  - "always_require_non_null_named_parameters" disabled (because we
+#    can't import the meta package from the SDK),
+#  - "avoid_equals_and_hash_code_on_mutable_classes" disabled (same reason),
+#  - "missing_whitespace_between_adjacent_strings" disabled (too many false
+#     positives),
+#  - "sort_constructors_first" disabled (because we have private fake
+#     constructors),
+#  - "prefer_final_fields" disabled (because we do weird things with
+#    private fields, especially on the PlatformDispatcher object),
+#  - "public_member_api_docs" enabled.
 
-include: ../../analysis_options.yaml
+analyzer:
+  exclude:
+    # Fixture depends on dart:ui and raises false positives.
+    - flutter_frontend_server/test/fixtures/lib/main.dart
+  strong-mode:
+    implicit-casts: false
+    implicit-dynamic: false
+  errors:
+    # treat missing required parameters as a warning (not a hint)
+    missing_required_param: warning
+    # treat missing returns as a warning (not a hint)
+    missing_return: warning
+    native_function_body_in_non_sdk_code: ignore
+    # allow having TODOs in the code
+    todo: ignore
+    # allow dart:ui to import dart:_internal
+    import_internal_library: ignore
+    # Turned off until null-safe rollout is complete.
+    unnecessary_null_comparison: ignore
 
 linter:
   rules:
-    public_member_api_docs: false
\ No newline at end of file
+    # these rules are documented on and in the same order as
+    # the Dart Lint rules page to make maintenance easier
+    # https://github.com/dart-lang/linter/blob/master/example/all.yaml
+    - always_declare_return_types
+    - always_put_control_body_on_new_line
+    # - always_put_required_named_parameters_first # we prefer having parameters in the same order as fields https://github.com/flutter/flutter/issues/10219
+    # - always_require_non_null_named_parameters # DIFFERENT FROM FLUTTER/FLUTTER
+    - always_specify_types
+    # - always_use_package_imports # we do this commonly
+    - annotate_overrides
+    # - avoid_annotating_with_dynamic # conflicts with always_specify_types
+    - avoid_bool_literals_in_conditional_expressions
+    # - avoid_catches_without_on_clauses # we do this commonly
+    # - avoid_catching_errors # we do this commonly
+    - avoid_classes_with_only_static_members # We want to avoid classes that can be instantiated but only have statics
+    # - avoid_double_and_int_checks # only useful when targeting JS runtime
+    # - avoid_dynamic_calls # not yet tested
+    - avoid_empty_else
+    # - avoid_equals_and_hash_code_on_mutable_classes # DIFFERENT FROM FLUTTER/FLUTTER
+    # - avoid_escaping_inner_quotes # not yet tested
+    - avoid_field_initializers_in_const_classes
+    - avoid_function_literals_in_foreach_calls
+    # - avoid_implementing_value_types # not yet tested
+    - avoid_init_to_null
+    # - avoid_js_rounded_ints # only useful when targeting JS runtime
+    - avoid_null_checks_in_equality_operators
+    # - avoid_positional_boolean_parameters # not yet tested
+    # - avoid_print # not yet tested
+    # - avoid_private_typedef_functions # we prefer having typedef (discussion in https://github.com/flutter/flutter/pull/16356)
+    # - avoid_redundant_argument_values # not yet tested
+    - avoid_relative_lib_imports
+    - avoid_renaming_method_parameters
+    - avoid_return_types_on_setters
+    # - avoid_returning_null # there are plenty of valid reasons to return null
+    # - avoid_returning_null_for_future # not yet tested
+    - avoid_returning_null_for_void
+    # - avoid_returning_this # there are plenty of valid reasons to return this
+    # - avoid_setters_without_getters # not yet tested
+    - avoid_shadowing_type_parameters
+    - avoid_single_cascade_in_expression_statements
+    - avoid_slow_async_io
+    - avoid_type_to_string
+    - avoid_types_as_parameter_names
+    # - avoid_types_on_closure_parameters # conflicts with always_specify_types
+    - avoid_unnecessary_containers
+    - avoid_unused_constructor_parameters
+    - avoid_void_async
+    # - avoid_web_libraries_in_flutter # not yet tested
+    - await_only_futures
+    - camel_case_extensions
+    - camel_case_types
+    - cancel_subscriptions
+    # - cascade_invocations # not yet tested
+    - cast_nullable_to_non_nullable
+    # - close_sinks # not reliable enough
+    # - comment_references # blocked on https://github.com/dart-lang/linter/issues/1142
+    # - constant_identifier_names # needs an opt-out https://github.com/dart-lang/linter/issues/204
+    - control_flow_in_finally
+    # - curly_braces_in_flow_control_structures # not required by flutter style
+    - deprecated_consistency
+    # - diagnostic_describe_all_properties # not yet tested
+    - directives_ordering
+    # - do_not_use_environment # we do this commonly
+    - empty_catches
+    - empty_constructor_bodies
+    - empty_statements
+    - exhaustive_cases
+    - file_names
+    - flutter_style_todos
+    - hash_and_equals
+    - implementation_imports
+    # - invariant_booleans # too many false positives: https://github.com/dart-lang/linter/issues/811
+    - iterable_contains_unrelated_type
+    # - join_return_with_assignment # not required by flutter style
+    - leading_newlines_in_multiline_strings
+    - library_names
+    - library_prefixes
+    # - lines_longer_than_80_chars # not required by flutter style
+    - list_remove_unrelated_type
+    # - literal_only_boolean_expressions # too many false positives: https://github.com/dart-lang/sdk/issues/34181
+    # - missing_whitespace_between_adjacent_strings # DIFFERENT FROM FLUTTER/FLUTTER
+    - no_adjacent_strings_in_list
+    # - no_default_cases # too many false positives
+    - no_duplicate_case_values
+    - no_logic_in_create_state
+    # - no_runtimeType_toString # ok in tests; we enable this only in packages/
+    - non_constant_identifier_names
+    - null_check_on_nullable_type_parameter
+    - null_closures
+    # - omit_local_variable_types # opposite of always_specify_types
+    # - one_member_abstracts # too many false positives
+    # - only_throw_errors # https://github.com/flutter/flutter/issues/5792
+    - overridden_fields
+    - package_api_docs
+    - package_names
+    - package_prefixed_library_names
+    # - parameter_assignments # we do this commonly
+    - prefer_adjacent_string_concatenation
+    - prefer_asserts_in_initializer_lists
+    # - prefer_asserts_with_message # not required by flutter style
+    - prefer_collection_literals
+    - prefer_conditional_assignment
+    - prefer_const_constructors
+    - prefer_const_constructors_in_immutables
+    - prefer_const_declarations
+    - prefer_const_literals_to_create_immutables
+    # - prefer_constructors_over_static_methods # far too many false positives
+    - prefer_contains
+    # - prefer_double_quotes # opposite of prefer_single_quotes
+    - prefer_equal_for_default_values
+    # - prefer_expression_function_bodies # conflicts with https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo#consider-using--for-short-functions-and-methods
+    # - prefer_final_fields # DIFFERENT FROM FLUTTER/FLUTTER
+    - prefer_final_in_for_each
+    - prefer_final_locals
+    - prefer_for_elements_to_map_fromIterable
+    - prefer_foreach
+    - prefer_function_declarations_over_variables
+    - prefer_generic_function_type_aliases
+    - prefer_if_elements_to_conditional_expressions
+    - prefer_if_null_operators
+    - prefer_initializing_formals
+    - prefer_inlined_adds
+    # - prefer_int_literals # conflicts with https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo#use-double-literals-for-double-constants
+    # - prefer_interpolation_to_compose_strings # doesn't work with raw strings, see https://github.com/dart-lang/linter/issues/2490
+    - prefer_is_empty
+    - prefer_is_not_empty
+    - prefer_is_not_operator
+    - prefer_iterable_whereType
+    # - prefer_mixin # https://github.com/dart-lang/language/issues/32
+    - prefer_null_aware_operators
+    # - prefer_relative_imports # incompatible with sub-package imports
+    - prefer_single_quotes
+    - prefer_spread_collections
+    - prefer_typing_uninitialized_variables
+    - prefer_void_to_null
+    - provide_deprecation_message
+    # - public_member_api_docs
+    - recursive_getters
+    - sized_box_for_whitespace
+    - slash_for_doc_comments
+    # - sort_child_properties_last # not yet tested
+    # - sort_constructors_first # DIFFERENT FROM FLUTTER/FLUTTER
+    # - sort_pub_dependencies # prevents separating pinned transitive dependencies
+    - sort_unnamed_constructors_first
+    - test_types_in_equals
+    - throw_in_finally
+    - tighten_type_of_initializing_formals
+    # - type_annotate_public_apis # subset of always_specify_types
+    - type_init_formals
+    # - unawaited_futures # too many false positives
+    - unnecessary_await_in_return
+    - unnecessary_brace_in_string_interps
+    - unnecessary_const
+    # - unnecessary_final # conflicts with prefer_final_locals
+    - unnecessary_getters_setters
+    # - unnecessary_lambdas # has false positives: https://github.com/dart-lang/linter/issues/498
+    - unnecessary_new
+    - unnecessary_null_aware_assignments
+    # - unnecessary_null_checks # not yet tested
+    - unnecessary_null_in_if_null_operators
+    - unnecessary_nullable_for_final_variable_declarations
+    - unnecessary_overrides
+    - unnecessary_parenthesis
+    # - unnecessary_raw_strings # not yet tested
+    - unnecessary_statements
+    - unnecessary_string_escapes
+    - unnecessary_string_interpolations
+    - unnecessary_this
+    - unrelated_type_equality_checks
+    # - unsafe_html # not yet tested
+    - use_full_hex_values_for_flutter_colors
+    - use_function_type_syntax_for_parameters
+    # - use_if_null_to_convert_nulls_to_bools # not yet tested
+    - use_is_even_rather_than_modulo
+    - use_key_in_widget_constructors
+    # - use_late_for_private_fields_and_variables # not yet tested
+    # - use_named_constants # not yet tested
+    - use_raw_strings
+    - use_rethrow_when_possible
+    # - use_setters_to_change_properties # not yet tested
+    # - use_string_buffers # has false positives: https://github.com/dart-lang/sdk/issues/34182
+    # - use_to_and_as_if_applicable # has false positives, so we prefer to catch this by code-review
+    - valid_regexps
+    - void_checks
diff --git a/engine/dev/README.md b/engine/dev/README.md
deleted file mode 100644
index e2bab8e..0000000
--- a/engine/dev/README.md
+++ /dev/null
@@ -1,202 +0,0 @@
-## What's `felt`?
-
-`felt` stands for "Flutter Engine Local Tester". It's a cli tool that aims to make development in the Flutter web engine more productive and pleasant.
-
-## What can `felt` do?
-
-`felt` supports multiple commands as follows:
-
-1. **`felt check-licenses`**: Checks that all Dart and JS source code files contain the correct license headers.
-2. **`felt test`**: Runs all or some tests depending on the passed arguments. It supports a watch mode for convenience.
-3. **`felt build`**: Builds the engine locally so it can be used by Flutter apps. It also supports a watch mode for more convenience.
-
-You could also run `felt help` or `felt help <command>` to get more information about the available commands and arguments.
-
-## How can I use `felt`?
-
-Once you have your local copy of the engine [setup](https://github.com/flutter/flutter/wiki/Setting-up-the-Engine-development-environment), it's recommended that you add `/path/to/engine/src/flutter/lib/web_ui/dev` to your `PATH`.
-Then you would be able to use the `felt` tool from anywhere:
-
-```
-felt check-licenses
-```
-
-or:
-
-```
-felt build --watch
-```
-
-If you don't want to add `felt` to your path, you can still invoke it using a relative path like `./web_ui/dev/felt <command>`
-
-## Speeding up your builds and tests
-
-If you are a Google employee, you can use an internal instance of Goma (go/ma)
-to parallelize your builds. Because Goma compiles code on remote servers, this
-option is particularly effective for building on low-powered laptops.
-
-Concurrency of various build steps can be configured via environment variables:
-
-- `FELT_DART2JS_CONCURRENCY` specifies the number of concurrent `dart2js`
-  processes used to compile tests. Default value is 8.
-- `FELT_TEST_CONCURRENCY` specifies the number of tests run concurrently.
-  Default value is 10.
-
-## Running web engine tests
-
-To run all tests on Chrome. This will run both integration tests and the unit tests:
-
-```
-felt test
-```
-
-To run a specific test:
-
-```
-felt test test/engine/util_test.dart
-```
-
-To enable watch mode so that the test re-runs on every change:
-
-```
-felt test --watch test/engine/util_test.dart
-```
-
-To run unit tests only:
-
-```
-felt test --unit-tests-only
-```
-
-To run integration tests only. For now these tests are only available on Chrome Desktop browsers. These tests will fetch the flutter repository for using `flutter drive` and `flutter pub get` commands. The repository will be synced to the youngest commit older than the engine commit.
-
-```
-felt test --integration-tests-only
-```
-
-To skip cloning the flutter repository use the following flag. This flag can save internet bandwidth. However use with caution. Note the tests results will not be consistent with CIs when this flag is set. flutter command should be set in the PATH for this flag to be useful. This flag can also be used to test local Flutter changes.
-
-```
-felt test --integration-tests-only --use-system-flutter
-```
-
-To run tests on Firefox (this will work only on a Linux device):
-
-```
-felt test --browser=firefox
-```
-
-For Chrome and Firefox, the tests run on a version locked on the [browser_lock.yaml](https://github.com/flutter/engine/blob/main/lib/web_ui/dev/browser_lock.yaml). In order to use another version, add the version argument:
-
-```
-felt test --browser=firefox --firefox-version=70.0.1
-```
-
-To run tests on Safari use the following command. It works on macOS devices and it uses the Safari installed on the OS. Currently there is no option for using another Safari version.
-
-```
-felt test --browser=safari
-```
-
-One can also use Safari running in iOS Simulator for running unit tests. There are few prerequisite steps:
-
-1. Please make sure that you installed Xcode.
-
-2. The default version used in the tests are in browser_lock.yaml file. Install the ios version to use for simulators: Xcode > Preferences > Components
-
-3. run `xcrun simctl list devices`. If the simulator you want is not installed use step 4.
-
-4. Use felt to create a simulator:
-
-```
-felt create_simulator --type='iOS' --version='13.1' --device='iPhone.11.Pro'
-```
-
-To run tests on ios-safari use the one of the following commands:
-
-```
-felt test --browser=ios-safari
-felt test --browser=ios-safari --version='13.1' --device='iPhone.11.Pro'
-felt test --browser=ios-safari test/alarm_clock_test.dart
-```
-
-To run tests on Windows Edge use the following command. It works on Windows devices and it uses the Edge installed on the OS.
-
-```
-felt_windows.bat test --browser=edge
-```
-
-To run a single test use the following command. Note that it does not work on Windows.
-
-```
-felt test test/golden_tests/engine/canvas_golden_test.dart
-```
-
-To debug a test on Chrome:
-
-```
-felt test --debug test/golden_tests/engine/canvas_golden_test.dart
-```
-
-## Configuration files
-
-`browser_lock.yaml` contains the version of browsers we use to test Flutter for
-web. Versions are not automatically updated whenever a new release is available.
-Instead, we update this file manually once in a while.
-
-`goldens_lock.yaml` refers to a revision in the https://github.com/flutter/goldens
-repo. Screenshot tests are compared with the golden files at that revision.
-When making engine changes that affect screenshots, first submit a PR to
-flutter/goldens updating the screenshots. Then update this file pointing to
-the new revision.
-
-## Developing the `felt` tool
-
-If you are making changes in the `felt` tool itself, you need to be aware of Dart snapshots. We create a Dart snapshot of the `felt` tool to make the startup faster.
-
-To make sure you are running the `felt` tool with your changes included, you would need to stop using the snapshot. This can be achieved through the environment variable `FELT_USE_SNAPSHOT`:
-
-```
-FELT_USE_SNAPSHOT=false felt <command>
-```
-
-or
-
-```
-FELT_USE_SNAPSHOT=0 felt <command>
-```
-
-_**Note**: if `FELT_USE_SNAPSHOT` is omitted or has any value other than "false" or "0", the snapshot mode will be enabled._
-
-## Upgrade Browser Version
-
-Since the engine code and infra recipes do not live in the same repository
-there are few steps to follow in order to upgrade a browser's version. For
-now these instructins are most relevant to Chrome.
-
-1. Dowload the binaries for the new browser/driver for each operaing system (macOS, linux, windows).
-2. Create CIPD packages for these packages. (More documentation is available for Googlers. go/cipd-flutter-web)
-3. Update the version in this repo. Do this by changing the related fields in `browser_lock.yaml` file.
-
-Note that for LUCI builders, for Chrome both unit and integration tests are using the same browser. (For Firefox [issue](https://github.com/flutter/flutter/issues/71617)
-
-Some useful links:
-
-1. For Chrome downloads [link](https://commondatastorage.googleapis.com/chromium-browser-snapshots/index.html)
-2. Browser and driver CIPD [packages](https://chrome-infra-packages.appspot.com/p/flutter_internal) (note: access is restricted for these packages)
-3. LUCI web [recipe](https://flutter.googlesource.com/recipes/+/refs/heads/main/recipes/web_engine.py)
-4. More general reading on CIPD packages [link](https://chromium.googlesource.com/chromium/src.git/+/main/docs/cipd_and_3pp.md#What-is-CIPD)
-
-## Troubleshooting
-
-### Can't load Kernel binary: Invalid kernel binary format version.
-
-Some times `.dart_tool` cache invalidation fails, and you'll end up with a cached version of `felt` that is not compatible with the Dart SDK that you're using.
-
-In that case, any invocation to `felt` will fail with:
-
-`Can't load Kernel binary: Invalid kernel binary format version.`
-
-The solution is to delete the cached `felt.snapshot` files within `engine/src/flutter/lib/web_ui`:
-
-**`rm .dart_tool/felt.snapshot*`**
diff --git a/engine/dev/browser.dart b/engine/dev/browser.dart
deleted file mode 100644
index 4c46d64..0000000
--- a/engine/dev/browser.dart
+++ /dev/null
@@ -1,202 +0,0 @@
-// Copyright 2013 The Flutter 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 'dart:math' as math;
-
-import 'package:image/image.dart';
-import 'package:pedantic/pedantic.dart';
-import 'package:stack_trace/stack_trace.dart';
-import 'package:test_api/src/backend/runtime.dart';
-import 'package:typed_data/typed_buffers.dart';
-
-/// Provides the environment for a specific web browser.
-abstract class BrowserEnvironment {
-  /// The [Runtime] used by `package:test` to identify this browser type.
-  Runtime get packageTestRuntime;
-
-  /// The name of the configuration YAML file used to configure `package:test`.
-  ///
-  /// The configuration file is expected to be a direct child of the `web_ui`
-  /// directory.
-  String get packageTestConfigurationYamlFile;
-
-  /// Prepares the OS environment to run tests for this browser.
-  ///
-  /// This may include things like installing browsers, and starting web drivers,
-  /// iOS Simulators, and/or Android emulators.
-  ///
-  /// Typically the browser environment is prepared once and supports multiple
-  /// browser instances.
-  Future<void> prepare();
-
-  /// Launches a browser instance.
-  ///
-  /// The browser will be directed to open the provided [url].
-  ///
-  /// If [debug] is true and the browser supports debugging, launches the
-  /// browser in debug mode by pausing test execution after the code is loaded
-  /// but before calling the `main()` function of the test, giving the
-  /// developer a chance to set breakpoints.
-  Browser launchBrowserInstance(Uri url, {bool debug = false});
-
-  /// Returns the screenshot manager used by this browser.
-  ///
-  /// If the browser does not support screenshots, returns null.
-  ScreenshotManager? getScreenshotManager();
-}
-
-/// An interface for running browser instances.
-///
-/// This is intentionally coarse-grained: browsers are controlled primary from
-/// inside a single tab. Thus this interface only provides support for closing
-/// the browser and seeing if it closes itself.
-///
-/// Any errors starting or running the browser process are reported through
-/// [onExit].
-abstract class Browser {
-  String get name;
-
-  /// The Observatory URL for this browser.
-  ///
-  /// Returns `null` for browsers that aren't running the Dart VM, or
-  /// if the Observatory URL can't be found.
-  Future<Uri>? get observatoryUrl => null;
-
-  /// The remote debugger URL for this browser.
-  ///
-  /// Returns `null` for browsers that don't support remote debugging,
-  /// or if the remote debugging URL can't be found.
-  Future<Uri>? get remoteDebuggerUrl => null;
-
-  /// The underlying process.
-  ///
-  /// This will fire once the process has started successfully.
-  Future<Process> get _process => _processCompleter.future;
-  final Completer<Process> _processCompleter = Completer<Process>();
-
-  /// Whether [close] has been called.
-  bool _closed = false;
-
-  /// A future that completes when the browser exits.
-  ///
-  /// If there's a problem starting or running the browser, this will complete
-  /// with an error.
-  Future<void> get onExit => _onExitCompleter.future;
-  final Completer<void> _onExitCompleter = Completer<void>();
-
-  /// Standard IO streams for the underlying browser process.
-  final List<StreamSubscription<void>> _ioSubscriptions = <StreamSubscription<void>>[];
-
-  /// Creates a new browser.
-  ///
-  /// This is intended to be called by subclasses. They pass in [startBrowser],
-  /// which asynchronously returns the browser process. Any errors in
-  /// [startBrowser] (even those raised asynchronously after it returns) are
-  /// piped to [onExit] and will cause the browser to be killed.
-  Browser(Future<Process> Function() startBrowser) {
-    // Don't return a Future here because there's no need for the caller to wait
-    // for the process to actually start. They should just wait for the HTTP
-    // request instead.
-    runZonedGuarded(() async {
-      final Process process = await startBrowser();
-      _processCompleter.complete(process);
-
-      final Uint8Buffer output = Uint8Buffer();
-      void drainOutput(Stream<List<int>> stream) {
-        try {
-          _ioSubscriptions
-              .add(stream.listen(output.addAll, cancelOnError: true));
-        } on StateError catch (_) {}
-      }
-
-      // If we don't drain the stdout and stderr the process can hang.
-      drainOutput(process.stdout);
-      drainOutput(process.stderr);
-
-      final int exitCode = await process.exitCode;
-
-      // This hack dodges an otherwise intractable race condition. When the user
-      // presses Control-C, the signal is sent to the browser and the test
-      // runner at the same time. It's possible for the browser to exit before
-      // the [Browser.close] is called, which would trigger the error below.
-      //
-      // A negative exit code signals that the process exited due to a signal.
-      // However, it's possible that this signal didn't come from the user's
-      // Control-C, in which case we do want to throw the error. The only way to
-      // resolve the ambiguity is to wait a brief amount of time and see if this
-      // browser is actually closed.
-      if (!_closed && exitCode < 0) {
-        await Future<void>.delayed(const Duration(milliseconds: 200));
-      }
-
-      if (!_closed && exitCode != 0) {
-        final String outputString = utf8.decode(output);
-        String message = '$name failed with exit code $exitCode.';
-        if (outputString.isNotEmpty) {
-          message += '\nStandard output:\n$outputString';
-        }
-
-        throw Exception(message);
-      }
-
-      _onExitCompleter.complete();
-    }, (dynamic error, StackTrace? stackTrace) {
-      // Ignore any errors after the browser has been closed.
-      if (_closed) {
-        return;
-      }
-
-      // Make sure the process dies even if the error wasn't fatal.
-      _process.then((Process process) => process.kill());
-
-      stackTrace ??= Trace.current();
-
-      if (_onExitCompleter.isCompleted) {
-        return;
-      }
-      _onExitCompleter.completeError(
-        Exception('Failed to run $name: $error.'),
-        stackTrace,
-      );
-    });
-  }
-
-  /// Kills the browser process.
-  ///
-  /// Returns the same [Future] as [onExit], except that it won't emit
-  /// exceptions.
-  Future<void> close() async {
-    _closed = true;
-
-    // If we don't manually close the stream the test runner can hang.
-    // For example this happens with Chrome Headless.
-    // See SDK issue: https://github.com/dart-lang/sdk/issues/31264
-    for (final StreamSubscription<void> stream in _ioSubscriptions) {
-      unawaited(stream.cancel());
-    }
-
-    (await _process).kill();
-
-    // Swallow exceptions. The user should explicitly use [onExit] for these.
-    return onExit.catchError((dynamic _) {});
-  }
-}
-
-/// Interface for capturing screenshots from a browser.
-abstract class ScreenshotManager {
-  /// Capture a screenshot.
-  ///
-  /// Please read the details for the implementing classes.
-  Future<Image> capture(math.Rectangle<num> region);
-
-  /// Suffix to be added to the end of the filename.
-  ///
-  /// Example file names:
-  /// - Chrome, no-suffix: backdrop_filter_clip_moved.actual.png
-  /// - iOS Safari: backdrop_filter_clip_moved.iOS_Safari.actual.png
-  String get filenameSuffix;
-}
diff --git a/engine/dev/browser_lock.dart b/engine/dev/browser_lock.dart
deleted file mode 100644
index f32815a..0000000
--- a/engine/dev/browser_lock.dart
+++ /dev/null
@@ -1,86 +0,0 @@
-// Copyright 2013 The Flutter 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:io' as io;
-
-import 'package:path/path.dart' as path;
-import 'package:yaml/yaml.dart';
-
-import 'common.dart';
-import 'environment.dart';
-
-/// Returns the browser configuration based on the `browser_lock.yaml` file in
-/// the current engine workspace.
-final BrowserLock browserLock = BrowserLock();
-
-/// Provides access to the contents of the `browser_lock.yaml` file.
-class BrowserLock {
-  factory BrowserLock() {
-    final io.File lockFile = io.File(
-      path.join(environment.webUiRootDir.path, 'dev', 'browser_lock.yaml'),
-    );
-    final YamlMap yaml = loadYaml(lockFile.readAsStringSync()) as YamlMap;
-    return BrowserLock._fromYaml(yaml);
-  }
-
-  BrowserLock._fromYaml(YamlMap yaml) :
-    chromeLock = ChromeLock._fromYaml(yaml['chrome'] as YamlMap),
-    firefoxLock = FirefoxLock._fromYaml(yaml['firefox'] as YamlMap),
-    edgeLock = EdgeLock._fromYaml(yaml['edge'] as YamlMap),
-    safariIosLock = SafariIosLock._fromYaml(yaml['safari_ios'] as YamlMap);
-
-  final ChromeLock chromeLock;
-  final FirefoxLock firefoxLock;
-  final EdgeLock edgeLock;
-  final SafariIosLock safariIosLock;
-}
-
-class ChromeLock {
-  ChromeLock._fromYaml(YamlMap yaml) :
-    linux = (yaml['Linux'] as int).toString(),
-    mac = (yaml['Mac'] as int).toString(),
-    windows = (yaml['Win'] as int).toString();
-
-  final String linux;
-  final String mac;
-  final String windows;
-
-  /// Return the version of Chromium to use for the current operating system.
-  String get versionForCurrentPlatform {
-    return PlatformBinding.instance.getChromeBuild(this);
-  }
-}
-
-class FirefoxLock {
-  FirefoxLock._fromYaml(YamlMap yaml) :
-    version = yaml['version'] as String;
-
-  final String version;
-}
-
-class EdgeLock {
-  EdgeLock._fromYaml(YamlMap yaml) :
-      launcherVersion = yaml['launcher_version'] as String;
-
-  final String launcherVersion;
-}
-
-class SafariIosLock {
-  SafariIosLock._fromYaml(YamlMap yaml) :
-    majorVersion = yaml['major_version'] as int,
-    minorVersion = yaml['minor_version'] as int,
-    device = yaml['device'] as String,
-    heightOfHeader = yaml['height_of_header'] as int,
-    heightOfFooter = yaml['height_of_footer'] as int,
-    scaleFactor = yaml['scale_factor'] as double;
-
-  final int majorVersion;
-  final int minorVersion;
-  final String device;
-  final int heightOfHeader;
-  final int heightOfFooter;
-  final double scaleFactor;
-
-  String get simulatorDescription => '$device with iOS $majorVersion.$minorVersion';
-}
diff --git a/engine/dev/browser_lock.yaml b/engine/dev/browser_lock.yaml
deleted file mode 100644
index e6baadd..0000000
--- a/engine/dev/browser_lock.yaml
+++ /dev/null
@@ -1,49 +0,0 @@
-# Please refer to the "Upgrade Browser Version" section in the README.md for

-# more details on how to update browser version numbers.

-chrome:

-  # It seems Chrome can't always release from the same build for all operating

-  # systems, so we specify per-OS build number.

-  #

-  # The OS names here must match what recipe Python expression

-  # `self.m.platform.name.capitalize()` evaluates to. See:

-  #

-  # recipe_modules/web_util/api.py

-  Linux: 927388 # Major version 96

-  Mac: 927385 # Major version 96

-  Win: 927369 # Major version 96

-

-## Firefox does not use CIPD. To update the version, simply update it in this

-## file.

-firefox:

-  version: '83.0'

-

-edge:

-  launcher_version: '1.2.0.0'

-

-safari_ios:

-  # Make sure this version is the same version supported by LUCI macOS bots.

-  # XCode on these bots will be updated once a year, do not forget to update

-  # `heightOfHeader` during this time.

-  major_version: 13

-  minor_version: 0

-  device: 'iPhone 11'

-  # `xcrun simctl` command is used to take screenshots. It takes the screenshot

-  # of the entire simulator. Therefore we need to crop all the parts other than

-  # the browsers' content. This file must be in sync with the local and LUCI

-  # versions of macOS, iOS Simulator, and Xcode.

-  # `heightOfHeader` is the number of pixels taken by the phone's header menu

-  # and the browsers address bar.

-  # TODO: https://github.com/flutter/flutter/issues/65672

-  height_of_header: 189

-  # `heightOfFooter` is the number of pixels taken by the phone's navigation

-  # menu.

-  height_of_footer: 250

-  # Most of the time tests use a portion of the screen to compare goldens

-  # instead of the entire screen. This area is reprented by a rectangle

-  # when taking screenshots. However the rectangle dimensions are in logical

-  # coordinates. In order to convert these coordinates to coordinates on the

-  # phone screeen we enlarge or shrink the area by applying a linear

-  # transformation by a scale_factor (a.k.a. we perform isotropic scaling).

-  # This value will be differ depending on the phone.

-  # For iOS 13.0 iPhone 11 Pro, this number is 1.15.

-  scale_factor: 1.00

diff --git a/engine/dev/build.dart b/engine/dev/build.dart
deleted file mode 100644
index 459dc19..0000000
--- a/engine/dev/build.dart
+++ /dev/null
@@ -1,104 +0,0 @@
-// Copyright 2013 The Flutter 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:io' show Platform;
-
-import 'package:args/command_runner.dart';
-import 'package:path/path.dart' as path;
-import 'package:watcher/src/watch_event.dart';
-
-import 'environment.dart';
-import 'pipeline.dart';
-import 'utils.dart';
-
-class BuildCommand extends Command<bool> with ArgUtils<bool> {
-  BuildCommand() {
-    argParser.addFlag(
-        'watch',
-        defaultsTo: false,
-        abbr: 'w',
-        help: 'Run the build in watch mode so it rebuilds whenever a change'
-            'is made. Disabled by default.',
-      );
-  }
-
-  @override
-  String get name => 'build';
-
-  @override
-  String get description => 'Build the Flutter web engine.';
-
-  bool get isWatchMode => boolArg('watch');
-
-  @override
-  FutureOr<bool> run() async {
-    final FilePath libPath = FilePath.fromWebUi('lib');
-    final Pipeline buildPipeline = Pipeline(steps: <PipelineStep>[
-      GnPipelineStep(),
-      NinjaPipelineStep(),
-    ]);
-    await buildPipeline.run();
-
-    if (isWatchMode) {
-      print('Initial build done!');
-      print('Watching directory: ${libPath.relativeToCwd}/');
-      await PipelineWatcher(
-        dir: libPath.absolute,
-        pipeline: buildPipeline,
-        // Ignore font files that are copied whenever tests run.
-        ignore: (WatchEvent event) => event.path.endsWith('.ttf'),
-      ).start();
-    }
-    return true;
-  }
-}
-
-/// Runs `gn`.
-///
-/// Not safe to interrupt as it may leave the `out/` directory in a corrupted
-/// state. GN is pretty quick though, so it's OK to not support interruption.
-class GnPipelineStep extends ProcessStep {
-  @override
-  String get description => 'gn';
-
-  @override
-  bool get isSafeToInterrupt => false;
-
-  @override
-  Future<ProcessManager> createProcess() {
-    print('Running gn...');
-    return startProcess(
-      path.join(environment.flutterDirectory.path, 'tools', 'gn'),
-      <String>[
-        '--unopt',
-        if (Platform.isMacOS) '--xcode-symlinks',
-        '--full-dart-sdk',
-      ],
-    );
-  }
-}
-
-/// Runs `autoninja`.
-///
-/// Can be safely interrupted.
-class NinjaPipelineStep extends ProcessStep {
-  @override
-  String get description => 'ninja';
-
-  @override
-  bool get isSafeToInterrupt => true;
-
-  @override
-  Future<ProcessManager> createProcess() {
-    print('Running autoninja...');
-    return startProcess(
-      'autoninja',
-      <String>[
-        '-C',
-        environment.hostDebugUnoptDir.path,
-      ],
-    );
-  }
-}
diff --git a/engine/dev/canvaskit_lock.yaml b/engine/dev/canvaskit_lock.yaml
deleted file mode 100644
index f66b919..0000000
--- a/engine/dev/canvaskit_lock.yaml
+++ /dev/null
@@ -1,4 +0,0 @@
-# Specifies the version of CanvasKit to use for Flutter Web apps.
-#
-# See `lib/web_ui/README.md` for how to update this file.
-canvaskit_version: "0.31.0"
diff --git a/engine/dev/canvaskit_roller.dart b/engine/dev/canvaskit_roller.dart
deleted file mode 100644
index 3aa804d..0000000
--- a/engine/dev/canvaskit_roller.dart
+++ /dev/null
@@ -1,168 +0,0 @@
-// Copyright 2013 The Flutter 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:convert' show json;
-import 'dart:io';
-
-import 'package:path/path.dart' as pathlib;
-import 'package:yaml/yaml.dart';
-
-import 'environment.dart';
-import 'utils.dart';
-
-/// Rolls CanvasKit to the version specified in `dev/canvaskit_lock.yaml`.
-///
-/// Detailed instructions for how to use this script can be found in
-/// `lib/web_ui/README.md`.
-Future<void> main(List<String> args) async {
-  final String canvaskitVersion = _readCanvaskitVersion();
-  print('Rolling CanvasKit to version $canvaskitVersion');
-
-  final Directory canvaskitDirectory = await Directory.systemTemp.createTemp('canvaskit-roll-$canvaskitVersion-');
-  print('Will use ${canvaskitDirectory.path} as staging directory.');
-
-  final String baseUrl = 'https://unpkg.com/canvaskit-wasm@$canvaskitVersion/bin/';
-  print('Downloading CanvasKit from $baseUrl');
-  final HttpClient client = HttpClient();
-  for (final String assetPath in _canvaskitAssets) {
-    final String assetUrl = '$baseUrl/$assetPath';
-    final File assetFile = File(pathlib.joinAll(<String>[
-      canvaskitDirectory.path,
-      'canvaskit',
-      ...assetPath.split('/'), // so it's compatible with Windows
-    ]));
-    await assetFile.parent.create(recursive: true);
-    final HttpClientRequest request = await client.getUrl(Uri.parse(assetUrl));
-    final HttpClientResponse response = await request.close();
-    final IOSink fileSink = assetFile.openWrite();
-    await response.pipe(fileSink);
-  }
-  client.close();
-
-  final File cipdConfigFile = File(pathlib.join(
-    canvaskitDirectory.path,
-    'cipd.yaml',
-  ));
-  await cipdConfigFile.writeAsString('''
-package: flutter/web/canvaskit_bundle
-description: A build of CanvasKit bundled with Flutter Web apps
-data:
-  - dir: canvaskit
-''');
-
-  print('Uploading to CIPD');
-  await runProcess('cipd', <String>[
-    'create',
-    '--tag=version:$canvaskitVersion',
-    '--pkg-def=cipd.yaml',
-    '--json-output=result.json',
-  ], workingDirectory: canvaskitDirectory.path);
-
-  final Map<String, dynamic> cipdResult = json.decode(File(pathlib.join(
-    canvaskitDirectory.path,
-    'result.json',
-  )).readAsStringSync()) as Map<String, dynamic>;
-  final String cipdInstanceId = cipdResult['result']['instance_id'] as String;
-
-  print('CIPD instance information:');
-  final String cipdInfo = await evalProcess('cipd', <String>[
-    'describe',
-    'flutter/web/canvaskit_bundle',
-    '--version=$cipdInstanceId',
-  ], workingDirectory: canvaskitDirectory.path);
-  print(cipdInfo.trim().split('\n').map((String line) => ' • $line').join('\n'));
-
-  print('Updating DEPS file');
-  await _updateDepsFile(cipdInstanceId);
-  await _updateCanvaskitInitializationCode(canvaskitVersion);
-
-  print('\nATTENTION: the roll process is not complete yet.');
-  print('Last step: for the roll to take effect submit an engine pull request from local git changes.');
-}
-
-const List<String> _canvaskitAssets = <String>[
-  'canvaskit.js',
-  'canvaskit.wasm',
-  'profiling/canvaskit.js',
-  'profiling/canvaskit.wasm',
-];
-
-String _readCanvaskitVersion() {
-  final YamlMap canvaskitLock = loadYaml(File(pathlib.join(
-    environment.webUiDevDir.path,
-    'canvaskit_lock.yaml',
-  )).readAsStringSync()) as YamlMap;
-  return canvaskitLock['canvaskit_version'] as String;
-}
-
-Future<void> _updateDepsFile(String cipdInstanceId) async {
-  final File depsFile = File(pathlib.join(
-    environment.flutterDirectory.path,
-    'DEPS',
-  ));
-
-  final String originalDepsCode = await depsFile.readAsString();
-  final List<String> rewrittenDepsCode = <String>[];
-  const String kCanvasKitDependencyKeyInDeps = '\'canvaskit_cipd_instance\': \'';
-  bool canvaskitDependencyFound = false;
-  for (final String line in originalDepsCode.split('\n')) {
-    if (line.trim().startsWith(kCanvasKitDependencyKeyInDeps)) {
-      canvaskitDependencyFound = true;
-      rewrittenDepsCode.add(
-        "  'canvaskit_cipd_instance': '$cipdInstanceId',",
-      );
-    } else {
-      rewrittenDepsCode.add(line);
-    }
-  }
-
-  if (!canvaskitDependencyFound) {
-    stderr.writeln(
-      'Failed to update the DEPS file.\n'
-      'Could not to locate CanvasKit dependency in the DEPS file. Make sure the '
-      'DEPS file contains a line like this:\n'
-      '\n'
-      '  \'canvaskit_cipd_instance\': \'SOME_VALUE\','
-    );
-    exit(1);
-  }
-
-  await depsFile.writeAsString(rewrittenDepsCode.join('\n'));
-}
-
-Future<void> _updateCanvaskitInitializationCode(String canvaskitVersion) async {
-  const String kCanvasKitVersionKey = 'const String _canvaskitVersion';
-  const String kPathToConfigurationCode = 'lib/src/engine/configuration.dart';
-  final File initializationFile = File(pathlib.join(
-    environment.webUiRootDir.path,
-    kPathToConfigurationCode,
-  ));
-  final String originalInitializationCode = await initializationFile.readAsString();
-
-  final List<String> rewrittenCode = <String>[];
-  bool canvaskitVersionFound = false;
-  for (final String line in originalInitializationCode.split('\n')) {
-    if (line.trim().startsWith(kCanvasKitVersionKey)) {
-      canvaskitVersionFound = true;
-      rewrittenCode.add(
-        "const String _canvaskitVersion = '$canvaskitVersion';",
-      );
-    } else {
-      rewrittenCode.add(line);
-    }
-  }
-
-  if (!canvaskitVersionFound) {
-    stderr.writeln(
-      'Failed to update CanvasKit version in $kPathToConfigurationCode.\n'
-      'Could not to locate the constant that defines the version. Make sure the '
-      '$kPathToConfigurationCode file contains a line like this:\n'
-      '\n'
-      'const String _canvaskitVersion = \'VERSION\';'
-    );
-    exit(1);
-  }
-
-  await initializationFile.writeAsString(rewrittenCode.join('\n'));
-}
diff --git a/engine/dev/chrome.dart b/engine/dev/chrome.dart
deleted file mode 100644
index f793153..0000000
--- a/engine/dev/chrome.dart
+++ /dev/null
@@ -1,282 +0,0 @@
-// Copyright 2013 The Flutter 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 'dart:math' as math;
-
-import 'package:image/image.dart';
-import 'package:pedantic/pedantic.dart';
-import 'package:test_api/src/backend/runtime.dart';
-import 'package:webkit_inspection_protocol/webkit_inspection_protocol.dart'
-    as wip;
-
-import 'browser.dart';
-import 'browser_lock.dart';
-import 'chrome_installer.dart';
-import 'common.dart';
-import 'environment.dart';
-
-/// Provides an environment for desktop Chrome.
-class ChromeEnvironment implements BrowserEnvironment {
-  late final BrowserInstallation _installation;
-
-  @override
-  Browser launchBrowserInstance(Uri url, {bool debug = false}) {
-    return Chrome(url, _installation, debug: debug);
-  }
-
-  @override
-  Runtime get packageTestRuntime => Runtime.chrome;
-
-  @override
-  Future<void> prepare() async {
-    final String version = browserLock.chromeLock.versionForCurrentPlatform;
-    _installation = await getOrInstallChrome(
-      version,
-      infoLog: isCirrus ? stdout : DevNull(),
-    );
-  }
-
-  @override
-  ScreenshotManager? getScreenshotManager() {
-    // Always compare screenshots when running tests locally. On CI only compare
-    // on Linux.
-    if (Platform.isLinux || !isLuci) {
-      return ChromeScreenshotManager();
-    }
-    return null;
-  }
-
-  @override
-  String get packageTestConfigurationYamlFile => 'dart_test_chrome.yaml';
-}
-
-/// Runs desktop Chrome.
-///
-/// Most of the communication with the browser is expected to happen via HTTP,
-/// so this exposes a bare-bones API. The browser starts as soon as the class is
-/// constructed, and is killed when [close] is called.
-///
-/// Any errors starting or running the process are reported through [onExit].
-class Chrome extends Browser {
-  @override
-  final String name = 'Chrome';
-
-  @override
-  final Future<Uri> remoteDebuggerUrl;
-
-  /// Starts a new instance of Chrome open to the given [url], which may be a
-  /// [Uri] or a [String].
-  factory Chrome(Uri url, BrowserInstallation installation, {bool debug = false}) {
-    final Completer<Uri> remoteDebuggerCompleter = Completer<Uri>.sync();
-    return Chrome._(() async {
-      // A good source of various Chrome CLI options:
-      // https://peter.sh/experiments/chromium-command-line-switches/
-      //
-      // Things to try:
-      // --font-render-hinting
-      // --enable-font-antialiasing
-      // --gpu-rasterization-msaa-sample-count
-      // --disable-gpu
-      // --disallow-non-exact-resource-reuse
-      // --disable-font-subpixel-positioning
-      final bool isChromeNoSandbox =
-          Platform.environment['CHROME_NO_SANDBOX'] == 'true';
-      final String dir = environment.webUiDartToolDir.createTempSync('test_chrome_user_data_').resolveSymbolicLinksSync();
-      final List<String> args = <String>[
-        '--user-data-dir=$dir',
-        url.toString(),
-        if (!debug)
-          '--headless',
-        if (isChromeNoSandbox)
-          '--no-sandbox',
-        // When headless, this is the actual size of the viewport.
-        if (!debug)
-          '--window-size=$kMaxScreenshotWidth,$kMaxScreenshotHeight',
-        // When debugging, run in maximized mode so there's enough room for DevTools.
-        if (debug)
-          '--start-maximized',
-        if (debug)
-          '--auto-open-devtools-for-tabs',
-        '--disable-extensions',
-        '--disable-popup-blocking',
-        // Indicates that the browser is in "browse without sign-in" (Guest session) mode.
-        '--bwsi',
-        '--no-first-run',
-        '--no-default-browser-check',
-        '--disable-default-apps',
-        '--disable-translate',
-        '--remote-debugging-port=$kDevtoolsPort',
-      ];
-
-      final Process process =
-          await _spawnChromiumProcess(installation.executable, args);
-
-      remoteDebuggerCompleter.complete(
-          getRemoteDebuggerUrl(Uri.parse('http://localhost:$kDevtoolsPort')));
-
-      unawaited(process.exitCode
-          .then((_) => Directory(dir).deleteSync(recursive: true)));
-
-      return process;
-    }, remoteDebuggerCompleter.future);
-  }
-
-  Chrome._(Future<Process> Function() startBrowser, this.remoteDebuggerUrl)
-      : super(startBrowser);
-}
-
-/// Used by [Chrome] to detect a glibc bug and retry launching the
-/// browser.
-///
-/// Once every few thousands of launches we hit this glibc bug:
-///
-/// https://sourceware.org/bugzilla/show_bug.cgi?id=19329.
-///
-/// When this happens Chrome spits out something like the following then exits with code 127:
-///
-///     Inconsistency detected by ld.so: ../elf/dl-tls.c: 493: _dl_allocate_tls_init: Assertion `listp->slotinfo[cnt].gen <= GL(dl_tls_generation)' failed!
-const String _kGlibcError = 'Inconsistency detected by ld.so';
-
-Future<Process> _spawnChromiumProcess(String executable, List<String> args, { String? workingDirectory }) async {
-  // Keep attempting to launch the browser until one of:
-  // - Chrome launched successfully, in which case we just return from the loop.
-  // - The tool detected an unretriable Chrome error, in which case we throw ToolExit.
-  while (true) {
-    final Process process = await Process.start(executable, args, workingDirectory: workingDirectory);
-
-    process.stdout
-      .transform(utf8.decoder)
-      .transform(const LineSplitter())
-      .listen((String line) {
-        print('[CHROME STDOUT]: $line');
-      });
-
-    // Wait until the DevTools are listening before trying to connect. This is
-    // only required for flutter_test --platform=chrome and not flutter run.
-    bool hitGlibcBug = false;
-    await process.stderr
-      .transform(utf8.decoder)
-      .transform(const LineSplitter())
-      .map((String line) {
-        print('[CHROME STDERR]:$line');
-        if (line.contains(_kGlibcError)) {
-          hitGlibcBug = true;
-        }
-        return line;
-      })
-      .firstWhere((String line) => line.startsWith('DevTools listening'), orElse: () {
-        if (hitGlibcBug) {
-          const String message = 'Encountered glibc bug '
-              'https://sourceware.org/bugzilla/show_bug.cgi?id=19329. '
-              'Will try launching browser again.';
-          print(message);
-          return message;
-        }
-        print('Failed to launch browser. Command used to launch it: ${args.join(' ')}');
-        throw Exception(
-          'Failed to launch browser. Make sure you are using an up-to-date '
-          'Chrome or Edge. Otherwise, consider using -d web-server instead '
-          'and filing an issue at https://github.com/flutter/flutter/issues.',
-        );
-      });
-
-    if (!hitGlibcBug) {
-      return process;
-    }
-
-    // A precaution that avoids accumulating browser processes, in case the
-    // glibc bug doesn't cause the browser to quit and we keep looping and
-    // launching more processes.
-    process.exitCode.timeout(const Duration(seconds: 1), onTimeout: () {
-      process.kill();
-      return -1;
-    });
-  }
-}
-
-/// Returns the full URL of the Chrome remote debugger for the main page.
-///
-/// This takes the [base] remote debugger URL (which points to a browser-wide
-/// page) and uses its JSON API to find the resolved URL for debugging the host
-/// page.
-Future<Uri> getRemoteDebuggerUrl(Uri base) async {
-  try {
-    final HttpClient client = HttpClient();
-    final HttpClientRequest request = await client.getUrl(base.resolve('/json/list'));
-    final HttpClientResponse response = await request.close();
-    final List<dynamic>? jsonObject =
-        await json.fuse(utf8).decoder.bind(response).single as List<dynamic>?;
-    return base.resolve(jsonObject!.first['devtoolsFrontendUrl'] as String);
-  } catch (_) {
-    // If we fail to talk to the remote debugger protocol, give up and return
-    // the raw URL rather than crashing.
-    return base;
-  }
-}
-
-/// [ScreenshotManager] implementation for Chrome.
-///
-/// This manager can be used for both macOS and Linux.
-// TODO(yjbanov): extends tests to Window, https://github.com/flutter/flutter/issues/65673
-class ChromeScreenshotManager extends ScreenshotManager {
-  @override
-  String get filenameSuffix => '';
-
-  /// Capture a screenshot of the web content.
-  ///
-  /// Uses Webkit Inspection Protocol server's `captureScreenshot` API.
-  ///
-  /// [region] is used to decide which part of the web content will be used in
-  /// test image. It includes starting coordinate x,y as well as height and
-  /// width of the area to capture.
-  @override
-  Future<Image> capture(math.Rectangle<num>? region) async {
-    final wip.ChromeConnection chromeConnection =
-        wip.ChromeConnection('localhost', kDevtoolsPort);
-    final wip.ChromeTab? chromeTab = await chromeConnection.getTab(
-        (wip.ChromeTab chromeTab) => chromeTab.url.contains('localhost'));
-    if (chromeTab == null) {
-      throw StateError(
-        'Failed locate Chrome tab with the test page',
-      );
-    }
-    final wip.WipConnection wipConnection = await chromeTab.connect();
-
-    Map<String, dynamic>? captureScreenshotParameters;
-    if (region != null) {
-      captureScreenshotParameters = <String, dynamic>{
-        'format': 'png',
-        'clip': <String, dynamic>{
-          'x': region.left,
-          'y': region.top,
-          'width': region.width,
-          'height': region.height,
-          'scale':
-              // This is NOT the DPI of the page, instead it's the "zoom level".
-              1,
-        },
-      };
-    }
-
-    // Setting hardware-independent screen parameters:
-    // https://chromedevtools.github.io/devtools-protocol/tot/Emulation
-    await wipConnection
-        .sendCommand('Emulation.setDeviceMetricsOverride', <String, dynamic>{
-      'width': kMaxScreenshotWidth,
-      'height': kMaxScreenshotHeight,
-      'deviceScaleFactor': 1,
-      'mobile': false,
-    });
-    final wip.WipResponse response = await wipConnection.sendCommand(
-        'Page.captureScreenshot', captureScreenshotParameters);
-
-    final Image screenshot =
-        decodePng(base64.decode(response.result!['data'] as String))!;
-
-    return screenshot;
-  }
-}
diff --git a/engine/dev/chrome_installer.dart b/engine/dev/chrome_installer.dart
deleted file mode 100644
index 72d432e..0000000
--- a/engine/dev/chrome_installer.dart
+++ /dev/null
@@ -1,253 +0,0 @@
-// Copyright 2013 The Flutter 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:io' as io;
-import 'dart:typed_data';
-
-import 'package:archive/archive.dart';
-import 'package:archive/archive_io.dart';
-import 'package:http/http.dart';
-import 'package:path/path.dart' as path;
-
-import 'common.dart';
-import 'environment.dart';
-import 'exceptions.dart';
-
-/// Returns the installation of Chrome, installing it if necessary.
-///
-/// If [requestedVersion] is null, uses the version specified on the
-/// command-line. If not specified on the command-line, uses the version
-/// specified in the "browser_lock.yaml" file.
-///
-/// If [requestedVersion] is not null, installs that version. The value
-/// may be "latest" (the latest available build of Chrome), "system"
-/// (manually installed Chrome on the current operating system), or an
-/// exact build nuber, such as 695653. Build numbers can be found here:
-///
-/// https://commondatastorage.googleapis.com/chromium-browser-snapshots/index.html?prefix=Linux_x64/
-Future<BrowserInstallation> getOrInstallChrome(
-  String requestedVersion, {
-  StringSink? infoLog,
-}) async {
-  infoLog ??= io.stdout;
-
-  if (requestedVersion == 'system') {
-    return BrowserInstallation(
-      version: 'system',
-      executable: await _findSystemChromeExecutable(),
-    );
-  }
-
-  ChromeInstaller? installer;
-  try {
-    installer = requestedVersion == 'latest'
-        ? await ChromeInstaller.latest()
-        : ChromeInstaller(version: requestedVersion);
-
-    if (installer.isInstalled) {
-      infoLog.writeln(
-          'Installation was skipped because Chrome version ${installer.version} is already installed.');
-    } else {
-      infoLog.writeln('Installing Chrome version: ${installer.version}');
-      await installer.install();
-      final BrowserInstallation installation = installer.getInstallation()!;
-      infoLog.writeln(
-          'Installations complete. To launch it run ${installation.executable}');
-    }
-    return installer.getInstallation()!;
-  } finally {
-    installer?.close();
-  }
-}
-
-Future<String> _findSystemChromeExecutable() async {
-  final io.ProcessResult which =
-      await io.Process.run('which', <String>['google-chrome']);
-
-  if (which.exitCode != 0) {
-    throw BrowserInstallerException(
-        'Failed to locate system Chrome installation.');
-  }
-
-  return which.stdout as String;
-}
-
-/// Manages the installation of a particular [version] of Chrome.
-class ChromeInstaller {
-  factory ChromeInstaller({
-    required String version,
-  }) {
-    if (version == 'system') {
-      throw BrowserInstallerException(
-          'Cannot install system version of Chrome. System Chrome must be installed manually.');
-    }
-    if (version == 'latest') {
-      throw BrowserInstallerException(
-          'Expected a concrete Chromer version, but got $version. Maybe use ChromeInstaller.latest()?');
-    }
-    final io.Directory chromeInstallationDir = io.Directory(
-      path.join(environment.webUiDartToolDir.path, 'chrome'),
-    );
-    final io.Directory versionDir = io.Directory(
-      path.join(chromeInstallationDir.path, version),
-    );
-    return ChromeInstaller._(
-      version: version,
-      chromeInstallationDir: chromeInstallationDir,
-      versionDir: versionDir,
-    );
-  }
-
-  static Future<ChromeInstaller> latest() async {
-    final String latestVersion = await fetchLatestChromeVersion();
-    return ChromeInstaller(version: latestVersion);
-  }
-
-  ChromeInstaller._({
-    required this.version,
-    required this.chromeInstallationDir,
-    required this.versionDir,
-  });
-
-  /// Chrome version managed by this installer.
-  final String version;
-
-  /// HTTP client used to download Chrome.
-  final Client client = Client();
-
-  /// Root directory that contains Chrome versions.
-  final io.Directory chromeInstallationDir;
-
-  /// Installation directory for Chrome of the requested [version].
-  final io.Directory versionDir;
-
-  bool get isInstalled {
-    return versionDir.existsSync();
-  }
-
-  BrowserInstallation? getInstallation() {
-    if (!isInstalled) {
-      return null;
-    }
-
-    return BrowserInstallation(
-      version: version,
-      executable: PlatformBinding.instance.getChromeExecutablePath(versionDir),
-    );
-  }
-
-  Future<void> install() async {
-    if (isLuci) {
-      throw StateError(
-        'Rejecting attempt to install Chromium on LUCI. LUCI is expected to '
-        'provide Chromium as a CIPD dependency managed using .ci.yaml.',
-      );
-    }
-
-    if (versionDir.existsSync()) {
-      versionDir.deleteSync(recursive: true);
-    }
-    versionDir.createSync(recursive: true);
-
-    final String url = PlatformBinding.instance.getChromeDownloadUrl(version);
-    print('Downloading Chrome from $url');
-
-    final StreamedResponse download = await client.send(Request(
-      'GET',
-      Uri.parse(url),
-    ));
-
-    final io.File downloadedFile =
-        io.File(path.join(versionDir.path, 'chrome.zip'));
-    await download.stream.pipe(downloadedFile.openWrite());
-
-    /// Windows LUCI bots does not have a `unzip`. Instead we are
-    /// using `archive` pub package.
-    ///
-    /// We didn't use `archieve` on Mac/Linux since the new files have
-    /// permission issues. For now we are not able change file permissions
-    /// from dart.
-    /// See: https://github.com/dart-lang/sdk/issues/15078.
-    if (io.Platform.isWindows) {
-      final Stopwatch stopwatch = Stopwatch()..start();
-
-      // Read the Zip file from disk.
-      final Uint8List bytes = downloadedFile.readAsBytesSync();
-
-      final Archive archive = ZipDecoder().decodeBytes(bytes);
-
-      // Extract the contents of the Zip archive to disk.
-      for (final ArchiveFile file in archive) {
-        final String filename = file.name;
-        if (file.isFile) {
-          final List<int> data = file.content as List<int>;
-          io.File(path.join(versionDir.path, filename))
-            ..createSync(recursive: true)
-            ..writeAsBytesSync(data);
-        } else {
-          io.Directory(path.join(versionDir.path, filename))
-              .create(recursive: true);
-        }
-      }
-
-      stopwatch.stop();
-      print(
-          'INFO: The unzip took ${stopwatch.elapsedMilliseconds ~/ 1000} seconds.');
-    } else {
-      // We have to unzip into a temporary directory and then copy the files
-      // out because our tests expect the files to be direct children of the
-      // version directory. However, the zip file contains a top-level directory
-      // named e.g. 'chrome-linux'. We need to copy the files out of that
-      // directory and into the version directory.
-      final io.Directory tmpDir = await io.Directory.systemTemp.createTemp();
-      final io.Directory unzipDir = io.Platform.isLinux ? tmpDir : versionDir;
-      final io.ProcessResult unzipResult =
-          await io.Process.run('unzip', <String>[
-        downloadedFile.path,
-        '-d',
-        unzipDir.path,
-      ]);
-      if (unzipResult.exitCode != 0) {
-        throw BrowserInstallerException(
-            'Failed to unzip the downloaded Chrome archive ${downloadedFile.path}.\n'
-            'With the version path ${versionDir.path}\n'
-            'The unzip process exited with code ${unzipResult.exitCode}.');
-      }
-      // For Linux, we need to copy over the files out of the chrome-linux
-      // sub directory.
-      if (io.Platform.isLinux) {
-        final io.Directory chromeLinuxDir =
-            await tmpDir.list().single as io.Directory;
-        await for (final io.FileSystemEntity entity in chromeLinuxDir.list()) {
-          await entity
-              .rename(path.join(versionDir.path, path.basename(entity.path)));
-        }
-        await tmpDir.delete(recursive: true);
-      }
-    }
-
-    downloadedFile.deleteSync();
-  }
-
-  void close() {
-    client.close();
-  }
-}
-
-/// Fetches the latest available Chrome build version.
-Future<String> fetchLatestChromeVersion() async {
-  final Client client = Client();
-  try {
-    final Response response = await client.get(Uri.parse(
-        'https://www.googleapis.com/download/storage/v1/b/chromium-browser-snapshots/o/Linux_x64%2FLAST_CHANGE?alt=media'));
-    if (response.statusCode != 200) {
-      throw BrowserInstallerException(
-          'Failed to fetch latest Chrome version. Server returned status code ${response.statusCode}');
-    }
-    return response.body;
-  } finally {
-    client.close();
-  }
-}
diff --git a/engine/dev/clean.dart b/engine/dev/clean.dart
deleted file mode 100644
index 5e47309..0000000
--- a/engine/dev/clean.dart
+++ /dev/null
@@ -1,59 +0,0 @@
-// Copyright 2013 The Flutter 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:io' as io;
-
-import 'package:args/command_runner.dart';
-import 'package:path/path.dart' as path;
-
-import 'environment.dart';
-import 'utils.dart';
-
-class CleanCommand extends Command<bool> with ArgUtils<bool> {
-  CleanCommand() {
-    argParser
-      ..addFlag(
-        'flutter',
-        defaultsTo: true,
-        help: 'Cleans up the .dart_tool directory under engine/src/flutter. Enabled by default.',
-      )
-      ..addFlag(
-        'ninja',
-        defaultsTo: false,
-        help: 'Also clean up the engine out directory with ninja output. Disabled by default.',
-      );
-  }
-
-  @override
-  String get name => 'clean';
-
-  bool get _alsoCleanNinja => boolArg('ninja');
-
-  bool get _alsoCleanFlutterRepo => boolArg('flutter');
-
-  @override
-  String get description => 'Deletes build caches and artifacts.';
-
-  @override
-  FutureOr<bool> run() async {
-    final List<io.FileSystemEntity> thingsToBeCleaned = <io.FileSystemEntity>[
-      environment.webUiDartToolDir,
-      environment.webUiBuildDir,
-      io.File(path.join(environment.webUiRootDir.path, '.packages')),
-      io.File(path.join(environment.webUiRootDir.path, 'pubspec.lock')),
-      if (_alsoCleanNinja)
-        environment.outDir,
-      if(_alsoCleanFlutterRepo)
-        environment.engineDartToolDir,
-    ];
-
-    await Future.wait(
-      thingsToBeCleaned
-        .where((io.FileSystemEntity entity) => entity.existsSync())
-        .map((io.FileSystemEntity entity) => entity.delete(recursive: true))
-    );
-    return true;
-  }
-}
diff --git a/engine/dev/common.dart b/engine/dev/common.dart
deleted file mode 100644
index 7f41d38..0000000
--- a/engine/dev/common.dart
+++ /dev/null
@@ -1,248 +0,0 @@
-// Copyright 2013 The Flutter 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:io' as io;
-
-import 'package:path/path.dart' as path;
-
-import 'browser.dart';
-import 'browser_lock.dart';
-import 'chrome.dart';
-import 'edge.dart';
-import 'firefox.dart';
-import 'safari_ios.dart';
-import 'safari_macos.dart';
-
-/// The port number for debugging.
-const int kDevtoolsPort = 12345;
-const int kMaxScreenshotWidth = 1024;
-const int kMaxScreenshotHeight = 1024;
-const double kMaxDiffRateFailure = 0.28 / 100; // 0.28%
-
-abstract class PlatformBinding {
-  static PlatformBinding get instance {
-    return _instance ??= _createInstance();
-  }
-
-  static PlatformBinding? _instance;
-
-  static PlatformBinding _createInstance() {
-    if (io.Platform.isLinux) {
-      return _LinuxBinding();
-    }
-    if (io.Platform.isMacOS) {
-      return _MacBinding();
-    }
-    if (io.Platform.isWindows) {
-      return _WindowsBinding();
-    }
-    throw '${io.Platform.operatingSystem} is not supported';
-  }
-
-  String getChromeBuild(ChromeLock chromeLock);
-  String getChromeDownloadUrl(String version);
-  String getFirefoxDownloadUrl(String version);
-  String getFirefoxDownloadFilename(String version);
-  String getChromeExecutablePath(io.Directory versionDir);
-  String getFirefoxExecutablePath(io.Directory versionDir);
-  String getFirefoxLatestVersionUrl();
-  String getMacApplicationLauncher();
-  String getCommandToRunEdge();
-}
-
-const String _kBaseDownloadUrl =
-    'https://www.googleapis.com/download/storage/v1/b/chromium-browser-snapshots/o';
-
-class _WindowsBinding implements PlatformBinding {
-  @override
-  String getChromeBuild(ChromeLock chromeLock) {
-    return chromeLock.windows;
-  }
-
-  @override
-  String getChromeDownloadUrl(String version) =>
-      'https://www.googleapis.com/download/storage/v1/b/chromium-browser-snapshots/o/Win%2F$version%2Fchrome-win.zip?alt=media';
-
-  @override
-  String getChromeExecutablePath(io.Directory versionDir) =>
-      path.join(versionDir.path, 'chrome.exe');
-
-  @override
-  String getFirefoxDownloadUrl(String version) =>
-      'https://download-installer.cdn.mozilla.net/pub/firefox/releases/$version/win64/en-US/'
-      '${getFirefoxDownloadFilename(version)}';
-
-  @override
-  String getFirefoxDownloadFilename(String version) => 'firefox-$version.exe';
-
-  @override
-  String getFirefoxExecutablePath(io.Directory versionDir) =>
-      path.join(versionDir.path, 'firefox', 'firefox');
-
-  @override
-  String getFirefoxLatestVersionUrl() =>
-      'https://download.mozilla.org/?product=firefox-latest&os=win&lang=en-US';
-
-  @override
-  String getMacApplicationLauncher() =>
-      throw UnsupportedError('Safari is not supported on Windows');
-
-  @override
-  String getCommandToRunEdge() => 'MicrosoftEdgeLauncher';
-}
-
-class _LinuxBinding implements PlatformBinding {
-  @override
-  String getChromeBuild(ChromeLock chromeLock) {
-    return chromeLock.linux;
-  }
-
-  @override
-  String getChromeDownloadUrl(String version) =>
-      '$_kBaseDownloadUrl/Linux_x64%2F$version%2Fchrome-linux.zip?alt=media';
-
-  @override
-  String getChromeExecutablePath(io.Directory versionDir) =>
-      path.join(versionDir.path, 'chrome');
-
-  @override
-  String getFirefoxDownloadUrl(String version) =>
-      'https://download-installer.cdn.mozilla.net/pub/firefox/releases/$version/linux-x86_64/en-US/'
-      '${getFirefoxDownloadFilename(version)}';
-
-  @override
-  String getFirefoxDownloadFilename(String version) =>
-      'firefox-$version.tar.bz2';
-
-  @override
-  String getFirefoxExecutablePath(io.Directory versionDir) =>
-      path.join(versionDir.path, 'firefox', 'firefox');
-
-  @override
-  String getFirefoxLatestVersionUrl() =>
-      'https://download.mozilla.org/?product=firefox-latest&os=linux64&lang=en-US';
-
-  @override
-  String getMacApplicationLauncher() =>
-      throw UnsupportedError('Safari is not supported on Linux');
-
-  @override
-  String getCommandToRunEdge() =>
-      throw UnsupportedError('Edge is not supported on Linux');
-}
-
-class _MacBinding implements PlatformBinding {
-  @override
-  String getChromeBuild(ChromeLock chromeLock) {
-    return chromeLock.mac;
-  }
-
-  @override
-  String getChromeDownloadUrl(String version) =>
-      '$_kBaseDownloadUrl/Mac%2F$version%2Fchrome-mac.zip?alt=media';
-
-  @override
-  String getChromeExecutablePath(io.Directory versionDir) => path.join(
-        versionDir.path,
-        'chrome-mac',
-        'Chromium.app',
-        'Contents',
-        'MacOS',
-        'Chromium',
-      );
-
-  @override
-  String getFirefoxDownloadUrl(String version) =>
-      'https://download-installer.cdn.mozilla.net/pub/firefox/releases/$version/mac/en-US/'
-      '${getFirefoxDownloadFilename(version)}';
-
-  @override
-  String getFirefoxDownloadFilename(String version) => 'Firefox $version.dmg';
-
-  @override
-  String getFirefoxExecutablePath(io.Directory versionDir) =>
-      path.join(versionDir.path, 'Firefox.app', 'Contents', 'MacOS', 'firefox');
-
-  @override
-  String getFirefoxLatestVersionUrl() =>
-      'https://download.mozilla.org/?product=firefox-latest&os=osx&lang=en-US';
-
-  @override
-  String getMacApplicationLauncher() => 'open';
-
-  @override
-  String getCommandToRunEdge() =>
-      throw UnimplementedError('Tests for Edge are not implemented for MacOS.');
-}
-
-class BrowserInstallation {
-  const BrowserInstallation({
-    required this.version,
-    required this.executable,
-  });
-
-  /// Browser version.
-  final String version;
-
-  /// Path the browser executable.
-  final String executable;
-}
-
-/// A string sink that swallows all input.
-class DevNull implements StringSink {
-  @override
-  void write(Object? obj) {}
-
-  @override
-  void writeAll(Iterable<dynamic> objects, [String separator = '']) {}
-
-  @override
-  void writeCharCode(int charCode) {}
-
-  @override
-  void writeln([Object? obj = '']) {}
-}
-
-/// Whether the felt command is running on Cirrus CI.
-bool get isCirrus => io.Platform.environment['CIRRUS_CI'] == 'true';
-
-/// Whether the felt command is running on LUCI.
-bool get isLuci => io.Platform.environment['LUCI_CONTEXT'] != null;
-
-/// Whether the felt command is running on one of the Continuous Integration
-/// environements.
-bool get isCi => isCirrus || isLuci;
-
-const String kChrome = 'chrome';
-const String kEdge = 'edge';
-const String kFirefox = 'firefox';
-const String kSafari = 'safari';
-const String kSafariIos = 'ios-safari';
-
-const List<String> kAllBrowserNames = <String>[
-  kChrome,
-  kEdge,
-  kFirefox,
-  kSafari,
-  kSafariIos,
-];
-
-/// Creates an environment for a browser.
-///
-/// The [browserName] matches the browser name passed as the `--browser` option.
-BrowserEnvironment getBrowserEnvironment(String browserName) {
-  switch (browserName) {
-    case kChrome:
-      return ChromeEnvironment();
-    case kEdge:
-      return EdgeEnvironment();
-    case kFirefox:
-      return FirefoxEnvironment();
-    case kSafari:
-      return SafariMacOsEnvironment();
-    case kSafariIos:
-      return SafariIosEnvironment();
-  }
-  throw UnsupportedError('Browser $browserName is not supported.');
-}
diff --git a/engine/dev/create_simulator.dart b/engine/dev/create_simulator.dart
deleted file mode 100644
index 85fd076..0000000
--- a/engine/dev/create_simulator.dart
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright 2013 The Flutter 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:args/command_runner.dart';
-import 'package:simulators/simulator_manager.dart';
-
-import 'browser_lock.dart';
-import 'utils.dart';
-
-class CreateSimulatorCommand extends Command<bool> with ArgUtils<bool> {
-  @override
-  String get name => 'create_simulator';
-
-  @override
-  String get description => 'Creates mobile simulators.';
-
-  @override
-  FutureOr<bool> run() async {
-    final IosSimulatorManager iosSimulatorManager = IosSimulatorManager();
-    try {
-      final SafariIosLock lock = browserLock.safariIosLock;
-      final IosSimulator simulator = await iosSimulatorManager.createSimulator(
-        lock.majorVersion,
-        lock.minorVersion,
-        lock.device,
-      );
-      print('INFO: Simulator created ${simulator.toString()}');
-    } catch (e) {
-      throw Exception('Error creating requested simulator. You can use Xcode '
-          'to install more versions: XCode > Preferences > Components.'
-          ' Exception: $e');
-    }
-    return true;
-  }
-}
diff --git a/engine/dev/edge.dart b/engine/dev/edge.dart
deleted file mode 100644
index bf3acc7..0000000
--- a/engine/dev/edge.dart
+++ /dev/null
@@ -1,74 +0,0 @@
-// Copyright 2013 The Flutter 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:io';
-
-import 'package:test_api/src/backend/runtime.dart';
-
-import 'browser.dart';
-import 'browser_lock.dart';
-import 'common.dart';
-import 'edge_installation.dart';
-
-/// Provides an environment for the desktop Microsoft Edge (Chromium-based).
-class EdgeEnvironment implements BrowserEnvironment {
-  @override
-  Browser launchBrowserInstance(Uri url, {bool debug = false}) {
-    return Edge(url);
-  }
-
-  @override
-  Runtime get packageTestRuntime => Runtime.internetExplorer;
-
-  @override
-  Future<void> prepare() async {
-    // Edge doesn't need any special prep.
-  }
-
-  @override
-  ScreenshotManager? getScreenshotManager() => null;
-
-  @override
-  String get packageTestConfigurationYamlFile => 'dart_test_edge.yaml';
-}
-
-/// Runs desktop Edge.
-///
-/// Most of the communication with the browser is expected to happen via HTTP,
-/// so this exposes a bare-bones API. The browser starts as soon as the class is
-/// constructed, and is killed when [close] is called.
-///
-/// Any errors starting or running the process are reported through [onExit].
-class Edge extends Browser {
-  @override
-  final String name = 'Edge';
-
-  /// Starts a new instance of Safari open to the given [url], which may be a
-  /// [Uri] or a [String].
-  factory Edge(Uri url) {
-    return Edge._(() async {
-      final BrowserInstallation installation = await getEdgeInstallation(
-        browserLock.edgeLock.launcherVersion,
-        infoLog: DevNull(),
-      );
-
-      // Debug is not a valid option for Edge. Remove it.
-      String pathToOpen = url.toString();
-      if(pathToOpen.contains('debug')) {
-        final int index = pathToOpen.indexOf('debug');
-        pathToOpen = pathToOpen.substring(0, index-1);
-      }
-
-      final Process process = await Process.start(
-        installation.executable,
-        <String>[pathToOpen,'-k'],
-      );
-
-      return process;
-    });
-  }
-
-  Edge._(Future<Process> Function() startBrowser) : super(startBrowser);
-}
diff --git a/engine/dev/edge_installation.dart b/engine/dev/edge_installation.dart
deleted file mode 100644
index c10e220..0000000
--- a/engine/dev/edge_installation.dart
+++ /dev/null
@@ -1,121 +0,0 @@
-// Copyright 2013 The Flutter 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:io' as io;
-
-import 'package:http/http.dart';
-import 'package:path/path.dart' as path;
-
-import 'browser_lock.dart';
-import 'common.dart';
-import 'environment.dart';
-
-/// Returns the installation of Edge.
-///
-/// Currently uses the Edge version installed on the operating system.
-///
-/// As explained on the Microsoft help page: `Microsoft Edge comes
-/// exclusively with Windows 10 and cannot be downloaded or installed separately.`
-/// See: https://support.microsoft.com/en-us/help/17171/microsoft-edge-get-to-know
-///
-// TODO(yjbanov): Investigate running tests for the tech preview downloads
-// from the beta channel.
-Future<BrowserInstallation> getEdgeInstallation(
-  String requestedVersion, {
-  StringSink? infoLog,
-}) async {
-  // For now these tests are aimed to run only on Windows machines local or on LUCI/CI.
-  // In the future we can investigate to run them on Android or on MacOS.
-  if (!io.Platform.isWindows) {
-    throw UnimplementedError(
-        'Tests for Edge on ${io.Platform.operatingSystem} is'
-        ' not supported.');
-  }
-
-  infoLog ??= io.stdout;
-
-  if (requestedVersion == 'system') {
-    // Since Edge is included in Windows, always assume there will be one on the
-    // system.
-    infoLog.writeln('Using the system version that is already installed.');
-    final EdgeLauncher edgeLauncher = EdgeLauncher();
-    if (edgeLauncher.isInstalled) {
-      infoLog.writeln('Launcher installation was skipped, already installed.');
-    } else {
-      infoLog.writeln('Installing MicrosoftEdgeLauncher');
-      await edgeLauncher.install();
-      infoLog.writeln(
-          'Installations complete. To launch it run ${edgeLauncher.executable}');
-    }
-
-    return BrowserInstallation(
-      version: 'system',
-      executable: io.Directory(path.join(
-              edgeLauncher.launcherInstallationDir.path,
-              PlatformBinding.instance.getCommandToRunEdge()))
-          .path,
-    );
-  } else {
-    infoLog.writeln('Unsupported version $requestedVersion.');
-    throw UnimplementedError();
-  }
-}
-
-/// `MicrosoftEdgeLauncher` is an executable for launching Edge.
-///
-/// It is useful for starting Edge from comand line or from a
-/// batch script.
-///
-/// See: https://github.com/MicrosoftEdge/edge-launcher
-class EdgeLauncher {
-  /// Path to the directory that contains `MicrosoftEdgeLauncher.exe`.
-  io.Directory get launcherInstallationDir => io.Directory(
-        path.join(environment.webUiDartToolDir.path, 'microsoftedgelauncher',
-            version),
-      );
-
-  io.File get executable => io.File(
-      path.join(launcherInstallationDir.path, 'MicrosoftEdgeLauncher.exe'));
-
-  bool get isInstalled => executable.existsSync();
-
-  /// Version number launcher executable  `MicrosoftEdgeLauncher`.
-  String get version => browserLock.edgeLock.launcherVersion;
-
-  /// Url for downloading  `MicrosoftEdgeLauncher`.
-  ///
-  /// Only useful in Windows, hence not added to [PlatformBinding].
-  String get windowsEdgeLauncherDownloadUrl =>
-      'https://github.com/MicrosoftEdge/edge-launcher/releases/download/$version/MicrosoftEdgeLauncher.exe';
-
-  EdgeLauncher();
-
-  /// Install the launcher if it does not exist in this system.
-  Future<void> install() async {
-    // Checks if the  `MicrosoftEdgeLauncher` executable exists.
-    if (isInstalled) {
-      return;
-    }
-
-    // Create directory for download.
-    if (!launcherInstallationDir.existsSync()) {
-      launcherInstallationDir.createSync(recursive: true);
-    }
-
-    final Client client = Client();
-
-    try {
-      // Download executable from Github.
-      final StreamedResponse download = await client.send(Request(
-        'GET',
-        Uri.parse(windowsEdgeLauncherDownloadUrl),
-      ));
-
-      await download.stream.pipe(executable.openWrite());
-    } finally {
-      client.close();
-    }
-  }
-}
diff --git a/engine/dev/environment.dart b/engine/dev/environment.dart
deleted file mode 100644
index 8bba0f5..0000000
--- a/engine/dev/environment.dart
+++ /dev/null
@@ -1,195 +0,0 @@
-// Copyright 2013 The Flutter 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:io' as io;
-import 'package:path/path.dart' as pathlib;
-
-import 'exceptions.dart';
-
-/// Contains various environment variables, such as common file paths and command-line options.
-Environment get environment {
-  return _environment ??= Environment();
-}
-
-Environment? _environment;
-
-/// Contains various environment variables, such as common file paths and command-line options.
-class Environment {
-  factory Environment() {
-    final io.File self = io.File.fromUri(io.Platform.script);
-    final io.Directory engineSrcDir = self.parent.parent.parent.parent.parent;
-    final io.Directory engineToolsDir =
-        io.Directory(pathlib.join(engineSrcDir.path, 'flutter', 'tools'));
-    final io.Directory outDir =
-        io.Directory(pathlib.join(engineSrcDir.path, 'out'));
-    final io.Directory hostDebugUnoptDir =
-        io.Directory(pathlib.join(outDir.path, 'host_debug_unopt'));
-    final io.Directory dartSdkDir =
-        io.Directory(pathlib.join(hostDebugUnoptDir.path, 'dart-sdk'));
-    final io.Directory webUiRootDir = io.Directory(
-        pathlib.join(engineSrcDir.path, 'flutter', 'lib', 'web_ui'));
-
-    for (final io.Directory expectedDirectory in <io.Directory>[
-      engineSrcDir,
-      outDir,
-      hostDebugUnoptDir,
-      dartSdkDir,
-      webUiRootDir
-    ]) {
-      if (!expectedDirectory.existsSync()) {
-        throw ToolExit('$expectedDirectory does not exist.');
-      }
-    }
-
-    return Environment._(
-      self: self,
-      webUiRootDir: webUiRootDir,
-      engineSrcDir: engineSrcDir,
-      engineToolsDir: engineToolsDir,
-      outDir: outDir,
-      hostDebugUnoptDir: hostDebugUnoptDir,
-      dartSdkDir: dartSdkDir,
-    );
-  }
-
-  Environment._({
-    required this.self,
-    required this.webUiRootDir,
-    required this.engineSrcDir,
-    required this.engineToolsDir,
-    required this.outDir,
-    required this.hostDebugUnoptDir,
-    required this.dartSdkDir,
-  });
-
-  /// The Dart script that's currently running.
-  final io.File self;
-
-  /// Path to the "web_ui" package sources.
-  final io.Directory webUiRootDir;
-
-  /// Path to the engine's "src" directory.
-  final io.Directory engineSrcDir;
-
-  /// Path to the engine's "tools" directory.
-  final io.Directory engineToolsDir;
-
-  /// Path to the engine's "out" directory.
-  ///
-  /// This is where you'll find the ninja output, such as the Dart SDK.
-  final io.Directory outDir;
-
-  /// The "host_debug_unopt" build of the Dart SDK.
-  final io.Directory hostDebugUnoptDir;
-
-  /// The root of the Dart SDK.
-  final io.Directory dartSdkDir;
-
-  /// The "dart" executable file.
-  String get dartExecutable => pathlib.join(dartSdkDir.path, 'bin', 'dart');
-
-  /// The "pub" executable file.
-  String get pubExecutable => pathlib.join(dartSdkDir.path, 'bin', 'pub');
-
-  /// Path to where github.com/flutter/engine is checked out inside the engine workspace.
-  io.Directory get flutterDirectory =>
-      io.Directory(pathlib.join(engineSrcDir.path, 'flutter'));
-  io.Directory get webSdkRootDir => io.Directory(pathlib.join(
-        flutterDirectory.path,
-        'web_sdk',
-      ));
-
-  /// Path to the "web_engine_tester" package.
-  io.Directory get webEngineTesterRootDir => io.Directory(pathlib.join(
-        webSdkRootDir.path,
-        'web_engine_tester',
-      ));
-
-  /// Path to the "build" directory, generated by "package:build_runner".
-  ///
-  /// This is where compiled output goes.
-  io.Directory get webUiBuildDir => io.Directory(pathlib.join(
-        webUiRootDir.path,
-        'build',
-      ));
-
-  /// Path to the ".dart_tool" directory, generated by various Dart tools.
-  io.Directory get webUiDartToolDir => io.Directory(pathlib.join(
-        webUiRootDir.path,
-        '.dart_tool',
-      ));
-
-  /// Path to the ".dart_tool" directory living under `engine/src/flutter`.
-  ///
-  /// This is a designated area for tool downloads which can be used by
-  /// multiple platforms. For exampe: Flutter repo for e2e tests.
-  io.Directory get engineDartToolDir => io.Directory(pathlib.join(
-        engineSrcDir.path,
-        'flutter',
-        '.dart_tool',
-      ));
-
-  /// Path to the "dev" directory containing engine developer tools and
-  /// configuration files.
-  io.Directory get webUiDevDir => io.Directory(pathlib.join(
-        webUiRootDir.path,
-        'dev',
-      ));
-
-  /// Path to the "test" directory containing web engine tests.
-  io.Directory get webUiTestDir => io.Directory(pathlib.join(
-        webUiRootDir.path,
-        'test',
-      ));
-
-  /// Path to the "lib" directory containing web engine code.
-  io.Directory get webUiLibDir => io.Directory(pathlib.join(
-        webUiRootDir.path,
-        'lib',
-      ));
-
-  /// Path to the clone of the flutter/goldens repository.
-  io.Directory get webUiGoldensRepositoryDirectory => io.Directory(pathlib.join(
-        webUiBuildDir.path,
-        'goldens',
-      ));
-
-  /// Path to the base directory to be used by Skia Gold.
-  io.Directory get webUiSkiaGoldDirectory => io.Directory(pathlib.join(
-        webUiDartToolDir.path,
-        'skia_gold',
-      ));
-
-  /// Directory to add test results which would later be uploaded to a gcs
-  /// bucket by LUCI.
-  io.Directory get webUiTestResultsDirectory => io.Directory(pathlib.join(
-        webUiDartToolDir.path,
-        'test_results',
-      ));
-
-  /// Path to the screenshots taken by iOS simulator.
-  io.Directory get webUiSimulatorScreenshotsDirectory =>
-      io.Directory(pathlib.join(
-        webUiDartToolDir.path,
-        'ios_screenshots',
-      ));
-
-  /// Path to the script that clones the Flutter repo.
-  io.File get cloneFlutterScript => io.File(pathlib.join(
-        engineToolsDir.path,
-        'clone_flutter.sh',
-      ));
-
-  /// Path to flutter.
-  ///
-  /// For example, this can be used to run `flutter pub get`.
-  ///
-  /// Only use [cloneFlutterScript] to clone flutter to the engine build.
-  io.File get flutterCommand => io.File(pathlib.join(
-        engineDartToolDir.path,
-        'flutter',
-        'bin',
-        'flutter',
-      ));
-}
diff --git a/engine/dev/exceptions.dart b/engine/dev/exceptions.dart
deleted file mode 100644
index 319eae1..0000000
--- a/engine/dev/exceptions.dart
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright 2013 The Flutter Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-class BrowserInstallerException implements Exception {
-  BrowserInstallerException(this.message);
-
-  final String message;
-
-  @override
-  String toString() => message;
-}
-
-/// Throw this exception in felt command to exit felt with a message and a
-/// non-zero exit code.
-class ToolExit implements Exception {
-  ToolExit(this.message, { this.exitCode = 1 });
-
-  final String message;
-  final int exitCode;
-
-  @override
-  String toString() => message;
-}
diff --git a/engine/dev/felt b/engine/dev/felt
deleted file mode 100755
index 1aef3b4..0000000
--- a/engine/dev/felt
+++ /dev/null
@@ -1,124 +0,0 @@
-#!/bin/bash
-set -e
-
-# felt: a command-line utility for building and testing Flutter web engine.
-#       It stands for Flutter Engine Local Tester.
-# TODO: Add git fetch --tags step. Tags are critical for the correct Dart
-# version.
-
-FELT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
-
-if [ -z "`which gclient`" ]
-then
-  echo "ERROR: gclient is not in your PATH"
-  echo "Fix: add the path to your installation of depot_tools to your PATH"
-  exit 1
-fi
-GCLIENT_PATH=`which gclient`
-
-if [ -z "`which autoninja`" ]
-then
-  echo "ERROR: autoninja is not in your PATH"
-  echo "Fix: add the path to your installation of depot_tools to your PATH"
-  exit 1
-fi
-NINJA_PATH=`which autoninja`
-
-ENGINE_SRC_DIR="$(dirname $(dirname $(dirname $(dirname ${FELT_DIR}))))"
-FLUTTER_DIR="${ENGINE_SRC_DIR}/flutter"
-WEB_UI_DIR="${FLUTTER_DIR}/lib/web_ui"
-DEV_DIR="${WEB_UI_DIR}/dev"
-OUT_DIR="${ENGINE_SRC_DIR}/out"
-HOST_DEBUG_UNOPT_DIR="${ENGINE_SRC_DIR}/out/host_debug_unopt"
-DART_SDK_DIR="${ENGINE_SRC_DIR}/out/host_debug_unopt/dart-sdk"
-GN="${FLUTTER_DIR}/tools/gn"
-DART_TOOL_DIR="${WEB_UI_DIR}/.dart_tool"
-DART_PATH="$DART_SDK_DIR/bin/dart"
-SNAPSHOT_PATH="${DART_TOOL_DIR}/felt.snapshot"
-STAMP_PATH="${DART_TOOL_DIR}/felt.snapshot.stamp"
-SCRIPT_PATH="${DEV_DIR}/felt.dart"
-REVISION="$(cd "$FLUTTER_DIR"; git rev-parse HEAD)"
-
-if [ ! -f "${DART_PATH}" ]
-then
-  echo "Compiling the Dart SDK."
-  gclient sync
-  $GN --unoptimized --full-dart-sdk
-  "$NINJA_PATH" -C "$HOST_DEBUG_UNOPT_DIR"
-fi
-
-install_deps() {
-  echo "Running \`dart pub get\` in 'engine/src/flutter/lib/web_ui'"
-  (cd "$WEB_UI_DIR"; $DART_PATH pub get)
-
-  echo "Running \`dart pub get\` in 'engine/src/flutter/web_sdk/web_engine_tester'"
-  (cd "$FLUTTER_DIR/web_sdk/web_engine_tester"; $DART_PATH pub get)
-}
-
-KERNEL_NAME=`uname`
-if [[ $KERNEL_NAME == *"Darwin"* ]]
-then
-  echo "Running on MacOS. Will check the file and user limits."
-  # Disable exit if the commands fails. We want to give more actionable
-  # error message.
-  set +e
-  ULIMIT_FILES=`ulimit -n`
-  # Increase the file limit if it is low. Note that these limits are changed
-  # only for this script (for this shell). After felt execution is completed
-  # no change is required to reset the original shell.
-  if [[ $ULIMIT_FILES -lt 50000 ]]
-  then
-    echo "File limits too low increasing the file limits"
-    # Get the max file limit.
-    MAX_ULIMIT_FILES=`launchctl limit maxfiles | sed -e 's/^[[:space:]]*//' | sed 's/ \{1,\}/ /g' | cut -d' ' -f2`
-    if [[ $MAX_ULIMIT_FILES -lt 50000 ]]
-    then
-      # Increase the maximum file limit.
-      sudo launchctl limit maxfiles 50000 200000
-    fi
-    ERROR=$(ulimit -n 50000 2>&1 >/dev/null)
-    if [[ ! -z $ERROR ]]
-    then
-      echo "Problem changing the file limit. Please try to reboot to use the higher limits. error: \n$ERROR" 1>&2
-    fi
-  fi
-  ULIMIT_USER=`ulimit -u`
-  # Increase the hard user limit if it is lower than 2048.
-  if [[ $ULIMIT_USER -lt 4096 ]]
-  then
-    echo "User limits too low increasing the user limits"
-    ERROR2=$(ulimit -u 4096 2>&1 >/dev/null)
-    if [[ ! -z $ERROR2 ]]
-    then
-      echo "Problem changing the user limit. Please try to reboot to use the higher limits. error: \n$ERROR2" 1>&2
-    fi
-  fi
-  # Set the value back to exit on non zero results.
-  set -e
-fi
-
-if [[ "$FELT_USE_SNAPSHOT" == "false" || "$FELT_USE_SNAPSHOT" == "0" ]]; then
-  echo "[Snapshot mode: off]"
-  # Running without snapshot means there is high likelihood of local changes. In
-  # that case, let's clear the snapshot to avoid any surprises.
-  rm -f "$SNAPSHOT_PATH"
-  rm -f "$STAMP_PATH"
-  install_deps
-  $DART_SDK_DIR/bin/dart "$DEV_DIR/felt.dart" $@
-else
-  # Create a new snapshot if any of the following is true:
-  #  * SNAPSHOT_PATH is not a file, or
-  #  * STAMP_PATH is not a file with nonzero size, or
-  #  * Contents of STAMP_PATH is not our local git HEAD revision, or
-  #  * pubspec.yaml last modified after pubspec.lock
-  if [[ ! -f $SNAPSHOT_PATH || ! -s "$STAMP_PATH" || "$(cat "$STAMP_PATH")" != "$REVISION" || "$WEB_UI_DIR/pubspec.yaml" -nt "$WEB_UI_DIR/pubspec.lock" ]]; then
-    echo "[Snapshot mode: on] (creating a new snapshot)"
-    install_deps
-    mkdir -p $DART_TOOL_DIR
-
-    "$DART_SDK_DIR/bin/dart" --snapshot="$SNAPSHOT_PATH" --packages="$WEB_UI_DIR/.packages" "$SCRIPT_PATH"
-    echo "$REVISION" > "$STAMP_PATH"
-  fi
-
-  $DART_SDK_DIR/bin/dart --packages="$WEB_UI_DIR/.packages" "$SNAPSHOT_PATH" $@
-fi
diff --git a/engine/dev/felt.bat b/engine/dev/felt.bat
deleted file mode 100644
index c3c15cf..0000000
--- a/engine/dev/felt.bat
+++ /dev/null
@@ -1,69 +0,0 @@
-:: felt: a command-line utility for Windows for building and testing
-:: Flutter web engine.
-:: FELT stands for Flutter Engine Local Tester.
-
-@ECHO OFF
-SETLOCAL
-
-:: Make sure gclient and ninja exist. Otherwise felt won't work.
-FOR /F "tokens=1-2 delims=:" %%a in ('where gclient') DO SET GCLIENT_PATH=%%b
-IF %GCLIENT_PATH%==[] (ECHO "ERROR: gclient is not in your PATH")
-
-FOR /F "tokens=1-2 delims=:" %%a in ('where ninja') DO SET NINJA_PATH=%%b
-IF %NINJA_PATH%==[] (ECHO "ERROR: ninja is not in your PATH")
-
-:: Starting from this script's path, walk up to engine source directory.
-SET SCRIPT_DIR=%~dp0
-FOR %%a IN ("%SCRIPT_DIR:~0,-1%") DO SET TMP=%%~dpa
-FOR %%a IN ("%TMP:~0,-1%") DO SET TMP=%%~dpa
-FOR %%a IN ("%TMP:~0,-1%") DO SET TMP=%%~dpa
-FOR %%a IN ("%TMP:~0,-1%") DO SET ENGINE_SRC_DIR=%%~dpa
-
-SET ENGINE_SRC_DIR=%ENGINE_SRC_DIR:~0,-1%
-SET OUT_DIR=%ENGINE_SRC_DIR%\out
-SET HOST_DEBUG_UNOPT_DIR=%OUT_DIR%\host_debug_unopt
-SET DART_SDK_DIR=%HOST_DEBUG_UNOPT_DIR%\dart-sdk
-SET DART_BIN=%DART_SDK_DIR%\bin\dart
-SET PUB_BIN=%DART_SDK_DIR%\bin\pub
-SET FLUTTER_DIR=%ENGINE_SRC_DIR%\flutter
-SET WEB_UI_DIR=%FLUTTER_DIR%\lib\web_ui
-SET DEV_DIR=%WEB_UI_DIR%\dev
-SET FELT_PATH=%DEV_DIR%\felt.dart
-SET DART_TOOL_DIR=%WEB_UI_DIR%\.dart_tool
-SET SNAPSHOT_PATH=%DART_TOOL_DIR%\felt.snapshot
-
-SET needsHostDebugUnoptRebuild=0
-for %%x in (%*) do (
-  if ["%%~x"]==["--clean"] (
-    ECHO Clean rebuild requested
-    SET needsHostDebugUnoptRebuild=1
-  )
-)
-
-IF NOT EXIST %OUT_DIR% (SET needsHostDebugUnoptRebuild=1)
-IF NOT EXIST %HOST_DEBUG_UNOPT_DIR% (SET needsHostDebugUnoptRebuild=1)
-
-IF %needsHostDebugUnoptRebuild%==1 (
-  ECHO Building host_debug_unopt
-  :: Delete old snapshot, if any, because the new Dart SDK may invalidate it.
-  IF EXIST "%SNAPSHOT_PATH%" (
-    del %SNAPSHOT_PATH%
-  )
-  CALL gclient sync -D
-  CALL python %GN% --unoptimized --full-dart-sdk
-  CALL ninja -C %HOST_DEBUG_UNOPT_DIR%)
-
-cd %WEB_UI_DIR%
-IF NOT EXIST "%SNAPSHOT_PATH%" (
-  ECHO Precompiling felt snapshot
-  CALL %PUB_BIN% get
-  %DART_BIN% --snapshot="%SNAPSHOT_PATH%" --packages="%WEB_UI_DIR%\.packages" %FELT_PATH%
-)
-
-IF %1==test (
-  %DART_SDK_DIR%\bin\dart --packages="%WEB_UI_DIR%\.packages" "%SNAPSHOT_PATH%" %* --browser=chrome
-) ELSE (
-  %DART_SDK_DIR%\bin\dart --packages="%WEB_UI_DIR%\.packages" "%SNAPSHOT_PATH%" %*
-)
-
-EXIT /B %ERRORLEVEL%
diff --git a/engine/dev/felt.dart b/engine/dev/felt.dart
deleted file mode 100644
index ec03686..0000000
--- a/engine/dev/felt.dart
+++ /dev/null
@@ -1,90 +0,0 @@
-// Copyright 2013 The Flutter 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:io' as io;
-
-import 'package:args/command_runner.dart';
-
-import 'build.dart';
-import 'clean.dart';
-import 'create_simulator.dart';
-import 'exceptions.dart';
-import 'licenses.dart';
-import 'run.dart';
-import 'test_runner.dart';
-import 'utils.dart';
-
-CommandRunner<bool> runner = CommandRunner<bool>(
-  'felt',
-  'Command-line utility for building and testing Flutter web engine.',
-)
-  ..addCommand(BuildCommand())
-  ..addCommand(CleanCommand())
-  ..addCommand(CreateSimulatorCommand())
-  ..addCommand(LicensesCommand())
-  ..addCommand(RunCommand())
-  ..addCommand(TestCommand());
-
-Future<void> main(List<String> rawArgs) async {
-  // Remove --clean from the list as that's processed by the wrapper script.
-  final List<String> args = rawArgs.where((String arg) => arg != '--clean').toList();
-
-  if (args.isEmpty) {
-    // The felt tool was invoked with no arguments. Print usage.
-    runner.printUsage();
-    io.exit(64); // Exit code 64 indicates a usage error.
-  }
-
-  _listenToShutdownSignals();
-
-  int exitCode = -1;
-  try {
-    final bool? result = await runner.run(args);
-    if (result != true) {
-      print('Sub-command failed: `${args.join(' ')}`');
-      exitCode = 1;
-    }
-  } on UsageException catch (e) {
-    print(e);
-    exitCode = 64; // Exit code 64 indicates a usage error.
-  } on ToolExit catch (exception) {
-    io.stderr.writeln(exception.message);
-    exitCode = exception.exitCode;
-  } on ProcessException catch (e) {
-    io.stderr.writeln('description: ${e.description}'
-        'executable: ${e.executable} '
-        'arguments: ${e.arguments} '
-        'exit code: ${e.exitCode}');
-    exitCode = e.exitCode ?? 1;
-  } catch (e) {
-    rethrow;
-  } finally {
-    await cleanup();
-    // The exit code is changed by one of the branches.
-    if (exitCode != -1) {
-      io.exit(exitCode);
-    }
-  }
-
-  // Sometimes the Dart VM refuses to quit.
-  io.exit(io.exitCode);
-}
-
-Future<void> _listenToShutdownSignals() async {
-  io.ProcessSignal.sigint.watch().listen((_) async {
-    print('Received SIGINT. Shutting down.');
-    await cleanup();
-    io.exit(1);
-  });
-
-  // SIGTERM signals are not generated under Windows.
-  // See https://docs.microsoft.com/en-us/previous-versions/xdkz3x12(v%3Dvs.140)
-  if (!io.Platform.isWindows) {
-    io.ProcessSignal.sigterm.watch().listen((_) async {
-      await cleanup();
-      print('Received SIGTERM. Shutting down.');
-      io.exit(1);
-    });
-  }
-}
diff --git a/engine/dev/felt_windows.bat b/engine/dev/felt_windows.bat
deleted file mode 100644
index f79bd49..0000000
--- a/engine/dev/felt_windows.bat
+++ /dev/null
@@ -1,65 +0,0 @@
-:: TODO(yjbanov): migrate LUCI to felt.bat and delete this file.

-:: felt_windows: a command-line utility for Windows for building and testing

-:: Flutter web engine.

-:: FELT stands for Flutter Engine Local Tester.

-

-@ECHO OFF

-SETLOCAL

-

-FOR /F "tokens=1-2 delims=:" %%a in ('where gclient') DO SET GCLIENT_PATH=%%b

-IF %GCLIENT_PATH%==[] (ECHO "ERROR: gclient is not in your PATH")

-

-FOR /F "tokens=1-2 delims=:" %%a in ('where ninja') DO SET NINJA_PATH=%%b

-IF %NINJA_PATH%==[] (ECHO "ERROR: ninja is not in your PATH")

-

-SET FELT_DIR=%~dp0

-

-:: web_ui directory is the parent of felt directory.

-FOR %%a IN ("%FELT_DIR:~0,-1%") DO SET WEB_UI_DIR=%%~dpa

-

-:: Flutter Directory is grandparent of web_ui directory.

-FOR %%a IN ("%WEB_UI_DIR:~0,-1%") DO SET orTempValue=%%~dpa

-FOR %%a IN ("%orTempValue:~0,-1%") DO SET FLUTTER_DIR=%%~dpa

-

-:: Engine source directory is the parent of flutter directory.

-FOR %%a IN ("%FLUTTER_DIR:~0,-1%") DO SET ENGINE_SRC_DIR=%%~dpa

-

-SET DEV_DIR="%WEB_UI_DIR%dev"

-SET OUT_DIR="%ENGINE_SRC_DIR%out"

-SET HOST_DEBUG_UNOPT_DIR="%ENGINE_SRC_DIR%out\host_debug_unopt"

-SET DART_SDK_DIR=%ENGINE_SRC_DIR%out\host_debug_unopt\dart-sdk

-SET PUB_DIR="%DART_SDK_DIR%\bin\pub"

-SET SCRIPT_PATH="%DEV_DIR%felt.dart"

-SET STAMP_PATH="%DART_TOOL_DIR%felt.snapshot.stamp"

-SET GN="%FLUTTER_DIR%tools\gn"

-SET DART_TOOL_DIR="%WEB_UI_DIR%.dart_tool"

-SET SNAPSHOT_PATH="%DART_TOOL_DIR%felt.snapshot"

-

-:: Set revision from using git in Flutter directory.

-CD %FLUTTER_DIR%

-FOR /F "tokens=1 delims=:" %%a in ('git rev-parse HEAD') DO SET REVISION=%%a

-

-SET orTempValue=1

-IF NOT EXIST %OUT_DIR% (SET orTempValue=0)

-IF NOT EXIST %HOST_DEBUG_UNOPT_DIR% (SET orTempValue=0)

-IF %orTempValue%==0 (

-  ECHO "Compiling the Dart SDK."

-  CALL gclient sync

-  CALL python %GN% --unoptimized --full-dart-sdk

-  CALL ninja -C %HOST_DEBUG_UNOPT_DIR%)

-

-:: TODO(yjbanov): The batch script does not support snanphot option.

-:: Support snapshot option.

-CALL :installdeps

-IF %1==test (%DART_SDK_DIR%\bin\dart "%DEV_DIR%\felt.dart" %* --browser=chrome) ELSE ( %DART_SDK_DIR%\bin\dart "%DEV_DIR%\felt.dart" %* )

-

-EXIT /B %ERRORLEVEL%

-

-:installdeps

-ECHO "Running \`pub get\` in 'engine/src/flutter/web_sdk/web_engine_tester'"

-cd "%FLUTTER_DIR%web_sdk\web_engine_tester"

-CALL %PUB_DIR% get

-ECHO "Running \`pub get\` in 'engine/src/flutter/lib/web_ui'"

-cd %WEB_UI_DIR%

-CALL %PUB_DIR% get

-EXIT /B 0

diff --git a/engine/dev/firefox.dart b/engine/dev/firefox.dart
deleted file mode 100644
index c661378..0000000
--- a/engine/dev/firefox.dart
+++ /dev/null
@@ -1,117 +0,0 @@
-// Copyright 2013 The Flutter 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:io';
-
-import 'package:path/path.dart' as path;
-import 'package:pedantic/pedantic.dart';
-import 'package:test_api/src/backend/runtime.dart';
-import 'package:test_core/src/util/io.dart';
-
-import 'browser.dart';
-import 'browser_lock.dart';
-import 'common.dart';
-import 'environment.dart';
-import 'firefox_installer.dart';
-
-/// Provides an environment for the desktop Firefox.
-class FirefoxEnvironment implements BrowserEnvironment {
-  late final BrowserInstallation _installation;
-
-  @override
-  Browser launchBrowserInstance(Uri url, {bool debug = false}) {
-    return Firefox(url, this, debug: debug);
-  }
-
-  @override
-  Runtime get packageTestRuntime => Runtime.firefox;
-
-  @override
-  Future<void> prepare() async {
-    _installation = await getOrInstallFirefox(
-      browserLock.firefoxLock.version,
-      infoLog: isCirrus ? stdout : DevNull(),
-    );
-  }
-
-  @override
-  String get packageTestConfigurationYamlFile => 'dart_test_firefox.yaml';
-
-  @override
-  ScreenshotManager? getScreenshotManager() => null;
-}
-
-/// Runs desktop Firefox.
-///
-/// Most of the communication with the browser is expected to happen via HTTP,
-/// so this exposes a bare-bones API. The browser starts as soon as the class is
-/// constructed, and is killed when [close] is called.
-///
-/// Any errors starting or running the process are reported through [onExit].
-class Firefox extends Browser {
-  @override
-  final String name = 'Firefox';
-
-  @override
-  final Future<Uri> remoteDebuggerUrl;
-
-  /// Starts a new instance of Firefox open to the given [url], which may be a
-  /// [Uri] or a [String].
-  factory Firefox(Uri url, FirefoxEnvironment firefoxEnvironment, {bool debug = false}) {
-    final BrowserInstallation installation = firefoxEnvironment._installation;
-    final Completer<Uri> remoteDebuggerCompleter = Completer<Uri>.sync();
-    return Firefox._(() async {
-      // Using a profile on opening will prevent popups related to profiles.
-      const String _profile = '''
-user_pref("browser.shell.checkDefaultBrowser", false);
-user_pref("dom.disable_open_during_load", false);
-user_pref("dom.max_script_run_time", 0);
-''';
-
-      final Directory temporaryProfileDirectory = Directory(
-          path.join(environment.webUiDartToolDir.path, 'firefox_profile'));
-
-      // A good source of various Firefox Command Line options:
-      // https://developer.mozilla.org/en-US/docs/Mozilla/Command_Line_Options#Browser
-      //
-      if (temporaryProfileDirectory.existsSync()) {
-        temporaryProfileDirectory.deleteSync(recursive: true);
-      }
-      temporaryProfileDirectory.createSync(recursive: true);
-      File(path.join(temporaryProfileDirectory.path, 'prefs.js'))
-          .writeAsStringSync(_profile);
-
-      final bool isMac = Platform.isMacOS;
-      final List<String> args = <String>[
-        url.toString(),
-        '--profile',
-        temporaryProfileDirectory.path,
-        if (!debug)
-          '--headless',
-        '-width $kMaxScreenshotWidth',
-        '-height $kMaxScreenshotHeight',
-        // On Mac Firefox uses the -- option prefix, while elsewhere it uses the - prefix.
-        '${isMac ? '-' : ''}-new-window',
-        '${isMac ? '-' : ''}-new-instance',
-        '--start-debugger-server $kDevtoolsPort',
-      ];
-
-      final Process process =
-          await Process.start(installation.executable, args);
-
-      remoteDebuggerCompleter.complete(
-          getRemoteDebuggerUrl(Uri.parse('http://localhost:$kDevtoolsPort')));
-
-      unawaited(process.exitCode.then((_) {
-        temporaryProfileDirectory.deleteSync(recursive: true);
-      }));
-
-      return process;
-    }, remoteDebuggerCompleter.future);
-  }
-
-  Firefox._(Future<Process> Function() startBrowser, this.remoteDebuggerUrl)
-      : super(startBrowser);
-}
diff --git a/engine/dev/firefox_installer.dart b/engine/dev/firefox_installer.dart
deleted file mode 100644
index 2d48c16..0000000
--- a/engine/dev/firefox_installer.dart
+++ /dev/null
@@ -1,331 +0,0 @@
-// Copyright 2013 The Flutter 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:io' as io;
-
-import 'package:http/http.dart';
-import 'package:path/path.dart' as path;
-
-import 'common.dart';
-import 'environment.dart';
-import 'exceptions.dart';
-
-/// Returns the installation of Firefox, installing it if necessary.
-///
-/// If [requestedVersion] is null, uses the version specified on the
-/// command-line. If not specified on the command-line, uses the version
-/// specified in the "browser_lock.yaml" file.
-///
-/// If [requestedVersion] is not null, installs that version. The value
-/// may be "latest" (the latest stable Firefox version), "system"
-/// (manually installed Firefox on the current operating system), or an
-/// exact version number such as 69.0.3. Versions of Firefox can be found here:
-///
-/// https://download-installer.cdn.mozilla.net/pub/firefox/releases/
-Future<BrowserInstallation> getOrInstallFirefox(
-  String requestedVersion, {
-  StringSink? infoLog,
-}) async {
-  // These tests are aimed to run only on the Linux containers in Cirrus.
-  // Therefore Firefox installation is implemented only for Linux now.
-  if (!io.Platform.isLinux && !io.Platform.isMacOS) {
-    throw UnimplementedError('Firefox Installer is only supported on Linux '
-        'and Mac operating systems');
-  }
-
-  infoLog ??= io.stdout;
-
-  if (requestedVersion == 'system') {
-    return BrowserInstallation(
-      version: 'system',
-      executable: await _findSystemFirefoxExecutable(),
-    );
-  }
-
-  FirefoxInstaller? installer;
-  try {
-    installer = requestedVersion == 'latest'
-        ? await FirefoxInstaller.latest()
-        : FirefoxInstaller(version: requestedVersion);
-
-    if (installer.isInstalled) {
-      infoLog.writeln(
-          'Installation was skipped because Firefox version ${installer.version} is already installed.');
-    } else {
-      infoLog.writeln('Installing Firefox version: ${installer.version}');
-      await installer.install();
-      final BrowserInstallation installation = installer.getInstallation()!;
-      infoLog.writeln(
-          'Installations complete. To launch it run ${installation.executable}');
-    }
-    return installer.getInstallation()!;
-  } finally {
-    installer?.close();
-  }
-}
-
-/// Manages the installation of a particular [version] of Firefox.
-class FirefoxInstaller {
-  factory FirefoxInstaller({
-    required String version,
-  }) {
-    if (version == 'system') {
-      throw BrowserInstallerException(
-          'Cannot install system version of Firefox. System Firefox must be installed manually.');
-    }
-    if (version == 'latest') {
-      throw BrowserInstallerException(
-          'Expected a concrete Firefox version, but got $version. Maybe use FirefoxInstaller.latest()?');
-    }
-    final io.Directory firefoxInstallationDir = io.Directory(
-      path.join(environment.webUiDartToolDir.path, 'firefox'),
-    );
-    final io.Directory versionDir = io.Directory(
-      path.join(firefoxInstallationDir.path, version),
-    );
-    return FirefoxInstaller._(
-      version: version,
-      firefoxInstallationDir: firefoxInstallationDir,
-      versionDir: versionDir,
-    );
-  }
-
-  static Future<FirefoxInstaller> latest() async {
-    final String latestVersion = io.Platform.isLinux
-        ? await fetchLatestFirefoxVersionLinux()
-        : await fetchLatestFirefoxVersionMacOS();
-    return FirefoxInstaller(version: latestVersion);
-  }
-
-  FirefoxInstaller._({
-    required this.version,
-    required this.firefoxInstallationDir,
-    required this.versionDir,
-  });
-
-  /// Firefox version managed by this installer.
-  final String version;
-
-  /// HTTP client used to download Firefox.
-  final Client client = Client();
-
-  /// Root directory that contains Firefox versions.
-  final io.Directory firefoxInstallationDir;
-
-  /// Installation directory for Firefox of the requested [version].
-  final io.Directory versionDir;
-
-  bool get isInstalled {
-    return versionDir.existsSync();
-  }
-
-  BrowserInstallation? getInstallation() {
-    if (!isInstalled) {
-      return null;
-    }
-
-    return BrowserInstallation(
-      version: version,
-      executable: PlatformBinding.instance.getFirefoxExecutablePath(versionDir),
-    );
-  }
-
-  /// Install the browser by downloading from the web.
-  Future<void> install() async {
-    final io.File downloadedFile = await _download();
-    if (io.Platform.isLinux) {
-      await _uncompress(downloadedFile);
-    } else if (io.Platform.isMacOS) {
-      await _mountDmgAndCopy(downloadedFile);
-    }
-    downloadedFile.deleteSync();
-  }
-
-  /// Downloads the browser version from web into a target file.
-  /// See [version].
-  Future<io.File> _download() async {
-    if (versionDir.existsSync()) {
-      versionDir.deleteSync(recursive: true);
-    }
-
-    versionDir.createSync(recursive: true);
-    final String url = PlatformBinding.instance.getFirefoxDownloadUrl(version);
-    final StreamedResponse download = await client.send(Request(
-      'GET',
-      Uri.parse(url),
-    ));
-
-    final io.File downloadedFile =
-        io.File(path.join(versionDir.path, PlatformBinding.instance.getFirefoxDownloadFilename(version)));
-    final io.IOSink sink = downloadedFile.openWrite();
-    await download.stream.pipe(sink);
-    await sink.flush();
-    await sink.close();
-
-    return downloadedFile;
-  }
-
-  /// Uncompress the downloaded browser files for operating systems that
-  /// use a zip archive.
-  /// See [version].
-  Future<void> _uncompress(io.File downloadedFile) async {
-    final io.ProcessResult unzipResult = await io.Process.run('tar', <String>[
-      '-x',
-      '-f',
-      downloadedFile.path,
-      '-C',
-      versionDir.path,
-    ]);
-
-    if (unzipResult.exitCode != 0) {
-      throw BrowserInstallerException(
-          'Failed to unzip the downloaded Firefox archive ${downloadedFile.path}.\n'
-          'The unzip process exited with code ${unzipResult.exitCode}.');
-    }
-  }
-
-  /// Mounts the dmg file using hdiutil, copies content of the volume to
-  /// target path and then unmounts dmg ready for deletion.
-  Future<void> _mountDmgAndCopy(io.File dmgFile) async {
-    final String volumeName = await _hdiUtilMount(dmgFile);
-
-    final String sourcePath = '$volumeName/Firefox.app';
-    final String targetPath = path.dirname(dmgFile.path);
-    try {
-      final io.ProcessResult installResult = await io.Process.run('cp', <String>[
-        '-r',
-        sourcePath,
-        targetPath,
-      ]);
-      if (installResult.exitCode != 0) {
-        throw BrowserInstallerException(
-            'Failed to copy Firefox disk image contents from '
-            '$sourcePath to $targetPath.\n'
-            'Exit code ${installResult.exitCode}.\n'
-            '${installResult.stderr}');
-      }
-    } finally {
-      await _hdiUtilUnmount(volumeName);
-    }
-  }
-
-  Future<String> _hdiUtilMount(io.File dmgFile) async {
-    final io.ProcessResult mountResult = await io.Process.run('hdiutil', <String>[
-      'attach',
-      '-readonly',
-      dmgFile.path,
-    ]);
-    if (mountResult.exitCode != 0) {
-      throw BrowserInstallerException(
-          'Failed to mount Firefox disk image ${dmgFile.path}.\n'
-              'Exit code ${mountResult.exitCode}.\n${mountResult.stderr}');
-    }
-
-    final List<String> processOutput = (mountResult.stdout as String).split('\n');
-    final String? volumePath = _volumeFromMountResult(processOutput);
-    if (volumePath == null) {
-      throw BrowserInstallerException(
-          'Failed to parse mount dmg result ${processOutput.join('\n')}.\n'
-              'Expected /Volumes/{volume name}');
-    }
-    return volumePath;
-  }
-
-  // Parses volume from mount result.
-  // Output is of form: {devicename} /Volumes/{name}.
-  String? _volumeFromMountResult(List<String> lines) {
-    for (final String line in lines) {
-      final int pos = line.indexOf('/Volumes');
-      if (pos != -1) {
-        return line.substring(pos);
-      }
-    }
-    return null;
-  }
-
-  Future<void> _hdiUtilUnmount(String volumeName) async {
-    final io.ProcessResult unmountResult = await io.Process.run('hdiutil', <String>[
-      'unmount',
-      volumeName,
-    ]);
-    if (unmountResult.exitCode != 0) {
-      throw BrowserInstallerException(
-          'Failed to unmount Firefox disk image $volumeName.\n'
-              'Exit code ${unmountResult.exitCode}. ${unmountResult.stderr}');
-    }
-  }
-
-  void close() {
-    client.close();
-  }
-}
-
-Future<String> _findSystemFirefoxExecutable() async {
-  final io.ProcessResult which =
-      await io.Process.run('which', <String>['firefox']);
-  final bool found = which.exitCode != 0;
-  const String fireFoxDefaultInstallPath =
-      '/Applications/Firefox.app/Contents/MacOS/firefox';
-  if (!found) {
-    if (io.Platform.isMacOS &&
-        io.File(fireFoxDefaultInstallPath).existsSync()) {
-      return Future<String>.value(fireFoxDefaultInstallPath);
-    }
-    throw BrowserInstallerException(
-        'Failed to locate system Firefox installation.');
-  }
-  return which.stdout as String;
-}
-
-/// Fetches the latest available Firefox build version on Linux.
-Future<String> fetchLatestFirefoxVersionLinux() async {
-  final RegExp forFirefoxVersion = RegExp('firefox-[0-9.]+[0-9]');
-  final io.HttpClientRequest request = await io.HttpClient()
-      .getUrl(Uri.parse(PlatformBinding.instance.getFirefoxLatestVersionUrl()));
-  request.followRedirects = false;
-  // We will parse the HttpHeaders to find the redirect location.
-  final io.HttpClientResponse response = await request.close();
-
-  final String location = response.headers.value('location')!;
-  final String version = forFirefoxVersion.stringMatch(location)!;
-
-  return version.substring(version.lastIndexOf('-') + 1);
-}
-
-/// Fetches the latest available Firefox build version on Mac OS.
-Future<String> fetchLatestFirefoxVersionMacOS() async {
-  final RegExp forFirefoxVersion = RegExp('firefox/releases/[0-9.]+[0-9]');
-  final io.HttpClientRequest request = await io.HttpClient()
-      .getUrl(Uri.parse(PlatformBinding.instance.getFirefoxLatestVersionUrl()));
-  request.followRedirects = false;
-  // We will parse the HttpHeaders to find the redirect location.
-  final io.HttpClientResponse response = await request.close();
-
-  final String location = response.headers.value('location')!;
-  final String version = forFirefoxVersion.stringMatch(location)!;
-  return version.substring(version.lastIndexOf('/') + 1);
-}
-
-Future<BrowserInstallation> getInstaller({String requestedVersion = 'latest'}) async {
-  FirefoxInstaller? installer;
-  try {
-    installer = requestedVersion == 'latest'
-        ? await FirefoxInstaller.latest()
-        : FirefoxInstaller(version: requestedVersion);
-
-    if (installer.isInstalled) {
-      print('Installation was skipped because Firefox version '
-          '${installer.version} is already installed.');
-    } else {
-      print('Installing Firefox version: ${installer.version}');
-      await installer.install();
-      final BrowserInstallation installation = installer.getInstallation()!;
-      print(
-          'Installations complete. To launch it run ${installation.executable}');
-    }
-    return installer.getInstallation()!;
-  } finally {
-    installer?.close();
-  }
-}
diff --git a/engine/dev/goldens_lock.yaml b/engine/dev/goldens_lock.yaml
deleted file mode 100644
index b2ba801..0000000
--- a/engine/dev/goldens_lock.yaml
+++ /dev/null
@@ -1,2 +0,0 @@
-repository: https://github.com/flutter/goldens.git
-revision: 0dd2e82050422c05e9daf652fa4267fb7c01f260
diff --git a/engine/dev/licenses.dart b/engine/dev/licenses.dart
deleted file mode 100644
index 7048576..0000000
--- a/engine/dev/licenses.dart
+++ /dev/null
@@ -1,96 +0,0 @@
-// Copyright 2013 The Flutter 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:io' as io;
-
-import 'package:args/command_runner.dart';
-import 'package:path/path.dart' as path;
-
-import 'environment.dart';
-
-class LicensesCommand extends Command<bool> {
-  @override
-  final String name = 'check-licenses';
-
-  @override
-  final String description = 'Check license headers.';
-
-  @override
-  bool run() {
-    _checkLicenseHeaders();
-    return true;
-  }
-
-  void _checkLicenseHeaders() {
-    final List<io.File> allSourceFiles =
-        _flatListSourceFiles(environment.webUiRootDir);
-    _expect(allSourceFiles.isNotEmpty,
-        'Dart source listing of ${environment.webUiRootDir.path} must not be empty.');
-
-    final List<String> allDartPaths =
-        allSourceFiles.map((io.File f) => f.path).toList();
-
-    for (final String expectedDirectory in const <String>[
-      'lib',
-      'test',
-      'dev',
-      'tool'
-    ]) {
-      final String expectedAbsoluteDirectory =
-          path.join(environment.webUiRootDir.path, expectedDirectory);
-      _expect(
-        allDartPaths
-            .where((String p) => p.startsWith(expectedAbsoluteDirectory))
-            .isNotEmpty,
-        'Must include the $expectedDirectory/ directory',
-      );
-    }
-
-    allSourceFiles.forEach(_expectLicenseHeader);
-    print('License headers OK!');
-  }
-
-  final RegExp _copyRegex =
-      RegExp(r'// Copyright 2013 The Flutter Authors\. All rights reserved\.');
-
-  void _expectLicenseHeader(io.File file) {
-    final List<String> head = file.readAsStringSync().split('\n').take(3).toList();
-
-    _expect(head.length >= 3, 'File too short: ${file.path}');
-    _expect(
-      _copyRegex.firstMatch(head[0]) != null,
-      'Invalid first line of license header in file ${file.path}',
-    );
-    _expect(
-      head[1] ==
-          '// Use of this source code is governed by a BSD-style license that can be',
-      'Invalid second line of license header in file ${file.path}',
-    );
-    _expect(
-      head[2] == '// found in the LICENSE file.',
-      'Invalid third line of license header in file ${file.path}',
-    );
-  }
-
-  void _expect(bool value, String requirement) {
-    if (!value) {
-      throw Exception('Test failed: $requirement');
-    }
-  }
-
-  List<io.File> _flatListSourceFiles(io.Directory directory) {
-    return directory.listSync(recursive: true).whereType<io.File>().where((io.File f) {
-      if (!f.path.endsWith('.dart') && !f.path.endsWith('.js')) {
-        // Not a source file we're checking.
-        return false;
-      }
-      if (path.isWithin(environment.webUiBuildDir.path, f.path) ||
-          path.isWithin(environment.webUiDartToolDir.path, f.path)) {
-        // Generated files.
-        return false;
-      }
-      return true;
-    }).toList();
-  }
-}
diff --git a/engine/dev/pipeline.dart b/engine/dev/pipeline.dart
deleted file mode 100644
index 18399fe..0000000
--- a/engine/dev/pipeline.dart
+++ /dev/null
@@ -1,266 +0,0 @@
-// Copyright 2013 The Flutter 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:io' as io;
-
-import 'package:path/path.dart' as path;
-import 'package:watcher/watcher.dart';
-
-import 'utils.dart';
-
-/// Describes what [Pipeline] is currently doing.
-enum PipelineStatus {
-  /// The pipeline has not started yet.
-  ///
-  /// This is the initial state of the pipeline.
-  idle,
-
-  /// The pipeline is running build steps.
-  started,
-
-  /// The pipeline is stopping.
-  stopping,
-
-  /// The pipeline is not running anything because it has been interrupted.
-  interrupted,
-
-  /// The pipeline is not running anything because it encountered an error.
-  error,
-
-  /// The pipeline is not running anything because it finished all build steps successfully.
-  done,
-}
-
-/// A step in the build pipeline.
-abstract class PipelineStep {
-  /// The name of this step.
-  ///
-  /// This value appears in logs, so it should be descriptive and human-readable.
-  String get description;
-
-  /// Whether it is safe to interrupt this step while it's running.
-  bool get isSafeToInterrupt;
-
-  /// Runs this step.
-  ///
-  /// The returned future is completed when the step is finished. The future
-  /// completes with an error if the step failed.
-  Future<void> run();
-
-  /// Interrupts this step, if it's already running.
-  ///
-  /// [Pipeline] only calls this if [isSafeToInterrupt] returns true.
-  Future<void> interrupt();
-}
-
-/// A helper class for implementing [PipelineStep] in terms of a process.
-abstract class ProcessStep implements PipelineStep {
-  ProcessManager? _process;
-  bool _isInterrupted = false;
-
-  /// Starts and returns the process that implements the logic of this pipeline
-  /// step.
-  Future<ProcessManager> createProcess();
-
-  @override
-  Future<void> interrupt() async {
-    _isInterrupted = true;
-    _process?.kill();
-  }
-
-  @override
-  Future<void> run() async {
-    final ProcessManager process = await createProcess();
-
-    if (_isInterrupted) {
-      // If the step was interrupted while creating the process, the
-      // `interrupt` won't kill the process; it must be done here.
-      process.kill();
-      return;
-    }
-
-    _process = process;
-    await process.wait();
-    _process = null;
-  }
-}
-
-/// Executes a sequence of asynchronous tasks, typically as part of a build/test
-/// process.
-///
-/// The pipeline can be executed by calling [start] and stopped by calling
-/// [stop].
-///
-/// When a pipeline is stopped, it switches to the [PipelineStatus.stopping]
-/// state. If [PipelineStep.isSafeToInterrupt] is true, interrupts the currently
-/// running step and skips the rest. Otherwise, waits until the current task
-/// finishes and skips the rest.
-class Pipeline {
-  Pipeline({required this.steps});
-
-  final Iterable<PipelineStep> steps;
-
-  PipelineStep? _currentStep;
-  Future<void>? _currentStepFuture;
-
-  PipelineStatus get status => _status;
-  PipelineStatus _status = PipelineStatus.idle;
-
-  /// Runs the steps of the pipeline.
-  ///
-  /// Returns a future that resolves after all steps have been performed.
-  ///
-  /// The future resolves to an error as soon as any of the steps fails.
-  ///
-  /// The pipeline may be interrupted by calling [stop] before the future
-  /// resolves.
-  Future<void> run() async {
-    _status = PipelineStatus.started;
-    try {
-      for (final PipelineStep step in steps) {
-        if (status != PipelineStatus.started) {
-          break;
-        }
-        _currentStep = step;
-        _currentStepFuture = step.run();
-        await _currentStepFuture;
-      }
-      _status = PipelineStatus.done;
-    } catch (_) {
-      _status = PipelineStatus.error;
-      rethrow;
-    } finally {
-      _currentStep = null;
-    }
-  }
-
-  /// Stops executing any more tasks in the pipeline.
-  ///
-  /// Tasks that are safe to interrupt (according to [PipelineStep.isSafeToInterrupt]),
-  /// are interrupted. Otherwise, waits for the current step to finish before
-  /// interrupting the pipeline.
-  Future<void> stop() async {
-    _status = PipelineStatus.stopping;
-    final PipelineStep? step = _currentStep;
-    if (step == null) {
-      _status = PipelineStatus.interrupted;
-      return;
-    }
-    if (step.isSafeToInterrupt) {
-      print('Interrupting ${step.description}');
-      await step.interrupt();
-      _status = PipelineStatus.interrupted;
-      return;
-    }
-    print('${step.description} cannot be interrupted. Waiting for it to complete.');
-    await _currentStepFuture;
-    _status = PipelineStatus.interrupted;
-  }
-}
-
-/// Signature of functions to be called when a [WatchEvent] is received.
-typedef WatchEventPredicate = bool Function(WatchEvent event);
-
-/// Responsible for watching a directory [dir] and executing the given
-/// [pipeline] whenever a change occurs in the directory.
-///
-/// The [ignore] callback can be used to customize the watching behavior to
-/// ignore certain files.
-class PipelineWatcher {
-  PipelineWatcher({
-    required this.dir,
-    required this.pipeline,
-    this.ignore,
-  }) : watcher = DirectoryWatcher(dir);
-
-  /// The path of the directory to watch for changes.
-  final String dir;
-
-  /// The pipeline to be executed when an event is fired by the watcher.
-  final Pipeline pipeline;
-
-  /// Used to watch a directory for any file system changes.
-  final DirectoryWatcher watcher;
-
-  /// A callback that determines whether to rerun the pipeline or not for a
-  /// given [WatchEvent] instance.
-  final WatchEventPredicate? ignore;
-
-  /// Activates the watcher.
-  Future<void> start() async {
-    watcher.events.listen(_onEvent);
-
-    // Listen to the `q` key stroke to stop the pipeline.
-    print('Press \'q\' to exit felt');
-
-    // Key strokes should be reported immediately and one at a time rather than
-    // wait for the user to hit ENTER and report the whole line. To achieve
-    // that, echo mode and line mode must be disabled.
-    io.stdin.echoMode = false;
-    io.stdin.lineMode = false;
-
-    await io.stdin.firstWhere((List<int> event) {
-      const int qKeyCode = 113;
-      final bool qEntered = event.isNotEmpty && event.first == qKeyCode;
-      return qEntered;
-    });
-    print('Stopping felt');
-    await pipeline.stop();
-  }
-
-  int _pipelineRunCount = 0;
-  Timer? _scheduledPipeline;
-
-  void _onEvent(WatchEvent event) {
-    if (ignore?.call(event) == true) {
-      return;
-    }
-
-    final String relativePath = path.relative(event.path, from: dir);
-    print('- [${event.type}] $relativePath');
-
-    _pipelineRunCount++;
-    _scheduledPipeline?.cancel();
-    _scheduledPipeline = Timer(const Duration(milliseconds: 100), () {
-      _scheduledPipeline = null;
-      _runPipeline();
-    });
-  }
-
-  Future<void> _runPipeline() async {
-    if (pipeline.status == PipelineStatus.stopping) {
-      // We are already trying to stop the pipeline. No need to do anything.
-      return;
-    }
-
-    if (pipeline.status == PipelineStatus.started) {
-      // If the pipeline already running, stop it before starting it again.
-      await pipeline.stop();
-    }
-
-    final int runCount = _pipelineRunCount;
-    try {
-      await pipeline.run();
-      _pipelineSucceeded(runCount);
-    } catch(error, stackTrace) {
-      // The error is printed but not rethrown. This is because in watch mode
-      // failures are expected. The idea is that the developer corrects the
-      // error, saves the file, and the pipeline reruns.
-      _pipelineFailed(error, stackTrace);
-    }
-  }
-
-  void _pipelineSucceeded(int pipelineRunCount) {
-    if (pipelineRunCount == _pipelineRunCount) {
-      print('*** Done! ***');
-      print('Press \'q\' to exit felt');
-    }
-  }
-
-  void _pipelineFailed(Object error, StackTrace stackTrace) {
-    print('felt command failed: $error');
-    print(stackTrace);
-  }
-}
diff --git a/engine/dev/run.dart b/engine/dev/run.dart
deleted file mode 100644
index 4592e95..0000000
--- a/engine/dev/run.dart
+++ /dev/null
@@ -1,95 +0,0 @@
-// Copyright 2013 The Flutter 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:io' as io;
-
-import 'package:args/command_runner.dart';
-
-import 'common.dart';
-import 'pipeline.dart';
-import 'steps/compile_tests_step.dart';
-import 'steps/run_tests_step.dart';
-import 'utils.dart';
-
-/// Runs build and test steps.
-///
-/// This command is designed to be invoked by the LUCI build graph. However, it
-/// is also usable locally.
-///
-/// Usage:
-///
-///     felt run name_of_build_step
-class RunCommand extends Command<bool> with ArgUtils<bool> {
-  RunCommand() {
-    argParser.addFlag(
-      'list',
-      abbr: 'l',
-      defaultsTo: false,
-      help: 'Lists all available build steps.',
-    );
-  }
-
-  @override
-  String get name => 'run';
-
-  bool get isListSteps => boolArg('list');
-
-  @override
-  String get description => 'Runs a build step.';
-
-  /// Build steps to run, in order specified.
-  List<String> get stepNames => argResults!.rest;
-
-  @override
-  FutureOr<bool> run() async {
-    // All available build steps.
-    final Map<String, PipelineStep> buildSteps = <String, PipelineStep>{
-      'compile_tests': CompileTestsStep(),
-      for (final String browserName in kAllBrowserNames)
-        'run_tests_$browserName': RunTestsStep(
-          browserName: browserName,
-          isDebug: false,
-          doUpdateScreenshotGoldens: false,
-          overridePathToCanvasKit: null,
-        ),
-    };
-
-    if (isListSteps) {
-      buildSteps.keys.forEach(print);
-      return true;
-    }
-
-    if (stepNames.isEmpty) {
-      throw UsageException('No build steps specified.', argParser.usage);
-    }
-
-    final List<String> unrecognizedStepNames = <String>[];
-    for (final String stepName in stepNames) {
-      if (!buildSteps.containsKey(stepName)) {
-        unrecognizedStepNames.add(stepName);
-      }
-    }
-    if (unrecognizedStepNames.isNotEmpty) {
-      io.stderr.writeln(
-        'Unknown build steps specified: ${unrecognizedStepNames.join(', ')}',
-      );
-      return false;
-    }
-
-    final List<PipelineStep> steps = <PipelineStep>[];
-    print('Running steps ${steps.join(', ')}');
-    for (final String stepName in stepNames) {
-      steps.add(buildSteps[stepName]!);
-    }
-
-    final Stopwatch stopwatch = Stopwatch()..start();
-    final Pipeline pipeline = Pipeline(steps: steps);
-    await pipeline.run();
-    stopwatch.stop();
-    print('Finished running steps in ${stopwatch.elapsedMilliseconds / 1000} seconds.');
-
-    return true;
-  }
-}
diff --git a/engine/dev/safari_installation.dart b/engine/dev/safari_installation.dart
deleted file mode 100644
index 497ae06..0000000
--- a/engine/dev/safari_installation.dart
+++ /dev/null
@@ -1,90 +0,0 @@
-// Copyright 2013 The Flutter 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:io' as io;
-
-import 'package:simulators/simulator_manager.dart';
-
-import 'browser_lock.dart';
-import 'common.dart';
-import 'utils.dart';
-
-/// Returns [IosSimulator] if the [Platform] is `macOS` and simulator
-/// is started.
-///
-/// Throws an [StateError] if these two conditions are not met.
-IosSimulator get iosSimulator {
-  if (!io.Platform.isMacOS) {
-    throw StateError('iOS Simulator is only available on macOS machines.');
-  }
-  if (_iosSimulator == null) {
-    throw StateError(
-      'iOS Simulator not started. Please first call initIOSSimulator method',
-    );
-  }
-  return _iosSimulator!;
-}
-IosSimulator? _iosSimulator;
-
-/// Inializes and boots an [IosSimulator] using the [iosMajorVersion],
-/// [iosMinorVersion] and [iosDevice] arguments.
-Future<void> initIosSimulator() async {
-  if (_iosSimulator != null) {
-    throw StateError('_iosSimulator can only be initialized once');
-  }
-  final IosSimulatorManager iosSimulatorManager = IosSimulatorManager();
-  final IosSimulator simulator;
-  final SafariIosLock lock = browserLock.safariIosLock;
-  try {
-    simulator = await iosSimulatorManager.getSimulator(
-      lock.majorVersion,
-      lock.minorVersion,
-      lock.device,
-    );
-    _iosSimulator = simulator;
-  } catch (e) {
-    io.stderr.writeln(
-      'Error getting iOS Simulator for ${lock.simulatorDescription}.\n'
-      'Try running `felt create` command before running tests.',
-    );
-    rethrow;
-  }
-
-  if (!simulator.booted) {
-    await simulator.boot();
-    print('INFO: Simulator ${simulator.id} booted.');
-    cleanupCallbacks.add(() async {
-      await simulator.shutdown();
-      print('INFO: Simulator ${simulator.id} shutdown.');
-    });
-  }
-}
-
-/// Returns the installation of Safari.
-///
-/// Currently uses the Safari version installed on the operating system.
-///
-/// Latest Safari version for Catalina, Mojave, High Siera is 13.
-///
-/// Latest Safari version for Sierra is 12.
-Future<BrowserInstallation> getOrInstallSafari({
-  StringSink? infoLog,
-}) async {
-  // These tests are aimed to run only on macOS machines local or on LUCI.
-  if (!io.Platform.isMacOS) {
-    throw UnimplementedError('Safari on ${io.Platform.operatingSystem} is'
-        ' not supported. Safari is only supported on macOS.');
-  }
-
-  infoLog ??= io.stdout;
-
-  // Since Safari is included in macOS, always assume there will be one on the
-  // system.
-  infoLog.writeln('Using the system version that is already installed.');
-  return BrowserInstallation(
-    version: 'system',
-    executable: PlatformBinding.instance.getMacApplicationLauncher(),
-  );
-}
diff --git a/engine/dev/safari_ios.dart b/engine/dev/safari_ios.dart
deleted file mode 100644
index 0dd41c0..0000000
--- a/engine/dev/safari_ios.dart
+++ /dev/null
@@ -1,213 +0,0 @@
-// Copyright 2013 The Flutter 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:io' as io;
-import 'dart:math' as math;
-
-import 'package:image/image.dart';
-import 'package:path/path.dart' as path;
-import 'package:test_api/src/backend/runtime.dart';
-
-import 'browser.dart';
-import 'browser_lock.dart';
-import 'environment.dart';
-import 'safari_installation.dart';
-import 'utils.dart';
-
-/// Provides an environment for the mobile variant of Safari running in an iOS
-/// simulator.
-class SafariIosEnvironment implements BrowserEnvironment {
-  @override
-  Browser launchBrowserInstance(Uri url, {bool debug = false}) {
-    return SafariIos(url);
-  }
-
-  @override
-  Runtime get packageTestRuntime => Runtime.safari;
-
-  @override
-  Future<void> prepare() async {
-    await initIosSimulator();
-  }
-
-  @override
-  ScreenshotManager? getScreenshotManager() {
-    return SafariIosScreenshotManager();
-  }
-
-  @override
-  String get packageTestConfigurationYamlFile => 'dart_test_safari.yaml';
-}
-
-/// Runs an instance of Safari for iOS (i.e. mobile Safari).
-///
-/// Most of the communication with the browser is expected to happen via HTTP,
-/// so this exposes a bare-bones API. The browser starts as soon as the class is
-/// constructed, and is killed when [close] is called.
-///
-/// Any errors starting or running the process are reported through [onExit].
-class SafariIos extends Browser {
-  @override
-  final String name = 'Safari iOS';
-
-  /// Starts a new instance of Safari open to the given [url], which may be a
-  /// [Uri].
-  factory SafariIos(Uri url) {
-    return SafariIos._(() async {
-      // iOS-Safari
-      // Uses `xcrun simctl`. It is a command line utility to control the
-      // Simulator. For more details on interacting with the simulator:
-      // https://developer.apple.com/library/archive/documentation/IDEs/Conceptual/iOS_Simulator_Guide/InteractingwiththeiOSSimulator/InteractingwiththeiOSSimulator.html
-      final io.Process process = await io.Process.start('xcrun', <String>[
-        'simctl',
-        'openurl', // Opens the url on Safari installed on the simulator.
-        'booted', // The simulator is already booted.
-        url.toString(),
-      ]);
-
-      return process;
-    });
-  }
-
-  SafariIos._(Future<io.Process> Function() startBrowser) : super(startBrowser);
-}
-
-/// [ScreenshotManager] implementation for Safari.
-///
-/// This manager will only be created/used for macOS.
-class SafariIosScreenshotManager extends ScreenshotManager {
-  @override
-  String get filenameSuffix => '.iOS_Safari';
-
-  SafariIosScreenshotManager() {
-    final SafariIosLock lock = browserLock.safariIosLock;
-    _heightOfHeader = lock.heightOfHeader;
-    _heightOfFooter = lock.heightOfFooter;
-    _scaleFactor = lock.scaleFactor;
-
-    /// Create the directory to use for taking screenshots, if it does not
-    /// exists.
-    if (!environment.webUiSimulatorScreenshotsDirectory.existsSync()) {
-      environment.webUiSimulatorScreenshotsDirectory.createSync();
-    }
-    // Temporary directories are deleted in the clenaup phase of after `felt`
-    // runs the tests.
-    temporaryDirectories.add(environment.webUiSimulatorScreenshotsDirectory);
-  }
-
-  /// This scale factor is used to enlarge/shrink the screenshot region
-  /// sent from the tests.
-  /// For more details see [_scaleScreenshotRegion(region)].
-  late final double _scaleFactor;
-
-  /// Height of the part to crop from the top of the image.
-  ///
-  /// `xcrun simctl` command takes the screenshot of the entire simulator. We
-  /// are cropping top bit from screenshot, otherwise due to the clock on top of
-  /// the screen, the screenshot will differ between each run.
-  /// Note that this gap can change per phone and per iOS version. For more
-  /// details refer to `browser_lock.yaml` file.
-  late final int _heightOfHeader;
-
-  /// Height of the part to crop from the bottom of the image.
-  ///
-  /// This area is the footer navigation bar of the phone, it is not the area
-  /// used by tests (which is inside the browser).
-  late final int _heightOfFooter;
-
-  /// Used as a suffix for the temporary file names used for screenshots.
-  int _fileNameCounter = 0;
-
-  /// Capture a screenshot of entire simulator.
-  ///
-  /// Example screenshot with dimensions: W x H.
-  ///
-  ///  <----------  W ------------->
-  ///  _____________________________
-  /// | Phone Top bar (clock etc.)  |   Ʌ
-  /// |_____________________________|   |
-  /// | Broswer search bar          |   |
-  /// |_____________________________|   |
-  /// | Web page content            |   |
-  /// |                             |   |
-  /// |                             |
-  /// |                             |   H
-  /// |                             |
-  /// |                             |   |
-  /// |                             |   |
-  /// |                             |   |
-  /// |                             |   |
-  /// |_____________________________|   |
-  /// | Phone footer bar            |   |
-  /// |_____________________________|   V
-  ///
-  /// After taking the screenshot, the image is cropped as heigh as
-  /// [_heightOfHeader] and [_heightOfFooter] from the top and bottom parts
-  /// consecutively. Hence web content has the dimensions:
-  ///
-  /// W x (H - [_heightOfHeader] - [_heightOfFooter])
-  ///
-  /// [region] is used to decide which part of the web content will be used in
-  /// test image. It includes starting coordinate x,y as well as height and
-  /// width of the area to capture.
-  ///
-  /// Uses simulator tool `xcrun simctl`'s 'screenshot' command.
-  @override
-  Future<Image> capture(math.Rectangle<num>? region) async {
-    final String filename = 'screenshot$_fileNameCounter.png';
-    _fileNameCounter++;
-
-    await iosSimulator.takeScreenshot(
-      filename, environment.webUiSimulatorScreenshotsDirectory,
-    );
-
-    final io.File file = io.File(path.join(
-        environment.webUiSimulatorScreenshotsDirectory.path, filename));
-    List<int> imageBytes;
-    if (!file.existsSync()) {
-      throw Exception('Failed to read the screenshot '
-          'screenshot$_fileNameCounter.png.');
-    }
-    imageBytes = await file.readAsBytes();
-    file.deleteSync();
-
-    final Image screenshot = decodePng(imageBytes)!;
-    // Create an image with no footer and header. The _heightOfHeader,
-    // _heightOfFooter values are already in real coordinates therefore
-    // they don't need to be scaled.
-    final Image content = copyCrop(
-      screenshot,
-      0,
-      _heightOfHeader,
-      screenshot.width,
-      screenshot.height - _heightOfFooter - _heightOfHeader,
-    );
-
-    if (region == null) {
-      return content;
-    } else {
-      final math.Rectangle<num> scaledRegion = _scaleScreenshotRegion(region);
-      return copyCrop(
-        content,
-        scaledRegion.left.toInt(),
-        scaledRegion.top.toInt(),
-        scaledRegion.width.toInt(),
-        scaledRegion.height.toInt(),
-      );
-    }
-  }
-
-  /// Perform a linear transform on the screenshot region to convert its
-  /// dimensions from linear coordinated to coordinated on the phone screen.
-  /// This uniform/isotropic scaling is done using [_scaleFactor].
-  math.Rectangle<num> _scaleScreenshotRegion(math.Rectangle<num> region) {
-    return math.Rectangle<num>(
-      region.left * _scaleFactor,
-      region.top * _scaleFactor,
-      region.width * _scaleFactor,
-      region.height * _scaleFactor,
-    );
-  }
-}
diff --git a/engine/dev/safari_macos.dart b/engine/dev/safari_macos.dart
deleted file mode 100644
index 1cbbed6..0000000
--- a/engine/dev/safari_macos.dart
+++ /dev/null
@@ -1,93 +0,0 @@
-// Copyright 2013 The Flutter 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' show json;
-import 'dart:io';
-
-import 'package:path/path.dart' as pathlib;
-import 'package:test_api/src/backend/runtime.dart';
-
-import 'browser.dart';
-import 'utils.dart';
-
-/// Provides an environment for the desktop variant of Safari running on macOS.
-class SafariMacOsEnvironment implements BrowserEnvironment {
-  @override
-  Browser launchBrowserInstance(Uri url, {bool debug = false}) {
-    return SafariMacOs(url);
-  }
-
-  @override
-  Runtime get packageTestRuntime => Runtime.safari;
-
-  @override
-  Future<void> prepare() async {
-    // Nothing extra to prepare for desktop Safari.
-  }
-
-  // We do not yet support screenshots on desktop Safari.
-  @override
-  ScreenshotManager? getScreenshotManager() => null;
-
-  @override
-  String get packageTestConfigurationYamlFile => 'dart_test_safari.yaml';
-}
-
-/// Runs an instance of Safari for macOS (i.e. desktop Safari).
-///
-/// Most of the communication with the browser is expected to happen via HTTP,
-/// so this exposes a bare-bones API. The browser starts as soon as the class is
-/// constructed, and is killed when [close] is called.
-///
-/// Any errors starting or running the process are reported through [onExit].
-class SafariMacOs extends Browser {
-  @override
-  final String name = 'Safari macOS';
-
-  /// Starts a new instance of Safari open to the given [url].
-  factory SafariMacOs(Uri url) {
-    return SafariMacOs._(() async {
-      // This hack to programmatically launch a test in Safari is borrowed from
-      // Karma: https://github.com/karma-runner/karma-safari-launcher/issues/29
-      //
-      // The issue is that opening an HTML file directly causes Safari to pop up
-      // a UI prompt to confirm the opening of a file. However, files under
-      // Library/Containers/com.apple.Safari/Data are exempt from this pop up.
-      // We create a "trampoline" file in this directory. The trampoline
-      // redirects the browser to the test URL in a <script>.
-      final String homePath = Platform.environment['HOME']!;
-      final Directory safariDataDirectory = Directory(pathlib.join(
-        homePath,
-        'Library/Containers/com.apple.Safari/Data',
-      ));
-      final Directory trampolineDirectory = await safariDataDirectory.createTemp('web-engine-test-trampoline-');
-
-      // Clean up trampoline files/directories before exiting felt.
-      cleanupCallbacks.add(() async {
-        if (trampolineDirectory.existsSync()) {
-          trampolineDirectory.delete(recursive: true);
-        }
-      });
-
-      final File trampoline = File(
-        pathlib.join(trampolineDirectory.path, 'trampoline.html'),
-      );
-      await trampoline.writeAsString('''
-<script>
-  location = ${json.encode(url.toString())};
-</script>
-      ''');
-
-      final Process process = await Process.start(
-        '/Applications/Safari.app/Contents/MacOS/Safari',
-        <String>[trampoline.path],
-      );
-
-      return process;
-    });
-  }
-
-  SafariMacOs._(Future<Process> Function() startBrowser) : super(startBrowser);
-}
diff --git a/engine/dev/steps/compile_tests_step.dart b/engine/dev/steps/compile_tests_step.dart
deleted file mode 100644
index 86088aa..0000000
--- a/engine/dev/steps/compile_tests_step.dart
+++ /dev/null
@@ -1,367 +0,0 @@
-// Copyright 2013 The Flutter 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:convert' show JsonEncoder;
-import 'dart:io' as io;
-
-import 'package:path/path.dart' as pathlib;
-import 'package:pool/pool.dart';
-import 'package:web_test_utils/goldens.dart';
-
-import '../environment.dart';
-import '../exceptions.dart';
-import '../pipeline.dart';
-import '../utils.dart';
-
-/// Compiles web tests and their dependencies into web_ui/build/.
-///
-/// Outputs of this step:
-///
-///  * canvaskit/   - CanvasKit artifacts
-///  * assets/      - test fonts
-///  * goldens/     - the goldens fetched from flutter/goldens
-///  * host/        - compiled test host page and static artifacts
-///  * test/        - compiled test code
-///  * test_images/ - test images copied from Skis sources.
-class CompileTestsStep implements PipelineStep {
-  CompileTestsStep({
-    this.skipGoldensRepoFetch = false,
-    this.testFiles,
-  });
-
-  final bool skipGoldensRepoFetch;
-  final List<FilePath>? testFiles;
-
-  @override
-  String get description => 'compile_tests';
-
-  @override
-  bool get isSafeToInterrupt => true;
-
-  @override
-  Future<void> interrupt() async {
-    await cleanup();
-  }
-
-  @override
-  Future<void> run() async {
-    await environment.webUiBuildDir.create();
-    if (!skipGoldensRepoFetch) {
-      await fetchGoldensRepo();
-    }
-    await copyCanvasKitFiles();
-    await buildHostPage();
-    await copyTestFonts();
-    await copySkiaTestImages();
-    await compileTests(testFiles ?? findAllTests());
-  }
-}
-
-const Map<String, String> _kTestFonts = <String, String>{
-  'Ahem': 'ahem.ttf',
-  'Roboto': 'Roboto-Regular.ttf',
-  'Noto Naskh Arabic UI': 'NotoNaskhArabic-Regular.ttf',
-  'Noto Color Emoji': 'NotoColorEmoji.ttf',
-};
-
-Future<void> copyTestFonts() async {
-  final String fontsPath = pathlib.join(
-    environment.flutterDirectory.path,
-    'third_party',
-    'txt',
-    'third_party',
-    'fonts',
-  );
-
-  final List<dynamic> fontManifest = <dynamic>[];
-  for (final MapEntry<String, String> fontEntry in _kTestFonts.entries) {
-    final String family = fontEntry.key;
-    final String fontFile = fontEntry.value;
-
-    fontManifest.add(<String, dynamic>{
-      'family': family,
-      'fonts': <dynamic>[
-        <String, String>{
-          'asset': 'fonts/$fontFile',
-        },
-      ],
-    });
-
-    final io.File sourceTtf = io.File(pathlib.join(fontsPath, fontFile));
-    final io.File destinationTtf = io.File(pathlib.join(
-      environment.webUiBuildDir.path,
-      'assets',
-      'fonts',
-      fontFile,
-    ));
-    await destinationTtf.create(recursive: true);
-    await sourceTtf.copy(destinationTtf.path);
-  }
-
-  final io.File fontManifestFile = io.File(pathlib.join(
-    environment.webUiBuildDir.path,
-    'assets',
-    'FontManifest.json',
-  ));
-  await fontManifestFile.create(recursive: true);
-  await fontManifestFile.writeAsString(
-    const JsonEncoder.withIndent('  ').convert(fontManifest),
-  );
-}
-
-Future<void> copySkiaTestImages() async {
-  final io.Directory testImagesDir = io.Directory(pathlib.join(
-    environment.engineSrcDir.path,
-    'third_party',
-    'skia',
-    'resources',
-    'images',
-  ));
-
-  for (final io.File imageFile in testImagesDir.listSync(recursive: true).whereType<io.File>()) {
-    final io.File destination = io.File(pathlib.join(
-      environment.webUiBuildDir.path,
-      'test_images',
-      pathlib.relative(imageFile.path, from: testImagesDir.path),
-    ));
-    destination.createSync(recursive: true);
-    await imageFile.copy(destination.path);
-  }
-}
-
-Future<void> copyCanvasKitFiles() async {
-  final io.Directory canvasKitDir = io.Directory(pathlib.join(
-    environment.engineSrcDir.path,
-    'third_party',
-    'web_dependencies',
-    'canvaskit',
-  ));
-
-  final Iterable<io.File> canvasKitFiles = canvasKitDir
-    .listSync(recursive: true, followLinks: true)
-    .whereType<io.File>();
-
-  final io.Directory targetDir = io.Directory(pathlib.join(
-    environment.webUiBuildDir.path,
-    'canvaskit',
-  ));
-
-  for (final io.File file in canvasKitFiles) {
-    final String relativePath = pathlib.relative(file.path, from: canvasKitDir.path);
-    final io.File targetFile = io.File(pathlib.join(
-      targetDir.path,
-      relativePath,
-    ));
-    await targetFile.create(recursive: true);
-    await file.copy(targetFile.path);
-  }
-}
-
-/// Compiles the specified unit tests.
-Future<void> compileTests(List<FilePath> testFiles) async {
-  final Stopwatch stopwatch = Stopwatch()..start();
-
-  // Separate HTML targets from CanvasKit targets because the two use
-  // different dart2js options.
-  final List<FilePath> htmlTargets = <FilePath>[];
-  final List<FilePath> canvasKitTargets = <FilePath>[];
-  final String canvasKitTestDirectory =
-      pathlib.join(environment.webUiTestDir.path, 'canvaskit');
-  for (final FilePath testFile in testFiles) {
-    if (pathlib.isWithin(canvasKitTestDirectory, testFile.absolute)) {
-      canvasKitTargets.add(testFile);
-    } else {
-      htmlTargets.add(testFile);
-    }
-  }
-
-  await Future.wait(<Future<void>>[
-    if (htmlTargets.isNotEmpty)
-      _compileTestsInParallel(targets: htmlTargets, forCanvasKit: false),
-    if (canvasKitTargets.isNotEmpty)
-      _compileTestsInParallel(targets: canvasKitTargets, forCanvasKit: true),
-  ]);
-
-  stopwatch.stop();
-
-  final int targetCount = htmlTargets.length + canvasKitTargets.length;
-  print(
-    'Built $targetCount tests in ${stopwatch.elapsedMilliseconds ~/ 1000} '
-    'seconds using $_dart2jsConcurrency concurrent dart2js processes.',
-  );
-}
-
-// Maximum number of concurrent dart2js processes to use.
-const int _dart2jsConcurrency = int.fromEnvironment('FELT_DART2JS_CONCURRENCY', defaultValue: 8);
-
-final Pool _dart2jsPool = Pool(_dart2jsConcurrency);
-
-/// Spawns multiple dart2js processes to compile [targets] in parallel.
-Future<void> _compileTestsInParallel({
-  required List<FilePath> targets,
-  required bool forCanvasKit,
-}) async {
-  final Stream<bool> results = _dart2jsPool.forEach(
-    targets,
-    (FilePath file) => compileUnitTest(file, forCanvasKit: forCanvasKit),
-  );
-  await for (final bool isSuccess in results) {
-    if (!isSuccess) {
-      throw ToolExit('Failed to compile tests.');
-    }
-  }
-}
-
-/// Compiles one unit test using `dart2js`.
-///
-/// When building for CanvasKit we have to use extra argument
-/// `DFLUTTER_WEB_USE_SKIA=true`.
-///
-/// Dart2js creates the following outputs:
-/// - target.browser_test.dart.js
-/// - target.browser_test.dart.js.deps
-/// - target.browser_test.dart.js.maps
-/// under the same directory with test file. If all these files are not in
-/// the same directory, Chrome dev tools cannot load the source code during
-/// debug.
-///
-/// All the files under test already copied from /test directory to /build
-/// directory before test are build. See [_copyFilesFromTestToBuild].
-///
-/// Later the extra files will be deleted in [_cleanupExtraFilesUnderTestDir].
-Future<bool> compileUnitTest(FilePath input, { required bool forCanvasKit }) async {
-  final String targetFileName = pathlib.join(
-    environment.webUiBuildDir.path,
-    '${input.relativeToWebUi}.browser_test.dart.js',
-  );
-
-  final io.Directory directoryToTarget = io.Directory(pathlib.join(
-      environment.webUiBuildDir.path,
-      pathlib.dirname(input.relativeToWebUi)));
-
-  if (!directoryToTarget.existsSync()) {
-    directoryToTarget.createSync(recursive: true);
-  }
-
-  final List<String> arguments = <String>[
-    'compile',
-    'js',
-    '--no-minify',
-    '--disable-inlining',
-    '--enable-asserts',
-    '--no-sound-null-safety',
-
-    // We do not want to auto-select a renderer in tests. As of today, tests
-    // are designed to run in one specific mode. So instead, we specify the
-    // renderer explicitly.
-    '-DFLUTTER_WEB_AUTO_DETECT=false',
-    '-DFLUTTER_WEB_USE_SKIA=$forCanvasKit',
-
-    '-O2',
-    '-o',
-    targetFileName, // target path.
-    input.relativeToWebUi, // current path.
-  ];
-
-  final int exitCode = await runProcess(
-    environment.dartExecutable,
-    arguments,
-    workingDirectory: environment.webUiRootDir.path,
-  );
-
-  if (exitCode != 0) {
-    io.stderr.writeln('ERROR: Failed to compile test $input. '
-        'Dart2js exited with exit code $exitCode');
-    return false;
-  } else {
-    return true;
-  }
-}
-
-Future<void> buildHostPage() async {
-  final String hostDartPath = pathlib.join('lib', 'static', 'host.dart');
-  final io.File hostDartFile = io.File(pathlib.join(
-    environment.webEngineTesterRootDir.path,
-    hostDartPath,
-  ));
-  final String targetDirectoryPath = pathlib.join(
-    environment.webUiBuildDir.path,
-    'host',
-  );
-  io.Directory(targetDirectoryPath).createSync(recursive: true);
-  final String targetFilePath = pathlib.join(
-    targetDirectoryPath,
-    'host.dart',
-  );
-
-  const List<String> staticFiles = <String>[
-    'favicon.ico',
-    'host.css',
-    'index.html',
-  ];
-  for (final String staticFilePath in staticFiles) {
-    final io.File source = io.File(pathlib.join(
-      environment.webEngineTesterRootDir.path,
-      'lib',
-      'static',
-      staticFilePath,
-    ));
-    final io.File destination = io.File(pathlib.join(
-      targetDirectoryPath,
-      staticFilePath,
-    ));
-    await source.copy(destination.path);
-  }
-
-  final io.File timestampFile = io.File(pathlib.join(
-    environment.webEngineTesterRootDir.path,
-    '$targetFilePath.js.timestamp',
-  ));
-
-  final String timestamp =
-      hostDartFile.statSync().modified.millisecondsSinceEpoch.toString();
-  if (timestampFile.existsSync()) {
-    final String lastBuildTimestamp = timestampFile.readAsStringSync();
-    if (lastBuildTimestamp == timestamp) {
-      // The file is still fresh. No need to rebuild.
-      return;
-    } else {
-      // Record new timestamp, but don't return. We need to rebuild.
-      print('${hostDartFile.path} timestamp changed. Rebuilding.');
-    }
-  } else {
-    print('Building ${hostDartFile.path}.');
-  }
-
-  final int exitCode = await runProcess(
-    environment.dartExecutable,
-    <String>[
-      'compile',
-      'js',
-      hostDartPath,
-      '-o',
-      '$targetFilePath.js',
-    ],
-    workingDirectory: environment.webEngineTesterRootDir.path,
-  );
-
-  if (exitCode != 0) {
-    throw ToolExit(
-      'Failed to compile ${hostDartFile.path}. Compiler '
-        'exited with exit code $exitCode',
-      exitCode: exitCode,
-    );
-  }
-
-  // Record the timestamp to avoid rebuilding unless the file changes.
-  timestampFile.writeAsStringSync(timestamp);
-}
-
-Future<void> fetchGoldensRepo() async {
-  print('INFO: Fetching goldens repo');
-  final GoldensRepoFetcher goldensRepoFetcher = GoldensRepoFetcher(
-      environment.webUiGoldensRepositoryDirectory,
-      pathlib.join(environment.webUiDevDir.path, 'goldens_lock.yaml'));
-  await goldensRepoFetcher.fetch();
-}
diff --git a/engine/dev/steps/run_tests_step.dart b/engine/dev/steps/run_tests_step.dart
deleted file mode 100644
index 185ae8e..0000000
--- a/engine/dev/steps/run_tests_step.dart
+++ /dev/null
@@ -1,325 +0,0 @@
-// Copyright 2013 The Flutter 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:io' as io;
-
-import 'package:path/path.dart' as pathlib;
-// TODO(yjbanov): remove hacks when this is fixed:
-//                https://github.com/dart-lang/test/issues/1521
-import 'package:test_api/src/backend/group.dart' as hack;
-import 'package:test_api/src/backend/live_test.dart' as hack;
-import 'package:test_api/src/backend/runtime.dart' as hack;
-import 'package:test_core/src/executable.dart' as test;
-import 'package:test_core/src/runner/configuration/reporters.dart' as hack;
-import 'package:test_core/src/runner/engine.dart' as hack;
-import 'package:test_core/src/runner/hack_register_platform.dart' as hack;
-import 'package:test_core/src/runner/reporter.dart' as hack;
-import 'package:web_test_utils/skia_client.dart';
-
-import '../browser.dart';
-import '../common.dart';
-import '../environment.dart';
-import '../exceptions.dart';
-import '../pipeline.dart';
-import '../test_platform.dart';
-import '../utils.dart';
-
-// Maximum number of tests that run concurrently.
-const int _testConcurrency = int.fromEnvironment('FELT_TEST_CONCURRENCY', defaultValue: 10);
-
-/// Runs web tests.
-///
-/// Assumes the artifacts from [CompileTestsStep] are available, either from
-/// running it prior to this step locally, or by having the build graph copy
-/// them from another bot.
-class RunTestsStep implements PipelineStep {
-  RunTestsStep({
-    required this.browserName,
-    required this.isDebug,
-    required this.doUpdateScreenshotGoldens,
-    this.testFiles,
-    required this.overridePathToCanvasKit,
-  }) : _browserEnvironment = getBrowserEnvironment(browserName);
-
-  final String browserName;
-  final List<FilePath>? testFiles;
-  final bool isDebug;
-  final bool doUpdateScreenshotGoldens;
-  final String? overridePathToCanvasKit;
-
-  final BrowserEnvironment _browserEnvironment;
-
-  /// Global list of shards that failed.
-  ///
-  /// This is used to make sure that when there's a test failure anywhere we
-  /// exit with a non-zero exit code.
-  ///
-  /// Shards must never be removed from this list, only added.
-  List<String> failedShards = <String>[];
-
-  /// Whether all test shards succeeded.
-  bool get allShardsPassed => failedShards.isEmpty;
-
-  @override
-  String get description => 'run_tests';
-
-  @override
-  bool get isSafeToInterrupt => true;
-
-  @override
-  Future<void> interrupt() async {}
-
-  @override
-  Future<void> run() async {
-    await _prepareTestResultsDirectory();
-    await _browserEnvironment.prepare();
-
-    final SkiaGoldClient? skiaClient = await _createSkiaClient();
-
-    final List<FilePath> testFiles = this.testFiles ?? findAllTests();
-
-    // Separate screenshot tests from unit-tests. Screenshot tests must run
-    // one at a time. Otherwise, they will end up screenshotting each other.
-    // This is not an issue for unit-tests.
-    final FilePath failureSmokeTestPath = FilePath.fromWebUi(
-      'test/golden_tests/golden_failure_smoke_test.dart',
-    );
-    final List<FilePath> screenshotTestFiles = <FilePath>[];
-    final List<FilePath> unitTestFiles = <FilePath>[];
-
-    for (final FilePath testFilePath in testFiles) {
-      if (!testFilePath.absolute.endsWith('_test.dart')) {
-        // Not a test file at all. Skip.
-        continue;
-      }
-      if (testFilePath == failureSmokeTestPath) {
-        // A smoke test that fails on purpose. Skip.
-        continue;
-      }
-
-      // All files under test/golden_tests are considered golden tests.
-      final bool isUnderGoldenTestsDirectory =
-          pathlib.split(testFilePath.relativeToWebUi).contains('golden_tests');
-      // Any file whose name ends with "_golden_test.dart" is run as a golden test.
-      final bool isGoldenTestFile = pathlib
-          .basename(testFilePath.relativeToWebUi)
-          .endsWith('_golden_test.dart');
-      if (isUnderGoldenTestsDirectory || isGoldenTestFile) {
-        screenshotTestFiles.add(testFilePath);
-      } else {
-        unitTestFiles.add(testFilePath);
-      }
-    }
-
-    // This test returns a non-zero exit code on purpose. Run it separately.
-    if (testFiles.contains(failureSmokeTestPath)) {
-      await _runTestBatch(
-        testFiles: <FilePath>[failureSmokeTestPath],
-        browserEnvironment: _browserEnvironment,
-        concurrency: 1,
-        expectFailure: true,
-        isDebug: isDebug,
-        doUpdateScreenshotGoldens: doUpdateScreenshotGoldens,
-        skiaClient: skiaClient,
-        overridePathToCanvasKit: overridePathToCanvasKit,
-      );
-    }
-
-    // Run non-screenshot tests with high concurrency.
-    if (unitTestFiles.isNotEmpty) {
-      await _runTestBatch(
-        testFiles: unitTestFiles,
-        browserEnvironment: _browserEnvironment,
-        concurrency: _testConcurrency,
-        expectFailure: false,
-        isDebug: isDebug,
-        doUpdateScreenshotGoldens: doUpdateScreenshotGoldens,
-        skiaClient: skiaClient,
-        overridePathToCanvasKit: overridePathToCanvasKit,
-      );
-      _checkExitCode('Unit tests');
-    }
-
-    // Run screenshot tests one at a time to prevent tests from screenshotting
-    // each other.
-    if (screenshotTestFiles.isNotEmpty) {
-      await _runTestBatch(
-        testFiles: screenshotTestFiles,
-        browserEnvironment: _browserEnvironment,
-        concurrency: 1,
-        expectFailure: false,
-        isDebug: isDebug,
-        doUpdateScreenshotGoldens: doUpdateScreenshotGoldens,
-        skiaClient: skiaClient,
-        overridePathToCanvasKit: overridePathToCanvasKit,
-      );
-      _checkExitCode('Golden tests');
-    }
-
-    if (!allShardsPassed) {
-      throw ToolExit(_createFailedShardsMessage());
-    }
-  }
-
-  void _checkExitCode(String shard) {
-    if (io.exitCode != 0) {
-      failedShards.add(shard);
-    }
-  }
-
-  String _createFailedShardsMessage() {
-    final StringBuffer message = StringBuffer(
-      'The following test shards failed:\n',
-    );
-    for (final String failedShard in failedShards) {
-      message.writeln(' - $failedShard');
-    }
-    return message.toString();
-  }
-
-  Future<SkiaGoldClient?> _createSkiaClient() async {
-    final SkiaGoldClient skiaClient = SkiaGoldClient(
-      environment.webUiSkiaGoldDirectory,
-      browserName: browserName,
-    );
-
-    if (!await _checkSkiaClient(skiaClient)) {
-      print('WARNING: Unable to use Skia Client in this environment.');
-      return null;
-    }
-
-    return skiaClient;
-  }
-
-  /// Checks whether the Skia Client is usable in this environment.
-  Future<bool> _checkSkiaClient(SkiaGoldClient skiaClient) async {
-    // Now let's check whether Skia Gold is reachable or not.
-    if (isLuci) {
-      if (SkiaGoldClient.isAvailable) {
-        try {
-          await skiaClient.auth();
-          return true;
-        } catch (e) {
-          print(e);
-        }
-      }
-    } else {
-      try {
-        // Check if we can reach Gold.
-        await skiaClient.getExpectationForTest('');
-        return true;
-      } on io.OSError catch (_) {
-        print('OSError occurred, could not reach Gold.');
-      } on io.SocketException catch (_) {
-        print('SocketException occurred, could not reach Gold.');
-      }
-    }
-
-    return false;
-  }
-}
-
-Future<void> _prepareTestResultsDirectory() async {
-  if (environment.webUiTestResultsDirectory.existsSync()) {
-    environment.webUiTestResultsDirectory.deleteSync(recursive: true);
-  }
-  environment.webUiTestResultsDirectory.createSync(recursive: true);
-}
-
-/// Runs a batch of tests.
-///
-/// Unless [expectFailure] is set to false, sets [io.exitCode] to a non-zero
-/// value if any tests fail.
-Future<void> _runTestBatch({
-  required List<FilePath> testFiles,
-  required bool isDebug,
-  required BrowserEnvironment browserEnvironment,
-  required bool doUpdateScreenshotGoldens,
-  required int concurrency,
-  required bool expectFailure,
-  required SkiaGoldClient? skiaClient,
-  required String? overridePathToCanvasKit,
-}) async {
-  final String configurationFilePath = pathlib.join(
-    environment.webUiRootDir.path,
-    browserEnvironment.packageTestConfigurationYamlFile,
-  );
-  final List<String> testArgs = <String>[
-    ...<String>['-r', 'compact'],
-    '--concurrency=$concurrency',
-    if (isDebug) '--pause-after-load',
-    // Don't pollute logs with output from tests that are expected to fail.
-    if (expectFailure)
-      '--reporter=name-only',
-    '--platform=${browserEnvironment.packageTestRuntime.identifier}',
-    '--precompiled=${environment.webUiBuildDir.path}',
-    '--configuration=$configurationFilePath',
-    '--',
-    ...testFiles.map((FilePath f) => f.relativeToWebUi).toList(),
-  ];
-
-  if (expectFailure) {
-    hack.registerReporter(
-      'name-only',
-      hack.ReporterDetails(
-      'Prints the name of the test, but suppresses all other test output.',
-      (_, hack.Engine engine, __) => NameOnlyReporter(engine)),
-    );
-  }
-
-  hack.registerPlatformPlugin(<hack.Runtime>[
-    browserEnvironment.packageTestRuntime,
-  ], () {
-    return BrowserPlatform.start(
-      browserEnvironment: browserEnvironment,
-      // It doesn't make sense to update a screenshot for a test that is
-      // expected to fail.
-      doUpdateScreenshotGoldens: !expectFailure && doUpdateScreenshotGoldens,
-      skiaClient: skiaClient,
-      overridePathToCanvasKit: overridePathToCanvasKit,
-    );
-  });
-
-  // We want to run tests with `web_ui` as a working directory.
-  final dynamic originalCwd = io.Directory.current;
-  io.Directory.current = environment.webUiRootDir.path;
-  try {
-    await test.main(testArgs);
-  } finally {
-    io.Directory.current = originalCwd;
-  }
-
-  if (expectFailure) {
-    if (io.exitCode != 0) {
-      // It failed, as expected.
-      print('Test successfully failed, as expected.');
-      io.exitCode = 0;
-    } else {
-      io.stderr.writeln(
-        'Tests ${testFiles.join(', ')} did not fail. Expected failure.',
-      );
-      io.exitCode = 1;
-    }
-  }
-}
-
-/// Prints the name of the test, but suppresses all other test output.
-///
-/// This is useful to prevent pollution of logs by tests that are expected to
-/// fail.
-class NameOnlyReporter implements hack.Reporter {
-  NameOnlyReporter(hack.Engine testEngine) {
-    testEngine.onTestStarted.listen(_printTestName);
-  }
-
-  void _printTestName(hack.LiveTest test) {
-    print('Running ${test.groups.map((hack.Group group) => group.name).join(' ')} ${test.individualName}');
-  }
-
-  @override
-  void pause() {}
-
-  @override
-  void resume() {}
-}
diff --git a/engine/dev/test_platform.dart b/engine/dev/test_platform.dart
deleted file mode 100644
index d9fd05c..0000000
--- a/engine/dev/test_platform.dart
+++ /dev/null
@@ -1,950 +0,0 @@
-// Copyright 2013 The Flutter 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 'dart:isolate';
-import 'dart:math';
-
-import 'package:async/async.dart';
-import 'package:http_multi_server/http_multi_server.dart';
-import 'package:image/image.dart';
-import 'package:package_config/package_config.dart';
-import 'package:path/path.dart' as p;
-import 'package:pool/pool.dart';
-import 'package:shelf/shelf.dart' as shelf;
-import 'package:shelf/shelf_io.dart' as shelf_io;
-import 'package:shelf_packages_handler/shelf_packages_handler.dart';
-import 'package:shelf_static/shelf_static.dart';
-import 'package:shelf_web_socket/shelf_web_socket.dart';
-import 'package:stream_channel/stream_channel.dart';
-
-import 'package:test_api/src/backend/runtime.dart';
-import 'package:test_api/src/backend/suite_platform.dart';
-import 'package:test_core/src/runner/configuration.dart';
-import 'package:test_core/src/runner/environment.dart';
-import 'package:test_core/src/runner/platform.dart';
-import 'package:test_core/src/runner/plugin/platform_helpers.dart';
-import 'package:test_core/src/runner/runner_suite.dart';
-import 'package:test_core/src/runner/suite.dart';
-import 'package:test_core/src/util/io.dart';
-import 'package:test_core/src/util/stack_trace_mapper.dart';
-
-import 'package:web_socket_channel/web_socket_channel.dart';
-import 'package:web_test_utils/goldens.dart';
-import 'package:web_test_utils/image_compare.dart';
-import 'package:web_test_utils/skia_client.dart';
-
-import 'browser.dart';
-import 'common.dart';
-import 'environment.dart' as env;
-
-/// Custom test platform that serves web engine unit tests.
-class BrowserPlatform extends PlatformPlugin {
-  /// Starts the server.
-  ///
-  /// [browserEnvironment] provides the browser environment to run the test.
-  ///
-  /// If [doUpdateScreenshotGoldens] is true updates screenshot golden files
-  /// instead of failing the test on screenshot mismatches.
-  static Future<BrowserPlatform> start({
-    required BrowserEnvironment browserEnvironment,
-    required bool doUpdateScreenshotGoldens,
-    required SkiaGoldClient? skiaClient,
-    required String? overridePathToCanvasKit,
-  }) async {
-    final shelf_io.IOServer server = shelf_io.IOServer(await HttpMultiServer.loopback(0));
-    return BrowserPlatform._(
-      browserEnvironment: browserEnvironment,
-      server: server,
-      isDebug: Configuration.current.pauseAfterLoad,
-      doUpdateScreenshotGoldens: doUpdateScreenshotGoldens,
-      packageConfig: await loadPackageConfigUri((await Isolate.packageConfig)!),
-      skiaClient: skiaClient,
-      overridePathToCanvasKit: overridePathToCanvasKit,
-    );
-  }
-
-  /// If true, runs the browser with a visible windows (i.e. not headless) and
-  /// pauses before running the tests to give the developer a chance to set
-  /// breakpoints in the code.
-  final bool isDebug;
-
-  /// The underlying server.
-  final shelf.Server server;
-
-  /// Provides the environment for the browser running tests.
-  final BrowserEnvironment browserEnvironment;
-
-  /// The URL for this server.
-  Uri get url => server.url.resolve('/');
-
-  /// A [OneOffHandler] for servicing WebSocket connections for
-  /// [BrowserManager]s.
-  ///
-  /// This is one-off because each [BrowserManager] can only connect to a single
-  /// WebSocket,
-  final OneOffHandler _webSocketHandler = OneOffHandler();
-
-  /// Handles taking screenshots during tests.
-  ///
-  /// Implementation will differ depending on the browser.
-  final ScreenshotManager? _screenshotManager;
-
-  /// Whether [close] has been called.
-  bool get _closed => _closeMemo.hasRun;
-
-  /// Whether to update screenshot golden files.
-  final bool doUpdateScreenshotGoldens;
-
-  late final shelf.Handler _packageUrlHandler = packagesDirHandler();
-
-  final PackageConfig packageConfig;
-
-  /// A client for communicating with the Skia Gold backend to fetch, compare
-  /// and update images.
-  final SkiaGoldClient? skiaClient;
-
-  final String? overridePathToCanvasKit;
-
-  BrowserPlatform._({
-    required this.browserEnvironment,
-    required this.server,
-    required this.isDebug,
-    required this.doUpdateScreenshotGoldens,
-    required this.packageConfig,
-    required this.skiaClient,
-    required this.overridePathToCanvasKit,
-  }) : _screenshotManager = browserEnvironment.getScreenshotManager() {
-    // The cascade of request handlers.
-    final shelf.Cascade cascade = shelf.Cascade()
-        // The web socket that carries the test channels for running tests and
-        // reporting restuls. See [_browserManagerFor] and [BrowserManager.start]
-        // for details on how the channels are established.
-        .add(_webSocketHandler.handler)
-
-        // Serves /packages/* requests; fetches files and sources from
-        // pubspec dependencies.
-        //
-        // Includes:
-        //  * Requests for Dart sources from source maps
-        //  * Assets that are part of the engine sources, such as Ahem.ttf
-        .add(_packageUrlHandler)
-
-        .add(_canvasKitOverrideHandler)
-
-        // Serves files from the web_ui/build/ directory at the root (/) URL path.
-        .add(buildDirectoryHandler)
-
-        .add(_testImageListingHandler)
-
-        // Serves the initial HTML for the test.
-        .add(_testBootstrapHandler)
-
-        // Serves files from the root of web_ui.
-        //
-        // This is needed because sourcemaps refer to local files, i.e. those
-        // that don't come from package dependencies, relative to web_ui/.
-        //
-        // Examples of URLs that this handles:
-        //  * /test/alarm_clock_test.dart
-        //  * /lib/src/engine/alarm_clock.dart
-        .add(createStaticHandler(env.environment.webUiRootDir.path))
-
-        // Serves absolute package URLs (i.e. not /packages/* but /Users/user/*/hosted/pub.dartlang.org/*).
-        // This handler goes last, after all more specific handlers failed to handle the request.
-        .add(_createAbsolutePackageUrlHandler())
-        .add(_screeshotHandler)
-        .add(_fileNotFoundCatcher);
-
-    server.mount(cascade.handler);
-  }
-
-  /// If a path to a custom local build of CanvasKit was specified, serve from
-  /// there instead of serving the default CanvasKit in the build/ directory.
-  Future<shelf.Response> _canvasKitOverrideHandler(shelf.Request request) async {
-    final String? pathOverride = overridePathToCanvasKit;
-
-    if (pathOverride == null || !request.url.path.startsWith('canvaskit/')) {
-      return shelf.Response.notFound('Not a request for CanvasKit.');
-    }
-
-    final File file = File(p.joinAll(<String>[
-      pathOverride,
-      ...p.split(request.url.path).skip(1),
-    ]));
-
-    if (!file.existsSync()) {
-      return shelf.Response.notFound('File not found: ${request.url.path}');
-    }
-
-    final String extension = p.extension(file.path);
-    final String? contentType = contentTypes[extension];
-
-    if (contentType == null) {
-      final String error = 'Failed to determine Content-Type for "${request.url.path}".';
-      stderr.writeln(error);
-      return shelf.Response.internalServerError(body: error);
-    }
-
-    return shelf.Response.ok(
-      file.readAsBytesSync(),
-      headers: <String, Object>{
-        HttpHeaders.contentTypeHeader: contentType,
-      },
-    );
-  }
-
-  /// Lists available test images under `web_ui/build/test_images`.
-  Future<shelf.Response> _testImageListingHandler(shelf.Request request) async {
-    const Map<String, String> supportedImageTypes = <String, String>{
-      '.png': 'image/png',
-      '.jpg': 'image/jpeg',
-      '.jpeg': 'image/jpeg',
-      '.gif': 'image/gif',
-      '.webp': 'image/webp',
-      '.bmp': 'image/bmp',
-    };
-
-    if (request.url.path != 'test_images/') {
-      return shelf.Response.notFound('Not found.');
-    }
-
-    final Directory testImageDirectory = Directory(p.join(
-      env.environment.webUiBuildDir.path,
-      'test_images',
-    ));
-
-    final List<String> testImageFiles = testImageDirectory
-      .listSync(recursive: true)
-      .whereType<File>()
-      .map<String>((File file) => p.relative(file.path, from: testImageDirectory.path))
-      .where((String path) => supportedImageTypes.containsKey(p.extension(path)))
-      .toList();
-
-    return shelf.Response.ok(
-      json.encode(testImageFiles),
-      headers: <String, Object>{
-        HttpHeaders.contentTypeHeader: 'application/json',
-      },
-    );
-  }
-
-  Future<shelf.Response> _fileNotFoundCatcher(shelf.Request request) async {
-    print('HTTP 404: ${request.url}');
-    return shelf.Response.notFound('File not found');
-  }
-
-  /// Handles URLs pointing to Dart sources using absolute URI paths.
-  ///
-  /// Dart source paths that dart2js puts in source maps for pub packages are
-  /// relative to the source map file. Example:
-  ///
-  ///     ../../../../../../../../../Users/yegor/AppData/Local/Pub/Cache/hosted/pub.dartlang.org/stack_trace-1.10.0/lib/src/frame.dart
-  ///
-  /// When the browser requests the file from the source map it sends a GET
-  /// request like this:
-  ///
-  ///     GET /Users/yegor/AppData/Local/Pub/Cache/hosted/pub.dartlang.org/stack_trace-1.10.0/lib/src/frame.dart
-  ///
-  /// There's no predictable structure in this URL. It's unclear whether this
-  /// is a request for a source file, or someone trying to hack your
-  /// workstation.
-  ///
-  /// This handler treats the URL as an absolute path, but instead of
-  /// unconditionally serving it, it first checks with `package_config.json` on
-  /// whether this is a request for a Dart source that's listed in pubspec
-  /// dependencies. For example, the `stack_trace` package would be listed in
-  /// `package_config.json` as:
-  ///
-  ///     file:///C:/Users/yegor/AppData/Local/Pub/Cache/hosted/pub.dartlang.org/stack_trace-1.10.0
-  ///
-  /// If the requested URL points into one of the packages in the package config,
-  /// the file is served. Otherwise, HTTP 404 is returned without file contents.
-  ///
-  /// To handle drive letters (C:\) and *nix file system roots, the URL and
-  /// package paths are initially stripped of the root and compared to each
-  /// other as prefixes. To actually read the file, the file system root is
-  /// prepended before creating the file.
-  shelf.Handler _createAbsolutePackageUrlHandler() {
-    final Map<String, Package> urlToPackage = <String, Package>{};
-    for (final Package package in packageConfig.packages) {
-      // Turns the URI as encoded in package_config.json to a file path.
-      final String configPath = p.fromUri(package.root);
-
-      // Strips drive letter and root prefix, if any, for example:
-      //
-      // C:\Users\user\AppData => Users\user\AppData
-      // /home/user/path.dart => home/user/path.dart
-      final String rootRelativePath = p.relative(configPath, from: p.rootPrefix(configPath));
-      urlToPackage[p.toUri(rootRelativePath).path] = package;
-    }
-    return (shelf.Request request) async {
-      final String requestedPath = request.url.path;
-      // The cast is needed because keys are non-null String, so there's no way
-      // to return null for a mismatch.
-      final String? packagePath = urlToPackage.keys.cast<String?>().firstWhere(
-        (String? packageUrl) => requestedPath.startsWith(packageUrl!),
-        orElse: () => null,
-      );
-      if (packagePath == null) {
-        return shelf.Response.notFound('Not a pub.dartlang.org request');
-      }
-
-      // Attach the root prefix, such as drive letter, and convert from URI to path.
-      // Examples:
-      //
-      // Users\user\AppData => C:\Users\user\AppData
-      // home/user/path.dart => /home/user/path.dart
-      final Package package = urlToPackage[packagePath]!;
-      final String filePath = p.join(
-        p.rootPrefix(p.fromUri(package.root.path)),
-        p.fromUri(requestedPath),
-      );
-      final File fileInPackage = File(filePath);
-      if (!fileInPackage.existsSync()) {
-        return shelf.Response.notFound('File not found: $requestedPath');
-      }
-      return shelf.Response.ok(fileInPackage.openRead());
-    };
-  }
-
-  Future<shelf.Response> _screeshotHandler(shelf.Request request) async {
-    if (!request.requestedUri.path.endsWith('/screenshot')) {
-      return shelf.Response.notFound(
-          'This request is not handled by the screenshot handler');
-    }
-
-    final String payload = await request.readAsString();
-    final Map<String, dynamic> requestData =
-        json.decode(payload) as Map<String, dynamic>;
-    final String filename = requestData['filename'] as String;
-
-    if (_screenshotManager == null) {
-      print(
-        'INFO: Skipping screenshot check for $filename. Current browser/OS '
-        'combination does not support screenshots.',
-      );
-      return shelf.Response.ok(json.encode('OK'));
-    }
-
-    final bool write = requestData['write'] as bool;
-    final double maxDiffRate = requestData.containsKey('maxdiffrate')
-        ? (requestData['maxdiffrate'] as num)
-            .toDouble() // can be parsed as either int or double
-        : kMaxDiffRateFailure;
-    final Map<String, dynamic> region =
-        requestData['region'] as Map<String, dynamic>;
-    final PixelComparison pixelComparison = PixelComparison.values.firstWhere(
-        (PixelComparison value) => value.toString() == requestData['pixelComparison']);
-    final String result = await _diffScreenshot(
-        filename, write, maxDiffRate, region, pixelComparison);
-    return shelf.Response.ok(json.encode(result));
-  }
-
-  Future<String> _diffScreenshot(
-      String filename,
-      bool write,
-      double maxDiffRateFailure,
-      Map<String, dynamic> region,
-      PixelComparison pixelComparison) async {
-    if (doUpdateScreenshotGoldens) {
-      write = true;
-    }
-
-    String goldensDirectory;
-    if (filename.startsWith('__local__')) {
-      filename = filename.substring('__local__/'.length);
-      goldensDirectory = p.join(
-        env.environment.webUiRootDir.path,
-        'test',
-        'golden_files',
-      );
-    } else {
-      goldensDirectory = p.join(
-        env.environment.webUiGoldensRepositoryDirectory.path,
-        'engine',
-        'web',
-      );
-    }
-
-    final Rectangle<num> regionAsRectange = Rectangle<num>(
-      region['x'] as num,
-      region['y'] as num,
-      region['width'] as num,
-      region['height'] as num,
-    );
-
-    // Take screenshot.
-    final Image screenshot = await _screenshotManager!.capture(regionAsRectange);
-
-    return compareImage(
-      screenshot,
-      doUpdateScreenshotGoldens,
-      filename,
-      pixelComparison,
-      maxDiffRateFailure,
-      skiaClient,
-      goldensDirectory: goldensDirectory,
-      filenameSuffix: _screenshotManager!.filenameSuffix,
-      write: write,
-    );
-  }
-
-  static const Map<String, String> contentTypes = <String, String>{
-    '.js': 'text/javascript',
-    '.wasm': 'application/wasm',
-    '.html': 'text/html',
-    '.htm': 'text/html',
-    '.css': 'text/css',
-    '.ico': 'image/icon-x',
-    '.png': 'image/png',
-    '.jpg': 'image/jpeg',
-    '.jpeg': 'image/jpeg',
-    '.gif': 'image/gif',
-    '.webp': 'image/webp',
-    '.bmp': 'image/bmp',
-    '.svg': 'image/svg+xml',
-    '.json': 'application/json',
-    '.ttf': 'font/ttf',
-    '.woff': 'font/woff',
-    '.woff2': 'font/woff2',
-  };
-
-  /// A simple file handler that serves files whose URLs and paths are
-  /// statically known.
-  ///
-  /// This is used for trivial use-cases, such as `favicon.ico`, host pages, etc.
-  shelf.Response buildDirectoryHandler(shelf.Request request) {
-    final File fileInBuild = File(p.join(
-      env.environment.webUiBuildDir.path,
-      request.url.path,
-    ));
-
-    if (!fileInBuild.existsSync()) {
-      return shelf.Response.notFound('File not found: ${request.url.path}');
-    }
-
-    final String extension = p.extension(fileInBuild.path);
-    final String? contentType = contentTypes[extension];
-
-    if (contentType == null) {
-      final String error = 'Failed to determine Content-Type for "${request.url.path}".';
-      stderr.writeln(error);
-      return shelf.Response.internalServerError(body: error);
-    }
-
-    return shelf.Response.ok(
-      fileInBuild.readAsBytesSync(),
-      headers: <String, Object>{
-        HttpHeaders.contentTypeHeader: contentType,
-      },
-    );
-  }
-
-  /// Serves the HTML file that bootstraps the test.
-  shelf.Response _testBootstrapHandler(shelf.Request request) {
-    final String path = p.fromUri(request.url);
-
-    if (path.endsWith('.html')) {
-      final String test = p.withoutExtension(path) + '.dart';
-
-      // Link to the Dart wrapper.
-      final String scriptBase = htmlEscape.convert(p.basename(test));
-      final String link = '<link rel="x-dart-test" href="$scriptBase">';
-
-      return shelf.Response.ok('''
-        <!DOCTYPE html>
-        <html>
-        <head>
-          <title>${htmlEscape.convert(test)} Test</title>
-          <meta name="assetBase" content="/">
-          <script>
-            window.flutterConfiguration = {
-              canvasKitBaseUrl: "/canvaskit/"
-            };
-          </script>
-          $link
-          <script src="packages/test/dart.js"></script>
-        </head>
-        </html>
-      ''', headers: <String, String>{'Content-Type': 'text/html'});
-    }
-
-    return shelf.Response.notFound('Not found.');
-  }
-
-  void _checkNotClosed() {
-    if (_closed) {
-      throw StateError('Cannot load test suite. Test platform is closed.');
-    }
-  }
-
-  /// Loads the test suite at [path] on the platform [platform].
-  ///
-  /// This will start a browser to load the suite if one isn't already running.
-  /// Throws an [ArgumentError] if `platform.platform` isn't a browser.
-  @override
-  Future<RunnerSuite> load(String path, SuitePlatform platform,
-      SuiteConfiguration suiteConfig, Object message) async {
-    _checkNotClosed();
-    if (suiteConfig.precompiledPath == null) {
-      throw Exception('This test platform only supports precompiled JS.');
-    }
-    final Runtime browser = platform.runtime;
-    assert(suiteConfig.runtimes.contains(browser.identifier));
-
-    if (!browser.isBrowser) {
-      throw ArgumentError('$browser is not a browser.');
-    }
-    _checkNotClosed();
-
-    final Uri suiteUrl = url.resolveUri(
-        p.toUri(p.withoutExtension(p.relative(path, from: env.environment.webUiBuildDir.path)) + '.html'));
-    _checkNotClosed();
-
-    final BrowserManager? browserManager = await _startBrowserManager();
-    if (browserManager == null) {
-      throw StateError('Failed to initialize browser manager for ${browser.name}');
-    }
-    _checkNotClosed();
-
-    final RunnerSuite suite = await browserManager.load(path, suiteUrl, suiteConfig, message);
-    _checkNotClosed();
-    return suite;
-  }
-
-  @override
-  StreamChannel<dynamic> loadChannel(String path, SuitePlatform platform) =>
-      throw UnimplementedError();
-
-  Future<BrowserManager?>? _browserManager;
-
-  /// Starts a browser manager for the browser provided by [browserEnvironment];
-  ///
-  /// If no browser manager is running yet, starts one.
-  Future<BrowserManager?> _startBrowserManager() {
-    if (_browserManager != null) {
-      return _browserManager!;
-    }
-
-    final Completer<WebSocketChannel> completer = Completer<WebSocketChannel>.sync();
-    final String path = _webSocketHandler.create(webSocketHandler(completer.complete));
-    final Uri webSocketUrl = url.replace(scheme: 'ws').resolve(path);
-    final Uri hostUrl = url
-        .resolve('host/index.html')
-        .replace(queryParameters: <String, dynamic>{
-      'managerUrl': webSocketUrl.toString(),
-      'debug': isDebug.toString()
-    });
-
-    final Future<BrowserManager?> future = BrowserManager.start(
-      browserEnvironment: browserEnvironment,
-      url: hostUrl,
-      future: completer.future,
-      packageConfig: packageConfig,
-      debug: isDebug,
-    );
-
-    // Store null values for browsers that error out so we know not to load them
-    // again.
-    _browserManager = future.catchError((dynamic _) => null);
-
-    return future;
-  }
-
-  /// Close all the browsers that the server currently has open.
-  ///
-  /// Note that this doesn't close the server itself. Browser tests can still be
-  /// loaded, they'll just spawn new browsers.
-  @override
-  Future<void> closeEphemeral() async {
-    if (_browserManager != null) {
-      final BrowserManager? result = await _browserManager!;
-      await result?.close();
-    }
-  }
-
-  /// Closes the server and releases all its resources.
-  ///
-  /// Returns a [Future] that completes once the server is closed and its
-  /// resources have been fully released.
-  @override
-  Future<void> close() {
-    return _closeMemo.runOnce(() async {
-      final List<Future<void>> futures = <Future<void>>[];
-      futures.add(Future<void>.microtask(() async {
-        if (_browserManager != null) {
-          final BrowserManager? result = await _browserManager!;
-          await result?.close();
-        }
-      }));
-      futures.add(server.close());
-
-      await Future.wait(futures);
-    });
-  }
-
-  final AsyncMemoizer<dynamic> _closeMemo = AsyncMemoizer<dynamic>();
-}
-
-/// A Shelf handler that provides support for one-time handlers.
-///
-/// This is useful for handlers that only expect to be hit once before becoming
-/// invalid and don't need to have a persistent URL.
-class OneOffHandler {
-  /// A map from URL paths to handlers.
-  final Map<String, shelf.Handler> _handlers = <String, shelf.Handler>{};
-
-  /// The counter of handlers that have been activated.
-  int _counter = 0;
-
-  /// The actual [shelf.Handler] that dispatches requests.
-  shelf.Handler get handler => _onRequest;
-
-  /// Creates a new one-off handler that forwards to [handler].
-  ///
-  /// Returns a string that's the URL path for hitting this handler, relative to
-  /// the URL for the one-off handler itself.
-  ///
-  /// [handler] will be unmounted as soon as it receives a request.
-  String create(shelf.Handler handler) {
-    final String path = _counter.toString();
-    _handlers[path] = handler;
-    _counter++;
-    return path;
-  }
-
-  /// Dispatches [request] to the appropriate handler.
-  FutureOr<shelf.Response> _onRequest(shelf.Request request) {
-    final List<String> components = p.url.split(request.url.path);
-    if (components.isEmpty) {
-      return shelf.Response.notFound(null);
-    }
-
-    final String path = components.removeAt(0);
-    final shelf.Handler? handler = _handlers.remove(path);
-    if (handler == null) {
-      return shelf.Response.notFound(null);
-    }
-    return handler(request.change(path: path));
-  }
-}
-
-/// Manages the connection to a single running browser.
-///
-/// This is in charge of telling the browser which test suites to load and
-/// converting its responses into [Suite] objects.
-class BrowserManager {
-  final PackageConfig packageConfig;
-
-  /// The browser instance that this is connected to via [_channel].
-  final Browser _browser;
-
-  /// The browser environment for this test.
-  final BrowserEnvironment _browserEnvironment;
-
-  /// The channel used to communicate with the browser.
-  ///
-  /// This is connected to a page running `static/host.dart`.
-  late final MultiChannel<dynamic> _channel;
-
-  /// A pool that ensures that limits the number of initial connections the
-  /// manager will wait for at once.
-  ///
-  /// This isn't the *total* number of connections; any number of iframes may be
-  /// loaded in the same browser. However, the browser can only load so many at
-  /// once, and we want a timeout in case they fail so we only wait for so many
-  /// at once.
-  final Pool _pool = Pool(8);
-
-  /// The ID of the next suite to be loaded.
-  ///
-  /// This is used to ensure that the suites can be referred to consistently
-  /// across the client and server.
-  int _suiteID = 0;
-
-  /// Whether the channel to the browser has closed.
-  bool _closed = false;
-
-  /// The completer for [_BrowserEnvironment.displayPause].
-  ///
-  /// This will be `null` as long as the browser isn't displaying a pause
-  /// screen.
-  CancelableCompleter<void>? _pauseCompleter;
-
-  /// The controller for [_BrowserEnvironment.onRestart].
-  final StreamController<dynamic> _onRestartController = StreamController<dynamic>.broadcast();
-
-  /// The environment to attach to each suite.
-  late final Future<_BrowserEnvironment> _environment;
-
-  /// Controllers for every suite in this browser.
-  ///
-  /// These are used to mark suites as debugging or not based on the browser's
-  /// pings.
-  final Set<RunnerSuiteController> _controllers = <RunnerSuiteController>{};
-
-  // A timer that's reset whenever we receive a message from the browser.
-  //
-  // Because the browser stops running code when the user is actively debugging,
-  // this lets us detect whether they're debugging reasonably accurately.
-  late final RestartableTimer _timer;
-
-  /// Starts the browser identified by [runtime] and has it connect to [url].
-  ///
-  /// [url] should serve a page that establishes a WebSocket connection with
-  /// this process. That connection, once established, should be emitted via
-  /// [future]. If [debug] is true, starts the browser in debug mode, with its
-  /// debugger interfaces on and detected.
-  ///
-  /// The [settings] indicate how to invoke this browser's executable.
-  ///
-  /// Returns the browser manager, or throws an [Exception] if a
-  /// connection fails to be established.
-  static Future<BrowserManager?> start({
-    required BrowserEnvironment browserEnvironment,
-    required Uri url,
-    required Future<WebSocketChannel> future,
-    required PackageConfig packageConfig,
-    bool debug = false,
-  }) {
-    final Browser browser = _newBrowser(url, browserEnvironment, debug: debug);
-
-    final Completer<BrowserManager> completer = Completer<BrowserManager>();
-
-    // For the cases where we use a delegator such as `adb` (for Android) or
-    // `xcrun` (for IOS), these delegator processes can shut down before the
-    // websocket is available. Therefore do not throw an error if process
-    // exits with exitCode 0. Note that `browser` will throw and error if the
-    // exit code was not 0, which will be processed by the next callback.
-    browser.onExit.catchError((Object error, StackTrace stackTrace) {
-      if (completer.isCompleted) {
-        return;
-      }
-      completer.completeError(error, stackTrace);
-    });
-
-    future.then((WebSocketChannel webSocket) {
-      if (completer.isCompleted) {
-        return;
-      }
-      completer.complete(BrowserManager._(packageConfig, browser, browserEnvironment, webSocket));
-    }).catchError((Object error, StackTrace stackTrace) {
-      browser.close();
-      if (completer.isCompleted) {
-        return null;
-      }
-      completer.completeError(error, stackTrace);
-    });
-
-    return completer.future;
-  }
-
-  /// Starts the browser and requests that it load the test page at [url].
-  ///
-  /// If [debug] is true, starts the browser in debug mode.
-  static Browser _newBrowser(Uri url, BrowserEnvironment browserEnvironment, {bool debug = false}) {
-    return browserEnvironment.launchBrowserInstance(url, debug: debug);
-  }
-
-  /// Creates a new BrowserManager that communicates with the browser over
-  /// [webSocket].
-  BrowserManager._(this.packageConfig, this._browser, this._browserEnvironment, WebSocketChannel webSocket) {
-    // The duration should be short enough that the debugging console is open as
-    // soon as the user is done setting breakpoints, but long enough that a test
-    // doing a lot of synchronous work doesn't trigger a false positive.
-    //
-    // Start this canceled because we don't want it to start ticking until we
-    // get some response from the iframe.
-    _timer = RestartableTimer(const Duration(seconds: 3), () {
-      for (final RunnerSuiteController controller in _controllers) {
-        controller.setDebugging(true);
-      }
-    })
-      ..cancel();
-
-    // Whenever we get a message, no matter which child channel it's for, we the
-    // know browser is still running code which means the user isn't debugging.
-    _channel = MultiChannel<dynamic>(
-        webSocket.cast<String>().transform(jsonDocument).changeStream((Stream<Object?> stream) {
-      return stream.map((Object? message) {
-        if (!_closed) {
-          _timer.reset();
-        }
-        for (final RunnerSuiteController controller in _controllers) {
-          controller.setDebugging(false);
-        }
-
-        return message;
-      });
-    }));
-
-    _environment = _loadBrowserEnvironment();
-    _channel.stream
-        .listen((dynamic message) => _onMessage(message as Map<dynamic, dynamic>), onDone: close);
-  }
-
-  /// Loads [_BrowserEnvironment].
-  Future<_BrowserEnvironment> _loadBrowserEnvironment() async {
-    return _BrowserEnvironment(this, await _browser.observatoryUrl,
-        await _browser.remoteDebuggerUrl, _onRestartController.stream);
-  }
-
-  /// Tells the browser the load a test suite from the URL [url].
-  ///
-  /// [url] should be an HTML page with a reference to the JS-compiled test
-  /// suite. [path] is the path of the original test suite file, which is used
-  /// for reporting. [suiteConfig] is the configuration for the test suite.
-  Future<RunnerSuite> load(String path, Uri url, SuiteConfiguration suiteConfig,
-      Object message) async {
-    url = url.replace(
-        fragment: Uri.encodeFull(jsonEncode(<String, dynamic>{
-      'metadata': suiteConfig.metadata.serialize(),
-      'browser': _browserEnvironment.packageTestRuntime.identifier
-    })));
-
-    final int suiteID = _suiteID++;
-    RunnerSuiteController? controller;
-    void closeIframe() {
-      if (_closed) {
-        return;
-      }
-      _controllers.remove(controller);
-      _channel.sink.add(<String, dynamic>{'command': 'closeSuite', 'id': suiteID});
-    }
-
-    // The virtual channel will be closed when the suite is closed, in which
-    // case we should unload the iframe.
-    final VirtualChannel<dynamic> virtualChannel = _channel.virtualChannel();
-    final int suiteChannelID = virtualChannel.id;
-    final StreamChannel<dynamic> suiteChannel = virtualChannel.transformStream(
-        StreamTransformer<dynamic, dynamic>.fromHandlers(handleDone: (EventSink<dynamic> sink) {
-      closeIframe();
-      sink.close();
-    }));
-
-    return _pool.withResource<RunnerSuite>(() async {
-      _channel.sink.add(<String, dynamic>{
-        'command': 'loadSuite',
-        'url': url.toString(),
-        'id': suiteID,
-        'channel': suiteChannelID
-      });
-
-      try {
-        controller = deserializeSuite(path, currentPlatform(_browserEnvironment.packageTestRuntime),
-            suiteConfig, await _environment, suiteChannel, message);
-
-        final String sourceMapFileName =
-            '${p.basename(path)}.browser_test.dart.js.map';
-        final String pathToTest = p.dirname(path);
-
-        final String mapPath = p.join(env.environment.webUiRootDir.path,
-            'build', pathToTest, sourceMapFileName);
-
-        final Map<String, Uri> packageMap = <String, Uri>{
-          for (Package p in packageConfig.packages) p.name: p.packageUriRoot
-        };
-        final JSStackTraceMapper mapper = JSStackTraceMapper(
-          await File(mapPath).readAsString(),
-          mapUrl: p.toUri(mapPath),
-          packageMap: packageMap,
-          sdkRoot: p.toUri(sdkDir),
-        );
-
-        controller!.channel('test.browser.mapper').sink.add(mapper.serialize());
-
-        _controllers.add(controller!);
-        return await controller!.suite;
-      } catch (_) {
-        closeIframe();
-        rethrow;
-      }
-    });
-  }
-
-  /// An implementation of [Environment.displayPause].
-  CancelableOperation<void> _displayPause() {
-    CancelableCompleter<void>? pauseCompleter = _pauseCompleter;
-    if (pauseCompleter != null) {
-      return pauseCompleter.operation;
-    }
-
-    pauseCompleter = CancelableCompleter<void>(onCancel: () {
-      _channel.sink.add(<String, String>{'command': 'resume'});
-      _pauseCompleter = null;
-    });
-    _pauseCompleter = pauseCompleter;
-
-    pauseCompleter.operation.value.whenComplete(() {
-      _pauseCompleter = null;
-    });
-
-    _channel.sink.add(<String, String>{'command': 'displayPause'});
-
-    return pauseCompleter.operation;
-  }
-
-  /// The callback for handling messages received from the host page.
-  void _onMessage(Map<dynamic, dynamic> message) {
-    switch (message['command'] as String) {
-      case 'ping':
-        break;
-
-      case 'restart':
-        _onRestartController.add(null);
-        break;
-
-      case 'resume':
-        _pauseCompleter?.complete();
-        break;
-
-      default:
-        // Unreachable.
-        assert(false);
-        break;
-    }
-  }
-
-  /// Closes the manager and releases any resources it owns, including closing
-  /// the browser.
-  Future<void> close() => _closeMemoizer.runOnce(() {
-        _closed = true;
-        _timer.cancel();
-        _pauseCompleter?.complete();
-        _pauseCompleter = null;
-        _controllers.clear();
-        return _browser.close();
-      });
-  final AsyncMemoizer<dynamic> _closeMemoizer = AsyncMemoizer<dynamic>();
-}
-
-/// An implementation of [Environment] for the browser.
-///
-/// All methods forward directly to [BrowserManager].
-class _BrowserEnvironment implements Environment {
-  final BrowserManager _manager;
-
-  @override
-  final bool supportsDebugging = true;
-
-  @override
-  final Uri? observatoryUrl;
-
-  @override
-  final Uri? remoteDebuggerUrl;
-
-  @override
-  final Stream<dynamic> onRestart;
-
-  _BrowserEnvironment(this._manager, this.observatoryUrl,
-      this.remoteDebuggerUrl, this.onRestart);
-
-  @override
-  CancelableOperation<void> displayPause() => _manager._displayPause();
-}
-
-bool get isCirrus => Platform.environment['CIRRUS_CI'] == 'true';
diff --git a/engine/dev/test_runner.dart b/engine/dev/test_runner.dart
deleted file mode 100644
index 543cd80..0000000
--- a/engine/dev/test_runner.dart
+++ /dev/null
@@ -1,204 +0,0 @@
-// Copyright 2013 The Flutter 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:io' as io;
-
-import 'package:args/command_runner.dart';
-import 'package:path/path.dart' as path;
-
-import 'package:watcher/src/watch_event.dart';
-
-import 'pipeline.dart';
-import 'steps/compile_tests_step.dart';
-import 'steps/run_tests_step.dart';
-import 'utils.dart';
-
-/// Runs tests.
-class TestCommand extends Command<bool> with ArgUtils<bool> {
-  TestCommand() {
-    argParser
-      ..addFlag(
-        'debug',
-        defaultsTo: false,
-        help: 'Pauses the browser before running a test, giving you an '
-            'opportunity to add breakpoints or inspect loaded code before '
-            'running the code.',
-      )
-      ..addFlag(
-        'watch',
-        defaultsTo: false,
-        abbr: 'w',
-        help: 'Run in watch mode so the tests re-run whenever a change is '
-            'made.',
-      )
-      ..addFlag(
-        'unit-tests-only',
-        defaultsTo: false,
-        help: 'felt test command runs the unit tests and the integration tests '
-            'at the same time. If this flag is set, only run the unit tests.',
-      )
-      ..addFlag('use-system-flutter',
-          defaultsTo: false,
-          help:
-              'integration tests are using flutter repository for various tasks'
-              ', such as flutter drive, flutter pub get. If this flag is set, felt '
-              'will use flutter command without cloning the repository. This flag '
-              'can save internet bandwidth. However use with caution. Note that '
-              'since flutter repo is always synced to youngest commit older than '
-              'the engine commit for the tests running in CI, the tests results '
-              'won\'t be consistent with CIs when this flag is set. flutter '
-              'command should be set in the PATH for this flag to be useful.'
-              'This flag can also be used to test local Flutter changes.')
-      ..addFlag(
-        'update-screenshot-goldens',
-        defaultsTo: false,
-        help:
-            'When running screenshot tests writes them to the file system into '
-            '.dart_tool/goldens. Use this option to bulk-update all screenshots, '
-            'for example, when a new browser version affects pixels.',
-      )
-      ..addFlag(
-        'skip-goldens-repo-fetch',
-        defaultsTo: false,
-        help: 'If set reuses the existig flutter/goldens repo clone. Use this '
-            'to avoid overwriting local changes when iterating on golden '
-            'tests. This is off by default.',
-      )
-      ..addOption(
-        'browser',
-        defaultsTo: 'chrome',
-        help: 'An option to choose a browser to run the tests. By default '
-              'tests run in Chrome.',
-      )
-      ..addFlag(
-        'fail-early',
-        defaultsTo: false,
-        negatable: true,
-        help: 'If set, causes the test runner to exit upon the first test '
-              'failure. If not set, the test runner will continue running '
-              'test despite failures and will report them after all tests '
-              'finish.',
-      )
-      ..addOption(
-        'canvaskit-path',
-        help: 'Optional. The path to a local build of CanvasKit to use in '
-              'tests. If omitted, the test runner uses the default CanvasKit '
-              'build.',
-      );
-  }
-
-  @override
-  final String name = 'test';
-
-  @override
-  final String description = 'Run tests.';
-
-  bool get isWatchMode => boolArg('watch');
-
-  bool get failEarly => boolArg('fail-early');
-
-  /// Whether to start the browser in debug mode.
-  ///
-  /// In this mode the browser pauses before running the test to allow
-  /// you set breakpoints or inspect the code.
-  bool get isDebug => boolArg('debug');
-
-  /// Paths to targets to run, e.g. a single test.
-  List<String> get targets => argResults!.rest;
-
-  /// The target test files to run.
-  List<FilePath> get targetFiles => targets.map((String t) => FilePath.fromCwd(t)).toList();
-
-  /// Whether all tests should run.
-  bool get runAllTests => targets.isEmpty;
-
-  /// The name of the browser to run tests in.
-  String get browserName => stringArg('browser');
-
-  /// When running screenshot tests writes them to the file system into
-  /// ".dart_tool/goldens".
-  bool get doUpdateScreenshotGoldens => boolArg('update-screenshot-goldens');
-
-  /// Whether to fetch the goldens repo prior to running tests.
-  bool get skipGoldensRepoFetch => boolArg('skip-goldens-repo-fetch');
-
-  /// Path to a CanvasKit build. Overrides the default CanvasKit.
-  String? get overridePathToCanvasKit => argResults!['canvaskit-path'] as String?;
-
-  @override
-  Future<bool> run() async {
-    final List<FilePath> testFiles = runAllTests
-          ? findAllTests()
-          : targetFiles;
-
-    final Pipeline testPipeline = Pipeline(steps: <PipelineStep>[
-      if (isWatchMode) ClearTerminalScreenStep(),
-      CompileTestsStep(
-        skipGoldensRepoFetch: skipGoldensRepoFetch,
-        testFiles: testFiles,
-      ),
-      RunTestsStep(
-        browserName: browserName,
-        testFiles: testFiles,
-        isDebug: isDebug,
-        doUpdateScreenshotGoldens: doUpdateScreenshotGoldens,
-        overridePathToCanvasKit: overridePathToCanvasKit,
-      ),
-    ]);
-    await testPipeline.run();
-
-    if (isWatchMode) {
-      final FilePath dir = FilePath.fromWebUi('');
-      print('');
-      print('Initial test run is done!');
-      print(
-          'Watching ${dir.relativeToCwd}/lib and ${dir.relativeToCwd}/test to re-run tests');
-      print('');
-      await PipelineWatcher(
-          dir: dir.absolute,
-          pipeline: testPipeline,
-          ignore: (WatchEvent event) {
-            // Ignore font files that are copied whenever tests run.
-            if (event.path.endsWith('.ttf')) {
-              return true;
-            }
-
-            // React to changes in lib/ and test/ folders.
-            final String relativePath =
-                path.relative(event.path, from: dir.absolute);
-            if (path.isWithin('lib', relativePath) ||
-                path.isWithin('test', relativePath)) {
-              return false;
-            }
-
-            // Ignore anything else.
-            return true;
-          }).start();
-    }
-    return true;
-  }
-}
-
-/// Clears the terminal screen and places the cursor at the top left corner.
-///
-/// This works on Linux and Mac. On Windows, it's a no-op.
-class ClearTerminalScreenStep implements PipelineStep {
-  @override
-  String get description => 'clearing terminal screen';
-
-  @override
-  bool get isSafeToInterrupt => false;
-
-  @override
-  Future<void> interrupt() async {}
-
-  @override
-  Future<void> run() async {
-    if (!io.Platform.isWindows) {
-      // See: https://en.wikipedia.org/wiki/ANSI_escape_code#CSI_sequences
-      print('\x1B[2J\x1B[1;2H');
-    }
-  }
-}
diff --git a/engine/dev/utils.dart b/engine/dev/utils.dart
deleted file mode 100644
index 1dad1a7..0000000
--- a/engine/dev/utils.dart
+++ /dev/null
@@ -1,381 +0,0 @@
-// Copyright 2013 The Flutter 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' show utf8, LineSplitter;
-import 'dart:io' as io;
-
-import 'package:args/command_runner.dart';
-import 'package:meta/meta.dart';
-import 'package:path/path.dart' as path;
-
-import 'environment.dart';
-import 'exceptions.dart';
-
-class FilePath {
-  FilePath.fromCwd(String relativePath)
-      : _absolutePath = path.absolute(relativePath);
-  FilePath.fromWebUi(String relativePath)
-      : _absolutePath = path.join(environment.webUiRootDir.path, relativePath);
-
-  final String _absolutePath;
-
-  String get absolute => _absolutePath;
-  String get relativeToCwd => path.relative(_absolutePath);
-  String get relativeToWebUi =>
-      path.relative(_absolutePath, from: environment.webUiRootDir.path);
-
-  @override
-  bool operator ==(Object other) {
-    return other is FilePath && other._absolutePath == _absolutePath;
-  }
-
-  @override
-  int get hashCode => _absolutePath.hashCode;
-
-  @override
-  String toString() => _absolutePath;
-}
-
-/// Runs [executable] merging its output into the current process' standard out and standard error.
-Future<int> runProcess(
-  String executable,
-  List<String> arguments, {
-  String? workingDirectory,
-  bool failureIsSuccess = false,
-  Map<String, String> environment = const <String, String>{},
-}) async {
-  final ProcessManager manager = await startProcess(
-    executable,
-    arguments,
-    workingDirectory: workingDirectory,
-    failureIsSuccess: failureIsSuccess,
-    environment: environment,
-  );
-  return manager.wait();
-}
-
-/// Runs the process and returns its standard output as a string.
-///
-/// Standard error output is ignored (use [ProcessManager.evalStderr] for that).
-///
-/// Throws an exception if the process exited with a non-zero exit code.
-Future<String> evalProcess(
-  String executable,
-  List<String> arguments, {
-  String? workingDirectory,
-  Map<String, String> environment = const <String, String>{},
-}) async {
-  final ProcessManager manager = await startProcess(
-    executable,
-    arguments,
-    workingDirectory: workingDirectory,
-    environment: environment,
-    evalOutput: true,
-  );
-  return manager.evalStdout();
-}
-
-/// Starts a process using the [executable], passing it [arguments].
-///
-/// Returns a process manager that decorates the process with extra
-/// functionality. See [ProcessManager] for what it can do.
-///
-/// If [workingDirectory] is not null makes it the current working directory of
-/// the process. Otherwise, the process inherits this processes working
-/// directory.
-///
-/// If [failureIsSuccess] is set to true, the returned [ProcessManager] treats
-/// non-zero exit codes as success, and zero exit code as failure.
-///
-/// If [evalOutput] is set to true, collects and decodes the process' standard
-/// streams into in-memory strings.
-Future<ProcessManager> startProcess(
-  String executable,
-  List<String> arguments, {
-  String? workingDirectory,
-  bool failureIsSuccess = false,
-  bool evalOutput = false,
-  Map<String, String> environment = const <String, String>{},
-}) async {
-  final io.Process process = await io.Process.start(
-    executable,
-    arguments,
-    workingDirectory: workingDirectory,
-    // Running the process in a system shell for Windows. Otherwise
-    // the process is not able to get Dart from path.
-    runInShell: io.Platform.isWindows,
-    // When [evalOutput] is false, we don't need to intercept the stdout of the
-    // sub-process. In this case, it's better to run the sub-process in the
-    // `inheritStdio` mode which lets it print directly to the terminal.
-    // This allows sub-processes such as `ninja` to use all kinds of terminal
-    // features like printing colors, printing progress on the same line, etc.
-    mode: evalOutput ? io.ProcessStartMode.normal : io.ProcessStartMode.inheritStdio,
-    environment: environment,
-  );
-  processesToCleanUp.add(process);
-
-  return ProcessManager._(
-    executable: executable,
-    arguments: arguments,
-    workingDirectory: workingDirectory,
-    process: process,
-    evalOutput: evalOutput,
-    failureIsSuccess: failureIsSuccess,
-  );
-}
-
-/// Manages a process running outside `felt`.
-class ProcessManager {
-  /// Creates a process manager that manages [process].
-  ProcessManager._({
-    required this.executable,
-    required this.arguments,
-    required this.workingDirectory,
-    required this.process,
-    required bool evalOutput,
-    required bool failureIsSuccess,
-  }) : _evalOutput = evalOutput, _failureIsSuccess = failureIsSuccess {
-    if (_evalOutput) {
-      _forwardStream(process.stdout, _stdout);
-      _forwardStream(process.stderr, _stderr);
-    }
-  }
-
-  /// The executable, from which the process was spawned.
-  final String executable;
-
-  /// The arguments passed to the prcess.
-  final List<String> arguments;
-
-  /// The current working directory (CWD) of the child process.
-  ///
-  /// If null, the child process inherits `felt`'s CWD.
-  final String? workingDirectory;
-
-  /// The process being managed by this manager.
-  final io.Process process;
-
-  /// Whether the standard output and standard error should be decoded into
-  /// strings while running the process.
-  final bool _evalOutput;
-
-  /// Whether non-zero exit code is considered successful completion of the
-  /// process.
-  ///
-  /// See also [wait].
-  final bool _failureIsSuccess;
-
-  final StringBuffer _stdout = StringBuffer();
-  final StringBuffer _stderr = StringBuffer();
-
-  void _forwardStream(Stream<List<int>> stream, StringSink buffer) {
-    stream
-      .transform(utf8.decoder)
-      .transform(const LineSplitter())
-      .listen(buffer.writeln);
-  }
-
-  /// Waits for the [process] to exit. Returns the exit code.
-  ///
-  /// The returned future completes successfully if:
-  ///
-  ///  * [failureIsSuccess] is false and the process exited with exit code 0.
-  ///  * [failureIsSuccess] is true and the process exited with a non-zero exit code.
-  ///
-  /// In all other cicumstances the future completes with an error.
-  Future<int> wait() async {
-    final int exitCode = await process.exitCode;
-    if (!_failureIsSuccess && exitCode != 0) {
-      _throwProcessException(
-        description: 'Sub-process failed.',
-        exitCode: exitCode,
-      );
-    }
-    return exitCode;
-  }
-
-  /// If [evalOutput] is true, wait for the process to finish then returns the
-  /// decoded standard streams.
-  Future<ProcessOutput> eval() async {
-    if (!_evalOutput) {
-      kill();
-      _throwProcessException(
-        description: 'Cannot eval process output. The process was launched '
-            'with `evalOutput` set to false.',
-      );
-    }
-    final int exitCode = await wait();
-    return ProcessOutput(
-      exitCode: exitCode,
-      stdout: _stdout.toString(),
-      stderr: _stderr.toString(),
-    );
-  }
-
-  /// A convenience method on top of [eval] that only extracts standard output.
-  Future<String> evalStdout() async {
-    return (await eval()).stdout;
-  }
-
-  /// A convenience method on top of [eval] that only extracts standard error.
-  Future<String> evalStderr() async {
-    return (await eval()).stderr;
-  }
-
-  @alwaysThrows
-  void _throwProcessException({required String description, int? exitCode}) {
-    throw ProcessException(
-      description: description,
-      executable: executable,
-      arguments: arguments,
-      workingDirectory: workingDirectory,
-      exitCode: exitCode,
-    );
-  }
-
-  /// Kills the [process] by sending it the [signal].
-  bool kill([io.ProcessSignal signal = io.ProcessSignal.sigterm]) {
-    return process.kill(signal);
-  }
-}
-
-/// Stringified standard output and standard error streams from a process.
-class ProcessOutput {
-  ProcessOutput({
-    required this.exitCode,
-    required this.stdout,
-    required this.stderr,
-  });
-
-  /// The exit code of the process.
-  final int exitCode;
-
-  /// Standard output of the process decoded as a string.
-  final String stdout;
-
-  /// Standard error of the process decoded as a string.
-  final String stderr;
-}
-
-Future<void> runFlutter(
-  String workingDirectory,
-  List<String> arguments, {
-  bool useSystemFlutter = false,
-}) async {
-  final String executable =
-      useSystemFlutter ? 'flutter' : environment.flutterCommand.path;
-  arguments.add('--local-engine=host_debug_unopt');
-  final int exitCode = await runProcess(
-    executable,
-    arguments,
-    workingDirectory: workingDirectory,
-  );
-
-  if (exitCode != 0) {
-    throw ToolExit(
-      'ERROR: Failed to run $executable with '
-      'arguments ${arguments.toString()}. Exited with exit code $exitCode',
-      exitCode: exitCode,
-    );
-  }
-}
-
-/// An exception related to an attempt to spawn a sub-process.
-@immutable
-class ProcessException implements Exception {
-  const ProcessException({
-    required this.description,
-    required this.executable,
-    required this.arguments,
-    required this.workingDirectory,
-    this.exitCode,
-  });
-
-  final String description;
-  final String executable;
-  final List<String> arguments;
-  final String? workingDirectory;
-
-  /// The exit code of the process.
-  ///
-  /// The value is null if the exception is thrown before the process exits.
-  /// For example, this can happen on invalid attempts to start a process, or
-  /// when a process is stuck and is unable to exit.
-  final int? exitCode;
-
-  @override
-  String toString() {
-    final StringBuffer message = StringBuffer();
-    message
-      ..writeln(description)
-      ..writeln('Command: $executable ${arguments.join(' ')}')
-      ..writeln('Working directory: ${workingDirectory ?? io.Directory.current.path}');
-    if (exitCode != null) {
-      message.writeln('Exit code: $exitCode');
-    }
-    return '$message';
-  }
-}
-
-/// Adds utility methods
-mixin ArgUtils<T> on Command<T> {
-  /// Extracts a boolean argument from [argResults].
-  bool boolArg(String name) => argResults![name] as bool;
-
-  /// Extracts a string argument from [argResults].
-  String stringArg(String name) => argResults![name] as String;
-}
-
-/// There might be proccesses started during the tests.
-///
-/// Use this list to store those Processes, for cleaning up before shutdown.
-final List<io.Process> processesToCleanUp = <io.Process>[];
-
-/// There might be temporary directories created during the tests.
-///
-/// Use this list to store those directories and for deleteing them before
-/// shutdown.
-final List<io.Directory> temporaryDirectories = <io.Directory>[];
-
-typedef AsyncCallback = Future<void> Function();
-
-/// There might be additional cleanup needs to be done after the tools ran.
-///
-/// Add these operations here to make sure that they will run before felt
-/// exit.
-final List<AsyncCallback> cleanupCallbacks = <AsyncCallback>[];
-
-/// Cleanup the remaning processes, close open browsers, delete temp files.
-Future<void> cleanup() async {
-  // Cleanup remaining processes if any.
-  if (processesToCleanUp.isNotEmpty) {
-    for (final io.Process process in processesToCleanUp) {
-      process.kill();
-    }
-  }
-  // Delete temporary directories.
-  if (temporaryDirectories.isNotEmpty) {
-    for (final io.Directory directory in temporaryDirectories) {
-      if (!directory.existsSync()) {
-        directory.deleteSync(recursive: true);
-      }
-    }
-  }
-
-  for (final AsyncCallback callback in cleanupCallbacks) {
-    await callback();
-  }
-}
-
-/// Scans the test/ directory for test files and returns them.
-List<FilePath> findAllTests() {
-  return environment.webUiTestDir
-      .listSync(recursive: true)
-      .whereType<io.File>()
-      .where((io.File f) => f.path.endsWith('_test.dart'))
-      .map<FilePath>((io.File f) => FilePath.fromWebUi(
-          path.relative(f.path, from: environment.webUiRootDir.path)))
-      .toList();
-}
diff --git a/engine/dev/web_engine_analysis.sh b/engine/dev/web_engine_analysis.sh
deleted file mode 100755
index fd1224d..0000000
--- a/engine/dev/web_engine_analysis.sh
+++ /dev/null
@@ -1,19 +0,0 @@
-#!/bin/bash
-set -e
-set -x
-
-# web_analysis: a command-line utility for running 'dart analyze' on Flutter Web
-# Engine. Used/Called by LUCI recipes:
-#
-# See: https://flutter.googlesource.com/recipes/+/refs/heads/main/recipes/web_engine.py
-
-echo "Engine path $ENGINE_PATH"
-WEB_UI_DIR="$ENGINE_PATH/src/flutter/lib/web_ui"
-DART_SDK_DIR="${ENGINE_PATH}/src/out/host_debug_unopt/dart-sdk"
-DART_PATH="$DART_SDK_DIR/bin/dart"
-
-echo "Running \`dart pub get\` in 'engine/src/flutter/lib/web_ui'"
-(cd "$WEB_UI_DIR"; $DART_PATH pub get)
-
-echo "Running \`dart analyze\` in 'engine/src/flutter/lib/web_ui'"
-(cd "$WEB_UI_DIR"; $DART_PATH analyze --fatal-infos)
diff --git a/engine/lib/src/engine/text/font_collection.dart b/engine/lib/src/engine/text/font_collection.dart
index 39e5214..ac3fb7a 100644
--- a/engine/lib/src/engine/text/font_collection.dart
+++ b/engine/lib/src/engine/text/font_collection.dart
@@ -309,7 +309,7 @@
     fontLoadStyle.innerHtml = '@font-face { $fontFaceDeclaration }';
     html.document.head!.append(fontLoadStyle);
 
-    // HACK: If this is an icon font, then when it loads it won't change the
+    // If this is an icon font, then when it loads it won't change the
     // width of our test string. So we just have to hope it loads before the
     // layout phase.
     if (family.toLowerCase().contains('icon')) {
diff --git a/engine/pubspec.yaml b/engine/pubspec.yaml
index d399ce7..cd7170c 100644
--- a/engine/pubspec.yaml
+++ b/engine/pubspec.yaml
@@ -19,12 +19,3 @@
   test: 1.17.7
   yaml: 3.0.0
   watcher: 1.0.0
-  web_test_utils:
-    path: ../../web_sdk/web_test_utils
-  web_engine_tester:
-    path: ../../web_sdk/web_engine_tester
-  simulators:
-    git:
-      url: https://github.com/flutter/web_installers.git
-      path: packages/simulators/
-      ref: 4a7b0a2c84b8993bf4d19030218de38c18838c26
diff --git a/engine/test/canvaskit/backdrop_filter_golden_test.dart b/engine/test/canvaskit/backdrop_filter_golden_test.dart
index 54acaa9..e6357c3 100644
--- a/engine/test/canvaskit/backdrop_filter_golden_test.dart
+++ b/engine/test/canvaskit/backdrop_filter_golden_test.dart
@@ -7,7 +7,7 @@
 import 'package:ui/src/engine.dart';
 import 'package:ui/ui.dart' as ui;
 
-import 'package:web_engine_tester/golden_tester.dart';
+import '../golden_tester.dart';
 
 import 'common.dart';
 
diff --git a/engine/test/canvaskit/canvas_golden_test.dart b/engine/test/canvaskit/canvas_golden_test.dart
index 4688346..ea5297a 100644
--- a/engine/test/canvaskit/canvas_golden_test.dart
+++ b/engine/test/canvaskit/canvas_golden_test.dart
@@ -11,7 +11,7 @@
 import 'package:ui/src/engine.dart';
 import 'package:ui/ui.dart' as ui;
 
-import 'package:web_engine_tester/golden_tester.dart';
+import '../golden_tester.dart';
 
 import 'common.dart';
 
diff --git a/engine/test/canvaskit/color_filter_golden_test.dart b/engine/test/canvaskit/color_filter_golden_test.dart
index 22c8694..0d39068 100644
--- a/engine/test/canvaskit/color_filter_golden_test.dart
+++ b/engine/test/canvaskit/color_filter_golden_test.dart
@@ -7,7 +7,7 @@
 import 'package:ui/src/engine.dart';
 import 'package:ui/ui.dart' as ui;
 
-import 'package:web_engine_tester/golden_tester.dart';
+import '../golden_tester.dart';
 
 import 'common.dart';
 
diff --git a/engine/test/canvaskit/common.dart b/engine/test/canvaskit/common.dart
index f062719..ab8dba7 100644
--- a/engine/test/canvaskit/common.dart
+++ b/engine/test/canvaskit/common.dart
@@ -8,7 +8,7 @@
 
 import 'package:ui/src/engine.dart';
 import 'package:ui/ui.dart' as ui;
-import 'package:web_engine_tester/golden_tester.dart';
+import '../golden_tester.dart';
 
 export '../common.dart';
 
diff --git a/engine/test/canvaskit/image_golden_test.dart b/engine/test/canvaskit/image_golden_test.dart
index 479e826..f36196a 100644
--- a/engine/test/canvaskit/image_golden_test.dart
+++ b/engine/test/canvaskit/image_golden_test.dart
@@ -10,7 +10,7 @@
 import 'package:test/test.dart';
 import 'package:ui/src/engine.dart';
 import 'package:ui/ui.dart' as ui;
-import 'package:web_engine_tester/golden_tester.dart';
+import '../golden_tester.dart';
 
 import '../matchers.dart';
 import 'common.dart';
diff --git a/engine/test/canvaskit/linear_gradient_golden_test.dart b/engine/test/canvaskit/linear_gradient_golden_test.dart
index 352f8de..cfd0e78 100644
--- a/engine/test/canvaskit/linear_gradient_golden_test.dart
+++ b/engine/test/canvaskit/linear_gradient_golden_test.dart
@@ -9,7 +9,7 @@
 import 'package:ui/src/engine.dart';
 import 'package:ui/ui.dart' as ui;
 
-import 'package:web_engine_tester/golden_tester.dart';
+import '../golden_tester.dart';
 
 import 'common.dart';
 
diff --git a/engine/test/canvaskit/shader_mask_golden_test.dart b/engine/test/canvaskit/shader_mask_golden_test.dart
index 9d282ab..282fc79 100644
--- a/engine/test/canvaskit/shader_mask_golden_test.dart
+++ b/engine/test/canvaskit/shader_mask_golden_test.dart
@@ -8,7 +8,7 @@
 import 'package:ui/src/engine.dart';
 import 'package:ui/ui.dart' as ui;
 
-import 'package:web_engine_tester/golden_tester.dart';
+import '../golden_tester.dart';
 
 import 'common.dart';
 
diff --git a/engine/test/canvaskit/sweep_gradient_golden_test.dart b/engine/test/canvaskit/sweep_gradient_golden_test.dart
index 78e667d..7a0c4ba 100644
--- a/engine/test/canvaskit/sweep_gradient_golden_test.dart
+++ b/engine/test/canvaskit/sweep_gradient_golden_test.dart
@@ -9,7 +9,7 @@
 import 'package:ui/src/engine.dart';
 import 'package:ui/ui.dart' as ui;
 
-import 'package:web_engine_tester/golden_tester.dart';
+import '../golden_tester.dart';
 
 import 'common.dart';
 
diff --git a/engine/test/golden_tester.dart b/engine/test/golden_tester.dart
new file mode 100644
index 0000000..c273b60
--- /dev/null
+++ b/engine/test/golden_tester.dart
@@ -0,0 +1,23 @@
+// Copyright 2013 The Flutter 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:ui/ui.dart';
+
+/// How to compare pixels within the image.
+///
+/// Keep this enum in sync with the one defined in `goldens.dart`.
+enum PixelComparison {
+  /// Allows minor blur and anti-aliasing differences by comparing a 3x3 grid
+  /// surrounding the pixel rather than direct 1:1 comparison.
+  fuzzy,
+
+  /// Compares one pixel at a time.
+  ///
+  /// Anti-aliasing or blur will result in higher diff rate.
+  precise,
+}
+
+Future<void> matchGoldenFile(String filename,
+    {bool write = false, Rect? region, double? maxDiffRatePercent, PixelComparison pixelComparison = PixelComparison.fuzzy}) async {
+}
diff --git a/engine/test/golden_tests/golden_failure_smoke_test.dart b/engine/test/golden_tests/golden_failure_smoke_test.dart
index 85297e5..b1cdaca 100644
--- a/engine/test/golden_tests/golden_failure_smoke_test.dart
+++ b/engine/test/golden_tests/golden_failure_smoke_test.dart
@@ -7,7 +7,7 @@
 import 'package:test/bootstrap/browser.dart';
 import 'package:test/test.dart';
 import 'package:ui/ui.dart';
-import 'package:web_engine_tester/golden_tester.dart';
+import '../golden_tester.dart';
 
 void main() {
   internalBootstrapBrowserTest(() => testMain);
diff --git a/engine/test/golden_tests/golden_success_smoke_test.dart b/engine/test/golden_tests/golden_success_smoke_test.dart
index b6ce8db..b7de18f 100644
--- a/engine/test/golden_tests/golden_success_smoke_test.dart
+++ b/engine/test/golden_tests/golden_success_smoke_test.dart
@@ -8,7 +8,7 @@
 import 'package:test/test.dart';
 import 'package:ui/src/engine.dart';
 import 'package:ui/ui.dart';
-import 'package:web_engine_tester/golden_tester.dart';
+import '../golden_tester.dart';
 
 void main() {
   internalBootstrapBrowserTest(() => testMain);
diff --git a/engine/test/html/bitmap_canvas_golden_test.dart b/engine/test/html/bitmap_canvas_golden_test.dart
index c78e491..4b86067 100644
--- a/engine/test/html/bitmap_canvas_golden_test.dart
+++ b/engine/test/html/bitmap_canvas_golden_test.dart
@@ -10,7 +10,7 @@
 import 'package:ui/src/engine.dart';
 import 'package:ui/ui.dart';
 
-import 'package:web_engine_tester/golden_tester.dart';
+import '../golden_tester.dart';
 import 'screenshot.dart';
 
 void main() {
diff --git a/engine/test/html/canvas_context_golden_test.dart b/engine/test/html/canvas_context_golden_test.dart
index 975cc38..6ff3d6f 100644
--- a/engine/test/html/canvas_context_golden_test.dart
+++ b/engine/test/html/canvas_context_golden_test.dart
@@ -9,7 +9,7 @@
 import 'package:ui/src/engine.dart' as engine;
 import 'package:ui/ui.dart' hide TextStyle;
 
-import 'package:web_engine_tester/golden_tester.dart';
+import '../golden_tester.dart';
 
 void main() {
   internalBootstrapBrowserTest(() => testMain);
diff --git a/engine/test/html/canvas_reuse_golden_test.dart b/engine/test/html/canvas_reuse_golden_test.dart
index 5fb44af..4821ebc 100644
--- a/engine/test/html/canvas_reuse_golden_test.dart
+++ b/engine/test/html/canvas_reuse_golden_test.dart
@@ -9,7 +9,7 @@
 import 'package:ui/src/engine.dart';
 import 'package:ui/ui.dart' hide TextStyle;
 
-import 'package:web_engine_tester/golden_tester.dart';
+import '../golden_tester.dart';
 
 void main() {
   internalBootstrapBrowserTest(() => testMain);
diff --git a/engine/test/html/canvas_winding_rule_golden_test.dart b/engine/test/html/canvas_winding_rule_golden_test.dart
index 7dd6ec7..b80f431 100644
--- a/engine/test/html/canvas_winding_rule_golden_test.dart
+++ b/engine/test/html/canvas_winding_rule_golden_test.dart
@@ -9,7 +9,7 @@
 import 'package:ui/src/engine.dart';
 import 'package:ui/ui.dart';
 
-import 'package:web_engine_tester/golden_tester.dart';
+import '../golden_tester.dart';
 
 void main() {
   internalBootstrapBrowserTest(() => testMain);
diff --git a/engine/test/html/compositing/backdrop_filter_golden_test.dart b/engine/test/html/compositing/backdrop_filter_golden_test.dart
index e26dfcc..797e900 100644
--- a/engine/test/html/compositing/backdrop_filter_golden_test.dart
+++ b/engine/test/html/compositing/backdrop_filter_golden_test.dart
@@ -9,7 +9,7 @@
 import 'package:ui/src/engine.dart' hide ClipRectEngineLayer, BackdropFilterEngineLayer;
 import 'package:ui/ui.dart';
 
-import 'package:web_engine_tester/golden_tester.dart';
+import '../../golden_tester.dart';
 
 void main() {
   internalBootstrapBrowserTest(() => testMain);
diff --git a/engine/test/html/compositing/color_filter_golden_test.dart b/engine/test/html/compositing/color_filter_golden_test.dart
index f07e0ab..873fef8 100644
--- a/engine/test/html/compositing/color_filter_golden_test.dart
+++ b/engine/test/html/compositing/color_filter_golden_test.dart
@@ -10,7 +10,7 @@
 import 'package:ui/src/engine.dart' hide PhysicalShapeEngineLayer;
 import 'package:ui/ui.dart';
 
-import 'package:web_engine_tester/golden_tester.dart';
+import '../../golden_tester.dart';
 
 const Rect region = Rect.fromLTWH(0, 0, 500, 500);
 
diff --git a/engine/test/html/compositing/compositing_golden_test.dart b/engine/test/html/compositing/compositing_golden_test.dart
index 4cfb4f4..2a5ae44 100644
--- a/engine/test/html/compositing/compositing_golden_test.dart
+++ b/engine/test/html/compositing/compositing_golden_test.dart
@@ -9,7 +9,7 @@
 import 'package:test/test.dart';
 import 'package:ui/src/engine.dart';
 import 'package:ui/ui.dart' as ui;
-import 'package:web_engine_tester/golden_tester.dart';
+import '../../golden_tester.dart';
 
 import '../../matchers.dart';
 
diff --git a/engine/test/html/drawing/canvas_arc_golden_test.dart b/engine/test/html/drawing/canvas_arc_golden_test.dart
index 5454e1a..aa76a12 100644
--- a/engine/test/html/drawing/canvas_arc_golden_test.dart
+++ b/engine/test/html/drawing/canvas_arc_golden_test.dart
@@ -9,7 +9,7 @@
 import 'package:ui/src/engine.dart';
 import 'package:ui/ui.dart';
 
-import 'package:web_engine_tester/golden_tester.dart';
+import '../../golden_tester.dart';
 
 void main() {
   internalBootstrapBrowserTest(() => testMain);
diff --git a/engine/test/html/drawing/canvas_draw_image_golden_test.dart b/engine/test/html/drawing/canvas_draw_image_golden_test.dart
index a4c9461..efacbb9 100644
--- a/engine/test/html/drawing/canvas_draw_image_golden_test.dart
+++ b/engine/test/html/drawing/canvas_draw_image_golden_test.dart
@@ -11,7 +11,7 @@
 import 'package:ui/src/engine.dart';
 import 'package:ui/ui.dart';
 
-import 'package:web_engine_tester/golden_tester.dart';
+import '../../golden_tester.dart';
 
 import '../screenshot.dart';
 
diff --git a/engine/test/html/drawing/canvas_draw_points_golden_test.dart b/engine/test/html/drawing/canvas_draw_points_golden_test.dart
index f871e38..88946e4 100644
--- a/engine/test/html/drawing/canvas_draw_points_golden_test.dart
+++ b/engine/test/html/drawing/canvas_draw_points_golden_test.dart
@@ -10,7 +10,7 @@
 import 'package:ui/src/engine.dart';
 import 'package:ui/ui.dart';
 
-import 'package:web_engine_tester/golden_tester.dart';
+import '../../golden_tester.dart';
 
 void main() {
   internalBootstrapBrowserTest(() => testMain);
diff --git a/engine/test/html/drawing/canvas_lines_golden_test.dart b/engine/test/html/drawing/canvas_lines_golden_test.dart
index 349b83c..c647ea8 100644
--- a/engine/test/html/drawing/canvas_lines_golden_test.dart
+++ b/engine/test/html/drawing/canvas_lines_golden_test.dart
@@ -9,7 +9,7 @@
 import 'package:ui/src/engine.dart';
 import 'package:ui/ui.dart';
 
-import 'package:web_engine_tester/golden_tester.dart';
+import '../../golden_tester.dart';
 
 void main() {
   internalBootstrapBrowserTest(() => testMain);
diff --git a/engine/test/html/drawing/canvas_rect_golden_test.dart b/engine/test/html/drawing/canvas_rect_golden_test.dart
index fb81bfd..b6b09a9 100644
--- a/engine/test/html/drawing/canvas_rect_golden_test.dart
+++ b/engine/test/html/drawing/canvas_rect_golden_test.dart
@@ -9,7 +9,7 @@
 import 'package:ui/src/engine.dart';
 import 'package:ui/ui.dart';
 
-import 'package:web_engine_tester/golden_tester.dart';
+import '../../golden_tester.dart';
 
 void main() {
   internalBootstrapBrowserTest(() => testMain);
diff --git a/engine/test/html/drawing/canvas_rrect_golden_test.dart b/engine/test/html/drawing/canvas_rrect_golden_test.dart
index f7d6d43..aef19ac 100644
--- a/engine/test/html/drawing/canvas_rrect_golden_test.dart
+++ b/engine/test/html/drawing/canvas_rrect_golden_test.dart
@@ -9,7 +9,7 @@
 import 'package:ui/src/engine.dart';
 import 'package:ui/ui.dart';
 
-import 'package:web_engine_tester/golden_tester.dart';
+import '../../golden_tester.dart';
 
 void main() {
   internalBootstrapBrowserTest(() => testMain);
diff --git a/engine/test/html/drawing/canvas_stroke_joins_golden_test.dart b/engine/test/html/drawing/canvas_stroke_joins_golden_test.dart
index 6eff9c3..6e38565 100644
--- a/engine/test/html/drawing/canvas_stroke_joins_golden_test.dart
+++ b/engine/test/html/drawing/canvas_stroke_joins_golden_test.dart
@@ -9,7 +9,7 @@
 import 'package:ui/src/engine.dart';
 import 'package:ui/ui.dart';
 
-import 'package:web_engine_tester/golden_tester.dart';
+import '../../golden_tester.dart';
 
 void main() {
   internalBootstrapBrowserTest(() => testMain);
diff --git a/engine/test/html/drawing/canvas_stroke_rects_golden_test.dart b/engine/test/html/drawing/canvas_stroke_rects_golden_test.dart
index 9339bca..55036fc 100644
--- a/engine/test/html/drawing/canvas_stroke_rects_golden_test.dart
+++ b/engine/test/html/drawing/canvas_stroke_rects_golden_test.dart
@@ -10,7 +10,7 @@
 import 'package:ui/src/engine.dart';
 import 'package:ui/ui.dart';
 
-import 'package:web_engine_tester/golden_tester.dart';
+import '../../golden_tester.dart';
 
 void main() {
   internalBootstrapBrowserTest(() => testMain);
diff --git a/engine/test/html/drawing/conic_golden_test.dart b/engine/test/html/drawing/conic_golden_test.dart
index ce028b6..63159b9 100644
--- a/engine/test/html/drawing/conic_golden_test.dart
+++ b/engine/test/html/drawing/conic_golden_test.dart
@@ -9,7 +9,7 @@
 import 'package:ui/src/engine.dart';
 import 'package:ui/ui.dart';
 
-import 'package:web_engine_tester/golden_tester.dart';
+import '../../golden_tester.dart';
 
 void main() {
   internalBootstrapBrowserTest(() => testMain);
diff --git a/engine/test/html/drawing/draw_vertices_golden_test.dart b/engine/test/html/drawing/draw_vertices_golden_test.dart
index 0d3c64f..00307e8 100644
--- a/engine/test/html/drawing/draw_vertices_golden_test.dart
+++ b/engine/test/html/drawing/draw_vertices_golden_test.dart
@@ -12,9 +12,8 @@
 import 'package:ui/src/engine.dart';
 import 'package:ui/ui.dart' hide TextStyle, ImageShader;
 
-import 'package:web_engine_tester/golden_tester.dart';
-
 import '../../common.dart';
+import '../../golden_tester.dart';
 
 void main() {
   internalBootstrapBrowserTest(() => testMain);
diff --git a/engine/test/html/paragraph/helper.dart b/engine/test/html/paragraph/helper.dart
index 7c913d3..de7a388 100644
--- a/engine/test/html/paragraph/helper.dart
+++ b/engine/test/html/paragraph/helper.dart
@@ -6,7 +6,7 @@
 
 import 'package:ui/src/engine.dart';
 import 'package:ui/ui.dart';
-import 'package:web_engine_tester/golden_tester.dart';
+import '../../golden_tester.dart';
 
 const Color white = Color(0xFFFFFFFF);
 const Color black = Color(0xFF000000);
diff --git a/engine/test/html/paragraph/text_scuba.dart b/engine/test/html/paragraph/text_scuba.dart
index e637182..1ab9a17 100644
--- a/engine/test/html/paragraph/text_scuba.dart
+++ b/engine/test/html/paragraph/text_scuba.dart
@@ -9,7 +9,7 @@
 import 'package:ui/src/engine.dart';
 import 'package:ui/ui.dart' as ui;
 
-import 'package:web_engine_tester/golden_tester.dart';
+import '../../golden_tester.dart';
 
 /// Class that controls some details of how screenshotting is made.
 ///
diff --git a/engine/test/html/path_metrics_golden_test.dart b/engine/test/html/path_metrics_golden_test.dart
index ac2e21b..6f7511d 100644
--- a/engine/test/html/path_metrics_golden_test.dart
+++ b/engine/test/html/path_metrics_golden_test.dart
@@ -8,7 +8,7 @@
 import 'package:test/test.dart';
 import 'package:ui/src/engine.dart';
 import 'package:ui/ui.dart' hide TextStyle;
-import 'package:web_engine_tester/golden_tester.dart';
+import '../golden_tester.dart';
 
 import '../matchers.dart';
 
diff --git a/engine/test/html/path_to_svg_golden_test.dart b/engine/test/html/path_to_svg_golden_test.dart
index f24c736..3ab7295 100644
--- a/engine/test/html/path_to_svg_golden_test.dart
+++ b/engine/test/html/path_to_svg_golden_test.dart
@@ -10,7 +10,7 @@
 import 'package:ui/src/engine.dart';
 import 'package:ui/ui.dart';
 
-import 'package:web_engine_tester/golden_tester.dart';
+import '../golden_tester.dart';
 
 void main() {
   internalBootstrapBrowserTest(() => testMain);
diff --git a/engine/test/html/path_transform_golden_test.dart b/engine/test/html/path_transform_golden_test.dart
index 12e4d3b..db15ef8 100644
--- a/engine/test/html/path_transform_golden_test.dart
+++ b/engine/test/html/path_transform_golden_test.dart
@@ -10,7 +10,7 @@
 import 'package:ui/src/engine.dart';
 import 'package:ui/ui.dart' hide TextStyle;
 
-import 'package:web_engine_tester/golden_tester.dart';
+import '../golden_tester.dart';
 
 void main() {
   internalBootstrapBrowserTest(() => testMain);
diff --git a/engine/test/html/picture_golden_test.dart b/engine/test/html/picture_golden_test.dart
index 1ab71d7..7a3c017 100644
--- a/engine/test/html/picture_golden_test.dart
+++ b/engine/test/html/picture_golden_test.dart
@@ -9,7 +9,7 @@
 import 'package:ui/src/engine.dart';
 import 'package:ui/ui.dart' as ui;
 
-import 'package:web_engine_tester/golden_tester.dart';
+import '../golden_tester.dart';
 
 void main() {
   internalBootstrapBrowserTest(() => testMain);
diff --git a/engine/test/html/recording_canvas_golden_test.dart b/engine/test/html/recording_canvas_golden_test.dart
index 649c7cd..7176fd5 100644
--- a/engine/test/html/recording_canvas_golden_test.dart
+++ b/engine/test/html/recording_canvas_golden_test.dart
@@ -10,7 +10,7 @@
 import 'package:test/test.dart';
 import 'package:ui/src/engine.dart';
 import 'package:ui/ui.dart' hide TextStyle;
-import 'package:web_engine_tester/golden_tester.dart';
+import '../golden_tester.dart';
 
 import '../matchers.dart';
 
diff --git a/engine/test/html/screenshot.dart b/engine/test/html/screenshot.dart
index 825fac6..8702cce 100644
--- a/engine/test/html/screenshot.dart
+++ b/engine/test/html/screenshot.dart
@@ -7,7 +7,7 @@
 import 'package:test/test.dart';
 import 'package:ui/src/engine.dart';
 import 'package:ui/ui.dart' as ui;
-import 'package:web_engine_tester/golden_tester.dart';
+import '../golden_tester.dart';
 
 /// Commit a recording canvas to a bitmap, and compare with the expected.
 Future<void> canvasScreenshot(RecordingCanvas rc, String fileName,
diff --git a/engine/test/html/shaders/gradient_golden_test.dart b/engine/test/html/shaders/gradient_golden_test.dart
index 72a2c35..56e46ff 100644
--- a/engine/test/html/shaders/gradient_golden_test.dart
+++ b/engine/test/html/shaders/gradient_golden_test.dart
@@ -11,9 +11,9 @@
 import 'package:ui/src/engine.dart';
 import 'package:ui/ui.dart';
 
-import 'package:web_engine_tester/golden_tester.dart';
-
 import '../../common.dart';
+import '../../golden_tester.dart';
+
 import '../paragraph/text_scuba.dart';
 
 // TODO(yjbanov): unskip Firefox tests when Firefox implements WebGL in headless mode.
diff --git a/engine/test/html/shaders/shader_mask_golden_test.dart b/engine/test/html/shaders/shader_mask_golden_test.dart
index 394d447..e2637ff 100644
--- a/engine/test/html/shaders/shader_mask_golden_test.dart
+++ b/engine/test/html/shaders/shader_mask_golden_test.dart
@@ -10,9 +10,8 @@
     hide ClipRectEngineLayer, BackdropFilterEngineLayer;
 import 'package:ui/ui.dart';
 
-import 'package:web_engine_tester/golden_tester.dart';
-
 import '../../common.dart';
+import '../../golden_tester.dart';
 
 /// To debug compositing failures on browsers, set this flag to true and run
 /// flutter run -d chrome --web-renderer=html
diff --git a/engine/test/html/shadow_golden_test.dart b/engine/test/html/shadow_golden_test.dart
index b1b4c75..204e572 100644
--- a/engine/test/html/shadow_golden_test.dart
+++ b/engine/test/html/shadow_golden_test.dart
@@ -9,7 +9,7 @@
 import 'package:ui/src/engine.dart';
 import 'package:ui/ui.dart';
 
-import 'package:web_engine_tester/golden_tester.dart';
+import '../golden_tester.dart';
 
 import 'paragraph/text_scuba.dart';
 
diff --git a/engine/tool/unicode_sync_script.dart b/engine/tool/unicode_sync_script.dart
deleted file mode 100644
index 6b87fb0..0000000
--- a/engine/tool/unicode_sync_script.dart
+++ /dev/null
@@ -1,496 +0,0 @@
-// Copyright 2013 The Flutter 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:io';
-
-import 'package:args/args.dart';
-import 'package:path/path.dart' as path;
-
-const int _kChar_A = 65;
-const int _kChar_a = 97;
-
-final ArgParser argParser = ArgParser()
-  ..addOption(
-    'words',
-    abbr: 'w',
-    help: 'Sync the word break properties.',
-  )
-  ..addOption(
-    'lines',
-    abbr: 'l',
-    help: 'Sync the line break properties.',
-  )
-  ..addFlag(
-    'dry',
-    abbr: 'd',
-    help: 'Dry mode does not write anything to disk. '
-        'The output is printed to the console.',
-  );
-
-/// A map of properties that could safely be normalized into other properties.
-///
-/// For example, a NL behaves exactly the same as BK so it gets normalized to BK
-/// in the generated code.
-const Map<String, String> normalizationTable = <String, String>{
-  // NL behaves exactly the same as BK.
-  // See: https://www.unicode.org/reports/tr14/tr14-45.html#NL
-  'NL': 'BK',
-  // In the absence of extra data (ICU data and language dictionaries), the
-  // following properties will be treated as AL (alphabetic): AI, SA, SG and XX.
-  // See LB1: https://www.unicode.org/reports/tr14/tr14-45.html#LB1
-  'AI': 'AL',
-  'SA': 'AL',
-  'SG': 'AL',
-  'XX': 'AL',
-  // https://unicode.org/reports/tr14/tr14-45.html#CJ
-  'CJ': 'NS',
-};
-
-/// A tuple that holds a [start] and [end] of a unicode range and a [property].
-class UnicodeRange {
-  const UnicodeRange(this.start, this.end, this.property);
-
-  final int start;
-  final int end;
-  final EnumValue property;
-
-  /// Checks if there's an overlap between this range and the [other] range.
-  bool isOverlapping(UnicodeRange other) {
-    return start <= other.end && end >= other.start;
-  }
-
-  /// Checks if the [other] range is adjacent to this range.
-  ///
-  /// Two ranges are considered adjacent if:
-  /// - The new range immediately follows this range, and
-  /// - The new range has the same property as this range.
-  bool isAdjacent(UnicodeRange other) {
-    return other.start == end + 1 && property == other.property;
-  }
-
-  /// Merges the ranges of the 2 [UnicodeRange]s if they are adjacent.
-  UnicodeRange extendRange(UnicodeRange extension) {
-    assert(isAdjacent(extension));
-    return UnicodeRange(start, extension.end, property);
-  }
-}
-
-final String codegenPath = path.join(
-  path.dirname(Platform.script.toFilePath()),
-  '../lib/src/engine/text',
-);
-final String wordBreakCodegen =
-    path.join(codegenPath, 'word_break_properties.dart');
-final String lineBreakCodegen =
-    path.join(codegenPath, 'line_break_properties.dart');
-
-/// Usage (from the root of the web_ui project).
-///
-/// To generate code for word break properties:
-/// ```
-/// dart tool/unicode_sync_script.dart -w <path/to/word/break/properties>
-/// ```
-///
-/// To generate code for line break properties:
-/// ```
-/// dart tool/unicode_sync_script.dart -l <path/to/line/break/properties>
-/// ```
-///
-/// To do a dry run, add the `-d` flag:
-///
-/// ```
-/// dart tool/unicode_sync_script.dart -d ...
-/// ```
-///
-/// This script parses the unicode word/line break properties(1) and generates Dart
-/// code(2) that can perform lookups in the unicode ranges to find what property
-/// a letter has.
-///
-/// (1) The word break properties file can be downloaded from:
-///     https://www.unicode.org/Public/13.0.0/ucd/auxiliary/WordBreakProperty.txt
-///
-///     The line break properties file can be downloaded from:
-///     https://www.unicode.org/Public/13.0.0/ucd/LineBreak.txt
-///
-/// (2) The codegen'd Dart files is located at:
-///     lib/src/engine/text/word_break_properties.dart
-///     lib/src/engine/text/line_break_properties.dart
-Future<void> main(List<String> arguments) async {
-  final ArgResults result = argParser.parse(arguments);
-  final PropertiesSyncer syncer = getSyncer(
-    result['words'] as String?,
-    result['lines'] as String?,
-    result['dry'] as bool,
-  );
-
-  syncer.perform();
-}
-
-PropertiesSyncer getSyncer(
-  String? wordBreakProperties,
-  String? lineBreakProperties,
-  bool dry,
-) {
-  if (wordBreakProperties == null && lineBreakProperties == null) {
-    print(
-        'Expecting either a word break properties file or a line break properties file. None was given.\n');
-    print(argParser.usage);
-    exit(64);
-  }
-  if (wordBreakProperties != null && lineBreakProperties != null) {
-    print(
-        'Expecting either a word break properties file or a line break properties file. Both were given.\n');
-    print(argParser.usage);
-    exit(64);
-  }
-  if (wordBreakProperties != null) {
-    return dry
-        ? WordBreakPropertiesSyncer.dry(wordBreakProperties)
-        : WordBreakPropertiesSyncer(wordBreakProperties, wordBreakCodegen);
-  } else {
-    return dry
-        ? LineBreakPropertiesSyncer.dry(lineBreakProperties!)
-        : LineBreakPropertiesSyncer(lineBreakProperties!, lineBreakCodegen);
-  }
-}
-
-/// Base class that provides common logic for syncing all kinds of unicode
-/// properties (e.g. word break properties, line break properties, etc).
-///
-/// Subclasses implement the [template] method which receives as argument the
-/// list of data parsed by [processLines].
-abstract class PropertiesSyncer {
-  PropertiesSyncer(this._src, this._dest) : _dryRun = false;
-  PropertiesSyncer.dry(this._src)
-      : _dest = null,
-        _dryRun = true;
-
-  final String _src;
-  final String? _dest;
-  final bool _dryRun;
-
-  String get prefix;
-  String get enumDocLink;
-
-  /// The default property to be used when a certain code point doesn't belong
-  /// to any known range.
-  String get defaultProperty;
-
-  Future<void> perform() async {
-    final List<String> lines = await File(_src).readAsLines();
-    final List<String> header = extractHeader(lines);
-    final PropertyCollection data =
-        PropertyCollection.fromLines(lines, defaultProperty);
-
-    final String output = template(header, data);
-
-    if (_dryRun) {
-      print(output);
-    } else {
-      final IOSink sink = File(_dest!).openWrite();
-      sink.write(output);
-    }
-  }
-
-  String template(List<String> header, PropertyCollection data) {
-    return '''
-// Copyright 2013 The Flutter Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// AUTO-GENERATED FILE.
-// Generated by: tool/unicode_sync_script.dart
-//
-// Source:
-// ${header.join('\n// ')}
-
-import 'unicode_range.dart';
-
-/// For an explanation of these enum values, see:
-///
-/// * $enumDocLink
-enum ${prefix}CharProperty {
-  ${_getEnumValues(data.enumCollection).join('\n  ')}
-}
-
-const String _packed${prefix}BreakProperties =
-  '${_packProperties(data)}';
-
-
-UnicodePropertyLookup<${prefix}CharProperty> ${prefix.toLowerCase()}Lookup =
-    UnicodePropertyLookup<${prefix}CharProperty>.fromPackedData(
-  _packed${prefix}BreakProperties,
-  ${_getSingleRangesCount(data)},
-  ${prefix}CharProperty.values,
-  ${prefix}CharProperty.$defaultProperty,
-);
-''';
-  }
-
-  Iterable<String> _getEnumValues(EnumCollection enumCollection) {
-    return enumCollection.values.expand(
-      (EnumValue value) => <String>[
-        if (value.normalizedFrom.isNotEmpty)
-          '// Normalized from: ${value.normalizedFrom.join(', ')}',
-        '${value.enumName}, // serialized as "${value.serialized}"',
-      ],
-    );
-  }
-
-  int _getSingleRangesCount(PropertyCollection data) {
-    int count = 0;
-    for (final UnicodeRange range in data.ranges) {
-      if (range.start == range.end) {
-        count++;
-      }
-    }
-    return count;
-  }
-
-  String _packProperties(PropertyCollection data) {
-    final StringBuffer buffer = StringBuffer();
-    for (final UnicodeRange range in data.ranges) {
-      buffer.write(range.start.toRadixString(36).padLeft(4, '0'));
-      if (range.start == range.end) {
-        buffer.write('!');
-      } else {
-        buffer.write(range.end.toRadixString(36).padLeft(4, '0'));
-      }
-      buffer.write(range.property.serialized);
-    }
-    return buffer.toString();
-  }
-}
-
-/// Syncs Unicode's word break properties.
-class WordBreakPropertiesSyncer extends PropertiesSyncer {
-  WordBreakPropertiesSyncer(String src, String dest) : super(src, dest);
-  WordBreakPropertiesSyncer.dry(String src) : super.dry(src);
-
-  @override
-  final String prefix = 'Word';
-
-  @override
-  final String enumDocLink =
-      'http://unicode.org/reports/tr29/#Table_Word_Break_Property_Values';
-
-  @override
-  final String defaultProperty = 'Unknown';
-}
-
-/// Syncs Unicode's line break properties.
-class LineBreakPropertiesSyncer extends PropertiesSyncer {
-  LineBreakPropertiesSyncer(String src, String dest) : super(src, dest);
-  LineBreakPropertiesSyncer.dry(String src) : super.dry(src);
-
-  @override
-  final String prefix = 'Line';
-
-  @override
-  final String enumDocLink =
-      'https://www.unicode.org/reports/tr14/tr14-45.html#DescriptionOfProperties';
-
-  @override
-  final String defaultProperty = 'AL';
-}
-
-/// Holds the collection of properties parsed from the unicode spec file.
-class PropertyCollection {
-  PropertyCollection.fromLines(List<String> lines, String defaultProperty) {
-    final List<UnicodeRange> unprocessedRanges = lines
-        .map(removeCommentFromLine)
-        .where((String line) => line.isNotEmpty)
-        .map(parseLineIntoUnicodeRange)
-        .toList();
-    // Insert the default property if it doesn't exist.
-    final EnumValue? found = enumCollection.values.cast<EnumValue?>().firstWhere(
-      (EnumValue? property) => property!.name == defaultProperty,
-      orElse: () => null,
-    );
-    if (found == null) {
-      enumCollection.add(defaultProperty);
-    }
-    ranges = processRanges(unprocessedRanges, defaultProperty).toList();
-  }
-
-  late List<UnicodeRange> ranges;
-
-  final EnumCollection enumCollection = EnumCollection();
-
-  /// Examples:
-  ///
-  /// 00C0..00D6    ; ALetter
-  /// 037F          ; ALetter
-  ///
-  /// Would be parsed into:
-  ///
-  /// ```dart
-  /// UnicodeRange(192, 214, EnumValue('ALetter'));
-  /// UnicodeRange(895, 895, EnumValue('ALetter'));
-  /// ```
-  UnicodeRange parseLineIntoUnicodeRange(String line) {
-    final List<String> split = line.split(';');
-    final String rangeStr = split[0].trim();
-    final String propertyStr = split[1].trim();
-
-    final EnumValue property = normalizationTable.containsKey(propertyStr)
-        ? enumCollection.add(normalizationTable[propertyStr]!, propertyStr)
-        : enumCollection.add(propertyStr);
-
-    return UnicodeRange(
-      getRangeStart(rangeStr),
-      getRangeEnd(rangeStr),
-      property,
-    );
-  }
-}
-
-/// Represents the collection of values of an enum.
-class EnumCollection {
-  final List<EnumValue> values = <EnumValue>[];
-
-  EnumValue add(String name, [String? normalizedFrom]) {
-    final int index =
-        values.indexWhere((EnumValue value) => value.name == name);
-    EnumValue value;
-    if (index == -1) {
-      value = EnumValue(values.length, name);
-      values.add(value);
-    } else {
-      value = values[index];
-    }
-
-    if (normalizedFrom != null) {
-      value.normalizedFrom.add(normalizedFrom);
-    }
-    return value;
-  }
-}
-
-/// Represents a single value in an [EnumCollection].
-class EnumValue {
-  EnumValue(this.index, this.name);
-
-  final int index;
-  final String name;
-
-  /// The properties that were normalized to this value.
-  final Set<String> normalizedFrom = <String>{};
-
-  /// Returns a serialized, compact format of the enum value.
-  ///
-  /// Enum values are serialized based on their index. We start serializing them
-  /// to "A", "B", "C", etc until we reach "Z". Then we continue with "a", "b",
-  /// "c", etc.
-  String get serialized {
-    // We assign uppercase letters to the first 26 enum values.
-    if (index < 26) {
-      return String.fromCharCode(_kChar_A + index);
-    }
-    // Enum values above 26 will be assigned a lowercase letter.
-    return String.fromCharCode(_kChar_a + index - 26);
-  }
-
-  /// Returns the enum name that'll be used in the Dart code.
-  ///
-  /// ```dart
-  /// enum CharProperty {
-  ///   ALetter, // <-- this is the name returned by this method ("ALetter").
-  ///   Numeric,
-  ///   // etc...
-  /// }
-  /// ```
-  String get enumName {
-    return name.replaceAll('_', '');
-  }
-}
-
-/// Sorts ranges and combines adjacent ranges that have the same property and
-/// can be merged.
-Iterable<UnicodeRange> processRanges(
-  List<UnicodeRange> data,
-  String defaultProperty,
-) {
-  data.sort(
-    // Ranges don't overlap so it's safe to sort based on the start of each
-    // range.
-    (UnicodeRange range1, UnicodeRange range2) =>
-        range1.start.compareTo(range2.start),
-  );
-  verifyNoOverlappingRanges(data);
-  return combineAdjacentRanges(data, defaultProperty);
-}
-
-/// Example:
-///
-/// ```
-/// 0x01C4..0x0293; ALetter
-/// 0x0294..0x0294; ALetter
-/// 0x0295..0x02AF; ALetter
-/// ```
-///
-/// will get combined into:
-///
-/// ```
-/// 0x01C4..0x02AF; ALetter
-/// ```
-List<UnicodeRange> combineAdjacentRanges(
-  List<UnicodeRange> data,
-  String defaultProperty,
-) {
-  final List<UnicodeRange> result = <UnicodeRange>[data.first];
-  for (int i = 1; i < data.length; i++) {
-    final UnicodeRange prev = result.last;
-    final UnicodeRange next = data[i];
-    if (prev.isAdjacent(next)) {
-      result.last = prev.extendRange(next);
-    } else if (prev.property == next.property &&
-        prev.property.name == defaultProperty) {
-      // When there's a gap between two ranges, but they both have the default
-      // property, it's safe to combine them.
-      result.last = prev.extendRange(next);
-    } else {
-      // Check if there's a gap between the previous range and this range.
-      result.add(next);
-    }
-  }
-  return result;
-}
-
-int getRangeStart(String range) {
-  return int.parse(range.split('..')[0], radix: 16);
-}
-
-int getRangeEnd(String range) {
-  if (range.contains('..')) {
-    return int.parse(range.split('..')[1], radix: 16);
-  }
-  return int.parse(range, radix: 16);
-}
-
-void verifyNoOverlappingRanges(List<UnicodeRange> data) {
-  for (int i = 1; i < data.length; i++) {
-    if (data[i].isOverlapping(data[i - 1])) {
-      throw Exception('Data contains overlapping ranges.');
-    }
-  }
-}
-
-List<String> extractHeader(List<String> lines) {
-  final List<String> headerLines = <String>[];
-  for (final String line in lines) {
-    if (line.trim() == '#' || line.trim().isEmpty) {
-      break;
-    }
-    if (line.isNotEmpty) {
-      headerLines.add(line);
-    }
-  }
-  return headerLines;
-}
-
-String removeCommentFromLine(String line) {
-  final int poundIdx = line.indexOf('#');
-  return (poundIdx == -1) ? line : line.substring(0, poundIdx);
-}
diff --git a/framework/analysis_options.yaml b/framework/analysis_options.yaml
new file mode 100644
index 0000000..ef60322
--- /dev/null
+++ b/framework/analysis_options.yaml
@@ -0,0 +1,241 @@
+# Specify analysis options.
+#
+# Until there are meta linter rules, each desired lint must be explicitly enabled.
+# See: https://github.com/dart-lang/linter/issues/288
+#
+# For a list of lints, see: http://dart-lang.github.io/linter/lints/
+# See the configuration guide for more
+# https://github.com/dart-lang/sdk/tree/main/pkg/analyzer#configuring-the-analyzer
+#
+# There are other similar analysis options files in the flutter repos,
+# which should be kept in sync with this file:
+#
+#   - analysis_options.yaml (this file)
+#   - https://github.com/flutter/plugins/blob/master/analysis_options.yaml
+#   - https://github.com/flutter/engine/blob/master/analysis_options.yaml
+#   - https://github.com/flutter/packages/blob/master/analysis_options.yaml
+#
+# This file contains the analysis options used by Flutter tools, such as IntelliJ,
+# Android Studio, and the `flutter analyze` command.
+
+analyzer:
+  strong-mode:
+    implicit-casts: false
+    implicit-dynamic: false
+  errors:
+    # treat missing required parameters as a warning (not a hint)
+    missing_required_param: warning
+    # treat missing returns as a warning (not a hint)
+    missing_return: warning
+    # allow having TODO comments in the code
+    todo: ignore
+    # allow self-reference to deprecated members (we do this because otherwise we have
+    # to annotate every member in every test, assert, etc, when we deprecate something)
+    deprecated_member_use_from_same_package: ignore
+    # TODO(ianh): https://github.com/flutter/flutter/issues/74381
+    # Clean up existing unnecessary imports, and remove line to ignore.
+    unnecessary_import: ignore
+    # Turned off until null-safe rollout is complete.
+    unnecessary_null_comparison: ignore
+  exclude:
+    - "bin/cache/**"
+      # Ignore protoc generated files
+    - "dev/conductor/lib/proto/*"
+
+linter:
+  rules:
+    # these rules are documented on and in the same order as
+    # the Dart Lint rules page to make maintenance easier
+    # https://github.com/dart-lang/linter/blob/master/example/all.yaml
+    - always_declare_return_types
+    - always_put_control_body_on_new_line
+    # - always_put_required_named_parameters_first # we prefer having parameters in the same order as fields https://github.com/flutter/flutter/issues/10219
+    - always_require_non_null_named_parameters
+    - always_specify_types
+    # - always_use_package_imports # we do this commonly
+    - annotate_overrides
+    # - avoid_annotating_with_dynamic # conflicts with always_specify_types
+    - avoid_bool_literals_in_conditional_expressions
+    # - avoid_catches_without_on_clauses # blocked on https://github.com/dart-lang/linter/issues/3023
+    # - avoid_catching_errors # blocked on https://github.com/dart-lang/linter/issues/3023
+    - avoid_classes_with_only_static_members
+    - avoid_double_and_int_checks
+    # - avoid_dynamic_calls
+    - avoid_empty_else
+    # - avoid_equals_and_hash_code_on_mutable_classes
+    # - avoid_escaping_inner_quotes
+    - avoid_field_initializers_in_const_classes
+    - avoid_function_literals_in_foreach_calls
+    - avoid_implementing_value_types
+    - avoid_init_to_null
+    - avoid_js_rounded_ints
+    # - avoid_multiple_declarations_per_line # seems to be a stylistic choice we don't subscribe to
+    - avoid_null_checks_in_equality_operators
+    # - avoid_positional_boolean_parameters # would have been nice to enable this but by now there's too many places that break it
+    # - avoid_print
+    # - avoid_private_typedef_functions # we prefer having typedef (discussion in https://github.com/flutter/flutter/pull/16356)
+    # - avoid_redundant_argument_values
+    - avoid_relative_lib_imports
+    - avoid_renaming_method_parameters
+    - avoid_return_types_on_setters
+    # - avoid_returning_null # still violated by some pre-nnbd code that we haven't yet migrated
+    - avoid_returning_null_for_future
+    - avoid_returning_null_for_void
+    # - avoid_returning_this # there are enough valid reasons to return `this` that this lint ends up with too many false positives
+    # - avoid_setters_without_getters
+    - avoid_shadowing_type_parameters
+    - avoid_single_cascade_in_expression_statements
+    - avoid_slow_async_io
+    - avoid_type_to_string
+    - avoid_types_as_parameter_names
+    # - avoid_types_on_closure_parameters # conflicts with always_specify_types
+    - avoid_unnecessary_containers
+    - avoid_unused_constructor_parameters
+    - avoid_void_async
+    # - avoid_web_libraries_in_flutter # we use web libraries in web-specific code, and our tests prevent us from using them elsewhere
+    - await_only_futures
+    - camel_case_extensions
+    - camel_case_types
+    - cancel_subscriptions
+    # - cascade_invocations # doesn't match the typical style of this repo
+    - cast_nullable_to_non_nullable
+    # - close_sinks # not reliable enough
+    # - comment_references # blocked on https://github.com/dart-lang/linter/issues/1142
+    # - constant_identifier_names # needs an opt-out https://github.com/dart-lang/linter/issues/204
+    - control_flow_in_finally
+    # - curly_braces_in_flow_control_structures # not required by flutter style
+    - depend_on_referenced_packages
+    # - deprecated_consistency
+    # - diagnostic_describe_all_properties # enabled only at the framework level (packages/flutter/lib)
+    # - directives_ordering
+    # - do_not_use_environment # there are appropriate times to use the environment, especially in our tests and build logic
+    - empty_catches
+    - empty_constructor_bodies
+    - empty_statements
+    - eol_at_end_of_file
+    - exhaustive_cases
+    - file_names
+    - flutter_style_todos
+    - hash_and_equals
+    - implementation_imports
+    # - invariant_booleans # too many false positives: https://github.com/dart-lang/linter/issues/811
+    - iterable_contains_unrelated_type
+    # - join_return_with_assignment # not required by flutter style
+    - leading_newlines_in_multiline_strings
+    - library_names
+    - library_prefixes
+    # - library_private_types_in_public_api
+    # - lines_longer_than_80_chars # not required by flutter style
+    - list_remove_unrelated_type
+    # - literal_only_boolean_expressions # too many false positives: https://github.com/dart-lang/linter/issues/453
+    - missing_whitespace_between_adjacent_strings
+    - no_adjacent_strings_in_list
+    # - no_default_cases
+    - no_duplicate_case_values
+    - no_logic_in_create_state
+    # - no_runtimeType_toString # ok in tests; we enable this only in packages/
+    - non_constant_identifier_names
+    # - noop_primitive_operations
+    - null_check_on_nullable_type_parameter
+    - null_closures
+    # - omit_local_variable_types # opposite of always_specify_types
+    # - one_member_abstracts # too many false positives
+    - only_throw_errors # this does get disabled in a few places where we have legacy code that uses strings et al
+    - overridden_fields
+    - package_api_docs
+    - package_names
+    - package_prefixed_library_names
+    # - parameter_assignments # we do this commonly
+    - prefer_adjacent_string_concatenation
+    - prefer_asserts_in_initializer_lists
+    # - prefer_asserts_with_message # not required by flutter style
+    - prefer_collection_literals
+    - prefer_conditional_assignment
+    # - prefer_const_constructors
+    # - prefer_const_constructors_in_immutables
+    - prefer_const_declarations
+    - prefer_const_literals_to_create_immutables
+    # - prefer_constructors_over_static_methods # far too many false positives
+    - prefer_contains
+    # - prefer_double_quotes # opposite of prefer_single_quotes
+    - prefer_equal_for_default_values
+    # - prefer_expression_function_bodies # conflicts with https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo#consider-using--for-short-functions-and-methods
+    # - prefer_final_fields
+    # - prefer_final_in_for_each
+    - prefer_final_locals
+    # - prefer_final_parameters # we should enable this one day (see also parameter_assignments)
+    - prefer_for_elements_to_map_fromIterable
+    - prefer_foreach
+    # - prefer_function_declarations_over_variables
+    - prefer_generic_function_type_aliases
+    - prefer_if_elements_to_conditional_expressions
+    - prefer_if_null_operators
+    - prefer_initializing_formals
+    - prefer_inlined_adds
+    # - prefer_int_literals # conflicts with https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo#use-double-literals-for-double-constants
+    # - prefer_interpolation_to_compose_strings
+    - prefer_is_empty
+    - prefer_is_not_empty
+    - prefer_is_not_operator
+    - prefer_iterable_whereType
+    # - prefer_mixin # Has false positives, see https://github.com/dart-lang/linter/issues/3018
+    - prefer_null_aware_operators
+    # - prefer_null_aware_method_calls # "call()" is confusing to people new to the language since it's not documented anywhere
+    # - prefer_relative_imports
+    - prefer_single_quotes
+    - prefer_spread_collections
+    - prefer_typing_uninitialized_variables
+    - prefer_void_to_null
+    - provide_deprecation_message
+    # - public_member_api_docs # enabled on a case-by-case basis; see e.g. packages/analysis_options.yaml
+    - recursive_getters
+    # - require_trailing_commas # blocked on https://github.com/dart-lang/sdk/issues/47441
+    - sized_box_for_whitespace
+    - slash_for_doc_comments
+    - sort_child_properties_last
+    # - sort_constructors_first
+    # - sort_pub_dependencies # prevents separating pinned transitive dependencies
+    - sort_unnamed_constructors_first
+    - test_types_in_equals
+    - throw_in_finally
+    - tighten_type_of_initializing_formals
+    # - type_annotate_public_apis # subset of always_specify_types
+    - type_init_formals
+    # - unawaited_futures # too many false positives, especially with the way AnimationController works
+    - unnecessary_await_in_return
+    - unnecessary_brace_in_string_interps
+    - unnecessary_const
+    - unnecessary_constructor_name
+    # - unnecessary_final # conflicts with prefer_final_locals
+    - unnecessary_getters_setters
+    # - unnecessary_lambdas # has false positives: https://github.com/dart-lang/linter/issues/498
+    - unnecessary_new
+    - unnecessary_null_aware_assignments
+    - unnecessary_null_checks
+    - unnecessary_null_in_if_null_operators
+    - unnecessary_nullable_for_final_variable_declarations
+    - unnecessary_overrides
+    - unnecessary_parenthesis
+    # - unnecessary_raw_strings # what's "necessary" is a matter of opinion; consistency across strings can help readability more than this lint
+    - unnecessary_statements
+    - unnecessary_string_escapes
+    - unnecessary_string_interpolations
+    - unnecessary_this
+    - unrelated_type_equality_checks
+    - unsafe_html
+    - use_build_context_synchronously
+    - use_full_hex_values_for_flutter_colors
+    # - use_function_type_syntax_for_parameters
+    # - use_if_null_to_convert_nulls_to_bools # blocked on https://github.com/dart-lang/sdk/issues/47436
+    - use_is_even_rather_than_modulo
+    - use_key_in_widget_constructors
+    - use_late_for_private_fields_and_variables
+    # - use_named_constants
+    - use_raw_strings
+    - use_rethrow_when_possible
+    - use_setters_to_change_properties
+    # - use_string_buffers # has false positives: https://github.com/dart-lang/sdk/issues/34182
+    - use_test_throws_matchers
+    # - use_to_and_as_if_applicable # has false positives, so we prefer to catch this by code-review
+    - valid_regexps
+    - void_checks
diff --git a/framework/examples/complex.dart b/framework/examples/complex.dart
index 93fa0c2..dd9a85a 100644
--- a/framework/examples/complex.dart
+++ b/framework/examples/complex.dart
@@ -172,36 +172,36 @@
 abstract class _Node {}
 
 class _LayoutNode extends _Node {
-  factory _LayoutNode.generate({int depth = 0}) {
-    final _LayoutNode node = _LayoutNode(
-      isColumn: depth % 2 == 0,
-      firstChild: depth >= maxDepth ? _LeafNode.generate() : _LayoutNode.generate(depth: depth + 1),
-      secondChild: depth >= maxDepth ? _LeafNode.generate() : _LayoutNode.generate(depth: depth + 1),
-    );
-    return node;
-  }
-
   _LayoutNode({
     required this.isColumn,
     required this.firstChild,
     required this.secondChild,
   });
 
+  factory _LayoutNode.generate({int depth = 0}) {
+    final _LayoutNode node = _LayoutNode(
+      isColumn: depth.isEven,
+      firstChild: depth >= maxDepth ? _LeafNode.generate() : _LayoutNode.generate(depth: depth + 1),
+      secondChild: depth >= maxDepth ? _LeafNode.generate() : _LayoutNode.generate(depth: depth + 1),
+    );
+    return node;
+  }
+
   final bool isColumn;
   final _Node firstChild;
   final _Node secondChild;
 }
 
 class _LeafNode extends _Node {
+  _LeafNode({
+    required this.kind,
+  });
+
   factory _LeafNode.generate() {
     return _LeafNode(
       kind: _WidgetKind.values[random.nextInt(_WidgetKind.values.length)],
     );
   }
 
-  _LeafNode({
-    required this.kind,
-  });
-
   final _WidgetKind kind;
 }
diff --git a/framework/examples/counter.dart b/framework/examples/counter.dart
index d60ef12..c27cb8f 100644
--- a/framework/examples/counter.dart
+++ b/framework/examples/counter.dart
@@ -1,6 +1,4 @@
-import 'dart:async';
 import 'package:flute/material.dart';
-import 'package:flute/scheduler.dart';
 
 void main() {
   runApp(MyApp());
diff --git a/framework/lib/io.dart b/framework/lib/io.dart
index 501213f..fbf7793 100644
--- a/framework/lib/io.dart
+++ b/framework/lib/io.dart
@@ -7,6 +7,7 @@
   throw Exception('Exiting with exit code $exitCode');
 }
 
+// ignore: avoid_classes_with_only_static_members
 class Platform {
   static String get operatingSystem => 'Android';
   static bool get isAndroid => true;
diff --git a/framework/lib/src/cupertino/form_section.dart b/framework/lib/src/cupertino/form_section.dart
index d25fb00..a5eff6c 100644
--- a/framework/lib/src/cupertino/form_section.dart
+++ b/framework/lib/src/cupertino/form_section.dart
@@ -289,7 +289,7 @@
                 ),
                 child: Padding(
                   padding: _kDefaultHeaderMargin,
-                  child: header!,
+                  child: header,
                 ),
               ),
             ),
@@ -312,7 +312,7 @@
                 ),
                 child: Padding(
                   padding: _kDefaultFooterMargin,
-                  child: footer!,
+                  child: footer,
                 ),
               ),
             ),
diff --git a/framework/lib/src/foundation/_bitfield_io.dart b/framework/lib/src/foundation/_bitfield_io.dart
index a4f3bac..bc96bdc 100644
--- a/framework/lib/src/foundation/_bitfield_io.dart
+++ b/framework/lib/src/foundation/_bitfield_io.dart
@@ -5,6 +5,7 @@
 import 'bitfield.dart' as bitfield;
 
 /// The dart:io implementation of [bitfield.kMaxUnsignedSMI].
+// ignore: avoid_js_rounded_ints
 const int kMaxUnsignedSMI = 0x3FFFFFFFFFFFFFFF;
 
 /// The dart:io implementation of [bitfield.Bitfield].
diff --git a/framework/lib/src/foundation/basic_types.dart b/framework/lib/src/foundation/basic_types.dart
index b190362..5fb0882 100644
--- a/framework/lib/src/foundation/basic_types.dart
+++ b/framework/lib/src/foundation/basic_types.dart
@@ -136,8 +136,8 @@
   }
 
   @override
-  Iterable<T> map<T>(T f(E e)) {
-    return CachingIterable<T>(super.map<T>(f).iterator);
+  Iterable<T> map<T>(T toElement(E e)) {
+    return CachingIterable<T>(super.map<T>(toElement).iterator);
   }
 
   @override
@@ -146,8 +146,8 @@
   }
 
   @override
-  Iterable<T> expand<T>(Iterable<T> f(E element)) {
-    return CachingIterable<T>(super.expand<T>(f).iterator);
+  Iterable<T> expand<T>(Iterable<T> toElements(E element)) {
+    return CachingIterable<T>(super.expand<T>(toElements).iterator);
   }
 
   @override
diff --git a/framework/lib/src/gestures/arena.dart b/framework/lib/src/gestures/arena.dart
index 49a1f65..6c1af01 100644
--- a/framework/lib/src/gestures/arena.dart
+++ b/framework/lib/src/gestures/arena.dart
@@ -267,7 +267,7 @@
   bool _debugLogDiagnostic(int pointer, String message, [ _GestureArena? state ]) {
     assert(() {
       if (debugPrintGestureArenaDiagnostics) {
-        final int? count = state != null ? state.members.length : null;
+        final int? count = state?.members.length;
         final String s = count != 1 ? 's' : '';
         debugPrint('Gesture arena ${pointer.toString().padRight(4)} ❙ $message${ count != null ? " with $count member$s." : ""}');
       }
diff --git a/framework/lib/src/gestures/velocity_tracker.dart b/framework/lib/src/gestures/velocity_tracker.dart
index 1b5e4c9..4c16021 100644
--- a/framework/lib/src/gestures/velocity_tracker.dart
+++ b/framework/lib/src/gestures/velocity_tracker.dart
@@ -305,7 +305,7 @@
       if (previousPoint == null || previousPoint.time <= time)
         return true;
       throw FlutterError(
-        'The position being added ($position) has a smaller timestamp ($time)'
+        'The position being added ($position) has a smaller timestamp ($time) '
         'than its predecessor: $previousPoint.'
       );
     }());
diff --git a/framework/lib/src/material/scaffold.dart b/framework/lib/src/material/scaffold.dart
index e56724c..6e87d68 100644
--- a/framework/lib/src/material/scaffold.dart
+++ b/framework/lib/src/material/scaffold.dart
@@ -2274,7 +2274,7 @@
       assert(debugCheckHasScaffoldMessenger(context));
       assert(
         _scaffoldMessenger != null,
-        'A SnackBar was shown by the ScaffoldMessenger, but has been called upon'
+        'A SnackBar was shown by the ScaffoldMessenger, but has been called upon '
           'to be removed from a Scaffold that is not registered with a '
           'ScaffoldMessenger, this can happen if a Scaffold has been rebuilt '
           'without an ancestor ScaffoldMessenger.',
@@ -2319,7 +2319,7 @@
       assert(debugCheckHasScaffoldMessenger(context));
       assert(
       _scaffoldMessenger != null,
-      'A SnackBar was shown by the ScaffoldMessenger, but has been called upon'
+      'A SnackBar was shown by the ScaffoldMessenger, but has been called upon '
         'to be removed from a Scaffold that is not registered with a '
         'ScaffoldMessenger, this can happen if a Scaffold has been rebuilt '
         'without an ancestor ScaffoldMessenger.',
diff --git a/framework/lib/src/material/time_picker.dart b/framework/lib/src/material/time_picker.dart
index cbc3e0f..2b36578 100644
--- a/framework/lib/src/material/time_picker.dart
+++ b/framework/lib/src/material/time_picker.dart
@@ -2215,7 +2215,7 @@
     confirmText: confirmText,
     helpText: helpText,
   );
-  return await showDialog<TimeOfDay>(
+  return showDialog<TimeOfDay>(
     context: context,
     useRootNavigator: useRootNavigator,
     builder: (BuildContext context) {
diff --git a/framework/lib/src/painting/image_provider.dart b/framework/lib/src/painting/image_provider.dart
index 70635ea..c8aaf97 100644
--- a/framework/lib/src/painting/image_provider.dart
+++ b/framework/lib/src/painting/image_provider.dart
@@ -115,8 +115,6 @@
     result.write('ImageConfiguration(');
     bool hasArguments = false;
     if (bundle != null) {
-      if (hasArguments)
-        result.write(', ');
       result.write('bundle: $bundle');
       hasArguments = true;
     }
@@ -675,7 +673,7 @@
       PaintingBinding.instance!.imageCache!.evict(key);
       throw StateError('Unable to read data');
     }
-    return await decode(data.buffer.asUint8List());
+    return decode(data.buffer.asUint8List());
   }
 }
 
diff --git a/framework/lib/src/rendering/object.dart b/framework/lib/src/rendering/object.dart
index b0f89d6..d539185 100644
--- a/framework/lib/src/rendering/object.dart
+++ b/framework/lib/src/rendering/object.dart
@@ -126,7 +126,6 @@
       // layer for repaint boundaries.
       child._layer = childLayer = OffsetLayer();
     } else {
-      assert(childLayer is OffsetLayer);
       assert(debugAlsoPaintedParent || childLayer.attached);
       childLayer.removeAllChildren();
     }
diff --git a/framework/lib/src/rendering/proxy_box.dart b/framework/lib/src/rendering/proxy_box.dart
index b73fff1..386c9fa 100644
--- a/framework/lib/src/rendering/proxy_box.dart
+++ b/framework/lib/src/rendering/proxy_box.dart
@@ -5226,7 +5226,7 @@
     assert(
       link.leaderSize != null || (link.leader == null || leaderAnchor == Alignment.topLeft),
       '$link: layer is linked to ${link.leader} but a valid leaderSize is not set. '
-      'leaderSize is required when leaderAnchor is not Alignment.topLeft'
+      'leaderSize is required when leaderAnchor is not Alignment.topLeft '
       '(current value is $leaderAnchor).',
     );
     final Offset effectiveLinkedOffset = leaderSize == null
diff --git a/framework/lib/src/rendering/sliver.dart b/framework/lib/src/rendering/sliver.dart
index 66864f7..8d3c24d 100644
--- a/framework/lib/src/rendering/sliver.dart
+++ b/framework/lib/src/rendering/sliver.dart
@@ -466,9 +466,8 @@
       return true;
     if (other is! SliverConstraints)
       return false;
-    assert(other is SliverConstraints && other.debugAssertIsValid());
-    return other is SliverConstraints
-        && other.axisDirection == axisDirection
+    assert(other.debugAssertIsValid());
+    return other.axisDirection == axisDirection
         && other.growthDirection == growthDirection
         && other.scrollOffset == scrollOffset
         && other.overlap == overlap
diff --git a/framework/lib/src/services/message_codecs.dart b/framework/lib/src/services/message_codecs.dart
index 3e18a8f..6166f47 100644
--- a/framework/lib/src/services/message_codecs.dart
+++ b/framework/lib/src/services/message_codecs.dart
@@ -358,6 +358,7 @@
                                    // decoding because we use tags to detect the type of value.
       buffer.putUint8(_valueFloat64);
       buffer.putFloat64(value);
+    // ignore: avoid_double_and_int_checks
     } else if (value is int) {
       if (-0x7fffffff - 1 <= value && value <= 0x7fffffff) {
         buffer.putUint8(_valueInt32);
diff --git a/framework/lib/src/ui/compositing.dart b/framework/lib/src/ui/compositing.dart
index b49e111..3346738 100644
--- a/framework/lib/src/ui/compositing.dart
+++ b/framework/lib/src/ui/compositing.dart
@@ -91,7 +91,7 @@
 /// {@endtemplate}
 class TransformEngineLayer extends _EngineLayerWrapper {
   TransformEngineLayer._(Float64List matrix4)
-    : this.matrix4 = Float64List.fromList(matrix4), super._();
+    : matrix4 = Float64List.fromList(matrix4), super._();
 
   final Float64List matrix4;
 }
diff --git a/framework/lib/src/ui/geometry.dart b/framework/lib/src/ui/geometry.dart
index 81f5b7f..f710613 100644
--- a/framework/lib/src/ui/geometry.dart
+++ b/framework/lib/src/ui/geometry.dart
@@ -671,8 +671,6 @@
     math.max(a.dy, b.dy),
   );
 
-  Float32List get _value32 => Float32List.fromList(<double>[left, top, right, bottom]);
-
   /// The offset of the left edge of this rectangle from the x axis.
   final double left;
 
diff --git a/framework/lib/src/ui/hash_codes.dart b/framework/lib/src/ui/hash_codes.dart
index 47891c4..09895c6 100644
--- a/framework/lib/src/ui/hash_codes.dart
+++ b/framework/lib/src/ui/hash_codes.dart
@@ -9,6 +9,7 @@
 class _HashEnd { const _HashEnd(); }
 const _HashEnd _hashEnd = _HashEnd();
 
+// ignore: avoid_classes_with_only_static_members
 /// Jenkins hash function, optimized for small integers.
 //
 // Borrowed from the dart sdk: sdk/lib/math/jenkins_smi_hash.dart.
diff --git a/framework/lib/src/ui/natives.dart b/framework/lib/src/ui/natives.dart
index f291cf9..21442ca 100644
--- a/framework/lib/src/ui/natives.dart
+++ b/framework/lib/src/ui/natives.dart
@@ -17,7 +17,7 @@
   _Logger._printDebugString(arg.toString());
 }
 
-class _Logger {
+class _Logger { // ignore: avoid_classes_with_only_static_members
   static void _printString(String? s) { throw UnimplementedError(); }
   static void _printDebugString(String? s) { throw UnimplementedError(); }
 }
diff --git a/framework/lib/src/ui/painting.dart b/framework/lib/src/ui/painting.dart
index 45d15a8..675f640 100644
--- a/framework/lib/src/ui/painting.dart
+++ b/framework/lib/src/ui/painting.dart
@@ -672,7 +672,7 @@
     if (error != null) {
       throw Exception(error);
     }
-    return await completer.future;
+    return completer.future;
   }
   String? _getNextFrame(void Function(_Image?, int) callback) { throw UnimplementedError(); }
   void dispose() { throw UnimplementedError(); }
@@ -835,7 +835,7 @@
     }
   }
 
-  _updateBoundsFromCurrent() {
+  void _updateBoundsFromCurrent() {
     _updateBounds(_currentX, _currentY);
   }
 
@@ -1188,11 +1188,11 @@
     assert(_offsetIsValid(offset));
     // This is a dummy implementation.
     final Path shifted = Path._();
-    shifted._methods = Uint8List.fromList(this._methods);
+    shifted._methods = Uint8List.fromList(_methods);
     shifted._methodsLength = _methodsLength;
-    shifted._data = Float32List.fromList(this._data);
+    shifted._data = Float32List.fromList(_data);
     shifted._dataLength = _dataLength;
-    shifted._objects = this._objects.toList();
+    shifted._objects = _objects.toList();
     shifted._isEmpty = _isEmpty;
     shifted._left = _left + offset.dx;
     shifted._top = _top + offset.dy;
@@ -1209,11 +1209,11 @@
     final double dx = matrix4[12];
     final double dy = matrix4[13];
     final Path transformed = Path._();
-    transformed._methods = Uint8List.fromList(this._methods);
+    transformed._methods = Uint8List.fromList(_methods);
     transformed._methodsLength = _methodsLength;
-    transformed._data = Float32List.fromList(this._data);
+    transformed._data = Float32List.fromList(_data);
     transformed._dataLength = _dataLength;
-    transformed._objects = this._objects.toList();
+    transformed._objects = _objects.toList();
     transformed._isEmpty = _isEmpty;
     transformed._left = _left + dx;
     transformed._top = _top + dy;
@@ -1233,12 +1233,12 @@
     assert(path2 != null); // ignore: unnecessary_null_comparison
     // This is a dummy implementation
     final Path combined = Path._();
-    combined._methods = Uint8List.fromList([
+    combined._methods = Uint8List.fromList(<int>[
       ...path1._methods,
       ...path2._methods,
     ]);
     combined._methodsLength = path1._methodsLength + path2._methodsLength;
-    combined._data = Float32List.fromList([
+    combined._data = Float32List.fromList(<double>[
       ...path1._data,
       ...path2._data,
     ]);
@@ -1294,7 +1294,7 @@
     if (currentMetric == null) {
       throw RangeError(
         'PathMetricIterator is not pointing to a PathMetric. This can happen in two situations:\n'
-        '- The iteration has not started yet. If so, call "moveNext" to start iteration.'
+        '- The iteration has not started yet. If so, call "moveNext" to start iteration. '
         '- The iterator ran out of elements. If so, check that "moveNext" returns true prior to calling "current".'
       );
     }
@@ -1986,7 +1986,7 @@
   static const int drawShadow = 29;
 }
 class Canvas {
-  Canvas(this._recorder, [ Rect? cullRect ]) : _cullRect = cullRect ?? Rect.largest, assert(_recorder != null) { // ignore: unnecessary_null_comparison
+  Canvas(PictureRecorder this._recorder) : assert(_recorder != null) { // ignore: unnecessary_null_comparison
     if (_recorder!.isRecording)
       throw ArgumentError('"recorder" must not already be associated with another Canvas.');
     _recorder!._canvas = this;
@@ -1996,47 +1996,14 @@
   // The Canvas holds a reference to the PictureRecorder to prevent the recorder from being
   // garbage collected until PictureRecorder.endRecording is called.
   PictureRecorder? _recorder;
-  final Rect _cullRect;
 
-  double _currentX = 0;
-  double _currentY = 0;
   Uint8List _methods = Uint8List(10);
   int _methodsLength = 0;
   Float32List _data = Float32List(30);
   int _dataLength = 0;
   List<Object> _objects = <Object>[];
-  bool _isEmpty = true;
-  double _left = 0;
-  double _top = 0;
-  double _right = 0;
-  double _bottom = 0;
   int _saveCount = 0;
 
-  void _updateBounds(double x, double y) {
-    if (_isEmpty) {
-      _left = _right = x;
-      _top = _bottom = y;
-      _isEmpty = false;
-    } else {
-      if (x < _left) {
-        _left = x;
-      }
-      if (x > _right) {
-        _right = x;
-      }
-      if (y < _top) {
-        _top = y;
-      }
-      if (y > _bottom) {
-        _bottom = y;
-      }
-    }
-  }
-
-  _updateBoundsFromCurrent() {
-    _updateBounds(_currentX, _currentY);
-  }
-
   void _addObject(Object object) {
     _objects.add(object);
   }
@@ -2084,15 +2051,6 @@
     _data[_dataLength++] = d;
   }
 
-  void _addData5(double a, double b, double c, double d, double e) {
-    _ensureDataLength(_dataLength + 5);
-    _data[_dataLength++] = a;
-    _data[_dataLength++] = b;
-    _data[_dataLength++] = c;
-    _data[_dataLength++] = d;
-    _data[_dataLength++] = e;
-  }
-
   void _addData6(double a, double b, double c, double d, double e, double f) {
     _ensureDataLength(_dataLength + 6);
     _data[_dataLength++] = a;
@@ -2599,12 +2557,6 @@
        assert(blurRadius >= 0.0, 'Text shadow blur radius should be non-negative.');
 
   static const int _kColorDefault = 0xFF000000;
-  // Constants for shadow encoding.
-  static const int _kBytesPerShadow = 16;
-  static const int _kColorOffset = 0 << 2;
-  static const int _kXOffset = 1 << 2;
-  static const int _kYOffset = 2 << 2;
-  static const int _kBlurOffset = 3 << 2;
   final Color color;
   final Offset offset;
   final double blurRadius;
@@ -2676,43 +2628,6 @@
   @override
   int get hashCode => hashValues(color, offset, blurRadius);
 
-  // Serialize [shadows] into ByteData. The format is a single uint_32_t at
-  // the beginning indicating the number of shadows, followed by _kBytesPerShadow
-  // bytes for each shadow.
-  static ByteData _encodeShadows(List<Shadow>? shadows) {
-    if (shadows == null)
-      return ByteData(0);
-
-    final int byteCount = shadows.length * _kBytesPerShadow;
-    final ByteData shadowsData = ByteData(byteCount);
-
-    int shadowOffset = 0;
-    for (int shadowIndex = 0; shadowIndex < shadows.length; ++shadowIndex) {
-      final Shadow shadow = shadows[shadowIndex];
-      // TODO(yjbanov): remove the null check when the framework is migrated. While the list
-      //                of shadows contains non-nullable elements, unmigrated code can still
-      //                pass nulls.
-      // ignore: unnecessary_null_comparison
-      if (shadow != null) {
-        shadowOffset = shadowIndex * _kBytesPerShadow;
-
-        shadowsData.setInt32(_kColorOffset + shadowOffset,
-          shadow.color.value ^ Shadow._kColorDefault, _kFakeHostEndian);
-
-        shadowsData.setFloat32(_kXOffset + shadowOffset,
-          shadow.offset.dx, _kFakeHostEndian);
-
-        shadowsData.setFloat32(_kYOffset + shadowOffset,
-          shadow.offset.dy, _kFakeHostEndian);
-
-        shadowsData.setFloat32(_kBlurOffset + shadowOffset,
-          shadow.blurRadius, _kFakeHostEndian);
-      }
-    }
-
-    return shadowsData;
-  }
-
   @override
   String toString() => 'TextShadow($color, $offset, $blurRadius)';
 }
diff --git a/framework/lib/src/ui/platform_dispatcher.dart b/framework/lib/src/ui/platform_dispatcher.dart
index 6e4ab0a..342f80f 100644
--- a/framework/lib/src/ui/platform_dispatcher.dart
+++ b/framework/lib/src/ui/platform_dispatcher.dart
@@ -504,11 +504,11 @@
   ///  * [SchedulerBinding], the Flutter framework class which manages the
   ///    scheduling of frames.
   void scheduleFrame() {
-    _frameTimer ??= new Timer(
+    _frameTimer ??= Timer(
       const Duration(milliseconds: 16),
       () {
         _frameTimer = null;
-        int microseconds = _frameTime.inMicroseconds;
+        final int microseconds = _frameTime.inMicroseconds;
         _frameTime += const Duration(milliseconds: 16);
         Timer.run(() {
           _beginFrame(microseconds);
@@ -1565,4 +1565,4 @@
       out.write('$separator$countryCode');
     return out.toString();
   }
-}
\ No newline at end of file
+}
diff --git a/framework/lib/src/ui/text.dart b/framework/lib/src/ui/text.dart
index 5f9f0df..d2d2a9d 100644
--- a/framework/lib/src/ui/text.dart
+++ b/framework/lib/src/ui/text.dart
@@ -801,16 +801,12 @@
       background: _background ?? other._background,
       foreground: _foreground ?? other._foreground,
       shadows: _shadows == null
-        ? other._shadows == null
-          ? null
-          : other._shadows
+        ? other._shadows
         : other._shadows == null
           ? _shadows
           : <Shadow>[..._shadows!, ...other._shadows!],
       fontFeatures: _fontFeatures == null
-        ? other._fontFeatures == null
-          ? null
-          : other._fontFeatures
+        ? other._fontFeatures
         : other._fontFeatures == null
           ? _fontFeatures
           : <FontFeature>[..._fontFeatures!, ...other._fontFeatures!],
@@ -1042,13 +1038,11 @@
          ellipsis,
          locale,
        ),
-       _textAlign = textAlign ?? TextAlign.start,
        _textDirection = textDirection ?? TextDirection.ltr,
        _maxLines = maxLines,
        _fontFamily = fontFamily,
        _fontSize = fontSize,
        _height = height,
-       _textHeightBehavior = textHeightBehavior ?? const TextHeightBehavior(),
        _fontWeight = fontWeight ?? FontWeight.normal,
        _fontStyle = fontStyle ?? FontStyle.normal,
        _strutStyle = strutStyle,
@@ -1056,13 +1050,11 @@
        _locale = locale;
 
   final Int32List _encoded;
-  final TextAlign _textAlign;
   final TextDirection _textDirection;
   final int? _maxLines;
   final String? _fontFamily;
   final double? _fontSize;
   final double? _height;
-  final TextHeightBehavior _textHeightBehavior;
   final FontWeight _fontWeight;
   final FontStyle _fontStyle;
   final StrutStyle? _strutStyle;
@@ -2049,9 +2041,7 @@
     _width = paragraphRight;
     _height = currentLineTop + currentLineHeight;
     final int? maxLines = _paragraphStyle._maxLines;
-    _didExceedMaxLines = maxLines == null
-      ? false
-      : _boxes.length > maxLines;
+    _didExceedMaxLines = maxLines != null && _boxes.length > maxLines;
   }
 
   /// Returns a list of text boxes that enclose the given text range.
@@ -2186,8 +2176,11 @@
 
   final double _width;
   final double _height;
+  // ignore: unused_field
   final int _alignment;
+  // ignore: unused_field
   final double _baselineOffset;
+  // ignore: unused_field
   final int? _baseline;
 
   @override
@@ -2235,6 +2228,7 @@
   }
 
   final ParagraphStyle _style;
+  // ignore: unused_field
   final List<String>? _strutFontFamilies;
 
   final List<TextStyle> _styleStack = <TextStyle>[];
@@ -2301,9 +2295,9 @@
     TextBaseline? baseline,
   }) {
     // Require a baseline to be specified if using a baseline-based alignment.
-    assert((alignment == PlaceholderAlignment.aboveBaseline ||
+    assert(!(alignment == PlaceholderAlignment.aboveBaseline ||
             alignment == PlaceholderAlignment.belowBaseline ||
-            alignment == PlaceholderAlignment.baseline) ? baseline != null : true);
+            alignment == PlaceholderAlignment.baseline) || baseline != null);
     // Default the baselineOffset to height if null. This will place the placeholder
     // fully above the baseline, similar to [PlaceholderAlignment.aboveBaseline].
     baselineOffset = baselineOffset ?? height;
diff --git a/framework/lib/src/ui/window.dart b/framework/lib/src/ui/window.dart
index 6a3db5d..a0de092 100644
--- a/framework/lib/src/ui/window.dart
+++ b/framework/lib/src/ui/window.dart
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+// See: https://github.com/dart-lang/linter/issues/2921
+// ignore_for_file: use_late_for_private_fields_and_variables
 
 part of dart.ui;
 
diff --git a/framework/lib/src/widgets/actions.dart b/framework/lib/src/widgets/actions.dart
index 8d95c68..4a1570e 100644
--- a/framework/lib/src/widgets/actions.dart
+++ b/framework/lib/src/widgets/actions.dart
@@ -587,9 +587,9 @@
     // concrete type of the intent at compile time.
     final Type type = intent?.runtimeType ?? T;
     assert(type != Intent,
-      'The type passed to "find" resolved to "Intent": either a non-Intent'
-      'generic type argument or an example intent derived from Intent must be'
-      'specified. Intent may be used as the generic type as long as the optional'
+      'The type passed to "find" resolved to "Intent": either a non-Intent '
+      'generic type argument or an example intent derived from Intent must be '
+      'specified. Intent may be used as the generic type as long as the optional '
       '"intent" argument is passed.');
 
     _visitActionsAncestors(context, (InheritedElement element) {
diff --git a/framework/lib/src/widgets/app.dart b/framework/lib/src/widgets/app.dart
index 64442da..6936ebb 100644
--- a/framework/lib/src/widgets/app.dart
+++ b/framework/lib/src/widgets/app.dart
@@ -1256,7 +1256,7 @@
     final NavigatorState? navigator = _navigator?.currentState;
     if (navigator == null)
       return false;
-    return await navigator.maybePop();
+    return navigator.maybePop();
   }
 
   @override
diff --git a/framework/lib/src/widgets/async.dart b/framework/lib/src/widgets/async.dart
index fdeee00..3828d10 100644
--- a/framework/lib/src/widgets/async.dart
+++ b/framework/lib/src/widgets/async.dart
@@ -253,8 +253,13 @@
   T get requireData {
     if (hasData)
       return data!;
-    if (hasError)
-      throw error!;
+    if (hasError) {
+      if (error is Exception) {
+        throw error! as Exception;
+      } else {
+        throw Exception(error);
+      }
+    }
     throw StateError('Snapshot has neither data nor error');
   }
 
diff --git a/framework/lib/src/widgets/fade_in_image.dart b/framework/lib/src/widgets/fade_in_image.dart
index 9aecf15..d2e4c6a 100644
--- a/framework/lib/src/widgets/fade_in_image.dart
+++ b/framework/lib/src/widgets/fade_in_image.dart
@@ -2,10 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import 'dart:typed_data';
-
 import 'package:flute/foundation.dart';
-import 'package:flute/services.dart';
 
 import 'basic.dart';
 import 'framework.dart';
diff --git a/framework/lib/src/widgets/form.dart b/framework/lib/src/widgets/form.dart
index 0d64127..65d70e3 100644
--- a/framework/lib/src/widgets/form.dart
+++ b/framework/lib/src/widgets/form.dart
@@ -490,6 +490,7 @@
   /// the value should be set by a call to [didChange], which ensures that
   /// `setState` is called.
   @protected
+  // ignore: use_setters_to_change_properties
   void setValue(T? value) {
     _value = value;
   }
diff --git a/framework/lib/src/widgets/framework.dart b/framework/lib/src/widgets/framework.dart
index ef79a91..bc98a95 100644
--- a/framework/lib/src/widgets/framework.dart
+++ b/framework/lib/src/widgets/framework.dart
@@ -3991,7 +3991,6 @@
     assert(_debugCheckStateIsActiveForAncestorLookup());
     final InheritedElement? ancestor = _inheritedWidgets == null ? null : _inheritedWidgets![targetType];
     if (ancestor != null) {
-      assert(ancestor is InheritedElement);
       return inheritFromElement(ancestor, aspect: aspect);
     }
     _hadUnsatisfiedDependencies = true;
@@ -4003,7 +4002,6 @@
     assert(_debugCheckStateIsActiveForAncestorLookup());
     final InheritedElement? ancestor = _inheritedWidgets == null ? null : _inheritedWidgets![T];
     if (ancestor != null) {
-      assert(ancestor is InheritedElement);
       return dependOnInheritedElement(ancestor, aspect: aspect) as T;
     }
     _hadUnsatisfiedDependencies = true;
@@ -6093,6 +6091,7 @@
   /// to [runApp]. The binding is responsible for driving the build pipeline by
   /// calling the build owner's [BuildOwner.buildScope] method. See
   /// [WidgetsBinding.drawFrame].
+  // ignore: use_setters_to_change_properties
   void assignOwner(BuildOwner owner) {
     _owner = owner;
   }
diff --git a/framework/lib/src/widgets/gesture_detector.dart b/framework/lib/src/widgets/gesture_detector.dart
index 4da60e6..de34b40 100644
--- a/framework/lib/src/widgets/gesture_detector.dart
+++ b/framework/lib/src/widgets/gesture_detector.dart
@@ -1322,7 +1322,6 @@
     final TapGestureRecognizer? tap = recognizers[TapGestureRecognizer] as TapGestureRecognizer?;
     if (tap == null)
       return null;
-    assert(tap is TapGestureRecognizer);
 
     return () {
       assert(tap != null);
@@ -1341,7 +1340,6 @@
       return null;
 
     return () {
-      assert(longPress is LongPressGestureRecognizer);
       if (longPress.onLongPressStart != null)
         longPress.onLongPressStart!(const LongPressStartDetails());
       if (longPress.onLongPress != null)
@@ -1360,7 +1358,6 @@
     final GestureDragUpdateCallback? horizontalHandler = horizontal == null ?
       null :
       (DragUpdateDetails details) {
-        assert(horizontal is HorizontalDragGestureRecognizer);
         if (horizontal.onDown != null)
           horizontal.onDown!(DragDownDetails());
         if (horizontal.onStart != null)
@@ -1374,7 +1371,6 @@
     final GestureDragUpdateCallback? panHandler = pan == null ?
       null :
       (DragUpdateDetails details) {
-        assert(pan is PanGestureRecognizer);
         if (pan.onDown != null)
           pan.onDown!(DragDownDetails());
         if (pan.onStart != null)
@@ -1402,7 +1398,6 @@
     final GestureDragUpdateCallback? verticalHandler = vertical == null ?
       null :
       (DragUpdateDetails details) {
-        assert(vertical is VerticalDragGestureRecognizer);
         if (vertical.onDown != null)
           vertical.onDown!(DragDownDetails());
         if (vertical.onStart != null)
@@ -1416,7 +1411,6 @@
     final GestureDragUpdateCallback? panHandler = pan == null ?
       null :
       (DragUpdateDetails details) {
-        assert(pan is PanGestureRecognizer);
         if (pan.onDown != null)
           pan.onDown!(DragDownDetails());
         if (pan.onStart != null)
diff --git a/framework/lib/src/widgets/image.dart b/framework/lib/src/widgets/image.dart
index 149ddb5..2000275 100644
--- a/framework/lib/src/widgets/image.dart
+++ b/framework/lib/src/widgets/image.dart
@@ -3,7 +3,6 @@
 // found in the LICENSE file.
 
 import 'dart:async';
-import 'package:flute/io.dart' show File;
 import 'dart:typed_data';
 
 import 'package:flute/foundation.dart';
diff --git a/framework/lib/src/widgets/navigator.dart b/framework/lib/src/widgets/navigator.dart
index c92bed3..449948d 100644
--- a/framework/lib/src/widgets/navigator.dart
+++ b/framework/lib/src/widgets/navigator.dart
@@ -176,6 +176,7 @@
     }
   }
 
+  // ignore: use_setters_to_change_properties
   void _updateRestorationId(String? restorationId) {
     _restorationScopeId.value = restorationId;
   }
diff --git a/framework/lib/src/widgets/preferred_size.dart b/framework/lib/src/widgets/preferred_size.dart
index e809b49..05c4874 100644
--- a/framework/lib/src/widgets/preferred_size.dart
+++ b/framework/lib/src/widgets/preferred_size.dart
@@ -17,6 +17,7 @@
 /// plus the height of the system status bar.
 ///
 /// Use [PreferredSize] to give a preferred size to an arbitrary widget.
+// ignore: avoid_implementing_value_types
 abstract class PreferredSizeWidget implements Widget {
   /// The size this widget would prefer if it were otherwise unconstrained.
   ///
diff --git a/framework/lib/src/widgets/routes.dart b/framework/lib/src/widgets/routes.dart
index 694c6f7..a7b3256 100644
--- a/framework/lib/src/widgets/routes.dart
+++ b/framework/lib/src/widgets/routes.dart
@@ -1333,7 +1333,7 @@
       if (await callback() != true)
         return RoutePopDisposition.doNotPop;
     }
-    return await super.willPop();
+    return super.willPop();
   }
 
   /// Enables this route to veto attempts by the user to dismiss it.
diff --git a/framework/lib/src/widgets/scroll_position.dart b/framework/lib/src/widgets/scroll_position.dart
index 8edbbad..bee9adf 100644
--- a/framework/lib/src/widgets/scroll_position.dart
+++ b/framework/lib/src/widgets/scroll_position.dart
@@ -318,6 +318,7 @@
   ///    middle of layout and applying the new position immediately.
   ///  * [animateTo], which is like [jumpTo] but animating to the
   ///    destination offset.
+  // ignore: use_setters_to_change_properties
   void correctPixels(double value) {
     _pixels = value;
   }
diff --git a/framework/lib/src/widgets/scrollable.dart b/framework/lib/src/widgets/scrollable.dart
index 08495ba..2495e6d 100644
--- a/framework/lib/src/widgets/scrollable.dart
+++ b/framework/lib/src/widgets/scrollable.dart
@@ -983,7 +983,7 @@
     final bool contextIsValid = focus != null && focus.context != null;
     if (contextIsValid) {
       // Check for primary scrollable within the current context
-      if (Scrollable.of(focus!.context!) != null)
+      if (Scrollable.of(focus.context!) != null)
         return true;
       // Check for fallback scrollable with context from PrimaryScrollController
       if (PrimaryScrollController.of(focus.context!) != null) {
diff --git a/framework/lib/src/widgets/widget_inspector.dart b/framework/lib/src/widgets/widget_inspector.dart
index 346eec7..fed4319 100644
--- a/framework/lib/src/widgets/widget_inspector.dart
+++ b/framework/lib/src/widgets/widget_inspector.dart
@@ -2607,7 +2607,7 @@
 
   ui.Picture _buildPicture(_InspectorOverlayRenderState state) {
     final ui.PictureRecorder recorder = ui.PictureRecorder();
-    final Canvas canvas = Canvas(recorder, state.overlayRect);
+    final Canvas canvas = Canvas(recorder);
     final Size size = state.overlayRect.size;
     // The overlay rect could have an offset if the widget inspector does
     // not take all the screen.