Add WASM target in gn (#31670)

diff --git a/BUILD.gn b/BUILD.gn
index 0f6245a2..114d606 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -31,6 +31,14 @@
   }
 }
 
+# Flutter SDK artifacts should only be built when either doing host builds, or
+# for cross-compiled desktop targets.
+# TODO: We can't build the engine artifacts for arm (32-bit) right now;
+# see https://github.com/flutter/flutter/issues/74322
+_build_engine_artifacts =
+    current_toolchain == host_toolchain ||
+    (is_linux && !is_chromeos && current_cpu != "arm") || is_mac
+
 if (flutter_prebuilt_dart_sdk) {
   copy_trees("_copy_trees") {
     sources = [
@@ -45,14 +53,6 @@
   }
 }
 
-# Flutter SDK artifacts should only be built when either doing host builds, or
-# for cross-compiled desktop targets.
-# TODO: We can't build the engine artifacts for arm (32-bit) right now;
-# see https://github.com/flutter/flutter/issues/74322
-_build_engine_artifacts =
-    current_toolchain == host_toolchain ||
-    (is_linux && !is_chromeos && current_cpu != "arm") || is_mac
-
 group("dart_sdk") {
   if (build_engine_artifacts) {
     if (flutter_prebuilt_dart_sdk) {
diff --git a/DEPS b/DEPS
index 23efd17..7f7d1ed 100644
--- a/DEPS
+++ b/DEPS
@@ -33,6 +33,12 @@
   # See `lib/web_ui/README.md` for how to roll CanvasKit to a new version.
   'canvaskit_cipd_instance': '8MSYGWVWzrTJIoVL00ZquruZs-weuwLBy1kt1AawJiIC',
 
+  # Do not download the Emscripten SDK by default.
+  # This prevents us from downloading the Emscripten toolchain for builds
+  # which do not build for the web. This toolchain is needed to build CanvasKit
+  # for the web engine.
+  'download_emsdk': False,
+
   # When updating the Dart revision, ensure that all entries that are
   # dependencies of Dart are also updated to match the entries in the
   # Dart SDK's DEPS file for that revision of Dart. The DEPS file for
@@ -155,7 +161,7 @@
    # Chromium-style dependencies.
 
   'src/third_party/icu':
-   Var('chromium_git') + '/chromium/deps/icu.git' + '@' + '2e0f2989441ec2f55abec30f48e89981dbac2c34',
+   Var('chromium_git') + '/chromium/deps/icu.git' + '@' + '1fa4e3959ec6637182b7318ac1d382799454806d',
 
   'src/third_party/khronos':
    Var('chromium_git') + '/chromium/src/third_party/khronos.git' + '@' + '7122230e90547962e0f0c627f62eeed3c701f275',
@@ -565,6 +571,11 @@
     'dep_type': 'cipd',
   },
 
+  'src/buildtools/emsdk': {
+   'url': Var('skia_git') + '/external/github.com/emscripten-core/emsdk.git' + '@' + 'fc645b7626ebf86530dbd82fbece74d457e7ae07',
+   'condition': 'download_emsdk',
+  },
+
   # Clang on mac and linux are expected to typically be the same revision.
   # They are separated out so that the autoroller can more easily manage them.
   'src/buildtools/mac-x64/clang': {
@@ -740,6 +751,15 @@
     ]
   },
   {
+    'name': 'Activate Emscripten SDK',
+    'pattern': '.',
+    'condition': 'download_emsdk',
+    'action': [
+      'python3',
+      'src/flutter/tools/activate_emsdk.py',
+    ]
+  },
+  {
     'name': 'Setup githooks',
     'pattern': '.',
     'condition': 'setup_githooks',
diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia
index 2133406..0cb5bf9 100644
--- a/ci/licenses_golden/licenses_skia
+++ b/ci/licenses_golden/licenses_skia
@@ -651,8 +651,17 @@
 FILE: ../../../third_party/skia/CQ_COMMITTERS
 FILE: ../../../third_party/skia/DEPS
 FILE: ../../../third_party/skia/DIR_METADATA
-FILE: ../../../third_party/skia/OWNERS.android
 FILE: ../../../third_party/skia/WORKSPACE.bazel
+FILE: ../../../third_party/skia/animations/checkbox.xml
+FILE: ../../../third_party/skia/animations/chest#1.jpg
+FILE: ../../../third_party/skia/animations/fire#1.jpg
+FILE: ../../../third_party/skia/animations/images#1.xml
+FILE: ../../../third_party/skia/animations/jet#1.jpg
+FILE: ../../../third_party/skia/animations/lines#1.xml
+FILE: ../../../third_party/skia/animations/movie#1.xml
+FILE: ../../../third_party/skia/animations/paths#1.xml
+FILE: ../../../third_party/skia/animations/redcross#1.jpg
+FILE: ../../../third_party/skia/animations/text#1.xml
 FILE: ../../../third_party/skia/bazel/BUILD.bazel
 FILE: ../../../third_party/skia/bazel/cc_binary_with_flags.bzl
 FILE: ../../../third_party/skia/bazel/common_config_settings/BUILD.bazel
@@ -754,6 +763,16 @@
 FILE: ../../../third_party/skia/experimental/tskit/tsconfig.json
 FILE: ../../../third_party/skia/experimental/wasm-hello-world/BUILD.bazel
 FILE: ../../../third_party/skia/experimental/wasm-hello-world/hello_world.html
+FILE: ../../../third_party/skia/experimental/wasm-skp-debugger/cpu.js
+FILE: ../../../third_party/skia/experimental/wasm-skp-debugger/debugger/anim.mskp
+FILE: ../../../third_party/skia/experimental/wasm-skp-debugger/debugger/index.html
+FILE: ../../../third_party/skia/experimental/wasm-skp-debugger/debugger/sample.skp
+FILE: ../../../third_party/skia/experimental/wasm-skp-debugger/externs.js
+FILE: ../../../third_party/skia/experimental/wasm-skp-debugger/fonts/NotoMono-Regular.ttf
+FILE: ../../../third_party/skia/experimental/wasm-skp-debugger/gpu.js
+FILE: ../../../third_party/skia/experimental/wasm-skp-debugger/helper.js
+FILE: ../../../third_party/skia/experimental/wasm-skp-debugger/karma.conf.js
+FILE: ../../../third_party/skia/experimental/wasm-skp-debugger/package.json
 FILE: ../../../third_party/skia/experimental/webgpu-bazel/example/index.html
 FILE: ../../../third_party/skia/experimental/webgpu-bazel/src/BUILD
 FILE: ../../../third_party/skia/gm/BUILD.bazel
@@ -784,7 +803,6 @@
 FILE: ../../../third_party/skia/include/private/chromium/BUILD.bazel
 FILE: ../../../third_party/skia/include/sksl/BUILD.bazel
 FILE: ../../../third_party/skia/include/svg/BUILD.bazel
-FILE: ../../../third_party/skia/include/third_party/BUILD.bazel
 FILE: ../../../third_party/skia/include/utils/BUILD.bazel
 FILE: ../../../third_party/skia/include/utils/mac/BUILD.bazel
 FILE: ../../../third_party/skia/infra/bots/BUILD.bazel
@@ -1893,6 +1911,7 @@
 FILE: ../../../third_party/skia/bench/SwizzleBench.cpp
 FILE: ../../../third_party/skia/bench/TileImageFilterBench.cpp
 FILE: ../../../third_party/skia/bench/VertexColorSpaceBench.cpp
+FILE: ../../../third_party/skia/experimental/xps_to_png/xps_to_png.cs
 FILE: ../../../third_party/skia/fuzz/Fuzz.cpp
 FILE: ../../../third_party/skia/fuzz/Fuzz.h
 FILE: ../../../third_party/skia/fuzz/FuzzGradients.cpp
@@ -3247,7 +3266,7 @@
 FILE: ../../../third_party/skia/src/effects/imagefilters/SkDisplacementMapImageFilter.cpp
 FILE: ../../../third_party/skia/src/effects/imagefilters/SkDropShadowImageFilter.cpp
 FILE: ../../../third_party/skia/src/effects/imagefilters/SkTileImageFilter.cpp
-FILE: ../../../third_party/skia/src/gpu/Blend.h
+FILE: ../../../third_party/skia/src/gpu/GrBlend.h
 FILE: ../../../third_party/skia/src/gpu/GrCaps.h
 FILE: ../../../third_party/skia/src/gpu/GrGeometryProcessor.h
 FILE: ../../../third_party/skia/src/gpu/GrPaint.cpp
@@ -3326,6 +3345,7 @@
 FILE: ../../../third_party/skia/bench/BulkRectBench.cpp
 FILE: ../../../third_party/skia/bench/DDLRecorderBench.cpp
 FILE: ../../../third_party/skia/bench/SkSLBench.cpp
+FILE: ../../../third_party/skia/experimental/wasm-skp-debugger/debugger_bindings.cpp
 FILE: ../../../third_party/skia/gm/asyncrescaleandread.cpp
 FILE: ../../../third_party/skia/gm/crbug_908646.cpp
 FILE: ../../../third_party/skia/gm/crbug_946965.cpp
@@ -3445,6 +3465,7 @@
 FILE: ../../../third_party/skia/bench/PolyUtilsBench.cpp
 FILE: ../../../third_party/skia/bench/ShaderMaskFilterBench.cpp
 FILE: ../../../third_party/skia/bench/TypefaceBench.cpp
+FILE: ../../../third_party/skia/experimental/pvg/draw_msg.proto
 FILE: ../../../third_party/skia/fuzz/FuzzCommon.cpp
 FILE: ../../../third_party/skia/fuzz/FuzzPathMeasure.cpp
 FILE: ../../../third_party/skia/fuzz/FuzzRegionOp.cpp
@@ -4182,6 +4203,21 @@
 FILE: ../../../third_party/skia/bench/FindCubicConvex180ChopsBench.cpp
 FILE: ../../../third_party/skia/bench/GlyphQuadFillBench.cpp
 FILE: ../../../third_party/skia/bench/TessellateBench.cpp
+FILE: ../../../third_party/skia/experimental/skrive/include/SkRive.h
+FILE: ../../../third_party/skia/experimental/skrive/src/Artboard.cpp
+FILE: ../../../third_party/skia/experimental/skrive/src/Color.cpp
+FILE: ../../../third_party/skia/experimental/skrive/src/Component.cpp
+FILE: ../../../third_party/skia/experimental/skrive/src/Drawable.cpp
+FILE: ../../../third_party/skia/experimental/skrive/src/Ellipse.cpp
+FILE: ../../../third_party/skia/experimental/skrive/src/Node.cpp
+FILE: ../../../third_party/skia/experimental/skrive/src/Paint.cpp
+FILE: ../../../third_party/skia/experimental/skrive/src/Rectangle.cpp
+FILE: ../../../third_party/skia/experimental/skrive/src/Shape.cpp
+FILE: ../../../third_party/skia/experimental/skrive/src/SkRive.cpp
+FILE: ../../../third_party/skia/experimental/skrive/src/reader/BinaryReader.cpp
+FILE: ../../../third_party/skia/experimental/skrive/src/reader/JsonReader.cpp
+FILE: ../../../third_party/skia/experimental/skrive/src/reader/StreamReader.cpp
+FILE: ../../../third_party/skia/experimental/skrive/src/reader/StreamReader.h
 FILE: ../../../third_party/skia/fuzz/FuzzSkParagraph.cpp
 FILE: ../../../third_party/skia/gm/3d.cpp
 FILE: ../../../third_party/skia/gm/bc1_transparency.cpp
@@ -6091,8 +6127,6 @@
 FILE: ../../../third_party/skia/experimental/graphite/src/mtl/MtlSampler.h
 FILE: ../../../third_party/skia/experimental/graphite/src/mtl/MtlSampler.mm
 FILE: ../../../third_party/skia/gm/bug12866.cpp
-FILE: ../../../third_party/skia/include/core/SkAlphaType.h
-FILE: ../../../third_party/skia/include/core/SkColorType.h
 FILE: ../../../third_party/skia/include/private/SkUniquePaintParamsID.h
 FILE: ../../../third_party/skia/src/core/SkBuiltInCodeSnippetID.h
 FILE: ../../../third_party/skia/src/core/SkDebugUtils.h
@@ -6102,8 +6136,6 @@
 FILE: ../../../third_party/skia/src/core/SkKeyHelpers.h
 FILE: ../../../third_party/skia/src/core/SkPaintParamsKey.cpp
 FILE: ../../../third_party/skia/src/core/SkPaintParamsKey.h
-FILE: ../../../third_party/skia/src/core/SkPipelineData.cpp
-FILE: ../../../third_party/skia/src/core/SkPipelineData.h
 FILE: ../../../third_party/skia/src/core/SkSLTypeShared.h
 FILE: ../../../third_party/skia/src/core/SkShaderCodeDictionary.cpp
 FILE: ../../../third_party/skia/src/core/SkShaderCodeDictionary.h
@@ -6181,6 +6213,7 @@
 FILE: ../../../third_party/skia/src/gpu/ops/PathTessellateOp.cpp
 FILE: ../../../third_party/skia/src/gpu/ops/PathTessellateOp.h
 FILE: ../../../third_party/skia/src/gpu/tessellate/AffineMatrix.h
+FILE: ../../../third_party/skia/src/gpu/tessellate/PatchWriter.cpp
 FILE: ../../../third_party/skia/src/gpu/tessellate/PatchWriter.h
 FILE: ../../../third_party/skia/src/gpu/tessellate/PathCurveTessellator.cpp
 FILE: ../../../third_party/skia/src/gpu/tessellate/PathCurveTessellator.h
diff --git a/ci/licenses_golden/licenses_third_party b/ci/licenses_golden/licenses_third_party
index 6bcef1f..4dbc7a9 100644
--- a/ci/licenses_golden/licenses_third_party
+++ b/ci/licenses_golden/licenses_third_party
@@ -1,4 +1,4 @@
-Signature: 80ac7a3b149c4cac0af4058b3b4a4bb6
+Signature: 63800bcd154af7272865369dc774c2c4
 
 UNUSED LICENSES:
 
@@ -18098,6 +18098,7 @@
 FILE: ../../../third_party/icu/cast/brkitr.patch
 FILE: ../../../third_party/icu/cast/icudtl.dat
 FILE: ../../../third_party/icu/chromeos/icudtl.dat
+FILE: ../../../third_party/icu/chromeos/icudtl.dat.hash
 FILE: ../../../third_party/icu/common/icudtb.dat
 FILE: ../../../third_party/icu/common/icudtl.dat
 FILE: ../../../third_party/icu/flutter/brkitr.patch
@@ -18227,6 +18228,7 @@
 FILE: ../../../third_party/icu/cast/brkitr.patch
 FILE: ../../../third_party/icu/cast/icudtl.dat
 FILE: ../../../third_party/icu/chromeos/icudtl.dat
+FILE: ../../../third_party/icu/chromeos/icudtl.dat.hash
 FILE: ../../../third_party/icu/common/icudtb.dat
 FILE: ../../../third_party/icu/common/icudtl.dat
 FILE: ../../../third_party/icu/flutter/brkitr.patch
@@ -18366,6 +18368,7 @@
 FILE: ../../../third_party/icu/cast/brkitr.patch
 FILE: ../../../third_party/icu/cast/icudtl.dat
 FILE: ../../../third_party/icu/chromeos/icudtl.dat
+FILE: ../../../third_party/icu/chromeos/icudtl.dat.hash
 FILE: ../../../third_party/icu/common/icudtb.dat
 FILE: ../../../third_party/icu/common/icudtl.dat
 FILE: ../../../third_party/icu/flutter/brkitr.patch
@@ -18500,6 +18503,7 @@
 FILE: ../../../third_party/icu/cast/brkitr.patch
 FILE: ../../../third_party/icu/cast/icudtl.dat
 FILE: ../../../third_party/icu/chromeos/icudtl.dat
+FILE: ../../../third_party/icu/chromeos/icudtl.dat.hash
 FILE: ../../../third_party/icu/common/icudtb.dat
 FILE: ../../../third_party/icu/common/icudtl.dat
 FILE: ../../../third_party/icu/flutter/brkitr.patch
@@ -18633,6 +18637,7 @@
 FILE: ../../../third_party/icu/cast/brkitr.patch
 FILE: ../../../third_party/icu/cast/icudtl.dat
 FILE: ../../../third_party/icu/chromeos/icudtl.dat
+FILE: ../../../third_party/icu/chromeos/icudtl.dat.hash
 FILE: ../../../third_party/icu/common/icudtb.dat
 FILE: ../../../third_party/icu/common/icudtl.dat
 FILE: ../../../third_party/icu/flutter/brkitr.patch
@@ -18771,6 +18776,7 @@
 FILE: ../../../third_party/icu/cast/brkitr.patch
 FILE: ../../../third_party/icu/cast/icudtl.dat
 FILE: ../../../third_party/icu/chromeos/icudtl.dat
+FILE: ../../../third_party/icu/chromeos/icudtl.dat.hash
 FILE: ../../../third_party/icu/common/icudtb.dat
 FILE: ../../../third_party/icu/common/icudtl.dat
 FILE: ../../../third_party/icu/flutter/brkitr.patch
@@ -18905,6 +18911,7 @@
 FILE: ../../../third_party/icu/cast/brkitr.patch
 FILE: ../../../third_party/icu/cast/icudtl.dat
 FILE: ../../../third_party/icu/chromeos/icudtl.dat
+FILE: ../../../third_party/icu/chromeos/icudtl.dat.hash
 FILE: ../../../third_party/icu/common/icudtb.dat
 FILE: ../../../third_party/icu/common/icudtl.dat
 FILE: ../../../third_party/icu/flutter/brkitr.patch
@@ -19038,6 +19045,7 @@
 FILE: ../../../third_party/icu/cast/brkitr.patch
 FILE: ../../../third_party/icu/cast/icudtl.dat
 FILE: ../../../third_party/icu/chromeos/icudtl.dat
+FILE: ../../../third_party/icu/chromeos/icudtl.dat.hash
 FILE: ../../../third_party/icu/common/icudtb.dat
 FILE: ../../../third_party/icu/common/icudtl.dat
 FILE: ../../../third_party/icu/flutter/brkitr.patch
@@ -19144,6 +19152,7 @@
 FILE: ../../../third_party/icu/cast/brkitr.patch
 FILE: ../../../third_party/icu/cast/icudtl.dat
 FILE: ../../../third_party/icu/chromeos/icudtl.dat
+FILE: ../../../third_party/icu/chromeos/icudtl.dat.hash
 FILE: ../../../third_party/icu/common/icudtb.dat
 FILE: ../../../third_party/icu/common/icudtl.dat
 FILE: ../../../third_party/icu/flutter/brkitr.patch
@@ -19317,6 +19326,7 @@
 FILE: ../../../third_party/icu/cast/brkitr.patch
 FILE: ../../../third_party/icu/cast/icudtl.dat
 FILE: ../../../third_party/icu/chromeos/icudtl.dat
+FILE: ../../../third_party/icu/chromeos/icudtl.dat.hash
 FILE: ../../../third_party/icu/common/icudtb.dat
 FILE: ../../../third_party/icu/common/icudtl.dat
 FILE: ../../../third_party/icu/flutter/brkitr.patch
diff --git a/ci/licenses_golden/tool_signature b/ci/licenses_golden/tool_signature
index f26ca83..5411720 100644
--- a/ci/licenses_golden/tool_signature
+++ b/ci/licenses_golden/tool_signature
@@ -1,2 +1,2 @@
-Signature: 270842a9eb9687ced20abdf5f131bafd
+Signature: 5efe7843d99ba138e2bb7dac2441b5b8
 
diff --git a/lib/web_ui/BUILD.gn b/lib/web_ui/BUILD.gn
new file mode 100644
index 0000000..955753e
--- /dev/null
+++ b/lib/web_ui/BUILD.gn
@@ -0,0 +1,7 @@
+# Copyright 2022 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.
+
+group("canvaskit") {
+  deps = [ "//third_party/skia/modules/canvaskit" ]
+}
diff --git a/lib/web_ui/README.md b/lib/web_ui/README.md
index f36a7b6..72603c3 100644
--- a/lib/web_ui/README.md
+++ b/lib/web_ui/README.md
@@ -265,6 +265,59 @@
 FELT_USE_SNAPSHOT=false felt <command>
 ```
 
+## Building CanvasKit
+
+To build CanvasKit locally, you must first set up your gclient config to
+activate the Emscripten SDK, which is the toolchain used to build CanvasKit.
+To do this, replace the contents of your .gclient file at the root of the
+project (i.e. in the parent directory of the `src` directory) with:
+
+```
+solutions = [
+  {
+    "managed": False,
+    "name": "src/flutter",
+    "url": "git@github.com:<your_username_here>/engine.git",
+    "custom_deps": {},
+    "deps_file": "DEPS",
+    "safesync_url": "",
+    "custom_vars": {
+      "download_emsdk": True,
+    },
+  },
+]
+```
+
+Now run `gclient sync` and it should pull in the Emscripten SDK and activate it.
+
+To build CanvasKit with `felt`, run:
+
+```
+felt build --build-canvaskit
+```
+
+This will build CanvasKit in `out/wasm_debug`. If you now run
+
+```
+felt test
+```
+
+it will detect that you have built CanvasKit and use that instead of the one
+from CIPD to run the tests against.
+
+### Upgrading the Emscripten SDK for the CanvasKit build
+
+The version of the Emscripten SDK should be kept up to date with the version
+used in the Skia build. That version can be found in
+`third_party/skia/bin/activate-emsdk`. It will probably also be necessary to
+roll the dependency on `third_party/emsdk` in DEPS to the same version as in
+`third_party/skia/DEPS`.
+
+Once you know the version for the Emscripten SDK, change the line in
+`tools/activate_emsdk.py` which defines `EMSDK_VERSION` to match Skia.
+
+
+
 [1]: https://github.com/flutter/flutter/wiki/Setting-up-the-Engine-development-environment
 [2]: https://github.com/flutter/engine/blob/main/lib/web_ui/dev/browser_lock.yaml
 [4]: https://chrome-infra-packages.appspot.com/p/flutter_internal
diff --git a/lib/web_ui/dev/build.dart b/lib/web_ui/dev/build.dart
index 459dc19..30a2f14 100644
--- a/lib/web_ui/dev/build.dart
+++ b/lib/web_ui/dev/build.dart
@@ -16,12 +16,18 @@
 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.',
-      );
+      'watch',
+      defaultsTo: false,
+      abbr: 'w',
+      help: 'Run the build in watch mode so it rebuilds whenever a change is '
+          'made. Disabled by default.',
+    );
+    argParser.addFlag(
+      'build-canvaskit',
+      defaultsTo: false,
+      help: 'Build CanvasKit locally instead of getting it from CIPD. Disabled '
+          'by default.',
+    );
   }
 
   @override
@@ -32,12 +38,14 @@
 
   bool get isWatchMode => boolArg('watch');
 
+  bool get buildCanvasKit => boolArg('build-canvaskit');
+
   @override
   FutureOr<bool> run() async {
     final FilePath libPath = FilePath.fromWebUi('lib');
     final Pipeline buildPipeline = Pipeline(steps: <PipelineStep>[
-      GnPipelineStep(),
-      NinjaPipelineStep(),
+      GnPipelineStep(buildCanvasKit),
+      NinjaPipelineStep(buildCanvasKit),
     ]);
     await buildPipeline.run();
 
@@ -60,16 +68,21 @@
 /// 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 {
+  GnPipelineStep(this.buildCanvasKit);
+
   @override
   String get description => 'gn';
 
   @override
   bool get isSafeToInterrupt => false;
 
+  /// Whether or not to build CanvasKit.
+  final bool buildCanvasKit;
+
   @override
   Future<ProcessManager> createProcess() {
     print('Running gn...');
-    return startProcess(
+    Future<ProcessManager> gnProcess = startProcess(
       path.join(environment.flutterDirectory.path, 'tools', 'gn'),
       <String>[
         '--unopt',
@@ -77,6 +90,17 @@
         '--full-dart-sdk',
       ],
     );
+    if (buildCanvasKit) {
+      gnProcess = gnProcess.then((_) {
+        return startProcess(
+          path.join(environment.flutterDirectory.path, 'tools', 'gn'),
+          <String>[
+            '--wasm',
+          ],
+        );
+      });
+    }
+    return gnProcess;
   }
 }
 
@@ -84,21 +108,38 @@
 ///
 /// Can be safely interrupted.
 class NinjaPipelineStep extends ProcessStep {
+  NinjaPipelineStep(this.buildCanvasKit);
+
   @override
   String get description => 'ninja';
 
   @override
   bool get isSafeToInterrupt => true;
 
+  /// Whether or not to build CanvasKit.
+  final bool buildCanvasKit;
+
   @override
   Future<ProcessManager> createProcess() {
     print('Running autoninja...');
-    return startProcess(
+    Future<ProcessManager> ninjaProcess = startProcess(
       'autoninja',
       <String>[
         '-C',
         environment.hostDebugUnoptDir.path,
       ],
     );
+    if (buildCanvasKit) {
+      ninjaProcess = ninjaProcess.then((_) {
+        return startProcess(
+          'autoninja',
+          <String>[
+            '-C',
+            environment.canvasKitOutDir.path,
+          ],
+        );
+      });
+    }
+    return ninjaProcess;
   }
 }
diff --git a/lib/web_ui/dev/environment.dart b/lib/web_ui/dev/environment.dart
index 792ea23..7fe5f90 100644
--- a/lib/web_ui/dev/environment.dart
+++ b/lib/web_ui/dev/environment.dart
@@ -25,6 +25,8 @@
         io.Directory(pathlib.join(engineSrcDir.path, 'out'));
     final io.Directory hostDebugUnoptDir =
         io.Directory(pathlib.join(outDir.path, 'host_debug_unopt'));
+    final io.Directory canvasKitOutDir =
+        io.Directory(pathlib.join(outDir.path, 'wasm_debug'));
     final io.Directory dartSdkDir =
         io.Directory(pathlib.join(hostDebugUnoptDir.path, 'dart-sdk'));
     final io.Directory webUiRootDir = io.Directory(
@@ -49,6 +51,7 @@
       engineToolsDir: engineToolsDir,
       outDir: outDir,
       hostDebugUnoptDir: hostDebugUnoptDir,
+      canvasKitOutDir: canvasKitOutDir,
       dartSdkDir: dartSdkDir,
     );
   }
@@ -60,6 +63,7 @@
     required this.engineToolsDir,
     required this.outDir,
     required this.hostDebugUnoptDir,
+    required this.canvasKitOutDir,
     required this.dartSdkDir,
   });
 
@@ -83,6 +87,9 @@
   /// The "host_debug_unopt" build of the Dart SDK.
   final io.Directory hostDebugUnoptDir;
 
+  /// The output directory for the build of CanvasKit.
+  final io.Directory canvasKitOutDir;
+
   /// The root of the Dart SDK.
   final io.Directory dartSdkDir;
 
diff --git a/lib/web_ui/dev/steps/compile_tests_step.dart b/lib/web_ui/dev/steps/compile_tests_step.dart
index dcca072..bfc85f5 100644
--- a/lib/web_ui/dev/steps/compile_tests_step.dart
+++ b/lib/web_ui/dev/steps/compile_tests_step.dart
@@ -122,30 +122,58 @@
 }
 
 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>();
+  // If CanvasKit has been built locally, use that instead of the CIPD version.
+  final io.File localCanvasKitWasm =
+      io.File(pathlib.join(environment.canvasKitOutDir.path, 'canvaskit.wasm'));
+  final bool builtLocalCanvasKit = localCanvasKitWasm.existsSync();
 
   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,
+  if (builtLocalCanvasKit) {
+    final List<io.File> canvasKitFiles = <io.File>[
+      localCanvasKitWasm,
+      io.File(pathlib.join(environment.canvasKitOutDir.path, 'canvaskit.js')),
+    ];
+    for (final io.File file in canvasKitFiles) {
+      final io.File normalTargetFile = io.File(pathlib.join(
+        targetDir.path,
+        pathlib.basename(file.path),
+      ));
+      final io.File profileTargetFile = io.File(pathlib.join(
+        targetDir.path,
+        'profiling',
+        pathlib.basename(file.path),
+      ));
+      await normalTargetFile.create(recursive: true);
+      await profileTargetFile.create(recursive: true);
+      await file.copy(normalTargetFile.path);
+      await file.copy(profileTargetFile.path);
+    }
+  } else {
+    final io.Directory canvasKitDir = io.Directory(pathlib.join(
+      environment.engineSrcDir.path,
+      'third_party',
+      'web_dependencies',
+      'canvaskit',
     ));
-    await targetFile.create(recursive: true);
-    await file.copy(targetFile.path);
+
+    final Iterable<io.File> canvasKitFiles = canvasKitDir
+        .listSync(recursive: true, followLinks: true)
+        .whereType<io.File>();
+
+    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);
+    }
   }
 }
 
@@ -221,7 +249,7 @@
 /// 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 {
+Future<bool> compileUnitTest(FilePath input, {required bool forCanvasKit}) async {
   final String targetFileName = pathlib.join(
     environment.webUiBuildDir.path,
     '${input.relativeToWebUi}.browser_test.dart.js',
@@ -340,7 +368,7 @@
   if (exitCode != 0) {
     throw ToolExit(
       'Failed to compile ${hostDartFile.path}. Compiler '
-        'exited with exit code $exitCode',
+      'exited with exit code $exitCode',
       exitCode: exitCode,
     );
   }
diff --git a/tools/activate_emsdk.py b/tools/activate_emsdk.py
new file mode 100644
index 0000000..91a05a5
--- /dev/null
+++ b/tools/activate_emsdk.py
@@ -0,0 +1,33 @@
+#!/usr/bin/env python
+
+# Copyright 2022 Google LLC
+#
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import os
+import subprocess
+import sys
+
+EMSDK_ROOT = os.path.join('src', 'buildtools', 'emsdk')
+
+EMSDK_PATH = os.path.join(EMSDK_ROOT, 'emsdk.py')
+
+# See lib/web_ui/README.md for instructions on updating the EMSDK version.
+EMSDK_VERSION = '3.1.3'
+
+def main():
+    try:
+        subprocess.check_call([sys.executable, EMSDK_PATH, 'install', EMSDK_VERSION])
+    except subprocess.CalledProcessError:
+        print ('Failed to install emsdk')
+        return 1
+    try:
+        subprocess.check_call([sys.executable, EMSDK_PATH, 'activate', EMSDK_VERSION])
+    except subprocess.CalledProcessError:
+        print ('Failed to activate emsdk')
+        return 1
+
+
+if __name__ == '__main__':
+  sys.exit(main())
\ No newline at end of file
diff --git a/tools/gn b/tools/gn
index cbc0541..3740f84 100755
--- a/tools/gn
+++ b/tools/gn
@@ -150,15 +150,17 @@
     gn_args['skia_use_wuffs'] = True
     gn_args['skia_use_expat'] = args.target_os == 'android'
     gn_args['skia_use_fontconfig'] = args.enable_fontconfig
-    gn_args['flutter_use_fontconfig'] = args.enable_fontconfig
-    gn_args['flutter_enable_skshaper'] = args.enable_skshaper
+
     if args.enable_skshaper:
       gn_args['skia_use_icu'] = True
-      gn_args['flutter_always_use_skshaper'] = args.always_use_skshaper
+      if args.target_os != 'wasm':
+        gn_args['flutter_always_use_skshaper'] = args.always_use_skshaper
+      else:
+        gn_args['skia_use_harfbuzz'] = True
+        gn_args['icu_use_data_file'] = False
     if args.target_os == 'winuwp':
       gn_args['skia_enable_winuwp'] = True
     gn_args['is_official_build'] = True    # Disable Skia test utilities.
-    gn_args['dart_component_kind'] = 'static_library' # Always link Dart in statically.
     gn_args['is_debug'] = args.unoptimized
     gn_args['android_full_debug'] = args.target_os == 'android' and args.unoptimized
     if args.clang is None:
@@ -168,6 +170,8 @@
 
     if args.target_os == 'android' or args.target_os == 'ios':
       gn_args['skia_gl_standard'] = 'gles'
+    elif args.target_os == 'wasm':
+      gn_args['skia_gl_standard'] = 'webgl'
     else:
       # We explicitly don't want to pick GL because we run GLES tests using SwiftShader.
       gn_args['skia_gl_standard'] = ''
@@ -175,8 +179,6 @@
     if not sys.platform.startswith(('cygwin', 'win')):
       gn_args['use_clang_static_analyzer'] = args.clang_static_analyzer
 
-    gn_args['embedder_for_target'] = args.embedder_for_target
-
     gn_args['enable_coverage'] = args.coverage
 
     if args.operator_new_alignment is not None:
@@ -201,20 +203,13 @@
         gn_args['use_ios_simulator'] = False
     elif args.target_os == 'fuchsia':
         gn_args['target_os'] = 'fuchsia'
+    elif args.target_os == 'wasm':
+        gn_args['target_os'] = 'wasm'
     elif args.target_os == 'winuwp':
         gn_args['target_os'] = 'winuwp'
     elif args.target_os is not None:
         gn_args['target_os'] = args.target_os
 
-    gn_args['dart_lib_export_symbols'] = False
-
-    if runtime_mode == 'debug':
-        gn_args['dart_runtime_mode'] = 'develop'
-    elif runtime_mode == 'jit_release':
-        gn_args['dart_runtime_mode'] = 'release'
-    else:
-        gn_args['dart_runtime_mode'] = runtime_mode
-
     if args.dart_debug:
         gn_args['dart_debug'] = True
 
@@ -235,16 +230,48 @@
       gn_args['target_cpu'] = args.linux_cpu
     elif args.target_os == 'fuchsia':
       gn_args['target_cpu'] = args.fuchsia_cpu
+    elif args.target_os == 'wasm':
+      gn_args['target_cpu'] = 'wasm'
     elif args.target_os == 'win':
       gn_args['target_cpu'] = args.windows_cpu
     else:
         # Building host artifacts
         gn_args['target_cpu'] = 'x64'
 
+    # Flutter-specific arguments which don't apply for a CanvasKit build.
+    if args.target_os != 'wasm':
+      gn_args['flutter_use_fontconfig'] = args.enable_fontconfig
+      gn_args['flutter_enable_skshaper'] = args.enable_skshaper
+      gn_args['dart_component_kind'] = 'static_library' # Always link Dart in statically.
+      gn_args['embedder_for_target'] = args.embedder_for_target
+      gn_args['dart_lib_export_symbols'] = False
+      gn_args['dart_target_arch'] = gn_args['target_cpu']
+      gn_args['flutter_runtime_mode'] = runtime_mode
+      gn_args['full_dart_sdk'] = args.full_dart_sdk
+      gn_args['dart_version_git_info'] = not args.no_dart_version_git_info
+
+      gn_args['dart_lib_export_symbols'] = False
+      if runtime_mode == 'debug':
+          gn_args['dart_runtime_mode'] = 'develop'
+      elif runtime_mode == 'jit_release':
+          gn_args['dart_runtime_mode'] = 'release';
+      else:
+          gn_args['dart_runtime_mode'] = runtime_mode
+      
+      # Desktop embeddings can have more dependencies than the engine library,
+      # which can be problematic in some build environments (e.g., building on
+      # Linux will bring in pkg-config dependencies at generation time). These
+      # flags allow preventing those those targets from being part of the build
+      # tree.
+      gn_args['enable_desktop_embeddings'] = not args.disable_desktop_embeddings
+
+      # Overrides whether Boring SSL is compiled with system as. Only meaningful
+      # on Android.
+      gn_args['bssl_use_clang_integrated_as'] = True
+    
     # DBC is not supported anymore.
     if args.interpreter:
       raise Exception('--interpreter is no longer needed on any supported platform.')
-    gn_args['dart_target_arch'] = gn_args['target_cpu']
 
     if sys.platform.startswith(('cygwin', 'win')) and args.target_os != 'win':
       if 'target_cpu' in gn_args:
@@ -258,8 +285,6 @@
     if gn_args['target_cpu'] == 'x86':
       gn_args['host_cpu'] = 'x86'
 
-    gn_args['flutter_runtime_mode'] = runtime_mode
-
     if args.target_sysroot:
       gn_args['target_sysroot'] = args.target_sysroot
       gn_args['custom_sysroot'] = args.target_sysroot
@@ -306,7 +331,7 @@
       # attributes in release modes till the toolchain is updated.
       gn_args['skia_enable_api_available_macro'] = args.runtime_mode != "release"
 
-    if sys.platform == 'darwin' and args.target_os not in ['android', 'fuchsia']:
+    if sys.platform == 'darwin' and args.target_os not in ['android', 'fuchsia', 'wasm']:
       # OpenGL is deprecated on macOS > 10.11.
       # This is not necessarily needed but enabling this until we have a way to
       # build a macOS metal only shell and a gl only shell.
@@ -317,7 +342,7 @@
     # Enable Vulkan on all platforms except for Android and iOS. This is just
     # to save on mobile binary size, as there's no reason the Vulkan embedder
     # features can't work on these platforms.
-    if args.target_os not in ['android', 'ios']:
+    if args.target_os not in ['android', 'ios', 'wasm']:
       gn_args['skia_use_vulkan'] = True
       gn_args['shell_enable_vulkan'] = True
       # Disable VMA's use of std::shared_mutex in environments where the
@@ -362,14 +387,7 @@
       # dart_platform_sdk=True means exclude web-related files, e.g. dart2js,
       # dartdevc, web SDK kernel and source files.
       gn_args['dart_platform_sdk'] = not args.full_dart_sdk
-    gn_args['full_dart_sdk'] = args.full_dart_sdk
-
-    # Desktop embeddings can have more dependencies than the engine library,
-    # which can be problematic in some build environments (e.g., building on
-    # Linux will bring in pkg-config dependencies at generation time). These
-    # flags allow preventing those those targets from being part of the build
-    # tree.
-    gn_args['enable_desktop_embeddings'] = not args.disable_desktop_embeddings
+    
     if args.build_glfw_shell is not None:
       gn_args['build_glfw_shell'] = args.build_glfw_shell
 
@@ -399,11 +417,6 @@
         sys.exit(1)
       gn_args['enable_vulkan_validation_layers'] = True
 
-    gn_args['dart_version_git_info'] = not args.no_dart_version_git_info
-
-    # Overrides whether Boring SSL is compiled with system as. Only meaningful
-    # on Android.
-    gn_args['bssl_use_clang_integrated_as'] = True
 
     # Enable pointer compression on 64-bit mobile targets.
     if args.target_os in ['android', 'ios'] and gn_args['target_cpu'] in ['x64' , 'arm64']:
@@ -424,7 +437,7 @@
 
 def parse_args(args):
   args = args[1:]
-  parser = argparse.ArgumentParser(description='A script run` gn gen`.')
+  parser = argparse.ArgumentParser(description='A script to run `gn gen`.')
 
   parser.add_argument('--unoptimized', default=False, action='store_true')
 
@@ -439,7 +452,7 @@
   parser.add_argument('--full-dart-debug', default=False, action='store_true', help='Implies --dart-debug ' +
       'and also disables optimizations in the Dart VM making it easier to step through VM code in the debugger.')
 
-  parser.add_argument('--target-os', type=str, choices=['android', 'ios', 'mac', 'linux', 'fuchsia', 'win', 'winuwp'])
+  parser.add_argument('--target-os', type=str, choices=['android', 'ios', 'mac', 'linux', 'fuchsia', 'wasm', 'win', 'winuwp'])
   parser.add_argument('--android', dest='target_os', action='store_const', const='android')
   parser.add_argument('--android-cpu', type=str, choices=['arm', 'x64', 'x86', 'arm64'], default='arm')
   parser.add_argument('--ios', dest='target_os', action='store_const', const='ios')
@@ -449,6 +462,7 @@
   parser.add_argument('--simulator', action='store_true', default=False)
   parser.add_argument('--linux', dest='target_os', action='store_const', const='linux')
   parser.add_argument('--fuchsia', dest='target_os', action='store_const', const='fuchsia')
+  parser.add_argument('--wasm', dest='target_os', action='store_const', const='wasm')
   parser.add_argument('--windows', dest='target_os', action='store_const', const='win')
   parser.add_argument('--winuwp', dest='target_os', action='store_const', const='winuwp')
 
diff --git a/tools/licenses/lib/filesystem.dart b/tools/licenses/lib/filesystem.dart
index 4cd01fc..a95b5f1 100644
--- a/tools/licenses/lib/filesystem.dart
+++ b/tools/licenses/lib/filesystem.dart
@@ -130,6 +130,7 @@
     // Giant data files
     case 'icudtl_dat.S':
     case 'icudtl.dat':
+    case 'icudtl.dat.hash':
       return FileType.binary;
   }
   switch (path.extension(name)) {