diff --git a/DEPS b/DEPS
index 34efb5d..86d2279 100644
--- a/DEPS
+++ b/DEPS
@@ -187,7 +187,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
-  'angle_revision': 'dd29370cdd4111e8a2099e60d99b2343ae12d49e',
+  'angle_revision': '93040c8d0d4a90b9edb9a16993812988ceb50027',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
@@ -238,7 +238,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
-  'catapult_revision': '395a627b8ad8b48dc4119fb6d309d29ec5d5fda0',
+  'catapult_revision': '56f8fb61b9d5db9fc96313d1675e7c28920b6e8c',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -246,7 +246,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling devtools-frontend
   # and whatever else without interference from each other.
-  'devtools_frontend_revision': 'a10679ba4c5862bac9bb3ca30d2172c94417eafa',
+  'devtools_frontend_revision': '76336cdd0411308cf1971b723614fb0477046435',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libprotobuf-mutator
   # and whatever else without interference from each other.
@@ -286,7 +286,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'spv_tools_revision': 'f20c0d7971c6705a99b91d7a0278eabbc5d91195',
+  'spv_tools_revision': 'e95fbfb1f509ad7a7fdfb72ac35fe412d72fc4a4',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -302,7 +302,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'dawn_revision': '2b24fab368ae39f1ef9df5cb3c3e756f5173dce1',
+  'dawn_revision': 'c00163ba0925a7de0936a0b0c8385e1986ae5a4c',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -933,7 +933,7 @@
     Var('chromium_git') + '/codecs/libgav1.git' + '@' + '638ef84819f8b3cd614dcf63378fe4814aa4cb2a',
 
   'src/third_party/glslang/src':
-    Var('chromium_git') + '/external/github.com/KhronosGroup/glslang.git' + '@' + '0b66fa3b62cb36a3bc86f5018cf92a5211b27156',
+    Var('chromium_git') + '/external/github.com/KhronosGroup/glslang.git' + '@' + 'b5757b95005bbf6b0287096c5b708c5e25645311',
 
   'src/third_party/google_toolbox_for_mac/src': {
       'url': Var('chromium_git') + '/external/github.com/google/google-toolbox-for-mac.git' + '@' + Var('google_toolbox_for_mac_revision'),
@@ -1211,7 +1211,7 @@
     Var('chromium_git') + '/external/github.com/cisco/openh264' + '@' + '6f26bce0b1c4e8ce0e13332f7c0083788def5fdf',
 
   'src/third_party/openscreen/src':
-    Var('chromium_git') + '/openscreen' + '@' + 'f954babacc6cd535d426d577d1fa0d0932539cb3',
+    Var('chromium_git') + '/openscreen' + '@' + '31665eb818aaefbb1ad706b0f2a99846125d1321',
 
   'src/third_party/openxr/src': {
     'url': Var('chromium_git') + '/external/github.com/KhronosGroup/OpenXR-SDK' + '@' + '9e97b73e7dd2bfc07745489d728f6a36665c648f',
@@ -1228,7 +1228,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' + '2da86adf0fb8fd64ae2e7ac4cc9eb7b45772a79c',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' + 'f5a94cc6837a834edde4d65e991c004a7fe99d4b',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3',
@@ -1331,7 +1331,7 @@
       'packages': [
           {
               'package': 'chromium/third_party/r8',
-              'version': 'QBuWB80TzI5JFXtwaZQbr91Ry3Lb0AmRl8kBchm5FY0C',
+              'version': 'I91wspV6GMc7l_m-k9v3-ooP-CBrK76OVc3rfnB5T7kC',
           },
       ],
       'condition': 'checkout_android',
@@ -1458,7 +1458,7 @@
   },
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '04c1b445019e10e54b96f70403d25cc54215faf3',
+    Var('webrtc_git') + '/src.git' + '@' + '52e2dbf1c11578414ffb5b155bf97a84b6ad647f',
 
   'src/third_party/libgifcodec':
      Var('skia_git') + '/libgifcodec' + '@'+  Var('libgifcodec_revision'),
@@ -1533,7 +1533,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@b6fff1b9fabd7189a8da4201cadc4dd5ea457a26',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@331b83f6375688a0b157a7ce6c01a37fcd779639',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/ash/ambient/ambient_controller.cc b/ash/ambient/ambient_controller.cc
index 0fcf3d3..704fe95 100644
--- a/ash/ambient/ambient_controller.cc
+++ b/ash/ambient/ambient_controller.cc
@@ -74,6 +74,8 @@
 
 void AmbientController::OnWidgetDestroying(views::Widget* widget) {
   refresh_timer_.Stop();
+  photo_model_.Clear();
+  weak_factory_.InvalidateWeakPtrs();
   container_view_->GetWidget()->RemoveObserver(this);
   container_view_ = nullptr;
 
@@ -170,9 +172,9 @@
     refresh_interval = base::TimeDelta::FromSeconds(5);
   }
 
-  refresh_timer_.Start(
-      FROM_HERE, refresh_interval,
-      base::BindOnce(&AmbientController::RefreshImage, base::Unretained(this)));
+  refresh_timer_.Start(FROM_HERE, refresh_interval,
+                       base::BindOnce(&AmbientController::RefreshImage,
+                                      weak_factory_.GetWeakPtr()));
 }
 
 void AmbientController::GetNextImage() {
diff --git a/ash/ambient/model/photo_model.cc b/ash/ambient/model/photo_model.cc
index 29bf8994..8f73254 100644
--- a/ash/ambient/model/photo_model.cc
+++ b/ash/ambient/model/photo_model.cc
@@ -54,6 +54,11 @@
     NotifyImagesChanged();
 }
 
+void PhotoModel::Clear() {
+  images_.clear();
+  current_image_index_ = 0;
+}
+
 gfx::ImageSkia PhotoModel::GetPrevImage() const {
   if (current_image_index_ == 0)
     return gfx::ImageSkia();
diff --git a/ash/ambient/model/photo_model.h b/ash/ambient/model/photo_model.h
index 7a8d5b8f..e41336c0 100644
--- a/ash/ambient/model/photo_model.h
+++ b/ash/ambient/model/photo_model.h
@@ -34,6 +34,9 @@
   // Add image to local storage.
   void AddNextImage(const gfx::ImageSkia& image);
 
+  // Clear local storage.
+  void Clear();
+
   // Get images from local storage. Could be null image.
   gfx::ImageSkia GetPrevImage() const;
   gfx::ImageSkia GetCurrImage() const;
diff --git a/ash/public/cpp/ambient/photo_controller.cc b/ash/public/cpp/ambient/photo_controller.cc
index 890f7d49..75e8b8a 100644
--- a/ash/public/cpp/ambient/photo_controller.cc
+++ b/ash/public/cpp/ambient/photo_controller.cc
@@ -16,13 +16,13 @@
 
 PhotoController::Topic::Topic() = default;
 
-PhotoController::Topic::~Topic() = default;
-
 PhotoController::Topic::Topic(const Topic&) = default;
 
 PhotoController::Topic& PhotoController::Topic::operator=(const Topic&) =
     default;
 
+PhotoController::Topic::~Topic() = default;
+
 // static
 PhotoController* PhotoController::Get() {
   return g_photo_controller;
diff --git a/ash/public/cpp/ambient/photo_controller.h b/ash/public/cpp/ambient/photo_controller.h
index 1269f6f..deb5a2c 100644
--- a/ash/public/cpp/ambient/photo_controller.h
+++ b/ash/public/cpp/ambient/photo_controller.h
@@ -43,10 +43,20 @@
 
   using PhotoDownloadCallback =
       base::OnceCallback<void(bool success, const gfx::ImageSkia&)>;
+  using GetSettingsCallback =
+      base::OnceCallback<void(const base::Optional<int>& topic_source)>;
+  using UpdateSettingsCallback = base::OnceCallback<void(bool success)>;
 
   // Get next image.
   virtual void GetNextImage(PhotoDownloadCallback callback) = 0;
 
+  // Get settings.
+  virtual void GetSettings(GetSettingsCallback callback) = 0;
+
+  // Update settings.
+  virtual void UpdateSettings(int topic_source,
+                              UpdateSettingsCallback callback) = 0;
+
  protected:
   PhotoController();
   virtual ~PhotoController();
diff --git a/ash/public/cpp/test/test_photo_controller.cc b/ash/public/cpp/test/test_photo_controller.cc
index 13af051..15f51ebf 100644
--- a/ash/public/cpp/test/test_photo_controller.cc
+++ b/ash/public/cpp/test/test_photo_controller.cc
@@ -22,4 +22,14 @@
   std::move(callback).Run(/*success=*/true, image);
 }
 
+void TestPhotoController::GetSettings(GetSettingsCallback callback) {
+  // 0 is the enum number for Google Photos.
+  std::move(callback).Run(/*topic_source=*/0);
+}
+
+void TestPhotoController::UpdateSettings(int topic_source,
+                                         UpdateSettingsCallback callback) {
+  std::move(callback).Run(/*success=*/true);
+}
+
 }  // namespace ash
diff --git a/ash/public/cpp/test/test_photo_controller.h b/ash/public/cpp/test/test_photo_controller.h
index cc0e2df..5beadf0 100644
--- a/ash/public/cpp/test/test_photo_controller.h
+++ b/ash/public/cpp/test/test_photo_controller.h
@@ -19,6 +19,9 @@
 
   // PhotoController:
   void GetNextImage(PhotoController::PhotoDownloadCallback callback) override;
+  void GetSettings(GetSettingsCallback callback) override;
+  void UpdateSettings(int topic_source,
+                      UpdateSettingsCallback callback) override;
 };
 
 }  // namespace ash
diff --git a/ash/shelf/scrollable_shelf_view.cc b/ash/shelf/scrollable_shelf_view.cc
index 74081ed..ed626046 100644
--- a/ash/shelf/scrollable_shelf_view.cc
+++ b/ash/shelf/scrollable_shelf_view.cc
@@ -2105,9 +2105,17 @@
 
 gfx::RoundedCornersF
 ScrollableShelfView::CalculateShelfContainerRoundedCorners() const {
-  if (!chromeos::switches::ShouldShowShelfHotseat() ||
-      !Shell::Get()->IsInTabletMode())
+  // This function may access TabletModeController during destruction of
+  // Hotseat. However, TabletModeController is destructed before Hotseat. So
+  // check the pointer explicitly here.
+  // TODO(andrewxu): reorder the destruction order in Shell::~Shell then remove
+  // the explicit check.
+  const bool is_in_tablet_mode =
+      Shell::Get()->tablet_mode_controller() && Shell::Get()->IsInTabletMode();
+
+  if (!chromeos::switches::ShouldShowShelfHotseat() || !is_in_tablet_mode) {
     return gfx::RoundedCornersF();
+  }
 
   const bool is_horizontal_alignment = GetShelf()->IsHorizontalAlignment();
   const float radius = (is_horizontal_alignment ? height() : width()) / 2.f;
diff --git a/build/config/android/config.gni b/build/config/android/config.gni
index 0df2b57..f63f5f3 100644
--- a/build/config/android/config.gni
+++ b/build/config/android/config.gni
@@ -68,6 +68,9 @@
     }
   }
 
+  # Our build rules support only KitKat+.
+  default_min_sdk_version = 19
+
   if (!defined(default_android_ndk_root)) {
     default_android_ndk_root = "//third_party/android_ndk"
     default_android_ndk_version = "r20"
@@ -232,7 +235,7 @@
     java_warnings_as_errors = !is_java_debug
 
     # Desugar with D8 rather than Desugar tool.
-    desugar_with_d8 = false
+    desugar_with_d8 = is_java_debug
   }
 
   # Path to where selected build variables are written to.
diff --git a/build/config/android/internal_rules.gni b/build/config/android/internal_rules.gni
index 79ffa1a..53137fd0 100644
--- a/build/config/android/internal_rules.gni
+++ b/build/config/android/internal_rules.gni
@@ -858,7 +858,7 @@
         lint_suppressions_file = "//build/android/lint/suppressions.xml"
       }
 
-      _min_sdk_version = 19
+      _min_sdk_version = default_min_sdk_version
       if (defined(invoker.min_sdk_version)) {
         _min_sdk_version = invoker.min_sdk_version
       }
@@ -1146,6 +1146,10 @@
   #   disable_incremental: Disable incremental dexing.
   template("dex") {
     assert(defined(invoker.output))
+    _min_sdk_version = default_min_sdk_version
+    if (defined(invoker.min_sdk_version)) {
+      _min_sdk_version = invoker.min_sdk_version
+    }
 
     _proguard_enabled =
         defined(invoker.proguard_enabled) && invoker.proguard_enabled
@@ -1153,15 +1157,20 @@
         _proguard_enabled && !defined(invoker.proguard_jar_path)
     _enable_multidex =
         !defined(invoker.enable_multidex) || invoker.enable_multidex
-    _enable_main_dex_list =
-        _enable_multidex &&
-        (!defined(invoker.min_sdk_version) || invoker.min_sdk_version < 21)
+    _enable_main_dex_list = _enable_multidex && _min_sdk_version < 21
     if (!_enable_main_dex_list) {
       if (defined(invoker.negative_main_dex_globs)) {
         not_needed(invoker, [ "negative_main_dex_globs" ])
       }
     }
 
+    # When using d8 to desugar, the dex merge step must not be set to a higher
+    # minSdkVersion than the libraries in order for the proper backported
+    # methods to be included.
+    if (invoker.enable_desugar && defined(invoker.input_dex_filearg)) {
+      _min_sdk_version = default_min_sdk_version
+    }
+
     assert(!_proguard_enabled || !(defined(invoker.input_dex_filearg) ||
                                        defined(invoker.input_classes_filearg) ||
                                        defined(invoker.input_class_jars)),
@@ -1214,6 +1223,7 @@
         args = [
           "--proguard-configs=@FileArg($_rebased_build_config:deps_info:proguard_all_configs)",
           "--input-paths=@FileArg($_rebased_build_config:deps_info:java_runtime_classpath)",
+          "--min-api=$_min_sdk_version",
         ]
         if (defined(invoker.proguard_args)) {
           args += invoker.proguard_args
@@ -1232,13 +1242,6 @@
           args += [ "--apply-mapping=$_rebased_apply_mapping_path" ]
         }
 
-        if (defined(invoker.min_sdk_version)) {
-          args += [
-            "--min-api",
-            "${invoker.min_sdk_version}",
-          ]
-        }
-
         if (_proguarding_with_r8 && _enable_main_dex_list) {
           if (defined(invoker.extra_main_dex_proguard_config)) {
             args += [
@@ -1368,6 +1371,7 @@
           rebase_path(depfile, root_build_dir),
           "--output",
           rebase_path(outputs[0], root_build_dir),
+          "--min-api=$_min_sdk_version",
         ]
 
         if (_proguard_enabled) {
@@ -1441,13 +1445,6 @@
           args += [ "--release" ]
         }
 
-        if (defined(invoker.min_sdk_version)) {
-          args += [
-            "--min-api",
-            "${invoker.min_sdk_version}",
-          ]
-        }
-
         if (invoker.enable_desugar) {
           args += [
             "--desugar",
diff --git a/build/config/android/rules.gni b/build/config/android/rules.gni
index c7414ad..f4af27f 100644
--- a/build/config/android/rules.gni
+++ b/build/config/android/rules.gni
@@ -1363,7 +1363,7 @@
         android_manifest = "//build/android/AndroidManifest.xml"
       }
       arsc_output = _resource_arsc_output
-      min_sdk_version = 19
+      min_sdk_version = default_min_sdk_version
       target_sdk_version = 23
     }
 
@@ -1852,7 +1852,7 @@
       supports_android = true
       requires_android = true
       if (!defined(invoker.min_sdk_version)) {
-        min_sdk_version = 19
+        min_sdk_version = default_min_sdk_version
       }
 
       if (!defined(jar_excluded_patterns)) {
@@ -2104,7 +2104,7 @@
   #     static_library_provider target will provide the dex file(s) for this
   #     target.
   #   min_sdk_version: The minimum Android SDK version this target supports.
-  #     Optional, default 19.
+  #     Optional, default $default_min_sdk_version.
   #   target_sdk_version: The target Android SDK version for this target.
   #     Optional, default 23.
   #   max_sdk_version: The maximum Android SDK version this target supports.
@@ -2137,7 +2137,7 @@
     _build_config = "$target_gen_dir/$target_name.build_config"
     _build_config_target = "$target_name$build_config_target_suffix"
 
-    _min_sdk_version = 19
+    _min_sdk_version = default_min_sdk_version
     _target_sdk_version = 23
     if (defined(invoker.min_sdk_version)) {
       _min_sdk_version = invoker.min_sdk_version
diff --git a/build/config/fuchsia/generate_runner_scripts.gni b/build/config/fuchsia/generate_runner_scripts.gni
index 3d45f12..1afbfa4 100644
--- a/build/config/fuchsia/generate_runner_scripts.gni
+++ b/build/config/fuchsia/generate_runner_scripts.gni
@@ -81,12 +81,17 @@
     }
     wrapper_script = generated_run_pkg_script_path
 
-    deps = [ invoker.package ]
-    if (defined(invoker.deps)) {
-      deps += invoker.deps
-    }
+    data_deps = [
+      invoker.package,
 
-    data_deps = [ invoker.package ]
+      # Runner scripts require access to "ids.txt" for symbolization, and to
+      # the "package" from which to get the name & version to deploy, which
+      # are outputs of the archive manifest generation action.
+      invoker.package + "__archive-manifest",
+    ]
+    if (defined(invoker.data_deps)) {
+      data_deps += invoker.data_deps
+    }
 
     # Declares the files that are needed for test execution on the
     # swarming test client.
@@ -133,7 +138,10 @@
         package_dep_target = package_dep[0]
         package_dep_name = package_dep[1]
 
-        deps += [ package_dep_target ]
+        data_deps += [
+          package_dep_target,
+          package_dep_target + "__archive-manifest",
+        ]
         package_dep_path = rebase_path(
                 get_label_info(package_dep_target, "target_gen_dir") + "/" +
                     package_dep_name + "/" + package_dep_name + ".far",
@@ -178,9 +186,9 @@
     executable = rebase_path("//build/fuchsia/deploy_to_amber_repo.py")
     wrapper_script = generated_install_pkg_script_path
 
-    deps = [ invoker.package ]
-    if (defined(invoker.deps)) {
-      deps += invoker.deps
+    data_deps = [ invoker.package ]
+    if (defined(invoker.data_deps)) {
+      data_deps += invoker.data_deps
     }
 
     # Build a list of all packages to install, and pass the list to the runner
@@ -191,7 +199,7 @@
         package_dep_target = package_dep[0]
         package_dep_name = package_dep[1]
 
-        deps += [ package_dep_target ]
+        data_deps += [ package_dep_target ]
         package_dep_path = rebase_path(
                 get_label_info(package_dep_target, "target_gen_dir") + "/" +
                     package_dep_name + "/" + package_dep_name + ".far",
diff --git a/build/config/fuchsia/package.gni b/build/config/fuchsia/package.gni
index 8fe34fe0..3324738 100644
--- a/build/config/fuchsia/package.gni
+++ b/build/config/fuchsia/package.gni
@@ -2,8 +2,12 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-import("//build/config/sysroot.gni")
+import("//third_party/fuchsia-sdk/sdk/build/component.gni")
+import("//third_party/fuchsia-sdk/sdk/build/package.gni")
 
+# DEPRECATED: Use the Fuchsia SDK's fuchsia_component() and fuchsia_package()
+# templates directly, in new code.
+#
 # Creates a Fuchsia .far package file containing a Fuchsia component.
 #
 # Parameters are:
@@ -28,210 +32,97 @@
 #
 # TODO(https://crbug.com/1050703): Migrate consumers to GN SDK equivalents.
 template("cr_fuchsia_package") {
-  pkg = {
-    forward_variables_from(invoker, "*")
+  assert(defined(invoker.binary))
 
-    if (defined(package_name_override)) {
-      package_name = package_name_override
-    } else {
-      package_name = invoker.target_name
-    }
+  if (defined(invoker.package_name_override)) {
+    _package_name = invoker.package_name_override
+  } else {
+    _package_name = invoker.target_name
+  }
 
-    if (defined(archive_name_override)) {
-      archive_filename = archive_name_override
-    } else {
-      archive_filename = package_name
-    }
+  _package_contents = [ invoker.binary ]
+  if (defined(invoker.deps)) {
+    _package_contents += invoker.deps
+  }
 
-    if (!defined(manifest)) {
+  _component_target = target_name + "__cr-component"
+  _package_components = [ ":${_component_target}" ]
+
+  # Declare the primary component for this package.
+  fuchsia_component(_component_target) {
+    forward_variables_from(invoker,
+                           [
+                             #"data",
+                             "testonly",
+                           ])
+
+    if (!defined(invoker.manifest)) {
       assert(testonly == true)
 
       # TODO(1019938): switch the default to tests.cmx which doesn't request
       # the deprecated-ambient-replace-as-executable feature.
-      manifest = "//build/config/fuchsia/tests-with-exec.cmx"
+      manifest_fragment = "//build/config/fuchsia/tests-with-exec.cmx"
+    } else {
+      manifest_fragment = invoker.manifest
+    }
+
+    manifest_fragment_data = read_file(manifest_fragment, "json")
+    if (!defined(manifest_fragment_data.program)) {
+      manifest_fragment_data.program = {
+        binary = get_label_info(invoker.binary, "name")
+      }
+    }
+    manifest = "$target_out_dir/" + target_name + "__component-manifest.cmx"
+    write_file(manifest, manifest_fragment_data, "json")
+
+    if (defined(invoker.component_name_override)) {
+      manifest_output_name = "${invoker.component_name_override}"
+    } else {
+      manifest_output_name = "${_package_name}"
+    }
+
+    data_deps = _package_contents
+  }
+
+  # Bundle manifests providing additional entrypoints into the package.
+  if (defined(invoker.additional_manifests)) {
+    foreach(filename, invoker.additional_manifests) {
+      _additional_component_target = target_name + "_" + filename
+      _package_components += [ ":${_additional_component_target}" ]
+      fuchsia_component(_additional_component_target) {
+        forward_variables_from(invoker, [ "testonly" ])
+        data_deps = _package_contents
+        manifest = filename
+      }
     }
   }
-  assert(defined(pkg.binary))
 
-  _pm_tool_path = "//third_party/fuchsia-sdk/sdk/tools/pm"
-
-  _pkg_out_dir = "${target_gen_dir}/${pkg.archive_filename}"
-  _runtime_deps_file = "$_pkg_out_dir/${pkg.archive_filename}.runtime_deps"
-  _archive_manifest = "$_pkg_out_dir/${pkg.archive_filename}.archive_manifest"
-  _build_ids_file = "$_pkg_out_dir/ids.txt"
-  _meta_far_file = "$_pkg_out_dir/meta.far"
-  _combined_far_file = "$_pkg_out_dir/${pkg.package_name}-0.far"
-  _final_far_file = "$_pkg_out_dir/${pkg.archive_filename}.far"
-  _package_info_path = "$_pkg_out_dir/package"
-
-  if (defined(pkg.component_name_override)) {
-    _generated_cmx = "$_pkg_out_dir/${pkg.component_name_override}.cmx"
+  # Generate a Fuchsia ARchive (FAR) of the requested name.
+  if (defined(invoker.archive_name_override)) {
+    _archive_name = invoker.archive_name_override
   } else {
-    _generated_cmx = "$_pkg_out_dir/${pkg.package_name}.cmx"
+    _archive_name = _package_name
   }
 
-  _write_manifest_target = "${pkg.package_name}__write_manifest"
-  _package_target = "${pkg.package_name}__pkg"
-  _bundle_target = "${pkg.package_name}__bundle"
+  if (_archive_name != _package_name) {
+    _archive_target = target_name + "__cr-archive"
 
-  # Generates a manifest file based on the GN runtime deps
-  # suitable for "pm" tool consumption.
-  action(_write_manifest_target) {
-    _depfile = "${target_gen_dir}/${target_name}_stamp.d"
-
-    forward_variables_from(invoker,
-                           [
-                             "data",
-                             "deps",
-                             "testonly",
-                           ])
-
-    script = "//build/config/fuchsia/prepare_package_inputs.py"
-
-    inputs = [
-      _runtime_deps_file,
-      pkg.manifest,
-    ]
-
-    outputs = [
-      _archive_manifest,
-      _build_ids_file,
-      _generated_cmx,
-    ]
-
-    if (!defined(deps)) {
-      deps = []
+    copy(target_name) {
+      deps = [ ":${_archive_target}" ]
+      _pkg_out_dir = "${target_gen_dir}/${_package_name}"
+      sources = [ "${_pkg_out_dir}/${_package_name}.far" ]
+      outputs = [ "${_pkg_out_dir}/${_archive_name}.far" ]
     }
-    deps += [ pkg.binary ]
-    data_deps = deps
-
-    # Use a depfile to trigger package rebuilds if any of the files (static
-    # assets, shared libraries, etc.) included by the package have changed.
-    depfile = _depfile
-
-    args = [
-      "--root-dir",
-      rebase_path("//", root_build_dir),
-      "--out-dir",
-      rebase_path(root_out_dir, root_build_dir),
-      "--app-name",
-      pkg.package_name,
-      "--app-filename",
-      get_label_info(pkg.binary, "name"),
-      "--manifest-input-path",
-      rebase_path(pkg.manifest, root_build_dir),
-      "--runtime-deps-file",
-      rebase_path(_runtime_deps_file, root_build_dir),
-      "--depfile-path",
-      rebase_path(_depfile, root_build_dir),
-      "--package-manifest-path",
-      rebase_path(_archive_manifest, root_build_dir),
-      "--component-manifest-path",
-      rebase_path(_generated_cmx, root_build_dir),
-      "--build-ids-file",
-      rebase_path(_build_ids_file, root_build_dir),
-    ]
-
-    if (defined(pkg.excluded_files)) {
-      foreach(filename, pkg.excluded_files) {
-        args += [
-          "--exclude-file",
-          filename,
-        ]
-      }
-    }
-
-    if (defined(pkg.additional_manifests)) {
-      foreach(filename, pkg.additional_manifests) {
-        args += [
-          "--additional-manifest",
-          rebase_path(filename),
-        ]
-      }
-    }
-
-    write_runtime_deps = _runtime_deps_file
+  } else {
+    _archive_target = target_name
   }
 
-  # Creates a signed Fuchsia metadata package.
-  action(_package_target) {
+  fuchsia_package(_archive_target) {
     forward_variables_from(invoker, [ "testonly" ])
-
-    script = "//build/gn_run_binary.py"
-
-    deps = [ ":$_write_manifest_target" ]
-
-    inputs = [
-      # Depend on the SDK hash, to ensure rebuild if the SDK tools change.
-      "//third_party/fuchsia-sdk/sdk/.hash",
-    ]
-
-    if (defined(pkg.additional_manifests)) {
-      inputs += pkg.additional_manifests
+    package_name = _package_name
+    if (defined(invoker.excluded_files)) {
+      excluded_files = invoker.excluded_files
     }
-
-    outputs = [ _meta_far_file ]
-
-    args = [
-      rebase_path(_pm_tool_path, root_build_dir),
-      "-o",
-      rebase_path(_pkg_out_dir, root_build_dir),
-      "-m",
-      rebase_path(_archive_manifest, root_build_dir),
-      "build",
-    ]
-  }
-
-  # Creates a package containing the metadata archive and blob data.
-  action(_bundle_target) {
-    forward_variables_from(invoker, [ "testonly" ])
-
-    script = "//build/gn_run_binary.py"
-
-    deps = [
-      ":$_package_target",
-      ":$_write_manifest_target",
-    ]
-
-    inputs = [
-      # Depend on the SDK hash, to ensure rebuild if the SDK tools change.
-      "//third_party/fuchsia-sdk/sdk/.hash",
-      _meta_far_file,
-      _archive_manifest,
-    ]
-
-    outputs = [ _combined_far_file ]
-
-    args = [
-      rebase_path(_pm_tool_path, root_build_dir),
-      "-o",
-      rebase_path(_pkg_out_dir, root_build_dir),
-      "-m",
-      rebase_path(_archive_manifest, root_build_dir),
-      "archive",
-    ]
-  }
-
-  # Copies the archive to a well-known path.
-  # TODO(kmarshall): Use a 'pm' output flag to write directly to the desired
-  # file path instead.
-  copy(target_name) {
-    forward_variables_from(invoker, [ "testonly" ])
-
-    # Allows dependent targets to make use of "ids.txt".
-    public_deps = [ ":$_write_manifest_target" ]
-
-    deps = [ ":$_bundle_target" ]
-
-    data = [
-      _final_far_file,
-
-      # Files specified here so that they can be read by isolated testbots.
-      _package_info_path,
-      _build_ids_file,
-    ]
-
-    sources = [ _combined_far_file ]
-    outputs = [ _final_far_file ]
+    deps = _package_components
   }
 }
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1
index 83c234d..77cf61c 100644
--- a/build/fuchsia/linux.sdk.sha1
+++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@
-0.20200402.2.2
\ No newline at end of file
+0.20200403.0.1
\ No newline at end of file
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1
index b486afd..51acab0f 100644
--- a/build/fuchsia/mac.sdk.sha1
+++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@
-0.20200402.2.3
\ No newline at end of file
+0.20200402.3.1
\ No newline at end of file
diff --git a/build/fuchsia/run_package.py b/build/fuchsia/run_package.py
index cdb3b71..8e18a57 100644
--- a/build/fuchsia/run_package.py
+++ b/build/fuchsia/run_package.py
@@ -20,7 +20,6 @@
 import threading
 import uuid
 
-from symbolizer import RunSymbolizer
 from symbolizer import SymbolizerFilter
 
 FAR = common.GetHostToolPathFromPlatform('far')
@@ -37,36 +36,6 @@
                                 stdout=subprocess.PIPE)
 
 
-def _BuildIdsPaths(package_paths):
-  """Generate build ids paths for symbolizer processes."""
-  build_ids_paths = map(
-      lambda package_path: os.path.join(
-          os.path.dirname(package_path), 'ids.txt'),
-      package_paths)
-  return build_ids_paths
-
-
-def StartSystemLogReader(target, package_paths, isolated_outputs_dir):
-  """Start a system log reader as a long-running SSH task.
-
-  Returns listener and symbolizer popen objects so that the caller can control
-  the popen process lifetimes."""
-  logging.info('Starting system log reader')
-
-  listener_proc = target.RunCommandPiped(['log_listener'],
-                                         stdout=subprocess.PIPE,
-                                         stderr=subprocess.STDOUT)
-
-  listener_log_path = os.path.join(isolated_outputs_dir, 'log_listener_log')
-  build_ids_paths = _BuildIdsPaths(package_paths)
-  listener_log = open(listener_log_path,'w', buffering=1)
-  symbolizer_proc = RunSymbolizer(listener_proc.stdout,
-                                  listener_log,
-                                  build_ids_paths)
-
-  return (listener_proc, symbolizer_proc)
-
-
 class MergedInputStream(object):
   """Merges a number of input streams into a UNIX pipe on a dedicated thread.
   Terminates when the file descriptor of the primary stream (the first in
@@ -225,7 +194,10 @@
         output_stream = process.stdout
 
       # Run the log data through the symbolizer process.
-      build_ids_paths = _BuildIdsPaths(package_paths)
+      build_ids_paths = map(
+          lambda package_path: os.path.join(
+              os.path.dirname(package_path), 'ids.txt'),
+          package_paths)
       output_stream = SymbolizerFilter(output_stream, build_ids_paths)
 
       for next_line in output_stream:
diff --git a/build/fuchsia/test_runner.py b/build/fuchsia/test_runner.py
index c315af7..02fb07f 100755
--- a/build/fuchsia/test_runner.py
+++ b/build/fuchsia/test_runner.py
@@ -18,7 +18,7 @@
 
 from common_args import AddCommonArgs, ConfigureLogging, GetDeploymentTargetForArgs
 from net_test_server import SetupTestServer
-from run_package import RunPackage, RunPackageArgs, StartSystemLogReader
+from run_package import RunPackage, RunPackageArgs
 
 DEFAULT_TEST_SERVER_CONCURRENCY = 4
 
@@ -146,12 +146,6 @@
                      for_package=args.package_name)
       child_args.append('--test-launcher-filter-file=' + TEST_FILTER_PATH)
 
-    isolated_outputs_dir = os.path.dirname(args.system_log_file)
-    if os.path.isdir(isolated_outputs_dir):
-      # Store logging popen objects so that they live as long as the target.
-      system_log_procs = StartSystemLogReader(target, args.package,
-                                              isolated_outputs_dir)
-
     test_server = None
     if args.enable_test_server:
       assert test_concurrency
diff --git a/chrome/MAJOR_BRANCH_DATE b/chrome/MAJOR_BRANCH_DATE
index ec637d7c..16c8984f 100644
--- a/chrome/MAJOR_BRANCH_DATE
+++ b/chrome/MAJOR_BRANCH_DATE
@@ -1 +1 @@
-MAJOR_BRANCH_DATE=2020-03-13
+MAJOR_BRANCH_DATE=2020-04-03
diff --git a/chrome/VERSION b/chrome/VERSION
index 012d275..a2444aa 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
-MAJOR=83
+MAJOR=84
 MINOR=0
-BUILD=4103
+BUILD=4104
 PATCH=0
diff --git a/chrome/android/chrome_junit_test_java_sources.gni b/chrome/android/chrome_junit_test_java_sources.gni
index 5632c4a..011f399 100644
--- a/chrome/android/chrome_junit_test_java_sources.gni
+++ b/chrome/android/chrome_junit_test_java_sources.gni
@@ -174,6 +174,7 @@
   "junit/src/org/chromium/chrome/browser/omnibox/voice/AssistantVoiceSearchServiceUnitTest.java",
   "junit/src/org/chromium/chrome/browser/page_info/PermissionParamsListBuilderUnitTest.java",
   "junit/src/org/chromium/chrome/browser/partnerbookmarks/PartnerBookmarksFaviconThrottleTest.java",
+  "junit/src/org/chromium/chrome/browser/partnerbookmarks/PartnerBookmarksReaderTest.java",
   "junit/src/org/chromium/chrome/browser/password_manager/settings/DialogManagerTest.java",
   "junit/src/org/chromium/chrome/browser/password_manager/settings/EnsureAsyncPostingRule.java",
   "junit/src/org/chromium/chrome/browser/password_manager/settings/ExportWarningDialogFragmentTest.java",
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
index 5c4a3862..89fa08d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
@@ -209,11 +209,6 @@
      */
     static final int NO_TOOLBAR_LAYOUT = -1;
 
-    /**
-     * Timeout in ms for reading PartnerBrowserCustomizations provider.
-     */
-    private static final int PARTNER_BROWSER_CUSTOMIZATIONS_TIMEOUT_MS = 10000;
-
     private C mComponent;
 
     protected ObservableSupplierImpl<TabModelSelector> mTabModelSelectorSupplier =
@@ -1099,8 +1094,7 @@
 
         if (mPartnerBrowserRefreshNeeded) {
             mPartnerBrowserRefreshNeeded = false;
-            PartnerBrowserCustomizations.getInstance().initializeAsync(
-                    getApplicationContext(), PARTNER_BROWSER_CUSTOMIZATIONS_TIMEOUT_MS);
+            PartnerBrowserCustomizations.getInstance().initializeAsync(getApplicationContext());
             PartnerBrowserCustomizations.getInstance().setOnInitializeAsyncFinished(() -> {
                 if (PartnerBrowserCustomizations.isIncognitoDisabled()) {
                     terminateIncognitoSession();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/LaunchIntentDispatcher.java b/chrome/android/java/src/org/chromium/chrome/browser/LaunchIntentDispatcher.java
index 2d99f5d..1064bcb3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/LaunchIntentDispatcher.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/LaunchIntentDispatcher.java
@@ -68,12 +68,6 @@
 
     private static final String TAG = "ActivitiyDispatcher";
 
-    /**
-     * Timeout in ms for reading PartnerBrowserCustomizations provider. We do not trust third party
-     * provider by default.
-     */
-    private static final int PARTNER_BROWSER_CUSTOMIZATIONS_TIMEOUT_MS = 10000;
-
     private final Activity mActivity;
     private final Intent mIntent;
     private final boolean mIsCustomTabIntent;
@@ -151,7 +145,7 @@
         // We want to initialize early because when there are no tabs to restore, we should possibly
         // show homepage, which might require reading PartnerBrowserCustomizations provider.
         PartnerBrowserCustomizations.getInstance().initializeAsync(
-                mActivity.getApplicationContext(), PARTNER_BROWSER_CUSTOMIZATIONS_TIMEOUT_MS);
+                mActivity.getApplicationContext());
 
         int tabId = IntentUtils.safeGetIntExtra(
                 mIntent, IntentHandler.TabOpenType.BRING_TAB_TO_FRONT_STRING, Tab.INVALID_TAB_ID);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/partnerbookmarks/PartnerBookmarksReader.java b/chrome/android/java/src/org/chromium/chrome/browser/partnerbookmarks/PartnerBookmarksReader.java
index 3bd4263..56f44dd 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/partnerbookmarks/PartnerBookmarksReader.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/partnerbookmarks/PartnerBookmarksReader.java
@@ -12,6 +12,7 @@
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.task.AsyncTask;
 import org.chromium.chrome.browser.AppHooks;
+import org.chromium.chrome.browser.partnercustomizations.PartnerBrowserCustomizations;
 import org.chromium.ui.base.ViewUtils;
 
 import java.util.HashSet;
@@ -57,6 +58,7 @@
     @GuardedBy("mProgressLock")
     private boolean mFaviconsFetchedFromServer;
     private boolean mFinishedReading;
+    private boolean mFinishedResolvingBrowserCustomizations;
 
     /**
      * Observer for listeners to receive updates when changes are made to the favicon cache.
@@ -90,12 +92,23 @@
     /**
      * Creates the instance of the reader.
      * @param context A Context object.
+     * @param browserCustomizations Provides status of partner customizations.
      */
-    public PartnerBookmarksReader(Context context) {
+    public PartnerBookmarksReader(
+            Context context, PartnerBrowserCustomizations browserCustomizations) {
         mContext = context;
         mNativePartnerBookmarksReader =
                 PartnerBookmarksReaderJni.get().init(PartnerBookmarksReader.this);
-        initializeAndDisableEditingIfNecessary();
+        if (!browserCustomizations.isInitialized()) {
+            browserCustomizations.initializeAsync(context);
+        }
+        browserCustomizations.setOnInitializeAsyncFinished(() -> {
+            if (browserCustomizations.isBookmarksEditingDisabled()) {
+                PartnerBookmarksReaderJni.get().disablePartnerBookmarksEditing();
+            }
+            mFinishedResolvingBrowserCustomizations = true;
+            maybeMarkCreationComplete();
+        });
     }
 
     /**
@@ -159,9 +172,7 @@
                     }
                     mFaviconThrottle.onFaviconFetched(url, result);
                     --mNumFaviconsInProgress;
-                    if (mNumFaviconsInProgress == 0 && mFinishedReading) {
-                        shutDown();
-                    }
+                    if (canShutdown()) shutDown();
                 }
             }
 
@@ -183,14 +194,23 @@
      * down the bookmark reader.
      */
     protected void onBookmarksRead() {
+        mFinishedReading = true;
+        maybeMarkCreationComplete();
+        synchronized (mProgressLock) {
+            if (canShutdown()) shutDown();
+        }
+    }
+
+    private void maybeMarkCreationComplete() {
+        if (!mFinishedReading || !mFinishedResolvingBrowserCustomizations) return;
         PartnerBookmarksReaderJni.get().partnerBookmarksCreationComplete(
                 mNativePartnerBookmarksReader, PartnerBookmarksReader.this);
-        mFinishedReading = true;
-        synchronized (mProgressLock) {
-            if (mNumFaviconsInProgress == 0) {
-                shutDown();
-            }
-        }
+    }
+
+    @GuardedBy("mProgressLock")
+    private boolean canShutdown() {
+        return mNumFaviconsInProgress == 0 && mFinishedReading
+                && mFinishedResolvingBrowserCustomizations;
     }
 
     /**
@@ -357,19 +377,6 @@
         }
     }
 
-    /**
-     * Disables partner bookmarks editing.
-     */
-    public static void disablePartnerBookmarksEditing() {
-        sForceDisableEditing = true;
-        if (sInitialized) PartnerBookmarksReaderJni.get().disablePartnerBookmarksEditing();
-    }
-
-    private static void initializeAndDisableEditingIfNecessary() {
-        sInitialized = true;
-        if (sForceDisableEditing) disablePartnerBookmarksEditing();
-    }
-
     @NativeMethods
     interface Natives {
         long init(PartnerBookmarksReader caller);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/partnerbookmarks/PartnerBookmarksShim.java b/chrome/android/java/src/org/chromium/chrome/browser/partnerbookmarks/PartnerBookmarksShim.java
index f356de4..819eaf3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/partnerbookmarks/PartnerBookmarksShim.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/partnerbookmarks/PartnerBookmarksShim.java
@@ -8,6 +8,7 @@
 import android.content.pm.ApplicationInfo;
 
 import org.chromium.chrome.browser.ChromeVersionInfo;
+import org.chromium.chrome.browser.partnercustomizations.PartnerBrowserCustomizations;
 
 /**
  * The Java counterpart for the C++ partner bookmarks shim.
@@ -30,7 +31,8 @@
         if (sIsReadingAttempted) return;
         sIsReadingAttempted = true;
 
-        PartnerBookmarksReader reader = new PartnerBookmarksReader(context);
+        PartnerBookmarksReader reader =
+                new PartnerBookmarksReader(context, PartnerBrowserCustomizations.getInstance());
 
         boolean systemOrPreStable =
                 (context.getApplicationInfo().flags & ApplicationInfo.FLAG_SYSTEM) == 1
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/partnercustomizations/PartnerBrowserCustomizations.java b/chrome/android/java/src/org/chromium/chrome/browser/partnercustomizations/PartnerBrowserCustomizations.java
index bae1d5a..ced20b4 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/partnercustomizations/PartnerBrowserCustomizations.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/partnercustomizations/PartnerBrowserCustomizations.java
@@ -24,7 +24,6 @@
 import org.chromium.chrome.browser.ChromeVersionInfo;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
 import org.chromium.chrome.browser.ntp.NewTabPage;
-import org.chromium.chrome.browser.partnerbookmarks.PartnerBookmarksReader;
 import org.chromium.components.embedder_support.util.UrlConstants;
 import org.chromium.components.embedder_support.util.UrlUtilities;
 import org.chromium.content_public.browser.UiThreadTaskTraits;
@@ -39,6 +38,9 @@
     private static final String TAG = "PartnerCustomize";
     private static final String PROVIDER_AUTHORITY = "com.android.partnerbrowsercustomizations";
 
+    /** Default timeout in ms for reading PartnerBrowserCustomizations provider. */
+    private static final int DEFAULT_TIMEOUT_MS = 10_000;
+
     private static final int HOMEPAGE_URL_MAX_LENGTH = 1000;
     // Private homepage structure.
     @VisibleForTesting
@@ -211,8 +213,7 @@
     /**
      * @return Whether partner bookmarks editing is disabled by the partner.
      */
-    @VisibleForTesting
-    boolean isBookmarksEditingDisabled() {
+    public boolean isBookmarksEditingDisabled() {
         return mBookmarksEditingDisabled;
     }
 
@@ -255,14 +256,23 @@
      * Constructs an async task that reads PartnerBrowserCustomization provider.
      *
      * @param context   The current application context.
+     */
+    public void initializeAsync(final Context context) {
+        initializeAsync(context, DEFAULT_TIMEOUT_MS);
+    }
+
+    /**
+     * Constructs an async task that reads PartnerBrowserCustomization provider.
+     *
+     * @param context   The current application context.
      * @param timeoutMs If initializing takes more than this time, cancels it. The unit is ms.
      */
-    public void initializeAsync(final Context context, long timeoutMs) {
+    @VisibleForTesting
+    void initializeAsync(final Context context, long timeoutMs) {
         mIsInitialized = false;
         Provider provider = AppHooks.get().getCustomizationProvider();
         // Setup an initializing async task.
         final AsyncTask<Void> initializeAsyncTask = new AsyncTask<Void>() {
-            private boolean mDisablePartnerBookmarksShim;
             private boolean mHomepageUriChanged;
 
             private void refreshHomepage() {
@@ -290,13 +300,7 @@
 
             private void refreshBookmarksEditingDisabled() {
                 try {
-                    boolean disabled = provider.isBookmarksEditingDisabled();
-                    // Only need to disable it once.
-                    if (disabled != mBookmarksEditingDisabled) {
-                        assert disabled;
-                        mDisablePartnerBookmarksShim = true;
-                    }
-                    mBookmarksEditingDisabled = disabled;
+                    mBookmarksEditingDisabled = provider.isBookmarksEditingDisabled();
                 } catch (Exception e) {
                     Log.w(TAG, "Partner disable bookmarks editing read failed : ", e);
                 }
@@ -355,11 +359,6 @@
                 if (mHomepageUriChanged && mListener != null) {
                     mListener.onHomepageUpdate();
                 }
-
-                // Disable partner bookmarks editing if necessary.
-                if (mDisablePartnerBookmarksShim) {
-                    PartnerBookmarksReader.disablePartnerBookmarksEditing();
-                }
             }
         };
 
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/partnerbookmarks/PartnerBookmarksReaderTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/partnerbookmarks/PartnerBookmarksReaderTest.java
new file mode 100644
index 0000000..84f6226
--- /dev/null
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/partnerbookmarks/PartnerBookmarksReaderTest.java
@@ -0,0 +1,170 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.partnerbookmarks;
+
+import android.content.Context;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.annotation.Config;
+
+import org.chromium.base.test.BaseRobolectricTestRunner;
+import org.chromium.base.test.util.JniMocker;
+import org.chromium.chrome.browser.partnercustomizations.PartnerBrowserCustomizations;
+
+/**
+ * Unit tests for PartnerBookmarksReader.
+ */
+@RunWith(BaseRobolectricTestRunner.class)
+@Config(manifest = Config.NONE)
+public class PartnerBookmarksReaderTest {
+    @Rule
+    public JniMocker mocker = new JniMocker();
+
+    @Mock
+    Context mContextMock;
+
+    @Mock
+    PartnerBookmarksReader.Natives mJniMock;
+
+    @Mock
+    PartnerBrowserCustomizations mBrowserCustomizations;
+
+    @Captor
+    ArgumentCaptor<Runnable> mBrowserCustomizationsInitCallback;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mocker.mock(PartnerBookmarksReaderJni.TEST_HOOKS, mJniMock);
+        Mockito.doNothing()
+                .when(mBrowserCustomizations)
+                .setOnInitializeAsyncFinished(mBrowserCustomizationsInitCallback.capture());
+    }
+
+    @Test
+    public void partnerBrowserCustomizations_BookmarkEditingDisabled_AlreadyInitialized() {
+        Mockito.when(mBrowserCustomizations.isInitialized()).thenReturn(true);
+
+        @SuppressWarnings("unused")
+        PartnerBookmarksReader reader =
+                new PartnerBookmarksReader(mContextMock, mBrowserCustomizations);
+
+        Mockito.verify(mBrowserCustomizations, Mockito.never()).initializeAsync(mContextMock);
+
+        Mockito.when(mBrowserCustomizations.isBookmarksEditingDisabled()).thenReturn(true);
+        mBrowserCustomizationsInitCallback.getValue().run();
+        Mockito.verify(mJniMock).disablePartnerBookmarksEditing();
+    }
+
+    @Test
+    public void partnerBrowserCustomizations_BookmarkEditingDisabled_NotAlreadyInitialized() {
+        Mockito.when(mBrowserCustomizations.isInitialized()).thenReturn(false);
+
+        @SuppressWarnings("unused")
+        PartnerBookmarksReader reader =
+                new PartnerBookmarksReader(mContextMock, mBrowserCustomizations);
+
+        Mockito.verify(mBrowserCustomizations).initializeAsync(mContextMock);
+
+        Mockito.when(mBrowserCustomizations.isBookmarksEditingDisabled()).thenReturn(true);
+        mBrowserCustomizationsInitCallback.getValue().run();
+        Mockito.verify(mJniMock).disablePartnerBookmarksEditing();
+    }
+
+    @Test
+    public void partnerBrowserCustomizations_BookmarkEditingAllowed_AlreadyInitialized() {
+        Mockito.when(mBrowserCustomizations.isInitialized()).thenReturn(true);
+
+        @SuppressWarnings("unused")
+        PartnerBookmarksReader reader =
+                new PartnerBookmarksReader(mContextMock, mBrowserCustomizations);
+
+        Mockito.verify(mBrowserCustomizations, Mockito.never()).initializeAsync(mContextMock);
+
+        Mockito.when(mBrowserCustomizations.isBookmarksEditingDisabled()).thenReturn(false);
+        mBrowserCustomizationsInitCallback.getValue().run();
+        Mockito.verify(mJniMock, Mockito.never()).disablePartnerBookmarksEditing();
+    }
+
+    @Test
+    public void partnerBrowserCustomizations_BookmarkEditingAllowed_NotAlreadyInitialized() {
+        Mockito.when(mBrowserCustomizations.isInitialized()).thenReturn(false);
+
+        @SuppressWarnings("unused")
+        PartnerBookmarksReader reader =
+                new PartnerBookmarksReader(mContextMock, mBrowserCustomizations);
+
+        Mockito.verify(mBrowserCustomizations).initializeAsync(mContextMock);
+
+        Mockito.when(mBrowserCustomizations.isBookmarksEditingDisabled()).thenReturn(false);
+        mBrowserCustomizationsInitCallback.getValue().run();
+        Mockito.verify(mJniMock, Mockito.never()).disablePartnerBookmarksEditing();
+    }
+
+    @Test
+    public void partnerBookmarksCreationComplete_NotCalledWithoutBrowserCustomizations() {
+        Mockito.when(mBrowserCustomizations.isInitialized()).thenReturn(true);
+
+        @SuppressWarnings("unused")
+        PartnerBookmarksReader reader =
+                new PartnerBookmarksReader(mContextMock, mBrowserCustomizations);
+        reader.onBookmarksRead();
+
+        Mockito.verify(mJniMock, Mockito.never())
+                .partnerBookmarksCreationComplete(Mockito.anyLong(), Mockito.any());
+    }
+
+    @Test
+    public void partnerBookmarksCreationComplete_NotCalledWithoutBookmarksRead() {
+        Mockito.when(mBrowserCustomizations.isInitialized()).thenReturn(true);
+
+        @SuppressWarnings("unused")
+        PartnerBookmarksReader reader =
+                new PartnerBookmarksReader(mContextMock, mBrowserCustomizations);
+        Mockito.when(mBrowserCustomizations.isBookmarksEditingDisabled()).thenReturn(false);
+        mBrowserCustomizationsInitCallback.getValue().run();
+
+        Mockito.verify(mJniMock, Mockito.never())
+                .partnerBookmarksCreationComplete(Mockito.anyLong(), Mockito.any());
+    }
+
+    @Test
+    public void partnerBookmarksCreationComplete_Called_WithCustomizationsFirstThenBookmarks() {
+        Mockito.when(mBrowserCustomizations.isInitialized()).thenReturn(true);
+
+        @SuppressWarnings("unused")
+        PartnerBookmarksReader reader =
+                new PartnerBookmarksReader(mContextMock, mBrowserCustomizations);
+        Mockito.when(mBrowserCustomizations.isBookmarksEditingDisabled()).thenReturn(false);
+        mBrowserCustomizationsInitCallback.getValue().run();
+        reader.onBookmarksRead();
+
+        Mockito.verify(mJniMock).partnerBookmarksCreationComplete(
+                Mockito.anyLong(), Mockito.eq(reader));
+    }
+
+    @Test
+    public void partnerBookmarksCreationComplete_Called_WithBookmarksFirstThenCustomizations() {
+        Mockito.when(mBrowserCustomizations.isInitialized()).thenReturn(true);
+
+        @SuppressWarnings("unused")
+        PartnerBookmarksReader reader =
+                new PartnerBookmarksReader(mContextMock, mBrowserCustomizations);
+        Mockito.when(mBrowserCustomizations.isBookmarksEditingDisabled()).thenReturn(false);
+        reader.onBookmarksRead();
+        mBrowserCustomizationsInitCallback.getValue().run();
+
+        Mockito.verify(mJniMock).partnerBookmarksCreationComplete(
+                Mockito.anyLong(), Mockito.eq(reader));
+    }
+}
diff --git a/chrome/app/chromeos_strings.grdp b/chrome/app/chromeos_strings.grdp
index f04a106a..45fe507 100644
--- a/chrome/app/chromeos_strings.grdp
+++ b/chrome/app/chromeos_strings.grdp
@@ -670,7 +670,7 @@
     OK
   </message>
   <message name="IDS_OOBE_DEMO_SETUP_PROGRESS_SCREEN_TITLE" desc="The title of the dialog that is shown when demo mode setup is in progress.">
-    Starting demo mode
+    Starting Demo Mode
   </message>
   <message name="IDS_OOBE_DEMO_SETUP_ERROR_SCREEN_TITLE" desc="The title of the dialog that is shown when demo mode setup failed.">
     Couldn't start demo mode
@@ -678,6 +678,13 @@
   <message name="IDS_OOBE_DEMO_SETUP_ERROR_SCREEN_RETRY_BUTTON_LABEL" desc="The label of the button that is shown on error screen and that retries demo mode setup with the previously chosen configuration.">
     OK
   </message>
+  <!-- Demo setup progress steps -->
+  <message name="IDS_OOBE_DEMO_SETUP_PROGRESS_STEP_DOWNLOAD" desc="List item shown when demo resources are being downloaded">
+    Downloading Resources
+  </message>
+  <message name="IDS_OOBE_DEMO_SETUP_PROGRESS_STEP_ENROLL" desc="List item shown when enterprise enrolling for demo mode">
+    Enrolling in Demo Mode
+  </message>
   <!-- Demo setup flow errors -->
   <message name="IDS_DEMO_SETUP_OFFLINE_POLICY_ERROR" desc="Error message shown on demo setup screen when loading or parsing offline policy failed.">
     Could not read offline demo mode policy.
diff --git a/chrome/app/chromeos_strings_grdp/IDS_OOBE_DEMO_SETUP_PROGRESS_SCREEN_TITLE.png.sha1 b/chrome/app/chromeos_strings_grdp/IDS_OOBE_DEMO_SETUP_PROGRESS_SCREEN_TITLE.png.sha1
new file mode 100644
index 0000000..54f698c6
--- /dev/null
+++ b/chrome/app/chromeos_strings_grdp/IDS_OOBE_DEMO_SETUP_PROGRESS_SCREEN_TITLE.png.sha1
@@ -0,0 +1 @@
+4c1599982a05093bbcbeec264265b8d32f870e53
\ No newline at end of file
diff --git a/chrome/app/chromeos_strings_grdp/IDS_OOBE_DEMO_SETUP_PROGRESS_STEP_DOWNLOAD.png.sha1 b/chrome/app/chromeos_strings_grdp/IDS_OOBE_DEMO_SETUP_PROGRESS_STEP_DOWNLOAD.png.sha1
new file mode 100644
index 0000000..54f698c6
--- /dev/null
+++ b/chrome/app/chromeos_strings_grdp/IDS_OOBE_DEMO_SETUP_PROGRESS_STEP_DOWNLOAD.png.sha1
@@ -0,0 +1 @@
+4c1599982a05093bbcbeec264265b8d32f870e53
\ No newline at end of file
diff --git a/chrome/app/chromeos_strings_grdp/IDS_OOBE_DEMO_SETUP_PROGRESS_STEP_ENROLL.png.sha1 b/chrome/app/chromeos_strings_grdp/IDS_OOBE_DEMO_SETUP_PROGRESS_STEP_ENROLL.png.sha1
new file mode 100644
index 0000000..54f698c6
--- /dev/null
+++ b/chrome/app/chromeos_strings_grdp/IDS_OOBE_DEMO_SETUP_PROGRESS_STEP_ENROLL.png.sha1
@@ -0,0 +1 @@
+4c1599982a05093bbcbeec264265b8d32f870e53
\ No newline at end of file
diff --git a/chrome/app/os_settings_strings.grdp b/chrome/app/os_settings_strings.grdp
index 6527f9c..a9eb75c 100644
--- a/chrome/app/os_settings_strings.grdp
+++ b/chrome/app/os_settings_strings.grdp
@@ -738,6 +738,12 @@
   <message name="IDS_SETTINGS_ACCOUNT_MANAGER_PRIMARY_ACCOUNT_TOOLTIP" desc="Tooltip for the primary account in accounts list in Settings describing how to sign out.">
     To sign out of the primary account on this device, click the time on your screen. In the menu that appears, click "Sign out".
   </message>
+  <message name="IDS_SETTINGS_ACCOUNT_MANAGER_CHILD_MANAGED_BY_ONE_PARENT_TOOLTIP" desc="Tooltip for the primary account in accounts list in Settings showing that this user is managed by parent's account.">
+    Account managed by <ph name="PARENT_EMAIL">$1<ex>user@example.com</ex></ph>. To sign out of the primary account on this device, click the time on your screen. In the menu that appears, click "Sign out".
+  </message>
+  <message name="IDS_SETTINGS_ACCOUNT_MANAGER_CHILD_MANAGED_BY_TWO_PARENTS_TOOLTIP" desc="Tooltip for the primary account in accounts list in Settings showing that this user is managed by two parents' accounts.">
+    Account managed by <ph name="FIRST_PARENT_EMAIL">$1<ex>first@example.com</ex></ph> and <ph name="SECOND_PARENT_EMAIL">$2<ex>second@example.com</ex></ph>. To sign out of the primary account on this device, click the time on your screen. In the menu that appears, click "Sign out".
+  </message>
   <message name="IDS_SETTINGS_ACCOUNT_MANAGER_REMOVE_ACCOUNT_LABEL" desc="Label of the Remove account button in Account Manager.">
     Remove this account
   </message>
@@ -818,6 +824,9 @@
   <message name="IDS_SETTINGS_PLUGIN_VM_CONFIRM_REMOVE_DIALOG_BODY" desc="Message of the confirmation dialog displayed before removal begins.">
     Removing Plugin VM will delete your VM. This includes its applications, settings, and data. Are you sure you wish to continue?
   </message>
+  <message name="IDS_SETTINGS_PLUGIN_VM_CAMERA_ACCESS_TITLE" desc="The text in the settings page for allowing camera access from Plugin VM.">
+    Give access to camera
+  </message>
 
   <!-- Crostini -->
   <message name="IDS_SETTINGS_CROSTINI_TITLE" desc="The title of Crostini section.">
diff --git a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_ACCOUNT_MANAGER_CHILD_MANAGED_BY_ONE_PARENT_TOOLTIP.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_ACCOUNT_MANAGER_CHILD_MANAGED_BY_ONE_PARENT_TOOLTIP.png.sha1
new file mode 100644
index 0000000..0d16bfc
--- /dev/null
+++ b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_ACCOUNT_MANAGER_CHILD_MANAGED_BY_ONE_PARENT_TOOLTIP.png.sha1
@@ -0,0 +1 @@
+76769a5956dd51706eaf82d94488d97ca5027069
\ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_ACCOUNT_MANAGER_CHILD_MANAGED_BY_TWO_PARENTS_TOOLTIP.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_ACCOUNT_MANAGER_CHILD_MANAGED_BY_TWO_PARENTS_TOOLTIP.png.sha1
new file mode 100644
index 0000000..d3afd7a
--- /dev/null
+++ b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_ACCOUNT_MANAGER_CHILD_MANAGED_BY_TWO_PARENTS_TOOLTIP.png.sha1
@@ -0,0 +1 @@
+e76198024821d9455a882a7d93b20a15e9014af3
\ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_PLUGIN_VM_CAMERA_ACCESS_TITLE.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_PLUGIN_VM_CAMERA_ACCESS_TITLE.png.sha1
new file mode 100644
index 0000000..aef80c1c
--- /dev/null
+++ b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_PLUGIN_VM_CAMERA_ACCESS_TITLE.png.sha1
@@ -0,0 +1 @@
+f94e7f724029c18d71fb54cddfdc0b93d9e246e0
\ No newline at end of file
diff --git a/chrome/browser/android/autofill_assistant/client_android.cc b/chrome/browser/android/autofill_assistant/client_android.cc
index 6e27427..51f4f0b 100644
--- a/chrome/browser/android/autofill_assistant/client_android.cc
+++ b/chrome/browser/android/autofill_assistant/client_android.cc
@@ -30,6 +30,7 @@
 #include "components/autofill_assistant/browser/access_token_fetcher.h"
 #include "components/autofill_assistant/browser/controller.h"
 #include "components/autofill_assistant/browser/features.h"
+#include "components/autofill_assistant/browser/switches.h"
 #include "components/autofill_assistant/browser/website_login_fetcher_impl.h"
 #include "components/password_manager/content/browser/content_password_manager_driver.h"
 #include "components/password_manager/content/browser/content_password_manager_driver_factory.h"
@@ -40,7 +41,6 @@
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/web_contents.h"
-#include "google_apis/google_api_keys.h"
 #include "url/gurl.h"
 
 using base::android::AttachCurrentThread;
@@ -48,16 +48,8 @@
 using base::android::JavaRef;
 
 namespace autofill_assistant {
-namespace switches {
-const char* const kAutofillAssistantServerKey = "autofill-assistant-key";
-const char* const kAutofillAssistantUrl = "autofill-assistant-url";
-}  // namespace switches
-
 namespace {
 
-const char* const kDefaultAutofillAssistantServerUrl =
-    "https://automate-pa.googleapis.com";
-
 // A direct action that corresponds to pressing the close or cancel button on
 // the UI.
 const char* const kCancelActionName = "cancel";
@@ -106,11 +98,6 @@
       java_object_(Java_AutofillAssistantClient_create(
           AttachCurrentThread(),
           reinterpret_cast<intptr_t>(this))) {
-  server_url_ = base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
-      switches::kAutofillAssistantUrl);
-  if (server_url_.empty()) {
-    server_url_ = kDefaultAutofillAssistantServerUrl;
-  }
 }
 
 ClientAndroid::~ClientAndroid() {
@@ -449,19 +436,8 @@
   ui_controller_android_.reset();
 }
 
-std::string ClientAndroid::GetApiKey() const {
-  std::string api_key;
-  if (google_apis::IsGoogleChromeAPIKeyUsed()) {
-    api_key = chrome::GetChannel() == version_info::Channel::STABLE
-                  ? google_apis::GetAPIKey()
-                  : google_apis::GetNonStableAPIKey();
-  }
-  const auto* command_line = base::CommandLine::ForCurrentProcess();
-  if (command_line->HasSwitch(switches::kAutofillAssistantServerKey)) {
-    api_key = command_line->GetSwitchValueASCII(
-        switches::kAutofillAssistantServerKey);
-  }
-  return api_key;
+version_info::Channel ClientAndroid::GetChannel() const {
+  return chrome::GetChannel();
 }
 
 std::string ClientAndroid::GetAccountEmailAddress() const {
@@ -504,10 +480,6 @@
   return website_login_fetcher_.get();
 }
 
-std::string ClientAndroid::GetServerUrl() const {
-  return server_url_;
-}
-
 std::string ClientAndroid::GetLocale() const {
   return base::android::GetDefaultLocaleString();
 }
@@ -596,4 +568,4 @@
 
 WEB_CONTENTS_USER_DATA_KEY_IMPL(ClientAndroid)
 
-}  // namespace autofill_assistant.
+}  // namespace autofill_assistant
diff --git a/chrome/browser/android/autofill_assistant/client_android.h b/chrome/browser/android/autofill_assistant/client_android.h
index e23ec88..a85b835 100644
--- a/chrome/browser/android/autofill_assistant/client_android.h
+++ b/chrome/browser/android/autofill_assistant/client_android.h
@@ -96,14 +96,13 @@
   // Overrides Client
   void AttachUI() override;
   void DestroyUI() override;
-  std::string GetApiKey() const override;
+  version_info::Channel GetChannel() const override;
   std::string GetAccountEmailAddress() const override;
   AccessTokenFetcher* GetAccessTokenFetcher() override;
   autofill::PersonalDataManager* GetPersonalDataManager() const override;
   password_manager::PasswordManagerClient* GetPasswordManagerClient()
       const override;
   WebsiteLoginFetcher* GetWebsiteLoginFetcher() const override;
-  std::string GetServerUrl() const override;
   std::string GetLocale() const override;
   std::string GetCountryCode() const override;
   DeviceContext GetDeviceContext() const override;
@@ -156,7 +155,6 @@
 
   base::OnceCallback<void(bool, const std::string&)>
       fetch_access_token_callback_;
-  std::string server_url_;
 
   base::WeakPtrFactory<ClientAndroid> weak_ptr_factory_{this};
 
diff --git a/chrome/browser/apps/app_service/app_launch_params.cc b/chrome/browser/apps/app_service/app_launch_params.cc
index 548409e..2bb4906 100644
--- a/chrome/browser/apps/app_service/app_launch_params.cc
+++ b/chrome/browser/apps/app_service/app_launch_params.cc
@@ -22,31 +22,4 @@
 
 AppLaunchParams::~AppLaunchParams() = default;
 
-AppLaunchParams CreateAppIdLaunchParamsWithEventFlags(
-    const std::string& app_id,
-    int event_flags,
-    apps::mojom::AppLaunchSource source,
-    int64_t display_id,
-    apps::mojom::LaunchContainer fallback_container) {
-  WindowOpenDisposition raw_disposition =
-      ui::DispositionFromEventFlags(event_flags);
-
-  apps::mojom::LaunchContainer container;
-  WindowOpenDisposition disposition;
-  if (raw_disposition == WindowOpenDisposition::NEW_FOREGROUND_TAB ||
-      raw_disposition == WindowOpenDisposition::NEW_BACKGROUND_TAB) {
-    container = apps::mojom::LaunchContainer::kLaunchContainerTab;
-    disposition = raw_disposition;
-  } else if (raw_disposition == WindowOpenDisposition::NEW_WINDOW) {
-    container = apps::mojom::LaunchContainer::kLaunchContainerWindow;
-    disposition = raw_disposition;
-  } else {
-    // Look at preference to find the right launch container.  If no preference
-    // is set, launch as a regular tab.
-    container = fallback_container;
-    disposition = WindowOpenDisposition::NEW_FOREGROUND_TAB;
-  }
-  return AppLaunchParams(app_id, container, disposition, source, display_id);
-}
-
 }  // namespace apps
diff --git a/chrome/browser/apps/app_service/app_launch_params.h b/chrome/browser/apps/app_service/app_launch_params.h
index 3a7a3e9..db658e5 100644
--- a/chrome/browser/apps/app_service/app_launch_params.h
+++ b/chrome/browser/apps/app_service/app_launch_params.h
@@ -74,16 +74,6 @@
   std::vector<base::FilePath> launch_files;
 };
 
-// Helper to create AppLaunchParams using event flags that allows user to
-// override the user-configured container using modifier keys. |display_id| is
-// the id of the display from which the app is launched.
-AppLaunchParams CreateAppIdLaunchParamsWithEventFlags(
-    const std::string& app_id,
-    int event_flags,
-    apps::mojom::AppLaunchSource source,
-    int64_t display_id,
-    apps::mojom::LaunchContainer fallback_container);
-
 }  // namespace apps
 
 #endif  // CHROME_BROWSER_APPS_APP_SERVICE_APP_LAUNCH_PARAMS_H_
diff --git a/chrome/browser/apps/app_service/app_service_metrics.cc b/chrome/browser/apps/app_service/app_service_metrics.cc
index 7cfc0273..1aef9004 100644
--- a/chrome/browser/apps/app_service/app_service_metrics.cc
+++ b/chrome/browser/apps/app_service/app_service_metrics.cc
@@ -123,6 +123,10 @@
       base::UmaHistogramEnumeration(
           "Apps.DefaultAppLaunch.FromInstalledNotification", default_app_name);
       break;
+    case apps::mojom::LaunchSource::kFromArc:
+      base::UmaHistogramEnumeration("Apps.DefaultAppLaunch.FromArc",
+                                    default_app_name);
+      break;
   }
 }
 
@@ -153,6 +157,7 @@
     case apps::mojom::LaunchSource::kFromMenu:
     case apps::mojom::LaunchSource::kFromInstalledNotification:
     case apps::mojom::LaunchSource::kFromTest:
+    case apps::mojom::LaunchSource::kFromArc:
       break;
   }
 }
diff --git a/chrome/browser/apps/app_service/app_service_proxy.cc b/chrome/browser/apps/app_service/app_service_proxy.cc
index 61bdbba6..03d21188 100644
--- a/chrome/browser/apps/app_service/app_service_proxy.cc
+++ b/chrome/browser/apps/app_service/app_service_proxy.cc
@@ -244,11 +244,12 @@
 
 void AppServiceProxy::LaunchAppWithIntent(
     const std::string& app_id,
+    int32_t event_flags,
     apps::mojom::IntentPtr intent,
     apps::mojom::LaunchSource launch_source,
     int64_t display_id) {
   if (app_service_.is_connected()) {
-    cache_.ForOneApp(app_id, [this, &intent, launch_source,
+    cache_.ForOneApp(app_id, [this, event_flags, &intent, launch_source,
                               display_id](const apps::AppUpdate& update) {
 #if defined(OS_CHROMEOS)
       if (MaybeShowLaunchPreventionDialog(update)) {
@@ -257,17 +258,18 @@
 #endif
       RecordAppLaunch(update.AppId(), launch_source);
       app_service_->LaunchAppWithIntent(update.AppType(), update.AppId(),
-                                        std::move(intent), launch_source,
-                                        display_id);
+                                        event_flags, std::move(intent),
+                                        launch_source, display_id);
     });
   }
 }
 
 void AppServiceProxy::LaunchAppWithUrl(const std::string& app_id,
+                                       int32_t event_flags,
                                        GURL url,
                                        apps::mojom::LaunchSource launch_source,
                                        int64_t display_id) {
-  LaunchAppWithIntent(app_id, apps_util::CreateIntentFromUrl(url),
+  LaunchAppWithIntent(app_id, event_flags, apps_util::CreateIntentFromUrl(url),
                       launch_source, display_id);
 }
 
diff --git a/chrome/browser/apps/app_service/app_service_proxy.h b/chrome/browser/apps/app_service/app_service_proxy.h
index b7fff63..ffa63499 100644
--- a/chrome/browser/apps/app_service/app_service_proxy.h
+++ b/chrome/browser/apps/app_service/app_service_proxy.h
@@ -118,17 +118,23 @@
                           apps::mojom::FilePathsPtr file_paths);
 
   // Launches an app for the given |app_id|, passing |intent| to the app.
+  // |event_flags| provides additional context about the action which launch the
+  // app (e.g. a middle click indicating opening a background tab).
   // |launch_source| is the possible app launch sources. |display_id| is the id
   // of the display from which the app is launched.
   void LaunchAppWithIntent(const std::string& app_id,
+                           int32_t event_flags,
                            apps::mojom::IntentPtr intent,
                            apps::mojom::LaunchSource launch_source,
                            int64_t display_id);
 
   // Launches an app for the given |app_id|, passing |url| to the app.
+  // |event_flags| provides additional context about the action which launch the
+  // app (e.g. a middle click indicating opening a background tab).
   // |launch_source| is the possible app launch sources. |display_id| is the id
   // of the display from which the app is launched.
   void LaunchAppWithUrl(const std::string& app_id,
+                        int32_t event_flags,
                         GURL url,
                         apps::mojom::LaunchSource launch_source,
                         int64_t display_id);
diff --git a/chrome/browser/apps/app_service/arc_apps.cc b/chrome/browser/apps/app_service/arc_apps.cc
index eab8af6..7d789f74 100644
--- a/chrome/browser/apps/app_service/arc_apps.cc
+++ b/chrome/browser/apps/app_service/arc_apps.cc
@@ -515,6 +515,7 @@
 }
 
 void ArcApps::LaunchAppWithIntent(const std::string& app_id,
+                                  int32_t event_flags,
                                   apps::mojom::IntentPtr intent,
                                   apps::mojom::LaunchSource launch_source,
                                   int64_t display_id) {
diff --git a/chrome/browser/apps/app_service/arc_apps.h b/chrome/browser/apps/app_service/arc_apps.h
index f5a1ca64..95e15088 100644
--- a/chrome/browser/apps/app_service/arc_apps.h
+++ b/chrome/browser/apps/app_service/arc_apps.h
@@ -82,6 +82,7 @@
                           apps::mojom::LaunchSource launch_source,
                           apps::mojom::FilePathsPtr file_paths) override;
   void LaunchAppWithIntent(const std::string& app_id,
+                           int32_t event_flags,
                            apps::mojom::IntentPtr intent,
                            apps::mojom::LaunchSource launch_source,
                            int64_t display_id) override;
diff --git a/chrome/browser/apps/app_service/browser_app_launcher.cc b/chrome/browser/apps/app_service/browser_app_launcher.cc
index a340b35..c9886bf 100644
--- a/chrome/browser/apps/app_service/browser_app_launcher.cc
+++ b/chrome/browser/apps/app_service/browser_app_launcher.cc
@@ -27,6 +27,23 @@
 
 BrowserAppLauncher::~BrowserAppLauncher() = default;
 
+content::WebContents* BrowserAppLauncher::LaunchAppWithParams(
+    const AppLaunchParams& params) {
+  const extensions::Extension* extension =
+      extensions::ExtensionRegistry::Get(profile_)->GetInstalledExtension(
+          params.app_id);
+  if ((!extension || extension->from_bookmark()) && web_app_launch_manager_) {
+    return web_app_launch_manager_->OpenApplication(params);
+  }
+
+  if (params.container ==
+          apps::mojom::LaunchContainer::kLaunchContainerWindow &&
+      extension && extension->from_bookmark()) {
+    web_app::RecordAppWindowLaunch(profile_, params.app_id);
+  }
+  return ::OpenApplication(profile_, params);
+}
+
 void BrowserAppLauncher::LaunchAppWithCallback(
     const std::string& app_id,
     const base::CommandLine& command_line,
diff --git a/chrome/browser/apps/app_service/browser_app_launcher.h b/chrome/browser/apps/app_service/browser_app_launcher.h
index dec1ec70..6d3caa92 100644
--- a/chrome/browser/apps/app_service/browser_app_launcher.h
+++ b/chrome/browser/apps/app_service/browser_app_launcher.h
@@ -9,6 +9,7 @@
 #include <string>
 
 #include "base/callback.h"
+#include "chrome/browser/apps/app_service/app_launch_params.h"
 #include "components/services/app_service/public/mojom/types.mojom.h"
 
 class Browser;
@@ -19,6 +20,10 @@
 class FilePath;
 }  // namespace base
 
+namespace content {
+class WebContents;
+}
+
 namespace web_app {
 class WebAppLaunchManager;
 }  // namespace web_app
@@ -39,7 +44,10 @@
   BrowserAppLauncher(const BrowserAppLauncher&) = delete;
   BrowserAppLauncher& operator=(const BrowserAppLauncher&) = delete;
 
-  // Attempt to open |app_id| in a new window or tab. Open an empty browser
+  // Launches an app for the given |app_id| in a way specified by |params|.
+  content::WebContents* LaunchAppWithParams(const AppLaunchParams& params);
+
+  // Attempts to open |app_id| in a new window or tab. Open an empty browser
   // window if unsuccessful. The user's preferred launch container for the app
   // (standalone window or browser tab) is used. |callback| will be called with
   // the container type used to open the app, kLaunchContainerNone if an empty
diff --git a/chrome/browser/apps/app_service/built_in_chromeos_apps.cc b/chrome/browser/apps/app_service/built_in_chromeos_apps.cc
index f4ade67..15a2949 100644
--- a/chrome/browser/apps/app_service/built_in_chromeos_apps.cc
+++ b/chrome/browser/apps/app_service/built_in_chromeos_apps.cc
@@ -189,6 +189,7 @@
 
 void BuiltInChromeOsApps::LaunchAppWithIntent(
     const std::string& app_id,
+    int32_t event_flags,
     apps::mojom::IntentPtr intent,
     apps::mojom::LaunchSource launch_source,
     int64_t display_id) {
diff --git a/chrome/browser/apps/app_service/built_in_chromeos_apps.h b/chrome/browser/apps/app_service/built_in_chromeos_apps.h
index 8ec8109..53b624e 100644
--- a/chrome/browser/apps/app_service/built_in_chromeos_apps.h
+++ b/chrome/browser/apps/app_service/built_in_chromeos_apps.h
@@ -52,6 +52,7 @@
                           apps::mojom::LaunchSource launch_source,
                           apps::mojom::FilePathsPtr file_paths) override;
   void LaunchAppWithIntent(const std::string& app_id,
+                           int32_t event_flags,
                            apps::mojom::IntentPtr intent,
                            apps::mojom::LaunchSource launch_source,
                            int64_t display_id) override;
diff --git a/chrome/browser/apps/app_service/crostini_apps.cc b/chrome/browser/apps/app_service/crostini_apps.cc
index d9c248a..da5f6e21 100644
--- a/chrome/browser/apps/app_service/crostini_apps.cc
+++ b/chrome/browser/apps/app_service/crostini_apps.cc
@@ -179,6 +179,7 @@
 }
 
 void CrostiniApps::LaunchAppWithIntent(const std::string& app_id,
+                                       int32_t event_flags,
                                        apps::mojom::IntentPtr intent,
                                        apps::mojom::LaunchSource launch_source,
                                        int64_t display_id) {
diff --git a/chrome/browser/apps/app_service/crostini_apps.h b/chrome/browser/apps/app_service/crostini_apps.h
index d8059712..165eae04 100644
--- a/chrome/browser/apps/app_service/crostini_apps.h
+++ b/chrome/browser/apps/app_service/crostini_apps.h
@@ -70,6 +70,7 @@
                           apps::mojom::LaunchSource launch_source,
                           apps::mojom::FilePathsPtr file_paths) override;
   void LaunchAppWithIntent(const std::string& app_id,
+                           int32_t event_flags,
                            apps::mojom::IntentPtr intent,
                            apps::mojom::LaunchSource launch_source,
                            int64_t display_id) override;
diff --git a/chrome/browser/apps/app_service/extension_apps.cc b/chrome/browser/apps/app_service/extension_apps.cc
index 85b0f69..fa18d30 100644
--- a/chrome/browser/apps/app_service/extension_apps.cc
+++ b/chrome/browser/apps/app_service/extension_apps.cc
@@ -25,6 +25,7 @@
 #include "chrome/browser/apps/app_service/launch_utils.h"
 #include "chrome/browser/apps/app_service/menu_util.h"
 #include "chrome/browser/chromeos/arc/arc_util.h"
+#include "chrome/browser/chromeos/arc/arc_web_contents_data.h"
 #include "chrome/browser/chromeos/child_accounts/time_limits/app_time_limit_interface.h"
 #include "chrome/browser/chromeos/crostini/crostini_util.h"
 #include "chrome/browser/chromeos/extensions/gfx_utils.h"
@@ -70,6 +71,7 @@
 #include "components/content_settings/core/common/content_settings_pattern.h"
 #include "components/content_settings/core/common/content_settings_types.h"
 #include "content/public/browser/clear_site_data_utils.h"
+#include "content/public/browser/web_contents.h"
 #include "extensions/browser/app_window/app_window.h"
 #include "extensions/browser/extension_system.h"
 #include "extensions/browser/management_policy.h"
@@ -136,6 +138,7 @@
     case apps::mojom::LaunchSource::kFromMenu:
     case apps::mojom::LaunchSource::kFromInstalledNotification:
     case apps::mojom::LaunchSource::kFromTest:
+    case apps::mojom::LaunchSource::kFromArc:
       return ash::LAUNCH_FROM_UNKNOWN;
   }
 }
@@ -156,6 +159,22 @@
   return launch_id;
 }
 
+// Get the |apps::mojom::Readiness| for the |app_id|. If the extension is
+// enabled, it returns |kReady|. If the extension is
+// |DISABLE_BLOCKED_BY_POLICY|, it returns |kDisabledByPolicy|. Otherwise it
+// returns |kDisabledByUser|.
+apps::mojom::Readiness GetReadiness(const std::string& app_id,
+                                    extensions::ExtensionPrefs* prefs) {
+  DCHECK(prefs);
+  if (prefs->IsExtensionDisabled(app_id)) {
+    return prefs->HasDisableReason(
+               app_id, extensions::disable_reason::DISABLE_BLOCKED_BY_POLICY)
+               ? apps::mojom::Readiness::kDisabledByPolicy
+               : apps::mojom::Readiness::kDisabledByUser;
+  }
+  return apps::mojom::Readiness::kReady;
+}
+
 }  // namespace
 
 namespace apps {
@@ -349,7 +368,9 @@
       continue;
     }
 
-    Publish(Convert(extension, apps::mojom::Readiness::kReady));
+    Publish(Convert(
+        extension,
+        GetReadiness(app_id, extensions::ExtensionPrefs::Get(profile_))));
   }
 }
 
@@ -364,7 +385,23 @@
         extensions::ExtensionRegistry::Get(profile_);
     ConvertVector(registry->enabled_extensions(),
                   apps::mojom::Readiness::kReady, &apps);
-    ConvertVector(registry->disabled_extensions(),
+
+    extensions::ExtensionSet extensions_disabled_by_policy,
+        extensions_disabled_by_user;
+    const extensions::ExtensionPrefs* prefs =
+        extensions::ExtensionPrefs::Get(profile_);
+    for (const auto& extension : registry->disabled_extensions()) {
+      if (prefs->HasDisableReason(
+              extension->id(),
+              extensions::disable_reason::DISABLE_BLOCKED_BY_POLICY)) {
+        extensions_disabled_by_policy.Insert(extension);
+      } else {
+        extensions_disabled_by_user.Insert(extension);
+      }
+    }
+    ConvertVector(extensions_disabled_by_policy,
+                  apps::mojom::Readiness::kDisabledByPolicy, &apps);
+    ConvertVector(extensions_disabled_by_user,
                   apps::mojom::Readiness::kDisabledByUser, &apps);
     ConvertVector(registry->terminated_extensions(),
                   apps::mojom::Readiness::kTerminated, &apps);
@@ -438,6 +475,7 @@
     case apps::mojom::LaunchSource::kFromMenu:
     case apps::mojom::LaunchSource::kFromInstalledNotification:
     case apps::mojom::LaunchSource::kFromTest:
+    case apps::mojom::LaunchSource::kFromArc:
       break;
   }
 
@@ -476,6 +514,7 @@
 }
 
 void ExtensionApps::LaunchAppWithIntent(const std::string& app_id,
+                                        int32_t event_flags,
                                         apps::mojom::IntentPtr intent,
                                         apps::mojom::LaunchSource launch_source,
                                         int64_t display_id) {
@@ -486,17 +525,30 @@
   const extensions::Extension* extension =
       extensions::ExtensionRegistry::Get(profile_)->GetInstalledExtension(
           app_id);
-  if (!extension || !extensions::util::IsAppLaunchable(app_id, profile_) ||
-      RunExtensionEnableFlow(
-          app_id,
-          base::BindOnce(&ExtensionApps::LaunchAppWithIntent,
-                         weak_factory_.GetWeakPtr(), app_id, std::move(intent),
-                         launch_source, display_id))) {
+  if (!extension || !extensions::util::IsAppLaunchable(app_id, profile_)) {
     return;
   }
 
-  AppLaunchParams params = CreateAppLaunchParamsForIntent(app_id, intent);
-  LaunchImpl(params);
+  if (!extensions::util::IsAppLaunchableWithoutEnabling(app_id, profile_)) {
+    RunExtensionEnableFlow(
+        app_id, base::BindOnce(&ExtensionApps::LaunchAppWithIntent,
+                               weak_factory_.GetWeakPtr(), app_id, event_flags,
+                               std::move(intent), launch_source, display_id));
+    return;
+  }
+
+  auto params = apps::CreateAppLaunchParamsForIntent(
+      app_id, event_flags, GetAppLaunchSource(launch_source), display_id,
+      extensions::GetLaunchContainer(extensions::ExtensionPrefs::Get(profile_),
+                                     extension),
+      intent);
+  auto* tab = LaunchImpl(params);
+
+  if (launch_source == apps::mojom::LaunchSource::kFromArc && tab) {
+    // Add a flag to remember this tab originated in the ARC context.
+    tab->SetUserData(&arc::ArcWebContentsData::kArcTransitionFlag,
+                     std::make_unique<arc::ArcWebContentsData>());
+  }
 }
 
 void ExtensionApps::SetPermission(const std::string& app_id,
@@ -935,7 +987,8 @@
 
   switch (reason) {
     case extensions::UnloadedExtensionReason::DISABLE:
-      readiness = apps::mojom::Readiness::kDisabledByUser;
+      readiness = GetReadiness(extension->id(),
+                               extensions::ExtensionPrefs::Get(profile_));
       break;
     case extensions::UnloadedExtensionReason::BLACKLIST:
       readiness = apps::mojom::Readiness::kDisabledByBlacklist;
@@ -1448,10 +1501,9 @@
   std::move(callback).Run(std::move(menu_items));
 }
 
-void ExtensionApps::LaunchImpl(const AppLaunchParams& params) {
+content::WebContents* ExtensionApps::LaunchImpl(const AppLaunchParams& params) {
   if (web_app_launch_manager_) {
-    web_app_launch_manager_->OpenApplication(params);
-    return;
+    return web_app_launch_manager_->OpenApplication(params);
   }
 
   if (params.container ==
@@ -1460,7 +1512,7 @@
     web_app::RecordAppWindowLaunch(profile_, params.app_id);
   }
 
-  ::OpenApplication(profile_, params);
+  return ::OpenApplication(profile_, params);
 }
 
 }  // namespace apps
diff --git a/chrome/browser/apps/app_service/extension_apps.h b/chrome/browser/apps/app_service/extension_apps.h
index 3addc6d..7d2e720 100644
--- a/chrome/browser/apps/app_service/extension_apps.h
+++ b/chrome/browser/apps/app_service/extension_apps.h
@@ -36,6 +36,10 @@
 
 class Profile;
 
+namespace content {
+class WebContents;
+}
+
 namespace extensions {
 class AppWindow;
 class ExtensionSet;
@@ -100,6 +104,7 @@
                           apps::mojom::LaunchSource launch_source,
                           apps::mojom::FilePathsPtr file_paths) override;
   void LaunchAppWithIntent(const std::string& app_id,
+                           int32_t event_flags,
                            apps::mojom::IntentPtr intent,
                            apps::mojom::LaunchSource launch_source,
                            int64_t display_id) override;
@@ -215,7 +220,7 @@
   void GetMenuModelForChromeBrowserApp(apps::mojom::MenuType menu_type,
                                        GetMenuModelCallback callback);
 
-  void LaunchImpl(const AppLaunchParams& params);
+  content::WebContents* LaunchImpl(const AppLaunchParams& params);
 
   mojo::Receiver<apps::mojom::Publisher> receiver_{this};
   mojo::RemoteSet<apps::mojom::Subscriber> subscribers_;
diff --git a/chrome/browser/apps/app_service/launch_utils.cc b/chrome/browser/apps/app_service/launch_utils.cc
index 7cfa5202..0220de4 100644
--- a/chrome/browser/apps/app_service/launch_utils.cc
+++ b/chrome/browser/apps/app_service/launch_utils.cc
@@ -120,20 +120,53 @@
   return browser;
 }
 
+AppLaunchParams CreateAppIdLaunchParamsWithEventFlags(
+    const std::string& app_id,
+    int event_flags,
+    apps::mojom::AppLaunchSource source,
+    int64_t display_id,
+    apps::mojom::LaunchContainer fallback_container) {
+  WindowOpenDisposition raw_disposition =
+      ui::DispositionFromEventFlags(event_flags);
+
+  apps::mojom::LaunchContainer container;
+  WindowOpenDisposition disposition;
+  if (raw_disposition == WindowOpenDisposition::NEW_FOREGROUND_TAB ||
+      raw_disposition == WindowOpenDisposition::NEW_BACKGROUND_TAB) {
+    container = apps::mojom::LaunchContainer::kLaunchContainerTab;
+    disposition = raw_disposition;
+  } else if (raw_disposition == WindowOpenDisposition::NEW_WINDOW) {
+    container = apps::mojom::LaunchContainer::kLaunchContainerWindow;
+    disposition = raw_disposition;
+  } else {
+    // Look at preference to find the right launch container.  If no preference
+    // is set, launch as a regular tab.
+    container = fallback_container;
+    disposition = WindowOpenDisposition::NEW_FOREGROUND_TAB;
+  }
+  return AppLaunchParams(app_id, container, disposition, source, display_id);
+}
+
 apps::AppLaunchParams CreateAppLaunchParamsForIntent(
     const std::string& app_id,
+    int32_t event_flags,
+    apps::mojom::AppLaunchSource source,
+    int64_t display_id,
+    apps::mojom::LaunchContainer fallback_container,
     const apps::mojom::IntentPtr& intent) {
-  apps::AppLaunchParams params(
-      app_id, apps::mojom::LaunchContainer::kLaunchContainerWindow,
-      WindowOpenDisposition::NEW_FOREGROUND_TAB,
-      apps::mojom::AppLaunchSource::kSourceNone);
+  auto params = CreateAppIdLaunchParamsWithEventFlags(
+      app_id, event_flags, source, display_id, fallback_container);
 
   if (intent->scheme.has_value() && intent->host.has_value() &&
       intent->path.has_value()) {
     params.source = apps::mojom::AppLaunchSource::kSourceIntentUrl;
+    std::string port;
+    if (intent->port.has_value()) {
+      port = ":" + intent->port.value();
+    }
     params.override_url =
         GURL(intent->scheme.value() + url::kStandardSchemeSeparator +
-             intent->host.value() + intent->path.value());
+             intent->host.value() + port + intent->path.value());
     DCHECK(params.override_url.is_valid());
   }
 
@@ -166,6 +199,8 @@
       return apps::mojom::AppLaunchSource::kSourceInstalledNotification;
     case apps::mojom::LaunchSource::kFromTest:
       return apps::mojom::AppLaunchSource::kSourceTest;
+    case apps::mojom::LaunchSource::kFromArc:
+      return apps::mojom::AppLaunchSource::kSourceArc;
   }
 }
 
diff --git a/chrome/browser/apps/app_service/launch_utils.h b/chrome/browser/apps/app_service/launch_utils.h
index 37c4882..c502934b 100644
--- a/chrome/browser/apps/app_service/launch_utils.h
+++ b/chrome/browser/apps/app_service/launch_utils.h
@@ -43,8 +43,22 @@
 // the new tab page.
 Browser* CreateBrowserWithNewTabPage(Profile* profile);
 
+// Helper to create AppLaunchParams using event flags that allows user to
+// override the user-configured container using modifier keys. |display_id| is
+// the id of the display from which the app is launched.
+AppLaunchParams CreateAppIdLaunchParamsWithEventFlags(
+    const std::string& app_id,
+    int event_flags,
+    apps::mojom::AppLaunchSource source,
+    int64_t display_id,
+    apps::mojom::LaunchContainer fallback_container);
+
 apps::AppLaunchParams CreateAppLaunchParamsForIntent(
     const std::string& app_id,
+    int32_t event_flags,
+    apps::mojom::AppLaunchSource source,
+    int64_t display_id,
+    apps::mojom::LaunchContainer fallback_container,
     const apps::mojom::IntentPtr& intent);
 
 apps::mojom::AppLaunchSource GetAppLaunchSource(
diff --git a/chrome/browser/apps/app_service/web_apps.cc b/chrome/browser/apps/app_service/web_apps.cc
index a1e796b..10ded4e 100644
--- a/chrome/browser/apps/app_service/web_apps.cc
+++ b/chrome/browser/apps/app_service/web_apps.cc
@@ -23,6 +23,7 @@
 #include "chrome/browser/apps/app_service/menu_util.h"
 #include "chrome/browser/apps/launch_service/launch_service.h"
 #include "chrome/browser/chromeos/arc/arc_util.h"
+#include "chrome/browser/chromeos/arc/arc_web_contents_data.h"
 #include "chrome/browser/chromeos/crostini/crostini_util.h"
 #include "chrome/browser/chromeos/extensions/gfx_utils.h"
 #include "chrome/browser/content_settings/host_content_settings_map_factory.h"
@@ -54,6 +55,7 @@
 #include "components/content_settings/core/common/content_settings_pattern.h"
 #include "components/content_settings/core/common/content_settings_types.h"
 #include "content/public/browser/clear_site_data_utils.h"
+#include "content/public/browser/web_contents.h"
 #include "url/url_constants.h"
 
 namespace {
@@ -244,6 +246,7 @@
 }
 
 void WebApps::LaunchAppWithIntent(const std::string& app_id,
+                                  int32_t event_flags,
                                   apps::mojom::IntentPtr intent,
                                   apps::mojom::LaunchSource launch_source,
                                   int64_t display_id) {
@@ -251,8 +254,20 @@
     return;
   }
 
-  AppLaunchParams params = CreateAppLaunchParamsForIntent(app_id, intent);
-  web_app_launch_manager_->OpenApplication(params);
+  auto params = apps::CreateAppLaunchParamsForIntent(
+      app_id, event_flags, GetAppLaunchSource(launch_source), display_id,
+      web_app::ConvertDisplayModeToAppLaunchContainer(
+          GetRegistrar().GetAppEffectiveDisplayMode(app_id)),
+      intent);
+  auto* tab = web_app_launch_manager_->OpenApplication(params);
+
+  if (launch_source != apps::mojom::LaunchSource::kFromArc || !tab) {
+    return;
+  }
+
+  // Add a flag to remember this tab originated in the ARC context.
+  tab->SetUserData(&arc::ArcWebContentsData::kArcTransitionFlag,
+                   std::make_unique<arc::ArcWebContentsData>());
 }
 
 void WebApps::SetPermission(const std::string& app_id,
diff --git a/chrome/browser/apps/app_service/web_apps.h b/chrome/browser/apps/app_service/web_apps.h
index 922ddde..d3afc72 100644
--- a/chrome/browser/apps/app_service/web_apps.h
+++ b/chrome/browser/apps/app_service/web_apps.h
@@ -82,6 +82,7 @@
                           apps::mojom::LaunchSource launch_source,
                           apps::mojom::FilePathsPtr file_paths) override;
   void LaunchAppWithIntent(const std::string& app_id,
+                           int32_t event_flags,
                            apps::mojom::IntentPtr intent,
                            apps::mojom::LaunchSource launch_source,
                            int64_t display_id) override;
diff --git a/chrome/browser/apps/app_shim/web_app_shim_manager_delegate_mac.cc b/chrome/browser/apps/app_shim/web_app_shim_manager_delegate_mac.cc
index b81d9d6a..86a801c0 100644
--- a/chrome/browser/apps/app_shim/web_app_shim_manager_delegate_mac.cc
+++ b/chrome/browser/apps/app_shim/web_app_shim_manager_delegate_mac.cc
@@ -5,7 +5,9 @@
 #include "chrome/browser/apps/app_shim/web_app_shim_manager_delegate_mac.h"
 
 #include "chrome/browser/apps/app_service/app_launch_params.h"
-#include "chrome/browser/apps/launch_service/launch_service.h"
+#include "chrome/browser/apps/app_service/app_service_proxy.h"
+#include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
+#include "chrome/browser/apps/app_service/browser_app_launcher.h"
 #include "chrome/browser/web_applications/components/app_shortcut_manager.h"
 #include "chrome/browser/web_applications/components/web_app_shortcut_mac.h"
 #include "chrome/browser/web_applications/web_app_provider.h"
@@ -99,7 +101,9 @@
       app_id, launch_container, WindowOpenDisposition::NEW_FOREGROUND_TAB,
       apps::mojom::AppLaunchSource::kSourceCommandLine);
   params.launch_files = files;
-  apps::LaunchService::Get(profile)->OpenApplication(params);
+  apps::AppServiceProxyFactory::GetForProfile(profile)
+      ->BrowserAppLauncher()
+      .LaunchAppWithParams(params);
 }
 
 void WebAppShimManagerDelegate::LaunchShim(
diff --git a/chrome/browser/apps/platform_apps/api/media_galleries/media_galleries_apitest.cc b/chrome/browser/apps/platform_apps/api/media_galleries/media_galleries_apitest.cc
index 1bdfd2d..4354096b 100644
--- a/chrome/browser/apps/platform_apps/api/media_galleries/media_galleries_apitest.cc
+++ b/chrome/browser/apps/platform_apps/api/media_galleries/media_galleries_apitest.cc
@@ -19,7 +19,9 @@
 #include "base/values.h"
 #include "build/build_config.h"
 #include "chrome/browser/apps/app_service/app_launch_params.h"
-#include "chrome/browser/apps/launch_service/launch_service.h"
+#include "chrome/browser/apps/app_service/app_service_proxy.h"
+#include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
+#include "chrome/browser/apps/app_service/browser_app_launcher.h"
 #include "chrome/browser/apps/platform_apps/api/media_galleries/media_galleries_api.h"
 #include "chrome/browser/apps/platform_apps/app_browsertest_util.h"
 #include "chrome/browser/browser_process.h"
@@ -274,7 +276,9 @@
       WindowOpenDisposition::NEW_WINDOW,
       apps::mojom::AppLaunchSource::kSourceTest);
   params.command_line = *base::CommandLine::ForCurrentProcess();
-  apps::LaunchService::Get(browser()->profile())->OpenApplication(params);
+  apps::AppServiceProxyFactory::GetForProfile(browser()->profile())
+      ->BrowserAppLauncher()
+      .LaunchAppWithParams(params);
 
   bool result = true;
   if (!catcher.GetNextResult()) {
diff --git a/chrome/browser/apps/platform_apps/app_browsertest.cc b/chrome/browser/apps/platform_apps/app_browsertest.cc
index a262206..b6d47020 100644
--- a/chrome/browser/apps/platform_apps/app_browsertest.cc
+++ b/chrome/browser/apps/platform_apps/app_browsertest.cc
@@ -20,7 +20,11 @@
 #include "base/threading/thread_restrictions.h"
 #include "build/build_config.h"
 #include "chrome/app/chrome_command_ids.h"
-#include "chrome/browser/apps/launch_service/launch_service.h"
+#include "chrome/browser/apps/app_service/app_launch_params.h"
+#include "chrome/browser/apps/app_service/app_service_proxy.h"
+#include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
+#include "chrome/browser/apps/app_service/browser_app_launcher.h"
+#include "chrome/browser/apps/app_service/launch_utils.h"
 #include "chrome/browser/apps/platform_apps/app_browsertest_util.h"
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/devtools/devtools_window.h"
@@ -64,6 +68,7 @@
 #include "net/test/embedded_test_server/embedded_test_server.h"
 #include "printing/buildflags/buildflags.h"
 #include "ui/base/window_open_disposition.h"
+#include "ui/display/types/display_constants.h"
 #include "url/gurl.h"
 
 #if defined(OS_CHROMEOS)
@@ -242,7 +247,9 @@
         apps::mojom::AppLaunchSource::kSourceTest);
     params.command_line = command_line;
     params.current_directory = test_data_dir_;
-    apps::LaunchService::Get(browser()->profile())->OpenApplication(params);
+    apps::AppServiceProxyFactory::GetForProfile(browser()->profile())
+        ->BrowserAppLauncher()
+        .LaunchAppWithParams(params);
 
     if (!catcher.GetNextResult()) {
       message_ = catcher.message();
@@ -881,8 +888,9 @@
     content::WindowedNotificationObserver app_loaded_observer(
         content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME,
         content::NotificationService::AllSources());
-    apps::LaunchService::Get(browser()->profile())
-        ->OpenApplication(apps::AppLaunchParams(
+    apps::AppServiceProxyFactory::GetForProfile(browser()->profile())
+        ->BrowserAppLauncher()
+        .LaunchAppWithParams(apps::AppLaunchParams(
             extension->id(), LaunchContainer::kLaunchContainerNone,
             WindowOpenDisposition::NEW_WINDOW,
             apps::mojom::AppLaunchSource::kSourceTest));
@@ -1029,8 +1037,9 @@
   ASSERT_TRUE(should_install.seen());
 
   ExtensionTestMessageListener launched_listener("Launched", false);
-  apps::LaunchService::Get(browser()->profile())
-      ->OpenApplication(apps::AppLaunchParams(
+  apps::AppServiceProxyFactory::GetForProfile(browser()->profile())
+      ->BrowserAppLauncher()
+      .LaunchAppWithParams(apps::AppLaunchParams(
           extension->id(), LaunchContainer::kLaunchContainerNone,
           WindowOpenDisposition::NEW_WINDOW,
           apps::mojom::AppLaunchSource::kSourceTest));
@@ -1053,8 +1062,9 @@
   ASSERT_TRUE(extension);
 
   ExtensionTestMessageListener launched_listener("Launched", false);
-  apps::LaunchService::Get(browser()->profile())
-      ->OpenApplication(apps::AppLaunchParams(
+  apps::AppServiceProxyFactory::GetForProfile(browser()->profile())
+      ->BrowserAppLauncher()
+      .LaunchAppWithParams(apps::AppLaunchParams(
           extension->id(), LaunchContainer::kLaunchContainerNone,
           WindowOpenDisposition::NEW_WINDOW,
           apps::mojom::AppLaunchSource::kSourceTest));
@@ -1093,8 +1103,9 @@
   ASSERT_TRUE(should_install.seen());
 
   ExtensionTestMessageListener launched_listener("Launched", false);
-  apps::LaunchService::Get(browser()->profile())
-      ->OpenApplication(apps::AppLaunchParams(
+  apps::AppServiceProxyFactory::GetForProfile(browser()->profile())
+      ->BrowserAppLauncher()
+      .LaunchAppWithParams(apps::AppLaunchParams(
           extension->id(), LaunchContainer::kLaunchContainerNone,
           WindowOpenDisposition::NEW_WINDOW,
           apps::mojom::AppLaunchSource::kSourceTest));
@@ -1120,8 +1131,9 @@
 
   {
     ExtensionTestMessageListener launched_listener("Launched", false);
-    apps::LaunchService::Get(browser()->profile())
-        ->OpenApplication(apps::AppLaunchParams(
+    apps::AppServiceProxyFactory::GetForProfile(browser()->profile())
+        ->BrowserAppLauncher()
+        .LaunchAppWithParams(apps::AppLaunchParams(
             extension->id(), LaunchContainer::kLaunchContainerNone,
             WindowOpenDisposition::NEW_WINDOW,
             apps::mojom::AppLaunchSource::kSourceTest));
@@ -1242,12 +1254,14 @@
   AppWindowRegistry* registry = AppWindowRegistry::Get(incognito_profile);
   ASSERT_TRUE(registry != NULL);
   registry->AddObserver(this);
-
-  apps::LaunchService::Get(incognito_profile)
-      ->OpenApplication(CreateAppLaunchParamsUserContainer(
-          incognito_profile, file_manager,
-          WindowOpenDisposition::NEW_FOREGROUND_TAB,
-          apps::mojom::AppLaunchSource::kSourceTest));
+  apps::AppServiceProxyFactory::GetForProfile(incognito_profile)
+      ->Launch(file_manager->id(),
+               apps::GetEventFlags(
+                   apps::mojom::LaunchContainer::kLaunchContainerWindow,
+                   WindowOpenDisposition::NEW_FOREGROUND_TAB,
+                   true /* prefer_container */),
+               apps::mojom::LaunchSource::kFromTest,
+               display::kInvalidDisplayId);
 
   while (!base::Contains(opener_app_ids_, file_manager->id())) {
     content::RunAllPendingInMessageLoop();
diff --git a/chrome/browser/apps/platform_apps/app_browsertest_util.cc b/chrome/browser/apps/platform_apps/app_browsertest_util.cc
index f7dc76ed..a64d9d82 100644
--- a/chrome/browser/apps/platform_apps/app_browsertest_util.cc
+++ b/chrome/browser/apps/platform_apps/app_browsertest_util.cc
@@ -9,7 +9,10 @@
 
 #include "base/command_line.h"
 #include "base/strings/stringprintf.h"
-#include "chrome/browser/apps/launch_service/launch_service.h"
+#include "chrome/browser/apps/app_service/app_launch_params.h"
+#include "chrome/browser/apps/app_service/app_service_proxy.h"
+#include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
+#include "chrome/browser/apps/app_service/browser_app_launcher.h"
 #include "chrome/browser/extensions/api/tabs/tabs_api.h"
 #include "chrome/browser/extensions/extension_function_test_utils.h"
 #include "chrome/browser/ui/apps/chrome_app_delegate.h"
@@ -154,16 +157,18 @@
 }
 
 void PlatformAppBrowserTest::LaunchPlatformApp(const Extension* extension) {
-  apps::LaunchService::Get(browser()->profile())
-      ->OpenApplication(apps::AppLaunchParams(
+  apps::AppServiceProxyFactory::GetForProfile(profile())
+      ->BrowserAppLauncher()
+      .LaunchAppWithParams(apps::AppLaunchParams(
           extension->id(), LaunchContainer::kLaunchContainerNone,
           WindowOpenDisposition::NEW_WINDOW,
           apps::mojom::AppLaunchSource::kSourceTest));
 }
 
 void PlatformAppBrowserTest::LaunchHostedApp(const Extension* extension) {
-  apps::LaunchService::Get(browser()->profile())
-      ->OpenApplication(CreateAppLaunchParamsUserContainer(
+  apps::AppServiceProxyFactory::GetForProfile(profile())
+      ->BrowserAppLauncher()
+      .LaunchAppWithParams(CreateAppLaunchParamsUserContainer(
           browser()->profile(), extension,
           WindowOpenDisposition::NEW_FOREGROUND_TAB,
           apps::mojom::AppLaunchSource::kSourceCommandLine));
diff --git a/chrome/browser/apps/platform_apps/app_window_browsertest.cc b/chrome/browser/apps/platform_apps/app_window_browsertest.cc
index 2337b18..bc73179 100644
--- a/chrome/browser/apps/platform_apps/app_window_browsertest.cc
+++ b/chrome/browser/apps/platform_apps/app_window_browsertest.cc
@@ -5,7 +5,9 @@
 #include "base/run_loop.h"
 #include "build/build_config.h"
 #include "chrome/browser/apps/app_service/app_launch_params.h"
-#include "chrome/browser/apps/launch_service/launch_service.h"
+#include "chrome/browser/apps/app_service/app_service_proxy.h"
+#include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
+#include "chrome/browser/apps/app_service/browser_app_launcher.h"
 #include "chrome/browser/apps/platform_apps/app_browsertest_util.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
@@ -231,8 +233,9 @@
       test_data_dir_.AppendASCII("platform_apps").AppendASCII("window_api"));
   EXPECT_TRUE(extension);
 
-  apps::LaunchService::Get(browser()->profile())
-      ->OpenApplication(apps::AppLaunchParams(
+  apps::AppServiceProxyFactory::GetForProfile(browser()->profile())
+      ->BrowserAppLauncher()
+      .LaunchAppWithParams(apps::AppLaunchParams(
           extension->id(), apps::mojom::LaunchContainer::kLaunchContainerNone,
           WindowOpenDisposition::NEW_WINDOW,
           apps::mojom::AppLaunchSource::kSourceTest));
diff --git a/chrome/browser/apps/platform_apps/extension_app_shim_manager_delegate_mac.cc b/chrome/browser/apps/platform_apps/extension_app_shim_manager_delegate_mac.cc
index ce39442..67beb29 100644
--- a/chrome/browser/apps/platform_apps/extension_app_shim_manager_delegate_mac.cc
+++ b/chrome/browser/apps/platform_apps/extension_app_shim_manager_delegate_mac.cc
@@ -5,8 +5,11 @@
 #include "chrome/browser/apps/platform_apps/extension_app_shim_manager_delegate_mac.h"
 
 #include "apps/launcher.h"
+#include "chrome/browser/apps/app_service/app_launch_params.h"
+#include "chrome/browser/apps/app_service/app_service_proxy.h"
+#include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
+#include "chrome/browser/apps/app_service/browser_app_launcher.h"
 #include "chrome/browser/apps/app_shim/app_shim_termination_manager.h"
-#include "chrome/browser/apps/launch_service/launch_service.h"
 #include "chrome/browser/apps/platform_apps/app_window_registry_util.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/extensions/launch_util.h"
@@ -180,7 +183,9 @@
         profile, extension, WindowOpenDisposition::NEW_FOREGROUND_TAB,
         apps::mojom::AppLaunchSource::kSourceCommandLine);
     params.launch_files = files;
-    apps::LaunchService::Get(profile)->OpenApplication(params);
+    apps::AppServiceProxyFactory::GetForProfile(profile)
+        ->BrowserAppLauncher()
+        .LaunchAppWithParams(params);
     return;
   }
   if (files.empty()) {
diff --git a/chrome/browser/background/background_mode_manager.cc b/chrome/browser/background/background_mode_manager.cc
index 4b1cbb5..4527e7a 100644
--- a/chrome/browser/background/background_mode_manager.cc
+++ b/chrome/browser/background/background_mode_manager.cc
@@ -26,7 +26,9 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "build/build_config.h"
 #include "chrome/app/chrome_command_ids.h"
-#include "chrome/browser/apps/launch_service/launch_service.h"
+#include "chrome/browser/apps/app_service/app_service_proxy.h"
+#include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
+#include "chrome/browser/apps/app_service/browser_app_launcher.h"
 #include "chrome/browser/background/background_application_list_model.h"
 #include "chrome/browser/background/background_mode_optimizer.h"
 #include "chrome/browser/browser_process.h"
@@ -349,8 +351,9 @@
 void BackgroundModeManager::LaunchBackgroundApplication(
     Profile* profile,
     const Extension* extension) {
-  apps::LaunchService::Get(profile)->OpenApplication(
-      CreateAppLaunchParamsUserContainer(
+  apps::AppServiceProxyFactory::GetForProfile(profile)
+      ->BrowserAppLauncher()
+      .LaunchAppWithParams(CreateAppLaunchParamsUserContainer(
           profile, extension, WindowOpenDisposition::NEW_FOREGROUND_TAB,
           apps::mojom::AppLaunchSource::kSourceBackground));
 }
diff --git a/chrome/browser/chromeos/app_mode/startup_app_launcher.cc b/chrome/browser/chromeos/app_mode/startup_app_launcher.cc
index 4ac093b..f1a0e257 100644
--- a/chrome/browser/chromeos/app_mode/startup_app_launcher.cc
+++ b/chrome/browser/chromeos/app_mode/startup_app_launcher.cc
@@ -14,7 +14,9 @@
 #include "base/time/time.h"
 #include "base/values.h"
 #include "chrome/browser/apps/app_service/app_launch_params.h"
-#include "chrome/browser/apps/launch_service/launch_service.h"
+#include "chrome/browser/apps/app_service/app_service_proxy.h"
+#include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
+#include "chrome/browser/apps/app_service/browser_app_launcher.h"
 #include "chrome/browser/chromeos/app_mode/kiosk_app_manager.h"
 #include "chrome/browser/chromeos/app_mode/kiosk_diagnosis_runner.h"
 #include "chrome/browser/chromeos/app_mode/startup_app_launcher_update_checker.h"
@@ -422,10 +424,12 @@
   SYSLOG(INFO) << "Attempt to launch app.";
 
   // Always open the app in a window.
-  apps::LaunchService::Get(profile_)->OpenApplication(apps::AppLaunchParams(
-      extension->id(), apps::mojom::LaunchContainer::kLaunchContainerWindow,
-      WindowOpenDisposition::NEW_WINDOW,
-      apps::mojom::AppLaunchSource::kSourceKiosk));
+  apps::AppServiceProxyFactory::GetForProfile(profile_)
+      ->BrowserAppLauncher()
+      .LaunchAppWithParams(apps::AppLaunchParams(
+          extension->id(), apps::mojom::LaunchContainer::kLaunchContainerWindow,
+          WindowOpenDisposition::NEW_WINDOW,
+          apps::mojom::AppLaunchSource::kSourceKiosk));
 
   KioskAppManager::Get()->InitSession(profile_, app_id_);
   session_manager::SessionManager::Get()->SessionStarted();
diff --git a/chrome/browser/chromeos/apps/intent_helper/common_apps_navigation_throttle.cc b/chrome/browser/chromeos/apps/intent_helper/common_apps_navigation_throttle.cc
index 876494f..183b829 100644
--- a/chrome/browser/chromeos/apps/intent_helper/common_apps_navigation_throttle.cc
+++ b/chrome/browser/chromeos/apps/intent_helper/common_apps_navigation_throttle.cc
@@ -10,6 +10,7 @@
 #include "base/stl_util.h"
 #include "chrome/browser/apps/app_service/app_service_proxy.h"
 #include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
+#include "chrome/browser/apps/app_service/launch_utils.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/intent_picker_tab_helper.h"
 #include "chrome/services/app_service/public/mojom/types.mojom.h"
@@ -100,8 +101,12 @@
     // TODO(crbug.com/853604): Distinguish the source from link and omnibox.
     apps::mojom::LaunchSource launch_source =
         apps::mojom::LaunchSource::kFromLink;
-    proxy->LaunchAppWithUrl(launch_name, url, launch_source,
-                            display::kDefaultDisplayId);
+    proxy->LaunchAppWithUrl(
+        launch_name,
+        GetEventFlags(apps::mojom::LaunchContainer::kLaunchContainerWindow,
+                      WindowOpenDisposition::NEW_WINDOW,
+                      /*prefer_container=*/true),
+        url, launch_source, display::kDefaultDisplayId);
     CloseOrGoBack(web_contents);
   }
 }
@@ -211,8 +216,12 @@
     if (preferred_app_id.has_value() &&
         base::Contains(app_ids, preferred_app_id.value())) {
       auto launch_source = apps::mojom::LaunchSource::kFromLink;
-      proxy->LaunchAppWithUrl(preferred_app_id.value(), url, launch_source,
-                              display::kDefaultDisplayId);
+      proxy->LaunchAppWithUrl(
+          preferred_app_id.value(),
+          GetEventFlags(apps::mojom::LaunchContainer::kLaunchContainerWindow,
+                        WindowOpenDisposition::NEW_WINDOW,
+                        /*prefer_container=*/true),
+          url, launch_source, display::kDefaultDisplayId);
       CloseOrGoBack(web_contents);
       return true;
     }
diff --git a/chrome/browser/chromeos/arc/accessibility/arc_accessibility_util.cc b/chrome/browser/chromeos/arc/accessibility/arc_accessibility_util.cc
index df9df0e..db1f5681 100644
--- a/chrome/browser/chromeos/arc/accessibility/arc_accessibility_util.cc
+++ b/chrome/browser/chromeos/arc/accessibility/arc_accessibility_util.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include "chrome/browser/chromeos/arc/accessibility/arc_accessibility_util.h"
+#include "chrome/browser/chromeos/arc/accessibility/accessibility_info_data_wrapper.h"
 
 #include "components/arc/mojom/accessibility_helper.mojom.h"
 #include "ui/accessibility/ax_enums.mojom.h"
@@ -15,9 +16,9 @@
 using AXNodeInfoData = mojom::AccessibilityNodeInfoData;
 using AXStringProperty = mojom::AccessibilityStringProperty;
 
-ax::mojom::Event ToAXEvent(
-    mojom::AccessibilityEventType arc_event_type,
-    mojom::AccessibilityNodeInfoData* focused_node_info_data) {
+ax::mojom::Event ToAXEvent(mojom::AccessibilityEventType arc_event_type,
+                           AccessibilityInfoDataWrapper* source_node,
+                           AccessibilityInfoDataWrapper* focused_node) {
   switch (arc_event_type) {
     case mojom::AccessibilityEventType::VIEW_FOCUSED:
     case mojom::AccessibilityEventType::VIEW_ACCESSIBILITY_FOCUSED:
@@ -30,7 +31,7 @@
     case mojom::AccessibilityEventType::VIEW_TEXT_SELECTION_CHANGED:
       return ax::mojom::Event::kTextSelectionChanged;
     case mojom::AccessibilityEventType::WINDOW_STATE_CHANGED: {
-      if (focused_node_info_data)
+      if (focused_node)
         return ax::mojom::Event::kFocus;
       else
         return ax::mojom::Event::kLayoutComplete;
@@ -50,16 +51,14 @@
     case mojom::AccessibilityEventType::VIEW_SCROLLED:
       return ax::mojom::Event::kScrollPositionChanged;
     case mojom::AccessibilityEventType::VIEW_SELECTED: {
-      // In Android, VIEW_SELECTED event is fired in the two cases below:
-      // 1. Changing a value in ProgressBar or TimePicker.
-      //    (this usage is NOT documented)
-      // 2. Selecting an item in the context of an AdapterView.
-      //    (officially documented in Android Developer doc below)
-      //    https://developer.android.com/reference/android/view/accessibility/AccessibilityEvent#TYPE_VIEW_SELECTED
-      if (focused_node_info_data && focused_node_info_data->range_info)
+      // VIEW_SELECTED event is not selection event in Chrome.
+      // See the comment on AXTreeSourceArc::NotifyAccessibilityEvent.
+      if (source_node && source_node->IsNode() &&
+          source_node->GetNode()->range_info) {
         return ax::mojom::Event::kValueChanged;
-      else
-        return ax::mojom::Event::kSelection;
+      } else {
+        return ax::mojom::Event::kFocus;
+      }
     }
     case mojom::AccessibilityEventType::VIEW_HOVER_EXIT:
     case mojom::AccessibilityEventType::TOUCH_EXPLORATION_GESTURE_START:
diff --git a/chrome/browser/chromeos/arc/accessibility/arc_accessibility_util.h b/chrome/browser/chromeos/arc/accessibility/arc_accessibility_util.h
index a3ef386..3c0d61f8 100644
--- a/chrome/browser/chromeos/arc/accessibility/arc_accessibility_util.h
+++ b/chrome/browser/chromeos/arc/accessibility/arc_accessibility_util.h
@@ -14,9 +14,11 @@
 #include "ui/accessibility/ax_enum_util.h"
 
 namespace arc {
+class AccessibilityInfoDataWrapper;
 
 ax::mojom::Event ToAXEvent(mojom::AccessibilityEventType arc_event_type,
-                           mojom::AccessibilityNodeInfoData* node_info_data);
+                           AccessibilityInfoDataWrapper* source_node,
+                           AccessibilityInfoDataWrapper* focused_node);
 
 base::Optional<mojom::AccessibilityActionType> ConvertToAndroidAction(
     ax::mojom::Action action);
diff --git a/chrome/browser/chromeos/arc/accessibility/ax_tree_source_arc.cc b/chrome/browser/chromeos/arc/accessibility/ax_tree_source_arc.cc
index eafe90c..a76ebb97 100644
--- a/chrome/browser/chromeos/arc/accessibility/ax_tree_source_arc.cc
+++ b/chrome/browser/chromeos/arc/accessibility/ax_tree_source_arc.cc
@@ -24,6 +24,7 @@
 
 using AXBooleanProperty = mojom::AccessibilityBooleanProperty;
 using AXEventData = mojom::AccessibilityEventData;
+using AXEventIntProperty = mojom::AccessibilityEventIntProperty;
 using AXEventType = mojom::AccessibilityEventType;
 using AXIntProperty = mojom::AccessibilityIntProperty;
 using AXIntListProperty = mojom::AccessibilityIntListProperty;
@@ -143,6 +144,26 @@
       android_focused_id_ = IsValid(adjusted_node) ? adjusted_node->GetId()
                                                    : event_data->source_id;
     }
+  } else if (event_data->event_type == AXEventType::VIEW_SELECTED) {
+    // In Android, VIEW_SELECTED event is dispatched in the two cases below:
+    // 1. Changing a value in ProgressBar or TimePicker.
+    // 2. Selecting an item in the context of an AdapterView.
+    AccessibilityInfoDataWrapper* source_node =
+        GetFromId(event_data->source_id);
+    if (!source_node || !source_node->IsNode())
+      return;
+    AXNodeInfoData* node_info = source_node->GetNode();
+    DCHECK(node_info);
+
+    bool is_range_change = !node_info->range_info.is_null();
+    if (!is_range_change) {
+      AccessibilityInfoDataWrapper* selected_node =
+          GetSelectedNodeInfoFromAdapterView(event_data);
+      if (!selected_node)
+        return;
+
+      android_focused_id_ = selected_node->GetId();
+    }
   } else if (event_data->event_type == AXEventType::WINDOW_STATE_CHANGED) {
     // When accessibility window changed, a11y event of WINDOW_CONTENT_CHANGED
     // is fired from Android multiple times.
@@ -196,8 +217,8 @@
 
   event_bundle.events.emplace_back();
   ui::AXEvent& event = event_bundle.events.back();
-  event.event_type = ToAXEvent(
-      event_data->event_type, focused_node ? focused_node->GetNode() : nullptr);
+  event.event_type = ToAXEvent(event_data->event_type,
+                               GetFromId(event_data->source_id), focused_node);
   event.id = event_data->source_id;
 
   if (HasProperty(event_data->int_properties,
@@ -452,6 +473,57 @@
   return nullptr;
 }
 
+AccessibilityInfoDataWrapper*
+AXTreeSourceArc::GetSelectedNodeInfoFromAdapterView(
+    AXEventData* event_data) const {
+  AccessibilityInfoDataWrapper* source_node = GetFromId(event_data->source_id);
+  if (!source_node || !source_node->IsNode())
+    return nullptr;
+
+  AXNodeInfoData* node_info = source_node->GetNode();
+  if (!node_info)
+    return nullptr;
+
+  AccessibilityInfoDataWrapper* selected_node = source_node;
+  if (!node_info->collection_item_info) {
+    // The event source is not an item of AdapterView. If the event source is
+    // AdapterView, select the child. Otherwise, this is an unrelated event.
+    int item_count, from_index, current_item_index;
+    if (!GetProperty(event_data->int_properties, AXEventIntProperty::ITEM_COUNT,
+                     &item_count) ||
+        !GetProperty(event_data->int_properties, AXEventIntProperty::FROM_INDEX,
+                     &from_index) ||
+        !GetProperty(event_data->int_properties,
+                     AXEventIntProperty::CURRENT_ITEM_INDEX,
+                     &current_item_index)) {
+      return nullptr;
+    }
+
+    int index = current_item_index - from_index;
+    if (index < 0)
+      return nullptr;
+
+    std::vector<AccessibilityInfoDataWrapper*> children;
+    source_node->GetChildren(&children);
+    if (index >= static_cast<int>(children.size()))
+      return nullptr;
+
+    selected_node = children[index];
+  }
+
+  // Sometimes a collection item is wrapped by a non-focusable node.
+  // Find a node with focusable property.
+  while (selected_node && !GetBooleanProperty(selected_node->GetNode(),
+                                              AXBooleanProperty::FOCUSABLE)) {
+    std::vector<AccessibilityInfoDataWrapper*> children;
+    selected_node->GetChildren(&children);
+    if (children.size() != 1)
+      break;
+    selected_node = children[0];
+  }
+  return selected_node;
+}
+
 void AXTreeSourceArc::UpdateAXNameCache(
     AccessibilityInfoDataWrapper* focused_node,
     const std::vector<std::string>& event_text) {
diff --git a/chrome/browser/chromeos/arc/accessibility/ax_tree_source_arc.h b/chrome/browser/chromeos/arc/accessibility/ax_tree_source_arc.h
index c11effa..6106e9f 100644
--- a/chrome/browser/chromeos/arc/accessibility/ax_tree_source_arc.h
+++ b/chrome/browser/chromeos/arc/accessibility/ax_tree_source_arc.h
@@ -126,6 +126,9 @@
   AccessibilityInfoDataWrapper* FindFirstFocusableNode(
       AccessibilityInfoDataWrapper* info_data) const;
 
+  AccessibilityInfoDataWrapper* GetSelectedNodeInfoFromAdapterView(
+      mojom::AccessibilityEventData* event_data) const;
+
   void UpdateAXNameCache(AccessibilityInfoDataWrapper* focused_node,
                          const std::vector<std::string>& event_text);
 
diff --git a/chrome/browser/chromeos/arc/accessibility/ax_tree_source_arc_unittest.cc b/chrome/browser/chromeos/arc/accessibility/ax_tree_source_arc_unittest.cc
index fdce147f..5ec25ff 100644
--- a/chrome/browser/chromeos/arc/accessibility/ax_tree_source_arc_unittest.cc
+++ b/chrome/browser/chromeos/arc/accessibility/ax_tree_source_arc_unittest.cc
@@ -22,6 +22,7 @@
 using AXCollectionInfoData = mojom::AccessibilityCollectionInfoData;
 using AXCollectionItemInfoData = mojom::AccessibilityCollectionItemInfoData;
 using AXEventData = mojom::AccessibilityEventData;
+using AXEventIntProperty = mojom::AccessibilityEventIntProperty;
 using AXEventType = mojom::AccessibilityEventType;
 using AXIntListProperty = mojom::AccessibilityIntListProperty;
 using AXIntProperty = mojom::AccessibilityIntProperty;
@@ -98,6 +99,15 @@
   prop_map.insert(std::make_pair(prop, value));
 }
 
+void SetProperty(AXEventData* event, AXEventIntProperty prop, int32_t value) {
+  if (!event->int_properties) {
+    event->int_properties = base::flat_map<AXEventIntProperty, int>();
+  }
+  auto& prop_map = event->int_properties.value();
+  base::EraseIf(prop_map, [prop](auto it) { return it.first == prop; });
+  prop_map.insert(std::make_pair(prop, value));
+}
+
 class MockAutomationEventRouter
     : public extensions::AutomationEventRouterInterface {
  public:
@@ -965,6 +975,7 @@
 TEST_F(AXTreeSourceArcTest, OnViewSelectedEvent) {
   auto event = AXEventData::New();
   event->task_id = 1;
+  event->event_type = AXEventType::VIEW_SELECTED;
 
   event->window_data = std::vector<mojom::AccessibilityWindowInfoDataPtr>();
   event->window_data->emplace_back(AXWindowInfoData::New());
@@ -973,32 +984,83 @@
   root_window->root_node_id = 10;
 
   event->node_data.emplace_back(AXNodeInfoData::New());
-  event->source_id = 1;  // button->id
   AXNodeInfoData* root = event->node_data.back().get();
   root->id = 10;
   SetProperty(root, AXIntListProperty::CHILD_NODE_IDS, std::vector<int>({1}));
 
-  // Add child node.
   event->node_data.emplace_back(AXNodeInfoData::New());
-  AXNodeInfoData* button = event->node_data.back().get();
-  button->id = 1;
-  SetProperty(button, AXBooleanProperty::FOCUSABLE, true);
-  SetProperty(button, AXBooleanProperty::IMPORTANCE, true);
+  AXNodeInfoData* list = event->node_data.back().get();
+  list->id = 1;
+  SetProperty(list, AXBooleanProperty::FOCUSABLE, true);
+  SetProperty(list, AXBooleanProperty::IMPORTANCE, true);
+  SetProperty(list, AXIntListProperty::CHILD_NODE_IDS,
+              std::vector<int>({2, 3, 4}));
 
-  // Ensure that button has a focus.
-  event->event_type = AXEventType::VIEW_FOCUSED;
-  CallNotifyAccessibilityEvent(event.get());
+  // Slider.
+  event->node_data.emplace_back(AXNodeInfoData::New());
+  AXNodeInfoData* slider = event->node_data.back().get();
+  slider->id = 2;
+  SetProperty(slider, AXBooleanProperty::FOCUSABLE, true);
+  SetProperty(slider, AXBooleanProperty::IMPORTANCE, true);
+  slider->range_info = AXRangeInfoData::New();
 
-  // Without range_info, kSelection event should be emitted. Usually this event
-  // is fired from AdapterView.
-  event->event_type = AXEventType::VIEW_SELECTED;
-  CallNotifyAccessibilityEvent(event.get());
-  EXPECT_EQ(1, GetDispatchedEventCount(ax::mojom::Event::kSelection));
+  // Simple list item.
+  event->node_data.emplace_back(AXNodeInfoData::New());
+  AXNodeInfoData* simple_item = event->node_data.back().get();
+  simple_item->id = 3;
+  SetProperty(simple_item, AXBooleanProperty::FOCUSABLE, true);
+  SetProperty(simple_item, AXBooleanProperty::IMPORTANCE, true);
+  simple_item->collection_item_info = AXCollectionItemInfoData::New();
 
-  // Set range_info, the event should be kValueChanged.
-  button->range_info = AXRangeInfoData::New();
+  // This node is not focusable.
+  event->node_data.emplace_back(AXNodeInfoData::New());
+  AXNodeInfoData* wrap_node = event->node_data.back().get();
+  wrap_node->id = 4;
+  SetProperty(wrap_node, AXBooleanProperty::IMPORTANCE, true);
+  SetProperty(wrap_node, AXIntListProperty::CHILD_NODE_IDS,
+              std::vector<int>({5}));
+  wrap_node->collection_item_info = AXCollectionItemInfoData::New();
+
+  // A list item expected to get the focus.
+  event->node_data.emplace_back(AXNodeInfoData::New());
+  AXNodeInfoData* item = event->node_data.back().get();
+  item->id = 5;
+  SetProperty(item, AXBooleanProperty::FOCUSABLE, true);
+  SetProperty(item, AXBooleanProperty::IMPORTANCE, true);
+
+  // A selected event from Slider is kValueChanged.
+  event->source_id = slider->id;
   CallNotifyAccessibilityEvent(event.get());
   EXPECT_EQ(1, GetDispatchedEventCount(ax::mojom::Event::kValueChanged));
+
+  // A selected event from a collection. In Android, these event properties are
+  // populated by AdapterView.
+  event->source_id = list->id;
+  SetProperty(event.get(), AXEventIntProperty::ITEM_COUNT, 3);
+  SetProperty(event.get(), AXEventIntProperty::FROM_INDEX, 0);
+  SetProperty(event.get(), AXEventIntProperty::CURRENT_ITEM_INDEX, 2);
+  CallNotifyAccessibilityEvent(event.get());
+  EXPECT_EQ(1, GetDispatchedEventCount(ax::mojom::Event::kFocus));
+
+  ui::AXTreeData data;
+  EXPECT_TRUE(CallGetTreeData(&data));
+  EXPECT_EQ(item->id, data.focus_id);
+
+  // A selected event from a collection item.
+  event->source_id = simple_item->id;
+  event->int_properties->clear();
+  CallNotifyAccessibilityEvent(event.get());
+  EXPECT_EQ(2, GetDispatchedEventCount(ax::mojom::Event::kFocus));
+
+  EXPECT_TRUE(CallGetTreeData(&data));
+  EXPECT_EQ(simple_item->id, data.focus_id);
+
+  // A selected event from non collection node is dropped.
+  event->source_id = item->id;
+  event->int_properties->clear();
+  CallNotifyAccessibilityEvent(event.get());
+  EXPECT_EQ(2,
+            GetDispatchedEventCount(ax::mojom::Event::kFocus));  // not changed
 }
 
 TEST_F(AXTreeSourceArcTest, OnWindowStateChangedEvent) {
diff --git a/chrome/browser/chromeos/arc/arc_support_host.cc b/chrome/browser/chromeos/arc/arc_support_host.cc
index 505e9c3..63a4d1bd 100644
--- a/chrome/browser/chromeos/arc/arc_support_host.cc
+++ b/chrome/browser/chromeos/arc/arc_support_host.cc
@@ -15,7 +15,9 @@
 #include "base/json/json_writer.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/values.h"
-#include "chrome/browser/apps/launch_service/launch_service.h"
+#include "chrome/browser/apps/app_service/app_service_proxy.h"
+#include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
+#include "chrome/browser/apps/app_service/browser_app_launcher.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
 #include "chrome/browser/consent_auditor/consent_auditor_factory.h"
@@ -128,8 +130,9 @@
           arc::kPlayStoreAppId);
   DCHECK(extension);
   DCHECK(extensions::util::IsAppLaunchable(arc::kPlayStoreAppId, profile));
-  apps::LaunchService::Get(profile)->OpenApplication(
-      CreateAppLaunchParamsUserContainer(
+  apps::AppServiceProxyFactory::GetForProfile(profile)
+      ->BrowserAppLauncher()
+      .LaunchAppWithParams(CreateAppLaunchParamsUserContainer(
           profile, extension, WindowOpenDisposition::NEW_WINDOW,
           apps::mojom::AppLaunchSource::kSourceChromeInternal));
 }
diff --git a/chrome/browser/chromeos/login/demo_mode/demo_app_launcher.cc b/chrome/browser/chromeos/login/demo_mode/demo_app_launcher.cc
index f6b696a..5be5f345c 100644
--- a/chrome/browser/chromeos/login/demo_mode/demo_app_launcher.cc
+++ b/chrome/browser/chromeos/login/demo_mode/demo_app_launcher.cc
@@ -8,7 +8,9 @@
 #include "base/files/file_path.h"
 #include "base/logging.h"
 #include "chrome/browser/apps/app_service/app_launch_params.h"
-#include "chrome/browser/apps/launch_service/launch_service.h"
+#include "chrome/browser/apps/app_service/app_service_proxy.h"
+#include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
+#include "chrome/browser/apps/app_service/browser_app_launcher.h"
 #include "chrome/browser/chromeos/app_mode/kiosk_app_manager.h"
 #include "chrome/browser/chromeos/login/ui/login_display_host.h"
 #include "chrome/browser/extensions/component_loader.h"
@@ -95,10 +97,12 @@
       NetworkTypePattern::Physical(), false,
       chromeos::network_handler::ErrorCallback());
 
-  apps::LaunchService::Get(profile)->OpenApplication(apps::AppLaunchParams(
-      extension_id, apps::mojom::LaunchContainer::kLaunchContainerWindow,
-      WindowOpenDisposition::NEW_WINDOW,
-      apps::mojom::AppLaunchSource::kSourceChromeInternal, true));
+  apps::AppServiceProxyFactory::GetForProfile(profile)
+      ->BrowserAppLauncher()
+      .LaunchAppWithParams(apps::AppLaunchParams(
+          extension_id, apps::mojom::LaunchContainer::kLaunchContainerWindow,
+          WindowOpenDisposition::NEW_WINDOW,
+          apps::mojom::AppLaunchSource::kSourceChromeInternal, true));
   KioskAppManager::Get()->InitSession(profile, extension_id);
 
   session_manager::SessionManager::Get()->SessionStarted();
diff --git a/chrome/browser/chromeos/login/demo_mode/demo_session.cc b/chrome/browser/chromeos/login/demo_mode/demo_session.cc
index 430ff2a..667f1a6 100644
--- a/chrome/browser/chromeos/login/demo_mode/demo_session.cc
+++ b/chrome/browser/chromeos/login/demo_mode/demo_session.cc
@@ -24,7 +24,9 @@
 #include "base/task/thread_pool.h"
 #include "base/timer/timer.h"
 #include "chrome/browser/apps/app_service/app_launch_params.h"
-#include "chrome/browser/apps/launch_service/launch_service.h"
+#include "chrome/browser/apps/app_service/app_service_proxy.h"
+#include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
+#include "chrome/browser/apps/app_service/browser_app_launcher.h"
 #include "chrome/browser/apps/platform_apps/app_load_service.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/browser_process_platform_part.h"
@@ -567,10 +569,12 @@
     return;
   Profile* profile = ProfileManager::GetActiveUserProfile();
   DCHECK(profile);
-  apps::LaunchService::Get(profile)->OpenApplication(apps::AppLaunchParams(
-      extension->id(), apps::mojom::LaunchContainer::kLaunchContainerWindow,
-      WindowOpenDisposition::NEW_WINDOW,
-      apps::mojom::AppLaunchSource::kSourceChromeInternal));
+  apps::AppServiceProxyFactory::GetForProfile(profile)
+      ->BrowserAppLauncher()
+      .LaunchAppWithParams(apps::AppLaunchParams(
+          extension->id(), apps::mojom::LaunchContainer::kLaunchContainerWindow,
+          WindowOpenDisposition::NEW_WINDOW,
+          apps::mojom::AppLaunchSource::kSourceChromeInternal));
 }
 
 void DemoSession::OnAppWindowActivated(extensions::AppWindow* app_window) {
diff --git a/chrome/browser/chromeos/login/demo_mode/demo_setup_browsertest.cc b/chrome/browser/chromeos/login/demo_mode/demo_setup_browsertest.cc
index 06643eba..fa77339 100644
--- a/chrome/browser/chromeos/login/demo_mode/demo_setup_browsertest.cc
+++ b/chrome/browser/chromeos/login/demo_mode/demo_setup_browsertest.cc
@@ -13,6 +13,7 @@
 #include "base/run_loop.h"
 #include "base/strings/strcat.h"
 #include "base/strings/string_number_conversions.h"
+#include "base/test/scoped_feature_list.h"
 #include "base/time/time_to_iso8601.h"
 #include "base/timer/timer.h"
 #include "base/values.h"
@@ -39,6 +40,7 @@
 #include "chrome/browser/ui/webui/chromeos/login/network_screen_handler.h"
 #include "chrome/browser/ui/webui/chromeos/login/welcome_screen_handler.h"
 #include "chrome/grit/generated_resources.h"
+#include "chromeos/constants/chromeos_features.h"
 #include "chromeos/constants/chromeos_switches.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/dbus/fake_update_engine_client.h"
@@ -1169,6 +1171,87 @@
   EXPECT_FALSE(IsCustomNetworkListElementShown("offlineDemoSetupListItemName"));
 }
 
+class DemoSetupProgressStepsTest : public DemoSetupTestBase {
+ public:
+  DemoSetupProgressStepsTest() {
+    scoped_feature_list_.InitAndEnableFeature(
+        features::kShowStepsInDemoModeSetup);
+  }
+  ~DemoSetupProgressStepsTest() override = default;
+
+  // Checks how many steps have been rendered in the demo setup screen.
+  int CountNumberOfStepsInUi() {
+    const std::string query =
+        "$('demo-setup-content').$$('oobe-dialog').querySelectorAll('progress-"
+        "list-item').length";
+
+    return test::OobeJS().GetInt(query);
+  }
+
+  // Checks how many steps are marked as pending in the demo setup screen.
+  int CountPendingStepsInUi() {
+    const std::string query =
+        "Object.values($('demo-setup-content').$$('oobe-dialog')."
+        "querySelectorAll('progress-list-item')).filter(node => "
+        "node.shadowRoot.querySelector('#icon-pending:not([hidden])')).length";
+
+    return test::OobeJS().GetInt(query);
+  }
+
+  // Checks how many steps are marked as active in the demo setup screen.
+  int CountActiveStepsInUi() {
+    const std::string query =
+        "Object.values($('demo-setup-content').$$('oobe-dialog')."
+        "querySelectorAll('progress-list-item')).filter(node => "
+        "node.shadowRoot.querySelector('#icon-active:not([hidden])')).length";
+
+    return test::OobeJS().GetInt(query);
+  }
+
+  // Checks how many steps are marked as complete in the demo setup screen.
+  int CountCompletedStepsInUi() {
+    const std::string query =
+        "Object.values($('demo-setup-content').$$('oobe-dialog')."
+        "querySelectorAll('progress-list-item')).filter(node => "
+        "node.shadowRoot.querySelector('#icon-completed:not([hidden])'))."
+        "length";
+
+    return test::OobeJS().GetInt(query);
+  }
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+  DISALLOW_COPY_AND_ASSIGN(DemoSetupProgressStepsTest);
+};
+
+IN_PROC_BROWSER_TEST_F(DemoSetupProgressStepsTest,
+                       SetupProgessStepsDisplayCorrectly) {
+  auto* const wizard_controller = WizardController::default_controller();
+  wizard_controller->SimulateDemoModeSetupForTesting(
+      DemoSession::DemoModeConfig::kOnline);
+  SimulateNetworkConnected();
+  SkipToScreen(DemoSetupScreenView::kScreenId);
+
+  DemoSetupScreen* demoSetupScreen = GetDemoSetupScreen();
+
+  DemoSetupController::DemoSetupStep orderedSteps[] = {
+      DemoSetupController::DemoSetupStep::kDownloadResources,
+      DemoSetupController::DemoSetupStep::kEnrollment,
+      DemoSetupController::DemoSetupStep::kComplete};
+
+  // Subtract 1 to account for kComplete step
+  int numSteps =
+      static_cast<int>(sizeof(orderedSteps) / sizeof(*orderedSteps)) - 1;
+  ASSERT_EQ(CountNumberOfStepsInUi(), numSteps);
+
+  for (int i = 0; i < numSteps; i++) {
+    demoSetupScreen->SetCurrentSetupStepForTest(orderedSteps[i]);
+    ASSERT_EQ(CountPendingStepsInUi(), numSteps - i - 1);
+    ASSERT_EQ(CountActiveStepsInUi(), 1);
+    ASSERT_EQ(CountCompletedStepsInUi(), i);
+  }
+}
+
 class DemoSetupArcUnsupportedTest : public DemoSetupTestBase {
  public:
   DemoSetupArcUnsupportedTest() = default;
diff --git a/chrome/browser/chromeos/login/demo_mode/demo_setup_controller.cc b/chrome/browser/chromeos/login/demo_mode/demo_setup_controller.cc
index 8d80d69..f21c75b 100644
--- a/chrome/browser/chromeos/login/demo_mode/demo_setup_controller.cc
+++ b/chrome/browser/chromeos/login/demo_mode/demo_setup_controller.cc
@@ -61,6 +61,19 @@
     "DemoMode.Setup.LoadingDuration";
 constexpr char kDemoSetupNumRetriesHistogram[] = "DemoMode.Setup.NumRetries";
 
+struct DemoSetupStepInfo {
+  DemoSetupController::DemoSetupStep step;
+  const int step_index;
+};
+
+base::span<const DemoSetupStepInfo> GetDemoSetupStepsInfo() {
+  static const DemoSetupStepInfo kDemoModeSetupStepsInfo[] = {
+      {DemoSetupController::DemoSetupStep::kDownloadResources, 0},
+      {DemoSetupController::DemoSetupStep::kEnrollment, 1},
+      {DemoSetupController::DemoSetupStep::kComplete, 2}};
+  return kDemoModeSetupStepsInfo;
+}
+
 // Get the DeviceLocalAccountPolicyStore for the account_id.
 policy::CloudPolicyStore* GetDeviceLocalAccountPolicyStore(
     const std::string& account_id) {
@@ -483,6 +496,32 @@
   return std::string();
 }
 
+// static
+base::Value DemoSetupController::GetDemoSetupSteps() {
+  base::Value setup_steps_dict(base::Value::Type::DICTIONARY);
+  for (auto entry : GetDemoSetupStepsInfo()) {
+    setup_steps_dict.SetIntPath(GetDemoSetupStepString(entry.step),
+                                entry.step_index);
+  }
+
+  return setup_steps_dict;
+}
+
+// static
+std::string DemoSetupController::GetDemoSetupStepString(
+    const DemoSetupStep step_enum) {
+  switch (step_enum) {
+    case DemoSetupStep::kDownloadResources:
+      return "downloadResources";
+    case DemoSetupStep::kEnrollment:
+      return "enrollment";
+    case DemoSetupStep::kComplete:
+      return "complete";
+  }
+
+  NOTREACHED();
+}
+
 DemoSetupController::DemoSetupController() {}
 
 DemoSetupController::~DemoSetupController() {
@@ -497,18 +536,20 @@
 void DemoSetupController::Enroll(
     OnSetupSuccess on_setup_success,
     OnSetupError on_setup_error,
-    const OnIncrementSetupProgress& increment_setup_progress) {
+    const OnSetCurrentSetupStep& set_current_setup_step) {
   DCHECK_NE(demo_config_, DemoSession::DemoModeConfig::kNone)
       << "Demo config needs to be explicitly set before calling Enroll()";
   DCHECK(!enrollment_helper_);
 
-  increment_setup_progress_ = increment_setup_progress;
+  set_current_setup_step_ = set_current_setup_step;
   on_setup_success_ = std::move(on_setup_success);
   on_setup_error_ = std::move(on_setup_error);
 
   VLOG(1) << "Starting demo setup "
           << DemoSession::DemoConfigToString(demo_config_);
 
+  SetCurrentSetupStep(DemoSetupStep::kDownloadResources);
+
   switch (demo_config_) {
     case DemoSession::DemoModeConfig::kOnline:
       LoadDemoResourcesCrOSComponent();
@@ -577,7 +618,7 @@
       base::TimeTicks::Now() - download_start_time_;
   base::UmaHistogramLongTimes100(kDemoSetupDownloadDurationHistogram,
                                  download_duration);
-  IncrementSetupProgress(/*complete=*/false);
+  SetCurrentSetupStep(DemoSetupStep::kEnrollment);
 
   if (demo_resources_->component_error().value() !=
       component_updater::CrOSComponentManager::Error::NONE) {
@@ -662,7 +703,6 @@
     base::UmaHistogramLongTimes100(kDemoSetupEnrollDurationHistogram,
                                    enroll_duration);
   }
-  IncrementSetupProgress(/*complete=*/false);
 
   // Try to load the policy for the device local account.
   if (demo_config_ == DemoSession::DemoModeConfig::kOffline) {
@@ -764,8 +804,6 @@
 }
 
 void DemoSetupController::OnDeviceRegistered() {
-  IncrementSetupProgress(/*complete=*/true);
-
   VLOG(1) << "Demo mode setup finished successfully.";
 
   if (demo_config_ == DemoSession::DemoModeConfig::kOnline) {
@@ -782,6 +820,8 @@
   base::UmaHistogramCounts100(kDemoSetupNumRetriesHistogram,
                               num_setup_retries_);
 
+  SetCurrentSetupStep(DemoSetupStep::kComplete);
+
   PrefService* prefs = g_browser_process->local_state();
   prefs->SetInteger(prefs::kDemoModeConfig, static_cast<int>(demo_config_));
   prefs->CommitPendingWrite();
@@ -790,9 +830,9 @@
     std::move(on_setup_success_).Run();
 }
 
-void DemoSetupController::IncrementSetupProgress(bool complete) {
-  if (!increment_setup_progress_.is_null())
-    increment_setup_progress_.Run(complete);
+void DemoSetupController::SetCurrentSetupStep(DemoSetupStep current_step) {
+  if (!set_current_setup_step_.is_null())
+    set_current_setup_step_.Run(current_step);
 }
 
 void DemoSetupController::SetupFailed(const DemoSetupError& error) {
diff --git a/chrome/browser/chromeos/login/demo_mode/demo_setup_controller.h b/chrome/browser/chromeos/login/demo_mode/demo_setup_controller.h
index 4835332..aaf8322 100644
--- a/chrome/browser/chromeos/login/demo_mode/demo_setup_controller.h
+++ b/chrome/browser/chromeos/login/demo_mode/demo_setup_controller.h
@@ -28,11 +28,21 @@
 
 class DemoResources;
 
-// Controlls enrollment flow for setting up Demo Mode.
+// Controls enrollment flow for setting up Demo Mode.
 class DemoSetupController
     : public EnterpriseEnrollmentHelper::EnrollmentStatusConsumer,
       public policy::CloudPolicyStore::Observer {
  public:
+  // All steps required for setup.
+  enum class DemoSetupStep {
+    // Downloading Demo Mode resources.
+    kDownloadResources,
+    // Enrolling in Demo Mode.
+    kEnrollment,
+    // Setup is complete.
+    kComplete
+  };
+
   // Contains information related to setup error.
   class DemoSetupError {
    public:
@@ -155,7 +165,8 @@
   // Demo mode setup callbacks.
   using OnSetupSuccess = base::OnceClosure;
   using OnSetupError = base::OnceCallback<void(const DemoSetupError&)>;
-  using OnIncrementSetupProgress = base::RepeatingCallback<void(bool)>;
+  using OnSetCurrentSetupStep =
+      base::RepeatingCallback<void(const DemoSetupStep)>;
   using HasPreinstalledDemoResourcesCallback = base::OnceCallback<void(bool)>;
 
   static void RegisterLocalStatePrefs(PrefRegistrySimple* registry);
@@ -177,6 +188,12 @@
   // Otherwise, returns an empty string.
   static std::string GetSubOrganizationEmail();
 
+  // Returns a dictionary mapping setup steps to step indices.
+  static base::Value GetDemoSetupSteps();
+
+  // Converts a step enum to a string e.g. to sent to JavaScript.
+  static std::string GetDemoSetupStepString(const DemoSetupStep step_enum);
+
   DemoSetupController();
   ~DemoSetupController() override;
 
@@ -194,11 +211,11 @@
   // performed and it should be set with set_enrollment_type() before calling
   // Enroll(). |on_setup_success| will be called when enrollment finishes
   // successfully. |on_setup_error| will be called when enrollment finishes with
-  // an error. |update_setup_progress| will be called when enrollment progress
-  // is updated.
+  // an error. |set_current_setup_step| will be called when an enrollment step
+  // completes.
   void Enroll(OnSetupSuccess on_setup_success,
               OnSetupError on_setup_error,
-              const OnIncrementSetupProgress& increment_setup_progress);
+              const OnSetCurrentSetupStep& set_current_setup_step);
 
   // Tries to mount the preinstalled offline resources necessary for offline
   // Demo Mode.
@@ -257,8 +274,8 @@
   // is completed. This is the last step of demo mode setup flow.
   void OnDeviceRegistered();
 
-  // Increments setup progress percentage for UI.
-  void IncrementSetupProgress(bool complete);
+  // Sets current setup step.
+  void SetCurrentSetupStep(DemoSetupStep current_step);
 
   // Finish the flow with an error.
   void SetupFailed(const DemoSetupError& error);
@@ -292,8 +309,8 @@
   // Path at which to mount preinstalled offline demo resources for tests.
   base::FilePath preinstalled_offline_resources_path_for_tests_;
 
-  // Callback to call when setup progress is updated.
-  OnIncrementSetupProgress increment_setup_progress_;
+  // Callback to call when setup step is updated.
+  OnSetCurrentSetupStep set_current_setup_step_;
 
   // Callback to call when enrollment finishes with an error.
   OnSetupError on_setup_error_;
diff --git a/chrome/browser/chromeos/login/demo_mode/demo_setup_controller_unittest.cc b/chrome/browser/chromeos/login/demo_mode/demo_setup_controller_unittest.cc
index 8dc7eb55c..d5041f7 100644
--- a/chrome/browser/chromeos/login/demo_mode/demo_setup_controller_unittest.cc
+++ b/chrome/browser/chromeos/login/demo_mode/demo_setup_controller_unittest.cc
@@ -63,14 +63,24 @@
     run_loop_->Quit();
   }
 
-  void IncrementSetupProgress(const bool complete) {}
+  void SetCurrentSetupStep(DemoSetupController::DemoSetupStep current_step) {
+    setup_step_ = current_step;
+  }
 
   // Wait until the setup result arrives (either OnSetupError or OnSetupSuccess
-  // is called), returns true when the result matches with |expected|.
-  bool WaitResult(bool expected) {
+  // is called), returns true when the success result matches with
+  // |success_expected| and setup step matches |setup_step_expected|.
+  bool WaitResult(bool success_expected,
+                  DemoSetupController::DemoSetupStep setup_step_expected) {
     // Run() stops immediately if Quit is already called.
     run_loop_->Run();
-    return succeeded_.has_value() && succeeded_.value() == expected;
+
+    const bool success_check =
+        succeeded_.has_value() && succeeded_.value() == success_expected;
+    const bool setup_step_check =
+        setup_step_.has_value() && setup_step_.value() == setup_step_expected;
+
+    return success_check && setup_step_check;
   }
 
   // Returns true if powerwash is required to recover from the error.
@@ -82,11 +92,13 @@
 
   void Reset() {
     succeeded_.reset();
+    setup_step_.reset();
     run_loop_ = std::make_unique<base::RunLoop>();
   }
 
  private:
   base::Optional<bool> succeeded_;
+  base::Optional<DemoSetupController::DemoSetupStep> setup_step_;
   base::Optional<DemoSetupController::DemoSetupError> error_;
   std::unique_ptr<base::RunLoop> run_loop_;
 
@@ -162,11 +174,11 @@
                      base::Unretained(helper_.get())),
       base::BindOnce(&DemoSetupControllerTestHelper::OnSetupError,
                      base::Unretained(helper_.get())),
-      base::BindRepeating(
-          &DemoSetupControllerTestHelper::IncrementSetupProgress,
-          base::Unretained(helper_.get())));
+      base::BindRepeating(&DemoSetupControllerTestHelper::SetCurrentSetupStep,
+                          base::Unretained(helper_.get())));
 
-  EXPECT_TRUE(helper_->WaitResult(true));
+  EXPECT_TRUE(
+      helper_->WaitResult(true, DemoSetupController::DemoSetupStep::kComplete));
   EXPECT_EQ("", GetDeviceRequisition());
 }
 
@@ -190,11 +202,11 @@
                      base::Unretained(helper_.get())),
       base::BindOnce(&DemoSetupControllerTestHelper::OnSetupError,
                      base::Unretained(helper_.get())),
-      base::BindRepeating(
-          &DemoSetupControllerTestHelper::IncrementSetupProgress,
-          base::Unretained(helper_.get())));
+      base::BindRepeating(&DemoSetupControllerTestHelper::SetCurrentSetupStep,
+                          base::Unretained(helper_.get())));
 
-  EXPECT_TRUE(helper_->WaitResult(false));
+  EXPECT_TRUE(helper_->WaitResult(
+      false, DemoSetupController::DemoSetupStep::kDownloadResources));
   EXPECT_TRUE(helper_->RequiresPowerwash());
   EXPECT_EQ("", GetDeviceRequisition());
 }
@@ -213,11 +225,11 @@
                      base::Unretained(helper_.get())),
       base::BindOnce(&DemoSetupControllerTestHelper::OnSetupError,
                      base::Unretained(helper_.get())),
-      base::BindRepeating(
-          &DemoSetupControllerTestHelper::IncrementSetupProgress,
-          base::Unretained(helper_.get())));
+      base::BindRepeating(&DemoSetupControllerTestHelper::SetCurrentSetupStep,
+                          base::Unretained(helper_.get())));
 
-  EXPECT_TRUE(helper_->WaitResult(false));
+  EXPECT_TRUE(helper_->WaitResult(
+      false, DemoSetupController::DemoSetupStep::kDownloadResources));
   EXPECT_TRUE(helper_->RequiresPowerwash());
   EXPECT_EQ("", GetDeviceRequisition());
 }
@@ -241,11 +253,11 @@
                      base::Unretained(helper_.get())),
       base::BindOnce(&DemoSetupControllerTestHelper::OnSetupError,
                      base::Unretained(helper_.get())),
-      base::BindRepeating(
-          &DemoSetupControllerTestHelper::IncrementSetupProgress,
-          base::Unretained(helper_.get())));
+      base::BindRepeating(&DemoSetupControllerTestHelper::SetCurrentSetupStep,
+                          base::Unretained(helper_.get())));
 
-  EXPECT_TRUE(helper_->WaitResult(false));
+  EXPECT_TRUE(helper_->WaitResult(
+      false, DemoSetupController::DemoSetupStep::kDownloadResources));
   EXPECT_FALSE(helper_->RequiresPowerwash());
   EXPECT_EQ("", GetDeviceRequisition());
 }
@@ -270,11 +282,11 @@
                      base::Unretained(helper_.get())),
       base::BindOnce(&DemoSetupControllerTestHelper::OnSetupError,
                      base::Unretained(helper_.get())),
-      base::BindRepeating(
-          &DemoSetupControllerTestHelper::IncrementSetupProgress,
-          base::Unretained(helper_.get())));
+      base::BindRepeating(&DemoSetupControllerTestHelper::SetCurrentSetupStep,
+                          base::Unretained(helper_.get())));
 
-  EXPECT_TRUE(helper_->WaitResult(false));
+  EXPECT_TRUE(helper_->WaitResult(
+      false, DemoSetupController::DemoSetupStep::kDownloadResources));
   EXPECT_TRUE(helper_->RequiresPowerwash());
   EXPECT_EQ("", GetDeviceRequisition());
 }
@@ -288,11 +300,11 @@
                      base::Unretained(helper_.get())),
       base::BindOnce(&DemoSetupControllerTestHelper::OnSetupError,
                      base::Unretained(helper_.get())),
-      base::BindRepeating(
-          &DemoSetupControllerTestHelper::IncrementSetupProgress,
-          base::Unretained(helper_.get())));
+      base::BindRepeating(&DemoSetupControllerTestHelper::SetCurrentSetupStep,
+                          base::Unretained(helper_.get())));
 
-  EXPECT_TRUE(helper_->WaitResult(true));
+  EXPECT_TRUE(
+      helper_->WaitResult(true, DemoSetupController::DemoSetupStep::kComplete));
   EXPECT_EQ("", GetDeviceRequisition());
 }
 
@@ -305,11 +317,11 @@
                      base::Unretained(helper_.get())),
       base::BindOnce(&DemoSetupControllerTestHelper::OnSetupError,
                      base::Unretained(helper_.get())),
-      base::BindRepeating(
-          &DemoSetupControllerTestHelper::IncrementSetupProgress,
-          base::Unretained(helper_.get())));
+      base::BindRepeating(&DemoSetupControllerTestHelper::SetCurrentSetupStep,
+                          base::Unretained(helper_.get())));
 
-  EXPECT_TRUE(helper_->WaitResult(false));
+  EXPECT_TRUE(helper_->WaitResult(
+      false, DemoSetupController::DemoSetupStep::kEnrollment));
   EXPECT_FALSE(helper_->RequiresPowerwash());
   EXPECT_EQ("", GetDeviceRequisition());
 }
@@ -324,11 +336,11 @@
                      base::Unretained(helper_.get())),
       base::BindOnce(&DemoSetupControllerTestHelper::OnSetupError,
                      base::Unretained(helper_.get())),
-      base::BindRepeating(
-          &DemoSetupControllerTestHelper::IncrementSetupProgress,
-          base::Unretained(helper_.get())));
+      base::BindRepeating(&DemoSetupControllerTestHelper::SetCurrentSetupStep,
+                          base::Unretained(helper_.get())));
 
-  EXPECT_TRUE(helper_->WaitResult(false));
+  EXPECT_TRUE(helper_->WaitResult(
+      false, DemoSetupController::DemoSetupStep::kEnrollment));
   EXPECT_TRUE(helper_->RequiresPowerwash());
   EXPECT_EQ("", GetDeviceRequisition());
 }
@@ -346,11 +358,11 @@
                      base::Unretained(helper_.get())),
       base::BindOnce(&DemoSetupControllerTestHelper::OnSetupError,
                      base::Unretained(helper_.get())),
-      base::BindRepeating(
-          &DemoSetupControllerTestHelper::IncrementSetupProgress,
-          base::Unretained(helper_.get())));
+      base::BindRepeating(&DemoSetupControllerTestHelper::SetCurrentSetupStep,
+                          base::Unretained(helper_.get())));
 
-  EXPECT_TRUE(helper_->WaitResult(false));
+  EXPECT_TRUE(helper_->WaitResult(
+      false, DemoSetupController::DemoSetupStep::kEnrollment));
   EXPECT_FALSE(helper_->RequiresPowerwash());
   EXPECT_EQ("", GetDeviceRequisition());
 }
@@ -364,11 +376,11 @@
                      base::Unretained(helper_.get())),
       base::BindOnce(&DemoSetupControllerTestHelper::OnSetupError,
                      base::Unretained(helper_.get())),
-      base::BindRepeating(
-          &DemoSetupControllerTestHelper::IncrementSetupProgress,
-          base::Unretained(helper_.get())));
+      base::BindRepeating(&DemoSetupControllerTestHelper::SetCurrentSetupStep,
+                          base::Unretained(helper_.get())));
 
-  EXPECT_TRUE(helper_->WaitResult(false));
+  EXPECT_TRUE(helper_->WaitResult(
+      false, DemoSetupController::DemoSetupStep::kEnrollment));
   EXPECT_FALSE(helper_->RequiresPowerwash());
   EXPECT_EQ("", GetDeviceRequisition());
 
@@ -382,11 +394,11 @@
                      base::Unretained(helper_.get())),
       base::BindOnce(&DemoSetupControllerTestHelper::OnSetupError,
                      base::Unretained(helper_.get())),
-      base::BindRepeating(
-          &DemoSetupControllerTestHelper::IncrementSetupProgress,
-          base::Unretained(helper_.get())));
+      base::BindRepeating(&DemoSetupControllerTestHelper::SetCurrentSetupStep,
+                          base::Unretained(helper_.get())));
 
-  EXPECT_TRUE(helper_->WaitResult(true));
+  EXPECT_TRUE(
+      helper_->WaitResult(true, DemoSetupController::DemoSetupStep::kComplete));
   EXPECT_EQ("", GetDeviceRequisition());
 }
 
diff --git a/chrome/browser/chromeos/login/screens/demo_setup_screen.cc b/chrome/browser/chromeos/login/screens/demo_setup_screen.cc
index f865c8baf..d21e6438 100644
--- a/chrome/browser/chromeos/login/screens/demo_setup_screen.cc
+++ b/chrome/browser/chromeos/login/screens/demo_setup_screen.cc
@@ -76,12 +76,18 @@
                      weak_ptr_factory_.GetWeakPtr()),
       base::BindOnce(&DemoSetupScreen::OnSetupError,
                      weak_ptr_factory_.GetWeakPtr()),
-      base::BindRepeating(&DemoSetupScreen::IncrementSetupProgress,
+      base::BindRepeating(&DemoSetupScreen::SetCurrentSetupStep,
                           weak_ptr_factory_.GetWeakPtr()));
 }
 
-void DemoSetupScreen::IncrementSetupProgress(bool complete) {
-  view_->IncrementSetupProgress(complete);
+void DemoSetupScreen::SetCurrentSetupStep(
+    const DemoSetupController::DemoSetupStep current_step) {
+  view_->SetCurrentSetupStep(current_step);
+}
+
+void DemoSetupScreen::SetCurrentSetupStepForTest(
+    const DemoSetupController::DemoSetupStep current_step) {
+  SetCurrentSetupStep(current_step);
 }
 
 void DemoSetupScreen::OnSetupError(
diff --git a/chrome/browser/chromeos/login/screens/demo_setup_screen.h b/chrome/browser/chromeos/login/screens/demo_setup_screen.h
index 1327c97..d924967 100644
--- a/chrome/browser/chromeos/login/screens/demo_setup_screen.h
+++ b/chrome/browser/chromeos/login/screens/demo_setup_screen.h
@@ -17,7 +17,7 @@
 
 class DemoSetupScreenView;
 
-// Controlls demo mode setup. The screen can be shown during OOBE. It allows
+// Controls demo mode setup. The screen can be shown during OOBE. It allows
 // user to setup retail demo mode on the device.
 class DemoSetupScreen : public BaseScreen {
  public:
@@ -34,6 +34,10 @@
   // then it has to call Bind(nullptr).
   void OnViewDestroyed(DemoSetupScreenView* view);
 
+  // Test utilities.
+  void SetCurrentSetupStepForTest(
+      const DemoSetupController::DemoSetupStep current_step);
+
  protected:
   // BaseScreen:
   void ShowImpl() override;
@@ -45,8 +49,9 @@
  private:
   void StartEnrollment();
 
-  // Increments setup progress percentage for UI.
-  void IncrementSetupProgress(bool complete);
+  // Updates current setup step.
+  void SetCurrentSetupStep(
+      const DemoSetupController::DemoSetupStep current_step);
 
   // Called when the setup flow finished with error.
   void OnSetupError(const DemoSetupController::DemoSetupError& error);
diff --git a/chrome/browser/chromeos/login/screens/error_screen.cc b/chrome/browser/chromeos/login/screens/error_screen.cc
index e8f0bf1..bbbcb42 100644
--- a/chrome/browser/chromeos/login/screens/error_screen.cc
+++ b/chrome/browser/chromeos/login/screens/error_screen.cc
@@ -11,7 +11,9 @@
 #include "base/logging.h"
 #include "chrome/browser/app_mode/app_mode_utils.h"
 #include "chrome/browser/apps/app_service/app_launch_params.h"
-#include "chrome/browser/apps/launch_service/launch_service.h"
+#include "chrome/browser/apps/app_service/app_service_proxy.h"
+#include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
+#include "chrome/browser/apps/app_service/browser_app_launcher.h"
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/chromeos/app_mode/certificate_manager_dialog.h"
 #include "chrome/browser/chromeos/app_mode/kiosk_app_manager.h"
@@ -299,10 +301,12 @@
       IDR_CONNECTIVITY_DIAGNOSTICS_MANIFEST,
       base::FilePath(extension_misc::kConnectivityDiagnosticsPath));
 
-  apps::LaunchService::Get(profile)->OpenApplication(apps::AppLaunchParams(
-      extension_id, apps::mojom::LaunchContainer::kLaunchContainerWindow,
-      WindowOpenDisposition::NEW_WINDOW,
-      apps::mojom::AppLaunchSource::kSourceChromeInternal));
+  apps::AppServiceProxyFactory::GetForProfile(profile)
+      ->BrowserAppLauncher()
+      .LaunchAppWithParams(apps::AppLaunchParams(
+          extension_id, apps::mojom::LaunchContainer::kLaunchContainerWindow,
+          WindowOpenDisposition::NEW_WINDOW,
+          apps::mojom::AppLaunchSource::kSourceChromeInternal));
   KioskAppManager::Get()->InitSession(profile, extension_id);
 
   LoginDisplayHost::default_host()->Finalize(base::BindOnce(
diff --git a/chrome/browser/chromeos/login/screens/mock_demo_setup_screen.h b/chrome/browser/chromeos/login/screens/mock_demo_setup_screen.h
index 4b143a9..94c22eaf 100644
--- a/chrome/browser/chromeos/login/screens/mock_demo_setup_screen.h
+++ b/chrome/browser/chromeos/login/screens/mock_demo_setup_screen.h
@@ -35,7 +35,8 @@
   MOCK_METHOD0(OnSetupSucceeded, void());
   MOCK_METHOD1(OnSetupFailed,
                void(const DemoSetupController::DemoSetupError& error));
-  MOCK_METHOD1(IncrementSetupProgress, void(bool complete));
+  MOCK_METHOD1(SetCurrentSetupStep,
+               void(const DemoSetupController::DemoSetupStep current_step));
 
   void Bind(DemoSetupScreen* screen) override;
 
diff --git a/chrome/browser/chromeos/login/ui/user_adding_screen_browsertest.cc b/chrome/browser/chromeos/login/ui/user_adding_screen_browsertest.cc
index 16c5493..0cea588 100644
--- a/chrome/browser/chromeos/login/ui/user_adding_screen_browsertest.cc
+++ b/chrome/browser/chromeos/login/ui/user_adding_screen_browsertest.cc
@@ -167,7 +167,8 @@
 }
 
 // TODO(crbug.com/1067461): Flakes on ASAN and linux-chromeos-dbg
-#if defined(OS_CHROMEOS) && (defined(ADDRESS_SANITIZER) || !defined(NDEBUG))
+#if defined(OS_CHROMEOS) && (defined(ADDRESS_SANITIZER) || \
+                             defined(MEMORY_SANITIZER) || !defined(NDEBUG))
 #define MAYBE_AddingSeveralUsers DISABLED_AddingSeveralUsers
 #else
 #define MAYBE_AddingSeveralUsers AddingSeveralUsers
diff --git a/chrome/browser/chromeos/plugin_vm/plugin_vm_pref_names.cc b/chrome/browser/chromeos/plugin_vm/plugin_vm_pref_names.cc
index 65821434..b38890b1 100644
--- a/chrome/browser/chromeos/plugin_vm/plugin_vm_pref_names.cc
+++ b/chrome/browser/chromeos/plugin_vm/plugin_vm_pref_names.cc
@@ -23,6 +23,9 @@
 const char kPluginVmImageExists[] = "plugin_vm.image_exists";
 // A boolean preference indicating whether Plugin VM is allowed to use printers.
 const char kPluginVmPrintersAllowed[] = "plugin_vm.printers_allowed";
+// A boolean preference indicating whether the camera should be shared with
+// PluginVm.
+const char kPluginVmCameraSharing[] = "plugin_vm.camera_sharing";
 // Preferences for storing engagement time data, as per
 // GuestOsEngagementMetrics.
 const char kEngagementPrefsPrefix[] = "plugin_vm.metrics";
@@ -33,6 +36,7 @@
   // TODO(crbug.com/1066760): For convenience this currently defaults to true,
   // but we'll need to revisit before launch.
   registry->RegisterBooleanPref(kPluginVmPrintersAllowed, true);
+  registry->RegisterBooleanPref(kPluginVmCameraSharing, false);
 
   guest_os::prefs::RegisterEngagementProfilePrefs(registry,
                                                   kEngagementPrefsPrefix);
diff --git a/chrome/browser/chromeos/plugin_vm/plugin_vm_pref_names.h b/chrome/browser/chromeos/plugin_vm/plugin_vm_pref_names.h
index 1f26dc6..bb54660 100644
--- a/chrome/browser/chromeos/plugin_vm/plugin_vm_pref_names.h
+++ b/chrome/browser/chromeos/plugin_vm/plugin_vm_pref_names.h
@@ -13,6 +13,7 @@
 extern const char kPluginVmImage[];
 extern const char kPluginVmImageExists[];
 extern const char kPluginVmPrintersAllowed[];
+extern const char kPluginVmCameraSharing[];
 extern const char kEngagementPrefsPrefix[];
 
 void RegisterProfilePrefs(PrefRegistrySimple* registry);
diff --git a/chrome/browser/devtools/devtools_eye_dropper.h b/chrome/browser/devtools/devtools_eye_dropper.h
index e032ee0..1db457e 100644
--- a/chrome/browser/devtools/devtools_eye_dropper.h
+++ b/chrome/browser/devtools/devtools_eye_dropper.h
@@ -51,6 +51,7 @@
       mojo::PendingRemote<viz::mojom::FrameSinkVideoConsumerFrameCallbacks>
           callbacks) override;
   void OnStopped() override;
+  void OnLog(const std::string& /*message*/) override {}
 
   EyeDropperCallback callback_;
   SkBitmap frame_;
diff --git a/chrome/browser/extensions/api/autofill_assistant_private/autofill_assistant_private_api.cc b/chrome/browser/extensions/api/autofill_assistant_private/autofill_assistant_private_api.cc
index 83041ce..364dcd5 100644
--- a/chrome/browser/extensions/api/autofill_assistant_private/autofill_assistant_private_api.cc
+++ b/chrome/browser/extensions/api/autofill_assistant_private/autofill_assistant_private_api.cc
@@ -16,6 +16,7 @@
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
 #include "chrome/browser/ui/browser.h"
+#include "chrome/common/channel_info.h"
 #include "components/autofill/core/browser/field_types.h"
 #include "extensions/browser/event_router_factory.h"
 
@@ -314,10 +315,11 @@
 
 void AutofillAssistantPrivateAPI::DestroyUI() {}
 
-std::string AutofillAssistantPrivateAPI::GetApiKey() const {
-  // TODO(crbug.com/1015753): Use chromium's keys and also consider the
-  // autofill-assistant-key here to override that particular key.
-  return "invalid";
+version_info::Channel AutofillAssistantPrivateAPI::GetChannel() const {
+  // TODO(crbug.com/1015753): Make a minimal client impl available in a common
+  // chrome/browser/autofill_assistant and share it with the android client
+  // impl.
+  return chrome::GetChannel();
 }
 
 std::string AutofillAssistantPrivateAPI::GetAccountEmailAddress() const {
@@ -346,13 +348,6 @@
   return nullptr;
 }
 
-std::string AutofillAssistantPrivateAPI::GetServerUrl() const {
-  // TODO(crbug.com/1015753): Consider the autofill-assistant-url for endpoint
-  // overrides and share the kDefaultAutofillAssistantServerUrl to expose it
-  // here.
-  return "https://automate-pa.googleapis.com";
-}
-
 std::string AutofillAssistantPrivateAPI::GetLocale() const {
   return "en-us";
 }
diff --git a/chrome/browser/extensions/api/autofill_assistant_private/autofill_assistant_private_api.h b/chrome/browser/extensions/api/autofill_assistant_private/autofill_assistant_private_api.h
index d047699d..796cf65 100644
--- a/chrome/browser/extensions/api/autofill_assistant_private/autofill_assistant_private_api.h
+++ b/chrome/browser/extensions/api/autofill_assistant_private/autofill_assistant_private_api.h
@@ -196,7 +196,7 @@
   // autofill_assistant::Client:
   void AttachUI() override;
   void DestroyUI() override;
-  std::string GetApiKey() const override;
+  version_info::Channel GetChannel() const override;
   std::string GetAccountEmailAddress() const override;
   autofill_assistant::AccessTokenFetcher* GetAccessTokenFetcher() override;
   autofill::PersonalDataManager* GetPersonalDataManager() const override;
@@ -204,7 +204,6 @@
       const override;
   autofill_assistant::WebsiteLoginFetcher* GetWebsiteLoginFetcher()
       const override;
-  std::string GetServerUrl() const override;
   std::string GetLocale() const override;
   std::string GetCountryCode() const override;
   autofill_assistant::DeviceContext GetDeviceContext() const override;
diff --git a/chrome/browser/extensions/api/management/chrome_management_api_delegate.cc b/chrome/browser/extensions/api/management/chrome_management_api_delegate.cc
index 5d08050..0a2c027 100644
--- a/chrome/browser/extensions/api/management/chrome_management_api_delegate.cc
+++ b/chrome/browser/extensions/api/management/chrome_management_api_delegate.cc
@@ -13,7 +13,9 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/task/post_task.h"
 #include "chrome/browser/apps/app_service/app_launch_params.h"
-#include "chrome/browser/apps/launch_service/launch_service.h"
+#include "chrome/browser/apps/app_service/app_service_proxy.h"
+#include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
+#include "chrome/browser/apps/app_service/browser_app_launcher.h"
 #include "chrome/browser/extensions/chrome_extension_function_details.h"
 #include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/extensions/launch_util.h"
@@ -327,9 +329,11 @@
   if (display_mode == blink::mojom::DisplayMode::kBrowser)
     launch_container = apps::mojom::LaunchContainer::kLaunchContainerTab;
 
-  apps::LaunchService::Get(profile)->OpenApplication(apps::AppLaunchParams(
-      app_id, launch_container, WindowOpenDisposition::NEW_FOREGROUND_TAB,
-      apps::mojom::AppLaunchSource::kSourceManagementApi));
+  apps::AppServiceProxyFactory::GetForProfile(profile)
+      ->BrowserAppLauncher()
+      .LaunchAppWithParams(apps::AppLaunchParams(
+          app_id, launch_container, WindowOpenDisposition::NEW_FOREGROUND_TAB,
+          apps::mojom::AppLaunchSource::kSourceManagementApi));
 }
 
 void OnWebAppInstallCompleted(InstallOrLaunchWebAppCallback callback,
@@ -389,10 +393,12 @@
   extensions::LaunchContainer launch_container =
       GetLaunchContainer(extensions::ExtensionPrefs::Get(context), extension);
   Profile* profile = Profile::FromBrowserContext(context);
-  apps::LaunchService::Get(profile)->OpenApplication(apps::AppLaunchParams(
-      extension->id(), launch_container,
-      WindowOpenDisposition::NEW_FOREGROUND_TAB,
-      apps::mojom::AppLaunchSource::kSourceManagementApi));
+  apps::AppServiceProxyFactory::GetForProfile(profile)
+      ->BrowserAppLauncher()
+      .LaunchAppWithParams(apps::AppLaunchParams(
+          extension->id(), launch_container,
+          WindowOpenDisposition::NEW_FOREGROUND_TAB,
+          apps::mojom::AppLaunchSource::kSourceManagementApi));
 
 #if defined(OS_CHROMEOS)
   chromeos::DemoSession::RecordAppLaunchSourceIfInDemoMode(
@@ -592,7 +598,7 @@
   // for, and received parent permission to install the extension.
   SupervisedUserService* supervised_user_service =
       SupervisedUserServiceFactory::GetForBrowserContext(context);
-  supervised_user_service->AddExtensionApproval(*extension);
+  supervised_user_service->AddOrUpdateExtensionApproval(*extension);
 #endif
 
   // If the extension was disabled for a permissions increase, the Management
diff --git a/chrome/browser/extensions/api/notifications/notifications_apitest.cc b/chrome/browser/extensions/api/notifications/notifications_apitest.cc
index 3c64e1f..799c3c57 100644
--- a/chrome/browser/extensions/api/notifications/notifications_apitest.cc
+++ b/chrome/browser/extensions/api/notifications/notifications_apitest.cc
@@ -11,7 +11,9 @@
 #include "base/strings/utf_string_conversions.h"
 #include "build/build_config.h"
 #include "chrome/browser/apps/app_service/app_launch_params.h"
-#include "chrome/browser/apps/launch_service/launch_service.h"
+#include "chrome/browser/apps/app_service/app_service_proxy.h"
+#include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
+#include "chrome/browser/apps/app_service/browser_app_launcher.h"
 #include "chrome/browser/apps/platform_apps/app_browsertest_util.h"
 #include "chrome/browser/extensions/api/notifications/extension_notification_display_helper.h"
 #include "chrome/browser/extensions/api/notifications/extension_notification_display_helper_factory.h"
@@ -205,8 +207,9 @@
   }
 
   void LaunchPlatformApp(const Extension* extension) {
-    apps::LaunchService::Get(browser()->profile())
-        ->OpenApplication(apps::AppLaunchParams(
+    apps::AppServiceProxyFactory::GetForProfile(browser()->profile())
+        ->BrowserAppLauncher()
+        .LaunchAppWithParams(apps::AppLaunchParams(
             extension->id(), apps::mojom::LaunchContainer::kLaunchContainerNone,
             WindowOpenDisposition::NEW_WINDOW,
             apps::mojom::AppLaunchSource::kSourceTest));
diff --git a/chrome/browser/extensions/api/settings_private/prefs_util.cc b/chrome/browser/extensions/api/settings_private/prefs_util.cc
index 99993cc..01a1b46 100644
--- a/chrome/browser/extensions/api/settings_private/prefs_util.cc
+++ b/chrome/browser/extensions/api/settings_private/prefs_util.cc
@@ -473,6 +473,8 @@
       settings_api::PrefType::PREF_TYPE_BOOLEAN;
   (*s_whitelist)[plugin_vm::prefs::kPluginVmPrintersAllowed] =
       settings_api::PrefType::PREF_TYPE_BOOLEAN;
+  (*s_whitelist)[plugin_vm::prefs::kPluginVmCameraSharing] =
+      settings_api::PrefType::PREF_TYPE_BOOLEAN;
 
   // Android Apps.
   (*s_whitelist)[arc::prefs::kArcEnabled] =
diff --git a/chrome/browser/extensions/api/webstore_private/webstore_private_api.cc b/chrome/browser/extensions/api/webstore_private/webstore_private_api.cc
index 92a6382..99b1e505 100644
--- a/chrome/browser/extensions/api/webstore_private/webstore_private_api.cc
+++ b/chrome/browser/extensions/api/webstore_private/webstore_private_api.cc
@@ -509,7 +509,7 @@
     OnParentPermissionReceived() {
   SupervisedUserService* service =
       SupervisedUserServiceFactory::GetForProfile(chrome_details_.GetProfile());
-  service->AddExtensionApproval(*dummy_extension_);
+  service->AddOrUpdateExtensionApproval(*dummy_extension_);
 
   HandleInstallProceed();
   Release();  // Matches the AddRef in Run().
diff --git a/chrome/browser/extensions/browsertest_util.cc b/chrome/browser/extensions/browsertest_util.cc
index d03fba0..b5b86ac4 100644
--- a/chrome/browser/extensions/browsertest_util.cc
+++ b/chrome/browser/extensions/browsertest_util.cc
@@ -12,7 +12,9 @@
 #include "base/run_loop.h"
 #include "base/test/bind_test_util.h"
 #include "chrome/browser/apps/app_service/app_launch_params.h"
-#include "chrome/browser/apps/launch_service/launch_service.h"
+#include "chrome/browser/apps/app_service/app_service_proxy.h"
+#include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
+#include "chrome/browser/apps/app_service/browser_app_launcher.h"
 #include "chrome/browser/extensions/launch_util.h"
 #include "chrome/browser/installable/installable_metrics.h"
 #include "chrome/browser/profiles/profile.h"
@@ -91,9 +93,12 @@
 
 Browser* LaunchAppBrowser(Profile* profile, const Extension* extension_app) {
   EXPECT_TRUE(
-      apps::LaunchService::Get(profile)->OpenApplication(apps::AppLaunchParams(
-          extension_app->id(), LaunchContainer::kLaunchContainerWindow,
-          WindowOpenDisposition::CURRENT_TAB, AppLaunchSource::kSourceTest)));
+      apps::AppServiceProxyFactory::GetForProfile(profile)
+          ->BrowserAppLauncher()
+          .LaunchAppWithParams(apps::AppLaunchParams(
+              extension_app->id(), LaunchContainer::kLaunchContainerWindow,
+              WindowOpenDisposition::CURRENT_TAB,
+              AppLaunchSource::kSourceTest)));
 
   Browser* browser = chrome::FindLastActive();
   bool is_correct_app_browser =
diff --git a/chrome/browser/extensions/corb_and_cors_extension_browsertest.cc b/chrome/browser/extensions/corb_and_cors_extension_browsertest.cc
index f6974cd..69f1f4c3 100644
--- a/chrome/browser/extensions/corb_and_cors_extension_browsertest.cc
+++ b/chrome/browser/extensions/corb_and_cors_extension_browsertest.cc
@@ -13,7 +13,9 @@
 #include "base/test/metrics/histogram_tester.h"
 #include "base/values.h"
 #include "chrome/browser/apps/app_service/app_launch_params.h"
-#include "chrome/browser/apps/launch_service/launch_service.h"
+#include "chrome/browser/apps/app_service/app_service_proxy.h"
+#include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
+#include "chrome/browser/apps/app_service/browser_app_launcher.h"
 #include "chrome/browser/extensions/api/tabs/tabs_api.h"
 #include "chrome/browser/extensions/extension_browsertest.h"
 #include "chrome/browser/extensions/extension_function_test_utils.h"
@@ -1693,8 +1695,9 @@
   content::WebContents* app_contents = nullptr;
   {
     content::WebContentsAddedObserver new_contents_observer;
-    apps::LaunchService::Get(browser()->profile())
-        ->OpenApplication(apps::AppLaunchParams(
+    apps::AppServiceProxyFactory::GetForProfile(browser()->profile())
+        ->BrowserAppLauncher()
+        .LaunchAppWithParams(apps::AppLaunchParams(
             app->id(), LaunchContainer::kLaunchContainerNone,
             WindowOpenDisposition::NEW_WINDOW,
             apps::mojom::AppLaunchSource::kSourceTest));
diff --git a/chrome/browser/extensions/extension_apitest.cc b/chrome/browser/extensions/extension_apitest.cc
index ef53294..b971613 100644
--- a/chrome/browser/extensions/extension_apitest.cc
+++ b/chrome/browser/extensions/extension_apitest.cc
@@ -17,7 +17,9 @@
 #include "base/strings/stringprintf.h"
 #include "build/build_config.h"
 #include "chrome/browser/apps/app_service/app_launch_params.h"
-#include "chrome/browser/apps/launch_service/launch_service.h"
+#include "chrome/browser/apps/app_service/app_service_proxy.h"
+#include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
+#include "chrome/browser/apps/app_service/browser_app_launcher.h"
 #include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/extensions/unpacked_installer.h"
 #include "chrome/browser/profiles/profile.h"
@@ -298,7 +300,9 @@
         extension->id(), LaunchContainer::kLaunchContainerNone,
         WindowOpenDisposition::NEW_WINDOW, AppLaunchSource::kSourceTest);
     params.command_line = *base::CommandLine::ForCurrentProcess();
-    apps::LaunchService::Get(browser()->profile())->OpenApplication(params);
+    apps::AppServiceProxyFactory::GetForProfile(browser()->profile())
+        ->BrowserAppLauncher()
+        .LaunchAppWithParams(params);
   }
 
   if (!catcher.GetNextResult()) {
diff --git a/chrome/browser/extensions/extension_browsertest.cc b/chrome/browser/extensions/extension_browsertest.cc
index cde9909..723185b 100644
--- a/chrome/browser/extensions/extension_browsertest.cc
+++ b/chrome/browser/extensions/extension_browsertest.cc
@@ -24,7 +24,9 @@
 #include "base/threading/thread_restrictions.h"
 #include "build/build_config.h"
 #include "chrome/browser/apps/app_service/app_launch_params.h"
-#include "chrome/browser/apps/launch_service/launch_service.h"
+#include "chrome/browser/apps/app_service/app_service_proxy.h"
+#include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
+#include "chrome/browser/apps/app_service/browser_app_launcher.h"
 #include "chrome/browser/extensions/browsertest_util.h"
 #include "chrome/browser/extensions/chrome_test_extension_loader.h"
 #include "chrome/browser/extensions/component_loader.h"
@@ -436,7 +438,9 @@
                                WindowOpenDisposition::NEW_WINDOW,
                                AppLaunchSource::kSourceTest);
   params.command_line = *base::CommandLine::ForCurrentProcess();
-  apps::LaunchService::Get(profile())->OpenApplication(params);
+  apps::AppServiceProxyFactory::GetForProfile(profile())
+      ->BrowserAppLauncher()
+      .LaunchAppWithParams(params);
   app_loaded_observer.Wait();
 
   return app;
diff --git a/chrome/browser/extensions/extension_service_sync_unittest.cc b/chrome/browser/extensions/extension_service_sync_unittest.cc
index 15c0eb0..d252c0f7 100644
--- a/chrome/browser/extensions/extension_service_sync_unittest.cc
+++ b/chrome/browser/extensions/extension_service_sync_unittest.cc
@@ -1925,16 +1925,6 @@
     EXPECT_EQ(base::Version(version), extension->version());
   }
 
-  // Simulate a custodian or child approval for enabling the extension coming in
-  // through Sync by adding the approved version to the map of approved
-  // extensions. It doesn't simulate a change in the disable reasons.
-  void SimulateApprovalChangeViaSync(const std::string& extension_id,
-                                     const std::string& version,
-                                     SyncChange::SyncChangeType type) {
-    supervised_user_service()->UpdateApprovedExtensions(extension_id, version,
-                                                        type);
-  }
-
   void CheckDisabledForCustodianApproval(const std::string& extension_id) {
     EXPECT_TRUE(registry()->disabled_extensions().Contains(extension_id));
     ExtensionPrefs* extension_prefs = ExtensionPrefs::Get(profile());
@@ -2053,13 +2043,12 @@
   base::FilePath path = data_dir().AppendASCII("good.crx");
   const Extension* extension = InstallCRX(path, INSTALL_NEW);
   std::string id = extension->id();
-  const std::string version("1.0.0.0");
 
   // The extension should be enabled.
   EXPECT_TRUE(registry()->enabled_extensions().Contains(id));
 
   // Simulate custodian approval.
-  SimulateApprovalChangeViaSync(id, version, SyncChange::ACTION_ADD);
+  supervised_user_service()->AddOrUpdateExtensionApproval(*extension);
   // The extension should still be enabled.
   EXPECT_TRUE(registry()->enabled_extensions().Contains(id));
 }
@@ -2154,7 +2143,8 @@
   std::string id = good_crx;
   std::string version("1.0.0.0");
 
-  SimulateApprovalChangeViaSync(id, version, SyncChange::ACTION_ADD);
+  supervised_user_service()->UpdateApprovedExtensionForTesting(
+      id, version, SupervisedUserService::ApprovedExtensionChange::kNew);
 
   // Now install an extension.
   base::FilePath path = data_dir().AppendASCII("good.crx");
@@ -2179,7 +2169,8 @@
   // Save the id, as the extension object will be destroyed during updating.
   std::string id = InstallNoPermissionsTestExtension();
   std::string version1("1");
-  SimulateApprovalChangeViaSync(id, version1, SyncChange::ACTION_ADD);
+  supervised_user_service()->UpdateApprovedExtensionForTesting(
+      id, version1, SupervisedUserService::ApprovedExtensionChange::kNew);
 
   // Update to a new version.
   std::string version2("2");
@@ -2215,7 +2206,8 @@
   // Simulate a custodian approval for re-enabling the extension coming in
   // through Sync, but set the old version. This can happen when there already
   // was a pending request for an earlier version of the extension.
-  SimulateApprovalChangeViaSync(id, version1, SyncChange::ACTION_ADD);
+  supervised_user_service()->UpdateApprovedExtensionForTesting(
+      id, version1, SupervisedUserService::ApprovedExtensionChange::kNew);
 
   // The re-enable should be ignored, since the version doesn't match.
   EXPECT_FALSE(registry()->enabled_extensions().Contains(id));
@@ -2245,7 +2237,8 @@
 
   // Simulate a custodian approval for re-enabling the extension coming in
   // through Sync.
-  SimulateApprovalChangeViaSync(id, version2, SyncChange::ACTION_ADD);
+  supervised_user_service()->UpdateApprovedExtensionForTesting(
+      id, version2, SupervisedUserService::ApprovedExtensionChange::kNew);
 
   // The extension should have gotten re-enabled.
   EXPECT_TRUE(registry()->enabled_extensions().Contains(id));
@@ -2280,7 +2273,8 @@
 
   std::string id = InstallPermissionsTestExtension();
   const std::string version1("1");
-  SimulateApprovalChangeViaSync(id, version1, SyncChange::ACTION_ADD);
+  supervised_user_service()->UpdateApprovedExtensionForTesting(
+      id, version1, SupervisedUserService::ApprovedExtensionChange::kNew);
 
   // Should see 1 kNewExtensionApprovalGranted metric count recorded.
   histogram_tester.ExpectUniqueSample(
@@ -2301,7 +2295,8 @@
   // Simulate a custodian approval for re-enabling the extension coming in
   // through Sync. Set a newer version than we have installed.
   const std::string version3("3");
-  SimulateApprovalChangeViaSync(id, version3, SyncChange::ACTION_UPDATE);
+  supervised_user_service()->UpdateApprovedExtensionForTesting(
+      id, version3, SupervisedUserService::ApprovedExtensionChange::kUpdate);
 
   // The re-enable should be delayed until the extension is updated to the
   // matching version.
@@ -2352,7 +2347,6 @@
       SetSupervisedUserExtensionsMayRequestPermissionsPref(true));
 
   base::FilePath path = data_dir().AppendASCII("good.crx");
-  std::string version("1.0.0.0");
 
   // Should be installed but disabled.
   const Extension* extension = InstallCRX(path, INSTALL_WITHOUT_LOAD);
@@ -2361,14 +2355,14 @@
   EXPECT_TRUE(registry()->disabled_extensions().Contains(good_crx));
   EXPECT_TRUE(IsPendingCustodianApproval(extension->id()));
 
-  SimulateApprovalChangeViaSync(good_crx, version, SyncChange::ACTION_ADD);
+  supervised_user_service()->AddOrUpdateExtensionApproval(*extension);
 
   // The extension should be enabled now.
   EXPECT_TRUE(registry()->enabled_extensions().Contains(good_crx));
   EXPECT_FALSE(IsPendingCustodianApproval(extension->id()));
 
   // Simulate approval removal coming via Sync.
-  SimulateApprovalChangeViaSync(good_crx, version, SyncChange::ACTION_DELETE);
+  supervised_user_service()->RemoveExtensionApproval(*extension);
 
   // The extension should be disabled now.
   EXPECT_TRUE(registry()->disabled_extensions().Contains(good_crx));
@@ -2388,7 +2382,8 @@
   std::string id = InstallNoPermissionsTestExtension();
   std::string version1("1");
 
-  SimulateApprovalChangeViaSync(id, version1, SyncChange::ACTION_ADD);
+  supervised_user_service()->UpdateApprovedExtensionForTesting(
+      id, version1, SupervisedUserService::ApprovedExtensionChange::kNew);
 
   // The extension should be enabled now.
   EXPECT_TRUE(registry()->enabled_extensions().Contains(id));
@@ -2407,14 +2402,14 @@
   // Check that the approved version has been updated in the prefs as well.
   // Prefs are updated via Sync.  If the prefs are updated, then the new
   // approved version has been pushed to Sync as well.
-  std::string approved_version;
-  std::string key = SupervisedUserSettingsService::MakeSplitSettingKey(
-      supervised_users::kApprovedExtensions, id);
+  PrefService* pref_service = profile()->GetPrefs();
+  ASSERT_TRUE(pref_service);
   const base::DictionaryValue* approved_extensions =
-      supervised_user_settings_service()->GetDictionaryAndSplitKey(&key);
-  approved_extensions->GetStringWithoutPathExpansion(id, &approved_version);
+      pref_service->GetDictionary(prefs::kSupervisedUserApprovedExtensions);
+  const std::string* approved_version = approved_extensions->FindStringKey(id);
+  ASSERT_TRUE(approved_version);
 
-  EXPECT_EQ(base::Version(approved_version), extension->version());
+  EXPECT_EQ(base::Version(*approved_version), extension->version());
   EXPECT_FALSE(IsPendingCustodianApproval(id));
 }
 
@@ -2431,7 +2426,8 @@
   std::string id = InstallPermissionsTestExtension();
   std::string version1("1");
 
-  SimulateApprovalChangeViaSync(id, version1, SyncChange::ACTION_ADD);
+  supervised_user_service()->UpdateApprovedExtensionForTesting(
+      id, version1, SupervisedUserService::ApprovedExtensionChange::kNew);
 
   // The extension should be enabled now.
   EXPECT_TRUE(registry()->enabled_extensions().Contains(id));
@@ -2447,7 +2443,8 @@
 
   std::string version2("2");
   // Approve an older version
-  SimulateApprovalChangeViaSync(id, version2, SyncChange::ACTION_UPDATE);
+  supervised_user_service()->UpdateApprovedExtensionForTesting(
+      id, version2, SupervisedUserService::ApprovedExtensionChange::kUpdate);
 
   // The extension should remain disabled.
   EXPECT_FALSE(registry()->enabled_extensions().Contains(id));
@@ -2458,7 +2455,8 @@
 
   EXPECT_TRUE(IsPendingCustodianApproval(id));
   // Approve the latest version
-  SimulateApprovalChangeViaSync(id, version3, SyncChange::ACTION_UPDATE);
+  supervised_user_service()->UpdateApprovedExtensionForTesting(
+      id, version3, SupervisedUserService::ApprovedExtensionChange::kUpdate);
 
   // The extension should be enabled again.
   EXPECT_TRUE(registry()->enabled_extensions().Contains(id));
@@ -2478,14 +2476,16 @@
   std::string id = InstallPermissionsTestExtension();
 
   std::string version1("1");
-  SimulateApprovalChangeViaSync(id, version1, SyncChange::ACTION_ADD);
+  supervised_user_service()->UpdateApprovedExtensionForTesting(
+      id, version1, SupervisedUserService::ApprovedExtensionChange::kNew);
 
   // The extension should be enabled now.
   EXPECT_TRUE(registry()->enabled_extensions().Contains(id));
 
   std::string version2("2");
   // Approve a newer version
-  SimulateApprovalChangeViaSync(id, version2, SyncChange::ACTION_UPDATE);
+  supervised_user_service()->UpdateApprovedExtensionForTesting(
+      id, version2, SupervisedUserService::ApprovedExtensionChange::kUpdate);
 
   // The extension should be disabled.
   CheckDisabledForCustodianApproval(id);
@@ -2554,7 +2554,8 @@
 
   // Now approve the extension.
   const std::string version1("1");
-  SimulateApprovalChangeViaSync(id, version1, SyncChange::ACTION_ADD);
+  supervised_user_service()->UpdateApprovedExtensionForTesting(
+      id, version1, SupervisedUserService::ApprovedExtensionChange::kNew);
 
   // The extension should be enabled now.
   EXPECT_TRUE(registry()->enabled_extensions().Contains(id));
@@ -2593,6 +2594,8 @@
   InitSupervisedUserExtensionInstallFeatures(
       SupervisedUserExtensionInstallFeatureMode::kFull);
 
+  base::HistogramTester histogram_tester;
+
   InitServices(/*profile_is_supervised=*/true);
 
   SetSupervisedUserExtensionsMayRequestPermissionsPref(true);
@@ -2600,7 +2603,17 @@
   std::string id = InstallPermissionsTestExtension();
   const std::string version1("1");
   // Simulate parent granting approval for the initial version.
-  SimulateApprovalChangeViaSync(id, version1, SyncChange::ACTION_ADD);
+  const Extension* extension1 = registry()->disabled_extensions().GetByID(id);
+  ASSERT_TRUE(extension1);
+  supervised_user_service()->AddOrUpdateExtensionApproval(*extension1);
+
+  // Should see 1 kNewExtensionApprovalGranted metric count recorded.
+  histogram_tester.ExpectUniqueSample(
+      "SupervisedUsers.Extensions",
+      SupervisedUserExtensionsMetricsRecorder::UmaExtensionState::
+          kNewExtensionApprovalGranted,
+      1);
+  histogram_tester.ExpectTotalCount("SupervisedUsers.Extensions", 1);
 
   // The extension should be enabled now.
   EXPECT_TRUE(registry()->enabled_extensions().Contains(id));
@@ -2611,8 +2624,18 @@
   UpdatePermissionsTestExtension(id, version2, DISABLED);
   EXPECT_TRUE(IsPendingCustodianApproval(id));
 
+  const Extension* extension2 = registry()->disabled_extensions().GetByID(id);
+  ASSERT_TRUE(extension2);
   // Simulate child granting approval for the new permissions.
-  SimulateApprovalChangeViaSync(id, version2, SyncChange::ACTION_UPDATE);
+  supervised_user_service()->AddOrUpdateExtensionApproval(*extension2);
+
+  // Should see 1 kNewVersionApprovalGranted metric count recorded.
+  histogram_tester.ExpectBucketCount(
+      "SupervisedUsers.Extensions",
+      SupervisedUserExtensionsMetricsRecorder::UmaExtensionState::
+          kNewVersionApprovalGranted,
+      1);
+  histogram_tester.ExpectTotalCount("SupervisedUsers.Extensions", 2);
 
   // The extension should be enabled now.
   EXPECT_TRUE(registry()->enabled_extensions().Contains(id));
@@ -2628,7 +2651,17 @@
   // The child should not be able to approve additional permissions when
   // kSupervisedUserExtensionsMayRequestPermissions is false, but suppose
   // somehow the child is able to circumvent controls and grant approval.
-  SimulateApprovalChangeViaSync(id, version3, SyncChange::ACTION_UPDATE);
+  const Extension* extension3 = registry()->disabled_extensions().GetByID(id);
+  ASSERT_TRUE(extension3);
+  supervised_user_service()->AddOrUpdateExtensionApproval(*extension3);
+
+  // Should see 1 kNewVersionApprovalGranted metric count recorded.
+  histogram_tester.ExpectBucketCount(
+      "SupervisedUsers.Extensions",
+      SupervisedUserExtensionsMetricsRecorder::UmaExtensionState::
+          kNewVersionApprovalGranted,
+      2);
+  histogram_tester.ExpectTotalCount("SupervisedUsers.Extensions", 3);
 
   // The extension should still be blocked.
   EXPECT_FALSE(registry()->enabled_extensions().Contains(id));
diff --git a/chrome/browser/extensions/standard_management_policy_provider.cc b/chrome/browser/extensions/standard_management_policy_provider.cc
index 46ca4e6..785ee71 100644
--- a/chrome/browser/extensions/standard_management_policy_provider.cc
+++ b/chrome/browser/extensions/standard_management_policy_provider.cc
@@ -85,19 +85,26 @@
 bool StandardManagementPolicyProvider::UserMayLoad(
     const Extension* extension,
     base::string16* error) const {
+  ExtensionManagement::InstallationMode installation_mode =
+      settings_->GetInstallationMode(extension);
+
+  // TODO(crbug.com/1065865): The special check for kOsSettingsAppId should be
+  // removed once OSSettings is moved to WebApps.
+#if defined(OS_CHROMEOS)
+  if (extension->id() == chromeos::default_web_apps::kOsSettingsAppId &&
+      (installation_mode == ExtensionManagement::INSTALLATION_BLOCKED ||
+       installation_mode == ExtensionManagement::INSTALLATION_REMOVED)) {
+    return ReturnLoadError(extension, error);
+  }
+#endif  // defined(OS_CHROMEOS)
+
   // Component extensions are always allowed, besides the camera app that can be
   // disabled by extension policy. This is a temporary solution until there's a
   // dedicated policy to disable the camera, at which point the special check in
   // the 'if' statement should be removed.
   // TODO(http://crbug.com/1002935)
-  // TODO(crbug.com/1065865): The special check for kOsSettingsAppId should be
-  // removed once OSSettings is moved to WebApps.
   if (Manifest::IsComponentLocation(extension->location()) &&
-      extension->id() != extension_misc::kCameraAppId
-#if defined(OS_CHROMEOS)
-      && extension->id() != chromeos::default_web_apps::kOsSettingsAppId
-#endif  // defined(OS_CHROMEOS)
-  ) {
+      extension->id() != extension_misc::kCameraAppId) {
     return true;
   }
 
@@ -112,13 +119,7 @@
   // by extension management policies. See crbug.com/786061.
   // TODO(calamity): This special case should be removed by removing bookmark
   // apps from external sources. See crbug.com/788245.
-  // TODO(crbug.com/1065865): The special check for kOsSettingsAppId should be
-  // removed once OSSettings is moved to WebApps.
-  if (extension->from_bookmark()
-#if defined(OS_CHROMEOS)
-      && extension->id() != chromeos::default_web_apps::kOsSettingsAppId
-#endif  // defined(OS_CHROMEOS)
-  )
+  if (extension->from_bookmark())
     return true;
 
   // Check whether the extension type is allowed.
@@ -148,8 +149,6 @@
       NOTREACHED();
   }
 
-  ExtensionManagement::InstallationMode installation_mode =
-      settings_->GetInstallationMode(extension);
   if (installation_mode == ExtensionManagement::INSTALLATION_BLOCKED ||
       installation_mode == ExtensionManagement::INSTALLATION_REMOVED) {
 #if BUILDFLAG(ENABLE_SUPERVISED_USERS)
diff --git a/chrome/browser/policy/extension_policy_browsertest.cc b/chrome/browser/policy/extension_policy_browsertest.cc
index ccecb13..23193649 100644
--- a/chrome/browser/policy/extension_policy_browsertest.cc
+++ b/chrome/browser/policy/extension_policy_browsertest.cc
@@ -373,6 +373,27 @@
             extension_prefs->GetDisableReasons(
                 chromeos::default_web_apps::kOsSettingsAppId));
 }
+
+// Ensure that OS Settings is not blocked by the ExtensionAllowedTypes policy.
+IN_PROC_BROWSER_TEST_F(ExtensionPolicyTest, ExtensionAllowedTypesOsSettings) {
+  extensions::ExtensionRegistry* registry = extension_registry();
+  const extensions::Extension* bookmark_app = InstallOSSettings();
+  ASSERT_TRUE(bookmark_app);
+  ASSERT_TRUE(registry->enabled_extensions().GetByID(
+      chromeos::default_web_apps::kOsSettingsAppId));
+
+  base::ListValue allowed_types;
+  allowed_types.AppendString("theme");
+  PolicyMap policies;
+  policies.Set(key::kExtensionAllowedTypes, POLICY_LEVEL_MANDATORY,
+               POLICY_SCOPE_USER, POLICY_SOURCE_CLOUD,
+               allowed_types.CreateDeepCopy(), nullptr);
+  UpdateProviderPolicy(policies);
+
+  extensions::ExtensionService* service = extension_service();
+  EXPECT_TRUE(service->IsExtensionEnabled(
+      chromeos::default_web_apps::kOsSettingsAppId));
+}
 #endif  // defined(OS_CHROMEOS)
 
 IN_PROC_BROWSER_TEST_F(ExtensionPolicyTest,
diff --git a/chrome/browser/policy/policy_browsertest.cc b/chrome/browser/policy/policy_browsertest.cc
index 737d73b..81278d2 100644
--- a/chrome/browser/policy/policy_browsertest.cc
+++ b/chrome/browser/policy/policy_browsertest.cc
@@ -60,6 +60,10 @@
 #include "base/values.h"
 #include "build/build_config.h"
 #include "chrome/app/chrome_command_ids.h"
+#include "chrome/browser/apps/app_service/app_launch_params.h"
+#include "chrome/browser/apps/app_service/app_service_proxy.h"
+#include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
+#include "chrome/browser/apps/app_service/browser_app_launcher.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/browser_process_platform_part.h"
 #include "chrome/browser/chrome_notification_types.h"
@@ -238,7 +242,6 @@
 #if !defined(OS_MACOSX)
 #include "base/compiler_specific.h"
 #include "chrome/browser/apps/app_service/app_launch_params.h"
-#include "chrome/browser/apps/launch_service/launch_service.h"
 #include "extensions/browser/app_window/app_window.h"
 #include "extensions/browser/app_window/app_window_registry.h"
 #include "extensions/browser/app_window/native_app_window.h"
@@ -1971,8 +1974,9 @@
   // Launch an app that tries to open a fullscreen window.
   TestAddAppWindowObserver add_window_observer(
       extensions::AppWindowRegistry::Get(browser()->profile()));
-  apps::LaunchService::Get(browser()->profile())
-      ->OpenApplication(apps::AppLaunchParams(
+  apps::AppServiceProxyFactory::GetForProfile(browser()->profile())
+      ->BrowserAppLauncher()
+      .LaunchAppWithParams(apps::AppLaunchParams(
           extension->id(), apps::mojom::LaunchContainer::kLaunchContainerNone,
           WindowOpenDisposition::NEW_WINDOW,
           apps::mojom::AppLaunchSource::kSourceTest));
diff --git a/chrome/browser/profiles/independent_otr_profile_manager.cc b/chrome/browser/profiles/independent_otr_profile_manager.cc
index 9cfc8a5e..5ef3bbe 100644
--- a/chrome/browser/profiles/independent_otr_profile_manager.cc
+++ b/chrome/browser/profiles/independent_otr_profile_manager.cc
@@ -10,6 +10,7 @@
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
 #include "base/stl_util.h"
+#include "base/strings/stringprintf.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/profiles/profile.h"
@@ -52,18 +53,17 @@
   DCHECK(!callback.is_null());
   if (!HasDependentProfiles(original_profile))
     observed_original_profiles_.Add(original_profile);
-  auto* otr_profile = original_profile->CreateOffTheRecordProfile();
+  // TODO(https://crbug.com/1033903): Receive profile id from the callers.
+  std::string profile_id = base::StringPrintf("profile::independent-otr-%i",
+                                              first_unused_unique_id_++);
+  auto* otr_profile = original_profile->GetOffTheRecordProfile(
+      Profile::OTRProfileID(profile_id));
   auto entry = refcounts_map_.emplace(otr_profile, 1);
   auto callback_entry =
       callbacks_map_.emplace(otr_profile, std::move(callback));
   DCHECK(entry.second);
   DCHECK(callback_entry.second);
 
-  content::NotificationService::current()->Notify(
-      chrome::NOTIFICATION_PROFILE_CREATED,
-      content::Source<Profile>(otr_profile),
-      content::NotificationService::NoDetails());
-
   return base::WrapUnique(new OTRProfileRegistration(this, otr_profile));
 }
 
@@ -101,7 +101,8 @@
   }
 }
 
-IndependentOTRProfileManager::IndependentOTRProfileManager() {
+IndependentOTRProfileManager::IndependentOTRProfileManager()
+    : first_unused_unique_id_(0) {
   BrowserList::AddObserver(this);
 }
 
diff --git a/chrome/browser/profiles/independent_otr_profile_manager.h b/chrome/browser/profiles/independent_otr_profile_manager.h
index bb3847b..1d804ae0 100644
--- a/chrome/browser/profiles/independent_otr_profile_manager.h
+++ b/chrome/browser/profiles/independent_otr_profile_manager.h
@@ -98,6 +98,10 @@
 
   ScopedObserver<Profile, ProfileObserver> observed_original_profiles_{this};
 
+  // TODO(https://crbug.com/1033903): Remove after getting unique id from
+  // owners.
+  int first_unused_unique_id_;
+
   DISALLOW_COPY_AND_ASSIGN(IndependentOTRProfileManager);
 };
 
diff --git a/chrome/browser/profiles/independent_otr_profile_manager_browsertest.cc b/chrome/browser/profiles/independent_otr_profile_manager_browsertest.cc
index 46cbd9c4..94185d5 100644
--- a/chrome/browser/profiles/independent_otr_profile_manager_browsertest.cc
+++ b/chrome/browser/profiles/independent_otr_profile_manager_browsertest.cc
@@ -166,72 +166,6 @@
 }
 
 IN_PROC_BROWSER_TEST_F(IndependentOTRProfileManagerTest,
-                       DeleteWaitsForLastBrowser) {
-  ProfileDestructionWatcher watcher;
-  Profile* otr_profile = nullptr;
-  Browser* otr_browser1 = nullptr;
-  Browser* otr_browser2 = nullptr;
-  {
-    auto profile_registration = manager_->CreateFromOriginalProfile(
-        browser()->profile(), base::BindOnce(&OriginalProfileNeverDestroyed));
-    otr_profile = profile_registration->profile();
-
-    otr_browser1 = CreateBrowser(otr_profile);
-    otr_browser2 = CreateBrowser(otr_profile);
-    ASSERT_NE(otr_browser1, otr_browser2);
-  }
-
-  base::RunLoop run_loop1;
-  BrowserRemovedWaiter removed_waiter1(otr_browser1,
-                                       run_loop1.QuitWhenIdleClosure());
-  otr_browser1->window()->Close();
-  run_loop1.Run();
-  ASSERT_FALSE(base::Contains(*BrowserList::GetInstance(), otr_browser1));
-  ASSERT_TRUE(base::Contains(*BrowserList::GetInstance(), otr_browser2));
-
-  watcher.Watch(otr_profile);
-  base::RunLoop run_loop2;
-  BrowserRemovedWaiter removed_waiter2(otr_browser2,
-                                       run_loop2.QuitWhenIdleClosure());
-  otr_browser2->window()->Close();
-  run_loop2.Run();
-  ASSERT_FALSE(base::Contains(*BrowserList::GetInstance(), otr_browser2));
-  EXPECT_TRUE(watcher.destroyed());
-}
-
-IN_PROC_BROWSER_TEST_F(IndependentOTRProfileManagerTest,
-                       DeleteImmediatelyWhenBrowsersAlreadyClosed) {
-  ProfileDestructionWatcher watcher;
-  {
-    auto profile_registration = manager_->CreateFromOriginalProfile(
-        browser()->profile(), base::BindOnce(&OriginalProfileNeverDestroyed));
-    auto* otr_profile = profile_registration->profile();
-
-    auto* otr_browser1 = CreateBrowser(otr_profile);
-    auto* otr_browser2 = CreateBrowser(otr_profile);
-    ASSERT_NE(otr_browser1, otr_browser2);
-
-    base::RunLoop run_loop1;
-    BrowserRemovedWaiter removed_waiter1(otr_browser1,
-                                         run_loop1.QuitWhenIdleClosure());
-    base::RunLoop run_loop2;
-    BrowserRemovedWaiter removed_waiter2(otr_browser2,
-                                         run_loop2.QuitWhenIdleClosure());
-    otr_browser1->window()->Close();
-    otr_browser2->window()->Close();
-    run_loop1.Run();
-    run_loop2.Run();
-    ASSERT_FALSE(base::Contains(*BrowserList::GetInstance(), otr_browser1));
-    ASSERT_FALSE(base::Contains(*BrowserList::GetInstance(), otr_browser2));
-
-    watcher.Watch(otr_profile);
-  }
-
-  base::RunLoop().RunUntilIdle();
-  EXPECT_TRUE(watcher.destroyed());
-}
-
-IN_PROC_BROWSER_TEST_F(IndependentOTRProfileManagerTest,
                        CreateTwoFromSameProfile) {
   ProfileDestructionWatcher watcher1;
   ProfileDestructionWatcher watcher2;
@@ -312,66 +246,6 @@
   EXPECT_TRUE(watcher2.destroyed());
 }
 
-IN_PROC_BROWSER_TEST_F(IndependentOTRProfileManagerTest,
-                       BrowserClosingDoesntRemoveProfileObserver) {
-  ProfileDestructionWatcher watcher1;
-  ProfileDestructionWatcher watcher2;
-
-  base::ScopedAllowBlockingForTesting allow_blocking;
-  ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
-#if defined(OS_CHROMEOS)
-  EnableProfileHelperTestSettings();
-#endif
-  original_profile_ = Profile::CreateProfile(temp_dir_.GetPath(), nullptr,
-                                             Profile::CREATE_MODE_SYNCHRONOUS);
-  ASSERT_TRUE(original_profile_);
-  auto profile_owner1 = RegistrationOwner(manager_, original_profile_.get());
-  auto* otr_profile1 = profile_owner1.profile();
-  Browser* otr_browser = nullptr;
-  {
-    auto profile_owner2 = RegistrationOwner(manager_, original_profile_.get());
-    auto* otr_profile2 = profile_owner2.profile();
-
-    otr_browser = CreateBrowser(otr_profile2);
-    watcher2.Watch(otr_profile2);
-  }
-  base::RunLoop run_loop;
-  BrowserRemovedWaiter removed_waiter(otr_browser,
-                                      run_loop.QuitWhenIdleClosure());
-  otr_browser->window()->Close();
-  run_loop.Run();
-  ASSERT_FALSE(base::Contains(*BrowserList::GetInstance(), otr_browser));
-  EXPECT_TRUE(watcher2.destroyed());
-
-  watcher1.Watch(otr_profile1);
-
-  SafelyDestroyOriginalProfile();
-
-  EXPECT_TRUE(watcher1.destroyed());
-}
-
-IN_PROC_BROWSER_TEST_F(IndependentOTRProfileManagerTest,
-                       CallbackNotCalledAfterUnregister) {
-  ProfileDestructionWatcher watcher;
-  Browser* otr_browser = nullptr;
-  Profile* otr_profile = nullptr;
-  {
-    auto profile_registration = manager_->CreateFromOriginalProfile(
-        browser()->profile(), base::BindOnce(&OriginalProfileNeverDestroyed));
-    otr_profile = profile_registration->profile();
-
-    otr_browser = CreateBrowser(otr_profile);
-  }
-  watcher.Watch(otr_profile);
-  base::RunLoop run_loop;
-  BrowserRemovedWaiter removed_waiter(otr_browser,
-                                      run_loop.QuitWhenIdleClosure());
-  otr_browser->window()->Close();
-  run_loop.Run();
-  ASSERT_FALSE(base::Contains(*BrowserList::GetInstance(), otr_browser));
-  EXPECT_TRUE(watcher.destroyed());
-}
-
 IN_PROC_BROWSER_TEST_F(IndependentOTRProfileManagerTest, Notifications) {
   // Create the OTR profile.
   content::WindowedNotificationObserver profile_created_observer(
diff --git a/chrome/browser/profiles/off_the_record_profile_impl.cc b/chrome/browser/profiles/off_the_record_profile_impl.cc
index 16c5fb0..313a0a7a3 100644
--- a/chrome/browser/profiles/off_the_record_profile_impl.cc
+++ b/chrome/browser/profiles/off_the_record_profile_impl.cc
@@ -120,8 +120,11 @@
 
 }  // namespace
 
-OffTheRecordProfileImpl::OffTheRecordProfileImpl(Profile* real_profile)
+OffTheRecordProfileImpl::OffTheRecordProfileImpl(
+    Profile* real_profile,
+    const OTRProfileID& otr_profile_id)
     : profile_(real_profile),
+      otr_profile_id_(otr_profile_id),
       io_data_(this),
       start_time_(base::Time::Now()),
       key_(std::make_unique<ProfileKey>(profile_->GetPath(),
@@ -295,21 +298,40 @@
   return true;
 }
 
+const Profile::OTRProfileID& OffTheRecordProfileImpl::GetOTRProfileID() const {
+  return otr_profile_id_;
+}
+
 bool OffTheRecordProfileImpl::IsIndependentOffTheRecordProfile() {
-  return !GetOriginalProfile()->HasOffTheRecordProfile() ||
-         GetOriginalProfile()->GetOffTheRecordProfile() != this;
+  return otr_profile_id_ != OTRProfileID::PrimaryID();
 }
 
-Profile* OffTheRecordProfileImpl::GetOffTheRecordProfile() {
-  return this;
+Profile* OffTheRecordProfileImpl::GetOffTheRecordProfile(
+    const OTRProfileID& otr_profile_id) {
+  if (otr_profile_id_ == otr_profile_id)
+    return this;
+  return profile_->GetOffTheRecordProfile(otr_profile_id);
 }
 
-void OffTheRecordProfileImpl::DestroyOffTheRecordProfile() {
-  // Suicide is bad!
+std::vector<Profile*> OffTheRecordProfileImpl::GetAllOffTheRecordProfiles() {
+  return profile_->GetAllOffTheRecordProfiles();
+}
+
+void OffTheRecordProfileImpl::DestroyOffTheRecordProfile(
+    Profile* /*otr_profile*/) {
+  // OffTheRecord profiles should be destroyed through a request to their
+  // original profile.
   NOTREACHED();
 }
 
-bool OffTheRecordProfileImpl::HasOffTheRecordProfile() {
+bool OffTheRecordProfileImpl::HasOffTheRecordProfile(
+    const OTRProfileID& otr_profile_id) {
+  if (otr_profile_id_ == otr_profile_id)
+    return true;
+  return profile_->HasOffTheRecordProfile(otr_profile_id);
+}
+
+bool OffTheRecordProfileImpl::HasAnyOffTheRecordProfile() {
   return true;
 }
 
@@ -587,7 +609,7 @@
 class GuestSessionProfile : public OffTheRecordProfileImpl {
  public:
   explicit GuestSessionProfile(Profile* real_profile)
-      : OffTheRecordProfileImpl(real_profile) {
+      : OffTheRecordProfileImpl(real_profile, OTRProfileID::PrimaryID()) {
     set_is_guest_profile(true);
   }
 
@@ -605,17 +627,19 @@
 };
 #endif
 
-Profile* Profile::CreateOffTheRecordProfile() {
-  OffTheRecordProfileImpl* profile = NULL;
+// static
+std::unique_ptr<Profile> Profile::CreateOffTheRecordProfile(
+    Profile* parent,
+    const OTRProfileID& otr_profile_id) {
+  std::unique_ptr<OffTheRecordProfileImpl> profile;
 #if defined(OS_CHROMEOS)
-  if (IsGuestSession())
-    profile = new GuestSessionProfile(this);
+  if (parent->IsGuestSession() && otr_profile_id == OTRProfileID::PrimaryID())
+    profile.reset(new GuestSessionProfile(parent));
 #endif
   if (!profile)
-    profile = new OffTheRecordProfileImpl(this);
+    profile.reset(new OffTheRecordProfileImpl(parent, otr_profile_id));
   profile->Init();
-  NotifyOffTheRecordProfileCreated(profile);
-  return profile;
+  return std::move(profile);
 }
 
 #if !defined(OS_ANDROID)
diff --git a/chrome/browser/profiles/off_the_record_profile_impl.h b/chrome/browser/profiles/off_the_record_profile_impl.h
index 4c34e6c..bdf1358 100644
--- a/chrome/browser/profiles/off_the_record_profile_impl.h
+++ b/chrome/browser/profiles/off_the_record_profile_impl.h
@@ -37,16 +37,23 @@
 ////////////////////////////////////////////////////////////////////////////////
 class OffTheRecordProfileImpl : public Profile {
  public:
-  explicit OffTheRecordProfileImpl(Profile* real_profile);
+  OffTheRecordProfileImpl(Profile* real_profile,
+                          const OTRProfileID& otr_profile_id);
   ~OffTheRecordProfileImpl() override;
   void Init();
 
   // Profile implementation.
   std::string GetProfileUserName() const override;
   ProfileType GetProfileType() const override;
-  Profile* GetOffTheRecordProfile() override;
-  void DestroyOffTheRecordProfile() override;
-  bool HasOffTheRecordProfile() override;
+  // TODO(https://crbug.com/1033903): Remove the default value.
+  Profile* GetOffTheRecordProfile(
+      const OTRProfileID& otr_profile_id = OTRProfileID::PrimaryID()) override;
+  std::vector<Profile*> GetAllOffTheRecordProfiles() override;
+  void DestroyOffTheRecordProfile(Profile* otr_profile) override;
+  // TODO(https://crbug.com/1033903): Remove the default value.
+  bool HasOffTheRecordProfile(
+      const OTRProfileID& otr_profile_id = OTRProfileID::PrimaryID()) override;
+  bool HasAnyOffTheRecordProfile() override;
   Profile* GetOriginalProfile() override;
   const Profile* GetOriginalProfile() const override;
   bool IsSupervised() const override;
@@ -100,6 +107,7 @@
   scoped_refptr<base::SequencedTaskRunner> GetIOTaskRunner() override;
   bool IsOffTheRecord() override;
   bool IsOffTheRecord() const override;
+  const OTRProfileID& GetOTRProfileID() const override;
   content::DownloadManagerDelegate* GetDownloadManagerDelegate() override;
   content::ResourceContext* GetResourceContext() override;
   content::BrowserPluginGuestManager* GetGuestManager() override;
@@ -142,6 +150,8 @@
   // The real underlying profile.
   Profile* profile_;
 
+  const OTRProfileID otr_profile_id_;
+
   std::unique_ptr<sync_preferences::PrefServiceSyncable> prefs_;
 
 #if !defined(OS_ANDROID)
diff --git a/chrome/browser/profiles/profile.cc b/chrome/browser/profiles/profile.cc
index 03f65f93..5321cda 100644
--- a/chrome/browser/profiles/profile.cc
+++ b/chrome/browser/profiles/profile.cc
@@ -103,6 +103,24 @@
 
 }  // namespace
 
+Profile::OTRProfileID::OTRProfileID(const std::string& profile_id)
+    : profile_id_(profile_id) {}
+
+// static
+const Profile::OTRProfileID Profile::OTRProfileID::PrimaryID() {
+  return OTRProfileID("profile::primary_otr");
+}
+
+const std::string& Profile::OTRProfileID::ToString() const {
+  return profile_id_;
+}
+
+std::ostream& operator<<(std::ostream& out,
+                         const Profile::OTRProfileID& profile_id) {
+  out << profile_id.ToString();
+  return out;
+}
+
 Profile::Profile()
     : restored_last_session_(false),
       sent_destroyed_notification_(false),
@@ -412,3 +430,10 @@
     chrome_variations_client_ = std::make_unique<ChromeVariationsClient>(this);
   return chrome_variations_client_.get();
 }
+
+void Profile::DestroyOffTheRecordProfile() {
+  OTRProfileID primary_otr_id = OTRProfileID::PrimaryID();
+  if (!HasOffTheRecordProfile(primary_otr_id))
+    return;
+  DestroyOffTheRecordProfile(GetOffTheRecordProfile(primary_otr_id));
+}
diff --git a/chrome/browser/profiles/profile.h b/chrome/browser/profiles/profile.h
index ee2f1e1b..4b47cd5 100644
--- a/chrome/browser/profiles/profile.h
+++ b/chrome/browser/profiles/profile.h
@@ -103,6 +103,39 @@
     GUEST_PROFILE,  // Guest session's profile
   };
 
+  class OTRProfileID {
+   public:
+    // Creates an OTR profile ID from |profile_id|.
+    // |profile_id| should follow the following naming scheme:
+    // "<component>::<subcomponent_id>". For example, "HaTS::WebDialog"
+    explicit OTRProfileID(const std::string& profile_id);
+
+    // ID used by the incognito and guest profiles.
+    // TODO(https://crbug.com/1033903): To be replaced with |IncognitoID| and
+    // |GuestID| when the use cases are reduced.
+    static const OTRProfileID PrimaryID();
+
+    bool operator==(const OTRProfileID& other) const {
+      return profile_id_ == other.profile_id_;
+    }
+
+    bool operator!=(const OTRProfileID& other) const {
+      return profile_id_ != other.profile_id_;
+    }
+
+    bool operator<(const OTRProfileID& other) const {
+      return profile_id_ < other.profile_id_;
+    }
+
+    // Returns this OTRProfileID in a string format that can be used for debug
+    // message.
+    const std::string& ToString() const;
+
+   private:
+    OTRProfileID() = default;
+    const std::string profile_id_;
+  };
+
   class Delegate {
    public:
     virtual ~Delegate();
@@ -150,6 +183,7 @@
   // Note that for Chrome this covers BOTH Incognito mode and Guest sessions.
   bool IsOffTheRecord() override = 0;
   virtual bool IsOffTheRecord() const = 0;
+  virtual const OTRProfileID& GetOTRProfileID() const = 0;
 
   variations::VariationsClient* GetVariationsClient() override;
 
@@ -169,27 +203,48 @@
   // implementations, this is usually the Google-services email address.
   virtual std::string GetProfileUserName() const = 0;
 
-  // Return the incognito version of this profile. The returned pointer
-  // is owned by the receiving profile. If the receiving profile is off the
-  // record, the same profile is returned.
+  // Return an OffTheRecord version of this profile with the given
+  // |otr_profile_id|. The returned pointer is owned by the receiving profile.
+  // If the receiving profile is OffTheRecord, the owner would be its original
+  // profile.
   //
-  // WARNING: This will create the OffTheRecord profile if it doesn't already
+  // WARNING I: This will create the OffTheRecord profile if it doesn't already
   // exist. If this isn't what you want, you need to check
   // HasOffTheRecordProfile() first.
-  virtual Profile* GetOffTheRecordProfile() = 0;
+  //
+  // WARNING II: Once a profile is no longer used, use
+  // ProfileDestroyer::DestroyProfileWhenAppropriate or
+  // ProfileDestroyer::DestroyOffTheRecordProfileNow to destroy it.
+  //
+  // TODO(https://crbug.com/1033903): Remove the default value.
+  virtual Profile* GetOffTheRecordProfile(
+      const OTRProfileID& otr_profile_id = OTRProfileID::PrimaryID()) = 0;
 
-  // Destroys the incognito profile.
-  virtual void DestroyOffTheRecordProfile() = 0;
+  // Returns all OffTheRecord profiles.
+  virtual std::vector<Profile*> GetAllOffTheRecordProfiles() = 0;
 
-  // True if an incognito profile exists.
-  virtual bool HasOffTheRecordProfile() = 0;
+  // Destroys the OffTheRecord profile.
+  virtual void DestroyOffTheRecordProfile(Profile* otr_profile) = 0;
+
+  // TODO(https://crbug.com/1033903): Remove this function when all the use
+  // cases are migrated to above version. The parameter-less version destroys
+  // the primary OffTheRecord profile.
+  void DestroyOffTheRecordProfile();
+
+  // True if an OffTheRecord profile with given id exists.
+  // TODO(https://crbug.com/1033903): Remove the default value.
+  virtual bool HasOffTheRecordProfile(
+      const OTRProfileID& otr_profile_id = OTRProfileID::PrimaryID()) = 0;
+
+  // Returns true if the profile has any OffTheRecord profiles.
+  virtual bool HasAnyOffTheRecordProfile() = 0;
 
   // Return the original "recording" profile. This method returns this if the
-  // profile is not incognito.
+  // profile is not OffTheRecord.
   virtual Profile* GetOriginalProfile() = 0;
 
   // Return the original "recording" profile. This method returns this if the
-  // profile is not incognito.
+  // profile is not OffTheRecord.
   virtual const Profile* GetOriginalProfile() const = 0;
 
   // Returns whether the profile is supervised (either a legacy supervised
@@ -223,11 +278,15 @@
   // Retrieves a pointer to the PrefService that manages the preferences
   // for OffTheRecord Profiles.  This PrefService is lazily created the first
   // time that this method is called.
+  // TODO(https://crbug.com/1065444): Investigate whether it's possible to
+  // remove.
   virtual PrefService* GetOffTheRecordPrefs() = 0;
 
   // Like GetOffTheRecordPrefs but gives a read-only view of prefs that can be
   // used even if there's no OTR profile at the moment
   // (i.e. HasOffTheRecordProfile is false).
+  // TODO(https://crbug.com/1065444): Investigate whether it's possible to
+  // remove.
   virtual PrefService* GetReadOnlyOffTheRecordPrefs();
 
   // Returns the main URLLoaderFactory.
@@ -236,7 +295,7 @@
 
   // Return whether 2 profiles are the same. 2 profiles are the same if they
   // represent the same profile. This can happen if there is pointer equality
-  // or if one profile is the incognito version of another profile (or vice
+  // or if one profile is the OffTheRecord version of another profile (or vice
   // versa).
   virtual bool IsSameProfile(Profile* profile) = 0;
 
@@ -330,14 +389,16 @@
 
   // Returns whether it is an Incognito profile. An Incognito profile is an
   // off-the-record profile that is not a guest profile.
+  //
+  // TODO(https://crbug.com/1033903): Update to return false for non-primary
+  // OTRs and update documentation above.
   bool IsIncognitoProfile() const;
 
-  // Returns true if this is an off the record profile that is independent from
-  // its original regular profile. This covers OTR profiles that are directly
-  // created using CreateOffTheRecordProfile() (such as done by
-  // IndependentOTRProfileManager). Calling GetOffTheRecordProfile on their
-  // GetOriginProfile will not point to themselves.
-  // This type of usage is not recommended.
+  // Returns true if this is a non-primary OffTheRecord profile. This type of
+  // OffTheRecord profiles have limited functionality and cannot be used to
+  // create a browser object.
+  //
+  // TODO(https://crbug.com/1033903): Rename to |CanSupportBrowsers|.
   virtual bool IsIndependentOffTheRecordProfile() = 0;
 
   // Returns whether it is a guest session. This covers both the guest profile
@@ -407,11 +468,6 @@
   // ProfileDestroyer, but in tests, some are not.
   void MaybeSendDestroyedNotification();
 
-  // Creates an OffTheRecordProfile which points to this Profile. The caller is
-  // responsible for sending a NOTIFICATION_PROFILE_CREATED when the profile is
-  // correctly assigned to its owner.
-  Profile* CreateOffTheRecordProfile();
-
 #if !defined(OS_ANDROID)
   // Convenience method to retrieve the default zoom level for the default
   // storage partition.
@@ -435,6 +491,13 @@
     is_system_profile_ = is_system_profile;
   }
 
+  // Creates an OffTheRecordProfile which points to this Profile. The caller is
+  // responsible for sending a NOTIFICATION_PROFILE_CREATED when the profile is
+  // correctly assigned to its owner.
+  static std::unique_ptr<Profile> CreateOffTheRecordProfile(
+      Profile* parent,
+      const OTRProfileID& otr_profile_id);
+
   // Returns a newly created ExtensionPrefStore suitable for the supplied
   // Profile.
   static PrefStore* CreateExtensionPrefStore(Profile*,
@@ -472,4 +535,7 @@
   bool operator()(Profile* a, Profile* b) const;
 };
 
+std::ostream& operator<<(std::ostream& out,
+                         const Profile::OTRProfileID& profile_id);
+
 #endif  // CHROME_BROWSER_PROFILES_PROFILE_H_
diff --git a/chrome/browser/profiles/profile_browsertest.cc b/chrome/browser/profiles/profile_browsertest.cc
index 67f9e50..92e3e93 100644
--- a/chrome/browser/profiles/profile_browsertest.cc
+++ b/chrome/browser/profiles/profile_browsertest.cc
@@ -21,6 +21,7 @@
 #include "base/path_service.h"
 #include "base/run_loop.h"
 #include "base/sequenced_task_runner.h"
+#include "base/stl_util.h"
 #include "base/synchronization/waitable_event.h"
 #include "base/task/post_task.h"
 #include "base/task/thread_pool/thread_pool_instance.h"
@@ -33,8 +34,10 @@
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/profiles/chrome_version_service.h"
+#include "chrome/browser/profiles/profile_destroyer.h"
 #include "chrome/browser/profiles/profile_impl.h"
 #include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/profiles/profile_observer.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/common/chrome_constants.h"
@@ -131,6 +134,29 @@
   MOCK_METHOD3(OnProfileCreated, void(Profile*, bool, bool));
 };
 
+class ProfileDestructionWatcher : public ProfileObserver {
+ public:
+  ProfileDestructionWatcher() = default;
+  ~ProfileDestructionWatcher() override = default;
+
+  void Watch(Profile* profile) { observed_profiles_.Add(profile); }
+
+  // ProfileObserver:
+  void OnProfileWillBeDestroyed(Profile* profile) override {
+    DCHECK(!destroyed_) << "Double profile destruction";
+    destroyed_ = true;
+    observed_profiles_.Remove(profile);
+  }
+
+  bool destroyed() const { return destroyed_; }
+
+ private:
+  bool destroyed_ = false;
+  ScopedObserver<Profile, ProfileObserver> observed_profiles_{this};
+
+  DISALLOW_COPY_AND_ASSIGN(ProfileDestructionWatcher);
+};
+
 // Creates a prefs file in the given directory.
 void CreatePrefsFileInDirectory(const base::FilePath& directory_path) {
   base::FilePath pref_path(directory_path.Append(chrome::kPreferencesFilename));
@@ -684,3 +710,106 @@
   // let them complete before |temp_dir| goes out of scope.
   FlushIoTaskRunnerAndSpinThreads();
 }
+
+// Verifies creating an OTR with non-primary id results in a different profile
+// from incognito profile.
+IN_PROC_BROWSER_TEST_F(ProfileBrowserTest, CreateNonPrimaryOTR) {
+  Profile::OTRProfileID otr_profile_id("profile::otr");
+
+  Profile* regular_profile = browser()->profile();
+  EXPECT_FALSE(regular_profile->HasAnyOffTheRecordProfile());
+
+  Profile* otr_profile =
+      regular_profile->GetOffTheRecordProfile(otr_profile_id);
+  EXPECT_TRUE(regular_profile->HasAnyOffTheRecordProfile());
+  EXPECT_TRUE(otr_profile->IsOffTheRecord());
+  EXPECT_EQ(otr_profile_id, otr_profile->GetOTRProfileID());
+  EXPECT_TRUE(regular_profile->HasOffTheRecordProfile(otr_profile_id));
+  EXPECT_NE(otr_profile, regular_profile->GetOffTheRecordProfile(
+                             Profile::OTRProfileID::PrimaryID()));
+
+  regular_profile->DestroyOffTheRecordProfile(otr_profile);
+  EXPECT_FALSE(regular_profile->HasOffTheRecordProfile(otr_profile_id));
+  EXPECT_TRUE(regular_profile->HasOffTheRecordProfile(
+      Profile::OTRProfileID::PrimaryID()));
+  EXPECT_TRUE(regular_profile->HasAnyOffTheRecordProfile());
+}
+
+// Verifies creating two OTRs with different ids results in different profiles.
+IN_PROC_BROWSER_TEST_F(ProfileBrowserTest, CreateTwoNonPrimaryOTRs) {
+  Profile::OTRProfileID otr_profile_id1("profile::otr1");
+  Profile::OTRProfileID otr_profile_id2("profile::otr2");
+
+  Profile* regular_profile = browser()->profile();
+
+  Profile* otr_profile1 =
+      regular_profile->GetOffTheRecordProfile(otr_profile_id1);
+  Profile* otr_profile2 =
+      regular_profile->GetOffTheRecordProfile(otr_profile_id2);
+
+  EXPECT_NE(otr_profile1, otr_profile2);
+  EXPECT_TRUE(regular_profile->HasOffTheRecordProfile(otr_profile_id1));
+  EXPECT_TRUE(regular_profile->HasOffTheRecordProfile(otr_profile_id2));
+
+  regular_profile->DestroyOffTheRecordProfile(otr_profile1);
+  EXPECT_FALSE(regular_profile->HasOffTheRecordProfile(otr_profile_id1));
+  EXPECT_TRUE(regular_profile->HasOffTheRecordProfile(otr_profile_id2));
+}
+
+// Verifies destroying regular profile will result in destruction of OTR
+// profiles.
+IN_PROC_BROWSER_TEST_F(ProfileBrowserTest, DestroyRegularProfileBeforeOTRs) {
+  Profile::OTRProfileID otr_profile_id1("profile::otr1");
+  Profile::OTRProfileID otr_profile_id2("profile::otr2");
+
+  base::ScopedAllowBlockingForTesting allow_blocking;
+  base::ScopedTempDir temp_dir;
+  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+
+  MockProfileDelegate delegate;
+  std::unique_ptr<Profile> regular_profile(CreateProfile(
+      temp_dir.GetPath(), &delegate, Profile::CREATE_MODE_SYNCHRONOUS));
+
+  // Creating a profile causes an implicit connection attempt to a Mojo
+  // service, which occurs as part of a new task. Before deleting |profile|,
+  // ensure this task runs to prevent a crash.
+  FlushIoTaskRunnerAndSpinThreads();
+
+  Profile* otr_profile1 =
+      regular_profile->GetOffTheRecordProfile(otr_profile_id1);
+  Profile* otr_profile2 =
+      regular_profile->GetOffTheRecordProfile(otr_profile_id2);
+
+  ProfileDestructionWatcher watcher1;
+  ProfileDestructionWatcher watcher2;
+  watcher1.Watch(otr_profile1);
+  watcher2.Watch(otr_profile2);
+
+  ProfileDestroyer::DestroyProfileWhenAppropriate(regular_profile.release());
+
+  EXPECT_TRUE(watcher1.destroyed());
+  EXPECT_TRUE(watcher2.destroyed());
+}
+
+// Tests Profile::GetAllOffTheRecordProfiles
+IN_PROC_BROWSER_TEST_F(ProfileBrowserTest, TestGetAllOffTheRecordProfiles) {
+  Profile::OTRProfileID otr_profile_id1("profile::otr1");
+  Profile::OTRProfileID otr_profile_id2("profile::otr2");
+
+  Profile* regular_profile = browser()->profile();
+
+  Profile* otr_profile1 =
+      regular_profile->GetOffTheRecordProfile(otr_profile_id1);
+  Profile* otr_profile2 =
+      regular_profile->GetOffTheRecordProfile(otr_profile_id2);
+  Profile* incognito_profile = regular_profile->GetOffTheRecordProfile(
+      Profile::OTRProfileID::PrimaryID());
+
+  std::vector<Profile*> all_otrs =
+      regular_profile->GetAllOffTheRecordProfiles();
+
+  EXPECT_EQ(3u, all_otrs.size());
+  EXPECT_TRUE(base::Contains(all_otrs, otr_profile1));
+  EXPECT_TRUE(base::Contains(all_otrs, otr_profile2));
+  EXPECT_TRUE(base::Contains(all_otrs, incognito_profile));
+}
diff --git a/chrome/browser/profiles/profile_destroyer.cc b/chrome/browser/profiles/profile_destroyer.cc
index 88c6128e..bcf58358 100644
--- a/chrome/browser/profiles/profile_destroyer.cc
+++ b/chrome/browser/profiles/profile_destroyer.cc
@@ -41,7 +41,7 @@
   // anyway, so we can't iterate them via AllHostsIterator anyway.
   if (profile->AsTestingProfile()) {
     if (profile->IsOffTheRecord())
-      profile->GetOriginalProfile()->DestroyOffTheRecordProfile();
+      profile->GetOriginalProfile()->DestroyOffTheRecordProfile(profile);
     else
       delete profile;
     return;
@@ -64,10 +64,7 @@
       // hosts referring to it are properly terminated.
       new ProfileDestroyer(profile, &profile_hosts);
     } else {
-      if (profile->IsIndependentOffTheRecordProfile())
-        delete profile;
-      else
-        profile->GetOriginalProfile()->DestroyOffTheRecordProfile();
+      profile->GetOriginalProfile()->DestroyOffTheRecordProfile(profile);
     }
     return;
   }
@@ -80,6 +77,8 @@
       profile_has_off_the_record ? profile->GetOffTheRecordProfile() : nullptr;
 #endif  // DCHECK_IS_ON()
 
+  // TODO(https://crbug.com/1033903): If profile has OTRs and they have hosts,
+  // create a |ProfileDestroyer| instead.
   delete profile;
 
 #if DCHECK_IS_ON()
@@ -127,11 +126,7 @@
     }
   }
 
-  if (profile->IsIndependentOffTheRecordProfile()) {
-    delete profile;
-  } else {
-    profile->GetOriginalProfile()->DestroyOffTheRecordProfile();
-  }
+  profile->GetOriginalProfile()->DestroyOffTheRecordProfile(profile);
 }
 
 ProfileDestroyer::ProfileDestroyer(Profile* const profile, HostSet* hosts)
diff --git a/chrome/browser/profiles/profile_impl.cc b/chrome/browser/profiles/profile_impl.cc
index 21cdcfacf..0a75e9d 100644
--- a/chrome/browser/profiles/profile_impl.cc
+++ b/chrome/browser/profiles/profile_impl.cc
@@ -28,6 +28,7 @@
 #include "base/no_destructor.h"
 #include "base/path_service.h"
 #include "base/sequenced_task_runner.h"
+#include "base/stl_util.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
@@ -776,16 +777,26 @@
   ChromePluginServiceFilter::GetInstance()->UnregisterProfile(this);
 #endif
 
-  // Destroy OTR profile and its profile services first.
-  if (off_the_record_profile_) {
-    ProfileDestroyer::DestroyOffTheRecordProfileNow(
-        off_the_record_profile_.get());
-  } else {
+  // Destroy all OTR profiles and their profile services first.
+  std::vector<Profile*> raw_otr_profiles;
+  bool primary_otr_available = false;
+
+  // Get a list of existing OTR profiles since |off_the_record_profile_| might
+  // be modified after the call to |DestroyOffTheRecordProfileNow|.
+  for (auto& otr_profile : otr_profiles_) {
+    raw_otr_profiles.push_back(otr_profile.second.get());
+    primary_otr_available |= (otr_profile.first == OTRProfileID::PrimaryID());
+  }
+
+  for (Profile* otr_profile : raw_otr_profiles)
+    ProfileDestroyer::DestroyOffTheRecordProfileNow(otr_profile);
+
 #if BUILDFLAG(ENABLE_EXTENSIONS)
+  if (!primary_otr_available) {
     ExtensionPrefValueMapFactory::GetForBrowserContext(this)
         ->ClearAllIncognitoSessionOnlyPreferences();
-#endif
   }
+#endif
 
   FullBrowserTransitionManager::Get()->OnProfileDestroyed(this);
 
@@ -858,33 +869,66 @@
   return false;
 }
 
+const Profile::OTRProfileID& ProfileImpl::GetOTRProfileID() const {
+  NOTREACHED();
+  static base::NoDestructor<OTRProfileID> otr_profile_id(
+      "ProfileImp::NoOTRProfileID");
+  return *otr_profile_id;
+}
+
 bool ProfileImpl::IsIndependentOffTheRecordProfile() {
   return false;
 }
 
-Profile* ProfileImpl::GetOffTheRecordProfile() {
-  if (!off_the_record_profile_) {
-    std::unique_ptr<Profile> p(CreateOffTheRecordProfile());
-    off_the_record_profile_.swap(p);
+Profile* ProfileImpl::GetOffTheRecordProfile(
+    const OTRProfileID& otr_profile_id) {
+  if (HasOffTheRecordProfile(otr_profile_id))
+    return otr_profiles_[otr_profile_id].get();
 
-    content::NotificationService::current()->Notify(
-        chrome::NOTIFICATION_PROFILE_CREATED,
-        content::Source<Profile>(off_the_record_profile_.get()),
-        content::NotificationService::NoDetails());
-  }
-  return off_the_record_profile_.get();
+  // Create a new OffTheRecordProfile
+  std::unique_ptr<Profile> otr_profile =
+      Profile::CreateOffTheRecordProfile(this, otr_profile_id);
+  Profile* raw_otr_profile = otr_profile.get();
+
+  otr_profiles_[otr_profile_id] = std::move(otr_profile);
+
+  NotifyOffTheRecordProfileCreated(raw_otr_profile);
+
+  content::NotificationService::current()->Notify(
+      chrome::NOTIFICATION_PROFILE_CREATED,
+      content::Source<Profile>(raw_otr_profile),
+      content::NotificationService::NoDetails());
+
+  return raw_otr_profile;
 }
 
-void ProfileImpl::DestroyOffTheRecordProfile() {
-  off_the_record_profile_.reset();
+std::vector<Profile*> ProfileImpl::GetAllOffTheRecordProfiles() {
+  std::vector<Profile*> raw_otr_profiles;
+  for (auto& otr : otr_profiles_)
+    raw_otr_profiles.push_back(otr.second.get());
+  return raw_otr_profiles;
+}
+
+void ProfileImpl::DestroyOffTheRecordProfile(Profile* otr_profile) {
+  CHECK(otr_profile);
+  OTRProfileID profile_id = otr_profile->GetOTRProfileID();
+  DCHECK(HasOffTheRecordProfile(profile_id));
+  otr_profiles_.erase(profile_id);
 #if BUILDFLAG(ENABLE_EXTENSIONS)
-  ExtensionPrefValueMapFactory::GetForBrowserContext(this)
-      ->ClearAllIncognitoSessionOnlyPreferences();
+  // Extensions are only supported on primary OTR profile.
+  if (profile_id == OTRProfileID::PrimaryID()) {
+    ExtensionPrefValueMapFactory::GetForBrowserContext(this)
+        ->ClearAllIncognitoSessionOnlyPreferences();
+  }
 #endif
 }
 
-bool ProfileImpl::HasOffTheRecordProfile() {
-  return off_the_record_profile_.get() != NULL;
+bool ProfileImpl::HasOffTheRecordProfile(const OTRProfileID& otr_profile_id) {
+  return base::Contains(otr_profiles_, otr_profile_id);
+}
+
+bool ProfileImpl::HasAnyOffTheRecordProfile() {
+  return !otr_profiles_.empty();
 }
 
 Profile* ProfileImpl::GetOriginalProfile() {
@@ -1297,8 +1341,8 @@
 bool ProfileImpl::IsSameProfile(Profile* profile) {
   if (profile == static_cast<Profile*>(this))
     return true;
-  Profile* otr_profile = off_the_record_profile_.get();
-  return otr_profile && profile == otr_profile;
+
+  return profile && profile->GetOriginalProfile() == this;
 }
 
 base::Time ProfileImpl::GetStartTime() const {
diff --git a/chrome/browser/profiles/profile_impl.h b/chrome/browser/profiles/profile_impl.h
index 326e2e9..6cb65e3 100644
--- a/chrome/browser/profiles/profile_impl.h
+++ b/chrome/browser/profiles/profile_impl.h
@@ -114,10 +114,17 @@
   base::Time GetCreationTime() const override;
   bool IsOffTheRecord() override;
   bool IsOffTheRecord() const override;
+  const OTRProfileID& GetOTRProfileID() const override;
   base::FilePath GetPath() const override;
-  Profile* GetOffTheRecordProfile() override;
-  void DestroyOffTheRecordProfile() override;
-  bool HasOffTheRecordProfile() override;
+  // TODO(https://crbug.com/1033903): Remove the default value.
+  Profile* GetOffTheRecordProfile(
+      const OTRProfileID& otr_profile_id = OTRProfileID::PrimaryID()) override;
+  std::vector<Profile*> GetAllOffTheRecordProfiles() override;
+  void DestroyOffTheRecordProfile(Profile* otr_profile) override;
+  // TODO(https://crbug.com/1033903): Remove the default value.
+  bool HasOffTheRecordProfile(
+      const OTRProfileID& otr_profile_id = OTRProfileID::PrimaryID()) override;
+  bool HasAnyOffTheRecordProfile() override;
   Profile* GetOriginalProfile() override;
   const Profile* GetOriginalProfile() const override;
   bool IsSupervised() const override;
@@ -284,7 +291,7 @@
   base::OneShotTimer create_session_service_timer_;
 #endif
 
-  std::unique_ptr<Profile> off_the_record_profile_;
+  std::map<OTRProfileID, std::unique_ptr<Profile>> otr_profiles_;
 
   // See GetStartTime for details.
   base::Time start_time_;
diff --git a/chrome/browser/renderer_context_menu/render_view_context_menu.cc b/chrome/browser/renderer_context_menu/render_view_context_menu.cc
index bc682e4..5652fbd 100644
--- a/chrome/browser/renderer_context_menu/render_view_context_menu.cc
+++ b/chrome/browser/renderer_context_menu/render_view_context_menu.cc
@@ -27,6 +27,10 @@
 #include "chrome/app/chrome_command_ids.h"
 #include "chrome/app/vector_icons/vector_icons.h"
 #include "chrome/browser/app_mode/app_mode_utils.h"
+#include "chrome/browser/apps/app_service/app_launch_params.h"
+#include "chrome/browser/apps/app_service/app_service_proxy.h"
+#include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
+#include "chrome/browser/apps/app_service/browser_app_launcher.h"
 #include "chrome/browser/apps/platform_apps/app_load_service.h"
 #include "chrome/browser/autocomplete/autocomplete_classifier_factory.h"
 #include "chrome/browser/browser_process.h"
@@ -162,7 +166,6 @@
 
 #if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "chrome/browser/apps/app_service/app_launch_params.h"
-#include "chrome/browser/apps/launch_service/launch_service.h"
 #include "chrome/browser/extensions/devtools_util.h"
 #include "chrome/browser/extensions/extension_service.h"
 #include "chrome/common/extensions/api/url_handlers/url_handlers_parser.h"
@@ -2690,7 +2693,9 @@
       WindowOpenDisposition::CURRENT_TAB,
       apps::mojom::AppLaunchSource::kSourceContextMenu);
   launch_params.override_url = params_.link_url;
-  apps::LaunchService::Get(GetProfile())->OpenApplication(launch_params);
+  apps::AppServiceProxyFactory::GetForProfile(GetProfile())
+      ->BrowserAppLauncher()
+      .LaunchAppWithParams(launch_params);
 }
 
 void RenderViewContextMenu::ExecProtocolHandler(int event_flags,
diff --git a/chrome/browser/resources/chromeos/edu_login/edu_login_css.html b/chrome/browser/resources/chromeos/edu_login/edu_login_css.html
index c4e89ef3..615dbed 100644
--- a/chrome/browser/resources/chromeos/edu_login/edu_login_css.html
+++ b/chrome/browser/resources/chromeos/edu_login/edu_login_css.html
@@ -8,11 +8,12 @@
       font-size: 2em;
       font-weight: normal;
       margin-bottom: 16px;
-      margin-top: 28px;
+      margin-top: 20px;
     }
 
     p {
       line-height: 1.5;
+      margin: 0;
       white-space: pre-line;
     }
 
diff --git a/chrome/browser/resources/chromeos/edu_login/edu_login_parent_info.html b/chrome/browser/resources/chromeos/edu_login/edu_login_parent_info.html
index 098cfeb2..db1456e 100644
--- a/chrome/browser/resources/chromeos/edu_login/edu_login_parent_info.html
+++ b/chrome/browser/resources/chromeos/edu_login/edu_login_parent_info.html
@@ -6,8 +6,8 @@
   .info-item {
     display: flex;
     flex: auto;
-    padding-bottom: 10px;
-    padding-top: 10px;
+    padding-bottom: 16px;
+    padding-top: 16px;
   }
 
   .info-item:not(:last-child) {
@@ -16,11 +16,13 @@
 
   .info-icon {
     flex-basis: 24px;
-    padding-top: 10px;
   }
 
   .info-title {
+    display: flex;
     flex: 1;
+    flex-direction: column;
+    justify-content: center;
     padding-inline-start: 24px;
   }
 </style>
@@ -29,8 +31,8 @@
   <span slot="main">
     <div class="main-padding">
       <if expr="_google_chrome">
-        <img id="google-full-logo" src="chrome://theme/IDR_LOGO_GOOGLE_COLOR_90"
-          alt="">
+        <img class="google-full-logo"
+            src="chrome://theme/IDR_LOGO_GOOGLE_COLOR_90" alt="">
       </if>
 
       <h1>$i18n{parentInfoTitle}</h1>
diff --git a/chrome/browser/resources/chromeos/edu_login/edu_login_parent_signin.html b/chrome/browser/resources/chromeos/edu_login/edu_login_parent_signin.html
index b59031a..1259845 100644
--- a/chrome/browser/resources/chromeos/edu_login/edu_login_parent_signin.html
+++ b/chrome/browser/resources/chromeos/edu_login/edu_login_parent_signin.html
@@ -8,6 +8,7 @@
     display: inline-flex;
     font-weight: 500;
     letter-spacing: .25px;
+    margin-bottom: 16px;
     max-width: 100%;
     min-width: 240px;
     padding: 3px 5px;
@@ -44,6 +45,7 @@
 
   .input-group {
     margin-bottom: 40px;
+    margin-top: 32px;
   }
 
   .field-wrapper {
@@ -136,8 +138,8 @@
   <span slot="main">
     <div class="main-padding">
       <if expr="_google_chrome">
-        <img id="google-full-logo" src="chrome://theme/IDR_LOGO_GOOGLE_COLOR_90"
-            alt="">
+        <img class="google-full-logo"
+            src="chrome://theme/IDR_LOGO_GOOGLE_COLOR_90" alt="">
       </if>
       <h1>[[getParentSigninTitle_(parent)]]</h1>
 
diff --git a/chrome/browser/resources/chromeos/edu_login/edu_login_parents.html b/chrome/browser/resources/chromeos/edu_login/edu_login_parents.html
index a9c57b6..f3d84b95 100644
--- a/chrome/browser/resources/chromeos/edu_login/edu_login_parents.html
+++ b/chrome/browser/resources/chromeos/edu_login/edu_login_parents.html
@@ -31,6 +31,10 @@
     height: var(--profile-icon-size);
     width: var(--profile-icon-size);
   }
+
+  #parentsListBody {
+    margin-bottom: 16px;
+  }
 </style>
 
 <edu-login-template>
@@ -41,8 +45,8 @@
             src="chrome://theme/IDR_LOGO_GOOGLE_COLOR_90" alt="">
       </if>
       <h1>$i18n{parentsListTitle}</h1>
-      <p id="parentsListBody" aria-hidden="true" class="secondary">
-        $i18n{parentsListBody}
+      <p id="parentsListBody" aria-hidden="true"
+          class="secondary">$i18n{parentsListBody}
       </p>
     </div>
     <div tabindex="0" role="listbox" aria-labelledby="parentsListBody">
diff --git a/chrome/browser/resources/chromeos/edu_login/edu_login_welcome.html b/chrome/browser/resources/chromeos/edu_login/edu_login_welcome.html
index 332ef0b..f6a36e0 100644
--- a/chrome/browser/resources/chromeos/edu_login/edu_login_welcome.html
+++ b/chrome/browser/resources/chromeos/edu_login/edu_login_welcome.html
@@ -2,6 +2,7 @@
   .main-padding {
     display: flex;
     flex-direction: column;
+    height: calc(100% - 90px);
   }
 
   .image-container {
diff --git a/chrome/browser/resources/chromeos/login/components/progress_list_item.css b/chrome/browser/resources/chromeos/login/components/progress_list_item.css
index 5e8a09f..fbf4f21 100644
--- a/chrome/browser/resources/chromeos/login/components/progress_list_item.css
+++ b/chrome/browser/resources/chromeos/login/components/progress_list_item.css
@@ -3,7 +3,6 @@
  * found in the LICENSE file. */
 
 #container {
-  align-items: center;
   height: 56px;
 }
 
@@ -15,7 +14,7 @@
   background-color: var(--google-grey-500);
   border-radius: 50%;
   height: 8px;
-  margin-start: 8px;
+  margin-inline-start: 8px;
   width: 8px;
 }
 
diff --git a/chrome/browser/resources/chromeos/login/components/progress_list_item.html b/chrome/browser/resources/chromeos/login/components/progress_list_item.html
index 67290f1..715cce4 100644
--- a/chrome/browser/resources/chromeos/login/components/progress_list_item.html
+++ b/chrome/browser/resources/chromeos/login/components/progress_list_item.html
@@ -25,30 +25,30 @@
 -->
 <dom-module id="progress-list-item">
   <template>
-    <template>
-      <link rel="stylesheet" href="progress_list_item.css">
-      <div class="flex layout horizontal center" id="container">
-        <div id="icon">
-          <div id="icon-pending" class="dot"
-              hidden="[[hidePending(active, completed)]]"></div>
-          <paper-spinner-lite id="icon-active" hidden="[[!active]]">
-          </paper-spinner-lite>
-          <iron-icon id="icon-completed" icon="cr:check"
-               hidden="[[hideCompleted(active, completed)]]"></iron-icon>
+    <link rel="stylesheet" href="progress_list_item.css">
+    <link rel="stylesheet" href="../oobe_flex_layout.css">
+
+    <div class="flex layout horizontal center" id="container" role="listitem">
+      <div id="icon">
+        <div id="icon-pending" class="dot"
+            hidden="[[hidePending_(active, completed)]]"></div>
+        <paper-spinner-lite id="icon-active" hidden="[[!active]]" active>
+        </paper-spinner-lite>
+        <iron-icon id="icon-completed" icon="cr:check"
+              hidden="[[hideCompleted_(active, completed)]]"></iron-icon>
+      </div>
+      <div id="text" class="content">
+        <div id="text-pending" hidden="[[hidePending_(active, completed)]]">
+          [[i18nDynamic(locale, textKey)]]
         </div>
-        <div id="text" class="content">
-          <div id="text-pending" hidden="[[hidePending(active, completed)]]">
-            [[i18nDynamic(locale, textKey)]]
-          </div>
-          <div id="text-active" hidden="[[!active]]">
-            [[fallbackText(locale, activeKey, textKey)]]
-          </div>
-          <div id="text-completed"
-               hidden="[[hideCompleted(active, completed)]]">
-            [[fallbackText(locale, completedKey, textKey)]]
-          </div>
+        <div id="text-active" hidden="[[!active]]">
+          [[fallbackText(locale, activeKey, textKey)]]
+        </div>
+        <div id="text-completed"
+              hidden="[[hideCompleted_(active, completed)]]">
+          [[fallbackText(locale, completedKey, textKey)]]
         </div>
       </div>
-    </template>
+    </div>
   </template>
 </dom-module>
diff --git a/chrome/browser/resources/chromeos/login/components/progress_list_item.js b/chrome/browser/resources/chromeos/login/components/progress_list_item.js
index 6a78d05f..fe10e29 100644
--- a/chrome/browser/resources/chromeos/login/components/progress_list_item.js
+++ b/chrome/browser/resources/chromeos/login/components/progress_list_item.js
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 Polymer({
-  is: 'progress-list-element',
+  is: 'progress-list-item',
 
   behaviors: [OobeI18nBehavior],
 
diff --git a/chrome/browser/resources/chromeos/login/custom_elements_oobe.html b/chrome/browser/resources/chromeos/login/custom_elements_oobe.html
index df3947d11..bc80f846 100644
--- a/chrome/browser/resources/chromeos/login/custom_elements_oobe.html
+++ b/chrome/browser/resources/chromeos/login/custom_elements_oobe.html
@@ -38,6 +38,7 @@
 <include src="enterprise_enrollment.html">
 <include src="sync_consent.html">
 <include src="fingerprint_setup.html">
+<include src="components/progress_list_item.html">
 <include src="demo_setup.html">
 <include src="demo_preferences.html">
 <include src="recommend_apps.html">
diff --git a/chrome/browser/resources/chromeos/login/custom_elements_oobe.js b/chrome/browser/resources/chromeos/login/custom_elements_oobe.js
index f4ce3cc..81cdbab 100644
--- a/chrome/browser/resources/chromeos/login/custom_elements_oobe.js
+++ b/chrome/browser/resources/chromeos/login/custom_elements_oobe.js
@@ -49,6 +49,7 @@
 // <include src="enterprise_enrollment.js">
 // <include src="sync_consent.js">
 // <include src="fingerprint_setup.js">
+// <include src="components/progress_list_item.js">
 // <include src="demo_setup.js">
 // <include src="demo_preferences.js">
 // <include src="recommend_apps.js">
diff --git a/chrome/browser/resources/chromeos/login/demo_setup.css b/chrome/browser/resources/chromeos/login/demo_setup.css
index 709f672..f22fd0bc 100644
--- a/chrome/browser/resources/chromeos/login/demo_setup.css
+++ b/chrome/browser/resources/chromeos/login/demo_setup.css
@@ -3,7 +3,7 @@
  * found in the LICENSE file. */
 
 /* Spinner shown on progress dialog. */
-paper-spinner-lite {
+paper-spinner-lite#spinner {
   height: 164px;
   width: 164px;
 }
diff --git a/chrome/browser/resources/chromeos/login/demo_setup.html b/chrome/browser/resources/chromeos/login/demo_setup.html
index 5fd028f9..47597a0 100644
--- a/chrome/browser/resources/chromeos/login/demo_setup.html
+++ b/chrome/browser/resources/chromeos/login/demo_setup.html
@@ -9,18 +9,28 @@
       <hd-iron-icon slot="oobe-icon"
           icon1x="oobe-32:computer" icon2x="oobe-64:computer">
       </hd-iron-icon>
-      <div slot="footer" class="flex layout vertical center center-justified">
-        <div hidden$="[[showProgressBarInDemoModeSetup_]]">
-          <paper-spinner-lite id="spinner" dir="ltr"
-              active></paper-spinner-lite>
-        </div>
-        <div hidden$="[[!showProgressBarInDemoModeSetup_]]">
-          <!-- TODO(josephkimsh): Confirm UI input for progress bar and
-            accompanying text. -->
-          <!-- Paper progress is indeterminate when percentage is 0. -->
-          <paper-progress indeterminate="[[!progressPercentage_]]"
-              value="[[progressPercentage_]]"></paper-progress>
-          <span>Starting up Demo Mode...</span>
+      <div slot="footer"
+          class="flex layout vertical center center-justified"
+          hidden$="[[showStepsInDemoModeSetup_]]">
+        <paper-spinner-lite id="spinner" dir="ltr"
+            active></paper-spinner-lite>
+      </div>
+      <div slot="footer" hidden$="[[!showStepsInDemoModeSetup_]]">
+        <div id="demo-setup-steps" role="list">
+          <progress-list-item text-key="demoSetupProgressStepDownload"
+              hidden="[[!shouldShowStep_('downloadResources', setupSteps_)]]"
+              active="[[stepIsActive_(
+                  'downloadResources', setupSteps_, currentStepIndex_)]]"
+              completed="[[stepIsCompleted_(
+                  'downloadResources', setupSteps_, currentStepIndex_)]]">
+          </progress-list-item>
+          <progress-list-item text-key="demoSetupProgressStepEnroll"
+              hidden="[[!shouldShowStep_('enrollment', setupSteps_)]]"
+              active="[[stepIsActive_(
+                  'enrollment', setupSteps_, currentStepIndex_)]]"
+              completed="[[stepIsCompleted_(
+                  'enrollment', setupSteps_, currentStepIndex_)]]">
+          </progress-list-item>
         </div>
       </div>
     </oobe-dialog>
diff --git a/chrome/browser/resources/chromeos/login/demo_setup.js b/chrome/browser/resources/chromeos/login/demo_setup.js
index 40134e2..d755964c 100644
--- a/chrome/browser/resources/chromeos/login/demo_setup.js
+++ b/chrome/browser/resources/chromeos/login/demo_setup.js
@@ -13,10 +13,18 @@
   behaviors: [OobeI18nBehavior, OobeDialogHostBehavior],
 
   properties: {
-    /** Percentage of progress completed in Demo Mode setup. */
-    progressPercentage_: {
+    /** Object mapping step strings to step indices */
+    setupSteps_: {
+      type: Object,
+      value() {
+        return /** @type {!Object} */ (loadTimeData.getValue('demoSetupSteps'));
+      }
+    },
+
+    /** Which step index is currently running in Demo Mode setup. */
+    currentStepIndex_: {
       type: Number,
-      value: 0,
+      value: -1,
     },
 
     /** Error message displayed on demoSetupErrorDialog screen. */
@@ -41,11 +49,11 @@
     },
 
     /** Feature flag to display progress bar instead of spinner during setup. */
-    showProgressBarInDemoModeSetup_: {
+    showStepsInDemoModeSetup_: {
       type: Boolean,
       readonly: true,
       value() {
-        return loadTimeData.getBoolean('showProgressBarInDemoModeSetup');
+        return loadTimeData.getBoolean('showStepsInDemoModeSetup');
       }
     }
   },
@@ -62,17 +70,13 @@
   },
 
   /**
-   * Called when the progress bar needs to be incremented. Every time progress
-   * is incremented, remaining progress is halved.
-   * @param {boolean} complete Set to true if progress is complete
+   * Called at the beginning of a setup step.
+   * @param {string} currentStep The new step name.
    */
-  incrementSetupProgress(complete) {
-    const maxPercentage = 100;
-    if (complete) {
-      this.progressPercentage_ = maxPercentage;
-    } else {
-      const remaining = maxPercentage - this.progressPercentage_;
-      this.progressPercentage_ += remaining / 2;
+  setCurrentSetupStep(currentStep) {
+    // If new step index not specified, remain unchanged.
+    if (this.setupSteps_.hasOwnProperty(currentStep)) {
+      this.currentStepIndex_ = this.setupSteps_[currentStep];
     }
   },
 
@@ -153,4 +157,36 @@
       return;
     chrome.send('login.DemoSetupScreen.userActed', ['close-setup']);
   },
+
+  /**
+   * Whether a given step should be rendered on the UI.
+   * @param {string} stepName The name of the step (from the enum).
+   * @param {!Object} setupSteps
+   * @private
+   */
+  shouldShowStep_(stepName, setupSteps) {
+    return setupSteps.hasOwnProperty(stepName);
+  },
+
+  /**
+   * Whether a given step is active.
+   * @param {string} stepName The name of the step (from the enum).
+   * @param {!Object} setupSteps
+   * @param {number} currentStepIndex
+   * @private
+   */
+  stepIsActive_(stepName, setupSteps, currentStepIndex) {
+    return currentStepIndex == setupSteps[stepName];
+  },
+
+  /**
+   * Whether a given step is completed.
+   * @param {string} stepName The name of the step (from the enum).
+   * @param {!Object} setupSteps
+   * @param {number} currentStepIndex
+   * @private
+   */
+  stepIsCompleted_(stepName, setupSteps, currentStepIndex) {
+    return currentStepIndex > setupSteps[stepName];
+  },
 });
diff --git a/chrome/browser/resources/chromeos/login/oobe_icons.html b/chrome/browser/resources/chromeos/login/oobe_icons.html
index 8bcb30a..e3417915 100644
--- a/chrome/browser/resources/chromeos/login/oobe_icons.html
+++ b/chrome/browser/resources/chromeos/login/oobe_icons.html
@@ -58,6 +58,10 @@
         <path fill="#1A73E8" fill-rule="nonzero" d="M11.467 22.133l-5.6-5.6L4 18.4l7.467 7.467 16-16L25.6 8z"></path>
         <path d="M0 0h32v32H0z"></path>
       </g>
+      <g id="checkcircle" fill="none" fill-rule="evenodd">
+        <path fill="#5BB974" fill-rule="nonzero"  d="M 16,2.6667 C 8.6400184,2.6667 2.6667,8.640018 2.6667,16 2.6667,23.359982 8.6400184,29.3333 16,29.3333 23.359982,29.3333 29.3333,23.359982 29.3333,16 29.3333,8.640018 23.359982,2.6667 16,2.6667 Z m 0,23.99994 C 10.120015,26.66664 5.33336,21.879985 5.33336,16 5.33336,10.120015 10.120015,5.33336 16,5.33336 c 5.879985,0 10.66664,4.786655 10.66664,10.66664 0,5.879985 -4.786655,10.66664 -10.66664,10.66664 z M 13.33334,18.933326 9.866682,15.466668 8.00002,17.33333 13.33334,22.66665 23.99998,12.00001 22.133318,10.133348 Z"></path>
+        <path d="M 0,8 H 24 V 32 H 0 Z"></path>
+      </g>
     </defs>
   </svg>
 </iron-iconset-svg>
@@ -111,10 +115,14 @@
         <path style=" stroke:none;fill-rule:nonzero;fill:rgb(35.294118%,35.294118%,35.294118%);fill-opacity:1;" d="M 53.332031 45.332031 C 56.265625 45.332031 58.640625 42.933594 58.640625 40 L 58.667969 13.332031 C 58.667969 10.398438 56.265625 8 53.332031 8 L 10.667969 8 C 7.734375 8 5.332031 10.398438 5.332031 13.332031 L 5.332031 40 C 5.332031 42.933594 7.734375 45.332031 10.667969 45.332031 Z M 10.667969 13.332031 L 53.332031 13.332031 L 53.332031 40 L 10.667969 40 Z M 12.453125 48 L 2.667969 48 L 2.667969 53.332031 L 61.332031 53.332031 L 61.332031 48 L 51.546875 48 ">
         </path>
       </g>
-      <g  id="checkmark" fill="none" fill-rule="evenodd">
+      <g id="checkmark" fill="none" fill-rule="evenodd">
         <path fill="#1A73E8" fill-rule="nonzero" d="M22.933 44.267l-11.2-11.2L8 36.8l14.933 14.933 32-32L51.2 16z"></path>
         <path d="M0 0h64v64H0z"></path>
       </g>
+      <g id="checkcircle" fill="none" fill-rule="evenodd">
+        <path fill="#5BB974" fill-rule="nonzero" d="M 32,5.3334 C 17.280037,5.3334 5.3334,17.280036 5.3334,32 5.3334,46.719964 17.280037,58.6666 32,58.6666 46.719964,58.6666 58.6666,46.719964 58.6666,32 58.6666,17.280036 46.719964,5.3334 32,5.3334 Z m 0,47.99988 C 20.24003,53.33328 10.66672,43.75997 10.66672,32 10.66672,20.24003 20.24003,10.66672 32,10.66672 43.75997,10.66672 53.33328,20.24003 53.33328,32 53.33328,43.75997 43.75997,53.33328 32,53.33328 Z M 26.66668,37.866652 19.733364,30.933336 16.00004,34.66666 26.66668,45.3333 47.99996,24.00002 44.266636,20.266696 Z"></path>
+        <path d="m -14.66665,30.66665 h 48 v 48 h -48 z"></path>
+      </g>
     </defs>
   </svg>
 </iron-iconset-svg>
diff --git a/chrome/browser/resources/chromeos/login/oobe_screen_demo_setup.js b/chrome/browser/resources/chromeos/login/oobe_screen_demo_setup.js
index 8438640..fecae8e 100644
--- a/chrome/browser/resources/chromeos/login/oobe_screen_demo_setup.js
+++ b/chrome/browser/resources/chromeos/login/oobe_screen_demo_setup.js
@@ -8,8 +8,7 @@
 
 login.createScreen('DemoSetupScreen', 'demo-setup', function() {
   return {
-    EXTERNAL_API:
-        ['incrementSetupProgress', 'onSetupSucceeded', 'onSetupFailed'],
+    EXTERNAL_API: ['setCurrentSetupStep', 'onSetupSucceeded', 'onSetupFailed'],
 
     /**
      * Demo setup module.
@@ -39,12 +38,11 @@
     },
 
     /**
-     * Called when the progress bar needs updating with a new percentage value.
-     * @param {number} percentage Number in range 0-100 denoting progress
-     * percentage.
+     * Called at the beginning of a setup step.
+     * @param {number} currentStepIndex
      */
-    incrementSetupProgress(complete) {
-      this.demoSetupModule_.incrementSetupProgress(complete);
+    setCurrentSetupStep(currentStepIndex) {
+      this.demoSetupModule_.setCurrentSetupStep(currentStepIndex);
     },
 
     /** Called when demo mode setup succeeded. */
diff --git a/chrome/browser/resources/settings/autofill_page/passwords_section.js b/chrome/browser/resources/settings/autofill_page/passwords_section.js
index 2a9d438..642b427 100644
--- a/chrome/browser/resources/settings/autofill_page/passwords_section.js
+++ b/chrome/browser/resources/settings/autofill_page/passwords_section.js
@@ -44,6 +44,11 @@
   ],
 
   properties: {
+    // <if expr="not chromeos">
+    /** @private */
+    storedAccounts_: Array,
+    // </if>
+
     /** Preferences state. */
     prefs: {
       type: Object,
@@ -98,7 +103,7 @@
     signedIn_: {
       type: Boolean,
       value: true,
-      computed: 'computeSignedIn_(syncStatus_.signedIn)',
+      computed: 'computeSignedIn_(syncStatus_, storedAccounts_)',
     },
 
     /** @private */
@@ -299,6 +304,13 @@
     syncBrowserProxy.sendSyncPrefsChanged();
     this.addWebUIListener('sync-prefs-changed', syncPrefsChanged);
 
+    // For non-ChromeOS, also check whether accounts are available.
+    // <if expr="not chromeos">
+    const storedAccountsChanged = accounts => this.storedAccounts_ = accounts;
+    syncBrowserProxy.getStoredAccounts().then(storedAccountsChanged);
+    this.addWebUIListener('stored-accounts-updated', storedAccountsChanged);
+    // </if>
+
     Polymer.RenderStatus.afterNextRender(this, function() {
       Polymer.IronA11yAnnouncer.requestAvailability();
     });
@@ -387,7 +399,9 @@
    * @private
    */
   computeSignedIn_() {
-    return !!this.syncStatus_ && !!this.syncStatus_.signedIn;
+    return !!this.syncStatus_ && !!this.syncStatus_.signedIn ?
+        !this.syncStatus_.hasError :
+        (!!this.storedAccounts_ && this.storedAccounts_.length > 0);
   },
 
   /**
diff --git a/chrome/browser/resources/settings/chromeos/os_people_page/account_manager.html b/chrome/browser/resources/settings/chromeos/os_people_page/account_manager.html
index aed92c9..2c3ef2f2 100644
--- a/chrome/browser/resources/settings/chromeos/os_people_page/account_manager.html
+++ b/chrome/browser/resources/settings/chromeos/os_people_page/account_manager.html
@@ -184,10 +184,10 @@
           <!-- If this is the Device Account, display the management status -->
           <template is="dom-if" if="[[item.isDeviceAccount]]">
             <cr-tooltip-icon id="primaryAccountTooltip" aria-hidden="true"
-                icon-class="cr:info-outline"
+                icon-class="[[getPrimaryAccountTooltipIcon_(isChildUser_)]]"
                 class="tooltip-primary-account"
-                tooltip-text="$i18n{accountManagerPrimaryAccountTooltip}"
-                icon-aria-label="$i18n{accountManagerPrimaryAccountTooltip}">
+                tooltip-text="[[getPrimaryAccountTooltip_(isChildUser_)]]"
+                icon-aria-label="[[getPrimaryAccountTooltip_(isChildUser_)]]">
             </cr-tooltip-icon>
             <span class="management-status"
                 aria-labelledby$="fullName-[[index]] email-[[index]]"
diff --git a/chrome/browser/resources/settings/chromeos/os_people_page/account_manager.js b/chrome/browser/resources/settings/chromeos/os_people_page/account_manager.js
index d011ced..e56e694 100644
--- a/chrome/browser/resources/settings/chromeos/os_people_page/account_manager.js
+++ b/chrome/browser/resources/settings/chromeos/os_people_page/account_manager.js
@@ -116,6 +116,24 @@
   },
 
   /**
+   * @return {string} cr icon name.
+   * @private
+   */
+  getPrimaryAccountTooltipIcon_() {
+    return this.isChildUser_ ? 'cr20:kite' : 'cr:info-outline';
+  },
+
+  /**
+   * @return {string} tooltip text
+   * @private
+   */
+  getPrimaryAccountTooltip_() {
+    return this.isChildUser_ ?
+        this.i18n('accountManagerPrimaryAccountChildManagedTooltip') :
+        this.i18n('accountManagerPrimaryAccountTooltip');
+  },
+
+  /**
    * @param {string} iconUrl
    * @return {string} A CSS image-set for multiple scale factors.
    * @private
diff --git a/chrome/browser/resources/settings/chromeos/os_route.js b/chrome/browser/resources/settings/chromeos/os_route.js
index 3be79980..2727191 100644
--- a/chrome/browser/resources/settings/chromeos/os_route.js
+++ b/chrome/browser/resources/settings/chromeos/os_route.js
@@ -105,7 +105,7 @@
     r.OS_LANGUAGES_INPUT_METHODs =
         r.OS_LANGUAGES.createChild('/osLanguages/inputMethods');
 
-    r.PRINTING = r.ADVANCED.createSection('/printing', 'printing');
+    r.OS_PRINTING = r.ADVANCED.createSection('/osPrinting', 'osPrinting');
 
     r.OS_ACCESSIBILITY = r.ADVANCED.createSection('/osAccessibility', 'a11y');
 
@@ -150,7 +150,7 @@
     r.DATETIME = r.ADVANCED.createSection('/dateTime', 'dateTime');
     r.DATETIME_TIMEZONE_SUBPAGE = r.DATETIME.createChild('/dateTime/timeZone');
 
-    r.CUPS_PRINTERS = r.PRINTING.createChild('/cupsPrinters');
+    r.CUPS_PRINTERS = r.OS_PRINTING.createChild('/cupsPrinters');
 
     r.MANAGE_ACCESSIBILITY =
         r.OS_ACCESSIBILITY.createChild('/manageAccessibility');
diff --git a/chrome/browser/resources/settings/chromeos/os_settings_menu/os_settings_menu.html b/chrome/browser/resources/settings/chromeos/os_settings_menu/os_settings_menu.html
index a322015d..0553a329 100644
--- a/chrome/browser/resources/settings/chromeos/os_settings_menu/os_settings_menu.html
+++ b/chrome/browser/resources/settings/chromeos/os_settings_menu/os_settings_menu.html
@@ -256,7 +256,7 @@
               $i18n{filesPageTitle}
             </div>
           </a>
-          <a href="/printing">
+          <a href="/osPrinting">
             <div class="item">
               <iron-icon icon="cr:print"></iron-icon>
               $i18n{printingPageTitle}
diff --git a/chrome/browser/resources/settings/chromeos/os_settings_page/os_settings_page.html b/chrome/browser/resources/settings/chromeos/os_settings_page/os_settings_page.html
index 025514e..ab29cef8 100644
--- a/chrome/browser/resources/settings/chromeos/os_settings_page/os_settings_page.html
+++ b/chrome/browser/resources/settings/chromeos/os_settings_page/os_settings_page.html
@@ -239,7 +239,7 @@
             </settings-section>
           </template>
           <settings-section page-title="$i18n{printingPageTitle}"
-              section="printing">
+              section="osPrinting">
             <os-settings-printing-page prefs="{{prefs}}">
             </os-settings-printing-page>
           </settings-section>
diff --git a/chrome/browser/resources/settings/chromeos/os_settings_routes.js b/chrome/browser/resources/settings/chromeos/os_settings_routes.js
index c9f88ce..dd9fdd4 100644
--- a/chrome/browser/resources/settings/chromeos/os_settings_routes.js
+++ b/chrome/browser/resources/settings/chromeos/os_settings_routes.js
@@ -53,6 +53,7 @@
  *   OS_LANGUAGES: !settings.Route,
  *   OS_LANGUAGES_DETAILS: !settings.Route,
  *   OS_LANGUAGES_INPUT_METHODS: !settings.Route,
+ *   OS_PRINTING: !settings.Route,
  *   OS_RESET: !settings.Route,
  *   OS_SEARCH: !settings.Route,
  *   OS_SYNC: !settings.Route,
@@ -63,7 +64,6 @@
  *   PLUGIN_VM_SHARED_PATHS: !settings.Route,
  *   POINTERS: !settings.Route,
  *   POWER: !settings.Route,
- *   PRINTING: !settings.Route,
  *   PRIVACY: !settings.Route,
  *   SEARCH: !settings.Route,
  *   RESET: !settings.Route,
diff --git a/chrome/browser/resources/settings/chromeos/plugin_vm_page/plugin_vm_subpage.html b/chrome/browser/resources/settings/chromeos/plugin_vm_page/plugin_vm_subpage.html
index bc8700f..4aab97ef 100644
--- a/chrome/browser/resources/settings/chromeos/plugin_vm_page/plugin_vm_subpage.html
+++ b/chrome/browser/resources/settings/chromeos/plugin_vm_page/plugin_vm_subpage.html
@@ -23,6 +23,11 @@
          label="$i18n{pluginVmPrinterAccess}"
          pref="{{prefs.plugin_vm.printers_allowed}}">
     </settings-toggle-button>
+    <template is="dom-if" if="[[showPluginVmCamera_]]">
+      <settings-toggle-button label="$i18n{pluginVmCameraAccessTitle}"
+          pref="{{prefs.plugin_vm.camera_sharing}}">
+      </settings-toggle-button>
+    </template>
     <div id="plugin-vm-remove" class="settings-box">
       <div id="pluginVmRemoveLabel" class="start">$i18n{pluginVmRemove}</div>
       <cr-button on-click="onRemoveClick_" id="pluginVmRemoveButton"
diff --git a/chrome/browser/resources/settings/chromeos/plugin_vm_page/plugin_vm_subpage.js b/chrome/browser/resources/settings/chromeos/plugin_vm_page/plugin_vm_subpage.js
index a2c70e8..611266c 100644
--- a/chrome/browser/resources/settings/chromeos/plugin_vm_page/plugin_vm_subpage.js
+++ b/chrome/browser/resources/settings/chromeos/plugin_vm_page/plugin_vm_subpage.js
@@ -24,6 +24,17 @@
       type: Boolean,
       value: false,
     },
+
+    /**
+     * Whether the toggle to share the camera with PluginVm should be shown.
+     * @private {boolean}
+     */
+    showPluginVmCamera_: {
+      type: Boolean,
+      value() {
+        return loadTimeData.getBoolean('showPluginVmCamera');
+      },
+    },
   },
 
   observers: [
diff --git a/chrome/browser/resources/settings/lazy_load.js b/chrome/browser/resources/settings/lazy_load.js
index befa7b77..752aeb1 100644
--- a/chrome/browser/resources/settings/lazy_load.js
+++ b/chrome/browser/resources/settings/lazy_load.js
@@ -30,7 +30,6 @@
 import './people_page/manage_profile.m.js';
 // </if>
 import './people_page/signout_dialog.m.js';
-import './people_page/sync_account_control.m.js';
 import './people_page/sync_controls.m.js';
 import './people_page/sync_page.m.js';
 
@@ -99,6 +98,7 @@
 export {CredentialManagementDialogPage} from './privacy_page/security_keys_credential_management_dialog.m.js';
 export {BioEnrollDialogPage} from './privacy_page/security_keys_bio_enroll_dialog.m.js';
 export {WebsiteUsageBrowserProxyImpl} from './site_settings/website_usage_browser_proxy.m.js';
+export {MAX_SIGNIN_PROMO_IMPRESSION} from './people_page/sync_account_control.m.js';
 
 // <if expr="not chromeos">
 export {SystemPageBrowserProxyImpl} from './system_page/system_page_browser_proxy.m.js';
diff --git a/chrome/browser/resources/settings/page_visibility.js b/chrome/browser/resources/settings/page_visibility.js
index 49574857..4ddb4ff 100644
--- a/chrome/browser/resources/settings/page_visibility.js
+++ b/chrome/browser/resources/settings/page_visibility.js
@@ -119,6 +119,13 @@
     // </if>
   }
 
+  /* #export */ function setPageVisibilityForTesting(testVisibility) {
+    settings.pageVisibility = testVisibility;
+  }
+
   // #cr_define_end
-  return {pageVisibility: pageVisibility};
+  return {
+    pageVisibility: pageVisibility,
+    setPageVisibilityForTesting: setPageVisibilityForTesting,
+  };
 });
diff --git a/chrome/browser/resources/settings/people_page/sync_account_control.js b/chrome/browser/resources/settings/people_page/sync_account_control.js
index a4885f2..84faffd 100644
--- a/chrome/browser/resources/settings/people_page/sync_account_control.js
+++ b/chrome/browser/resources/settings/people_page/sync_account_control.js
@@ -8,7 +8,7 @@
  */
 cr.define('settings', function() {
   /** @const {number} */
-  const MAX_SIGNIN_PROMO_IMPRESSION = 10;
+  /* #export */ const MAX_SIGNIN_PROMO_IMPRESSION = 10;
 
   Polymer({
     is: 'settings-sync-account-control',
diff --git a/chrome/browser/resources/settings/route.js b/chrome/browser/resources/settings/route.js
index 6c10820..8a14db1 100644
--- a/chrome/browser/resources/settings/route.js
+++ b/chrome/browser/resources/settings/route.js
@@ -228,7 +228,7 @@
   /**
    * @return {!settings.Router} A router with the browser settings routes.
    */
-  function buildRouter() {
+  /* #export */ function buildRouter() {
     return new settings.Router(createBrowserSettingsRoutes());
   }
 
diff --git a/chrome/browser/resources/settings/settings.js b/chrome/browser/resources/settings/settings.js
index 93dd7a9..ae17b8c 100644
--- a/chrome/browser/resources/settings/settings.js
+++ b/chrome/browser/resources/settings/settings.js
@@ -52,12 +52,12 @@
 export {OpenWindowProxyImpl} from './open_window_proxy.m.js';
 export {PageStatus, StatusAction, SyncBrowserProxyImpl} from './people_page/sync_browser_proxy.m.js';
 export {ProfileInfoBrowserProxyImpl} from './people_page/profile_info_browser_proxy.m.js';
-export {pageVisibility} from './page_visibility.m.js';
+export {pageVisibility, setPageVisibilityForTesting} from './page_visibility.m.js';
 export {PasswordManagerProxy, PasswordManagerImpl} from './autofill_page/password_manager_proxy.m.js';
 export {PluralStringProxyImpl} from './plural_string_proxy.m.js';
 export {prefToString, stringToPrefValue} from './prefs/pref_util.m.js';
 export {ResetBrowserProxyImpl} from './reset_page/reset_browser_proxy.m.js';
-export {routes} from './route.m.js';
+export {buildRouter, routes} from './route.m.js';
 export {Route, Router} from './router.m.js';
 export {SearchEnginesBrowserProxyImpl} from './search_engines_page/search_engines_browser_proxy.m.js';
 export {PrivacyPageBrowserProxyImpl, SecureDnsMode, SecureDnsUiManagementMode} from './privacy_page/privacy_page_browser_proxy.m.js';
diff --git a/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_utils.cc b/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_utils.cc
index 76925c17..981d34e 100644
--- a/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_utils.cc
+++ b/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_utils.cc
@@ -173,11 +173,22 @@
                                  ? response.dlp_scan_verdict().status() ==
                                        DlpDeepScanningVerdict::SUCCESS
                                  : true;
-  bool malware_verdict_success =
-      response.has_malware_scan_verdict()
-          ? response.malware_scan_verdict().verdict() !=
-                MalwareDeepScanningVerdict::VERDICT_UNSPECIFIED
-          : true;
+
+  bool malware_verdict_success = true;
+  if (response.has_malware_scan_verdict()) {
+    switch (response.malware_scan_verdict().verdict()) {
+      case MalwareDeepScanningVerdict::VERDICT_UNSPECIFIED:
+      case MalwareDeepScanningVerdict::SCAN_FAILURE:
+        malware_verdict_success = false;
+        break;
+      case MalwareDeepScanningVerdict::MALWARE:
+      case MalwareDeepScanningVerdict::UWS:
+      case MalwareDeepScanningVerdict::CLEAN:
+        malware_verdict_success = true;
+        break;
+    }
+  }
+
   bool success = dlp_verdict_success && malware_verdict_success;
   std::string result_value = BinaryUploadServiceResultToString(result, success);
 
diff --git a/chrome/browser/sharing/sharing_metrics.cc b/chrome/browser/sharing/sharing_metrics.cc
index 6e5f964..7240686 100644
--- a/chrome/browser/sharing/sharing_metrics.cc
+++ b/chrome/browser/sharing/sharing_metrics.cc
@@ -399,6 +399,11 @@
         base::StrCat(
             {metric_prefix, ".", PulseIntervalToString(pulse_interval)}),
         result);
+    base::UmaHistogramEnumeration(
+        base::StrCat({metric_prefix, ".",
+                      DevicePlatformToString(receiving_device_platform), ".",
+                      PulseIntervalToString(pulse_interval)}),
+        result);
   }
 }
 
diff --git a/chrome/browser/supervised_user/supervised_user_constants.cc b/chrome/browser/supervised_user/supervised_user_constants.cc
index cd21497..dc6e883 100644
--- a/chrome/browser/supervised_user/supervised_user_constants.cc
+++ b/chrome/browser/supervised_user/supervised_user_constants.cc
@@ -10,9 +10,6 @@
 const char kAccountConsistencyMirrorRequired[] =
     "AccountConsistencyMirrorRequired";
 #endif
-#if BUILDFLAG(ENABLE_EXTENSIONS)
-const char kApprovedExtensions[] = "ApprovedExtensions";
-#endif
 const char kAuthorizationHeaderFormat[] = "Bearer %s";
 const char kCameraMicDisabled[] = "CameraMicDisabled";
 const char kContentPackDefaultFilteringBehavior[] =
diff --git a/chrome/browser/supervised_user/supervised_user_constants.h b/chrome/browser/supervised_user/supervised_user_constants.h
index d7cbb19..26c8ea5 100644
--- a/chrome/browser/supervised_user/supervised_user_constants.h
+++ b/chrome/browser/supervised_user/supervised_user_constants.h
@@ -5,8 +5,6 @@
 #ifndef CHROME_BROWSER_SUPERVISED_USER_SUPERVISED_USER_CONSTANTS_H_
 #define CHROME_BROWSER_SUPERVISED_USER_SUPERVISED_USER_CONSTANTS_H_
 
-#include "extensions/buildflags/buildflags.h"
-
 namespace supervised_users {
 
 // Keys for supervised user settings. These are configured remotely and mapped
@@ -14,9 +12,6 @@
 #if defined(OS_CHROMEOS)
 extern const char kAccountConsistencyMirrorRequired[];
 #endif
-#if BUILDFLAG(ENABLE_EXTENSIONS)
-extern const char kApprovedExtensions[];
-#endif
 extern const char kAuthorizationHeaderFormat[];
 extern const char kCameraMicDisabled[];
 extern const char kContentPackDefaultFilteringBehavior[];
diff --git a/chrome/browser/supervised_user/supervised_user_extensions_metrics_recorder.cc b/chrome/browser/supervised_user/supervised_user_extensions_metrics_recorder.cc
index 409e807..d247b34dc 100644
--- a/chrome/browser/supervised_user/supervised_user_extensions_metrics_recorder.cc
+++ b/chrome/browser/supervised_user/supervised_user_extensions_metrics_recorder.cc
@@ -19,16 +19,16 @@
 
 // static
 void SupervisedUserExtensionsMetricsRecorder::RecordExtensionsUmaMetrics(
-    syncer::SyncChange::SyncChangeType type) {
+    SupervisedUserService::ApprovedExtensionChange type) {
   switch (type) {
-    case syncer::SyncChange::ACTION_ADD:
+    case SupervisedUserService::ApprovedExtensionChange::kNew:
       // Record UMA metrics for custodian approval for a new extension.
       base::RecordAction(
           base::UserMetricsAction(kNewExtensionApprovalGrantedActionName));
       base::UmaHistogramEnumeration(
           kHistogramName, UmaExtensionState::kNewExtensionApprovalGranted);
       break;
-    case syncer::SyncChange::ACTION_UPDATE:
+    case SupervisedUserService::ApprovedExtensionChange::kUpdate:
       // Record UMA metrics for child approval for a newer version of an
       // existing extension.
       base::RecordAction(
@@ -36,14 +36,11 @@
       base::UmaHistogramEnumeration(
           kHistogramName, UmaExtensionState::kNewVersionApprovalGranted);
       break;
-    case syncer::SyncChange::ACTION_DELETE:
+    case SupervisedUserService::ApprovedExtensionChange::kRemove:
       // Record UMA metrics for removing an extension.
       base::RecordAction(base::UserMetricsAction(kRemovedActionName));
       base::UmaHistogramEnumeration(kHistogramName,
                                     UmaExtensionState::kRemoved);
       break;
-    case syncer::SyncChange::ACTION_INVALID:
-      NOTREACHED();
-      break;
   }
 }
diff --git a/chrome/browser/supervised_user/supervised_user_extensions_metrics_recorder.h b/chrome/browser/supervised_user/supervised_user_extensions_metrics_recorder.h
index 2c09e93..734c1d8a 100644
--- a/chrome/browser/supervised_user/supervised_user_extensions_metrics_recorder.h
+++ b/chrome/browser/supervised_user/supervised_user_extensions_metrics_recorder.h
@@ -5,7 +5,7 @@
 #ifndef CHROME_BROWSER_SUPERVISED_USER_SUPERVISED_USER_EXTENSIONS_METRICS_RECORDER_H_
 #define CHROME_BROWSER_SUPERVISED_USER_SUPERVISED_USER_EXTENSIONS_METRICS_RECORDER_H_
 
-#include "components/sync/model/sync_change.h"
+#include "chrome/browser/supervised_user/supervised_user_service.h"
 
 // Records UMA metrics for child users using extensions.
 // TODO(tobyhuang): Reevaluate if this class should be converted to a namespace
@@ -38,7 +38,7 @@
       const SupervisedUserExtensionsMetricsRecorder&) = delete;
 
   static void RecordExtensionsUmaMetrics(
-      syncer::SyncChange::SyncChangeType type);
+      SupervisedUserService::ApprovedExtensionChange type);
 };
 
 #endif  // CHROME_BROWSER_SUPERVISED_USER_SUPERVISED_USER_EXTENSIONS_METRICS_RECORDER_H_
diff --git a/chrome/browser/supervised_user/supervised_user_pref_store.cc b/chrome/browser/supervised_user/supervised_user_pref_store.cc
index 966cdd7..462bfa9 100644
--- a/chrome/browser/supervised_user/supervised_user_pref_store.cc
+++ b/chrome/browser/supervised_user/supervised_user_pref_store.cc
@@ -52,16 +52,20 @@
         prefs::kSupervisedUserManualURLs,
     },
     {
-        supervised_users::kForceSafeSearch, prefs::kForceGoogleSafeSearch,
+        supervised_users::kForceSafeSearch,
+        prefs::kForceGoogleSafeSearch,
     },
     {
-        supervised_users::kSafeSitesEnabled, prefs::kSupervisedUserSafeSites,
+        supervised_users::kSafeSitesEnabled,
+        prefs::kSupervisedUserSafeSites,
     },
     {
-        supervised_users::kSigninAllowed, prefs::kSigninAllowed,
+        supervised_users::kSigninAllowed,
+        prefs::kSigninAllowed,
     },
     {
-        supervised_users::kUserName, prefs::kProfileName,
+        supervised_users::kUserName,
+        prefs::kProfileName,
     },
 };
 
diff --git a/chrome/browser/supervised_user/supervised_user_service.cc b/chrome/browser/supervised_user/supervised_user_service.cc
index ae5d2d8..d5c4e9574 100644
--- a/chrome/browser/supervised_user/supervised_user_service.cc
+++ b/chrome/browser/supervised_user/supervised_user_service.cc
@@ -19,6 +19,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/task/post_task.h"
 #include "base/task/thread_pool.h"
+#include "base/values.h"
 #include "base/version.h"
 #include "build/build_config.h"
 #include "chrome/browser/browser_process.h"
@@ -27,7 +28,6 @@
 #include "chrome/browser/profiles/profile_key.h"
 #include "chrome/browser/supervised_user/permission_request_creator.h"
 #include "chrome/browser/supervised_user/supervised_user_constants.h"
-#include "chrome/browser/supervised_user/supervised_user_extensions_metrics_recorder.h"
 #include "chrome/browser/supervised_user/supervised_user_features.h"
 #include "chrome/browser/supervised_user/supervised_user_filtering_switches.h"
 #include "chrome/browser/supervised_user/supervised_user_service_factory.h"
@@ -67,6 +67,7 @@
 #include "chrome/browser/extensions/extension_management.h"
 #include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/extensions/extension_util.h"
+#include "chrome/browser/supervised_user/supervised_user_extensions_metrics_recorder.h"
 #include "extensions/browser/extension_prefs.h"
 #include "extensions/browser/extension_system.h"
 #endif
@@ -126,6 +127,9 @@
 #if BUILDFLAG(ENABLE_EXTENSIONS)
   registry->RegisterBooleanPref(
       prefs::kSupervisedUserExtensionsMayRequestPermissions, false);
+  registry->RegisterDictionaryPref(
+      prefs::kSupervisedUserApprovedExtensions,
+      user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF);
 #endif
   registry->RegisterDictionaryPref(prefs::kSupervisedUserManualHosts);
   registry->RegisterDictionaryPref(prefs::kSupervisedUserManualURLs);
@@ -326,60 +330,27 @@
 }
 
 #if BUILDFLAG(ENABLE_EXTENSIONS)
-void SupervisedUserService::AddExtensionApproval(
+void SupervisedUserService::AddOrUpdateExtensionApproval(
     const extensions::Extension& extension) {
-  UpdateApprovedExtensions(extension.id(), extension.VersionString(),
-                           syncer::SyncChange::SyncChangeType::ACTION_ADD);
+  bool has_key = base::Contains(approved_extensions_map_, extension.id());
+  ApprovedExtensionChange update_type = has_key
+                                            ? ApprovedExtensionChange::kUpdate
+                                            : ApprovedExtensionChange::kNew;
+  UpdateApprovedExtension(extension.id(), extension.VersionString(),
+                          update_type);
 }
 
 void SupervisedUserService::RemoveExtensionApproval(
     const extensions::Extension& extension) {
-  UpdateApprovedExtensions(extension.id(), extension.VersionString(),
-                           syncer::SyncChange::SyncChangeType::ACTION_DELETE);
+  UpdateApprovedExtension(extension.id(), extension.VersionString(),
+                          ApprovedExtensionChange::kRemove);
 }
 
-void SupervisedUserService::UpdateApprovedExtensions(
+void SupervisedUserService::UpdateApprovedExtensionForTesting(
     const std::string& extension_id,
     const std::string& version,
-    syncer::SyncChange::SyncChangeType type) {
-  std::string key = SupervisedUserSettingsService::MakeSplitSettingKey(
-      supervised_users::kApprovedExtensions, extension_id);
-  syncer::SyncData sync_data =
-      SupervisedUserSettingsService::CreateSyncDataForSetting(
-          key, base::Value(version));
-
-  syncer::SyncChangeList list(1,
-                              syncer::SyncChange(FROM_HERE, type, sync_data));
-  GetSettingsService()->ProcessSyncChanges(FROM_HERE, list);
-
-  // Keep track of currently approved extensions. We may need to disable them if
-  // they are not in the approved map anymore.
-  std::set<std::string> extensions_to_be_checked;
-  for (const auto& extension : approved_extensions_map_)
-    extensions_to_be_checked.insert(extension.first);
-
-  approved_extensions_map_.clear();
-
-  const base::DictionaryValue* dict =
-      GetSettingsService()->GetDictionaryAndSplitKey(&key);
-  for (base::DictionaryValue::Iterator it(*dict); !it.IsAtEnd(); it.Advance()) {
-    std::string version_str;
-    bool result = it.value().GetAsString(&version_str);
-    DCHECK(result);
-    base::Version version(version_str);
-    if (version.IsValid()) {
-      approved_extensions_map_[it.key()] = version;
-      extensions_to_be_checked.insert(it.key());
-    } else {
-      LOG(WARNING) << "Invalid version number " << version_str;
-    }
-  }
-
-  for (const auto& extension_id : extensions_to_be_checked) {
-    ChangeExtensionStateIfNecessary(extension_id);
-  }
-
-  SupervisedUserExtensionsMetricsRecorder::RecordExtensionsUmaMetrics(type);
+    ApprovedExtensionChange type) {
+  UpdateApprovedExtension(extension_id, version, type);
 }
 
 bool SupervisedUserService::
@@ -446,6 +417,12 @@
         base::BindRepeating(
             &SupervisedUserService::OnDefaultFilteringBehaviorChanged,
             base::Unretained(this)));
+#if BUILDFLAG(ENABLE_EXTENSIONS)
+    pref_change_registrar_.Add(
+        prefs::kSupervisedUserApprovedExtensions,
+        base::BindRepeating(&SupervisedUserService::UpdateApprovedExtensions,
+                            base::Unretained(this)));
+#endif  // BUILDFLAG(ENABLE_EXTENSIONS)
     pref_change_registrar_.Add(
         prefs::kSupervisedUserSafeSites,
         base::BindRepeating(&SupervisedUserService::OnSafeSitesSettingChanged,
@@ -472,6 +449,10 @@
     UpdateManualHosts();
     UpdateManualURLs();
 
+#if BUILDFLAG(ENABLE_EXTENSIONS)
+    UpdateApprovedExtensions();
+#endif  // BUILDFLAG(ENABLE_EXTENSIONS)
+
 #if !defined(OS_ANDROID)
     // TODO(bauerb): Get rid of the platform-specific #ifdef here.
     // http://crbug.com/313377
@@ -482,6 +463,9 @@
 
     pref_change_registrar_.Remove(
         prefs::kDefaultSupervisedUserFilteringBehavior);
+#if BUILDFLAG(ENABLE_EXTENSIONS)
+    pref_change_registrar_.Remove(prefs::kSupervisedUserApprovedExtensions);
+#endif  // BUILDFLAG(ENABLE_EXTENSIONS)
     pref_change_registrar_.Remove(prefs::kSupervisedUserManualHosts);
     pref_change_registrar_.Remove(prefs::kSupervisedUserManualURLs);
     for (const char* pref : kCustodianInfoPrefs) {
@@ -510,6 +494,12 @@
       profile_->GetProfileKey());
 }
 
+PrefService* SupervisedUserService::GetPrefService() {
+  PrefService* pref_service = profile_->GetPrefs();
+  DCHECK(pref_service) << "PrefService not found";
+  return pref_service;
+}
+
 size_t SupervisedUserService::FindEnabledPermissionRequestCreator(
     size_t start) {
   for (size_t i = start; i < permissions_creators_.size(); ++i) {
@@ -702,11 +692,11 @@
   const base::DictionaryValue* dict =
       profile_->GetPrefs()->GetDictionary(prefs::kSupervisedUserManualHosts);
   std::map<std::string, bool> host_map;
-  for (base::DictionaryValue::Iterator it(*dict); !it.IsAtEnd(); it.Advance()) {
+  for (auto it : dict->DictItems()) {
     bool allow = false;
-    bool result = it.value().GetAsBoolean(&allow);
+    bool result = it.second.GetAsBoolean(&allow);
     DCHECK(result);
-    host_map[it.key()] = allow;
+    host_map[it.first] = allow;
   }
   url_filter_.SetManualHosts(std::move(host_map));
 
@@ -718,11 +708,11 @@
   const base::DictionaryValue* dict =
       profile_->GetPrefs()->GetDictionary(prefs::kSupervisedUserManualURLs);
   std::map<GURL, bool> url_map;
-  for (base::DictionaryValue::Iterator it(*dict); !it.IsAtEnd(); it.Advance()) {
+  for (auto it : dict->DictItems()) {
     bool allow = false;
-    bool result = it.value().GetAsBoolean(&allow);
+    bool result = it.second.GetAsBoolean(&allow);
     DCHECK(result);
-    url_map[GURL(it.key())] = allow;
+    url_map[GURL(it.first)] = allow;
   }
   url_filter_.SetManualURLs(std::move(url_map));
 
@@ -890,10 +880,8 @@
           id, extensions::disable_reason::DISABLE_PERMISSIONS_INCREASE) &&
       approved_extensions_map_.count(id) > 0 &&
       approved_extensions_map_[id] < version) {
-    approved_extensions_map_[id] = version;
-
-    UpdateApprovedExtensions(id, version.GetString(),
-                             syncer::SyncChange::ACTION_ADD);
+    UpdateApprovedExtension(id, version.GetString(),
+                            ApprovedExtensionChange::kUpdate);
   } else {
     // Upon extension update, the approved version may (or may not) match the
     // installed one. Therefore, a change in extension state might be required.
@@ -955,6 +943,60 @@
   }
 }
 
+void SupervisedUserService::UpdateApprovedExtension(
+    const std::string& extension_id,
+    const std::string& version,
+    ApprovedExtensionChange type) {
+  PrefService* pref_service = GetPrefService();
+  DictionaryPrefUpdate update(pref_service,
+                              prefs::kSupervisedUserApprovedExtensions);
+  base::DictionaryValue* approved_extensions = update.Get();
+  DCHECK(approved_extensions)
+      << "kSupervisedUserApprovedExtensions pref not found";
+  switch (type) {
+    case ApprovedExtensionChange::kNew:
+    case ApprovedExtensionChange::kUpdate:
+      approved_extensions->SetStringKey(extension_id, std::move(version));
+      break;
+    case ApprovedExtensionChange::kRemove:
+      approved_extensions->RemoveKey(extension_id);
+      break;
+  }
+
+  SupervisedUserExtensionsMetricsRecorder::RecordExtensionsUmaMetrics(type);
+}
+
+void SupervisedUserService::UpdateApprovedExtensions() {
+  // Keep track of currently approved extensions. We need to disable them if
+  // they are not in the approved map anymore.
+  std::set<std::string> extensions_to_be_checked;
+  for (const auto& extension : approved_extensions_map_)
+    extensions_to_be_checked.insert(extension.first);
+
+  // The purpose here is to re-populate the approved_extensions_map_, which is
+  // used in GetExtensionState() to keep track of approved extensions.
+  approved_extensions_map_.clear();
+
+  const base::DictionaryValue* dict = profile_->GetPrefs()->GetDictionary(
+      prefs::kSupervisedUserApprovedExtensions);
+  for (auto it : dict->DictItems()) {
+    std::string version_str;
+    bool result = it.second.GetAsString(&version_str);
+    DCHECK(result);
+    base::Version version(version_str);
+    if (version.IsValid()) {
+      approved_extensions_map_[it.first] = version;
+      extensions_to_be_checked.insert(it.first);
+    } else {
+      LOG(WARNING) << "Invalid version number " << version_str;
+    }
+  }
+
+  for (const auto& extension_id : extensions_to_be_checked) {
+    ChangeExtensionStateIfNecessary(extension_id);
+  }
+}
+
 void SupervisedUserService::SetExtensionsActive() {
   extensions::ExtensionSystem* extension_system =
       extensions::ExtensionSystem::Get(profile_);
diff --git a/chrome/browser/supervised_user/supervised_user_service.h b/chrome/browser/supervised_user/supervised_user_service.h
index 4df2171c..e33462e 100644
--- a/chrome/browser/supervised_user/supervised_user_service.h
+++ b/chrome/browser/supervised_user/supervised_user_service.h
@@ -32,7 +32,6 @@
 #if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "base/strings/string16.h"
 #include "chrome/browser/ui/supervised_user/parent_permission_dialog.h"
-#include "components/sync/model/sync_change.h"
 #include "extensions/browser/extension_registry.h"
 #include "extensions/browser/extension_registry_observer.h"
 #include "extensions/browser/management_policy.h"
@@ -40,6 +39,7 @@
 
 class Browser;
 class PermissionRequestCreator;
+class PrefService;
 class Profile;
 class SupervisedUserServiceObserver;
 class SupervisedUserSettingsService;
@@ -86,6 +86,20 @@
     virtual bool SetActive(bool active) = 0;
   };
 
+#if BUILDFLAG(ENABLE_EXTENSIONS)
+  // These enum values represent operations to manage the
+  // kSupervisedUserApprovedExtensions user pref, which maps extension ids to
+  // approved versions.
+  enum class ApprovedExtensionChange {
+    // Adds a new approved extension to the pref.
+    kNew,
+    // Updates the version of an already-approved extension.
+    kUpdate,
+    // Removes extension approval.
+    kRemove
+  };
+#endif  // BUILDFLAG(ENABLE_EXTENSIONS)
+
   ~SupervisedUserService() override;
 
   static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry);
@@ -191,20 +205,19 @@
 
 #if BUILDFLAG(ENABLE_EXTENSIONS)
   // Updates the map of approved extensions to add approval for |extension|.
-  void AddExtensionApproval(const extensions::Extension& extension);
+  void AddOrUpdateExtensionApproval(const extensions::Extension& extension);
 
   // Updates the map of approved extensions to remove approval for |extension|.
   void RemoveExtensionApproval(const extensions::Extension& extension);
 
-  // Updates the map of approved extensions.
-  // If possible, use the simpler methods declared above.
-  // If |type| is SyncChangeType::ADD, then add custodian approval for enabling
-  // the extension by adding the approved version to the map of approved
-  // extensions. If |type| is SyncChangeType::DELETE, then remove the extension
-  // from the map of approved extensions.
-  void UpdateApprovedExtensions(const std::string& extension_id,
-                                const std::string& version,
-                                syncer::SyncChange::SyncChangeType type);
+  // Simulates a custodian or child approval for enabling the extension coming
+  // in through Sync by adding the approved version to the map of approved
+  // extensions. Removes approval by passing in
+  // ApprovedExtensionChange::kRemove. It doesn't simulate a change in the
+  // disable reasons.
+  void UpdateApprovedExtensionForTesting(const std::string& extension_id,
+                                         const std::string& version,
+                                         ApprovedExtensionChange type);
 
   bool GetSupervisedUserExtensionsMayRequestPermissionsPref() const;
 
@@ -276,16 +289,37 @@
   // "Permissions for sites, apps and extensions" toggle.
   bool ShouldBlockExtension(const std::string& extension_id) const;
 
-  // Extensions helper to SetActive().
-  void SetExtensionsActive();
-
   // Enables/Disables extensions upon change in approved version of the
   // extension_id. This function is idempotent.
   void ChangeExtensionStateIfNecessary(const std::string& extension_id);
+
+  // Updates the map of approved extensions.
+  // Use AddOrUpdateExtensionApproval() or RemoveExtensionApproval() for public
+  // access.
+  // If |type| is kNew, then adds custodian approval for enabling the extension
+  // by adding the approved version to the map of approved extensions.
+  // If |type| is kUpdate, then updates the approved version for the extension
+  // in the map.
+  // If |type| is kRemove, then removes the extension from the map of approved
+  // extensions.
+  void UpdateApprovedExtension(const std::string& extension_id,
+                               const std::string& version,
+                               ApprovedExtensionChange type);
+
+  // Updates the map of approved extensions when the corresponding preference is
+  // changed.
+  void UpdateApprovedExtensions();
+
+  // Extensions helper to SetActive().
+  void SetExtensionsActive();
 #endif  // BUILDFLAG(ENABLE_EXTENSIONS)
 
+  // Returns the SupervisedUserSettingsService associated with |profile_|.
   SupervisedUserSettingsService* GetSettingsService();
 
+  // Returns the PrefService associated with |profile_|.
+  PrefService* GetPrefService();
+
   size_t FindEnabledPermissionRequestCreator(size_t start);
   void AddPermissionRequestInternal(
       const CreatePermissionRequestCallback& create_request,
diff --git a/chrome/browser/ui/ash/ambient/backdrop/photo_client_impl.cc b/chrome/browser/ui/ash/ambient/backdrop/photo_client_impl.cc
index 7e5f548f..e478c3f 100644
--- a/chrome/browser/ui/ash/ambient/backdrop/photo_client_impl.cc
+++ b/chrome/browser/ui/ash/ambient/backdrop/photo_client_impl.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/ui/ash/ambient/backdrop/photo_client_impl.h"
 
 #include <utility>
+#include <vector>
 
 #include "ash/public/cpp/ambient/ambient_prefs.h"
 #include "base/base64.h"
@@ -157,8 +158,23 @@
 void PhotoClientImpl::FetchTopicInfo(OnTopicInfoFetchedCallback callback) {
   // TODO(b/148463064): Access token will be requested and cached before
   // entering lock screen.
+  // Consolidate the functions of StartToFetchTopicInfo, StartToGetSettings, and
+  // StartToUpdateSettings after this is done.
   RequestAccessToken(base::BindOnce(&PhotoClientImpl::StartToFetchTopicInfo,
-                                    base::Unretained(this),
+                                    weak_factory_.GetWeakPtr(),
+                                    std::move(callback)));
+}
+
+void PhotoClientImpl::GetSettings(GetSettingsCallback callback) {
+  RequestAccessToken(base::BindOnce(&PhotoClientImpl::StartToGetSettings,
+                                    weak_factory_.GetWeakPtr(),
+                                    std::move(callback)));
+}
+
+void PhotoClientImpl::UpdateSettings(int topic_source,
+                                     UpdateSettingsCallback callback) {
+  RequestAccessToken(base::BindOnce(&PhotoClientImpl::StartToUpdateSettings,
+                                    weak_factory_.GetWeakPtr(), topic_source,
                                     std::move(callback)));
 }
 
@@ -172,6 +188,8 @@
   signin::ScopeSet scopes;
   scopes.insert(kPhotosOAuthScope);
   // TODO(b/148463064): Handle retry refresh token and multiple requests.
+  // Currently only one request is allowed.
+  DCHECK(!access_token_fetcher_);
   access_token_fetcher_ = identity_manager->CreateAccessTokenFetcherForAccount(
       account_info.account_id, /*oauth_consumer_name=*/"ChromeOS_AmbientMode",
 
@@ -186,45 +204,22 @@
     signin::AccessTokenInfo access_token_info) {
   access_token_fetcher_.reset();
   if (gaia_id.empty() || access_token_info.token.empty()) {
-    std::move(callback).Run(/*success=*/false, base::nullopt);
+    std::move(callback).Run(base::nullopt);
     return;
   }
 
   std::string client_id = GetClientId();
   chromeos::ambient::BackdropClientConfig::Request request =
-      backdrop_client_config_.GetFetchTopicInfoRequest(
+      backdrop_client_config_.CreateFetchTopicInfoRequest(
           gaia_id, access_token_info.token, client_id);
   auto resource_request = CreateResourceRequest(request);
 
-  net::NetworkTrafficAnnotationTag traffic_annotation =
-      net::DefineNetworkTrafficAnnotation("backdrop_fetch_topics",
-                                          R"(
-            semantics {
-              sender: "ChromeOS Ambient Mode"
-              description:
-                "The ChromeOS Ambient Mode displays a rich set of photos for "
-                "users to choose from."
-              trigger:
-                "When ChromeOS Ambient Mode starts"
-              data:
-                "The Backdrop protocol buffer messages. Containing user auth"
-                "token and a unique random client id."
-              destination: GOOGLE_OWNED_SERVICE
-            }
-            policy {
-              cookies_allowed: NO
-              setting:
-                "NA"
-              policy_exception_justification:
-                "Not implemented, considered not necessary."
-            })");
-
   // |base::Unretained| is safe because this instance outlives
   // |backdrop_url_loader_|.
   DCHECK(!backdrop_url_loader_);
   backdrop_url_loader_ = std::make_unique<BackdropURLLoader>();
   backdrop_url_loader_->Start(
-      std::move(resource_request), request.body, traffic_annotation,
+      std::move(resource_request), request.body, NO_TRAFFIC_ANNOTATION_YET,
       base::BindOnce(&PhotoClientImpl::OnTopicInfoFetched,
                      base::Unretained(this), std::move(callback)));
 }
@@ -239,5 +234,84 @@
   backdrop::ScreenUpdate::Topic backdrop_topic =
       BackdropClientConfig::ParseFetchTopicInfoResponse(*response);
   ash::PhotoController::Topic topic = CreateTopicFrom(backdrop_topic);
-  std::move(callback).Run(/*success=*/true, topic);
+  std::move(callback).Run(topic);
+}
+
+void PhotoClientImpl::StartToGetSettings(
+    GetSettingsCallback callback,
+    const std::string& gaia_id,
+    GoogleServiceAuthError error,
+    signin::AccessTokenInfo access_token_info) {
+  access_token_fetcher_.reset();
+
+  if (gaia_id.empty() || access_token_info.token.empty()) {
+    std::move(callback).Run(/*topic_source=*/base::nullopt);
+    return;
+  }
+
+  std::string client_id = GetClientId();
+  BackdropClientConfig::Request request =
+      backdrop_client_config_.CreateGetSettingsRequest(
+          gaia_id, access_token_info.token, client_id);
+  auto resource_request = CreateResourceRequest(request);
+
+  // |base::Unretained| is safe because this instance outlives
+  // |backdrop_url_loader_|.
+  DCHECK(!backdrop_url_loader_);
+  backdrop_url_loader_ = std::make_unique<BackdropURLLoader>();
+  backdrop_url_loader_->Start(
+      std::move(resource_request), request.body, NO_TRAFFIC_ANNOTATION_YET,
+      base::BindOnce(&PhotoClientImpl::OnGetSettings, base::Unretained(this),
+                     std::move(callback)));
+}
+
+void PhotoClientImpl::OnGetSettings(GetSettingsCallback callback,
+                                    std::unique_ptr<std::string> response) {
+  DCHECK(backdrop_url_loader_);
+  backdrop_url_loader_.reset();
+
+  int topic_source = BackdropClientConfig::ParseGetSettingsResponse(*response);
+  if (topic_source == -1)
+    std::move(callback).Run(base::nullopt);
+  else
+    std::move(callback).Run(topic_source);
+}
+
+void PhotoClientImpl::StartToUpdateSettings(
+    int topic_source,
+    UpdateSettingsCallback callback,
+    const std::string& gaia_id,
+    GoogleServiceAuthError error,
+    signin::AccessTokenInfo access_token_info) {
+  access_token_fetcher_.reset();
+
+  if (gaia_id.empty() || access_token_info.token.empty()) {
+    std::move(callback).Run(/*success=*/false);
+    return;
+  }
+
+  std::string client_id = GetClientId();
+  BackdropClientConfig::Request request =
+      backdrop_client_config_.CreateUpdateSettingsRequest(
+          gaia_id, access_token_info.token, client_id, topic_source);
+  auto resource_request = CreateResourceRequest(request);
+
+  // |base::Unretained| is safe because this instance outlives
+  // |backdrop_url_loader_|.
+  DCHECK(!backdrop_url_loader_);
+  backdrop_url_loader_ = std::make_unique<BackdropURLLoader>();
+  backdrop_url_loader_->Start(
+      std::move(resource_request), request.body, NO_TRAFFIC_ANNOTATION_YET,
+      base::BindOnce(&PhotoClientImpl::OnUpdateSettings, base::Unretained(this),
+                     std::move(callback)));
+}
+
+void PhotoClientImpl::OnUpdateSettings(UpdateSettingsCallback callback,
+                                       std::unique_ptr<std::string> response) {
+  DCHECK(backdrop_url_loader_);
+  backdrop_url_loader_.reset();
+
+  const bool success =
+      BackdropClientConfig::ParseUpdateSettingsResponse(*response);
+  std::move(callback).Run(success);
 }
diff --git a/chrome/browser/ui/ash/ambient/backdrop/photo_client_impl.h b/chrome/browser/ui/ash/ambient/backdrop/photo_client_impl.h
index 0b248fb..b0729a0 100644
--- a/chrome/browser/ui/ash/ambient/backdrop/photo_client_impl.h
+++ b/chrome/browser/ui/ash/ambient/backdrop/photo_client_impl.h
@@ -7,7 +7,7 @@
 
 #include <memory>
 
-#include "base/callback.h"
+#include "ash/public/cpp/ambient/photo_controller.h"
 #include "base/memory/weak_ptr.h"
 #include "chrome/browser/ui/ash/ambient/photo_client.h"
 #include "chromeos/assistant/internal/ambient/backdrop_client_config.h"
@@ -23,15 +23,22 @@
 // The photo client impl talks to Backdrop service.
 class PhotoClientImpl : public PhotoClient {
  public:
+  using GetSettingsCallback = ash::PhotoController::GetSettingsCallback;
+  using UpdateSettingsCallback = ash::PhotoController::UpdateSettingsCallback;
+
   PhotoClientImpl();
-  ~PhotoClientImpl() override;
   PhotoClientImpl(const PhotoClientImpl&) = delete;
   PhotoClientImpl& operator=(const PhotoClientImpl&) = delete;
+  ~PhotoClientImpl() override;
 
   // PhotoClient:
   void FetchTopicInfo(OnTopicInfoFetchedCallback callback) override;
+  void GetSettings(GetSettingsCallback callback) override;
+  void UpdateSettings(int topic_source,
+                      UpdateSettingsCallback callback) override;
 
  private:
+  using BackdropClientConfig = chromeos::ambient::BackdropClientConfig;
   using GetAccessTokenCallback =
       base::OnceCallback<void(const std::string& gaia_id,
                               GoogleServiceAuthError error,
@@ -47,12 +54,29 @@
   void OnTopicInfoFetched(OnTopicInfoFetchedCallback callback,
                           std::unique_ptr<std::string> response);
 
+  void StartToGetSettings(GetSettingsCallback callback,
+                          const std::string& gaia_id,
+                          GoogleServiceAuthError error,
+                          signin::AccessTokenInfo access_token_info);
+
+  void OnGetSettings(GetSettingsCallback callback,
+                     std::unique_ptr<std::string> response);
+
+  void StartToUpdateSettings(int topic_sources,
+                             UpdateSettingsCallback callback,
+                             const std::string& gaia_id,
+                             GoogleServiceAuthError error,
+                             signin::AccessTokenInfo access_token_info);
+
+  void OnUpdateSettings(UpdateSettingsCallback callback,
+                        std::unique_ptr<std::string> response);
+
   // The url loader for the Backdrop service request.
   std::unique_ptr<BackdropURLLoader> backdrop_url_loader_;
 
   std::unique_ptr<signin::AccessTokenFetcher> access_token_fetcher_;
 
-  chromeos::ambient::BackdropClientConfig backdrop_client_config_;
+  BackdropClientConfig backdrop_client_config_;
 
   base::WeakPtrFactory<PhotoClientImpl> weak_factory_{this};
 };
diff --git a/chrome/browser/ui/ash/ambient/photo_client.cc b/chrome/browser/ui/ash/ambient/photo_client.cc
index 44c2aa3..d0d26970 100644
--- a/chrome/browser/ui/ash/ambient/photo_client.cc
+++ b/chrome/browser/ui/ash/ambient/photo_client.cc
@@ -4,6 +4,9 @@
 
 #include "chrome/browser/ui/ash/ambient/photo_client.h"
 
+#include <vector>
+
+#include "base/callback.h"
 #include "build/buildflag.h"
 #include "chromeos/assistant/buildflags.h"
 
@@ -21,5 +24,16 @@
 }
 
 void PhotoClient::FetchTopicInfo(OnTopicInfoFetchedCallback callback) {
-  std::move(callback).Run(/*success=*/false, base::nullopt);
+  std::move(callback).Run(/*topic=*/base::nullopt);
+}
+
+void PhotoClient::GetSettings(
+    ash::PhotoController::GetSettingsCallback callback) {
+  std::move(callback).Run(/*topic_source=*/base::nullopt);
+}
+
+void PhotoClient::UpdateSettings(
+    int topic_source,
+    ash::PhotoController::UpdateSettingsCallback callback) {
+  std::move(callback).Run(/*success=*/false);
 }
diff --git a/chrome/browser/ui/ash/ambient/photo_client.h b/chrome/browser/ui/ash/ambient/photo_client.h
index 79113d9..ea4406d0 100644
--- a/chrome/browser/ui/ash/ambient/photo_client.h
+++ b/chrome/browser/ui/ash/ambient/photo_client.h
@@ -8,14 +8,13 @@
 #include <memory>
 
 #include "ash/public/cpp/ambient/photo_controller.h"
-#include "base/callback.h"
+#include "base/callback_forward.h"
 #include "base/optional.h"
 
 // The interface of a client to retrieve photos.
 class PhotoClient {
  public:
   using OnTopicInfoFetchedCallback = base::OnceCallback<void(
-      bool success,
       const base::Optional<ash::PhotoController::Topic>& topic)>;
 
   // Creates PhotoClient based on the build flag ENABLE_CROS_LIBASSISTANT.
@@ -27,6 +26,12 @@
   virtual ~PhotoClient() = default;
 
   virtual void FetchTopicInfo(OnTopicInfoFetchedCallback callback);
+
+  virtual void GetSettings(ash::PhotoController::GetSettingsCallback callback);
+
+  virtual void UpdateSettings(
+      int topic_source,
+      ash::PhotoController::UpdateSettingsCallback callback);
 };
 
 #endif  // CHROME_BROWSER_UI_ASH_AMBIENT_PHOTO_CLIENT_H_
diff --git a/chrome/browser/ui/ash/ambient/photo_controller_impl.cc b/chrome/browser/ui/ash/ambient/photo_controller_impl.cc
index 54ad188..d3dd858b 100644
--- a/chrome/browser/ui/ash/ambient/photo_controller_impl.cc
+++ b/chrome/browser/ui/ash/ambient/photo_controller_impl.cc
@@ -12,9 +12,8 @@
 #include "components/account_id/account_id.h"
 #include "ui/gfx/image/image_skia.h"
 
-PhotoControllerImpl::PhotoControllerImpl() : weak_factory_(this) {
-  photo_client_ = PhotoClient::Create();
-}
+PhotoControllerImpl::PhotoControllerImpl()
+    : photo_client_(PhotoClient::Create()) {}
 
 PhotoControllerImpl::~PhotoControllerImpl() = default;
 
@@ -24,11 +23,19 @@
                      weak_factory_.GetWeakPtr(), std::move(callback)));
 }
 
+void PhotoControllerImpl::GetSettings(GetSettingsCallback callback) {
+  photo_client_->GetSettings(std::move(callback));
+}
+
+void PhotoControllerImpl::UpdateSettings(int topic_source,
+                                         UpdateSettingsCallback callback) {
+  photo_client_->UpdateSettings(topic_source, std::move(callback));
+}
+
 void PhotoControllerImpl::OnNextImageInfoFetched(
     PhotoDownloadCallback callback,
-    bool success,
     const base::Optional<ash::PhotoController::Topic>& topic) {
-  if (!success ||
+  if (!topic.has_value() ||
       (topic->url.empty() && !topic->portrait_image_url.has_value())) {
     std::move(callback).Run(/*success=*/false, gfx::ImageSkia());
     return;
@@ -41,5 +48,5 @@
           ->GetAccountId();
   ash::AssistantImageDownloader::GetInstance()->Download(
       account_id, GURL(image_url),
-      base::BindOnce(std::move(callback), success));
+      base::BindOnce(std::move(callback), /*success=*/true));
 }
diff --git a/chrome/browser/ui/ash/ambient/photo_controller_impl.h b/chrome/browser/ui/ash/ambient/photo_controller_impl.h
index c327f60f..e81e456 100644
--- a/chrome/browser/ui/ash/ambient/photo_controller_impl.h
+++ b/chrome/browser/ui/ash/ambient/photo_controller_impl.h
@@ -24,16 +24,18 @@
 
   // ash::PhotoController:
   void GetNextImage(PhotoDownloadCallback callback) override;
+  void GetSettings(GetSettingsCallback callback) override;
+  void UpdateSettings(int topic_source,
+                      UpdateSettingsCallback callback) override;
 
  private:
   void OnNextImageInfoFetched(
       PhotoDownloadCallback callback,
-      bool success,
       const base::Optional<ash::PhotoController::Topic>& topic);
 
   std::unique_ptr<PhotoClient> photo_client_;
 
-  base::WeakPtrFactory<PhotoControllerImpl> weak_factory_;
+  base::WeakPtrFactory<PhotoControllerImpl> weak_factory_{this};
 
   DISALLOW_COPY_AND_ASSIGN(PhotoControllerImpl);
 };
diff --git a/chrome/browser/ui/ash/chrome_new_window_client.cc b/chrome/browser/ui/ash/chrome_new_window_client.cc
index 3147572..7819b5ba 100644
--- a/chrome/browser/ui/ash/chrome_new_window_client.cc
+++ b/chrome/browser/ui/ash/chrome_new_window_client.cc
@@ -17,7 +17,6 @@
 #include "chrome/browser/apps/app_service/app_service_proxy.h"
 #include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
 #include "chrome/browser/apps/app_service/launch_utils.h"
-#include "chrome/browser/apps/launch_service/launch_service.h"
 #include "chrome/browser/chromeos/apps/metrics/intent_handling_metrics.h"
 #include "chrome/browser/chromeos/arc/arc_util.h"
 #include "chrome/browser/chromeos/arc/arc_web_contents_data.h"
@@ -121,6 +120,7 @@
      {ChromePage::PLUGINVMDETAILS, chrome::kPluginVmDetailsSubPage},
      {ChromePage::PLUGINVMSHAREDPATHS, chrome::kPluginVmSharedPathsSubPage},
      {ChromePage::OSACCESSIBILITY, chrome::kOsAccessibilitySubPage},
+     {ChromePage::OSPRINTING, chrome::kOsPrintingSubPage},
      {ChromePage::OSRESET, chrome::kOsResetSubPage},
      {ChromePage::OSSEARCH, chrome::kOsSearchSubPage},
      {ChromePage::POINTEROVERLAY, chrome::kPointerOverlaySubPage},
@@ -144,6 +144,7 @@
         {ChromePage::PASSWORDS, chrome::kPasswordManagerSubPage},
         {ChromePage::PRIVACY, chrome::kPrivacySubPage},
         {ChromePage::RESET, chrome::kResetSubPage},
+        {ChromePage::PRINTING, chrome::kPrintingSettingsSubPage},
         {ChromePage::SEARCH, chrome::kSearchSubPage},
         {ChromePage::SYNCSETUP, chrome::kSyncSetupSubPage},
         {ChromePage::LANGUAGES, chrome::kLanguagesSubPage},
@@ -424,26 +425,24 @@
     return;
   }
 
-  auto launch_container = apps::mojom::LaunchContainer::kLaunchContainerWindow;
+  int event_flags = apps::GetEventFlags(
+      apps::mojom::LaunchContainer::kLaunchContainerWindow,
+      WindowOpenDisposition::NEW_WINDOW, /*prefer_container=*/false);
   if (web_app::WebAppProviderBase::GetProviderBase(profile)
           ->registrar()
           .GetAppEffectiveDisplayMode(*app_id) ==
       blink::mojom::DisplayMode::kBrowser) {
-    launch_container = apps::mojom::LaunchContainer::kLaunchContainerTab;
+    event_flags = apps::GetEventFlags(
+        apps::mojom::LaunchContainer::kLaunchContainerTab,
+        WindowOpenDisposition::NEW_FOREGROUND_TAB, /*prefer_container=*/false);
   }
 
-  apps::AppLaunchParams params = apps::AppLaunchParams(
-      *app_id, launch_container, WindowOpenDisposition::NEW_WINDOW,
-      apps::mojom::AppLaunchSource::kSourceArc);
-  params.override_url = url;
-  content::WebContents* tab =
-      apps::LaunchService::Get(profile)->OpenApplication(params);
-  if (!tab)
-    return;
-
-  // Add a flag to remember this tab originated in the ARC context.
-  tab->SetUserData(&arc::ArcWebContentsData::kArcTransitionFlag,
-                   std::make_unique<arc::ArcWebContentsData>());
+  apps::AppServiceProxy* proxy =
+      apps::AppServiceProxyFactory::GetForProfile(profile);
+  DCHECK(proxy);
+  proxy->LaunchAppWithUrl(*app_id, event_flags, url,
+                          apps::mojom::LaunchSource::kFromArc,
+                          display::kInvalidDisplayId);
 }
 
 void ChromeNewWindowClient::OpenArcCustomTab(
diff --git a/chrome/browser/ui/ash/chrome_new_window_client_browsertest.cc b/chrome/browser/ui/ash/chrome_new_window_client_browsertest.cc
index dca10f87..3b80ffa 100644
--- a/chrome/browser/ui/ash/chrome_new_window_client_browsertest.cc
+++ b/chrome/browser/ui/ash/chrome_new_window_client_browsertest.cc
@@ -280,6 +280,8 @@
                      base_url.Resolve(chrome::kManageAccessibilitySubPage));
   TestOpenChromePage(ChromePage::NETWORKSTYPEVPN,
                      base_url.Resolve(chrome::kVPNSettingsSubPage));
+  TestOpenChromePage(ChromePage::OSPRINTING,
+                     base_url.Resolve(chrome::kOsPrintingSubPage));
   TestOpenChromePage(ChromePage::POINTEROVERLAY,
                      base_url.Resolve(chrome::kPointerOverlaySubPage));
   TestOpenChromePage(ChromePage::OSRESET,
@@ -363,6 +365,8 @@
                      base_url.Resolve(chrome::kPasswordManagerSubPage));
   TestOpenChromePage(ChromePage::RESET,
                      base_url.Resolve(chrome::kResetSubPage));
+  TestOpenChromePage(ChromePage::PRINTING,
+                     base_url.Resolve(chrome::kPrintingSettingsSubPage));
   TestOpenChromePage(ChromePage::SEARCH,
                      base_url.Resolve(chrome::kSearchSubPage));
   TestOpenChromePage(ChromePage::SYNCSETUP,
diff --git a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_browsertest.cc b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_browsertest.cc
index bc29f2f6..f0a57fc 100644
--- a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_browsertest.cc
+++ b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_browsertest.cc
@@ -35,10 +35,11 @@
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
 #include "chrome/app/chrome_command_ids.h"
+#include "chrome/browser/apps/app_service/app_launch_params.h"
 #include "chrome/browser/apps/app_service/app_service_proxy.h"
 #include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
+#include "chrome/browser/apps/app_service/browser_app_launcher.h"
 #include "chrome/browser/apps/app_service/launch_utils.h"
-#include "chrome/browser/apps/launch_service/launch_service.h"
 #include "chrome/browser/apps/platform_apps/app_browsertest_util.h"
 #include "chrome/browser/chromeos/accessibility/accessibility_manager.h"
 #include "chrome/browser/chromeos/accessibility/speech_monitor.h"
@@ -1525,10 +1526,12 @@
   const Extension* extension = extension_registry()->GetExtensionById(
       last_loaded_extension_id(), extensions::ExtensionRegistry::ENABLED);
   EXPECT_TRUE(extension);
-  apps::LaunchService::Get(profile())->OpenApplication(apps::AppLaunchParams(
-      extension->id(), extensions::LaunchContainer::kLaunchContainerTab,
-      WindowOpenDisposition::NEW_WINDOW,
-      apps::mojom::AppLaunchSource::kSourceTest));
+  apps::AppServiceProxyFactory::GetForProfile(profile())
+      ->BrowserAppLauncher()
+      .LaunchAppWithParams(apps::AppLaunchParams(
+          extension->id(), extensions::LaunchContainer::kLaunchContainerTab,
+          WindowOpenDisposition::NEW_WINDOW,
+          apps::mojom::AppLaunchSource::kSourceTest));
 
   EXPECT_EQ(++browsers, NumberOfDetectedLauncherBrowsers(false));
   EXPECT_EQ(++tabs, NumberOfDetectedLauncherBrowsers(true));
diff --git a/chrome/browser/ui/browser_browsertest.cc b/chrome/browser/ui/browser_browsertest.cc
index 9eb4169..a578db19 100644
--- a/chrome/browser/ui/browser_browsertest.cc
+++ b/chrome/browser/ui/browser_browsertest.cc
@@ -28,7 +28,9 @@
 #include "build/build_config.h"
 #include "chrome/app/chrome_command_ids.h"
 #include "chrome/browser/apps/app_service/app_launch_params.h"
-#include "chrome/browser/apps/launch_service/launch_service.h"
+#include "chrome/browser/apps/app_service/app_service_proxy.h"
+#include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
+#include "chrome/browser/apps/app_service/browser_app_launcher.h"
 #include "chrome/browser/chrome_content_browser_client.h"
 #include "chrome/browser/command_updater.h"
 #include "chrome/browser/defaults.h"
@@ -1384,8 +1386,9 @@
 
   // Launch it in a window, as AppLauncherHandler::HandleLaunchApp() would.
   WebContents* app_window =
-      apps::LaunchService::Get(browser()->profile())
-          ->OpenApplication(apps::AppLaunchParams(
+      apps::AppServiceProxyFactory::GetForProfile(browser()->profile())
+          ->BrowserAppLauncher()
+          .LaunchAppWithParams(apps::AppLaunchParams(
               extension_app->id(),
               apps::mojom::LaunchContainer::kLaunchContainerWindow,
               WindowOpenDisposition::NEW_WINDOW,
@@ -1556,8 +1559,9 @@
 
   // Launch it in a window, as AppLauncherHandler::HandleLaunchApp() would.
   WebContents* app_window =
-      apps::LaunchService::Get(browser()->profile())
-          ->OpenApplication(apps::AppLaunchParams(
+      apps::AppServiceProxyFactory::GetForProfile(browser()->profile())
+          ->BrowserAppLauncher()
+          .LaunchAppWithParams(apps::AppLaunchParams(
               extension_app->id(),
               apps::mojom::LaunchContainer::kLaunchContainerWindow,
               WindowOpenDisposition::NEW_WINDOW,
diff --git a/chrome/browser/ui/browser_commands.cc b/chrome/browser/ui/browser_commands.cc
index d5561d7..b2b19044 100644
--- a/chrome/browser/ui/browser_commands.cc
+++ b/chrome/browser/ui/browser_commands.cc
@@ -16,7 +16,9 @@
 #include "build/build_config.h"
 #include "chrome/app/chrome_command_ids.h"
 #include "chrome/browser/apps/app_service/app_launch_params.h"
-#include "chrome/browser/apps/launch_service/launch_service.h"
+#include "chrome/browser/apps/app_service/app_service_proxy.h"
+#include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
+#include "chrome/browser/apps/app_service/browser_app_launcher.h"
 #include "chrome/browser/bookmarks/bookmark_model_factory.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/browsing_data/browsing_data_helper.h"
@@ -613,7 +615,9 @@
     const apps::AppLaunchParams params = apps::AppLaunchParams(
         app_id, launch_container, WindowOpenDisposition::NEW_WINDOW,
         apps::mojom::AppLaunchSource::kSourceKeyboard);
-    apps::LaunchService::Get(profile)->OpenApplication(params);
+    apps::AppServiceProxyFactory::GetForProfile(profile)
+        ->BrowserAppLauncher()
+        .LaunchAppWithParams(params);
     return;
   }
 
diff --git a/chrome/browser/ui/chrome_pages.cc b/chrome/browser/ui/chrome_pages.cc
index d17e500..39e1344 100644
--- a/chrome/browser/ui/chrome_pages.cc
+++ b/chrome/browser/ui/chrome_pages.cc
@@ -19,9 +19,10 @@
 #include "base/system/sys_info.h"
 #include "build/branding_buildflags.h"
 #include "build/build_config.h"
+#include "chrome/browser/apps/app_service/app_launch_params.h"
 #include "chrome/browser/apps/app_service/app_service_proxy.h"
 #include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
-#include "chrome/browser/apps/launch_service/launch_service.h"
+#include "chrome/browser/apps/app_service/browser_app_launcher.h"
 #include "chrome/browser/bookmarks/bookmark_model_factory.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/chromeos/extensions/default_web_app_ids.h"
@@ -154,7 +155,9 @@
         profile, extension, 0, apps::mojom::AppLaunchSource::kSourceUntracked,
         -1);
     params.override_url = GURL(BuildQueryString(profile));
-    apps::LaunchService::Get(profile)->OpenApplication(params);
+    apps::AppServiceProxyFactory::GetForProfile(profile)
+        ->BrowserAppLauncher()
+        .LaunchAppWithParams(params);
     return;
   }
   DVLOG(1) << "ReleaseNotes App Not Found";
diff --git a/chrome/browser/ui/cocoa/apps/native_app_window_cocoa_browsertest.mm b/chrome/browser/ui/cocoa/apps/native_app_window_cocoa_browsertest.mm
index 76fa20a3..2d629f4b 100644
--- a/chrome/browser/ui/cocoa/apps/native_app_window_cocoa_browsertest.mm
+++ b/chrome/browser/ui/cocoa/apps/native_app_window_cocoa_browsertest.mm
@@ -13,11 +13,13 @@
 #import "base/mac/scoped_nsobject.h"
 #include "base/macros.h"
 #include "chrome/browser/apps/app_service/app_launch_params.h"
+#include "chrome/browser/apps/app_service/app_service_proxy.h"
+#include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
+#include "chrome/browser/apps/app_service/browser_app_launcher.h"
 #include "chrome/browser/apps/app_shim/app_shim_host_bootstrap_mac.h"
 #include "chrome/browser/apps/app_shim/app_shim_host_mac.h"
 #include "chrome/browser/apps/app_shim/app_shim_manager_mac.h"
 #include "chrome/browser/apps/app_shim/test/app_shim_listener_test_api_mac.h"
-#include "chrome/browser/apps/launch_service/launch_service.h"
 #include "chrome/browser/apps/platform_apps/app_browsertest_util.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/browser_process_platform_part.h"
@@ -62,8 +64,9 @@
       content::WindowedNotificationObserver app_loaded_observer(
           content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME,
           content::NotificationService::AllSources());
-      apps::LaunchService::Get(profile())->OpenApplication(
-          apps::AppLaunchParams(
+      apps::AppServiceProxyFactory::GetForProfile(profile())
+          ->BrowserAppLauncher()
+          .LaunchAppWithParams(apps::AppLaunchParams(
               app_->id(), apps::mojom::LaunchContainer::kLaunchContainerNone,
               WindowOpenDisposition::NEW_WINDOW,
               apps::mojom::AppLaunchSource::kSourceTest));
diff --git a/chrome/browser/ui/extensions/app_launch_params.cc b/chrome/browser/ui/extensions/app_launch_params.cc
index 05f76b3..ee3bfb5 100644
--- a/chrome/browser/ui/extensions/app_launch_params.cc
+++ b/chrome/browser/ui/extensions/app_launch_params.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/ui/extensions/app_launch_params.h"
 
+#include "chrome/browser/apps/app_service/launch_utils.h"
 #include "chrome/browser/extensions/launch_util.h"
 #include "chrome/browser/profiles/profile.h"
 #include "extensions/browser/extension_prefs.h"
diff --git a/chrome/browser/ui/extensions/hosted_app_browsertest.cc b/chrome/browser/ui/extensions/hosted_app_browsertest.cc
index 903c18b..84fa6e5 100644
--- a/chrome/browser/ui/extensions/hosted_app_browsertest.cc
+++ b/chrome/browser/ui/extensions/hosted_app_browsertest.cc
@@ -17,6 +17,7 @@
 #include "base/strings/string_split.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/test/bind_test_util.h"
 #include "base/test/metrics/user_action_tester.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/values.h"
@@ -38,8 +39,12 @@
 #include "chrome/browser/ui/web_applications/test/web_app_browsertest_util.h"
 #include "chrome/browser/ui/web_applications/web_app_launch_utils.h"
 #include "chrome/browser/ui/web_applications/web_app_menu_model.h"
+#include "chrome/browser/web_applications/components/app_registrar.h"
 #include "chrome/browser/web_applications/components/app_shortcut_manager.h"
 #include "chrome/browser/web_applications/components/external_install_options.h"
+#include "chrome/browser/web_applications/components/install_manager.h"
+#include "chrome/browser/web_applications/components/web_app_constants.h"
+#include "chrome/browser/web_applications/components/web_app_helpers.h"
 #include "chrome/browser/web_applications/components/web_app_id.h"
 #include "chrome/browser/web_applications/components/web_app_provider_base.h"
 #include "chrome/browser/web_applications/test/web_app_install_observer.h"
@@ -647,6 +652,36 @@
       EvalJs(subframe, "document.body.innerText.trim();").ExtractString());
 }
 
+using BookmarkAppTest = HostedOrWebAppTest;
+
+IN_PROC_BROWSER_TEST_P(BookmarkAppTest, InstallFromSync) {
+  ASSERT_TRUE(https_server()->Start());
+
+  const GURL app_url =
+      https_server()->GetURL("/banners/manifest_test_page.html");
+  const web_app::AppId app_id = web_app::GenerateAppIdFromURL(app_url);
+
+  auto web_app_info = std::make_unique<WebApplicationInfo>();
+  web_app_info->app_url = app_url;
+  web_app_info->scope = app_url.GetWithoutFilename();
+
+  base::RunLoop run_loop;
+  web_app::WebAppProviderBase* const provider =
+      web_app::WebAppProviderBase::GetProviderBase(profile());
+  DCHECK(provider);
+  provider->install_manager().InstallBookmarkAppFromSync(
+      app_id, std::move(web_app_info),
+      base::BindLambdaForTesting([&](const web_app::AppId& installed_app_id,
+                                     web_app::InstallResultCode code) {
+        EXPECT_EQ(web_app::InstallResultCode::kSuccessNewInstall, code);
+        EXPECT_EQ(app_id, installed_app_id);
+        run_loop.Quit();
+      }));
+  run_loop.Run();
+  EXPECT_EQ(web_app::DisplayMode::kStandalone,
+            provider->registrar().GetAppDisplayMode(app_id));
+}
+
 // Tests that platform apps can still load mixed content.
 IN_PROC_BROWSER_TEST_P(HostedAppTestWithAutoupgradesDisabled,
                        MixedContentInPlatformApp) {
@@ -1797,6 +1832,10 @@
                          ::testing::Values(AppType::HOSTED_APP));
 
 INSTANTIATE_TEST_SUITE_P(All,
+                         BookmarkAppTest,
+                         ::testing::Values(AppType::BOOKMARK_APP));
+
+INSTANTIATE_TEST_SUITE_P(All,
                          HostedAppTestWithAutoupgradesDisabled,
                          ::testing::Values(AppType::HOSTED_APP));
 
diff --git a/chrome/browser/ui/permission_bubble/permission_bubble_browser_test_util.cc b/chrome/browser/ui/permission_bubble/permission_bubble_browser_test_util.cc
index eba911014..a1c6b30 100644
--- a/chrome/browser/ui/permission_bubble/permission_bubble_browser_test_util.cc
+++ b/chrome/browser/ui/permission_bubble/permission_bubble_browser_test_util.cc
@@ -6,7 +6,9 @@
 
 #include "base/command_line.h"
 #include "chrome/browser/apps/app_service/app_launch_params.h"
-#include "chrome/browser/apps/launch_service/launch_service.h"
+#include "chrome/browser/apps/app_service/app_service_proxy.h"
+#include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
+#include "chrome/browser/apps/app_service/browser_app_launcher.h"
 #include "chrome/browser/extensions/extension_browsertest.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_window.h"
@@ -58,7 +60,9 @@
       apps::mojom::AppLaunchSource::kSourceTest);
 
   content::WebContents* app_contents =
-      apps::LaunchService::Get(browser()->profile())->OpenApplication(params);
+      apps::AppServiceProxyFactory::GetForProfile(browser()->profile())
+          ->BrowserAppLauncher()
+          .LaunchAppWithParams(params);
   CHECK(app_contents);
   return app_contents;
 }
diff --git a/chrome/browser/ui/settings_window_manager_browsertest_chromeos.cc b/chrome/browser/ui/settings_window_manager_browsertest_chromeos.cc
index b77e519..f1a0bbb 100644
--- a/chrome/browser/ui/settings_window_manager_browsertest_chromeos.cc
+++ b/chrome/browser/ui/settings_window_manager_browsertest_chromeos.cc
@@ -6,7 +6,9 @@
 
 #include "base/macros.h"
 #include "chrome/browser/apps/app_service/app_launch_params.h"
-#include "chrome/browser/apps/launch_service/launch_service.h"
+#include "chrome/browser/apps/app_service/app_service_proxy.h"
+#include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
+#include "chrome/browser/apps/app_service/browser_app_launcher.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_finder.h"
 #include "chrome/browser/ui/browser_list.h"
@@ -112,8 +114,9 @@
   web_app::AppId settings_app_id = *web_app::GetAppIdForSystemWebApp(
       browser()->profile(), web_app::SystemAppType::SETTINGS);
   content::WebContents* contents =
-      apps::LaunchService::Get(browser()->profile())
-          ->OpenApplication(apps::AppLaunchParams(
+      apps::AppServiceProxyFactory::GetForProfile(browser()->profile())
+          ->BrowserAppLauncher()
+          .LaunchAppWithParams(apps::AppLaunchParams(
               settings_app_id,
               apps::mojom::LaunchContainer::kLaunchContainerWindow,
               WindowOpenDisposition::NEW_WINDOW,
diff --git a/chrome/browser/ui/thumbnails/thumbnail_tab_helper.h b/chrome/browser/ui/thumbnails/thumbnail_tab_helper.h
index ceaa1a4..73e012f 100644
--- a/chrome/browser/ui/thumbnails/thumbnail_tab_helper.h
+++ b/chrome/browser/ui/thumbnails/thumbnail_tab_helper.h
@@ -91,6 +91,7 @@
       mojo::PendingRemote<::viz::mojom::FrameSinkVideoConsumerFrameCallbacks>
           callbacks) override;
   void OnStopped() override;
+  void OnLog(const std::string& /*message*/) override {}
 
   // Returns the dimensions of the multipurpose thumbnail that should be
   // captured from an entire webpage. Can be cropped or compressed later.
diff --git a/chrome/browser/ui/views/crostini/crostini_browser_test_util.cc b/chrome/browser/ui/views/crostini/crostini_browser_test_util.cc
index 6fd2207..93fe3d9 100644
--- a/chrome/browser/ui/views/crostini/crostini_browser_test_util.cc
+++ b/chrome/browser/ui/views/crostini/crostini_browser_test_util.cc
@@ -130,3 +130,32 @@
           component_updater::CrOSComponentManager::Error::INSTALL_FAILURE,
           base::FilePath(), base::FilePath()));
 }
+
+class WebContentsWaiter : public content::WebContentsObserver {
+ public:
+  enum Operation { LOAD };  // Add other operations as required.
+  explicit WebContentsWaiter(content::WebContents* contents,
+                             Operation operation)
+      : content::WebContentsObserver(contents), operation_(operation) {}
+
+  ~WebContentsWaiter() override = default;
+
+  void Wait() { run_loop_.Run(); }
+
+  // content::WebContentsObserver:
+  void DidFinishLoad(content::RenderFrameHost* render_frame_host,
+                     const GURL& validated_url) override {
+    if (operation_ == LOAD) {
+      run_loop_.Quit();
+    }
+  }
+
+ private:
+  base::RunLoop run_loop_;
+  Operation operation_;
+};
+
+void CrostiniDialogBrowserTest::WaitForLoadFinished(
+    content::WebContents* contents) {
+  WebContentsWaiter(contents, WebContentsWaiter::LOAD).Wait();
+}
diff --git a/chrome/browser/ui/views/crostini/crostini_browser_test_util.h b/chrome/browser/ui/views/crostini/crostini_browser_test_util.h
index 3958c91..468ac07 100644
--- a/chrome/browser/ui/views/crostini/crostini_browser_test_util.h
+++ b/chrome/browser/ui/views/crostini/crostini_browser_test_util.h
@@ -15,6 +15,10 @@
 
 class CrostiniBrowserTestChromeBrowserMainExtraParts;
 
+namespace content {
+class WebContents;
+}
+
 // Common base for Crostini dialog broswer tests. Allows tests to set network
 // connection type.
 class CrostiniDialogBrowserTest : public DialogBrowserTest {
@@ -32,6 +36,8 @@
 
   void UnregisterTermina();
 
+  void WaitForLoadFinished(content::WebContents* contents);
+
  protected:
   const bool register_termina_;
 
diff --git a/chrome/browser/ui/views/crostini/crostini_update_component_view_browsertest.cc b/chrome/browser/ui/views/crostini/crostini_update_component_view_browsertest.cc
index 0d5e7f2..bd64705 100644
--- a/chrome/browser/ui/views/crostini/crostini_update_component_view_browsertest.cc
+++ b/chrome/browser/ui/views/crostini/crostini_update_component_view_browsertest.cc
@@ -24,25 +24,6 @@
 #include "content/public/browser/web_contents_observer.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-class LoadFinishedWaiter : public content::WebContentsObserver {
- public:
-  explicit LoadFinishedWaiter(content::WebContents* contents)
-      : content::WebContentsObserver(contents) {}
-
-  ~LoadFinishedWaiter() override = default;
-
-  void Wait() { run_loop_.Run(); }
-
-  // content::WebContentsObserver:
-  void DidFinishLoad(content::RenderFrameHost* render_frame_host,
-                     const GURL& validated_url) override {
-    run_loop_.Quit();
-  }
-
- private:
-  base::RunLoop run_loop_;
-};
-
 class CrostiniUpdateComponentViewBrowserTest
     : public CrostiniDialogBrowserTest {
  public:
@@ -153,8 +134,8 @@
     Browser* terminal_browser = web_app::FindSystemWebAppBrowser(
         browser()->profile(), web_app::SystemAppType::TERMINAL);
     CHECK_NE(nullptr, terminal_browser);
-    LoadFinishedWaiter(terminal_browser->tab_strip_model()->GetWebContentsAt(0))
-        .Wait();
+    WaitForLoadFinished(
+        terminal_browser->tab_strip_model()->GetWebContentsAt(0));
   }
 
   ExpectView();
diff --git a/chrome/browser/ui/views/frame/browser_window_property_manager_browsertest_win.cc b/chrome/browser/ui/views/frame/browser_window_property_manager_browsertest_win.cc
index b141fac..060033b 100644
--- a/chrome/browser/ui/views/frame/browser_window_property_manager_browsertest_win.cc
+++ b/chrome/browser/ui/views/frame/browser_window_property_manager_browsertest_win.cc
@@ -17,7 +17,9 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/win/scoped_propvariant.h"
 #include "chrome/browser/apps/app_service/app_launch_params.h"
-#include "chrome/browser/apps/launch_service/launch_service.h"
+#include "chrome/browser/apps/app_service/app_service_proxy.h"
+#include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
+#include "chrome/browser/apps/app_service/browser_app_launcher.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/extensions/extension_browsertest.h"
 #include "chrome/browser/profiles/profile.h"
@@ -199,8 +201,9 @@
       LoadExtension(test_data_dir_.AppendASCII("app/"));
   EXPECT_TRUE(extension);
 
-  apps::LaunchService::Get(browser()->profile())
-      ->OpenApplication(apps::AppLaunchParams(
+  apps::AppServiceProxyFactory::GetForProfile(browser()->profile())
+      ->BrowserAppLauncher()
+      .LaunchAppWithParams(apps::AppLaunchParams(
           extension->id(), apps::mojom::LaunchContainer::kLaunchContainerWindow,
           WindowOpenDisposition::NEW_FOREGROUND_TAB,
           apps::mojom::AppLaunchSource::kSourceTest));
diff --git a/chrome/browser/ui/views/omnibox/omnibox_view_views.cc b/chrome/browser/ui/views/omnibox/omnibox_view_views.cc
index 3ee576fab..af38122 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_view_views.cc
+++ b/chrome/browser/ui/views/omnibox/omnibox_view_views.cc
@@ -133,6 +133,10 @@
 OmniboxState::~OmniboxState() {
 }
 
+enum OmniboxMenuCommands {
+  kShowUrl = views::Textfield::MenuCommands::kLastCommandId + 1,
+};
+
 }  // namespace
 
 // Animation chosen to match the default values in the edwardjung prototype.
@@ -496,20 +500,13 @@
   // In the base class, touch text selection is deactivated when a command is
   // executed. Since we are not always calling the base class implementation
   // here, we need to deactivate touch text selection here, too.
-  //
-  // Note: while it looks weird that some of these cases are IDC_* and some are
-  // IDS_*, that is intentional. The commands that do not have matching IDC_*
-  // constants are registered using their names' IDS constants as their command
-  // IDs. If at some point in the future someone adds a new IDC constant that
-  // clashes with one of these IDS constants, this stanza will fail to compile
-  // because there'll be a duplicate switch case.
   DestroyTouchSelection();
   switch (command_id) {
     // These commands don't invoke the popup via OnBefore/AfterPossibleChange().
     case IDC_PASTE_AND_GO:
       model()->PasteAndGo(GetClipboardText());
       return;
-    case IDS_SHOW_URL:
+    case kShowUrl:
       model()->Unelide(true /* exit_query_in_omnibox */);
       return;
     case IDC_SHOW_FULL_URLS:
@@ -525,7 +522,7 @@
       return;
 
     // These commands do invoke the popup.
-    case IDS_APP_PASTE:
+    case Textfield::kPaste:
       ExecuteTextEditCommand(ui::TextEditCommand::PASTE);
       return;
     default:
@@ -1454,13 +1451,13 @@
 }
 
 bool OmniboxViewViews::IsCommandIdEnabled(int command_id) const {
-  if (command_id == IDS_APP_PASTE)
+  if (command_id == Textfield::kPaste)
     return !GetReadOnly() && !GetClipboardText().empty();
   if (command_id == IDC_PASTE_AND_GO)
     return !GetReadOnly() && model()->CanPasteAndGo(GetClipboardText());
 
   // Menu item is only shown when it is valid.
-  if (command_id == IDS_SHOW_URL)
+  if (command_id == kShowUrl)
     return true;
   if (command_id == IDC_SHOW_FULL_URLS)
     return true;
@@ -1837,7 +1834,7 @@
           location_bar_view_->GetWebContents())) {
     send_tab_to_self::RecordSendTabToSelfClickResult(
         send_tab_to_self::kOmniboxMenu, SendTabToSelfClickResult::kShowItem);
-    int index = menu_contents->GetIndexOfCommandId(IDS_APP_UNDO);
+    int index = menu_contents->GetIndexOfCommandId(Textfield::kUndo);
     // Add a separator if this is not the first item.
     if (index)
       menu_contents->InsertSeparatorAt(index++, ui::NORMAL_SEPARATOR);
@@ -1870,7 +1867,7 @@
     menu_contents->InsertSeparatorAt(++index, ui::NORMAL_SEPARATOR);
   }
 
-  int paste_position = menu_contents->GetIndexOfCommandId(IDS_APP_PASTE);
+  int paste_position = menu_contents->GetIndexOfCommandId(Textfield::kPaste);
   DCHECK_GE(paste_position, 0);
   menu_contents->InsertItemWithStringIdAt(paste_position + 1, IDC_PASTE_AND_GO,
                                           IDS_PASTE_AND_GO);
@@ -1884,15 +1881,12 @@
     if (!GetReadOnly() && !model()->user_input_in_progress() &&
         GetText() !=
             controller()->GetLocationBarModel()->GetFormattedFullURL()) {
-      menu_contents->AddItemWithStringId(IDS_SHOW_URL, IDS_SHOW_URL);
+      menu_contents->AddItemWithStringId(kShowUrl, IDS_SHOW_URL);
     }
   }
 
   menu_contents->AddSeparator(ui::NORMAL_SEPARATOR);
 
-  // Minor note: We use IDC_ for command id here while the underlying textfield
-  // is using IDS_ for all its command ids. This is because views cannot depend
-  // on IDC_ for now.
   menu_contents->AddItemWithStringId(IDC_EDIT_SEARCH_ENGINES,
                                      IDS_EDIT_SEARCH_ENGINES);
 
diff --git a/chrome/browser/ui/views/toolbar/reload_button.cc b/chrome/browser/ui/views/toolbar/reload_button.cc
index 39e34d09..e373445 100644
--- a/chrome/browser/ui/views/toolbar/reload_button.cc
+++ b/chrome/browser/ui/views/toolbar/reload_button.cc
@@ -28,13 +28,6 @@
 
 namespace {
 
-// Contents of the Reload drop-down menu.
-const int kReloadMenuItems[]  = {
-  IDS_RELOAD_MENU_NORMAL_RELOAD_ITEM,
-  IDS_RELOAD_MENU_HARD_RELOAD_ITEM,
-  IDS_RELOAD_MENU_EMPTY_AND_HARD_RELOAD_ITEM,
-};
-
 const gfx::VectorIcon& GetIconForMode(ReloadButton::IconStyle icon_style,
                                       bool is_reload) {
   const bool touch_ui = ui::TouchUiController::Get()->touch_ui();
@@ -224,39 +217,21 @@
 bool ReloadButton::GetAcceleratorForCommandId(
     int command_id,
     ui::Accelerator* accelerator) const {
-  switch (command_id) {
-    case IDS_RELOAD_MENU_NORMAL_RELOAD_ITEM:
-      GetWidget()->GetAccelerator(IDC_RELOAD, accelerator);
-      return true;
-    case IDS_RELOAD_MENU_HARD_RELOAD_ITEM:
-      GetWidget()->GetAccelerator(IDC_RELOAD_BYPASSING_CACHE, accelerator);
-      return true;
-  }
   return GetWidget()->GetAccelerator(command_id, accelerator);
 }
 
 void ReloadButton::ExecuteCommand(int command_id, int event_flags) {
-  int browser_command = 0;
-  switch (command_id) {
-    case IDS_RELOAD_MENU_NORMAL_RELOAD_ITEM:
-      browser_command = IDC_RELOAD;
-      break;
-    case IDS_RELOAD_MENU_HARD_RELOAD_ITEM:
-      browser_command = IDC_RELOAD_BYPASSING_CACHE;
-      break;
-    case IDS_RELOAD_MENU_EMPTY_AND_HARD_RELOAD_ITEM:
-      browser_command = IDC_RELOAD_CLEARING_CACHE;
-      break;
-    default:
-      NOTREACHED();
-  }
-  ExecuteBrowserCommand(browser_command, event_flags);
+  ExecuteBrowserCommand(command_id, event_flags);
 }
 
 std::unique_ptr<ui::SimpleMenuModel> ReloadButton::CreateMenuModel() {
   auto menu_model = std::make_unique<ui::SimpleMenuModel>(this);
-  for (int item : kReloadMenuItems)
-    menu_model->AddItemWithStringId(item, item);
+  menu_model->AddItemWithStringId(IDC_RELOAD,
+                                  IDS_RELOAD_MENU_NORMAL_RELOAD_ITEM);
+  menu_model->AddItemWithStringId(IDC_RELOAD_BYPASSING_CACHE,
+                                  IDS_RELOAD_MENU_HARD_RELOAD_ITEM);
+  menu_model->AddItemWithStringId(IDC_RELOAD_CLEARING_CACHE,
+                                  IDS_RELOAD_MENU_EMPTY_AND_HARD_RELOAD_ITEM);
   return menu_model;
 }
 
diff --git a/chrome/browser/ui/web_applications/system_web_app_ui_utils.cc b/chrome/browser/ui/web_applications/system_web_app_ui_utils.cc
index 50af7567..c075fc24 100644
--- a/chrome/browser/ui/web_applications/system_web_app_ui_utils.cc
+++ b/chrome/browser/ui/web_applications/system_web_app_ui_utils.cc
@@ -14,6 +14,7 @@
 #include "base/optional.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/apps/app_service/app_launch_params.h"
+#include "chrome/browser/apps/app_service/launch_utils.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser_list.h"
 #include "chrome/browser/ui/browser_navigator.h"
diff --git a/chrome/browser/ui/web_applications/test/web_app_browsertest_util.cc b/chrome/browser/ui/web_applications/test/web_app_browsertest_util.cc
index 27d1d12..fc86418 100644
--- a/chrome/browser/ui/web_applications/test/web_app_browsertest_util.cc
+++ b/chrome/browser/ui/web_applications/test/web_app_browsertest_util.cc
@@ -7,7 +7,9 @@
 #include "base/run_loop.h"
 #include "base/test/bind_test_util.h"
 #include "chrome/browser/apps/app_service/app_launch_params.h"
-#include "chrome/browser/apps/launch_service/launch_service.h"
+#include "chrome/browser/apps/app_service/app_service_proxy.h"
+#include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
+#include "chrome/browser/apps/app_service/browser_app_launcher.h"
 #include "chrome/browser/installable/installable_metrics.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_finder.h"
@@ -59,10 +61,12 @@
 
 Browser* LaunchWebAppBrowser(Profile* profile, const AppId& app_id) {
   EXPECT_TRUE(
-      apps::LaunchService::Get(profile)->OpenApplication(apps::AppLaunchParams(
-          app_id, apps::mojom::LaunchContainer::kLaunchContainerWindow,
-          WindowOpenDisposition::CURRENT_TAB,
-          apps::mojom::AppLaunchSource::kSourceTest)));
+      apps::AppServiceProxyFactory::GetForProfile(profile)
+          ->BrowserAppLauncher()
+          .LaunchAppWithParams(apps::AppLaunchParams(
+              app_id, apps::mojom::LaunchContainer::kLaunchContainerWindow,
+              WindowOpenDisposition::CURRENT_TAB,
+              apps::mojom::AppLaunchSource::kSourceTest)));
 
   Browser* browser = chrome::FindLastActive();
   bool is_correct_app_browser =
@@ -84,10 +88,12 @@
 
 Browser* LaunchBrowserForWebAppInTab(Profile* profile, const AppId& app_id) {
   content::WebContents* web_contents =
-      apps::LaunchService::Get(profile)->OpenApplication(apps::AppLaunchParams(
-          app_id, apps::mojom::LaunchContainer::kLaunchContainerTab,
-          WindowOpenDisposition::NEW_FOREGROUND_TAB,
-          apps::mojom::AppLaunchSource::kSourceTest));
+      apps::AppServiceProxyFactory::GetForProfile(profile)
+          ->BrowserAppLauncher()
+          .LaunchAppWithParams(apps::AppLaunchParams(
+              app_id, apps::mojom::LaunchContainer::kLaunchContainerTab,
+              WindowOpenDisposition::NEW_FOREGROUND_TAB,
+              apps::mojom::AppLaunchSource::kSourceTest));
   DCHECK(web_contents);
 
   WebAppTabHelperBase* tab_helper =
diff --git a/chrome/browser/ui/web_applications/web_app_controller_browsertest.cc b/chrome/browser/ui/web_applications/web_app_controller_browsertest.cc
index 117d3170..24d01c0 100644
--- a/chrome/browser/ui/web_applications/web_app_controller_browsertest.cc
+++ b/chrome/browser/ui/web_applications/web_app_controller_browsertest.cc
@@ -5,7 +5,9 @@
 #include "chrome/browser/ui/web_applications/web_app_controller_browsertest.h"
 
 #include "chrome/browser/apps/app_service/app_launch_params.h"
-#include "chrome/browser/apps/launch_service/launch_service.h"
+#include "chrome/browser/apps/app_service/app_service_proxy.h"
+#include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
+#include "chrome/browser/apps/app_service/browser_app_launcher.h"
 #include "chrome/browser/banners/test_app_banner_manager_desktop.h"
 #include "chrome/browser/extensions/browsertest_util.h"
 #include "chrome/browser/predictors/loading_predictor_config.h"
@@ -134,7 +136,9 @@
       WindowOpenDisposition::NEW_WINDOW,
       apps::mojom::AppLaunchSource::kSourceTest);
   content::WebContents* contents =
-      apps::LaunchService::Get(profile())->OpenApplication(params);
+      apps::AppServiceProxyFactory::GetForProfile(profile())
+          ->BrowserAppLauncher()
+          .LaunchAppWithParams(params);
   url_observer.Wait();
   return contents;
 }
diff --git a/chrome/browser/ui/web_applications/web_app_file_handling_browsertest.cc b/chrome/browser/ui/web_applications/web_app_file_handling_browsertest.cc
index db77ce3..722ce6ca 100644
--- a/chrome/browser/ui/web_applications/web_app_file_handling_browsertest.cc
+++ b/chrome/browser/ui/web_applications/web_app_file_handling_browsertest.cc
@@ -10,7 +10,9 @@
 #include "base/test/scoped_feature_list.h"
 #include "base/threading/thread_restrictions.h"
 #include "chrome/browser/apps/app_service/app_launch_params.h"
-#include "chrome/browser/apps/launch_service/launch_service.h"
+#include "chrome/browser/apps/app_service/app_service_proxy.h"
+#include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
+#include "chrome/browser/apps/app_service/browser_app_launcher.h"
 #include "chrome/browser/ui/web_applications/web_app_controller_browsertest.h"
 #include "chrome/browser/web_applications/components/file_handler_manager.h"
 #include "chrome/browser/web_applications/components/web_app_prefs_utils.h"
@@ -150,7 +152,9 @@
   navigation_observer.StartWatchingNewWebContents();
 
   content::WebContents* web_contents =
-      apps::LaunchService::Get(profile)->OpenApplication(params);
+      apps::AppServiceProxyFactory::GetForProfile(profile)
+          ->BrowserAppLauncher()
+          .LaunchAppWithParams(params);
 
   navigation_observer.Wait();
 
diff --git a/chrome/browser/ui/web_applications/web_app_guest_session_browsertest_chromeos.cc b/chrome/browser/ui/web_applications/web_app_guest_session_browsertest_chromeos.cc
index 6177d719..61bcf03 100644
--- a/chrome/browser/ui/web_applications/web_app_guest_session_browsertest_chromeos.cc
+++ b/chrome/browser/ui/web_applications/web_app_guest_session_browsertest_chromeos.cc
@@ -3,7 +3,9 @@
 // found in the LICENSE file.
 
 #include "chrome/browser/apps/app_service/app_launch_params.h"
-#include "chrome/browser/apps/launch_service/launch_service.h"
+#include "chrome/browser/apps/app_service/app_service_proxy.h"
+#include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
+#include "chrome/browser/apps/app_service/browser_app_launcher.h"
 #include "chrome/browser/chromeos/extensions/default_web_app_ids.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/web_applications/system_web_app_manager.h"
@@ -46,7 +48,9 @@
       apps::mojom::AppLaunchSource::kSourceTest);
 
   content::WebContents* contents =
-      apps::LaunchService::Get(profile)->OpenApplication(params);
+      apps::AppServiceProxyFactory::GetForProfile(profile)
+          ->BrowserAppLauncher()
+          .LaunchAppWithParams(params);
   EXPECT_EQ(GURL(chrome::kChromeUIOSSettingsURL), contents->GetVisibleURL());
 }
 
diff --git a/chrome/browser/ui/webui/chromeos/crostini_upgrader/crostini_upgrader_dialog.cc b/chrome/browser/ui/webui/chromeos/crostini_upgrader/crostini_upgrader_dialog.cc
index e5848706..11110bd0 100644
--- a/chrome/browser/ui/webui/chromeos/crostini_upgrader/crostini_upgrader_dialog.cc
+++ b/chrome/browser/ui/webui/chromeos/crostini_upgrader/crostini_upgrader_dialog.cc
@@ -46,7 +46,11 @@
       only_run_launch_closure_on_restart_(only_run_launch_closure_on_restart),
       launch_closure_{std::move(launch_closure)} {}
 
-CrostiniUpgraderDialog::~CrostiniUpgraderDialog() = default;
+CrostiniUpgraderDialog::~CrostiniUpgraderDialog() {
+  if (deletion_closure_for_testing_) {
+    std::move(deletion_closure_for_testing_).Run();
+  }
+}
 
 void CrostiniUpgraderDialog::GetDialogSize(gfx::Size* size) const {
   size->SetSize(::kDialogWidth, ::kDialogHeight);
@@ -65,10 +69,19 @@
   params->z_order = ui::ZOrderLevel::kNormal;
 }
 
+void CrostiniUpgraderDialog::SetDeletionClosureForTesting(
+    base::OnceClosure deletion_closure_for_testing) {
+  deletion_closure_for_testing_ = std::move(deletion_closure_for_testing);
+}
+
 bool CrostiniUpgraderDialog::CanCloseDialog() const {
   // TODO(929571): If other WebUI Dialogs also need to let the WebUI control
   // closing logic, we should find a more general solution.
 
+  if (deletion_closure_for_testing_) {
+    // Running in a test.
+    return true;
+  }
   // Disallow closing without WebUI consent.
   return upgrader_ui_ == nullptr || upgrader_ui_->can_close();
 }
diff --git a/chrome/browser/ui/webui/chromeos/crostini_upgrader/crostini_upgrader_dialog.h b/chrome/browser/ui/webui/chromeos/crostini_upgrader/crostini_upgrader_dialog.h
index dc94be2..7f1a219 100644
--- a/chrome/browser/ui/webui/chromeos/crostini_upgrader/crostini_upgrader_dialog.h
+++ b/chrome/browser/ui/webui/chromeos/crostini_upgrader/crostini_upgrader_dialog.h
@@ -22,6 +22,9 @@
   static void Show(base::OnceClosure launch_closure,
                    bool only_run_launch_closure_on_restart = false);
 
+  void SetDeletionClosureForTesting(
+      base::OnceClosure deletion_closure_for_testing);
+
  private:
   explicit CrostiniUpgraderDialog(base::OnceClosure launch_closure,
                                   bool only_run_launch_closure_on_restart);
@@ -37,10 +40,10 @@
   void OnCloseContents(content::WebContents* source,
                        bool* out_close_dialog) override;
 
- private:
   CrostiniUpgraderUI* upgrader_ui_ = nullptr;  // Not owned.
   const bool only_run_launch_closure_on_restart_;
   base::OnceClosure launch_closure_;
+  base::OnceClosure deletion_closure_for_testing_;
 };
 
 }  // namespace chromeos
diff --git a/chrome/browser/ui/webui/chromeos/crostini_upgrader/crostini_upgrader_dialog_browsertest.cc b/chrome/browser/ui/webui/chromeos/crostini_upgrader/crostini_upgrader_dialog_browsertest.cc
new file mode 100644
index 0000000..d106d06
--- /dev/null
+++ b/chrome/browser/ui/webui/chromeos/crostini_upgrader/crostini_upgrader_dialog_browsertest.cc
@@ -0,0 +1,136 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/webui/chromeos/crostini_upgrader/crostini_upgrader_dialog.h"
+
+#include "base/feature_list.h"
+#include "base/metrics/histogram_base.h"
+#include "base/run_loop.h"
+#include "base/test/metrics/histogram_tester.h"
+#include "chrome/browser/chromeos/crostini/crostini_manager.h"
+#include "chrome/browser/chromeos/crostini/crostini_test_helper.h"
+#include "chrome/browser/chromeos/crostini/crostini_util.h"
+#include "chrome/browser/chromeos/guest_os/guest_os_registry_service.h"
+#include "chrome/browser/chromeos/guest_os/guest_os_registry_service_factory.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/views/crostini/crostini_browser_test_util.h"
+#include "chrome/browser/ui/webui/chromeos/crostini_upgrader/crostini_upgrader.mojom.h"
+#include "chrome/common/webui_url_constants.h"
+#include "chromeos/dbus/cicerone/cicerone_service.pb.h"
+#include "content/public/browser/web_ui.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+constexpr char kDesktopFileId[] = "test_app";
+constexpr int kDisplayId = 0;
+
+class CrostiniUpgraderDialogBrowserTest : public CrostiniDialogBrowserTest {
+ public:
+  CrostiniUpgraderDialogBrowserTest()
+      : CrostiniDialogBrowserTest(true /*register_termina*/),
+        app_id_(crostini::CrostiniTestHelper::GenerateAppId(kDesktopFileId)) {}
+
+  // DialogBrowserTest:
+  void ShowUi(const std::string& name) override {
+    chromeos::CrostiniUpgraderDialog::Show(base::DoNothing(), false);
+  }
+
+  chromeos::CrostiniUpgraderDialog* GetCrostiniUpgraderDialog() {
+    auto url = GURL{chrome::kChromeUICrostiniUpgraderUrl};
+    return reinterpret_cast<chromeos::CrostiniUpgraderDialog*>(
+        chromeos::SystemWebDialogDelegate::FindInstance(url.spec()));
+  }
+
+  void SafelyCloseDialog() {
+    auto* upgrader_dialog = GetCrostiniUpgraderDialog();
+
+    // Make sure the WebUI has launches sufficiently. Closing immediately would
+    // miss breakages in the underlying plumbing.
+    auto* web_contents = upgrader_dialog->GetWebUIForTest()->GetWebContents();
+    WaitForLoadFinished(web_contents);
+
+    // Now there should be enough WebUI hooked up to close properly.
+    base::RunLoop run_loop;
+    upgrader_dialog->SetDeletionClosureForTesting(run_loop.QuitClosure());
+    upgrader_dialog->Close();
+    run_loop.Run();
+  }
+
+  void ExpectDialog() {
+    // A new Widget was created in ShowUi() or since the last VerifyUi().
+    EXPECT_TRUE(crostini_manager()->GetCrostiniDialogStatus(
+        crostini::DialogType::UPGRADER));
+
+    EXPECT_NE(nullptr, GetCrostiniUpgraderDialog());
+  }
+
+  void ExpectNoDialog() {
+    // No new Widget was created in ShowUi() or since the last VerifyUi().
+    EXPECT_FALSE(crostini_manager()->GetCrostiniDialogStatus(
+        crostini::DialogType::UPGRADER));
+    // Our dialog has really been deleted.
+    EXPECT_EQ(nullptr, GetCrostiniUpgraderDialog());
+  }
+
+  void RegisterApp() {
+    vm_tools::apps::ApplicationList app_list =
+        crostini::CrostiniTestHelper::BasicAppList(
+            kDesktopFileId, crostini::kCrostiniDefaultVmName,
+            crostini::kCrostiniDefaultContainerName);
+    guest_os::GuestOsRegistryServiceFactory::GetForProfile(browser()->profile())
+        ->UpdateApplicationList(app_list);
+  }
+
+  void DowngradeOSRelease() {
+    vm_tools::cicerone::OsRelease os_release;
+    os_release.set_id("debian");
+    os_release.set_version_id("9");
+    auto container_id = crostini::DefaultContainerId();
+    crostini_manager()->SetContainerOsRelease(
+        container_id.vm_name, container_id.container_name, os_release);
+  }
+
+  const std::string& app_id() const { return app_id_; }
+
+  crostini::CrostiniManager* crostini_manager() {
+    return crostini::CrostiniManager::GetForProfile(browser()->profile());
+  }
+
+ private:
+  std::string app_id_;
+
+  DISALLOW_COPY_AND_ASSIGN(CrostiniUpgraderDialogBrowserTest);
+};
+
+IN_PROC_BROWSER_TEST_F(CrostiniUpgraderDialogBrowserTest,
+                       NoDialogOnNormalStartup) {
+  base::HistogramTester histogram_tester;
+  RegisterApp();
+
+  crostini::LaunchCrostiniApp(browser()->profile(), app_id(), kDisplayId);
+  ExpectNoDialog();
+}
+
+IN_PROC_BROWSER_TEST_F(CrostiniUpgraderDialogBrowserTest, ShowsOnAppLaunch) {
+  base::HistogramTester histogram_tester;
+
+  DowngradeOSRelease();
+  RegisterApp();
+
+  crostini::LaunchCrostiniApp(browser()->profile(), app_id(), kDisplayId);
+  ExpectDialog();
+
+  SafelyCloseDialog();
+  ExpectNoDialog();
+
+  // Once only - second time we launch an app, the dialog should not appear.
+  crostini::LaunchCrostiniApp(browser()->profile(), app_id(), kDisplayId);
+  ExpectNoDialog();
+
+  histogram_tester.ExpectUniqueSample(
+      crostini::kUpgradeDialogEventHistogram,
+      static_cast<base::HistogramBase::Sample>(
+          crostini::UpgradeDialogEvent::kDialogShown),
+      1);
+}
diff --git a/chrome/browser/ui/webui/chromeos/login/demo_setup_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/demo_setup_screen_handler.cc
index 40c86e9..8dd97791 100644
--- a/chrome/browser/ui/webui/chromeos/login/demo_setup_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/demo_setup_screen_handler.cc
@@ -52,8 +52,10 @@
              DemoSetupController::DemoSetupError::RecoveryMethod::kPowerwash);
 }
 
-void DemoSetupScreenHandler::IncrementSetupProgress(bool complete) {
-  CallJS("login.DemoSetupScreen.incrementSetupProgress", complete);
+void DemoSetupScreenHandler::SetCurrentSetupStep(
+    DemoSetupController::DemoSetupStep current_step) {
+  CallJS("login.DemoSetupScreen.setCurrentSetupStep",
+         DemoSetupController::GetDemoSetupStepString(current_step));
 }
 
 void DemoSetupScreenHandler::OnSetupSucceeded() {
@@ -72,13 +74,21 @@
                IDS_OOBE_DEMO_SETUP_ERROR_SCREEN_RETRY_BUTTON_LABEL);
   builder->Add("demoSetupErrorScreenPowerwashButtonLabel",
                IDS_LOCAL_STATE_ERROR_POWERWASH_BUTTON);
+
+  builder->Add("demoSetupProgressStepDownload",
+               IDS_OOBE_DEMO_SETUP_PROGRESS_STEP_DOWNLOAD);
+  builder->Add("demoSetupProgressStepEnroll",
+               IDS_OOBE_DEMO_SETUP_PROGRESS_STEP_ENROLL);
 }
 
 void DemoSetupScreenHandler::GetAdditionalParameters(
     base::DictionaryValue* parameters) {
   parameters->SetBoolKey(
-      "showProgressBarInDemoModeSetup",
-      base::FeatureList::IsEnabled(features::kShowProgressBarInDemoModeSetup));
+      "showStepsInDemoModeSetup",
+      base::FeatureList::IsEnabled(features::kShowStepsInDemoModeSetup));
+
+  parameters->SetPath("demoSetupSteps",
+                      DemoSetupController::GetDemoSetupSteps());
 }
 
 }  // namespace chromeos
diff --git a/chrome/browser/ui/webui/chromeos/login/demo_setup_screen_handler.h b/chrome/browser/ui/webui/chromeos/login/demo_setup_screen_handler.h
index 732b33d6..e21cadb 100644
--- a/chrome/browser/ui/webui/chromeos/login/demo_setup_screen_handler.h
+++ b/chrome/browser/ui/webui/chromeos/login/demo_setup_screen_handler.h
@@ -28,8 +28,9 @@
   // Sets view and screen.
   virtual void Bind(DemoSetupScreen* screen) = 0;
 
-  // Increments setup progress percentage for UI.
-  virtual void IncrementSetupProgress(bool complete) = 0;
+  // Updates current setup step.
+  virtual void SetCurrentSetupStep(
+      DemoSetupController::DemoSetupStep current_step) = 0;
 
   // Handles successful setup.
   virtual void OnSetupSucceeded() = 0;
@@ -53,7 +54,8 @@
   void Show() override;
   void Hide() override;
   void Bind(DemoSetupScreen* screen) override;
-  void IncrementSetupProgress(bool complete) override;
+  void SetCurrentSetupStep(
+      DemoSetupController::DemoSetupStep current_step) override;
   void OnSetupFailed(const DemoSetupController::DemoSetupError& error) override;
   void OnSetupSucceeded() override;
 
diff --git a/chrome/browser/ui/webui/ntp/app_launcher_handler.cc b/chrome/browser/ui/webui/ntp/app_launcher_handler.cc
index 93cb95d..d2c6d2ab 100644
--- a/chrome/browser/ui/webui/ntp/app_launcher_handler.cc
+++ b/chrome/browser/ui/webui/ntp/app_launcher_handler.cc
@@ -20,7 +20,10 @@
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/values.h"
-#include "chrome/browser/apps/launch_service/launch_service.h"
+#include "chrome/browser/apps/app_service/app_launch_params.h"
+#include "chrome/browser/apps/app_service/app_service_proxy.h"
+#include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
+#include "chrome/browser/apps/app_service/browser_app_launcher.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/extensions/crx_installer.h"
@@ -535,7 +538,9 @@
             : apps::mojom::LaunchContainer::kLaunchContainerTab,
         disposition, apps::mojom::AppLaunchSource::kSourceNewTabPage);
     params.override_url = override_url;
-    apps::LaunchService::Get(profile)->OpenApplication(params);
+    apps::AppServiceProxyFactory::GetForProfile(profile)
+        ->BrowserAppLauncher()
+        .LaunchAppWithParams(params);
   } else {
     // To give a more "launchy" experience when using the NTP launcher, we close
     // it automatically.
@@ -552,7 +557,9 @@
         extensions::AppLaunchSource::kSourceNewTabPage);
     params.override_url = override_url;
     WebContents* new_contents =
-        apps::LaunchService::Get(profile)->OpenApplication(params);
+        apps::AppServiceProxyFactory::GetForProfile(profile)
+            ->BrowserAppLauncher()
+            .LaunchAppWithParams(params);
 
     // This will also destroy the handler, so do not perform any actions after.
     if (new_contents != old_contents && browser &&
diff --git a/chrome/browser/ui/webui/settings/chromeos/os_settings_localized_strings_provider.cc b/chrome/browser/ui/webui/settings/chromeos/os_settings_localized_strings_provider.cc
index d9796ed..c7abe54 100644
--- a/chrome/browser/ui/webui/settings/chromeos/os_settings_localized_strings_provider.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/os_settings_localized_strings_provider.cc
@@ -31,6 +31,8 @@
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
 #include "chrome/browser/policy/profile_policy_connector.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/supervised_user/supervised_user_service.h"
+#include "chrome/browser/supervised_user/supervised_user_service_factory.h"
 #include "chrome/browser/ui/webui/chromeos/assistant_optin/assistant_optin_utils.h"
 #include "chrome/browser/ui/webui/chromeos/bluetooth_dialog_localized_strings_provider.h"
 #include "chrome/browser/ui/webui/chromeos/network_element_localized_strings_provider.h"
@@ -796,8 +798,12 @@
       {"pluginVmRemoveButton", IDS_SETTINGS_PLUGIN_VM_REMOVE_BUTTON},
       {"pluginVmRemoveConfirmationDialogMessage",
        IDS_SETTINGS_PLUGIN_VM_CONFIRM_REMOVE_DIALOG_BODY},
+      {"pluginVmCameraAccessTitle", IDS_SETTINGS_PLUGIN_VM_CAMERA_ACCESS_TITLE},
   };
   AddLocalizedStringsBulk(html_source, kLocalizedStrings);
+  html_source->AddBoolean("showPluginVmCamera",
+                          base::FeatureList::IsEnabled(
+                              chromeos::features::kPluginVmShowCameraSetting));
 }
 
 void AddAndroidAppStrings(content::WebUIDataSource* html_source) {
@@ -857,7 +863,8 @@
   AddLocalizedStringsBulk(html_source, kLocalizedStrings);
 }
 
-void AddParentalControlStrings(content::WebUIDataSource* html_source) {
+void AddParentalControlStrings(content::WebUIDataSource* html_source,
+                               Profile* profile) {
   static constexpr webui::LocalizedString kLocalizedStrings[] = {
       {"parentalControlsPageTitle", IDS_SETTINGS_PARENTAL_CONTROLS_PAGE_TITLE},
       {"parentalControlsPageSetUpLabel",
@@ -875,6 +882,27 @@
 
   html_source->AddBoolean(
       "isChild", user_manager::UserManager::Get()->IsLoggedInAsChildUser());
+
+  if (user_manager::UserManager::Get()->IsLoggedInAsChildUser()) {
+    SupervisedUserService* supervised_user_service =
+        SupervisedUserServiceFactory::GetForProfile(profile);
+    std::string custodian = supervised_user_service->GetCustodianName();
+    std::string second_custodian =
+        supervised_user_service->GetSecondCustodianName();
+
+    base::string16 child_managed_tooltip;
+    if (second_custodian.empty()) {
+      child_managed_tooltip = l10n_util::GetStringFUTF16(
+          IDS_SETTINGS_ACCOUNT_MANAGER_CHILD_MANAGED_BY_ONE_PARENT_TOOLTIP,
+          base::UTF8ToUTF16(custodian));
+    } else {
+      child_managed_tooltip = l10n_util::GetStringFUTF16(
+          IDS_SETTINGS_ACCOUNT_MANAGER_CHILD_MANAGED_BY_TWO_PARENTS_TOOLTIP,
+          base::UTF8ToUTF16(custodian), base::UTF8ToUTF16(second_custodian));
+    }
+    html_source->AddString("accountManagerPrimaryAccountChildManagedTooltip",
+                           child_managed_tooltip);
+  }
 }
 
 void AddBluetoothStrings(content::WebUIDataSource* html_source) {
@@ -2047,7 +2075,7 @@
   AddGoogleAssistantStrings(html_source, profile);
   AddLanguagesStrings(html_source);
   AddMultideviceStrings(html_source);
-  AddParentalControlStrings(html_source);
+  AddParentalControlStrings(html_source, profile);
   AddPageVisibilityStrings(html_source);
   AddPeoplePageStrings(html_source, profile);
   AddPersonalizationStrings(html_source);
diff --git a/chrome/browser/web_applications/extensions/install_manager_bookmark_app_unittest.cc b/chrome/browser/web_applications/extensions/install_manager_bookmark_app_unittest.cc
index b0449d5f..b2d26069 100644
--- a/chrome/browser/web_applications/extensions/install_manager_bookmark_app_unittest.cc
+++ b/chrome/browser/web_applications/extensions/install_manager_bookmark_app_unittest.cc
@@ -643,6 +643,8 @@
 
   url_loader().SetNextLoadUrlResult(
       GURL("about:blank"), web_app::WebAppUrlLoader::Result::kUrlLoaded);
+  url_loader().SetNextLoadUrlResult(
+      AppUrl(), web_app::WebAppUrlLoader::Result::kRedirectedUrlLoaded);
 
   {
     base::RunLoop run_loop;
diff --git a/chrome/browser/web_applications/system_web_app_manager_browsertest.cc b/chrome/browser/web_applications/system_web_app_manager_browsertest.cc
index 53cf771..6ff16f0 100644
--- a/chrome/browser/web_applications/system_web_app_manager_browsertest.cc
+++ b/chrome/browser/web_applications/system_web_app_manager_browsertest.cc
@@ -10,13 +10,15 @@
 #include <vector>
 
 #include "base/bind.h"
+#include "base/files/file_path.h"
 #include "base/files/file_util.h"
+#include "base/path_service.h"
 #include "base/run_loop.h"
 #include "base/test/scoped_feature_list.h"
 #include "chrome/browser/apps/app_service/app_launch_params.h"
 #include "chrome/browser/apps/app_service/app_service_proxy.h"
 #include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
-#include "chrome/browser/apps/launch_service/launch_service.h"
+#include "chrome/browser/apps/app_service/browser_app_launcher.h"
 #include "chrome/browser/native_file_system/native_file_system_permission_request_manager.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser_finder.h"
@@ -26,6 +28,7 @@
 #include "chrome/browser/web_applications/test/test_web_app_provider.h"
 #include "chrome/browser/web_applications/web_app_tab_helper.h"
 #include "chrome/common/chrome_features.h"
+#include "chrome/common/chrome_paths.h"
 #include "chrome/common/webui_url_constants.h"
 #include "chrome/services/app_service/public/cpp/app_registry_cache.h"
 #include "chrome/services/app_service/public/cpp/app_update.h"
@@ -120,11 +123,9 @@
 
 content::WebContents* SystemWebAppManagerBrowserTestBase::LaunchApp(
     const apps::AppLaunchParams& params) {
-  // Use apps::LaunchService::OpenApplication() to get the most coverage. E.g.,
-  // this is what is invoked by file_manager::file_tasks::ExecuteWebTask() on
-  // ChromeOS.
-  return apps::LaunchService::Get(browser()->profile())
-      ->OpenApplication(params);
+  return apps::AppServiceProxyFactory::GetForProfile(browser()->profile())
+      ->BrowserAppLauncher()
+      .LaunchAppWithParams(params);
 }
 
 SystemWebAppManagerBrowserTest::SystemWebAppManagerBrowserTest(
@@ -272,7 +273,9 @@
   content::TestNavigationObserver navigation_observer(launch_url);
   navigation_observer.StartWatchingNewWebContents();
   content::WebContents* web_contents =
-      apps::LaunchService::Get(browser()->profile())->OpenApplication(params);
+      apps::AppServiceProxyFactory::GetForProfile(browser()->profile())
+          ->BrowserAppLauncher()
+          .LaunchAppWithParams(params);
   navigation_observer.Wait();
 
   // Set up a Promise that resolves to launchParams, when launchQueue's consumer
@@ -309,7 +312,9 @@
                                              &temp_file_path2));
   params.launch_files = {temp_file_path2};
   content::WebContents* web_contents2 =
-      apps::LaunchService::Get(browser()->profile())->OpenApplication(params);
+      apps::AppServiceProxyFactory::GetForProfile(browser()->profile())
+          ->BrowserAppLauncher()
+          .LaunchAppWithParams(params);
 
   // WebContents* should be the same because we are passing launchParams to the
   // opened application.
@@ -331,6 +336,165 @@
   SystemWebAppManagerLaunchDirectoryBrowserTest()
       : SystemWebAppManagerFileHandlingBrowserTestBase(
             IncludeLaunchDirectory::kYes) {}
+
+  // Returns the content of |jsIdentifier| file handle.
+  std::string ReadContentFromJsFileHandle(content::WebContents* web_contents,
+                                          const std::string& jsIdentifier) {
+    std::string js_file_content;
+    EXPECT_TRUE(content::ExecuteScriptAndExtractString(
+        web_contents,
+        "Promise.resolve(" + jsIdentifier + ")" +
+            ".then(async (identifier) => {"
+            "  const file = await identifier.getFile();"
+            "  const content = await file.text();"
+            "  window.domAutomationController.send(content);"
+            "});",
+        &js_file_content));
+    return js_file_content;
+  }
+
+  // Writes |contentToWrite| to |jsIdentifier| file handle. Returns whether
+  // JavaScript execution finishes.
+  bool WriteContentToJsFileHandle(content::WebContents* web_contents,
+                                  const std::string& jsIdentifier,
+                                  const std::string& contentToWrite) {
+    bool file_written;
+    EXPECT_TRUE(content::ExecuteScriptAndExtractBool(
+        web_contents,
+        content::JsReplace(
+            "Promise.resolve(" + jsIdentifier + ")" +
+                ".then(async (file_handle) => {"
+                "  const writable = await file_handle.createWritable();"
+                "  await writable.write($1);"
+                "  await writable.close();"
+                "  window.domAutomationController.send(true);"
+                "});",
+            contentToWrite),
+        &file_written));
+    return file_written;
+  }
+
+  // Remove file by |file_name| from |jsIdentifier| directory handle. Returns
+  // whether JavaScript execution finishes.
+  bool RemoveFileFromJsDirectoryHandle(content::WebContents* web_contents,
+                                       const std::string& jsIdentifier,
+                                       const std::string& file_name) {
+    bool file_removed;
+    EXPECT_TRUE(content::ExecuteScriptAndExtractBool(
+        web_contents,
+        content::JsReplace("Promise.resolve(" + jsIdentifier + ")" +
+                               ".then(async (dir_handle) => {"
+                               "  await dir_handle.removeEntry($1);"
+                               "  domAutomationController.send(true);"
+                               "});",
+                           file_name),
+        &file_removed));
+    return file_removed;
+  }
+
+  std::string ReadFileContent(const base::FilePath& path) {
+    std::string content;
+    EXPECT_TRUE(base::ReadFileToString(path, &content));
+    return content;
+  }
+
+  // Launch the App with |base_dir| and a file inside this directory, then test
+  // SWA can 1) read and write to the launch file; 2) read and write to other
+  // files inside the launch directory; 3) read and write to the launch
+  // directory (i.e. list and delete files).
+  void TestPermissionsForLaunchDirectory(const base::FilePath& base_dir) {
+    apps::AppLaunchParams params = LaunchParamsForApp(GetMockAppType());
+    params.source = apps::mojom::AppLaunchSource::kSourceChromeInternal;
+
+    base::ScopedAllowBlockingForTesting allow_blocking;
+
+    // Create the launch file, which stores 4 characters "test".
+    base::FilePath launch_file_path;
+    ASSERT_TRUE(base::CreateTemporaryFileInDir(base_dir, &launch_file_path));
+    ASSERT_EQ(4, base::WriteFile(launch_file_path, "test", 4));
+
+    // Launch the App.
+    const GURL& launch_url = WebAppProvider::Get(browser()->profile())
+                                 ->registrar()
+                                 .GetAppLaunchURL(params.app_id);
+    params.launch_files = {launch_file_path};
+    content::TestNavigationObserver navigation_observer(launch_url);
+    navigation_observer.StartWatchingNewWebContents();
+    content::WebContents* web_contents =
+        apps::AppServiceProxyFactory::GetForProfile(browser()->profile())
+            ->BrowserAppLauncher()
+            .LaunchAppWithParams(params);
+    navigation_observer.Wait();
+
+    // Launch directories and files passed to system web apps should
+    // automatically be granted write permission. Users should not get
+    // permission prompts. So we auto deny them (if they show up).
+    NativeFileSystemPermissionRequestManager::FromWebContents(web_contents)
+        ->set_auto_response_for_test(permissions::PermissionAction::DENIED);
+
+    // Set up a Promise to resolves to launchParams, when launchQueue's consumer
+    // callback is called.
+    EXPECT_TRUE(content::ExecuteScript(
+        web_contents,
+        "window.launchParamsPromise = new Promise(resolve => {"
+        "  window.resolveLaunchParamsPromise = resolve;"
+        "});"
+        "launchQueue.setConsumer(launchParams => {"
+        "  window.resolveLaunchParamsPromise(launchParams);"
+        "});"));
+
+    // Wait for launch. Set window.launchParams for manipulation.
+    EXPECT_TRUE(content::ExecuteScript(
+        web_contents,
+        "window.launchParamsPromise.then(launchParams => {"
+        "  window.launchParams = launchParams;"
+        "});"));
+
+    // Check we can read and write to the launch file.
+    std::string launch_file_js_handle = "window.launchParams.files[1]";
+    EXPECT_EQ("test",
+              ReadContentFromJsFileHandle(web_contents, launch_file_js_handle));
+    EXPECT_TRUE(WriteContentToJsFileHandle(web_contents, launch_file_js_handle,
+                                           "js_written"));
+    EXPECT_EQ("js_written", ReadFileContent(launch_file_path));
+
+    // Check we can read and write to a different file inside the directory.
+    // Note, this also checks we can read the launch directory, using
+    // directory_handle.getFile().
+    base::FilePath non_launch_file_path;
+    ASSERT_TRUE(
+        base::CreateTemporaryFileInDir(base_dir, &non_launch_file_path));
+    ASSERT_EQ(5, base::WriteFile(non_launch_file_path, "test2", 5));
+
+    std::string non_launch_file_js_handle =
+        content::JsReplace("window.launchParams.files[0].getFile($1)",
+                           non_launch_file_path.BaseName().AsUTF8Unsafe());
+    EXPECT_EQ("test2", ReadContentFromJsFileHandle(web_contents,
+                                                   non_launch_file_js_handle));
+    EXPECT_TRUE(WriteContentToJsFileHandle(
+        web_contents, non_launch_file_js_handle, "js_written2"));
+    EXPECT_EQ("js_written2", ReadFileContent(non_launch_file_path));
+
+    // Check the launch file can be deleted.
+    std::string launch_dir_js_handle = "window.launchParams.files[0]";
+    EXPECT_TRUE(RemoveFileFromJsDirectoryHandle(
+        web_contents, launch_dir_js_handle,
+        launch_file_path.BaseName().AsUTF8Unsafe()));
+    EXPECT_FALSE(base::PathExists(launch_file_path));
+
+    // Check the non-launch file can be deleted.
+    EXPECT_TRUE(RemoveFileFromJsDirectoryHandle(
+        web_contents, launch_dir_js_handle,
+        non_launch_file_path.BaseName().AsUTF8Unsafe()));
+    EXPECT_FALSE(base::PathExists(non_launch_file_path));
+
+    // Check a file can be created.
+    std::string new_file_js_handle = content::JsReplace(
+        "window.launchParams.files[0].getFile($1, {create:true})", "new_file");
+    EXPECT_TRUE(WriteContentToJsFileHandle(web_contents, new_file_js_handle,
+                                           "js_new_file"));
+    EXPECT_EQ("js_new_file", ReadFileContent(base_dir.AppendASCII("new_file")));
+  }
 };
 
 // Launching behavior for apps that do not want to received launch directory are
@@ -359,7 +523,9 @@
   content::TestNavigationObserver navigation_observer(launch_url);
   navigation_observer.StartWatchingNewWebContents();
   content::WebContents* web_contents =
-      apps::LaunchService::Get(browser()->profile())->OpenApplication(params);
+      apps::AppServiceProxyFactory::GetForProfile(browser()->profile())
+          ->BrowserAppLauncher()
+          .LaunchAppWithParams(params);
   navigation_observer.Wait();
 
   // Set up a Promise that resolves to launchParams, when launchQueue's consumer
@@ -423,7 +589,9 @@
                                              &temp_file_path2));
   params.launch_files = {temp_file_path2};
   content::WebContents* web_contents2 =
-      apps::LaunchService::Get(browser()->profile())->OpenApplication(params);
+      apps::AppServiceProxyFactory::GetForProfile(browser()->profile())
+          ->BrowserAppLauncher()
+          .LaunchAppWithParams(params);
 
   // WebContents* should be the same because we are passing launchParams to the
   // opened application.
@@ -462,45 +630,33 @@
       "domAutomationController.send(window.secondLaunchParams.files[1].name)",
       &file_name));
   EXPECT_EQ(temp_file_path2.BaseName().AsUTF8Unsafe(), file_name);
+}
 
-  // Launch directories and files passed to system web apps should automatically
-  // be granted write permission. Users should not get permission prompts. Here
-  // we execute some JavaScript code that modifies and deletes files in the
-  // directory.
+IN_PROC_BROWSER_TEST_P(SystemWebAppManagerLaunchDirectoryBrowserTest,
+                       ReadWritePermissions_OrdinaryDirectory) {
+  WaitForTestSystemAppInstall();
 
-  // Auto deny prompts (if they show up).
-  NativeFileSystemPermissionRequestManager::FromWebContents(web_contents)
-      ->set_auto_response_for_test(permissions::PermissionAction::DENIED);
+  // Test for ordinary directory.
+  base::ScopedAllowBlockingForTesting allow_blocking;
+  base::ScopedTempDir temp_directory;
+  ASSERT_TRUE(temp_directory.CreateUniqueTempDir());
+  TestPermissionsForLaunchDirectory(temp_directory.GetPath());
+}
 
-  // Modifies the launch file. Reuse the first launch directory.
-  bool writer_closed;
-  EXPECT_TRUE(content::ExecuteScriptAndExtractBool(
-      web_contents,
-      "window.firstLaunchParams.files[1].createWritable().then("
-      "  async writer => {"
-      "    console.log(writer);"
-      "    await writer.write('test');"
-      "    await writer.close();"
-      "    domAutomationController.send(true);"
-      "  }"
-      ");",
-      &writer_closed));
-  EXPECT_TRUE(writer_closed);
+IN_PROC_BROWSER_TEST_P(SystemWebAppManagerLaunchDirectoryBrowserTest,
+                       ReadWritePermissions_SensitiveDirectory) {
+  WaitForTestSystemAppInstall();
 
-  std::string read_contents;
-  EXPECT_TRUE(base::ReadFileToString(temp_file_path, &read_contents));
-  EXPECT_EQ("test", read_contents);
-
-  // Deletes the launch file. Reuse the second launch directory.
-  bool file_removed;
-  EXPECT_TRUE(content::ExecuteScriptAndExtractBool(
-      web_contents,
-      "window.secondLaunchParams.files[0].removeEntry("
-      "  window.secondLaunchParams.files[1].name"
-      ").then(_ => domAutomationController.send(true));",
-      &file_removed));
-  EXPECT_TRUE(file_removed);
-  EXPECT_FALSE(base::PathExists(temp_file_path2));
+  // Test for sensitive directory (which are otherwise blocked by
+  // NativeFileSystem API). It is safe to use |chrome::DIR_DEFAULT_DOWNLOADS|,
+  // because InProcBrowserTest fixture sets up different download directory for
+  // each test cases.
+  base::ScopedAllowBlockingForTesting allow_blocking;
+  base::FilePath sensitive_dir;
+  ASSERT_TRUE(
+      base::PathService::Get(chrome::DIR_DEFAULT_DOWNLOADS, &sensitive_dir));
+  ASSERT_TRUE(base::DirectoryExists(sensitive_dir));
+  TestPermissionsForLaunchDirectory(sensitive_dir);
 }
 
 class SystemWebAppManagerFileHandlingOriginTrialsBrowserTest
@@ -541,7 +697,9 @@
   content::TestNavigationObserver navigation_observer(launch_url);
   navigation_observer.StartWatchingNewWebContents();
   content::WebContents* web_contents =
-      apps::LaunchService::Get(browser()->profile())->OpenApplication(params);
+      apps::AppServiceProxyFactory::GetForProfile(browser()->profile())
+          ->BrowserAppLauncher()
+          .LaunchAppWithParams(params);
   navigation_observer.Wait();
 
   // Wait for the Promise to resolve.
diff --git a/chrome/browser/web_applications/web_app_icon_manager_browsertest.cc b/chrome/browser/web_applications/web_app_icon_manager_browsertest.cc
index 941a799f..c8f9b4b9 100644
--- a/chrome/browser/web_applications/web_app_icon_manager_browsertest.cc
+++ b/chrome/browser/web_applications/web_app_icon_manager_browsertest.cc
@@ -6,7 +6,9 @@
 #include "base/test/bind_test_util.h"
 #include "base/test/scoped_feature_list.h"
 #include "chrome/browser/apps/app_service/app_launch_params.h"
-#include "chrome/browser/apps/launch_service/launch_service.h"
+#include "chrome/browser/apps/app_service/app_service_proxy.h"
+#include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
+#include "chrome/browser/apps/app_service/browser_app_launcher.h"
 #include "chrome/browser/installable/installable_metrics.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_finder.h"
@@ -100,7 +102,9 @@
         WindowOpenDisposition::NEW_WINDOW,
         apps::mojom::AppLaunchSource::kSourceTest);
     content::WebContents* contents =
-        apps::LaunchService::Get(browser()->profile())->OpenApplication(params);
+        apps::AppServiceProxyFactory::GetForProfile(browser()->profile())
+            ->BrowserAppLauncher()
+            .LaunchAppWithParams(params);
     controller = chrome::FindBrowserWithWebContents(contents)
                      ->app_controller()
                      ->AsWebAppBrowserController();
diff --git a/chrome/browser/web_applications/web_app_install_manager.cc b/chrome/browser/web_applications/web_app_install_manager.cc
index a5e39f8..3b46395 100644
--- a/chrome/browser/web_applications/web_app_install_manager.cc
+++ b/chrome/browser/web_applications/web_app_install_manager.cc
@@ -147,10 +147,10 @@
       finalizer(), data_retriever_factory_.Run());
 
   base::OnceClosure start_task = base::BindOnce(
-      &WebAppInstallTask::InstallWebAppFromInfoRetrieveIcons,
+      &WebAppInstallTask::LoadAndInstallWebAppFromSync,
       base::Unretained(task.get()), EnsureWebContentsCreated(),
-      std::move(web_application_info), is_locally_installed,
-      WebappInstallSource::SYNC,
+      base::Unretained(url_loader_.get()), std::move(web_application_info),
+      is_locally_installed, WebappInstallSource::SYNC,
       base::BindOnce(&WebAppInstallManager::OnQueuedTaskCompleted,
                      base::Unretained(this), task.get(), std::move(callback)));
 
diff --git a/chrome/browser/web_applications/web_app_install_task.cc b/chrome/browser/web_applications/web_app_install_task.cc
index 6f895358..303073f 100644
--- a/chrome/browser/web_applications/web_app_install_task.cc
+++ b/chrome/browser/web_applications/web_app_install_task.cc
@@ -228,8 +228,9 @@
                      base::Unretained(this), /*force_shortcut_app=*/false));
 }
 
-void WebAppInstallTask::InstallWebAppFromInfoRetrieveIcons(
+void WebAppInstallTask::LoadAndInstallWebAppFromSync(
     content::WebContents* web_contents,
+    WebAppUrlLoader* url_loader,
     std::unique_ptr<WebApplicationInfo> web_application_info,
     bool is_locally_installed,
     WebappInstallSource install_source,
@@ -241,18 +242,17 @@
   install_source_ = install_source;
   background_installation_ = true;
 
-  std::vector<GURL> icon_urls =
-      GetValidIconUrlsToDownload(*web_application_info);
+  // Default display mode, used if we fail to load the manifest.
+  web_application_info->display_mode = DisplayMode::kBrowser;
 
-  // Skip downloading the page favicons as everything in is the URL list.
-  data_retriever_->GetIcons(
-      web_contents, icon_urls, /*skip_page_fav_icons*/ true,
-      install_source_ == WebappInstallSource::SYNC
-          ? WebAppIconDownloader::Histogram::kForSync
-          : WebAppIconDownloader::Histogram::kForCreate,
-      base::BindOnce(&WebAppInstallTask::OnIconsRetrieved,
-                     base::Unretained(this), std::move(web_application_info),
-                     is_locally_installed));
+  const GURL launch_url = web_application_info->app_url;
+  url_loader->LoadUrl(
+      launch_url, web_contents,
+      WebAppUrlLoader::UrlComparison::kIgnoreQueryParamsAndRef,
+      base::BindOnce(
+          &WebAppInstallTask::OnWebAppFromSyncUrlLoadedRetrieveManifest,
+          weak_ptr_factory_.GetWeakPtr(), std::move(web_application_info),
+          is_locally_installed));
 }
 
 void WebAppInstallTask::UpdateWebAppFromInfo(
@@ -616,6 +616,59 @@
                      for_installable_site));
 }
 
+void WebAppInstallTask::OnWebAppFromSyncUrlLoadedRetrieveManifest(
+    std::unique_ptr<WebApplicationInfo> web_application_info,
+    bool is_locally_installed,
+    WebAppUrlLoader::Result result) {
+  if (ShouldStopInstall())
+    return;
+
+  if (result != WebAppUrlLoader::Result::kUrlLoaded) {
+    WebAppFromSyncLoadIcons(std::move(web_application_info),
+                            is_locally_installed);
+    return;
+  }
+
+  web_contents()->GetManifest(
+      base::BindOnce(&WebAppInstallTask::OnWebAppFromSyncManifestRetrieved,
+                     weak_ptr_factory_.GetWeakPtr(),
+                     std::move(web_application_info), is_locally_installed));
+}
+
+void WebAppInstallTask::OnWebAppFromSyncManifestRetrieved(
+    std::unique_ptr<WebApplicationInfo> web_application_info,
+    bool is_locally_installed,
+    const GURL& manifest_url,
+    const blink::Manifest& manifest) {
+  if (ShouldStopInstall())
+    return;
+
+  if (!manifest.IsEmpty()) {
+    // TODO(crbug.com/1067519): Copy more fields from the manifest.
+    web_application_info->display_mode = manifest.display;
+  }
+
+  WebAppFromSyncLoadIcons(std::move(web_application_info),
+                          is_locally_installed);
+}
+
+void WebAppInstallTask::WebAppFromSyncLoadIcons(
+    std::unique_ptr<WebApplicationInfo> web_application_info,
+    bool is_locally_installed) {
+  std::vector<GURL> icon_urls =
+      GetValidIconUrlsToDownload(*web_application_info);
+
+  // Skip downloading the page favicons as everything in is the URL list.
+  data_retriever_->GetIcons(
+      web_contents(), icon_urls, /*skip_page_fav_icons*/ true,
+      install_source_ == WebappInstallSource::SYNC
+          ? WebAppIconDownloader::Histogram::kForSync
+          : WebAppIconDownloader::Histogram::kForCreate,
+      base::BindOnce(&WebAppInstallTask::OnIconsRetrieved,
+                     base::Unretained(this), std::move(web_application_info),
+                     is_locally_installed));
+}
+
 void WebAppInstallTask::OnIconsRetrieved(
     std::unique_ptr<WebApplicationInfo> web_app_info,
     bool is_locally_installed,
diff --git a/chrome/browser/web_applications/web_app_install_task.h b/chrome/browser/web_applications/web_app_install_task.h
index 7d48f61..f3ce279 100644
--- a/chrome/browser/web_applications/web_app_install_task.h
+++ b/chrome/browser/web_applications/web_app_install_task.h
@@ -118,10 +118,13 @@
       InstallManager::OnceInstallCallback callback);
 
   // Starts background installation of a web app: does not show UI dialog.
-  // |web_application_info| contains all the data needed for installation. Icons
-  // will be downloaded from the icon URLs provided in |web_application_info|.
-  void InstallWebAppFromInfoRetrieveIcons(
+  // |web_application_info| contains most of the data needed for installation.
+  // The launch URL and its manifest are loaded to determine the display mode.
+  // Icons will be downloaded from the icon URLs provided in
+  // |web_application_info|. Doesn't memorize |url_loader| pointer.
+  void LoadAndInstallWebAppFromSync(
       content::WebContents* web_contents,
+      WebAppUrlLoader* url_loader,
       std::unique_ptr<WebApplicationInfo> web_application_info,
       bool is_locally_installed,
       WebappInstallSource install_source,
@@ -204,6 +207,19 @@
       const std::string& intent,
       bool should_intent_to_store);
 
+  void OnWebAppFromSyncUrlLoadedRetrieveManifest(
+      std::unique_ptr<WebApplicationInfo> web_application_info,
+      bool is_locally_installed,
+      WebAppUrlLoader::Result result);
+  void OnWebAppFromSyncManifestRetrieved(
+      std::unique_ptr<WebApplicationInfo> web_application_info,
+      bool is_locally_installed,
+      const GURL& manifest_url,
+      const blink::Manifest& manifest);
+  void WebAppFromSyncLoadIcons(
+      std::unique_ptr<WebApplicationInfo> web_application_info,
+      bool is_locally_installed);
+
   void OnIconsRetrieved(std::unique_ptr<WebApplicationInfo> web_app_info,
                         bool is_locally_installed,
                         IconsMap icons_map);
diff --git a/chrome/browser/web_applications/web_app_install_task_unittest.cc b/chrome/browser/web_applications/web_app_install_task_unittest.cc
index 4fdeb2ea..0cb08280 100644
--- a/chrome/browser/web_applications/web_app_install_task_unittest.cc
+++ b/chrome/browser/web_applications/web_app_install_task_unittest.cc
@@ -333,16 +333,16 @@
     return app_id;
   }
 
-  AppId InstallWebAppFromInfoRetrieveIcons(const GURL& url,
-                                           bool is_locally_installed) {
+  AppId LoadAndInstallWebAppFromSync(const GURL& url,
+                                     bool is_locally_installed) {
     AppId app_id;
     auto web_app_info = std::make_unique<WebApplicationInfo>();
     web_app_info->app_url = url;
 
     base::RunLoop run_loop;
-    install_task_->InstallWebAppFromInfoRetrieveIcons(
-        web_contents(), std::move(web_app_info), is_locally_installed,
-        WebappInstallSource::SYNC,
+    install_task_->LoadAndInstallWebAppFromSync(
+        web_contents(), &url_loader(), std::move(web_app_info),
+        is_locally_installed, WebappInstallSource::SYNC,
         base::BindLambdaForTesting(
             [&](const AppId& installed_app_id, InstallResultCode code) {
               ASSERT_EQ(InstallResultCode::kSuccessNewInstall, code);
@@ -908,12 +908,14 @@
   run_loop.Run();
 }
 
-TEST_F(WebAppInstallTaskTest, InstallWebAppFromInfoRetrieveIcons_TwoIcons) {
+TEST_F(WebAppInstallTaskTest, LoadAndInstallWebAppFromSync_TwoIcons) {
   SetInstallFinalizerForTesting();
 
   const GURL url{"https://example.com/path"};
   const GURL icon1_url{"https://example.com/path/icon1.png"};
   const GURL icon2_url{"https://example.com/path/icon2.png"};
+  url_loader().SetNextLoadUrlResult(
+      url, WebAppUrlLoader::Result::kRedirectedUrlLoaded);
 
   CreateDefaultDataToRetrieve(url);
 
@@ -940,9 +942,9 @@
 
   base::RunLoop run_loop;
 
-  install_task_->InstallWebAppFromInfoRetrieveIcons(
-      web_contents(), std::move(web_app_info), /*is_locally_installed=*/true,
-      WebappInstallSource::SYNC,
+  install_task_->LoadAndInstallWebAppFromSync(
+      web_contents(), &url_loader(), std::move(web_app_info),
+      /*is_locally_installed=*/true, WebappInstallSource::SYNC,
       base::BindLambdaForTesting(
           [&](const AppId& installed_app_id, InstallResultCode code) {
             EXPECT_EQ(InstallResultCode::kSuccessNewInstall, code);
@@ -978,10 +980,12 @@
   run_loop.Run();
 }
 
-TEST_F(WebAppInstallTaskTest, InstallWebAppFromInfoRetrieveIcons_NoIcons) {
+TEST_F(WebAppInstallTaskTest, LoadAndInstallWebAppFromSync_NoIcons) {
   SetInstallFinalizerForTesting();
 
   const GURL url{"https://example.com/path"};
+  url_loader().SetNextLoadUrlResult(
+      url, WebAppUrlLoader::Result::kRedirectedUrlLoaded);
   CreateDefaultDataToRetrieve(url);
   const AppId app_id = GenerateAppIdFromURL(url);
 
@@ -992,9 +996,9 @@
 
   base::RunLoop run_loop;
 
-  install_task_->InstallWebAppFromInfoRetrieveIcons(
-      web_contents(), std::move(web_app_info), /*is_locally_installed=*/false,
-      WebappInstallSource::SYNC,
+  install_task_->LoadAndInstallWebAppFromSync(
+      web_contents(), &url_loader(), std::move(web_app_info),
+      /*is_locally_installed=*/false, WebappInstallSource::SYNC,
       base::BindLambdaForTesting(
           [&](const AppId& installed_app_id, InstallResultCode code) {
             EXPECT_EQ(InstallResultCode::kSuccessNewInstall, code);
@@ -1162,21 +1166,26 @@
   }
 }
 
-TEST_F(WebAppInstallTaskTest,
-       InstallWebAppFromInfoRetrieveIcons_LocallyInstallled) {
+TEST_F(WebAppInstallTaskTest, LoadAndInstallWebAppFromSync_LocallyInstallled) {
   {
     const auto url = GURL("https://example.com/");
+    url_loader().SetNextLoadUrlResult(
+        url, WebAppUrlLoader::Result::kRedirectedUrlLoaded);
     CreateDataToRetrieve(url, /*open_as_window*/ false);
     auto app_id =
-        InstallWebAppFromInfoRetrieveIcons(url, /*is_locally_installed*/ false);
+        LoadAndInstallWebAppFromSync(url, /*is_locally_installed*/ false);
     EXPECT_FALSE(registrar().GetAppById(app_id)->is_locally_installed());
+    EXPECT_EQ(registrar().GetAppDisplayMode(app_id), DisplayMode::kBrowser);
   }
   {
     const auto url = GURL("https://example.org/");
+    url_loader().SetNextLoadUrlResult(
+        url, WebAppUrlLoader::Result::kRedirectedUrlLoaded);
     CreateDataToRetrieve(url, /*open_as_window*/ false);
     auto app_id =
-        InstallWebAppFromInfoRetrieveIcons(url, /*is_locally_installed*/ true);
+        LoadAndInstallWebAppFromSync(url, /*is_locally_installed*/ true);
     EXPECT_TRUE(registrar().GetAppById(app_id)->is_locally_installed());
+    EXPECT_EQ(registrar().GetAppDisplayMode(app_id), DisplayMode::kBrowser);
   }
 }
 
diff --git a/chrome/common/pref_names.cc b/chrome/common/pref_names.cc
index cd1dc81..107b0ad 100644
--- a/chrome/common/pref_names.cc
+++ b/chrome/common/pref_names.cc
@@ -128,6 +128,13 @@
 // Boolean that is true when user feedback to Google is allowed.
 const char kUserFeedbackAllowed[] = "feedback_allowed";
 
+#if BUILDFLAG(ENABLE_SUPERVISED_USERS) && BUILDFLAG(ENABLE_EXTENSIONS)
+// DictionaryValue that maps extension ids to the approved version of this
+// extension for a supervised user. Missing extensions are not approved.
+const char kSupervisedUserApprovedExtensions[] =
+    "profile.managed.approved_extensions";
+#endif  // BUILDFLAG(ENABLE_SUPERVISED_USERS) && BUILDFLAG(ENABLE_EXTENSIONS)
+
 // Stores the email address associated with the google account of the custodian
 // of the supervised user, set when the supervised user is created.
 const char kSupervisedUserCustodianEmail[] = "profile.managed.custodian_email";
diff --git a/chrome/common/pref_names.h b/chrome/common/pref_names.h
index a63f8709..aad39d21 100644
--- a/chrome/common/pref_names.h
+++ b/chrome/common/pref_names.h
@@ -43,6 +43,9 @@
 extern const char kSessionExitType[];
 extern const char kObservedSessionTime[];
 extern const char kSiteEngagementLastUpdateTime[];
+#if BUILDFLAG(ENABLE_SUPERVISED_USERS) && BUILDFLAG(ENABLE_EXTENSIONS)
+extern const char kSupervisedUserApprovedExtensions[];
+#endif  // BUILDFLAG(ENABLE_SUPERVISED_USERS) && BUILDFLAG(ENABLE_EXTENSIONS)
 extern const char kSupervisedUserCustodianEmail[];
 extern const char kSupervisedUserCustodianName[];
 extern const char kSupervisedUserCustodianObfuscatedGaiaId[];
diff --git a/chrome/common/webui_url_constants.cc b/chrome/common/webui_url_constants.cc
index e0d6965..77b97f0 100644
--- a/chrome/common/webui_url_constants.cc
+++ b/chrome/common/webui_url_constants.cc
@@ -429,6 +429,7 @@
 const char kNetworkDetailSubPage[] = "networkDetail";
 const char kNetworksSubPage[] = "networks";
 const char kOsAccessibilitySubPage[] = "osAccessibility";
+const char kOsPrintingSubPage[] = "osPrinting";
 const char kOsResetSubPage[] = "osReset";
 const char kOsSearchSubPage[] = "osSearch";
 const char kPluginVmDetailsSubPage[] = "pluginVm/details";
@@ -488,6 +489,7 @@
       kOsLanguagesSubPage,
       kOsLanguagesDetailsSubPage,
       kOsLanguagesInputMethodsSubPage,
+      kOsPrintingSubPage,
       kOsResetSubPage,
       kOsSearchSubPage,
       kPointerOverlaySubPage,
diff --git a/chrome/common/webui_url_constants.h b/chrome/common/webui_url_constants.h
index 0fe1781..a8d1d76 100644
--- a/chrome/common/webui_url_constants.h
+++ b/chrome/common/webui_url_constants.h
@@ -389,6 +389,7 @@
 extern const char kOsLanguagesSubPage[];
 extern const char kOsLanguagesDetailsSubPage[];
 extern const char kOsLanguagesInputMethodsSubPage[];
+extern const char kOsPrintingSubPage[];
 extern const char kOsResetSubPage[];
 extern const char kOsSearchSubPage[];
 extern const char kPluginVmDetailsSubPage[];
diff --git a/chrome/services/app_service/app_service_impl.cc b/chrome/services/app_service/app_service_impl.cc
index 1857f9a6..fcf5289 100644
--- a/chrome/services/app_service/app_service_impl.cc
+++ b/chrome/services/app_service/app_service_impl.cc
@@ -137,6 +137,7 @@
 void AppServiceImpl::LaunchAppWithIntent(
     apps::mojom::AppType app_type,
     const std::string& app_id,
+    int32_t event_flags,
     apps::mojom::IntentPtr intent,
     apps::mojom::LaunchSource launch_source,
     int64_t display_id) {
@@ -144,8 +145,8 @@
   if (iter == publishers_.end()) {
     return;
   }
-  iter->second->LaunchAppWithIntent(app_id, std::move(intent), launch_source,
-                                    display_id);
+  iter->second->LaunchAppWithIntent(app_id, event_flags, std::move(intent),
+                                    launch_source, display_id);
 }
 
 void AppServiceImpl::SetPermission(apps::mojom::AppType app_type,
diff --git a/chrome/services/app_service/app_service_impl.h b/chrome/services/app_service/app_service_impl.h
index f12953a..85c97e5e 100644
--- a/chrome/services/app_service/app_service_impl.h
+++ b/chrome/services/app_service/app_service_impl.h
@@ -63,6 +63,7 @@
                           apps::mojom::FilePathsPtr file_paths) override;
   void LaunchAppWithIntent(apps::mojom::AppType app_type,
                            const std::string& app_id,
+                           int32_t event_flags,
                            apps::mojom::IntentPtr intent,
                            apps::mojom::LaunchSource launch_source,
                            int64_t display_id) override;
diff --git a/chrome/services/app_service/app_service_impl_unittest.cc b/chrome/services/app_service/app_service_impl_unittest.cc
index dffab48a..50baca4 100644
--- a/chrome/services/app_service/app_service_impl_unittest.cc
+++ b/chrome/services/app_service/app_service_impl_unittest.cc
@@ -90,6 +90,7 @@
                           apps::mojom::FilePathsPtr file_paths) override {}
 
   void LaunchAppWithIntent(const std::string& app_id,
+                           int32_t event_flags,
                            apps::mojom::IntentPtr intent,
                            apps::mojom::LaunchSource launch_source,
                            int64_t display_id) override {}
diff --git a/chrome/services/app_service/public/cpp/intent_util.cc b/chrome/services/app_service/public/cpp/intent_util.cc
index 3b6b749..315b0bd 100644
--- a/chrome/services/app_service/public/cpp/intent_util.cc
+++ b/chrome/services/app_service/public/cpp/intent_util.cc
@@ -32,6 +32,7 @@
   auto intent = apps::mojom::Intent::New();
   intent->scheme = url.scheme();
   intent->host = url.host();
+  intent->port = url.port();
   intent->path = url.path();
   return intent;
 }
diff --git a/chrome/services/app_service/public/mojom/app_service.mojom b/chrome/services/app_service/public/mojom/app_service.mojom
index 30e7465..0ff7c93 100644
--- a/chrome/services/app_service/public/mojom/app_service.mojom
+++ b/chrome/services/app_service/public/mojom/app_service.mojom
@@ -54,6 +54,7 @@
   LaunchAppWithIntent(
       AppType app_type,
       string app_id,
+      int32 event_flags,
       Intent intent,
       LaunchSource launch_source,
       int64 display_id);
@@ -148,6 +149,7 @@
   // of app platform.
   LaunchAppWithIntent(
     string app_id,
+    int32 event_flags,
     Intent intent,
     LaunchSource launch_source,
     int64 display_id);
diff --git a/chrome/services/app_service/public/mojom/types.mojom b/chrome/services/app_service/public/mojom/types.mojom
index dc39c281..2fcf8aa 100644
--- a/chrome/services/app_service/public/mojom/types.mojom
+++ b/chrome/services/app_service/public/mojom/types.mojom
@@ -188,6 +188,7 @@
   kFromMenu = 14,                   // Menu.
   kFromInstalledNotification = 15,  // Installed notification
   kFromTest = 16,                   // Test
+  kFromArc = 17,                    // Arc.
 };
 
 enum TriState {
@@ -286,6 +287,7 @@
 struct Intent {
   string? scheme; // URL scheme. e.g. https.
   string? host;   // URL host. e.g. www.google.com.
+  string? port;   // URL host. e.g. 8080.
   string? path;   // URL path. e.g. /abc.
 };
 
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 3bd43b5..cf539ef 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -2425,6 +2425,7 @@
         "../browser/ui/web_applications/web_app_guest_session_browsertest_chromeos.cc",
         "../browser/ui/webui/chromeos/add_supervision/add_supervision_metrics_recorder_browsertest.cc",
         "../browser/ui/webui/chromeos/add_supervision/add_supervision_ui_browsertest.cc",
+        "../browser/ui/webui/chromeos/crostini_upgrader/crostini_upgrader_dialog_browsertest.cc",
         "../browser/ui/webui/chromeos/login/discover/discover_browser_test.cc",
         "../browser/ui/webui/chromeos/login/discover/discover_browser_test.h",
         "../browser/ui/webui/chromeos/login/discover/modules/discover_module_launch_help_app_test.cc",
diff --git a/chrome/test/base/testing_profile.cc b/chrome/test/base/testing_profile.cc
index 9f28b4e..c2586aa 100644
--- a/chrome/test/base/testing_profile.cc
+++ b/chrome/test/base/testing_profile.cc
@@ -13,6 +13,7 @@
 #include "base/files/file_util.h"
 #include "base/location.h"
 #include "base/macros.h"
+#include "base/no_destructor.h"
 #include "base/path_service.h"
 #include "base/run_loop.h"
 #include "base/single_thread_task_runner.h"
@@ -752,14 +753,29 @@
   return original_profile_;
 }
 
-void TestingProfile::SetOffTheRecordProfile(std::unique_ptr<Profile> profile) {
-  DCHECK(!IsOffTheRecord());
-  if (profile)
-    DCHECK_EQ(this, profile->GetOriginalProfile());
-  incognito_profile_ = std::move(profile);
+const Profile::OTRProfileID& TestingProfile::GetOTRProfileID() const {
+  // TODO(https://crbug.com//1033903): Remove this variable and add support for
+  // non-primary OTRs.
+  static base::NoDestructor<Profile::OTRProfileID> incognito_profile_id(
+      Profile::OTRProfileID::PrimaryID());
+  DCHECK(IsOffTheRecord());
+
+  return *incognito_profile_id;
 }
 
-Profile* TestingProfile::GetOffTheRecordProfile() {
+void TestingProfile::SetOffTheRecordProfile(
+    std::unique_ptr<Profile> otr_profile) {
+  // TODO(https://crbug.com//1033903): Add support for non-primary OTRs.
+  DCHECK(!IsOffTheRecord());
+  if (otr_profile)
+    DCHECK_EQ(this, otr_profile->GetOriginalProfile());
+  incognito_profile_ = std::move(otr_profile);
+}
+
+Profile* TestingProfile::GetOffTheRecordProfile(
+    const OTRProfileID& otr_profile_id) {
+  // TODO(https://crbug.com//1033903): Add support for non-primary OTRs.
+  DCHECK(otr_profile_id == OTRProfileID::PrimaryID());
   if (IsOffTheRecord())
     return this;
   if (!incognito_profile_)
@@ -767,11 +783,34 @@
   return incognito_profile_.get();
 }
 
-void TestingProfile::DestroyOffTheRecordProfile() {
+std::vector<Profile*> TestingProfile::GetAllOffTheRecordProfiles() {
+  // TODO(https://crbug.com//1033903): Add support for non-primary OTRs.
+  std::vector<Profile*> otr_profiles;
+
+  if (incognito_profile_)
+    otr_profiles.push_back(incognito_profile_.get());
+
+  return otr_profiles;
+}
+
+void TestingProfile::DestroyOffTheRecordProfile(Profile* otr_profile) {
+  // TODO(https://crbug.com//1033903): Add support for non-primary OTRs.
   incognito_profile_.reset();
 }
 
-bool TestingProfile::HasOffTheRecordProfile() {
+void TestingProfile::DestroyOffTheRecordProfile() {
+  DestroyOffTheRecordProfile(incognito_profile_.get());
+}
+
+bool TestingProfile::HasOffTheRecordProfile(
+    const OTRProfileID& otr_profile_id) {
+  // TODO(https://crbug.com//1033903): Add support for non-primary OTRs.
+  DCHECK(otr_profile_id == OTRProfileID::PrimaryID());
+  return incognito_profile_.get() != nullptr;
+}
+
+bool TestingProfile::HasAnyOffTheRecordProfile() {
+  // TODO(https://crbug.com//1033903): Add support for non-primary OTRs.
   return incognito_profile_.get() != nullptr;
 }
 
@@ -812,8 +851,7 @@
 }
 
 bool TestingProfile::IsIndependentOffTheRecordProfile() {
-  return !GetOriginalProfile()->HasOffTheRecordProfile() ||
-         GetOriginalProfile()->GetOffTheRecordProfile() != this;
+  return IsOffTheRecord() && GetOTRProfileID() != OTRProfileID::PrimaryID();
 }
 
 bool TestingProfile::AllowsBrowserWindows() const {
diff --git a/chrome/test/base/testing_profile.h b/chrome/test/base/testing_profile.h
index 7de6198..d288a67 100644
--- a/chrome/test/base/testing_profile.h
+++ b/chrome/test/base/testing_profile.h
@@ -156,6 +156,9 @@
     // Build an incognito profile, owned by |original_profile|. Note: unless you
     // need to customize the Builder, or access TestingProfile member functions,
     // you can use original_profile->GetOffTheRecordProfile().
+    //
+    // TODO(https://crbug.com/1033903): Add BuildOffTheRecord to add possibility
+    // of creating non-primary OTRs.
     TestingProfile* BuildIncognito(TestingProfile* original_profile);
 
    private:
@@ -268,10 +271,10 @@
   void SetNetworkContext(
       std::unique_ptr<network::mojom::NetworkContext> network_context);
 
-  // Called on the parent of an incognito |profile|. Usually called from the
-  // constructor of an incognito TestingProfile, but can also be used by tests
-  // to provide an OffTheRecordProfileImpl instance.
-  void SetOffTheRecordProfile(std::unique_ptr<Profile> profile);
+  // Called on the parent of an OffTheRecord |otr_profile|. Usually called from
+  // the constructor of an OffTheRecord TestingProfile, but can also be used by
+  // tests to provide an OffTheRecordProfileImpl instance.
+  void SetOffTheRecordProfile(std::unique_ptr<Profile> otr_profile);
 
   void SetSupervisedUserId(const std::string& id);
 
@@ -288,6 +291,7 @@
   // profile dynamically.
   bool IsOffTheRecord() final;
   bool IsOffTheRecord() const final;
+  const OTRProfileID& GetOTRProfileID() const override;
   content::DownloadManagerDelegate* GetDownloadManagerDelegate() override;
   content::ResourceContext* GetResourceContext() override;
   content::BrowserPluginGuestManager* GetGuestManager() override;
@@ -315,9 +319,19 @@
   std::string GetProfileUserName() const override;
   ProfileType GetProfileType() const override;
 
-  Profile* GetOffTheRecordProfile() override;
-  void DestroyOffTheRecordProfile() override;
-  bool HasOffTheRecordProfile() override;
+  // TODO(https://crbug.com/1033903): Remove the default value.
+  Profile* GetOffTheRecordProfile(
+      const OTRProfileID& otr_profile_id = OTRProfileID::PrimaryID()) override;
+  std::vector<Profile*> GetAllOffTheRecordProfiles() override;
+  void DestroyOffTheRecordProfile(Profile* otr_profile) override;
+  // TODO(https://crbug.com/1033903): Remove this function when all the use
+  // cases are migrated to above version. The parameter-less version destroys
+  // the primary off the record profile.
+  void DestroyOffTheRecordProfile();
+  // TODO(https://crbug.com/1033903): Remove the default value.
+  bool HasOffTheRecordProfile(
+      const OTRProfileID& otr_profile_id = OTRProfileID::PrimaryID()) override;
+  bool HasAnyOffTheRecordProfile() override;
   Profile* GetOriginalProfile() override;
   const Profile* GetOriginalProfile() const override;
   bool IsSupervised() const override;
diff --git a/chrome/test/data/webui/BUILD.gn b/chrome/test/data/webui/BUILD.gn
index 4d3ca73..cd02eb3 100644
--- a/chrome/test/data/webui/BUILD.gn
+++ b/chrome/test/data/webui/BUILD.gn
@@ -261,6 +261,8 @@
     "$root_gen_dir/chrome/test/data/webui/settings/protocol_handlers_tests.m.js",
     "$root_gen_dir/chrome/test/data/webui/settings/recent_site_permissions_test.m.js",
     "$root_gen_dir/chrome/test/data/webui/settings/reset_page_test.m.js",
+    "$root_gen_dir/chrome/test/data/webui/settings/reset_profile_banner_test.m.js",
+    "$root_gen_dir/chrome/test/data/webui/settings/route_tests.m.js",
     "$root_gen_dir/chrome/test/data/webui/settings/safety_check_page_test.m.js",
     "$root_gen_dir/chrome/test/data/webui/settings/search_engines_page_test.m.js",
     "$root_gen_dir/chrome/test/data/webui/settings/search_page_test.m.js",
@@ -287,6 +289,7 @@
     "$root_gen_dir/chrome/test/data/webui/settings/site_list_entry_tests.m.js",
     "$root_gen_dir/chrome/test/data/webui/settings/site_list_tests.m.js",
     "$root_gen_dir/chrome/test/data/webui/settings/site_settings_page_test.m.js",
+    "$root_gen_dir/chrome/test/data/webui/settings/sync_account_control_test.m.js",
     "$root_gen_dir/chrome/test/data/webui/settings/sync_test_util.m.js",
     "$root_gen_dir/chrome/test/data/webui/settings/test_extension_control_browser_proxy.m.js",
     "$root_gen_dir/chrome/test/data/webui/settings/test_hats_browser_proxy.m.js",
diff --git a/chrome/test/data/webui/new_tab_page/logo_test.js b/chrome/test/data/webui/new_tab_page/logo_test.js
index 6bb8a76..d68aa77d 100644
--- a/chrome/test/data/webui/new_tab_page/logo_test.js
+++ b/chrome/test/data/webui/new_tab_page/logo_test.js
@@ -107,7 +107,8 @@
     assertStyle(logo.$.multiColoredLogo, 'display', 'none');
   });
 
-  test('receiving resize message resizes doodle', async () => {
+  // Disabled for flakiness, see https://crbug.com/1065812.
+  test.skip('receiving resize message resizes doodle', async () => {
     // Arrange.
     const logo = await createLogo({content: {url: {url: 'https://foo.com'}}});
     const transitionend = eventToPromise('transitionend', logo.$.iframe);
diff --git a/chrome/test/data/webui/new_tab_page/new_tab_page_browsertest.js b/chrome/test/data/webui/new_tab_page/new_tab_page_browsertest.js
index b20c9f9..dc03db6 100644
--- a/chrome/test/data/webui/new_tab_page/new_tab_page_browsertest.js
+++ b/chrome/test/data/webui/new_tab_page/new_tab_page_browsertest.js
@@ -156,7 +156,6 @@
   }
 };
 
-// Disabled for flakiness, see https://crbug.com/1065812
-TEST_F('NewTabPageLogoTest', 'DISABLED_All', function() {
+TEST_F('NewTabPageLogoTest', 'All', function() {
   mocha.run();
 });
diff --git a/chrome/test/data/webui/settings/BUILD.gn b/chrome/test/data/webui/settings/BUILD.gn
index 58cf12f..a55da60 100644
--- a/chrome/test/data/webui/settings/BUILD.gn
+++ b/chrome/test/data/webui/settings/BUILD.gn
@@ -56,6 +56,8 @@
     "protocol_handlers_tests.js",
     "recent_site_permissions_test.js",
     "reset_page_test.js",
+    "reset_profile_banner_test.js",
+    "route_tests.js",
     "safety_check_page_test.js",
     "search_engines_page_test.js",
     "search_page_test.js",
@@ -82,6 +84,7 @@
     "site_favicon_test.js",
     "site_settings_page_test.js",
     "startup_urls_page_test.js",
+    "sync_account_control_test.js",
     "sync_test_util.js",
     "test_about_page_browser_proxy.js",
     "test_extension_control_browser_proxy.js",
@@ -138,6 +141,7 @@
                          "autofill_test_util.makePasswordCheckStatus|makePasswordCheckStatus",
                          "export_passwords_tests.run|run",
                          "reset_page.TestResetBrowserProxy|TestResetBrowserProxy",
+                         "settings.buildRouterForTesting|buildRouter",
                          "settings.CHROME_CLEANUP_DEFAULT_ITEMS_TO_SHOW|CHROME_CLEANUP_DEFAULT_ITEMS_TO_SHOW",
                          "settings.ChromeCleanupIdleReason|ChromeCleanupIdleReason",
                          "settings.defaultSettingLabel|defaultSettingLabel",
@@ -146,7 +150,9 @@
                          "settings.FakeSettingsPrivate|FakeSettingsPrivate",
                          "settings.getFakeLanguagePrefs|getFakeLanguagePrefs",
                          "settings.kMenuCloseDelay|kMenuCloseDelay",
+                         "settings.MAX_SIGNIN_PROMO_IMPRESSION|MAX_SIGNIN_PROMO_IMPRESSION",
                          "settings.setLanguageSettingsPrivateApiForTest|setLanguageSettingsPrivateApiForTest",
+                         "settings.setPageVisibilityForTesting|setPageVisibilityForTesting",
                          "settings.setSearchManagerForTesting|setSearchManagerForTesting",
                          "settings.TestLanguagesBrowserProxy|TestLanguagesBrowserProxy",
                          "settings.TestLifetimeBrowserProxy|TestLifetimeBrowserProxy",
diff --git a/chrome/test/data/webui/settings/cr_settings_browsertest.js b/chrome/test/data/webui/settings/cr_settings_browsertest.js
index e001a1fc..fb4ca63 100644
--- a/chrome/test/data/webui/settings/cr_settings_browsertest.js
+++ b/chrome/test/data/webui/settings/cr_settings_browsertest.js
@@ -1982,7 +1982,7 @@
 };
 
 TEST_F('CrSettingsRouteTest', 'All', function() {
-  mocha.run();
+  runMochaSuite('route');
 });
 
 /**
@@ -1992,7 +1992,7 @@
 function CrSettingsNonExistentRouteTest() {}
 
 CrSettingsNonExistentRouteTest.prototype = {
-  __proto__: CrSettingsBrowserTest.prototype,
+  __proto__: CrSettingsRouteTest.prototype,
 
   /** @override */
   browsePreload: 'chrome://settings/non/existent/route',
@@ -2005,15 +2005,7 @@
 GEN('#define MAYBE_NonExistentRoute NonExistentRoute');
 GEN('#endif');
 TEST_F('CrSettingsNonExistentRouteTest', 'MAYBE_NonExistentRoute', function() {
-  suite('NonExistentRoutes', function() {
-    test('redirect to basic', function() {
-      assertEquals(
-          settings.routes.BASIC,
-          settings.Router.getInstance().getCurrentRoute());
-      assertEquals('/', location.pathname);
-    });
-  });
-  mocha.run();
+  runMochaSuite('NonExistentRoute');
 });
 
 /**
@@ -2023,56 +2015,14 @@
 function CrSettingsRouteDynamicParametersTest() {}
 
 CrSettingsRouteDynamicParametersTest.prototype = {
-  __proto__: CrSettingsBrowserTest.prototype,
+  __proto__: CrSettingsRouteTest.prototype,
 
   /** @override */
   browsePreload: 'chrome://settings/search?guid=a%2Fb&foo=42',
 };
 
 TEST_F('CrSettingsRouteDynamicParametersTest', 'All', function() {
-  suite('DynamicParameters', function() {
-    test('get parameters from URL and navigation', function(done) {
-      assertEquals(
-          settings.routes.SEARCH,
-          settings.Router.getInstance().getCurrentRoute());
-      assertEquals(
-          'a/b',
-          settings.Router.getInstance().getQueryParameters().get('guid'));
-      assertEquals(
-          '42', settings.Router.getInstance().getQueryParameters().get('foo'));
-
-      const params = new URLSearchParams();
-      params.set('bar', 'b=z');
-      params.set('biz', '3');
-      settings.Router.getInstance().navigateTo(
-          settings.routes.SEARCH_ENGINES, params);
-      assertEquals(
-          settings.routes.SEARCH_ENGINES,
-          settings.Router.getInstance().getCurrentRoute());
-      assertEquals(
-          'b=z', settings.Router.getInstance().getQueryParameters().get('bar'));
-      assertEquals(
-          '3', settings.Router.getInstance().getQueryParameters().get('biz'));
-      assertEquals('?bar=b%3Dz&biz=3', window.location.search);
-
-      window.addEventListener('popstate', function(event) {
-        assertEquals(
-            '/search', settings.Router.getInstance().getCurrentRoute().path);
-        assertEquals(
-            settings.routes.SEARCH,
-            settings.Router.getInstance().getCurrentRoute());
-        assertEquals(
-            'a/b',
-            settings.Router.getInstance().getQueryParameters().get('guid'));
-        assertEquals(
-            '42',
-            settings.Router.getInstance().getQueryParameters().get('foo'));
-        done();
-      });
-      window.history.back();
-    });
-  });
-  mocha.run();
+  runMochaSuite('DynamicParameters');
 });
 
 /**
diff --git a/chrome/test/data/webui/settings/cr_settings_v3_browsertest.js b/chrome/test/data/webui/settings/cr_settings_v3_browsertest.js
index 6e538e8d..4b3ecb9 100644
--- a/chrome/test/data/webui/settings/cr_settings_v3_browsertest.js
+++ b/chrome/test/data/webui/settings/cr_settings_v3_browsertest.js
@@ -354,6 +354,34 @@
       runMochaSuite('HappinessTrackingSurveys');
     });
 
+// eslint-disable-next-line no-var
+var CrSettingsRouteV3Test = class extends CrSettingsV3BrowserTest {
+  /** @override */
+  get browsePreload() {
+    return 'chrome://settings/test_loader.html?module=settings/route_tests.m.js';
+  }
+};
+
+TEST_F('CrSettingsRouteV3Test', 'Basic', function() {
+  runMochaSuite('route');
+});
+
+TEST_F('CrSettingsRouteV3Test', 'DynamicParameters', function() {
+  runMochaSuite('DynamicParameters');
+});
+
+// Copied from Polymer 2 test:
+// Failing on ChromiumOS dbg. https://crbug.com/709442
+GEN('#if (defined(OS_WIN) || defined(OS_CHROMEOS)) && !defined(NDEBUG)');
+GEN('#define MAYBE_NonExistentRoute DISABLED_NonExistentRoute');
+GEN('#else');
+GEN('#define MAYBE_NonExistentRoute NonExistentRoute');
+GEN('#endif');
+
+TEST_F('CrSettingsRouteV3Test', 'MAYBE_NonExistentRoute', function() {
+  runMochaSuite('NonExistentRoute');
+});
+
 [['AllSites', 'all_sites_tests.m.js'],
  ['AppearanceFontsPage', 'appearance_fonts_page_test.m.js'],
  ['AppearancePage', 'appearance_page_test.m.js'],
@@ -385,6 +413,7 @@
  ['ProtocolHandlers', 'protocol_handlers_tests.m.js'],
  ['RecentSitePermissions', 'recent_site_permissions_test.m.js'],
  ['ResetPage', 'reset_page_test.m.js'],
+ ['ResetProfileBanner', 'reset_profile_banner_test.m.js'],
  ['SearchEngines', 'search_engines_page_test.m.js'],
  ['SearchPage', 'search_page_test.m.js'],
  ['Search', 'search_settings_test.m.js'],
@@ -402,6 +431,7 @@
  ['Slider', 'settings_slider_tests.m.js'],
  ['StartupUrlsPage', 'startup_urls_page_test.m.js'],
  ['Subpage', 'settings_subpage_test.m.js'],
+ ['SyncAccountControl', 'sync_account_control_test.m.js'],
  ['Textarea', 'settings_textarea_tests.m.js'],
  ['ToggleButton', 'settings_toggle_button_tests.m.js'],
  ['ZoomLevels', 'zoom_levels_tests.m.js'],
diff --git a/chrome/test/data/webui/settings/reset_profile_banner_test.js b/chrome/test/data/webui/settings/reset_profile_banner_test.js
index a437c7e..62c0b30 100644
--- a/chrome/test/data/webui/settings/reset_profile_banner_test.js
+++ b/chrome/test/data/webui/settings/reset_profile_banner_test.js
@@ -2,6 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+// clang-format off
+// #import {Router, routes, ResetBrowserProxyImpl} from 'chrome://settings/settings.js';
+// #import {TestResetBrowserProxy} from 'chrome://test/settings/test_reset_browser_proxy.m.js';
+// clang-format on
+
 suite('BannerTests', function() {
   let resetBanner = null;
   let browserProxy = null;
diff --git a/chrome/test/data/webui/settings/route_tests.js b/chrome/test/data/webui/settings/route_tests.js
index 7b538cc..133c434 100644
--- a/chrome/test/data/webui/settings/route_tests.js
+++ b/chrome/test/data/webui/settings/route_tests.js
@@ -2,6 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+// clang-format off
+// #import {buildRouter, pageVisibility, routes, Route, Router, setPageVisibilityForTesting} from 'chrome://settings/settings.js';
+// #import {isChromeOS} from 'chrome://resources/js/cr.m.js';
+// clang-format on
+
 suite('route', function() {
   /**
    * Returns a new promise that resolves after a window 'popstate' event.
@@ -214,13 +219,13 @@
   });
 
   test('pageVisibility affects route availability', function() {
-    settings.pageVisibility = {
+    settings.setPageVisibilityForTesting({
       appearance: false,
       autofill: false,
       defaultBrowser: false,
       onStartup: false,
       reset: false,
-    };
+    });
     loadTimeData.overrideValues({showOSSettings: false});
 
     const router = settings.buildRouterForTesting();
@@ -257,3 +262,74 @@
             settings.routes.LANGUAGES.getAbsolutePath());
       });
 });
+
+suite('DynamicParameters', function() {
+  setup(function() {
+    // TODO(https://crbug.com/1026426): Remove conditional when Polymer 2 tests
+    // are no longer run.
+    if (window.location.pathname === '/test_loader.html') {
+      PolymerTest.clearBody();
+      window.history.replaceState({}, '', 'search?guid=a%2Fb&foo=42');
+      const settingsUi = document.createElement('settings-ui');
+      document.body.appendChild(settingsUi);
+    }
+  });
+
+  test('get parameters from URL and navigation', function(done) {
+    assertEquals(
+        settings.routes.SEARCH,
+        settings.Router.getInstance().getCurrentRoute());
+    assertEquals(
+        'a/b', settings.Router.getInstance().getQueryParameters().get('guid'));
+    assertEquals(
+        '42', settings.Router.getInstance().getQueryParameters().get('foo'));
+
+    const params = new URLSearchParams();
+    params.set('bar', 'b=z');
+    params.set('biz', '3');
+    settings.Router.getInstance().navigateTo(
+        settings.routes.SEARCH_ENGINES, params);
+    assertEquals(
+        settings.routes.SEARCH_ENGINES,
+        settings.Router.getInstance().getCurrentRoute());
+    assertEquals(
+        'b=z', settings.Router.getInstance().getQueryParameters().get('bar'));
+    assertEquals(
+        '3', settings.Router.getInstance().getQueryParameters().get('biz'));
+    assertEquals('?bar=b%3Dz&biz=3', window.location.search);
+
+    window.addEventListener('popstate', function(event) {
+      assertEquals(
+          '/search', settings.Router.getInstance().getCurrentRoute().path);
+      assertEquals(
+          settings.routes.SEARCH,
+          settings.Router.getInstance().getCurrentRoute());
+      assertEquals(
+          'a/b',
+          settings.Router.getInstance().getQueryParameters().get('guid'));
+      assertEquals(
+          '42', settings.Router.getInstance().getQueryParameters().get('foo'));
+      done();
+    });
+    window.history.back();
+  });
+});
+
+suite('NonExistentRoute', function() {
+  setup(function() {
+    // TODO(https://crbug.com/1026426): Remove conditional when Polymer 2 tests
+    // are no longer run.
+    if (window.location.pathname === '/test_loader.html') {
+      PolymerTest.clearBody();
+      window.history.replaceState({}, '', 'non/existent/route');
+      const settingsUi = document.createElement('settings-ui');
+      document.body.appendChild(settingsUi);
+    }
+  });
+
+  test('redirect to basic', function() {
+    assertEquals(
+        settings.routes.BASIC, settings.Router.getInstance().getCurrentRoute());
+    assertEquals('/', location.pathname);
+  });
+});
diff --git a/chrome/test/data/webui/settings/sync_account_control_test.js b/chrome/test/data/webui/settings/sync_account_control_test.js
index fe8387a..7c4b134 100644
--- a/chrome/test/data/webui/settings/sync_account_control_test.js
+++ b/chrome/test/data/webui/settings/sync_account_control_test.js
@@ -2,6 +2,18 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+// clang-format off
+// #import {Router, routes, SyncBrowserProxyImpl, StatusAction} from 'chrome://settings/settings.js';
+// #import {MAX_SIGNIN_PROMO_IMPRESSION} from 'chrome://settings/lazy_load.js';
+// #import {TestSyncBrowserProxy} from 'chrome://test/settings/test_sync_browser_proxy.m.js';
+// #import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+// #import {isChromeOS} from 'chrome://resources/js/cr.m.js';
+// #import {setupRouterWithSyncRoutes, simulateStoredAccounts} from 'chrome://test/settings/sync_test_util.m.js';
+// #import {isChildVisible, isVisible} from 'chrome://test/test_util.m.js';
+// clang-format on
+
+// TODO(https://crbug.com/1063530): Remove all ignores in this file, once
+// i18nPolymer works correctly in Polymer 3.
 cr.define('settings_sync_account_control', function() {
 
   suite('SyncAccountControl', function() {
@@ -160,10 +172,10 @@
       // Avatar row shows the right account.
       assertTrue(test_util.isChildVisible(testElement, '#promo-header'));
       assertTrue(test_util.isChildVisible(testElement, '#avatar-row'));
-      assertTrue(userInfo.textContent.includes('fooName'));
-      assertTrue(userInfo.textContent.includes('foo@foo.com'));
-      assertFalse(userInfo.textContent.includes('barName'));
-      assertFalse(userInfo.textContent.includes('bar@bar.com'));
+      /* #ignore */ assertTrue(userInfo.textContent.includes('fooName'));
+      /* #ignore */ assertTrue(userInfo.textContent.includes('foo@foo.com'));
+      /* #ignore */ assertFalse(userInfo.textContent.includes('barName'));
+      /* #ignore */ assertFalse(userInfo.textContent.includes('bar@bar.com'));
 
       // Menu contains the right items.
       assertTrue(!!testElement.$$('#menu'));
@@ -201,10 +213,13 @@
             // email.
             items[1].click();
             Polymer.dom.flush();
-            assertFalse(userInfo.textContent.includes('fooName'));
-            assertFalse(userInfo.textContent.includes('foo@foo.com'));
-            assertTrue(userInfo.textContent.includes('barName'));
-            assertTrue(userInfo.textContent.includes('bar@bar.com'));
+            /* #ignore */ assertFalse(
+                /* #ignore */ userInfo.textContent.includes('fooName'));
+            /* #ignore */ assertFalse(
+                /* #ignore */ userInfo.textContent.includes('foo@foo.com'));
+            /* #ignore */ assertTrue(userInfo.textContent.includes('barName'));
+            /* #ignore */ assertTrue(
+                /* #ignore */ userInfo.textContent.includes('bar@bar.com'));
             assertTrue(test_util.isVisible(syncButton));
 
             browserProxy.resetResolver('startSyncingWithEmail');
@@ -248,10 +263,10 @@
       }
 
       const userInfo = testElement.$$('#user-info');
-      assertTrue(userInfo.textContent.includes('barName'));
-      assertTrue(userInfo.textContent.includes('bar@bar.com'));
-      assertFalse(userInfo.textContent.includes('fooName'));
-      assertFalse(userInfo.textContent.includes('foo@foo.com'));
+      /* #ignore */ assertTrue(userInfo.textContent.includes('barName'));
+      /* #ignore */ assertTrue(userInfo.textContent.includes('bar@bar.com'));
+      /* #ignore */ assertFalse(userInfo.textContent.includes('fooName'));
+      /* #ignore */ assertFalse(userInfo.textContent.includes('foo@foo.com'));
 
       assertFalse(test_util.isChildVisible(testElement, '#sync-button'));
       assertTrue(test_util.isChildVisible(testElement, '#turn-off'));
@@ -262,7 +277,7 @@
 
       assertEquals(
           settings.Router.getInstance().getCurrentRoute(),
-          settings.routes.SIGN_OUT);
+          settings.Router.getInstance().getRoutes().SIGN_OUT);
     });
 
     test('signed in, has error', function() {
@@ -283,9 +298,9 @@
       assertTrue(!!testElement.$$('[icon="settings:sync-problem"]'));
       let displayedText =
           userInfo.querySelector('span:not([hidden])').textContent;
-      assertFalse(displayedText.includes('barName'));
-      assertFalse(displayedText.includes('fooName'));
-      assertTrue(displayedText.includes('Sync isn\'t working'));
+      /* #ignore */ assertFalse(displayedText.includes('barName'));
+      /* #ignore */ assertFalse(displayedText.includes('fooName'));
+      /* #ignore */ assertTrue(displayedText.includes('Sync isn\'t working'));
       // The sync error button is shown to resolve the error.
       assertTrue(test_util.isChildVisible(testElement, '#sync-error-button'));
 
@@ -302,9 +317,9 @@
                      .classList.contains('sync-paused'));
       assertTrue(!!testElement.$$('[icon=\'settings:sync-disabled\']'));
       displayedText = userInfo.querySelector('span:not([hidden])').textContent;
-      assertFalse(displayedText.includes('barName'));
-      assertFalse(displayedText.includes('fooName'));
-      assertTrue(displayedText.includes('Sync is paused'));
+      /* #ignore */ assertFalse(displayedText.includes('barName'));
+      /* #ignore */ assertFalse(displayedText.includes('fooName'));
+      /* #ignore */ assertTrue(displayedText.includes('Sync is paused'));
       // The sync error button is shown to resolve the error.
       assertTrue(test_util.isChildVisible(testElement, '#sync-error-button'));
 
@@ -322,9 +337,9 @@
                      .classList.contains('sync-disabled'));
       assertTrue(!!testElement.$$('[icon=\'cr:sync\']'));
       displayedText = userInfo.querySelector('span:not([hidden])').textContent;
-      assertFalse(displayedText.includes('barName'));
-      assertFalse(displayedText.includes('fooName'));
-      assertTrue(displayedText.includes('Sync disabled'));
+      /* #ignore */ assertFalse(displayedText.includes('barName'));
+      /* #ignore */ assertFalse(displayedText.includes('fooName'));
+      /* #ignore */ assertTrue(displayedText.includes('Sync disabled'));
       assertFalse(test_util.isChildVisible(testElement, '#sync-error-button'));
 
       testElement.syncStatus = {
@@ -340,9 +355,9 @@
                      .classList.contains('sync-problem'));
       assertTrue(!!testElement.$$('[icon="settings:sync-problem"]'));
       displayedText = userInfo.querySelector('span:not([hidden])').textContent;
-      assertFalse(displayedText.includes('barName'));
-      assertFalse(displayedText.includes('fooName'));
-      assertTrue(displayedText.includes('Sync isn\'t working'));
+      /* #ignore */ assertFalse(displayedText.includes('barName'));
+      /* #ignore */ assertFalse(displayedText.includes('fooName'));
+      /* #ignore */ assertTrue(displayedText.includes('Sync isn\'t working'));
 
       testElement.syncStatus = {
         firstSetupInProgress: false,
@@ -358,10 +373,11 @@
                      .classList.contains('sync-problem'));
       assertTrue(!!testElement.$$('[icon="settings:sync-problem"]'));
       displayedText = userInfo.querySelector('span:not([hidden])').textContent;
-      assertFalse(displayedText.includes('barName'));
-      assertFalse(displayedText.includes('fooName'));
-      assertFalse(displayedText.includes('Sync isn\'t working'));
-      assertTrue(displayedText.includes('Error syncing passwords'));
+      /* #ignore */ assertFalse(displayedText.includes('barName'));
+      /* #ignore */ assertFalse(displayedText.includes('fooName'));
+      /* #ignore */ assertFalse(displayedText.includes('Sync isn\'t working'));
+      /* #ignore */ assertTrue(
+          /* #ignore */ displayedText.includes('Error syncing passwords'));
       // The sync error button is shown to resolve the error.
       assertTrue(test_util.isChildVisible(testElement, '#sync-error-button'));
       assertTrue(test_util.isChildVisible(testElement, '#turn-off'));
@@ -382,8 +398,9 @@
       const userInfo = testElement.$$('#user-info');
       const setupButtons = testElement.$$('#setup-buttons');
 
-      assertTrue(userInfo.textContent.includes('barName'));
-      assertTrue(userInfo.textContent.includes('Setup in progress...'));
+      /* #ignore */ assertTrue(userInfo.textContent.includes('barName'));
+      /* #ignore */ assertTrue(
+          /* #ignore */ userInfo.textContent.includes('Setup in progress...'));
       assertTrue(test_util.isVisible(setupButtons));
     });
 
@@ -508,4 +525,5 @@
       assertTrue(testElement.$$('#sign-in').disabled);
     });
   });
+  // #cr_define_end
 });
diff --git a/chrome/test/ppapi/ppapi_browsertest.cc b/chrome/test/ppapi/ppapi_browsertest.cc
index ad2d4745..6846387 100644
--- a/chrome/test/ppapi/ppapi_browsertest.cc
+++ b/chrome/test/ppapi/ppapi_browsertest.cc
@@ -16,7 +16,9 @@
 #include "base/threading/thread_restrictions.h"
 #include "build/build_config.h"
 #include "chrome/browser/apps/app_service/app_launch_params.h"
-#include "chrome/browser/apps/launch_service/launch_service.h"
+#include "chrome/browser/apps/app_service/app_service_proxy.h"
+#include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
+#include "chrome/browser/apps/app_service/browser_app_launcher.h"
 #include "chrome/browser/content_settings/host_content_settings_map_factory.h"
 #include "chrome/browser/extensions/extension_browsertest.h"
 #include "chrome/browser/profiles/profile.h"
@@ -2284,7 +2286,9 @@
         WindowOpenDisposition::NEW_WINDOW,
         apps::mojom::AppLaunchSource::kSourceTest);
     params.command_line = *base::CommandLine::ForCurrentProcess();
-    apps::LaunchService::Get(browser()->profile())->OpenApplication(params);
+    apps::AppServiceProxyFactory::GetForProfile(browser()->profile())
+        ->BrowserAppLauncher()
+        .LaunchAppWithParams(params);
   }
 
   void RunTests(const std::string& extension_dirname) {
diff --git a/chromeos/constants/chromeos_features.cc b/chromeos/constants/chromeos_features.cc
index cde1a2e..78cc3299 100644
--- a/chromeos/constants/chromeos_features.cc
+++ b/chromeos/constants/chromeos_features.cc
@@ -298,9 +298,9 @@
 const base::Feature kShowPlayInDemoMode{"ShowPlayInDemoMode",
                                         base::FEATURE_ENABLED_BY_DEFAULT};
 
-// Shows the progress bar during Demo Mode setup.
-const base::Feature kShowProgressBarInDemoModeSetup{
-    "ShowProgressBarInDemoModeSetup", base::FEATURE_DISABLED_BY_DEFAULT};
+// Shows individual steps during Demo Mode setup.
+const base::Feature kShowStepsInDemoModeSetup{
+    "ShowStepsInDemoModeSetup", base::FEATURE_DISABLED_BY_DEFAULT};
 
 // Uses experimental component version for smart dim.
 const base::Feature kSmartDimExperimentalComponent{
diff --git a/chromeos/constants/chromeos_features.h b/chromeos/constants/chromeos_features.h
index 2dbed49..d2799637 100644
--- a/chromeos/constants/chromeos_features.h
+++ b/chromeos/constants/chromeos_features.h
@@ -136,7 +136,7 @@
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
 extern const base::Feature kShowPlayInDemoMode;
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
-extern const base::Feature kShowProgressBarInDemoModeSetup;
+extern const base::Feature kShowStepsInDemoModeSetup;
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
 extern const base::Feature kSmartDimExperimentalComponent;
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
diff --git a/components/arc/mojom/intent_helper.mojom b/components/arc/mojom/intent_helper.mojom
index 87a74291..2916146 100644
--- a/components/arc/mojom/intent_helper.mojom
+++ b/components/arc/mojom/intent_helper.mojom
@@ -173,14 +173,16 @@
   AMBIENTMODE,
   PLUGINVMDETAILS,
   PLUGINVMSHAREDPATHS,
-  OSRESET,
   OSACCESSIBILITY,
   OSLANGUAGES,
   OSLANGUAGESDETAILS,
   OSLANGUAGESINPUTMETHODS,
+  OSPRINTING,
+  PRINTING,
   OSSEARCH,
+  OSRESET,
 
-  LAST = OSSEARCH,
+  LAST = OSRESET,
 };
 
 // Describes an unique chrome app.
diff --git a/components/autofill_assistant/browser/BUILD.gn b/components/autofill_assistant/browser/BUILD.gn
index aa4c9ee5..4e295f5 100644
--- a/components/autofill_assistant/browser/BUILD.gn
+++ b/components/autofill_assistant/browser/BUILD.gn
@@ -139,6 +139,8 @@
     "state.h",
     "string_conversions_util.cc",
     "string_conversions_util.h",
+    "switches.cc",
+    "switches.h",
     "top_padding.cc",
     "top_padding.h",
     "trigger_context.cc",
@@ -219,6 +221,7 @@
     "//base",
     "//base/test:test_support",
     "//components/autofill/core/browser:test_support",
+    "//components/version_info",
     "//testing/gmock",
     "//testing/gtest",
   ]
diff --git a/components/autofill_assistant/browser/DEPS b/components/autofill_assistant/browser/DEPS
index 6f760c5..82a5fd1 100644
--- a/components/autofill_assistant/browser/DEPS
+++ b/components/autofill_assistant/browser/DEPS
@@ -2,7 +2,7 @@
   "+chrome/android/features/autofill_assistant/test_support_jni_headers",
   "+components/autofill",
   "+components/password_manager/core/browser",
-  "+components/version_info/version_info.h",
+  "+components/version_info",
   "+content/public/browser",
   "+content/public/test",
   "+content/shell/browser/shell.h",
diff --git a/components/autofill_assistant/browser/client.h b/components/autofill_assistant/browser/client.h
index dfb11fb..e8345ec 100644
--- a/components/autofill_assistant/browser/client.h
+++ b/components/autofill_assistant/browser/client.h
@@ -17,7 +17,11 @@
 
 namespace password_manager {
 class PasswordManagerClient;
-}
+}  // namespace password_manager
+
+namespace version_info {
+enum class Channel;
+}  // namespace version_info
 
 namespace autofill_assistant {
 class AccessTokenFetcher;
@@ -38,8 +42,9 @@
   // Destroys the UI immediately.
   virtual void DestroyUI() = 0;
 
-  // Returns the API key to be used for requests to the backend.
-  virtual std::string GetApiKey() const = 0;
+  // Returns the channel for the installation (canary, dev, beta, stable).
+  // Returns unknown otherwise.
+  virtual version_info::Channel GetChannel() const = 0;
 
   // Returns the e-mail address that corresponds to the auth credentials. Might
   // be empty.
@@ -58,9 +63,6 @@
   // Returns the currently active login fetcher.
   virtual WebsiteLoginFetcher* GetWebsiteLoginFetcher() const = 0;
 
-  // Returns the server URL to be used for requests to the backend.
-  virtual std::string GetServerUrl() const = 0;
-
   // Returns the locale.
   virtual std::string GetLocale() const = 0;
 
diff --git a/components/autofill_assistant/browser/mock_client.h b/components/autofill_assistant/browser/mock_client.h
index 3d595ed3..40470c7 100644
--- a/components/autofill_assistant/browser/mock_client.h
+++ b/components/autofill_assistant/browser/mock_client.h
@@ -11,6 +11,7 @@
 #include "components/autofill_assistant/browser/metrics.h"
 #include "components/autofill_assistant/browser/mock_personal_data_manager.h"
 #include "components/autofill_assistant/browser/website_login_fetcher.h"
+#include "components/version_info/channel.h"
 #include "testing/gmock/include/gmock/gmock.h"
 
 namespace autofill_assistant {
@@ -20,8 +21,7 @@
   MockClient();
   ~MockClient();
 
-  MOCK_CONST_METHOD0(GetApiKey, std::string());
-  MOCK_CONST_METHOD0(GetServerUrl, std::string());
+  MOCK_CONST_METHOD0(GetChannel, version_info::Channel());
   MOCK_CONST_METHOD0(GetLocale, std::string());
   MOCK_CONST_METHOD0(GetCountryCode, std::string());
   MOCK_CONST_METHOD0(GetDeviceContext, DeviceContext());
diff --git a/components/autofill_assistant/browser/service_impl.cc b/components/autofill_assistant/browser/service_impl.cc
index 720cecd4..439b2c44 100644
--- a/components/autofill_assistant/browser/service_impl.cc
+++ b/components/autofill_assistant/browser/service_impl.cc
@@ -15,24 +15,24 @@
 #include "base/strings/stringprintf.h"
 #include "components/autofill_assistant/browser/client.h"
 #include "components/autofill_assistant/browser/protocol_utils.h"
+#include "components/autofill_assistant/browser/switches.h"
 #include "components/autofill_assistant/browser/trigger_context.h"
 #include "components/version_info/version_info.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/storage_partition.h"
 #include "crypto/sha2.h"
+#include "google_apis/google_api_keys.h"
 #include "net/base/load_flags.h"
 #include "net/http/http_request_headers.h"
 #include "net/http/http_status_code.h"
 #include "net/traffic_annotation/network_traffic_annotation.h"
 #include "url/url_canon_stdstring.h"
 
+namespace autofill_assistant {
 namespace {
-namespace switches {
-// --autofill_assistant-auth=false disables authentication. This is only useful
-// during development, as prod instances require authentication.
-const char* const kAutofillAssistantAuth = "autofill-assistant-auth";
-}  // namespace switches
 
+const char* const kDefaultAutofillAssistantServerUrl =
+    "https://automate-pa.googleapis.com";
 const char* const kScriptEndpoint = "/v1/supportsSite2";
 const char* const kActionEndpoint = "/v1/actions2";
 
@@ -54,21 +54,45 @@
             "This feature can be disabled in settings."
           policy_exception_justification: "Not implemented."
         })");
-}  // namespace
 
-namespace autofill_assistant {
+std::string GetAPIKey(version_info::Channel channel) {
+  const auto* command_line = base::CommandLine::ForCurrentProcess();
+  if (command_line->HasSwitch(switches::kAutofillAssistantServerKey)) {
+    return command_line->GetSwitchValueASCII(
+        switches::kAutofillAssistantServerKey);
+  }
+
+  if (google_apis::IsGoogleChromeAPIKeyUsed()) {
+    return channel == version_info::Channel::STABLE
+               ? google_apis::GetAPIKey()
+               : google_apis::GetNonStableAPIKey();
+  }
+  return "";
+}
+
+std::string GetServerUrl() {
+  std::string server_url =
+      base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
+          switches::kAutofillAssistantUrl);
+  if (server_url.empty()) {
+    server_url = kDefaultAutofillAssistantServerUrl;
+  }
+  return server_url;
+}
+
+}  // namespace
 
 // static
 std::unique_ptr<ServiceImpl> ServiceImpl::Create(
     content::BrowserContext* context,
     Client* client) {
-  GURL server_url(client->GetServerUrl());
+  GURL server_url(GetServerUrl());
   DCHECK(server_url.is_valid());
 
   return std::make_unique<ServiceImpl>(
-      client->GetApiKey(), server_url, context, client->GetAccessTokenFetcher(),
-      client->GetLocale(), client->GetCountryCode(), client->GetDeviceContext(),
-      client);
+      GetAPIKey(client->GetChannel()), server_url, context,
+      client->GetAccessTokenFetcher(), client->GetLocale(),
+      client->GetCountryCode(), client->GetDeviceContext(), client);
 }
 
 ServiceImpl::ServiceImpl(const std::string& api_key,
diff --git a/components/autofill_assistant/browser/service_impl_unittest.cc b/components/autofill_assistant/browser/service_impl_unittest.cc
index f5616a48..031ef051 100644
--- a/components/autofill_assistant/browser/service_impl_unittest.cc
+++ b/components/autofill_assistant/browser/service_impl_unittest.cc
@@ -19,9 +19,6 @@
 class ServiceImplTest : public ::testing::Test {
  public:
   ServiceImplTest() {
-    ON_CALL(mock_client_, GetServerUrl)
-        .WillByDefault(Return("https://www.google.com/"));
-
     service_impl_ = ServiceImpl::Create(nullptr, &mock_client_);
   }
 
diff --git a/components/autofill_assistant/browser/switches.cc b/components/autofill_assistant/browser/switches.cc
new file mode 100644
index 0000000..da90dfb8
--- /dev/null
+++ b/components/autofill_assistant/browser/switches.cc
@@ -0,0 +1,22 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/autofill_assistant/browser/switches.h"
+
+namespace autofill_assistant {
+namespace switches {
+
+// Sets the API key to be used instead of Chrome's default key when sending
+// requests to the backend.
+const char kAutofillAssistantServerKey[] = "autofill-assistant-key";
+
+// Overrides the default backend URL.
+const char kAutofillAssistantUrl[] = "autofill-assistant-url";
+
+// Disables authentication when set to false. This is only useful
+// during development, as prod instances require authentication.
+const char kAutofillAssistantAuth[] = "autofill-assistant-auth";
+
+}  // namespace switches
+}  // namespace autofill_assistant
diff --git a/components/autofill_assistant/browser/switches.h b/components/autofill_assistant/browser/switches.h
new file mode 100644
index 0000000..b966fbc2
--- /dev/null
+++ b/components/autofill_assistant/browser/switches.h
@@ -0,0 +1,18 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_SWITCHES_H_
+#define COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_SWITCHES_H_
+
+namespace autofill_assistant {
+namespace switches {
+
+extern const char kAutofillAssistantServerKey[];
+extern const char kAutofillAssistantUrl[];
+extern const char kAutofillAssistantAuth[];
+
+}  // namespace switches
+}  // namespace autofill_assistant
+
+#endif  // COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_SWITCHES_H_
diff --git a/components/external_intents/android/java/src/org/chromium/components/external_intents/ExternalNavigationHandler.java b/components/external_intents/android/java/src/org/chromium/components/external_intents/ExternalNavigationHandler.java
index db9e57a..661b9c2 100644
--- a/components/external_intents/android/java/src/org/chromium/components/external_intents/ExternalNavigationHandler.java
+++ b/components/external_intents/android/java/src/org/chromium/components/external_intents/ExternalNavigationHandler.java
@@ -48,7 +48,7 @@
     private static final String TAG = "UrlHandler";
 
     // Enables debug logging on a local build.
-    private static final boolean DEBUG = false;
+    private static final boolean DEBUG = true;
 
     private static final String WTAI_URL_PREFIX = "wtai://wp/";
     private static final String WTAI_MC_URL_PREFIX = "wtai://wp/mc;";
diff --git a/components/gcm_driver/gcm_driver_desktop.cc b/components/gcm_driver/gcm_driver_desktop.cc
index 5b0de0c..d4e32d8 100644
--- a/components/gcm_driver/gcm_driver_desktop.cc
+++ b/components/gcm_driver/gcm_driver_desktop.cc
@@ -1064,7 +1064,6 @@
   // codes, the instance ID will assume no current ID and generate a new one
   // if the gcm client is not ready and we pass an empty string to the callback
   // below. We should fix this!
-  UMA_HISTOGRAM_ENUMERATION("GCM.GetInstanceIDData.ClientStarted", result);
   if (result != GCMClient::SUCCESS) {
     DLOG(ERROR)
         << "Unable to get the InstanceID data: cannot start the GCM Client";
diff --git a/components/password_manager/core/browser/password_generation_manager.cc b/components/password_manager/core/browser/password_generation_manager.cc
index 93622ac8..8dcabc5 100644
--- a/components/password_manager/core/browser/password_generation_manager.cc
+++ b/components/password_manager/core/browser/password_generation_manager.cc
@@ -19,8 +19,6 @@
 namespace {
 
 using autofill::PasswordForm;
-using metrics_util::GenerationPresaveConflict;
-using metrics_util::LogGenerationPresaveConflict;
 
 std::vector<PasswordForm> DeepCopyVector(
     const std::vector<const PasswordForm*>& forms) {
@@ -132,7 +130,6 @@
 }
 
 void PasswordDataForUI::Save() {
-  LogPresavedUpdateUIDismissalReason(metrics_util::CLICKED_SAVE);
   bubble_interaction_cb_.Run(true, pending_form_);
 }
 
@@ -152,17 +149,14 @@
 }
 
 void PasswordDataForUI::OnNopeUpdateClicked() {
-  LogPresavedUpdateUIDismissalReason(metrics_util::CLICKED_CANCEL);
   bubble_interaction_cb_.Run(false, pending_form_);
 }
 
 void PasswordDataForUI::OnNeverClicked() {
-  LogPresavedUpdateUIDismissalReason(metrics_util::CLICKED_NEVER);
   bubble_interaction_cb_.Run(false, pending_form_);
 }
 
 void PasswordDataForUI::OnNoInteraction(bool is_update) {
-  LogPresavedUpdateUIDismissalReason(metrics_util::NO_DIRECT_INTERACTION);
   bubble_interaction_cb_.Run(false, pending_form_);
 }
 
@@ -209,8 +203,6 @@
     const PasswordForm* conflict =
         FindUsernameConflict(generated, non_federated_matches);
     if (conflict) {
-      LogGenerationPresaveConflict(
-          GenerationPresaveConflict::kConflictWithEmptyUsername);
       auto bubble_launcher = std::make_unique<PasswordDataForUI>(
           std::move(generated), non_federated_matches, federated_matches,
           base::BindRepeating(&PasswordGenerationManager::OnPresaveBubbleResult,
@@ -218,13 +210,7 @@
       client_->PromptUserToSaveOrUpdatePassword(std::move(bubble_launcher),
                                                 true);
       return;
-    } else {
-      LogGenerationPresaveConflict(
-          GenerationPresaveConflict::kNoConflictWithEmptyUsername);
     }
-  } else {
-    LogGenerationPresaveConflict(
-        GenerationPresaveConflict::kNoUsernameConflict);
   }
   driver->GeneratedPasswordAccepted(generated.password_value);
 }
diff --git a/components/password_manager/core/browser/password_generation_manager_unittest.cc b/components/password_manager/core/browser/password_generation_manager_unittest.cc
index 0283127a..d3713c1 100644
--- a/components/password_manager/core/browser/password_generation_manager_unittest.cc
+++ b/components/password_manager/core/browser/password_generation_manager_unittest.cc
@@ -6,7 +6,6 @@
 
 #include "base/memory/scoped_refptr.h"
 #include "base/strings/utf_string_conversions.h"
-#include "base/test/metrics/histogram_tester.h"
 #include "base/test/simple_test_clock.h"
 #include "base/test/task_environment.h"
 #include "components/password_manager/core/browser/fake_form_fetcher.h"
@@ -176,7 +175,6 @@
 // Check that accepting a generated password simply relays the message to the
 // driver.
 TEST_F(PasswordGenerationManagerTest, GeneratedPasswordAccepted_EmptyStore) {
-  base::HistogramTester histogram_tester;
   PasswordForm generated = CreateGenerated();
   MockPasswordManagerDriver driver;
   FakeFormFetcher fetcher;
@@ -186,16 +184,12 @@
       std::move(generated), fetcher.GetNonFederatedMatches(),
       fetcher.GetFederatedMatches(), driver.AsWeakPtr());
   EXPECT_FALSE(manager().HasGeneratedPassword());
-  histogram_tester.ExpectUniqueSample(
-      "PasswordGeneration.PresaveConflict",
-      metrics_util::GenerationPresaveConflict::kNoUsernameConflict, 1);
 }
 
 // In case of accepted password conflicts with an existing username the
 // credential can be presaved with an empty one. Thus, no conflict happens and
 // the driver should be notified directly.
 TEST_F(PasswordGenerationManagerTest, GeneratedPasswordAccepted_Conflict) {
-  base::HistogramTester histogram_tester;
   PasswordForm generated = CreateGenerated();
   const PasswordForm saved = CreateSaved();
   generated.username_value = saved.username_value;
@@ -208,13 +202,9 @@
       std::move(generated), fetcher.GetNonFederatedMatches(),
       fetcher.GetFederatedMatches(), driver.AsWeakPtr());
   EXPECT_FALSE(manager().HasGeneratedPassword());
-  histogram_tester.ExpectUniqueSample(
-      "PasswordGeneration.PresaveConflict",
-      metrics_util::GenerationPresaveConflict::kNoConflictWithEmptyUsername, 1);
 }
 
 TEST_F(PasswordGenerationManagerTest, GeneratedPasswordAccepted_UpdateUI) {
-  base::HistogramTester histogram_tester;
   MockPasswordManagerDriver driver;
   EXPECT_CALL(driver, GeneratedPasswordAccepted(_)).Times(0);
   std::unique_ptr<PasswordFormManagerForUI> ui_form =
@@ -231,9 +221,6 @@
             ui_form->GetPendingCredentials().password_value);
   EXPECT_THAT(ui_form->GetInteractionsStats(), IsEmpty());
   EXPECT_FALSE(ui_form->IsBlacklisted());
-  histogram_tester.ExpectUniqueSample(
-      "PasswordGeneration.PresaveConflict",
-      metrics_util::GenerationPresaveConflict::kConflictWithEmptyUsername, 1);
 }
 
 TEST_F(PasswordGenerationManagerTest,
diff --git a/components/password_manager/core/browser/password_manager_metrics_util.cc b/components/password_manager/core/browser/password_manager_metrics_util.cc
index b327c6b..5e479a1 100644
--- a/components/password_manager/core/browser/password_manager_metrics_util.cc
+++ b/components/password_manager/core/browser/password_manager_metrics_util.cc
@@ -43,12 +43,6 @@
                                 reason, NUM_UI_RESPONSES);
 }
 
-void LogPresavedUpdateUIDismissalReason(UIDismissalReason reason) {
-  base::UmaHistogramEnumeration(
-      "PasswordManager.PresavedUpdateUIDismissalReason", reason,
-      NUM_UI_RESPONSES);
-}
-
 void LogLeakDialogTypeAndDismissalReason(LeakDialogType type,
                                          LeakDialogDismissalReason reason) {
   static constexpr char kHistogram[] =
@@ -243,10 +237,6 @@
                             value);
 }
 
-void LogGenerationPresaveConflict(GenerationPresaveConflict value) {
-  base::UmaHistogramEnumeration("PasswordGeneration.PresaveConflict", value);
-}
-
 void LogGenerationDialogChoice(GenerationDialogChoice choice,
                                PasswordGenerationType type) {
   switch (type) {
diff --git a/components/password_manager/core/browser/password_manager_metrics_util.h b/components/password_manager/core/browser/password_manager_metrics_util.h
index 56edd89..c0d4b8f 100644
--- a/components/password_manager/core/browser/password_manager_metrics_util.h
+++ b/components/password_manager/core/browser/password_manager_metrics_util.h
@@ -433,18 +433,6 @@
   kMaxValue = kRejected
 };
 
-// Type of the conflict with existing credentials when starting password
-// generation.
-enum class GenerationPresaveConflict {
-  // Credential can be presaved as is.
-  kNoUsernameConflict = 0,
-  // Credential can be presaved without username.
-  kNoConflictWithEmptyUsername = 1,
-  // Credential should overwrite one without username.
-  kConflictWithEmptyUsername = 2,
-  kMaxValue = kConflictWithEmptyUsername
-};
-
 // Log the |reason| a user dismissed the password manager UI except save/update
 // bubbles.
 void LogGeneralUIDismissalReason(UIDismissalReason reason);
@@ -455,10 +443,6 @@
 // Log the |reason| a user dismissed the update password bubble.
 void LogUpdateUIDismissalReason(UIDismissalReason reason);
 
-// Log the |reason| a user dismissed the update password bubble when resolving a
-// conflict during generation.
-void LogPresavedUpdateUIDismissalReason(UIDismissalReason reason);
-
 // Log the |type| of a leak dialog shown to the user and the |reason| why it was
 // dismissed.
 void LogLeakDialogTypeAndDismissalReason(LeakDialogType type,
@@ -565,10 +549,6 @@
     GenerationDialogChoice choice,
     autofill::password_generation::PasswordGenerationType type);
 
-// Log whether there is a conflict with existing credentials when presaving
-// a generated password.
-void LogGenerationPresaveConflict(GenerationPresaveConflict value);
-
 #if defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED)
 // Log a save gaia password change event.
 void LogGaiaPasswordHashChange(GaiaPasswordHashChange event,
diff --git a/components/policy/proto/device_management_backend.proto b/components/policy/proto/device_management_backend.proto
index e40655d..2e0a147 100644
--- a/components/policy/proto/device_management_backend.proto
+++ b/components/policy/proto/device_management_backend.proto
@@ -283,6 +283,9 @@
     // to access Google Docs.
     // Please see go/cros-demo-mode and go/demo-mode-account-brainstorm.
     CHROME_OS_DEMO_MODE = 2;
+    // Authcode will be used by the enterprise-managed Chrome Browser to
+    // register for policy invalidations. This is requested during enrollment.
+    CHROME_BROWSER = 3;
   }
 
   // Device type indicates the intended use of the auth code.
diff --git a/components/viz/common/features.cc b/components/viz/common/features.cc
index f9c1cf8..86507b0 100644
--- a/components/viz/common/features.cc
+++ b/components/viz/common/features.cc
@@ -65,6 +65,10 @@
 const base::Feature kUsePreferredIntervalForVideo{
     "UsePreferredIntervalForVideo", base::FEATURE_DISABLED_BY_DEFAULT};
 
+// Whether we should log extra debug information to webrtc native log.
+const base::Feature kWebRtcLogCapturePipeline{
+    "WebRtcLogCapturePipeline", base::FEATURE_DISABLED_BY_DEFAULT};
+
 bool IsVizHitTestingDebugEnabled() {
   return base::CommandLine::ForCurrentProcess()->HasSwitch(
       switches::kEnableVizHitTestDebug);
@@ -137,4 +141,8 @@
   return base::FeatureList::IsEnabled(kSplitPartiallyOccludedQuads);
 }
 
+bool ShouldWebRtcLogCapturePipeline() {
+  return base::FeatureList::IsEnabled(kWebRtcLogCapturePipeline);
+}
+
 }  // namespace features
diff --git a/components/viz/common/features.h b/components/viz/common/features.h
index c66194b..0b38e70 100644
--- a/components/viz/common/features.h
+++ b/components/viz/common/features.h
@@ -24,6 +24,7 @@
 VIZ_COMMON_EXPORT extern const base::Feature kUsePreferredIntervalForVideo;
 VIZ_COMMON_EXPORT extern const base::Feature kUseRealBuffersForPageFlipTest;
 VIZ_COMMON_EXPORT extern const base::Feature kSplitPartiallyOccludedQuads;
+VIZ_COMMON_EXPORT extern const base::Feature kWebRtcLogCapturePipeline;
 
 VIZ_COMMON_EXPORT bool IsVizHitTestingDebugEnabled();
 VIZ_COMMON_EXPORT bool IsUsingSkiaForGLReadback();
@@ -37,6 +38,7 @@
 VIZ_COMMON_EXPORT bool IsUsingPreferredIntervalForVideo();
 VIZ_COMMON_EXPORT bool ShouldUseRealBuffersForPageFlipTest();
 VIZ_COMMON_EXPORT bool ShouldSplitPartiallyOccludedQuads();
+VIZ_COMMON_EXPORT bool ShouldWebRtcLogCapturePipeline();
 
 }  // namespace features
 
diff --git a/components/viz/host/client_frame_sink_video_capturer.cc b/components/viz/host/client_frame_sink_video_capturer.cc
index c6d041e..b77d91d2 100644
--- a/components/viz/host/client_frame_sink_video_capturer.cc
+++ b/components/viz/host/client_frame_sink_video_capturer.cc
@@ -157,6 +157,12 @@
                              std::move(callbacks));
 }
 
+void ClientFrameSinkVideoCapturer::OnLog(const std::string& message) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  consumer_->OnLog(message);
+}
+
 void ClientFrameSinkVideoCapturer::OnStopped() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
diff --git a/components/viz/host/client_frame_sink_video_capturer.h b/components/viz/host/client_frame_sink_video_capturer.h
index 409edd4..471756d 100644
--- a/components/viz/host/client_frame_sink_video_capturer.h
+++ b/components/viz/host/client_frame_sink_video_capturer.h
@@ -123,7 +123,7 @@
       mojo::PendingRemote<mojom::FrameSinkVideoConsumerFrameCallbacks>
           callbacks) final;
   void OnStopped() final;
-
+  void OnLog(const std::string& message) final;
   // Establishes connection to FrameSinkVideoCapturer and sends the existing
   // configuration.
   void EstablishConnection();
diff --git a/components/viz/service/frame_sinks/frame_sink_manager_impl.cc b/components/viz/service/frame_sinks/frame_sink_manager_impl.cc
index e5c65b3..2ed4c9e 100644
--- a/components/viz/service/frame_sinks/frame_sink_manager_impl.cc
+++ b/components/viz/service/frame_sinks/frame_sink_manager_impl.cc
@@ -58,7 +58,8 @@
       hit_test_manager_(surface_manager()),
       restart_id_(params.restart_id),
       run_all_compositor_stages_before_draw_(
-          params.run_all_compositor_stages_before_draw) {
+          params.run_all_compositor_stages_before_draw),
+      log_capture_pipeline_in_webrtc_(params.log_capture_pipeline_in_webrtc) {
   surface_manager_.AddObserver(&hit_test_manager_);
   surface_manager_.AddObserver(this);
 }
@@ -273,7 +274,8 @@
   video_capturers_.emplace(std::make_unique<FrameSinkVideoCapturerImpl>(
       this, std::move(receiver),
       std::make_unique<media::VideoCaptureOracle>(
-          true /* enable_auto_throttling */)));
+          true /* enable_auto_throttling */),
+      log_capture_pipeline_in_webrtc_));
 }
 
 void FrameSinkManagerImpl::EvictSurfaces(
diff --git a/components/viz/service/frame_sinks/frame_sink_manager_impl.h b/components/viz/service/frame_sinks/frame_sink_manager_impl.h
index 14af688..71c3ff6 100644
--- a/components/viz/service/frame_sinks/frame_sink_manager_impl.h
+++ b/components/viz/service/frame_sinks/frame_sink_manager_impl.h
@@ -73,6 +73,7 @@
     OutputSurfaceProvider* output_surface_provider = nullptr;
     uint32_t restart_id = BeginFrameSource::kNotRestartableId;
     bool run_all_compositor_stages_before_draw = false;
+    bool log_capture_pipeline_in_webrtc = false;
   };
   explicit FrameSinkManagerImpl(const InitParams& params);
   // TODO(kylechar): Cleanup tests and remove this constructor.
@@ -296,6 +297,9 @@
   // Whether display scheduler should wait for all pipeline stages before draw.
   const bool run_all_compositor_stages_before_draw_;
 
+  // Whether capture pipeline should emit log messages to webrtc log.
+  const bool log_capture_pipeline_in_webrtc_;
+
   // Contains registered frame sink ids, debug labels and synchronization
   // labels. Map entries will be created when frame sink is registered and
   // destroyed when frame sink is invalidated.
diff --git a/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl.cc b/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl.cc
index 5ad9fc9..24770ba 100644
--- a/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl.cc
+++ b/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl.cc
@@ -10,6 +10,7 @@
 
 #include "base/bind.h"
 #include "base/bind_helpers.h"
+#include "base/callback.h"
 #include "base/memory/read_only_shared_memory_region.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/stl_util.h"
@@ -63,16 +64,21 @@
 FrameSinkVideoCapturerImpl::FrameSinkVideoCapturerImpl(
     FrameSinkVideoCapturerManager* frame_sink_manager,
     mojo::PendingReceiver<mojom::FrameSinkVideoCapturer> receiver,
-    std::unique_ptr<media::VideoCaptureOracle> oracle)
+    std::unique_ptr<media::VideoCaptureOracle> oracle,
+    bool log_to_webrtc)
     : frame_sink_manager_(frame_sink_manager),
       copy_request_source_(base::UnguessableToken::Create()),
       clock_(base::DefaultTickClock::GetInstance()),
       oracle_(std::move(oracle)),
       frame_pool_(kDesignLimitMaxFrames),
-      feedback_weak_factory_(oracle_.get()) {
+      feedback_weak_factory_(oracle_.get()),
+      log_to_webrtc_(log_to_webrtc) {
   DCHECK(frame_sink_manager_);
   DCHECK(oracle_);
-
+  if (log_to_webrtc_) {
+    oracle_->SetLogCallback(base::BindRepeating(
+        &FrameSinkVideoCapturerImpl::OnLog, base::Unretained(this)));
+  }
   // Instantiate a default base::OneShotTimer instance.
   refresh_frame_retry_timer_.emplace();
 
@@ -337,6 +343,11 @@
   if (source_size != oracle_->source_size()) {
     oracle_->SetSourceSize(source_size);
     InvalidateEntireSource();
+    if (log_to_webrtc_) {
+      consumer_->OnLog(
+          base::StringPrintf("VFC: RefreshSoon() changed active frame size: %s",
+                             source_size.ToString().c_str()));
+    }
   }
 
   MaybeCaptureFrame(VideoCaptureOracle::kRefreshRequest, gfx::Rect(),
@@ -360,6 +371,11 @@
   } else {
     oracle_->SetSourceSize(frame_size);
     InvalidateEntireSource();
+    if (log_to_webrtc_ && consumer_) {
+      consumer_->OnLog(
+          base::StringPrintf("VFC: OnFramedamaged() changed frame size: %s",
+                             frame_size.ToString().c_str()));
+    }
   }
 
   MaybeCaptureFrame(VideoCaptureOracle::kCompositorUpdate, damage_rect,
@@ -583,6 +599,30 @@
   // If the frame is a resurrected one, just deliver it since it already
   // contains the most up-to-date capture of the source content.
   if (can_resurrect_content) {
+    if (log_to_webrtc_) {
+      std::string strides = "";
+      switch (frame->format()) {
+        case media::PIXEL_FORMAT_I420:
+          strides = base::StringPrintf("strideY:%d StrideU:%d StrideV:%d",
+                                       frame->stride(VideoFrame::kYPlane),
+                                       frame->stride(VideoFrame::kUPlane),
+                                       frame->stride(VideoFrame::kVPlane));
+          break;
+        case media::PIXEL_FORMAT_ARGB:
+          strides = base::StringPrintf("strideRGBA:%d",
+                                       frame->stride(VideoFrame::kARGBPlane));
+          break;
+        default:
+          strides = "strides:???";
+      }
+      consumer_->OnLog(base::StringPrintf(
+          "VFC: Ressurecting frame format=%s frame_coded_size: %s "
+          "frame_visible_rect: %s frame_natural_size: %s %s",
+          VideoPixelFormatToString(frame->format()).c_str(),
+          frame->coded_size().ToString().c_str(),
+          frame->visible_rect().ToString().c_str(),
+          frame->natural_size().ToString().c_str(), strides.c_str()));
+    }
     OnFrameReadyForDelivery(capture_frame_number, oracle_frame_number,
                             content_rect, std::move(frame));
     return;
@@ -613,6 +653,20 @@
   // being captured.
   dirty_rect_ = gfx::Rect();
 
+  if (log_to_webrtc_) {
+    std::string format =
+        pixel_format_ == media::PIXEL_FORMAT_I420 ? "I420" : "RGBA_bitmap";
+    consumer_->OnLog(base::StringPrintf(
+        "VFC: Sending CopyRequest: "
+        "format=%s area:%s "
+        "scale_from: %s "
+        "scale_to: %s "
+        "frame pool utilization: %f",
+        format.c_str(), request->area().ToString().c_str(),
+        request->scale_from().ToString().c_str(),
+        request->scale_to().ToString().c_str(), utilization));
+  }
+
   resolved_target_->RequestCopyOfOutput(LocalSurfaceId(), std::move(request));
 }
 
@@ -630,6 +684,38 @@
   DCHECK(frame);
   DCHECK(result);
 
+  if (log_to_webrtc_ && consumer_) {
+    std::string format = "";
+    std::string strides = "";
+    switch (result->format()) {
+      case CopyOutputResult::Format::I420_PLANES:
+        format = "I420";
+        strides = base::StringPrintf("strideY:%d StrideU:%d StrideV:%d",
+                                     frame->stride(VideoFrame::kYPlane),
+                                     frame->stride(VideoFrame::kUPlane),
+                                     frame->stride(VideoFrame::kVPlane));
+        break;
+      case CopyOutputResult::Format::RGBA_BITMAP:
+        format = "RGBA_Bitmap";
+        strides = base::StringPrintf("strideRGBA:%d",
+                                     frame->stride(VideoFrame::kARGBPlane));
+        break;
+      case CopyOutputResult::Format::RGBA_TEXTURE:
+        format = "RGBA_Texture";
+        strides = base::StringPrintf("strideRGBA:%d",
+                                     frame->stride(VideoFrame::kARGBPlane));
+        break;
+    }
+    consumer_->OnLog(base::StringPrintf(
+        "VFC: got CopyOutputResult: format=%s size:%s frame_coded_size: %s "
+        "frame_visible_rect: %s frame_natural_size: %s content_rect: %s %s",
+        format.c_str(), result->size().ToString().c_str(),
+        frame->coded_size().ToString().c_str(),
+        frame->visible_rect().ToString().c_str(),
+        frame->natural_size().ToString().c_str(),
+        content_rect.ToString().c_str(), strides.c_str()));
+  }
+
   // Stop() should have canceled any outstanding copy requests. So, by reaching
   // this point, |consumer_| should be bound.
   DCHECK(consumer_);
@@ -840,6 +926,12 @@
   return gfx::Rect(x, y, r - x, b - y);
 }
 
+void FrameSinkVideoCapturerImpl::OnLog(const std::string& message) {
+  if (log_to_webrtc_ && consumer_) {
+    consumer_->OnLog(message);
+  }
+}
+
 FrameSinkVideoCapturerImpl::CapturedFrame::CapturedFrame(
     int64_t capture_frame_number,
     OracleFrameNumber oracle_frame_number,
diff --git a/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl.h b/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl.h
index 5efea91b..36db91ce 100644
--- a/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl.h
+++ b/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl.h
@@ -10,7 +10,7 @@
 #include <memory>
 #include <queue>
 #include <vector>
-
+#include "base/callback_forward.h"
 #include "base/containers/flat_map.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
@@ -79,7 +79,8 @@
   FrameSinkVideoCapturerImpl(
       FrameSinkVideoCapturerManager* frame_sink_manager,
       mojo::PendingReceiver<mojom::FrameSinkVideoCapturer> receiver,
-      std::unique_ptr<media::VideoCaptureOracle> oracle);
+      std::unique_ptr<media::VideoCaptureOracle> oracle,
+      bool log_to_webrtc);
 
   ~FrameSinkVideoCapturerImpl() final;
 
@@ -227,6 +228,8 @@
   // numbers.
   static gfx::Rect ExpandRectToI420SubsampleBoundaries(const gfx::Rect& rect);
 
+  void OnLog(const std::string& message);
+
   // Owner/Manager of this instance.
   FrameSinkVideoCapturerManager* const frame_sink_manager_;
 
@@ -335,6 +338,9 @@
   // in-flight frame deliveries.
   base::WeakPtrFactory<media::VideoCaptureOracle> feedback_weak_factory_;
 
+  // Enables debug log messages to be sent to webrtc native log.
+  const bool log_to_webrtc_;
+
   // A weak pointer factory used for cancelling the results from any in-flight
   // copy output requests.
   base::WeakPtrFactory<FrameSinkVideoCapturerImpl> capture_weak_factory_{this};
diff --git a/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl_unittest.cc b/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl_unittest.cc
index 153ae35..1e99e7b 100644
--- a/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl_unittest.cc
+++ b/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl_unittest.cc
@@ -124,6 +124,7 @@
 
   MOCK_METHOD0(OnFrameCapturedMock, void());
   MOCK_METHOD0(OnStopped, void());
+  MOCK_METHOD1(OnLog, void(const std::string&));
 
   int num_frames_received() const { return frames_.size(); }
 
@@ -379,7 +380,7 @@
         true /* enable_auto_throttling */);
     oracle_ = oracle.get();
     capturer_ = std::make_unique<FrameSinkVideoCapturerImpl>(
-        &frame_sink_manager_, mojo::NullReceiver(), std::move(oracle));
+        &frame_sink_manager_, mojo::NullReceiver(), std::move(oracle), false);
   }
 
   void SetUp() override {
diff --git a/components/viz/service/main/viz_compositor_thread_runner_impl.cc b/components/viz/service/main/viz_compositor_thread_runner_impl.cc
index 58a329b..e216d82 100644
--- a/components/viz/service/main/viz_compositor_thread_runner_impl.cc
+++ b/components/viz/service/main/viz_compositor_thread_runner_impl.cc
@@ -198,6 +198,8 @@
   init_params.restart_id = params->restart_id;
   init_params.run_all_compositor_stages_before_draw =
       run_all_compositor_stages_before_draw;
+  init_params.log_capture_pipeline_in_webrtc =
+      features::ShouldWebRtcLogCapturePipeline();
 
   frame_sink_manager_ = std::make_unique<FrameSinkManagerImpl>(init_params);
   frame_sink_manager_->BindAndSetClient(
diff --git a/content/browser/devtools/devtools_video_consumer.h b/content/browser/devtools/devtools_video_consumer.h
index aa86ec8..fe4ab8181 100644
--- a/content/browser/devtools/devtools_video_consumer.h
+++ b/content/browser/devtools/devtools_video_consumer.h
@@ -70,6 +70,7 @@
       mojo::PendingRemote<viz::mojom::FrameSinkVideoConsumerFrameCallbacks>
           callbacks) override;
   void OnStopped() override;
+  void OnLog(const std::string& /*message*/) override {}
 
   // Default min frame size is 1x1, as otherwise, nothing would be captured.
   static constexpr gfx::Size kDefaultMinFrameSize = gfx::Size(1, 1);
diff --git a/content/browser/media/capture/frame_sink_video_capture_device.cc b/content/browser/media/capture/frame_sink_video_capture_device.cc
index 2630aae..3c93ee9 100644
--- a/content/browser/media/capture/frame_sink_video_capture_device.cc
+++ b/content/browser/media/capture/frame_sink_video_capture_device.cc
@@ -263,6 +263,21 @@
   OnFatalError("Capturer service cannot continue.");
 }
 
+void FrameSinkVideoCaptureDevice::OnLog(const std::string& message) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  if (receiver_) {
+    if (BrowserThread::CurrentlyOn(BrowserThread::IO)) {
+      receiver_->OnLog(message);
+    } else {
+      base::PostTask(
+          FROM_HERE, {BrowserThread::IO},
+          base::BindOnce(&media::VideoFrameReceiver::OnLog,
+                         base::Unretained(receiver_.get()), message));
+    }
+  }
+}
+
 void FrameSinkVideoCaptureDevice::OnTargetChanged(
     const viz::FrameSinkId& frame_sink_id) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
diff --git a/content/browser/media/capture/frame_sink_video_capture_device.h b/content/browser/media/capture/frame_sink_video_capture_device.h
index ae98dcb..4508c09 100644
--- a/content/browser/media/capture/frame_sink_video_capture_device.h
+++ b/content/browser/media/capture/frame_sink_video_capture_device.h
@@ -82,6 +82,7 @@
       mojo::PendingRemote<viz::mojom::FrameSinkVideoConsumerFrameCallbacks>
           callbacks) final;
   void OnStopped() final;
+  void OnLog(const std::string& message) final;
 
   // These are called to notify when the capture target has changed or was
   // permanently lost.
diff --git a/content/browser/renderer_host/input/touch_selection_controller_client_aura.cc b/content/browser/renderer_host/input/touch_selection_controller_client_aura.cc
index 1d1d45a..99f3db5 100644
--- a/content/browser/renderer_host/input/touch_selection_controller_client_aura.cc
+++ b/content/browser/renderer_host/input/touch_selection_controller_client_aura.cc
@@ -19,6 +19,7 @@
 #include "ui/aura/env.h"
 #include "ui/aura/window.h"
 #include "ui/base/clipboard/clipboard.h"
+#include "ui/base/pointer/touch_editing_controller.h"
 #include "ui/events/event_observer.h"
 #include "ui/gfx/geometry/point_conversions.h"
 #include "ui/gfx/geometry/size_conversions.h"
@@ -433,11 +434,11 @@
   bool readable = rwhva_->GetTextInputType() != ui::TEXT_INPUT_TYPE_PASSWORD;
   bool has_selection = !rwhva_->GetSelectedText().empty();
   switch (command_id) {
-    case IDS_APP_CUT:
+    case ui::TouchEditable::kCut:
       return editable && readable && has_selection;
-    case IDS_APP_COPY:
+    case ui::TouchEditable::kCopy:
       return readable && has_selection;
-    case IDS_APP_PASTE: {
+    case ui::TouchEditable::kPaste: {
       base::string16 result;
       ui::Clipboard::GetForCurrentThread()->ReadText(
           ui::ClipboardBuffer::kCopyPaste, &result);
@@ -456,13 +457,13 @@
     return;
 
   switch (command_id) {
-    case IDS_APP_CUT:
+    case ui::TouchEditable::kCut:
       host_delegate->Cut();
       break;
-    case IDS_APP_COPY:
+    case ui::TouchEditable::kCopy:
       host_delegate->Copy();
       break;
-    case IDS_APP_PASTE:
+    case ui::TouchEditable::kPaste:
       host_delegate->Paste();
       break;
     default:
diff --git a/content/browser/renderer_host/input/touch_selection_controller_client_child_frame.cc b/content/browser/renderer_host/input/touch_selection_controller_client_child_frame.cc
index 49db289c..6692731 100644
--- a/content/browser/renderer_host/input/touch_selection_controller_client_child_frame.cc
+++ b/content/browser/renderer_host/input/touch_selection_controller_client_child_frame.cc
@@ -12,6 +12,7 @@
 #include "content/public/browser/touch_selection_controller_client_manager.h"
 #include "content/public/common/use_zoom_for_dsf_policy.h"
 #include "ui/base/clipboard/clipboard.h"
+#include "ui/base/pointer/touch_editing_controller.h"
 #include "ui/gfx/geometry/point_conversions.h"
 #include "ui/strings/grit/ui_strings.h"
 
@@ -150,11 +151,11 @@
 
   bool has_selection = !rwhv_->GetSelectedText().empty();
   switch (command_id) {
-    case IDS_APP_CUT:
+    case ui::TouchEditable::kCut:
       return editable && readable && has_selection;
-    case IDS_APP_COPY:
+    case ui::TouchEditable::kCopy:
       return readable && has_selection;
-    case IDS_APP_PASTE: {
+    case ui::TouchEditable::kPaste: {
       base::string16 result;
       ui::Clipboard::GetForCurrentThread()->ReadText(
           ui::ClipboardBuffer::kCopyPaste, &result);
@@ -174,13 +175,13 @@
     return;
 
   switch (command_id) {
-    case IDS_APP_CUT:
+    case ui::TouchEditable::kCut:
       host_delegate->Cut();
       break;
-    case IDS_APP_COPY:
+    case ui::TouchEditable::kCopy:
       host_delegate->Copy();
       break;
-    case IDS_APP_PASTE:
+    case ui::TouchEditable::kPaste:
       host_delegate->Paste();
       break;
     default:
diff --git a/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt
index 2b598bc..65a89e6 100644
--- a/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt
@@ -732,6 +732,7 @@
 crbug.com/906742 [ android qualcomm no-passthrough ] conformance2/glsl3/compare-structs-containing-arrays.html [ Failure ]
 crbug.com/1008535 [ android qualcomm-adreno-(tm)-540 passthrough ] conformance2/textures/misc/tex-image-with-bad-args-from-dom-elements.html [ Skip ]
 crbug.com/1000354 [ android qualcomm-adreno-(tm)-540 passthrough ] conformance2/reading/read-pixels-from-fbo-test.html [ Failure ]
+crbug.com/951628 [ android qualcomm-adreno-(tm)-540 passthrough ] conformance/rendering/blending.html [ Failure ]
 crbug.com/981216 [ android qualcomm-adreno-(tm)-540 ] deqp/functional/gles3/fbocolorbuffer/tex2d_01.html [ RetryOnFailure ]
 crbug.com/949321 [ android qualcomm ] deqp/functional/gles3/framebufferblit/default_framebuffer_02.html [ RetryOnFailure ]
 crbug.com/1022410 [ android qualcomm ] conformance2/transform_feedback/switching-objects.html [ RetryOnFailure ]
diff --git a/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt
index 8544745..7d674a9 100644
--- a/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt
@@ -200,6 +200,7 @@
 crbug.com/750896 [ win10 nvidia-0x1cb3 d3d9 ] conformance/textures/image_bitmap_from_video/* [ RetryOnFailure ]
 crbug.com/750896 [ win10 nvidia-0x1cb3 d3d9 ] conformance/textures/video/* [ RetryOnFailure ]
 crbug.com/829389 [ win10 nvidia-0x1cb3 d3d9 passthrough ] conformance/uniforms/uniform-samplers-test.html [ RetryOnFailure ]
+crbug.com/angleproject/4539 [ win10 nvidia-0x1cb3 d3d9 passthrough ] conformance/extensions/oes-texture-half-float-linear.html [ RetryOnFailure ]
 
 # Win10 / NVIDIA Quadro P400 failures
 crbug.com/728670 [ win10 nvidia-0x1cb3 ] conformance/textures/image_bitmap_from_video/tex-2d-rgba-rgba-unsigned_short_5_5_5_1.html [ RetryOnFailure ]
diff --git a/fuchsia/runners/cast/cast_runner.cmx b/fuchsia/runners/cast/cast_runner.cmx
index 987ad94..43e4234 100644
--- a/fuchsia/runners/cast/cast_runner.cmx
+++ b/fuchsia/runners/cast/cast_runner.cmx
@@ -3,7 +3,6 @@
     "features": [
       "config-data"
     ],
-    "$comment": "Not all services are passed to WebEngine, see cast_runner.cc",
     "services": [
       "chromium.cast.ApplicationConfigManager",
       "fuchsia.accessibility.semantics.SemanticsManager",
diff --git a/ios/chrome/app/application_delegate/app_state.h b/ios/chrome/app/application_delegate/app_state.h
index 37aeaa46..86fa7a1f 100644
--- a/ios/chrome/app/application_delegate/app_state.h
+++ b/ios/chrome/app/application_delegate/app_state.h
@@ -96,6 +96,9 @@
 // -performActionForShortcutItem or -openURL.
 - (void)launchFromURLHandled:(BOOL)URLHandled;
 
+// Returns the foreground and active scene, if there is one.
+- (SceneState*)foregroundActiveScene;
+
 // Returns a list of all connected scenes.
 - (NSArray<SceneState*>*)connectedScenes;
 
diff --git a/ios/chrome/app/application_delegate/app_state.mm b/ios/chrome/app/application_delegate/app_state.mm
index 100a5d65..8a391afb 100644
--- a/ios/chrome/app/application_delegate/app_state.mm
+++ b/ios/chrome/app/application_delegate/app_state.mm
@@ -505,6 +505,16 @@
 
 #pragma mark - Multiwindow-related
 
+- (SceneState*)foregroundActiveScene {
+  for (SceneState* sceneState in self.connectedScenes) {
+    if (sceneState.activationLevel == SceneActivationLevelForegroundActive) {
+      return sceneState;
+    }
+  }
+
+  return nil;
+}
+
 - (NSArray<SceneState*>*)connectedScenes {
   if (IsMultiwindowSupported()) {
     NSMutableArray* sceneStates = [[NSMutableArray alloc] init];
diff --git a/ios/chrome/app/main_application_delegate.mm b/ios/chrome/app/main_application_delegate.mm
index 0e1d2e1..1da1713 100644
--- a/ios/chrome/app/main_application_delegate.mm
+++ b/ios/chrome/app/main_application_delegate.mm
@@ -374,6 +374,12 @@
   return _appState;
 }
 
++ (AppState*)sharedAppState {
+  return base::mac::ObjCCast<MainApplicationDelegate>(
+             [[UIApplication sharedApplication] delegate])
+      .appState;
+}
+
 + (MainController*)sharedMainController {
   return base::mac::ObjCCast<MainApplicationDelegate>(
              [[UIApplication sharedApplication] delegate])
diff --git a/ios/chrome/app/main_application_delegate_testing.h b/ios/chrome/app/main_application_delegate_testing.h
index 0da219e..42b521f 100644
--- a/ios/chrome/app/main_application_delegate_testing.h
+++ b/ios/chrome/app/main_application_delegate_testing.h
@@ -15,6 +15,7 @@
 @property(nonatomic, readonly) AppState* appState;
 
 + (MainController*)sharedMainController;
++ (AppState*)sharedAppState;
 
 @end
 #endif  // IOS_CHROME_APP_MAIN_APPLICATION_DELEGATE_TESTING_H_
diff --git a/ios/chrome/app/main_controller.mm b/ios/chrome/app/main_controller.mm
index 1677aca..3b3cece8 100644
--- a/ios/chrome/app/main_controller.mm
+++ b/ios/chrome/app/main_controller.mm
@@ -1244,24 +1244,6 @@
 
 @implementation MainController (TestingOnly)
 
-- (BOOL)tabSwitcherActive {
-  return self.sceneController.isTabSwitcherActive;
-}
-
-- (void)dismissModalDialogsWithCompletion:(ProceduralBlock)completion
-                           dismissOmnibox:(BOOL)dismissOmnibox {
-  [self.sceneController dismissModalDialogsWithCompletion:completion
-                                           dismissOmnibox:dismissOmnibox];
-}
-
-- (void)setTabSwitcher:(id<TabSwitcher>)switcher {
-  [self.sceneController setTabSwitcher:switcher];
-}
-
-- (id<TabSwitcher>)tabSwitcher {
-  return self.sceneController.tabSwitcher;
-}
-
 - (void)setStartupParametersWithURL:(const GURL&)launchURL {
   NSString* sourceApplication = @"Fake App";
   self.startupParameters = [ChromeAppStartupParameters
@@ -1269,9 +1251,4 @@
                      fromSourceApplication:sourceApplication];
 }
 
-// Defined in FirstRunAppInterface for EGTests.
-- (void)showFirstRunUI {
-  [self.sceneController showFirstRunUI];
-}
-
 @end
diff --git a/ios/chrome/app/main_controller_private.h b/ios/chrome/app/main_controller_private.h
index 745505d..15502522 100644
--- a/ios/chrome/app/main_controller_private.h
+++ b/ios/chrome/app/main_controller_private.h
@@ -25,20 +25,10 @@
 // Methods that only exist for tests.
 @interface MainController (TestingOnly)
 
-// Tab switcher state.
-- (BOOL)tabSwitcherActive;
-
-@property(nonatomic, strong) id<TabSwitcher> tabSwitcher;
-
 // Sets the internal startup state to indicate that the launch was triggered
 // by an external app opening the given URL.
 - (void)setStartupParametersWithURL:(const GURL&)launchURL;
 
-// Dismisses all modal dialogs, excluding the omnibox if |dismissOmnibox| is
-// NO, then call |completion|.
-- (void)dismissModalDialogsWithCompletion:(ProceduralBlock)completion
-                           dismissOmnibox:(BOOL)dismissOmnibox;
-
 @end
 
 #endif  // IOS_CHROME_APP_MAIN_CONTROLLER_PRIVATE_H_
diff --git a/ios/chrome/browser/ui/first_run/BUILD.gn b/ios/chrome/browser/ui/first_run/BUILD.gn
index e1adf9b..639ce62 100644
--- a/ios/chrome/browser/ui/first_run/BUILD.gn
+++ b/ios/chrome/browser/ui/first_run/BUILD.gn
@@ -158,6 +158,8 @@
     "//ios/chrome/app:app_internal",
     "//ios/chrome/browser",
     "//ios/chrome/browser/sync",
+    "//ios/chrome/browser/ui/main:scene",
+    "//ios/chrome/browser/ui/main:scene_testing",
     "//ios/chrome/test/app:test_support",
   ]
 }
@@ -180,6 +182,8 @@
     "//ios/chrome/app:app_internal",
     "//ios/chrome/browser",
     "//ios/chrome/browser/sync",
+    "//ios/chrome/browser/ui/main:scene",
+    "//ios/chrome/browser/ui/main:scene_testing",
     "//ios/chrome/test/app:test_support",
     "//ios/third_party/earl_grey2:app_framework+link",
   ]
diff --git a/ios/chrome/browser/ui/first_run/first_run_app_interface.mm b/ios/chrome/browser/ui/first_run/first_run_app_interface.mm
index c36755b..9b3e6edb 100644
--- a/ios/chrome/browser/ui/first_run/first_run_app_interface.mm
+++ b/ios/chrome/browser/ui/first_run/first_run_app_interface.mm
@@ -12,22 +12,18 @@
 #include "ios/chrome/browser/sync/sync_setup_service.h"
 #include "ios/chrome/browser/sync/sync_setup_service_factory.h"
 #include "ios/chrome/browser/ui/first_run/welcome_to_chrome_view_controller.h"
+#import "ios/chrome/browser/ui/main/scene_controller.h"
+#import "ios/chrome/browser/ui/main/scene_controller_testing.h"
 #import "ios/chrome/test/app/chrome_test_util.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
 #endif
 
-// TODO(crbug.com/1019942): Develop a better way to show the first run UI that
-// doesn't require exposing private API.
-@interface MainController (ExposedForTesting)
-- (void)showFirstRunUI;
-@end
-
 @implementation FirstRunAppInterface
 
 + (void)showFirstRunUI {
-  [chrome_test_util::GetMainController() showFirstRunUI];
+  [chrome_test_util::GetForegroundActiveSceneController() showFirstRunUI];
 }
 
 + (void)setUMACollectionEnabled:(BOOL)enabled {
diff --git a/ios/chrome/browser/ui/main/BUILD.gn b/ios/chrome/browser/ui/main/BUILD.gn
index ae656e1..e5eb7eed 100644
--- a/ios/chrome/browser/ui/main/BUILD.gn
+++ b/ios/chrome/browser/ui/main/BUILD.gn
@@ -4,6 +4,12 @@
 
 import("//ios/build/chrome_build.gni")
 
+source_set("scene_testing") {
+  sources = [ "scene_controller_testing.h" ]
+  deps = [ "//ios/chrome/browser:utils" ]
+  libs = [ "UIKit.framework" ]
+}
+
 source_set("scene_guts") {
   sources = [ "scene_controller_guts.h" ]
   deps = [
@@ -31,6 +37,7 @@
   deps = [
     ":main",
     ":scene_guts",
+    ":scene_testing",
     "//base",
     "//components/signin/public/identity_manager",
     "//components/url_formatter",
diff --git a/ios/chrome/browser/ui/main/scene_controller_guts.h b/ios/chrome/browser/ui/main/scene_controller_guts.h
index c99b14bb..bb202da 100644
--- a/ios/chrome/browser/ui/main/scene_controller_guts.h
+++ b/ios/chrome/browser/ui/main/scene_controller_guts.h
@@ -17,9 +17,6 @@
 
 @protocol SceneControllerGuts <WebStateListObserving>
 
-- (void)startUpChromeUIPostCrash:(BOOL)isPostCrashLaunch
-                 needRestoration:(BOOL)needsRestoration;
-
 - (void)dismissModalsAndOpenSelectedTabInMode:
             (ApplicationModeForTabOpening)targetMode
                             withUrlLoadParams:
@@ -27,17 +24,11 @@
                                dismissOmnibox:(BOOL)dismissOmnibox
                                    completion:(ProceduralBlock)completion;
 
-// Testing only.
-- (void)showFirstRunUI;
-- (void)setTabSwitcher:(id<TabSwitcher>)switcher;
-- (id<TabSwitcher>)tabSwitcher;
-- (BOOL)isTabSwitcherActive;
-
-- (void)dismissModalDialogsWithCompletion:(ProceduralBlock)completion
-                           dismissOmnibox:(BOOL)dismissOmnibox;
-
 #pragma mark - iOS 12 compat
 
+- (void)startUpChromeUIPostCrash:(BOOL)isPostCrashLaunch
+                 needRestoration:(BOOL)needsRestoration;
+
 // Method called on SceneController when the scene disconnects. Exposed here for
 // iOS 12 compatibility.
 - (void)teardownUI;
diff --git a/ios/chrome/browser/ui/main/scene_controller_testing.h b/ios/chrome/browser/ui/main/scene_controller_testing.h
new file mode 100644
index 0000000..e8522db
--- /dev/null
+++ b/ios/chrome/browser/ui/main/scene_controller_testing.h
@@ -0,0 +1,27 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_UI_MAIN_SCENE_CONTROLLER_TESTING_H_
+#define IOS_CHROME_BROWSER_UI_MAIN_SCENE_CONTROLLER_TESTING_H_
+
+#import "ios/chrome/browser/procedural_block_types.h"
+
+@protocol TabSwitcher;
+
+// Methods exposed for testing. This is terrible and should be rewritten.
+@interface SceneController ()
+
+- (void)showFirstRunUI;
+- (void)setTabSwitcher:(id<TabSwitcher>)switcher;
+- (id<TabSwitcher>)tabSwitcher;
+- (BOOL)isTabSwitcherActive;
+
+// Dismisses all modal dialogs, excluding the omnibox if |dismissOmnibox| is
+// NO, then call |completion|.
+- (void)dismissModalDialogsWithCompletion:(ProceduralBlock)completion
+                           dismissOmnibox:(BOOL)dismissOmnibox;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_UI_MAIN_SCENE_CONTROLLER_TESTING_H_
diff --git a/ios/chrome/credential_provider_extension/credential_provider_view_controller.mm b/ios/chrome/credential_provider_extension/credential_provider_view_controller.mm
index 09e17d1..e2abcd2 100644
--- a/ios/chrome/credential_provider_extension/credential_provider_view_controller.mm
+++ b/ios/chrome/credential_provider_extension/credential_provider_view_controller.mm
@@ -33,41 +33,44 @@
 
 - (void)prepareCredentialListForServiceIdentifiers:
     (NSArray<ASCredentialServiceIdentifier*>*)serviceIdentifiers {
-  self.listCoordinator = [[CredentialListCoordinator alloc]
-      initWithBaseViewController:self
-                         context:self.extensionContext
-              serviceIdentifiers:serviceIdentifiers];
-  [self.listCoordinator start];
-}
-
-- (void)viewDidAppear:(BOOL)animated {
-  [super viewDidAppear:animated];
-
-  // TODO(crbug.com/1045455): Remove, this is for testing purpose only.
   [self reauthenticateIfNeededWithCompletionHandler:^(
             ReauthenticationResult result) {
-    // TODO(crbug.com/1045455): Check result before cancelling.
-    [self.extensionContext
-        cancelRequestWithError:
-            [[NSError alloc]
-                initWithDomain:ASExtensionErrorDomain
-                          code:ASExtensionErrorCode::ASExtensionErrorCodeFailed
-                      userInfo:nil]];
+    if (result != ReauthenticationResult::kFailure) {
+      self.listCoordinator = [[CredentialListCoordinator alloc]
+          initWithBaseViewController:self
+                             context:self.extensionContext
+                  serviceIdentifiers:serviceIdentifiers];
+      [self.listCoordinator start];
+
+    } else {
+      [self.extensionContext
+          cancelRequestWithError:
+              [[NSError alloc] initWithDomain:ASExtensionErrorDomain
+                                         code:ASExtensionErrorCode::
+                                                  ASExtensionErrorCodeFailed
+                                     userInfo:nil]];
+    }
   }];
 }
 
 #pragma mark - Properties
 
 - (ReauthenticationHandler*)reauthenticationHandler {
-  if (!self.reauthenticationModule) {
-    self.reauthenticationModule = [[ReauthenticationModule alloc]
-        initWithSuccessfulReauthTimeAccessor:self];
-    self.reauthenticationHandler = [[ReauthenticationHandler alloc]
+  if (!_reauthenticationHandler) {
+    _reauthenticationHandler = [[ReauthenticationHandler alloc]
         initWithReauthenticationModule:self.reauthenticationModule];
   }
   return _reauthenticationHandler;
 }
 
+- (ReauthenticationModule*)reauthenticationModule {
+  if (!_reauthenticationModule) {
+    _reauthenticationModule = [[ReauthenticationModule alloc]
+        initWithSuccessfulReauthTimeAccessor:self];
+  }
+  return _reauthenticationModule;
+}
+
 #pragma mark - Private
 
 - (void)reauthenticateIfNeededWithCompletionHandler:
diff --git a/ios/chrome/test/app/BUILD.gn b/ios/chrome/test/app/BUILD.gn
index 144bb626..0289675 100644
--- a/ios/chrome/test/app/BUILD.gn
+++ b/ios/chrome/test/app/BUILD.gn
@@ -72,6 +72,8 @@
     "//ios/chrome/browser/ui/browser_view",
     "//ios/chrome/browser/ui/commands",
     "//ios/chrome/browser/ui/main",
+    "//ios/chrome/browser/ui/main:scene",
+    "//ios/chrome/browser/ui/main:scene_testing",
     "//ios/chrome/browser/ui/settings",
     "//ios/chrome/browser/ui/settings:settings_root",
     "//ios/chrome/browser/ui/settings/password",
diff --git a/ios/chrome/test/app/chrome_test_util.h b/ios/chrome/test/app/chrome_test_util.h
index ca860a4..ff0ccb9 100644
--- a/ios/chrome/test/app/chrome_test_util.h
+++ b/ios/chrome/test/app/chrome_test_util.h
@@ -14,6 +14,8 @@
 class ChromeBrowserState;
 @class MainController;
 @class NewTabPageController;
+@class SceneController;
+@class SceneState;
 @class UIViewController;
 
 namespace chrome_test_util {
@@ -21,6 +23,12 @@
 // Returns the main controller.
 MainController* GetMainController();
 
+// Returns the foreground, active scene.
+SceneState* GetForegroundActiveScene();
+
+// Returns the foreground, active scene controller.
+SceneController* GetForegroundActiveSceneController();
+
 // Returns the current, non-incognito ChromeBrowserState.
 ChromeBrowserState* GetOriginalBrowserState();
 
diff --git a/ios/chrome/test/app/chrome_test_util.mm b/ios/chrome/test/app/chrome_test_util.mm
index f41e916..7223e45 100644
--- a/ios/chrome/test/app/chrome_test_util.mm
+++ b/ios/chrome/test/app/chrome_test_util.mm
@@ -9,6 +9,7 @@
 #import "base/test/ios/wait_util.h"
 #include "components/metrics/metrics_pref_names.h"
 #include "components/metrics/metrics_service.h"
+#import "ios/chrome/app/application_delegate/app_state.h"
 #import "ios/chrome/app/application_delegate/metrics_mediator.h"
 #import "ios/chrome/app/application_delegate/metrics_mediator_testing.h"
 #import "ios/chrome/app/chrome_overlay_window.h"
@@ -24,6 +25,9 @@
 #import "ios/chrome/browser/metrics/previous_session_info_private.h"
 #import "ios/chrome/browser/ui/browser_view/browser_view_controller.h"
 #import "ios/chrome/browser/ui/main/bvc_container_view_controller.h"
+#import "ios/chrome/browser/ui/main/scene_controller.h"
+#import "ios/chrome/browser/ui/main/scene_controller_testing.h"
+#import "ios/chrome/browser/ui/main/scene_state.h"
 #include "ios/chrome/browser/ui/util/ui_util.h"
 #import "ios/chrome/test/app/tab_test_util.h"
 #import "ios/web/public/navigation/navigation_context.h"
@@ -78,6 +82,15 @@
   return [MainApplicationDelegate sharedMainController];
 }
 
+SceneState* GetForegroundActiveScene() {
+  return MainApplicationDelegate.sharedAppState.foregroundActiveScene;
+}
+
+SceneController* GetForegroundActiveSceneController() {
+  return MainApplicationDelegate.sharedAppState.foregroundActiveScene
+      .controller;
+}
+
 ChromeBrowserState* GetOriginalBrowserState() {
   return GetBrowserState(false);
 }
@@ -132,8 +145,8 @@
 }
 
 void ClearPresentedState() {
-  [GetMainController() dismissModalDialogsWithCompletion:nil
-                                          dismissOmnibox:YES];
+  [GetForegroundActiveSceneController() dismissModalDialogsWithCompletion:nil
+                                                           dismissOmnibox:YES];
 }
 
 void SetBooleanLocalStatePref(const char* pref_name, bool value) {
diff --git a/ios/chrome/test/app/tab_test_util.mm b/ios/chrome/test/app/tab_test_util.mm
index 6576a78..c56d18cc 100644
--- a/ios/chrome/test/app/tab_test_util.mm
+++ b/ios/chrome/test/app/tab_test_util.mm
@@ -17,6 +17,8 @@
 #import "ios/chrome/browser/tabs/tab_title_util.h"
 #import "ios/chrome/browser/ui/commands/browser_commands.h"
 #import "ios/chrome/browser/ui/commands/open_new_tab_command.h"
+#import "ios/chrome/browser/ui/main/scene_controller.h"
+#import "ios/chrome/browser/ui/main/scene_controller_testing.h"
 #import "ios/chrome/browser/ui/tab_grid/tab_switcher.h"
 #import "ios/chrome/browser/url_loading/url_loading_params.h"
 #import "ios/chrome/browser/web_state_list/web_state_list.h"
@@ -57,12 +59,12 @@
 void OpenNewTab() {
   @autoreleasepool {  // Make sure that all internals are deallocated.
     OpenNewTabCommand* command = [OpenNewTabCommand command];
-    if (GetMainController().tabSwitcherActive) {
+    if (GetForegroundActiveSceneController().isTabSwitcherActive) {
       // The TabGrid is currently presented.
       Browser* browser =
-          GetMainController().interfaceProvider.mainInterface.browser;
+          GetForegroundActiveScene().interfaceProvider.mainInterface.browser;
       UrlLoadParams params = UrlLoadParams::InNewTab(GURL(kChromeUINewTabURL));
-      [GetMainController().tabSwitcher
+      [GetForegroundActiveSceneController().tabSwitcher
           dismissWithNewTabAnimationToBrowser:browser
                             withUrlLoadParams:params
                                       atIndex:INT_MAX];
@@ -84,12 +86,12 @@
 void OpenNewIncognitoTab() {
   @autoreleasepool {  // Make sure that all internals are deallocated.
     OpenNewTabCommand* command = [OpenNewTabCommand incognitoTabCommand];
-    if (GetMainController().tabSwitcherActive) {
+    if (GetForegroundActiveSceneController().isTabSwitcherActive) {
       // The TabGrid is currently presented.
-      Browser* browser =
-          GetMainController().interfaceProvider.incognitoInterface.browser;
+      Browser* browser = GetForegroundActiveScene()
+                             .interfaceProvider.incognitoInterface.browser;
       UrlLoadParams params = UrlLoadParams::InNewTab(GURL(kChromeUINewTabURL));
-      [GetMainController().tabSwitcher
+      [GetForegroundActiveSceneController().tabSwitcher
           dismissWithNewTabAnimationToBrowser:browser
                             withUrlLoadParams:params
                                       atIndex:INT_MAX];
@@ -147,7 +149,7 @@
 }
 
 NSUInteger GetIndexOfActiveNormalTab() {
-  TabModel* model = chrome_test_util::GetMainController()
+  TabModel* model = chrome_test_util::GetForegroundActiveSceneController()
                         .interfaceProvider.mainInterface.tabModel;
   return model.webStateList->active_index();
 }
@@ -158,11 +160,12 @@
 
 void CloseAllTabs() {
   if (GetIncognitoTabCount()) {
-    [GetMainController()
+    [GetForegroundActiveSceneController()
             .interfaceProvider.incognitoInterface.tabModel closeAllTabs];
   }
   if (GetMainTabCount()) {
-    [GetMainController().interfaceProvider.mainInterface.tabModel closeAllTabs];
+    [GetForegroundActiveScene()
+            .interfaceProvider.mainInterface.tabModel closeAllTabs];
   }
 }
 
diff --git a/ios/web_view/BUILD.gn b/ios/web_view/BUILD.gn
index 45276a9..9bc2f49 100644
--- a/ios/web_view/BUILD.gn
+++ b/ios/web_view/BUILD.gn
@@ -2,7 +2,6 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-import("//build/buildflag_header.gni")
 import("//build/config/ios/ios_sdk.gni")
 import("//build/config/ios/rules.gni")
 import("//build/config/locales.gni")
@@ -15,15 +14,6 @@
 import("//testing/test.gni")
 import("//tools/grit/repack.gni")
 
-buildflag_header("web_view_buildflags") {
-  header = "cwv_web_view_buildflags.h"
-
-  flags = [
-    "IOS_WEB_VIEW_ENABLE_SYNC=$ios_web_view_enable_sync",
-    "IOS_WEB_VIEW_ENABLE_AUTOFILL=$ios_web_view_enable_autofill",
-  ]
-}
-
 group("all_tests") {
   testonly = true
   deps = [
@@ -44,22 +34,40 @@
 
 # These are defined as vars so they can be shared with different targets below.
 ios_web_view_public_headers = [
+  "public/cwv_autofill_controller.h",
+  "public/cwv_autofill_controller_delegate.h",
+  "public/cwv_autofill_data_manager.h",
+  "public/cwv_autofill_data_manager_observer.h",
+  "public/cwv_autofill_form.h",
+  "public/cwv_autofill_profile.h",
+  "public/cwv_autofill_suggestion.h",
   "public/cwv_back_forward_list.h",
   "public/cwv_back_forward_list_item.h",
+  "public/cwv_credit_card.h",
+  "public/cwv_credit_card_expiration_fixer.h",
+  "public/cwv_credit_card_name_fixer.h",
+  "public/cwv_credit_card_saver.h",
+  "public/cwv_credit_card_verifier.h",
   "public/cwv_download_task.h",
   "public/cwv_export.h",
   "public/cwv_favicon.h",
   "public/cwv_flags.h",
   "public/cwv_html_element.h",
+  "public/cwv_identity.h",
   "public/cwv_navigation_action.h",
   "public/cwv_navigation_delegate.h",
   "public/cwv_navigation_type.h",
+  "public/cwv_password.h",
   "public/cwv_preferences.h",
+  "public/cwv_preferences_autofill.h",
   "public/cwv_preview_element_info.h",
   "public/cwv_script_command.h",
   "public/cwv_scroll_view.h",
   "public/cwv_scroll_view_delegate.h",
   "public/cwv_ssl_status.h",
+  "public/cwv_sync_controller.h",
+  "public/cwv_sync_controller_data_source.h",
+  "public/cwv_sync_controller_delegate.h",
   "public/cwv_translation_controller.h",
   "public/cwv_translation_controller_delegate.h",
   "public/cwv_translation_language.h",
@@ -68,37 +76,11 @@
   "public/cwv_user_content_controller.h",
   "public/cwv_user_script.h",
   "public/cwv_web_view.h",
+  "public/cwv_web_view_autofill.h",
   "public/cwv_web_view_configuration.h",
+  "public/cwv_web_view_configuration_autofill.h",
+  "public/cwv_web_view_configuration_sync.h",
 ]
-if (ios_web_view_enable_sync) {
-  ios_web_view_public_headers += [
-    "public/cwv_identity.h",
-    "public/cwv_sync_controller.h",
-    "public/cwv_sync_controller_data_source.h",
-    "public/cwv_sync_controller_delegate.h",
-    "public/cwv_web_view_configuration_sync.h",
-  ]
-}
-if (ios_web_view_enable_autofill) {
-  ios_web_view_public_headers += [
-    "public/cwv_autofill_controller.h",
-    "public/cwv_autofill_controller_delegate.h",
-    "public/cwv_autofill_data_manager.h",
-    "public/cwv_autofill_data_manager_observer.h",
-    "public/cwv_autofill_form.h",
-    "public/cwv_autofill_profile.h",
-    "public/cwv_autofill_suggestion.h",
-    "public/cwv_credit_card.h",
-    "public/cwv_credit_card_expiration_fixer.h",
-    "public/cwv_credit_card_name_fixer.h",
-    "public/cwv_credit_card_saver.h",
-    "public/cwv_credit_card_verifier.h",
-    "public/cwv_password.h",
-    "public/cwv_preferences_autofill.h",
-    "public/cwv_web_view_autofill.h",
-    "public/cwv_web_view_configuration_autofill.h",
-  ]
-}
 
 source_set("web_view_sources") {
   sources = ios_web_view_public_headers
@@ -107,6 +89,27 @@
     "internal/app/application_context.mm",
     "internal/app/web_view_io_thread.h",
     "internal/app/web_view_io_thread.mm",
+    "internal/autofill/cwv_autofill_client_ios_bridge.h",
+    "internal/autofill/cwv_autofill_controller.mm",
+    "internal/autofill/cwv_autofill_controller_internal.h",
+    "internal/autofill/cwv_autofill_data_manager.mm",
+    "internal/autofill/cwv_autofill_data_manager_internal.h",
+    "internal/autofill/cwv_autofill_form.mm",
+    "internal/autofill/cwv_autofill_form_internal.h",
+    "internal/autofill/cwv_autofill_profile.mm",
+    "internal/autofill/cwv_autofill_profile_internal.h",
+    "internal/autofill/cwv_autofill_suggestion.mm",
+    "internal/autofill/cwv_autofill_suggestion_internal.h",
+    "internal/autofill/cwv_credit_card.mm",
+    "internal/autofill/cwv_credit_card_expiration_fixer.mm",
+    "internal/autofill/cwv_credit_card_expiration_fixer_internal.h",
+    "internal/autofill/cwv_credit_card_internal.h",
+    "internal/autofill/cwv_credit_card_name_fixer.mm",
+    "internal/autofill/cwv_credit_card_name_fixer_internal.h",
+    "internal/autofill/cwv_credit_card_saver.mm",
+    "internal/autofill/cwv_credit_card_saver_internal.h",
+    "internal/autofill/cwv_credit_card_verifier.mm",
+    "internal/autofill/cwv_credit_card_verifier_internal.h",
     "internal/autofill/web_view_autocomplete_history_manager_factory.h",
     "internal/autofill/web_view_autocomplete_history_manager_factory.mm",
     "internal/autofill/web_view_autofill_client_ios.h",
@@ -160,6 +163,10 @@
     "internal/language/web_view_language_model_manager_factory.mm",
     "internal/language/web_view_url_language_histogram_factory.h",
     "internal/language/web_view_url_language_histogram_factory.mm",
+    "internal/passwords/cwv_password.mm",
+    "internal/passwords/cwv_password_controller.h",
+    "internal/passwords/cwv_password_controller.mm",
+    "internal/passwords/cwv_password_internal.h",
     "internal/passwords/web_view_password_feature_manager.h",
     "internal/passwords/web_view_password_feature_manager.mm",
     "internal/passwords/web_view_password_manager_client.h",
@@ -172,6 +179,7 @@
     "internal/passwords/web_view_password_store_factory.mm",
     "internal/pref_names.h",
     "internal/pref_names.mm",
+    "internal/signin/cwv_identity.mm",
     "internal/signin/ios_web_view_signin_client.h",
     "internal/signin/ios_web_view_signin_client.mm",
     "internal/signin/web_view_device_accounts_provider_impl.h",
@@ -182,6 +190,8 @@
     "internal/signin/web_view_signin_client_factory.mm",
     "internal/signin/web_view_signin_error_controller_factory.h",
     "internal/signin/web_view_signin_error_controller_factory.mm",
+    "internal/sync/cwv_sync_controller.mm",
+    "internal/sync/cwv_sync_controller_internal.h",
     "internal/sync/web_view_device_info_sync_service_factory.h",
     "internal/sync/web_view_device_info_sync_service_factory.mm",
     "internal/sync/web_view_gcm_profile_service_factory.h",
@@ -233,46 +243,9 @@
     "internal/webdata_services/web_view_web_data_service_wrapper_factory.h",
     "internal/webdata_services/web_view_web_data_service_wrapper_factory.mm",
   ]
-  if (ios_web_view_enable_sync) {
-    sources += [
-      "internal/signin/cwv_identity.mm",
-      "internal/sync/cwv_sync_controller.mm",
-      "internal/sync/cwv_sync_controller_internal.h",
-    ]
-  }
-  if (ios_web_view_enable_autofill) {
-    sources += [
-      "internal/autofill/cwv_autofill_client_ios_bridge.h",
-      "internal/autofill/cwv_autofill_controller.mm",
-      "internal/autofill/cwv_autofill_controller_internal.h",
-      "internal/autofill/cwv_autofill_data_manager.mm",
-      "internal/autofill/cwv_autofill_data_manager_internal.h",
-      "internal/autofill/cwv_autofill_form.mm",
-      "internal/autofill/cwv_autofill_form_internal.h",
-      "internal/autofill/cwv_autofill_profile.mm",
-      "internal/autofill/cwv_autofill_profile_internal.h",
-      "internal/autofill/cwv_autofill_suggestion.mm",
-      "internal/autofill/cwv_autofill_suggestion_internal.h",
-      "internal/autofill/cwv_credit_card.mm",
-      "internal/autofill/cwv_credit_card_expiration_fixer.mm",
-      "internal/autofill/cwv_credit_card_expiration_fixer_internal.h",
-      "internal/autofill/cwv_credit_card_internal.h",
-      "internal/autofill/cwv_credit_card_name_fixer.mm",
-      "internal/autofill/cwv_credit_card_name_fixer_internal.h",
-      "internal/autofill/cwv_credit_card_saver.mm",
-      "internal/autofill/cwv_credit_card_saver_internal.h",
-      "internal/autofill/cwv_credit_card_verifier.mm",
-      "internal/autofill/cwv_credit_card_verifier_internal.h",
-      "internal/passwords/cwv_password.mm",
-      "internal/passwords/cwv_password_controller.h",
-      "internal/passwords/cwv_password_controller.mm",
-      "internal/passwords/cwv_password_internal.h",
-    ]
-  }
 
   public_deps = [
     ":web_view_all_frames",
-    ":web_view_buildflags",
     ":web_view_main_frame",
     ":web_view_resources",
     "//base",
@@ -349,12 +322,7 @@
   ]
 }
 
-# Generate the umbrella header to exclude headers for disabled features.
-# This is preferable to guarding header imports using flags defined in
-# :web_view_buildflags because:
-# 1. Confusing to see headers for unsupported features in umbrella header.
-# 2. A need to include cwv_web_view_buildflags.h as part of public headers and
-#    any headers it itself includes, despite not being part of the public API.
+# Generate the umbrella header.
 action("web_view_umbrella_header") {
   script = "//build/config/ios/generate_umbrella_header.py"
 
@@ -555,10 +523,8 @@
   deps = [
     ":repack_locales",
     ":repack_resources",
+    ":repack_scalable_resources",
   ]
-  if (ios_web_view_enable_autofill) {
-    deps += [ ":repack_scalable_resources" ]
-  }
 }
 
 if (additional_toolchains == [] || current_toolchain == default_toolchain) {
diff --git a/ios/web_view/features.gni b/ios/web_view/features.gni
index 7db5fe0c..6d731811 100644
--- a/ios/web_view/features.gni
+++ b/ios/web_view/features.gni
@@ -6,12 +6,6 @@
   # Controls if cronet is included.
   ios_web_view_include_cronet = true
 
-  # Controls if sync APIs are exposed.
-  ios_web_view_enable_sync = true
-
-  # Controls if autofill APIs are exposed.
-  ios_web_view_enable_autofill = true
-
   # Controls the output name of the built framework.
   ios_web_view_output_name = "ChromeWebView"
 }
diff --git a/ios/web_view/internal/app/application_context.mm b/ios/web_view/internal/app/application_context.mm
index eaefa7d..f8d251d9 100644
--- a/ios/web_view/internal/app/application_context.mm
+++ b/ios/web_view/internal/app/application_context.mm
@@ -15,11 +15,11 @@
 #include "components/prefs/pref_service.h"
 #include "components/prefs/pref_service_factory.h"
 #include "components/proxy_config/pref_proxy_config_tracker_impl.h"
+#include "components/signin/public/identity_manager/identity_manager.h"
 #include "components/translate/core/browser/translate_download_manager.h"
 #include "components/variations/net/variations_http_headers.h"
 #include "ios/web/public/thread/web_task_traits.h"
 #include "ios/web/public/thread/web_thread.h"
-#include "ios/web_view/cwv_web_view_buildflags.h"
 #include "ios/web_view/internal/app/web_view_io_thread.h"
 #import "ios/web_view/internal/cwv_flags_internal.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
@@ -31,10 +31,6 @@
 #include "services/network/public/mojom/network_context.mojom.h"
 #include "ui/base/l10n/l10n_util_mac.h"
 
-#if BUILDFLAG(IOS_WEB_VIEW_ENABLE_SYNC)
-#include "components/signin/public/identity_manager/identity_manager.h"
-#endif
-
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
 #endif
@@ -102,9 +98,7 @@
     scoped_refptr<PrefRegistrySimple> pref_registry(new PrefRegistrySimple);
     flags_ui::PrefServiceFlagsStorage::RegisterPrefs(pref_registry.get());
     PrefProxyConfigTrackerImpl::RegisterPrefs(pref_registry.get());
-#if BUILDFLAG(IOS_WEB_VIEW_ENABLE_SYNC)
     signin::IdentityManager::RegisterLocalStatePrefs(pref_registry.get());
-#endif  // BUILDFLAG(IOS_WEB_VIEW_ENABLE_SYNC)
 
     base::FilePath local_state_path;
     base::PathService::Get(base::DIR_APP_DATA, &local_state_path);
diff --git a/ios/web_view/internal/autofill/cwv_autofill_client_ios_bridge.h b/ios/web_view/internal/autofill/cwv_autofill_client_ios_bridge.h
index 63d92ea..5cfcad1 100644
--- a/ios/web_view/internal/autofill/cwv_autofill_client_ios_bridge.h
+++ b/ios/web_view/internal/autofill/cwv_autofill_client_ios_bridge.h
@@ -47,6 +47,20 @@
                             callback:(autofill::AutofillClient::
                                           UploadSaveCardPromptCallback)callback;
 
+// Bridge For AutofillClient's method |ConfirmAccountNameFixFlow|.
+- (void)
+    confirmCreditCardAccountName:(const base::string16&)name
+                        callback:
+                            (base::OnceCallback<void(const base::string16&)>)
+                                callback;
+
+// Bridge For AutofillClient's method |ConfirmExpirationDateFixFlow|.
+- (void)confirmCreditCardExpirationWithCard:(const autofill::CreditCard&)card
+                                   callback:
+                                       (base::OnceCallback<void(
+                                            const base::string16&,
+                                            const base::string16&)>)callback;
+
 // Bridge for AutofillClient's method |CreditCardUploadCompleted|.
 - (void)handleCreditCardUploadCompleted:(BOOL)cardSaved;
 
diff --git a/ios/web_view/internal/autofill/cwv_autofill_controller.mm b/ios/web_view/internal/autofill/cwv_autofill_controller.mm
index b76f3af..9cca322a 100644
--- a/ios/web_view/internal/autofill/cwv_autofill_controller.mm
+++ b/ios/web_view/internal/autofill/cwv_autofill_controller.mm
@@ -37,7 +37,9 @@
 #import "ios/web_view/internal/autofill/cwv_autofill_form_internal.h"
 #import "ios/web_view/internal/autofill/cwv_autofill_profile_internal.h"
 #import "ios/web_view/internal/autofill/cwv_autofill_suggestion_internal.h"
+#import "ios/web_view/internal/autofill/cwv_credit_card_expiration_fixer_internal.h"
 #import "ios/web_view/internal/autofill/cwv_credit_card_internal.h"
+#import "ios/web_view/internal/autofill/cwv_credit_card_name_fixer_internal.h"
 #import "ios/web_view/internal/autofill/cwv_credit_card_saver_internal.h"
 #import "ios/web_view/internal/autofill/cwv_credit_card_verifier_internal.h"
 #include "ios/web_view/internal/autofill/web_view_autocomplete_history_manager_factory.h"
@@ -425,6 +427,39 @@
 }
 
 - (void)
+    confirmCreditCardAccountName:(const base::string16&)name
+                        callback:
+                            (base::OnceCallback<void(const base::string16&)>)
+                                callback {
+  if (![_delegate respondsToSelector:@selector(autofillController:
+                                         confirmCreditCardNameWithFixer:)]) {
+    return;
+  }
+
+  CWVCreditCardNameFixer* fixer = [[CWVCreditCardNameFixer alloc]
+      initWithName:base::SysUTF16ToNSString(name)
+          callback:std::move(callback)];
+  [_delegate autofillController:self confirmCreditCardNameWithFixer:fixer];
+}
+
+- (void)confirmCreditCardExpirationWithCard:(const autofill::CreditCard&)card
+                                   callback:
+                                       (base::OnceCallback<void(
+                                            const base::string16&,
+                                            const base::string16&)>)callback {
+  if (![_delegate respondsToSelector:@selector
+                  (autofillController:confirmCreditCardExpirationWithFixer:)]) {
+    return;
+  }
+
+  CWVCreditCardExpirationFixer* fixer = [[CWVCreditCardExpirationFixer alloc]
+      initWithCreditCard:card
+                callback:std::move(callback)];
+  [_delegate autofillController:self
+      confirmCreditCardExpirationWithFixer:fixer];
+}
+
+- (void)
     confirmSaveCreditCardToCloud:(const autofill::CreditCard&)creditCard
                legalMessageLines:(autofill::LegalMessageLines)legalMessageLines
            saveCreditCardOptions:
diff --git a/ios/web_view/internal/autofill/web_view_autofill_client_ios.mm b/ios/web_view/internal/autofill/web_view_autofill_client_ios.mm
index 3f26a66..0061630 100644
--- a/ios/web_view/internal/autofill/web_view_autofill_client_ios.mm
+++ b/ios/web_view/internal/autofill/web_view_autofill_client_ios.mm
@@ -181,14 +181,22 @@
 
 void WebViewAutofillClientIOS::ConfirmAccountNameFixFlow(
     base::OnceCallback<void(const base::string16&)> callback) {
-  NOTIMPLEMENTED();
+  base::Optional<AccountInfo> primary_account_info =
+      identity_manager_->FindExtendedAccountInfoForAccountWithRefreshToken(
+          identity_manager_->GetPrimaryAccountInfo());
+  base::string16 account_name =
+      primary_account_info ? base::UTF8ToUTF16(primary_account_info->full_name)
+                           : base::string16();
+  [bridge_ confirmCreditCardAccountName:account_name
+                               callback:std::move(callback)];
 }
 
 void WebViewAutofillClientIOS::ConfirmExpirationDateFixFlow(
     const CreditCard& card,
     base::OnceCallback<void(const base::string16&, const base::string16&)>
         callback) {
-  NOTIMPLEMENTED();
+  [bridge_ confirmCreditCardExpirationWithCard:card
+                                      callback:std::move(callback)];
 }
 
 void WebViewAutofillClientIOS::ConfirmSaveCreditCardToCloud(
diff --git a/ios/web_view/internal/cwv_preferences.mm b/ios/web_view/internal/cwv_preferences.mm
index 85ad5e4..4d5117ea 100644
--- a/ios/web_view/internal/cwv_preferences.mm
+++ b/ios/web_view/internal/cwv_preferences.mm
@@ -4,16 +4,12 @@
 
 #import "ios/web_view/internal/cwv_preferences_internal.h"
 
+#include "components/autofill/core/common/autofill_prefs.h"
 #include "components/language/core/browser/pref_names.h"
+#include "components/password_manager/core/common/password_manager_pref_names.h"
 #include "components/prefs/pref_service.h"
 #include "components/translate/core/browser/translate_pref_names.h"
 #include "components/translate/core/browser/translate_prefs.h"
-#include "ios/web_view/cwv_web_view_buildflags.h"
-
-#if BUILDFLAG(IOS_WEB_VIEW_ENABLE_AUTOFILL)
-#include "components/autofill/core/common/autofill_prefs.h"
-#include "components/password_manager/core/common/password_manager_pref_names.h"
-#endif  // BUILDFLAG(IOS_WEB_VIEW_ENABLE_AUTOFILL)
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
@@ -48,7 +44,6 @@
   translatePrefs.ResetToDefaults();
 }
 
-#if BUILDFLAG(IOS_WEB_VIEW_ENABLE_AUTOFILL)
 #pragma mark - Autofill
 
 - (void)setProfileAutofillEnabled:(BOOL)enabled {
@@ -77,6 +72,4 @@
       password_manager::prefs::kCredentialsEnableService);
 }
 
-#endif  // BUILDFLAG(IOS_WEB_VIEW_ENABLE_AUTOFILL)
-
 @end
diff --git a/ios/web_view/internal/cwv_web_view.mm b/ios/web_view/internal/cwv_web_view.mm
index 5f1292c..f67bda60 100644
--- a/ios/web_view/internal/cwv_web_view.mm
+++ b/ios/web_view/internal/cwv_web_view.mm
@@ -37,7 +37,6 @@
 #import "ios/web/public/web_state_delegate_bridge.h"
 #import "ios/web/public/web_state_observer_bridge.h"
 #import "ios/web/public/web_view_only/wk_web_view_configuration_util.h"
-#include "ios/web_view/cwv_web_view_buildflags.h"
 #import "ios/web_view/internal/autofill/cwv_autofill_controller_internal.h"
 #import "ios/web_view/internal/cwv_back_forward_list_internal.h"
 #import "ios/web_view/internal/cwv_favicon_internal.h"
@@ -137,9 +136,7 @@
 @property(nonatomic, readwrite) NSURL* visibleURL;
 @property(nonatomic, readwrite) NSString* visibleLocationString;
 @property(nonatomic, readwrite) CWVSSLStatus* visibleSSLStatus;
-#if BUILDFLAG(IOS_WEB_VIEW_ENABLE_AUTOFILL)
 @property(nonatomic, readonly) CWVAutofillController* autofillController;
-#endif  // BUILDFLAG(IOS_WEB_VIEW_ENABLE_AUTOFILL)
 
 // Updates the availability of the back/forward navigation properties exposed
 // through |canGoBack| and |canGoForward|, and also updates |backForwardList|.
@@ -148,10 +145,8 @@
 - (void)updateCurrentURLs;
 // Updates |title| property.
 - (void)updateTitle;
-#if BUILDFLAG(IOS_WEB_VIEW_ENABLE_AUTOFILL)
 // Returns a new CWVAutofillController created from |_webState|.
 - (CWVAutofillController*)newAutofillController;
-#endif  // BUILDFLAG(IOS_WEB_VIEW_ENABLE_AUTOFILL)
 // Returns a new CWVTranslationController created from |_webState|.
 - (CWVTranslationController*)newTranslationController;
 // Updates |_webState| visiblity.
@@ -166,9 +161,7 @@
 
 @implementation CWVWebView
 
-#if BUILDFLAG(IOS_WEB_VIEW_ENABLE_AUTOFILL)
 @synthesize autofillController = _autofillController;
-#endif  // BUILDFLAG(IOS_WEB_VIEW_ENABLE_AUTOFILL)
 @synthesize backForwardList = _backForwardList;
 @synthesize canGoBack = _canGoBack;
 @synthesize canGoForward = _canGoForward;
@@ -628,7 +621,6 @@
       initWithTranslateClient:translateClient];
 }
 
-#if BUILDFLAG(IOS_WEB_VIEW_ENABLE_AUTOFILL)
 #pragma mark - Autofill
 
 - (CWVAutofillController*)autofillController {
@@ -660,8 +652,6 @@
                                       passwordController:passwordController];
 }
 
-#endif  // BUILDFLAG(IOS_WEB_VIEW_ENABLE_AUTOFILL)
-
 #pragma mark - Preserving and Restoring State
 
 - (void)encodeRestorableStateWithCoder:(NSCoder*)coder {
@@ -804,14 +794,12 @@
     _translationController.delegate = delegate;
   }
 
-#if BUILDFLAG(IOS_WEB_VIEW_ENABLE_AUTOFILL)
   // Recreate and restore the delegate only if previously lazily loaded.
   if (_autofillController) {
     id<CWVAutofillControllerDelegate> delegate = _autofillController.delegate;
     _autofillController = [self newAutofillController];
     _autofillController.delegate = delegate;
   }
-#endif  // BUILDFLAG(IOS_WEB_VIEW_ENABLE_AUTOFILL)
 
   [self addInternalWebViewAsSubview];
 
diff --git a/ios/web_view/internal/cwv_web_view_configuration.mm b/ios/web_view/internal/cwv_web_view_configuration.mm
index 0fa5186..e199882 100644
--- a/ios/web_view/internal/cwv_web_view_configuration.mm
+++ b/ios/web_view/internal/cwv_web_view_configuration.mm
@@ -12,7 +12,6 @@
 #include "components/keyed_service/core/service_access_type.h"
 #include "components/password_manager/core/browser/password_store_default.h"
 #include "components/sync/driver/sync_service.h"
-#include "ios/web_view/cwv_web_view_buildflags.h"
 #include "ios/web_view/internal/app/application_context.h"
 #import "ios/web_view/internal/autofill/cwv_autofill_data_manager_internal.h"
 #include "ios/web_view/internal/autofill/web_view_personal_data_manager_factory.h"
@@ -40,18 +39,14 @@
   NSHashTable* _webViews;
 }
 
-#if BUILDFLAG(IOS_WEB_VIEW_ENABLE_AUTOFILL)
 // This web view configuration's autofill data manager.
 // nil if CWVWebViewConfiguration is created with +incognitoConfiguration.
 @property(nonatomic, readonly, nullable)
     CWVAutofillDataManager* autofillDataManager;
-#endif  // BUILDFLAG(IOS_WEB_VIEW_ENABLE_AUTOFILL)
 
-#if BUILDFLAG(IOS_WEB_VIEW_ENABLE_SYNC)
 // This web view configuration's sync controller.
 // nil if CWVWebViewConfiguration is created with +incognitoConfiguration.
 @property(nonatomic, readonly, nullable) CWVSyncController* syncController;
-#endif  // BUILDFLAG(IOS_WEB_VIEW_ENABLE_SYNC)
 
 // Initializes configuration with the specified browser state mode.
 - (instancetype)initWithBrowserState:
@@ -61,13 +56,9 @@
 
 @implementation CWVWebViewConfiguration
 
-#if BUILDFLAG(IOS_WEB_VIEW_ENABLE_AUTOFILL)
 @synthesize autofillDataManager = _autofillDataManager;
-#endif  // BUILDFLAG(IOS_WEB_VIEW_ENABLE_AUTOFILL)
 @synthesize preferences = _preferences;
-#if BUILDFLAG(IOS_WEB_VIEW_ENABLE_SYNC)
 @synthesize syncController = _syncController;
-#endif  // BUILDFLAG(IOS_WEB_VIEW_ENABLE_SYNC)
 @synthesize userContentController = _userContentController;
 
 namespace {
@@ -131,8 +122,8 @@
   return self;
 }
 
-#if BUILDFLAG(IOS_WEB_VIEW_ENABLE_AUTOFILL)
 #pragma mark - Autofill
+
 - (CWVAutofillDataManager*)autofillDataManager {
   if (!_autofillDataManager && self.persistent) {
     autofill::PersonalDataManager* personalDataManager =
@@ -147,10 +138,9 @@
   }
   return _autofillDataManager;
 }
-#endif  // BUILDFLAG(IOS_WEB_VIEW_ENABLE_AUTOFILL)
 
-#if BUILDFLAG(IOS_WEB_VIEW_ENABLE_SYNC)
 #pragma mark - Sync
+
 - (CWVSyncController*)syncController {
   if (!_syncController && self.persistent) {
     syncer::SyncService* syncService =
@@ -183,7 +173,6 @@
   }
   return _syncController;
 }
-#endif  // BUILDFLAG(IOS_WEB_VIEW_ENABLE_SYNC)
 
 #pragma mark - Public Methods
 
@@ -205,9 +194,7 @@
   for (CWVWebView* webView in _webViews) {
     [webView shutDown];
   }
-#if BUILDFLAG(IOS_WEB_VIEW_ENABLE_SYNC)
   [_syncController shutDown];
-#endif  // BUILDFLAG(IOS_WEB_VIEW_ENABLE_SYNC)
   _browserState.reset();
 }
 
diff --git a/ios/web_view/internal/web_view_browser_state.mm b/ios/web_view/internal/web_view_browser_state.mm
index 26d0d55..9cc85c09 100644
--- a/ios/web_view/internal/web_view_browser_state.mm
+++ b/ios/web_view/internal/web_view_browser_state.mm
@@ -31,7 +31,6 @@
 #include "components/translate/core/browser/translate_prefs.h"
 #include "ios/web/public/thread/web_task_traits.h"
 #include "ios/web/public/thread/web_thread.h"
-#include "ios/web_view/cwv_web_view_buildflags.h"
 #include "ios/web_view/internal/app/application_context.h"
 #import "ios/web_view/internal/autofill/web_view_autofill_log_router_factory.h"
 #include "ios/web_view/internal/autofill/web_view_personal_data_manager_factory.h"
@@ -109,9 +108,7 @@
 
   base::ThreadRestrictions::SetIOAllowed(wasIOAllowed);
 
-#if BUILDFLAG(IOS_WEB_VIEW_ENABLE_SYNC)
   ActiveStateManager::FromBrowserState(this)->SetActive(true);
-#endif  // BUILDFLAG(IOS_WEB_VIEW_ENABLE_SYNC)
 
   BrowserStateDependencyManager::GetInstance()->CreateBrowserStateServices(
       this);
@@ -121,9 +118,7 @@
   BrowserStateDependencyManager::GetInstance()->DestroyBrowserStateServices(
       this);
 
-#if BUILDFLAG(IOS_WEB_VIEW_ENABLE_SYNC)
   ActiveStateManager::FromBrowserState(this)->SetActive(false);
-#endif  // BUILDFLAG(IOS_WEB_VIEW_ENABLE_SYNC)
 
   base::PostTask(FROM_HERE, {web::WebThread::IO},
                  base::BindOnce(&WebViewURLRequestContextGetter::ShutDown,
@@ -170,33 +165,22 @@
                                      true);
   language::LanguagePrefs::RegisterProfilePrefs(pref_registry);
   translate::TranslatePrefs::RegisterProfilePrefs(pref_registry);
-
-#if BUILDFLAG(IOS_WEB_VIEW_ENABLE_AUTOFILL)
   autofill::prefs::RegisterProfilePrefs(pref_registry);
   password_manager::PasswordManager::RegisterProfilePrefs(pref_registry);
-#endif  // BUILDFLAG(IOS_WEB_VIEW_ENABLE_AUTOFILL)
-
-#if BUILDFLAG(IOS_WEB_VIEW_ENABLE_SYNC)
   sync_sessions::SessionSyncPrefs::RegisterProfilePrefs(pref_registry);
   syncer::SyncPrefs::RegisterProfilePrefs(pref_registry);
   syncer::DeviceInfoPrefs::RegisterProfilePrefs(pref_registry);
-#endif  // BUILDFLAG(IOS_WEB_VIEW_ENABLE_SYNC)
 
   // Instantiate all factories to setup dependency graph for pref registration.
   WebViewLanguageModelManagerFactory::GetInstance();
   WebViewTranslateRankerFactory::GetInstance();
   WebViewUrlLanguageHistogramFactory::GetInstance();
   WebViewTranslateAcceptLanguagesFactory::GetInstance();
-
-#if BUILDFLAG(IOS_WEB_VIEW_ENABLE_AUTOFILL)
   autofill::WebViewAutofillLogRouterFactory::GetInstance();
   WebViewPersonalDataManagerFactory::GetInstance();
   WebViewWebDataServiceWrapperFactory::GetInstance();
   WebViewPasswordManagerLogRouterFactory::GetInstance();
   WebViewPasswordStoreFactory::GetInstance();
-#endif  // BUILDFLAG(IOS_WEB_VIEW_ENABLE_AUTOFILL)
-
-#if BUILDFLAG(IOS_WEB_VIEW_ENABLE_SYNC)
   WebViewCookieSettingsFactory::GetInstance();
   WebViewHostContentSettingsMapFactory::GetInstance();
   WebViewSigninClientFactory::GetInstance();
@@ -206,7 +190,6 @@
   WebViewProfileInvalidationProviderFactory::GetInstance();
   WebViewProfileSyncServiceFactory::GetInstance();
   WebViewModelTypeStoreServiceFactory::GetInstance();
-#endif  // BUILDFLAG(IOS_WEB_VIEW_ENABLE_SYNC)
 
   BrowserStateDependencyManager::GetInstance()
       ->RegisterBrowserStatePrefsForServices(pref_registry);
diff --git a/ios/web_view/internal/web_view_web_main_parts.mm b/ios/web_view/internal/web_view_web_main_parts.mm
index 521b938..20a40249 100644
--- a/ios/web_view/internal/web_view_web_main_parts.mm
+++ b/ios/web_view/internal/web_view_web_main_parts.mm
@@ -12,7 +12,6 @@
 #include "components/autofill/core/common/autofill_features.h"
 #include "components/autofill/core/common/autofill_payments_features.h"
 #include "components/content_settings/core/common/content_settings_pattern.h"
-#include "ios/web_view/cwv_web_view_buildflags.h"
 #include "ios/web_view/internal/app/application_context.h"
 #import "ios/web_view/internal/cwv_flags_internal.h"
 #import "ios/web_view/internal/cwv_web_view_configuration_internal.h"
@@ -49,7 +48,6 @@
 
   ApplicationContext::GetInstance()->PreCreateThreads();
 
-#if BUILDFLAG(IOS_WEB_VIEW_ENABLE_SYNC)
   std::unique_ptr<base::FeatureList> feature_list(new base::FeatureList);
   std::string enable_features = base::JoinString(
       {autofill::features::kAutofillUpstream.name,
@@ -61,16 +59,13 @@
       /*enable_features=*/enable_features,
       /*disable_features=*/disabled_features);
   base::FeatureList::SetInstance(std::move(feature_list));
-#endif  // BUILDFLAG(IOS_WEB_VIEW_ENABLE_SYNC)
 }
 
 void WebViewWebMainParts::PreMainMessageLoopRun() {
   WebViewTranslateService::GetInstance()->Initialize();
 
-#if BUILDFLAG(IOS_WEB_VIEW_ENABLE_SYNC)
   ContentSettingsPattern::SetNonWildcardDomainNonPortSchemes(
       /*schemes=*/nullptr, 0);
-#endif  // BUILDFLAG(IOS_WEB_VIEW_ENABLE_SYNC)
 }
 
 void WebViewWebMainParts::PostMainMessageLoopRun() {
diff --git a/ios/web_view/public/cwv_autofill_controller_delegate.h b/ios/web_view/public/cwv_autofill_controller_delegate.h
index e309607..da53b219 100644
--- a/ios/web_view/public/cwv_autofill_controller_delegate.h
+++ b/ios/web_view/public/cwv_autofill_controller_delegate.h
@@ -14,6 +14,8 @@
 @class CWVAutofillFormSuggestion;
 @class CWVAutofillProfile;
 @class CWVCreditCard;
+@class CWVCreditCardExpirationFixer;
+@class CWVCreditCardNameFixer;
 @class CWVCreditCardSaver;
 @class CWVCreditCardVerifier;
 @class CWVPassword;
@@ -101,6 +103,22 @@
 - (void)autofillController:(CWVAutofillController*)autofillController
     saveCreditCardWithSaver:(CWVCreditCardSaver*)saver;
 
+// Called if the card holder's name needs to be confirmed by the user before the
+// card can be saved. This can happen if a user doesn't have a GPay account or
+// attempted to save a credit card without providing a name for it.
+// |fixer| encapsulates information needed to assist with this fix attempt.
+// Life time of |fixer| should be managed by the delegate.
+- (void)autofillController:(CWVAutofillController*)autofillController
+    confirmCreditCardNameWithFixer:(CWVCreditCardNameFixer*)fixer;
+
+// Called if the card's expiration needs to be corrected before the the card can
+// be saved. This can happen if a user attempted to save a credit card with an
+// expired expiration date.
+// |fixer| encapsulates information needed to assist with this fix attempt.
+// Life time of |fixer| should be managed by the delegate.
+- (void)autofillController:(CWVAutofillController*)autofillController
+    confirmCreditCardExpirationWithFixer:(CWVCreditCardExpirationFixer*)fixer;
+
 // Called when the user needs to use |verifier| to verify a credit card.
 // Lifetime of |verifier| should be managed by the delegate.
 - (void)autofillController:(CWVAutofillController*)autofillController
diff --git a/ios/web_view/tools/build.py b/ios/web_view/tools/build.py
index c876d0e..819d554 100755
--- a/ios/web_view/tools/build.py
+++ b/ios/web_view/tools/build.py
@@ -186,10 +186,6 @@
                       help='Additional gn args to pass through to ninja.')
   parser.add_argument('--include_cronet', action='store_true',
                       help='Combines Cronet and ChromeWebView as 1 framework.')
-  parser.add_argument('--enable_sync', action='store_true',
-                      help='Enables public API for sync.')
-  parser.add_argument('--enable_autofill', action='store_true',
-                      help='Enables public API for autofill.')
   build_configs = ['Debug', 'Release']
   target_devices = ['iphonesimulator', 'iphoneos']
   parser.add_argument('--build_configs', nargs='+', default=build_configs,
@@ -221,14 +217,6 @@
     output_name = 'CronetChromeWebView'
   else:
     extra_gn_options += 'ios_web_view_include_cronet=false '
-  if options.enable_sync:
-    extra_gn_options += 'ios_web_view_enable_sync=true '
-  else:
-    extra_gn_options += 'ios_web_view_enable_sync=false '
-  if options.enable_autofill:
-    extra_gn_options += 'ios_web_view_enable_autofill=true '
-  else:
-    extra_gn_options += 'ios_web_view_enable_autofill=false '
   extra_gn_options += 'ios_web_view_output_name="%s" ' % output_name
   # This prevents Breakpad from being included in the final binary to avoid
   # duplicate symbols with the client app.
diff --git a/media/capture/content/video_capture_oracle.cc b/media/capture/content/video_capture_oracle.cc
index 5e8d29df..2a1e3c1 100644
--- a/media/capture/content/video_capture_oracle.cc
+++ b/media/capture/content/video_capture_oracle.cc
@@ -6,6 +6,7 @@
 
 #include <algorithm>
 
+#include "base/callback.h"
 #include "base/compiler_specific.h"
 #include "base/format_macros.h"
 #include "base/numerics/safe_conversions.h"
@@ -412,6 +413,15 @@
   const int decreased_area = AnalyzeForDecreasedArea(analyze_time);
   if (decreased_area > 0) {
     resolution_chooser_.SetTargetFrameArea(decreased_area);
+    if (!emit_log_message_cb_.is_null()) {
+      emit_log_message_cb_.Run(base::StringPrintf(
+          "VFC: CaptureOracle - Decreasing resolution. "
+          "buffer_utilization_: %lf "
+          "estimated_cappable_area: %lf "
+          "capture_size: %s ",
+          buffer_pool_utilization_.current(), estimated_capable_area_.current(),
+          capture_size_.ToString().c_str()));
+    }
     return;
   }
 
@@ -587,4 +597,9 @@
          (now - accumulator.update_time() <= kMaxTimeSinceLastFeedbackUpdate);
 }
 
+void VideoCaptureOracle::SetLogCallback(
+    base::RepeatingCallback<void(const std::string&)> emit_log_cb) {
+  emit_log_message_cb_ = std::move(emit_log_cb);
+}
+
 }  // namespace media
diff --git a/media/capture/content/video_capture_oracle.h b/media/capture/content/video_capture_oracle.h
index c1eb299..212b8be4 100644
--- a/media/capture/content/video_capture_oracle.h
+++ b/media/capture/content/video_capture_oracle.h
@@ -5,7 +5,7 @@
 #ifndef MEDIA_CAPTURE_CONTENT_VIDEO_CAPTURE_ORACLE_H_
 #define MEDIA_CAPTURE_CONTENT_VIDEO_CAPTURE_ORACLE_H_
 
-#include "base/callback_forward.h"
+#include "base/callback.h"
 #include "base/time/time.h"
 #include "media/base/feedback_signal_accumulator.h"
 #include "media/capture/capture_export.h"
@@ -139,6 +139,11 @@
   static constexpr base::TimeDelta kDefaultMinSizeChangePeriod =
       base::TimeDelta::FromSeconds(3);
 
+  void SetLogCallback(
+      base::RepeatingCallback<void(const std::string&)> emit_log_cb);
+
+  void Log(const std::string& message);
+
  private:
   // Retrieve/Assign a frame timestamp by capture |frame_number|.  Only valid
   // when IsFrameInRecentHistory(frame_number) returns true.
@@ -253,6 +258,10 @@
   // animation.  This determines whether capture size increases will be
   // aggressive (because content is not animating).
   base::TimeTicks last_time_animation_was_detected_;
+
+  // Callback for webrtc native log. It should check for corresponding feature
+  // inside.
+  base::RepeatingCallback<void(const std::string&)> emit_log_message_cb_;
 };
 
 }  // namespace media
diff --git a/media/gpu/v4l2/v4l2_device.cc b/media/gpu/v4l2/v4l2_device.cc
index ee5c443..31ca817 100644
--- a/media/gpu/v4l2/v4l2_device.cc
+++ b/media/gpu/v4l2/v4l2_device.cc
@@ -47,6 +47,10 @@
 // Maximum number of requests that can be created.
 constexpr size_t kMaxNumRequests = 32;
 
+gfx::Rect V4L2RectToGfxRect(const v4l2_rect& rect) {
+  return gfx::Rect(rect.left, rect.top, rect.width, rect.height);
+}
+
 }  // namespace
 
 V4L2ExtCtrl::V4L2ExtCtrl(uint32_t id) {
@@ -881,6 +885,57 @@
   return current_format_;
 }
 
+std::pair<base::Optional<struct v4l2_format>, int> V4L2Queue::GetFormat() {
+  struct v4l2_format format = {};
+  format.type = type_;
+  if (device_->Ioctl(VIDIOC_G_FMT, &format) != 0) {
+    VPQLOGF(2) << "Failed to get format";
+    return std::make_pair(base::nullopt, errno);
+  }
+
+  return std::make_pair(format, 0);
+}
+
+base::Optional<gfx::Rect> V4L2Queue::GetVisibleRect() {
+  // Some drivers prior to 4.13 only accept the non-MPLANE variant when using
+  // VIDIOC_G_SELECTION. This block can be removed once we stop supporting
+  // kernels < 4.13.
+  // For details, see the note at
+  // https://www.kernel.org/doc/html/latest/media/uapi/v4l/vidioc-g-selection.html
+  enum v4l2_buf_type compose_type;
+  switch (type_) {
+    case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+      compose_type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+      break;
+    case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+      compose_type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+      break;
+    default:
+      compose_type = type_;
+      break;
+  }
+
+  struct v4l2_selection selection = {};
+  selection.type = compose_type;
+  selection.target = V4L2_SEL_TGT_COMPOSE;
+  if (device_->Ioctl(VIDIOC_G_SELECTION, &selection) == 0) {
+    DVQLOGF(3) << "VIDIOC_G_SELECTION is supported";
+    return V4L2RectToGfxRect(selection.r);
+  }
+
+  // TODO(acourbot) using VIDIOC_G_CROP is considered legacy and can be
+  // removed once no active devices use it anymore.
+  DVQLOGF(3) << "Fallback to VIDIOC_G_CROP";
+  struct v4l2_crop crop = {};
+  crop.type = type_;
+  if (device_->Ioctl(VIDIOC_G_CROP, &crop) == 0) {
+    return V4L2RectToGfxRect(crop.c);
+  }
+
+  VQLOGF(1) << "Failed to get visible rect";
+  return base::nullopt;
+}
+
 size_t V4L2Queue::AllocateBuffers(size_t count, enum v4l2_memory memory) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(!free_buffers_);
@@ -906,13 +961,12 @@
   // This should not be required, but Tegra's VIDIOC_QUERYBUF will fail on
   // output buffers if the number of specified planes does not exactly match the
   // format.
-  struct v4l2_format format = {.type = type_};
-  int ret = device_->Ioctl(VIDIOC_G_FMT, &format);
-  if (ret) {
-    VPQLOGF(1) << "VIDIOC_G_FMT failed";
+  base::Optional<v4l2_format> format = GetFormat().first;
+  if (!format) {
+    VQLOGF(1) << "Cannot get format.";
     return 0;
   }
-  planes_count_ = format.fmt.pix_mp.num_planes;
+  planes_count_ = format->fmt.pix_mp.num_planes;
   DCHECK_LE(planes_count_, static_cast<size_t>(VIDEO_MAX_PLANES));
 
   struct v4l2_requestbuffers reqbufs = {};
@@ -921,7 +975,7 @@
   reqbufs.memory = memory;
   DVQLOGF(3) << "Requesting " << count << " buffers.";
 
-  ret = device_->Ioctl(VIDIOC_REQBUFS, &reqbufs);
+  int ret = device_->Ioctl(VIDIOC_REQBUFS, &reqbufs);
   if (ret) {
     VPQLOGF(1) << "VIDIOC_REQBUFS failed";
     return 0;
@@ -934,7 +988,7 @@
 
   // Now query all buffer information.
   for (size_t i = 0; i < reqbufs.count; i++) {
-    auto buffer = V4L2Buffer::Create(device_, type_, memory_, format, i);
+    auto buffer = V4L2Buffer::Create(device_, type_, memory_, *format, i);
 
     if (!buffer) {
       DeallocateBuffers();
diff --git a/media/gpu/v4l2/v4l2_device.h b/media/gpu/v4l2/v4l2_device.h
index 346ec0c9..a8e76ce 100644
--- a/media/gpu/v4l2/v4l2_device.h
+++ b/media/gpu/v4l2/v4l2_device.h
@@ -293,6 +293,26 @@
                                                size_t buffer_size)
       WARN_UNUSED_RESULT;
 
+  // Returns the currently set format on the queue. The result is returned as
+  // a std::pair where the first member is the format, or base::nullopt if the
+  // format could not be obtained due to an ioctl error. The second member is
+  // only used in case of an error and contains the |errno| set by the failing
+  // ioctl. If the first member is not base::nullopt, the second member will
+  // always be zero.
+  //
+  // If the second member is 0, then the first member is guaranteed to have
+  // a valid value. So clients that are not interested in the precise error
+  // message can just check that the first member is valid and go on.
+  //
+  // This pair is used because not all failures to get the format are
+  // necessarily errors, so we need to way to let the use decide whether it
+  // is one or not.
+  std::pair<base::Optional<struct v4l2_format>, int> GetFormat();
+
+  // Codec-specific method to get the visible rectangle of the queue, using the
+  // VIDIOC_G_SELECTION ioctl if available, or VIDIOC_G_CROP as a fallback.
+  base::Optional<gfx::Rect> GetVisibleRect();
+
   // Allocate |count| buffers for the current format of this queue, with a
   // specific |memory| allocation, and returns the number of buffers allocated
   // or zero if an error occurred, or if references to any previously allocated
diff --git a/media/gpu/v4l2/v4l2_image_processor_backend.cc b/media/gpu/v4l2/v4l2_image_processor_backend.cc
index 12eeef48..6498537 100644
--- a/media/gpu/v4l2/v4l2_image_processor_backend.cc
+++ b/media/gpu/v4l2/v4l2_image_processor_backend.cc
@@ -574,6 +574,33 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(backend_sequence_checker_);
 
   while (!input_job_queue_.empty()) {
+    if (!input_queue_->IsStreaming()) {
+      const VideoFrame& input_frame =
+          *(input_job_queue_.front()->input_frame.get());
+      const gfx::Size input_buffer_size(input_frame.stride(0),
+                                        input_frame.coded_size().height());
+      if (!ReconfigureV4L2Format(input_buffer_size, input_frame.visible_rect(),
+                                 V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)) {
+        NotifyError();
+        return;
+      }
+    }
+
+    if (input_job_queue_.front()
+            ->output_frame &&  // output_frame is nullptr in ALLOCATE mode.
+        !output_queue_->IsStreaming()) {
+      const VideoFrame& output_frame =
+          *(input_job_queue_.front()->output_frame.get());
+      const gfx::Size output_buffer_size(output_frame.stride(0),
+                                         output_frame.coded_size().height());
+      if (!ReconfigureV4L2Format(output_buffer_size,
+                                 output_frame.visible_rect(),
+                                 V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)) {
+        NotifyError();
+        return;
+      }
+    }
+
     // We need one input and one output buffer to schedule the job
     auto input_buffer = input_queue_->GetFreeBuffer();
     auto output_buffer = output_queue_->GetFreeBuffer();
@@ -641,6 +668,39 @@
   return true;
 }
 
+bool V4L2ImageProcessorBackend::ReconfigureV4L2Format(
+    const gfx::Size& size,
+    const gfx::Rect& visible_rect,
+    enum v4l2_buf_type type) {
+  v4l2_format format{};
+  format.type = type;
+  if (device_->Ioctl(VIDIOC_G_FMT, &format) != 0) {
+    VPLOGF(1) << "ioctl() failed: VIDIOC_G_FMT";
+    return false;
+  }
+
+  if (static_cast<int>(format.fmt.pix_mp.width) == size.width() &&
+      static_cast<int>(format.fmt.pix_mp.height) == size.height()) {
+    return true;
+  }
+  format.fmt.pix_mp.width = size.width();
+  format.fmt.pix_mp.height = size.height();
+  if (device_->Ioctl(VIDIOC_S_FMT, &format) != 0) {
+    VPLOGF(1) << "ioctl() failed: VIDIOC_S_FMT";
+    return false;
+  }
+  if (!ApplyCrop(visible_rect, type)) {
+    return false;
+  }
+
+  auto queue = device_->GetQueue(type);
+  const size_t num_buffers = queue->AllocatedBuffersCount();
+  const v4l2_memory memory_type = queue->GetMemoryType();
+  DCHECK_GT(num_buffers, 0u);
+  return queue->DeallocateBuffers() &&
+         AllocateV4L2Buffers(queue.get(), num_buffers, memory_type);
+}
+
 bool V4L2ImageProcessorBackend::CreateInputBuffers() {
   VLOGF(2);
   DCHECK_CALLED_ON_VALID_SEQUENCE(backend_sequence_checker_);
diff --git a/media/gpu/v4l2/v4l2_image_processor_backend.h b/media/gpu/v4l2/v4l2_image_processor_backend.h
index 5214eeb..bd1c78ac 100644
--- a/media/gpu/v4l2/v4l2_image_processor_backend.h
+++ b/media/gpu/v4l2/v4l2_image_processor_backend.h
@@ -135,6 +135,10 @@
   bool CreateOutputBuffers();
   // Specify |visible_rect| to v4l2 |type| queue.
   bool ApplyCrop(const gfx::Rect& visible_rect, enum v4l2_buf_type type);
+  // Reconfigure |size| and |visible_rect| to v4l2 |type| queue.
+  bool ReconfigureV4L2Format(const gfx::Size& size,
+                             const gfx::Rect& visible_rect,
+                             enum v4l2_buf_type type);
 
   // Callback of VideoFrame destruction. Since VideoFrame destruction
   // callback might be executed on any sequence, we use a thunk to post the
diff --git a/media/gpu/v4l2/v4l2_slice_video_decode_accelerator.cc b/media/gpu/v4l2/v4l2_slice_video_decode_accelerator.cc
index 4f9ff1d5..dd2c2e8 100644
--- a/media/gpu/v4l2/v4l2_slice_video_decode_accelerator.cc
+++ b/media/gpu/v4l2/v4l2_slice_video_decode_accelerator.cc
@@ -649,15 +649,12 @@
 
   // Since VdaVideoDecoder doesn't allocate PictureBuffer with size adjusted by
   // itself, we have to adjust here.
-  struct v4l2_format format;
-  memset(&format, 0, sizeof(format));
-  format.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
-
-  if (device_->Ioctl(VIDIOC_G_FMT, &format) != 0) {
-    PLOG(ERROR) << "Failed getting OUTPUT format";
+  auto ret = input_queue_->GetFormat().first;
+  if (!ret) {
     NOTIFY_ERROR(PLATFORM_FAILURE);
     return false;
   }
+  struct v4l2_format format = std::move(*ret);
 
   format.fmt.pix_mp.width = pic_size.width();
   format.fmt.pix_mp.height = pic_size.height();
@@ -669,13 +666,12 @@
   }
 
   // Get the coded size from the CAPTURE queue
-  memset(&format, 0, sizeof(format));
-  format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
-  if (device_->Ioctl(VIDIOC_G_FMT, &format) != 0) {
-    PLOG(ERROR) << "Failed getting CAPTURE format";
+  ret = output_queue_->GetFormat().first;
+  if (!ret) {
     NOTIFY_ERROR(PLATFORM_FAILURE);
     return false;
   }
+  format = std::move(*ret);
 
   coded_size_.SetSize(base::checked_cast<int>(format.fmt.pix_mp.width),
                       base::checked_cast<int>(format.fmt.pix_mp.height));
diff --git a/media/gpu/v4l2/v4l2_video_decode_accelerator.cc b/media/gpu/v4l2/v4l2_video_decode_accelerator.cc
index 7147d9f..928bec4 100644
--- a/media/gpu/v4l2/v4l2_video_decode_accelerator.cc
+++ b/media/gpu/v4l2/v4l2_video_decode_accelerator.cc
@@ -2084,18 +2084,19 @@
   DCHECK(decoder_thread_.task_runner()->BelongsToCurrentThread());
 
   *again = false;
-  memset(format, 0, sizeof(*format));
-  format->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
-  if (device_->Ioctl(VIDIOC_G_FMT, format) != 0) {
-    if (errno == EINVAL) {
+
+  auto ret = output_queue_->GetFormat();
+  switch (ret.second) {
+    case 0:
+      *format = *ret.first;
+      break;
+    case EINVAL:
       // EINVAL means we haven't seen sufficient stream to decode the format.
       *again = true;
       return true;
-    } else {
-      PLOG(ERROR) << "ioctl() failed: VIDIOC_G_FMT";
+    default:
       NOTIFY_ERROR(PLATFORM_FAILURE);
       return false;
-    }
   }
 
   // Make sure we are still getting the format we set on initialization.
@@ -2146,30 +2147,11 @@
     const gfx::Size& coded_size) {
   DCHECK(decoder_thread_.task_runner()->BelongsToCurrentThread());
 
-  struct v4l2_rect* visible_rect;
-  struct v4l2_selection selection_arg;
-  memset(&selection_arg, 0, sizeof(selection_arg));
-  selection_arg.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-  selection_arg.target = V4L2_SEL_TGT_COMPOSE;
-
-  if (device_->Ioctl(VIDIOC_G_SELECTION, &selection_arg) == 0) {
-    DVLOGF(3) << "VIDIOC_G_SELECTION is supported";
-    visible_rect = &selection_arg.r;
-  } else {
-    DVLOGF(3) << "Fallback to VIDIOC_G_CROP";
-    struct v4l2_crop crop_arg;
-    memset(&crop_arg, 0, sizeof(crop_arg));
-    crop_arg.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
-
-    if (device_->Ioctl(VIDIOC_G_CROP, &crop_arg) != 0) {
-      VPLOGF(1) << "ioctl() VIDIOC_G_CROP failed";
-      return coded_size;
-    }
-    visible_rect = &crop_arg.c;
+  auto ret = output_queue_->GetVisibleRect();
+  if (!ret) {
+    return coded_size;
   }
-
-  gfx::Rect rect(visible_rect->left, visible_rect->top, visible_rect->width,
-                 visible_rect->height);
+  gfx::Rect rect = std::move(*ret);
   DVLOGF(3) << "visible rectangle is " << rect.ToString();
   if (!gfx::Rect(coded_size).Contains(rect)) {
     DVLOGF(3) << "visible rectangle " << rect.ToString()
diff --git a/media/gpu/v4l2/v4l2_video_decoder_backend_stateless.cc b/media/gpu/v4l2/v4l2_video_decoder_backend_stateless.cc
index 3ca859a4..b8c3400 100644
--- a/media/gpu/v4l2/v4l2_video_decoder_backend_stateless.cc
+++ b/media/gpu/v4l2/v4l2_video_decoder_backend_stateless.cc
@@ -529,13 +529,12 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK_EQ(input_queue_->QueuedBuffersCount(), 0u);
 
-  struct v4l2_format format = {};
-
-  format.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
-  if (device_->Ioctl(VIDIOC_G_FMT, &format) != 0) {
+  auto ret = input_queue_->GetFormat().first;
+  if (!ret) {
     VPLOGF(1) << "Failed getting OUTPUT format";
     return false;
   }
+  struct v4l2_format format = std::move(*ret);
 
   format.fmt.pix_mp.width = pic_size.width();
   format.fmt.pix_mp.height = pic_size.height();
diff --git a/printing/BUILD.gn b/printing/BUILD.gn
index 9b19b2d..246c15c7 100644
--- a/printing/BUILD.gn
+++ b/printing/BUILD.gn
@@ -31,13 +31,19 @@
              "set enable_pdf=true.")
 }
 
+# Enable the CUPS IPP printing backend.
+# TODO(crbug.com/226176): Remove this after CUPS PPD API calls are removed.
+declare_args() {
+  use_cups_ipp = use_cups && !is_desktop_linux
+}
+
 # Several targets want to include this header file. We separate it out
 # here so multiple targets can depend on it.
 source_set("printing_export") {
   sources = [ "printing_export.h" ]
 }
 
-if (use_cups && is_chromeos) {
+if (use_cups_ipp && is_chromeos) {
   ipp_handler_map_path = "$target_gen_dir/backend/ipp_handler_map.cc"
 
   ipp_code_generate("ipp_handlers_generate") {
@@ -217,7 +223,7 @@
     # of the print backend and enables a custom implementation instead.
     defines += [ "PRINT_BACKEND_AVAILABLE" ]
 
-    if (is_chromeos || is_mac) {
+    if (use_cups_ipp) {
       sources += [
         "backend/cups_connection.cc",
         "backend/cups_connection.h",
@@ -271,7 +277,7 @@
     }
   }
 
-  if (is_chromeos || is_mac) {
+  if (use_cups_ipp || is_chromeos) {
     sources += [
       "printer_query_result.h",
       "printer_status.cc",
@@ -370,7 +376,7 @@
   if (use_cups) {
     configs += [ ":cups" ]
 
-    if (is_chromeos || is_mac) {
+    if (use_cups_ipp) {
       sources += [ "backend/cups_ipp_helper_unittest.cc" ]
     }
 
diff --git a/services/viz/privileged/mojom/compositing/frame_sink_video_capture.mojom b/services/viz/privileged/mojom/compositing/frame_sink_video_capture.mojom
index c1ae4fe..331efbe 100644
--- a/services/viz/privileged/mojom/compositing/frame_sink_video_capture.mojom
+++ b/services/viz/privileged/mojom/compositing/frame_sink_video_capture.mojom
@@ -52,6 +52,9 @@
   // Indicates that OnFrameCaptured() will not be called again, an end-of-stream
   // signal.
   OnStopped();
+  // Called to deliver a log message to the consumer.
+  // Currently used to populate WebRTC native logs.
+  OnLog(string message);
 };
 
 // Interface to an implementation that captures the frames of a
diff --git a/services/viz/public/cpp/compositing/quads_mojom_traits.h b/services/viz/public/cpp/compositing/quads_mojom_traits.h
index ea4d147..8054def 100644
--- a/services/viz/public/cpp/compositing/quads_mojom_traits.h
+++ b/services/viz/public/cpp/compositing/quads_mojom_traits.h
@@ -544,11 +544,13 @@
 template <>
 struct StructTraits<viz::mojom::DrawQuadDataView, DrawQuadWithSharedQuadState> {
   static const gfx::Rect& rect(const DrawQuadWithSharedQuadState& input) {
+    DCHECK(input.quad->rect.size().GetCheckedArea().IsValid());
     return input.quad->rect;
   }
 
   static const gfx::Rect& visible_rect(
       const DrawQuadWithSharedQuadState& input) {
+    DCHECK(input.quad->rect.Contains(input.quad->visible_rect));
     return input.quad->visible_rect;
   }
 
diff --git a/testing/buildbot/manage.py b/testing/buildbot/manage.py
index 7077c9d..72531bd 100755
--- a/testing/buildbot/manage.py
+++ b/testing/buildbot/manage.py
@@ -171,6 +171,7 @@
   'tools_unittests',
   'video_engine_tests',
   'voice_engine_unittests',
+  'voip_unittests',
   'webrtc_nonparallel_tests',
   'xmllite_xmpp_unittests',
 
diff --git a/testing/test.gni b/testing/test.gni
index eda4330..d2b54c2 100644
--- a/testing/test.gni
+++ b/testing/test.gni
@@ -217,7 +217,7 @@
       package = ":$_pkg_target"
       package_name_override = _output_name
 
-      deps = [ "//testing/buildbot/filters:fuchsia_filters" ]
+      data_deps = [ "//testing/buildbot/filters:fuchsia_filters" ]
     }
 
     cr_fuchsia_package(_pkg_target) {
diff --git a/third_party/binutils/README.chromium b/third_party/binutils/README.chromium
index b40ec3c..3dc5547 100644
--- a/third_party/binutils/README.chromium
+++ b/third_party/binutils/README.chromium
@@ -1,6 +1,7 @@
 Name: binutils
 URL: http://www.gnu.org/software/binutils/
 Version: 2.30
+CPEPrefix: cpe:/a:gnu:binutils:2.30
 License: GPL v2
 License File: NOT_SHIPPED
 Security Critical: no
diff --git a/third_party/blink/public/mojom/use_counter/css_property_id.mojom b/third_party/blink/public/mojom/use_counter/css_property_id.mojom
index dfc2bf2..07465f3 100644
--- a/third_party/blink/public/mojom/use_counter/css_property_id.mojom
+++ b/third_party/blink/public/mojom/use_counter/css_property_id.mojom
@@ -218,7 +218,7 @@
     kAliasWebkitAnimationName = 172,
     kAliasWebkitAnimationPlayState = 173,
     kAliasWebkitAnimationTimingFunction = 174,
-    kWebkitAppearance = 175,
+    kAliasWebkitAppearance = 175,
     // kWebkitAspectRatio = 176,
     kAliasWebkitBackfaceVisibility = 177,
     kAliasWebkitBackgroundClip = 178,
@@ -701,6 +701,7 @@
     kSubtreeVisibility = 655,
     kMathStyle = 656,
     kAspectRatio = 657,
+    kAppearance = 658,
     // 1. Add new features above this line (don't change the assigned numbers of
     //    the existing items).
     // 2. Run the src/tools/metrics/histograms/update_use_counter_css.py script
diff --git a/third_party/blink/public/mojom/web_feature/web_feature.mojom b/third_party/blink/public/mojom/web_feature/web_feature.mojom
index 19c5293..a416dc5 100644
--- a/third_party/blink/public/mojom/web_feature/web_feature.mojom
+++ b/third_party/blink/public/mojom/web_feature/web_feature.mojom
@@ -2225,7 +2225,7 @@
   kV8RTCRtpReceiver_JitterBufferDelayHint_AttributeGetter = 2886,
   kV8RTCRtpReceiver_JitterBufferDelayHint_AttributeSetter = 2887,
   kMediaCapabilitiesDecodingInfoWithKeySystemConfig = 2888,
-  kRevertInCustomIdent = 2889,
+  kOBSOLETE_RevertInCustomIdent = 2889,
   kUnoptimizedImagePolicies = 2890,
   kVTTCueParser = 2891,
   kMediaElementTextTrackContainer = 2892,
@@ -2536,6 +2536,9 @@
   kCrossOriginOpenerPolicySameOriginAllowPopups = 3198,
   kCrossOriginEmbedderPolicyRequireCorp = 3199,
   kCoopAndCoepIsolated = 3200,
+  // The above items are available in M83 branch.
+
+  kWrongBaselineOfButtonElement = 3201,
 
   // Add new features immediately above this line. Don't change assigned
   // numbers of any item, and don't reuse removed slots.
diff --git a/third_party/blink/renderer/bindings/core/v8/generated_code_helper.cc b/third_party/blink/renderer/bindings/core/v8/generated_code_helper.cc
index 59b2a13b..7dafcca 100644
--- a/third_party/blink/renderer/bindings/core/v8/generated_code_helper.cc
+++ b/third_party/blink/renderer/bindings/core/v8/generated_code_helper.cc
@@ -232,6 +232,40 @@
                                : nullptr;
 }
 
+v8::MaybeLocal<v8::Function> CreateNamedConstructorFunction(
+    ScriptState* script_state,
+    v8::FunctionCallback callback,
+    const char* func_name,
+    int func_length,
+    const WrapperTypeInfo* wrapper_type_info) {
+  v8::Isolate* isolate = script_state->GetIsolate();
+  const DOMWrapperWorld& world = script_state->World();
+  V8PerIsolateData* per_isolate_data = V8PerIsolateData::From(isolate);
+  const void* callback_key = reinterpret_cast<const void*>(callback);
+
+  // Named constructors are not interface objcets (despite that they're
+  // pretending so), but we reuse the cache of interface objects, which just
+  // works because both are V8 function template.
+  v8::Local<v8::FunctionTemplate> function_template =
+      per_isolate_data->FindInterfaceTemplate(world, callback_key);
+  if (function_template.IsEmpty()) {
+    function_template = v8::FunctionTemplate::New(
+        isolate, callback, v8::Local<v8::Value>(), v8::Local<v8::Signature>(),
+        func_length, v8::ConstructorBehavior::kAllow,
+        v8::SideEffectType::kHasSideEffect);
+    v8::Local<v8::FunctionTemplate> interface_template =
+        wrapper_type_info->DomTemplate(isolate, world);
+    function_template->SetPrototypeProviderTemplate(interface_template);
+    function_template->ReadOnlyPrototype();
+    function_template->SetClassName(V8AtomicString(isolate, func_name));
+    function_template->InstanceTemplate()->SetInternalFieldCount(
+        kV8DefaultWrapperInternalFieldCount);
+    per_isolate_data->SetInterfaceTemplate(world, callback_key,
+                                           function_template);
+  }
+  return function_template->GetFunction(script_state->GetContext());
+}
+
 v8::Local<v8::Array> EnumerateIndexedProperties(v8::Isolate* isolate,
                                                 uint32_t length) {
   Vector<v8::Local<v8::Value>> elements;
diff --git a/third_party/blink/renderer/bindings/core/v8/generated_code_helper.h b/third_party/blink/renderer/bindings/core/v8/generated_code_helper.h
index b461f47..4907fc9 100644
--- a/third_party/blink/renderer/bindings/core/v8/generated_code_helper.h
+++ b/third_party/blink/renderer/bindings/core/v8/generated_code_helper.h
@@ -151,6 +151,13 @@
 CORE_EXPORT ExecutionContext* ExecutionContextFromV8Wrappable(
     const DOMParser* parser);
 
+CORE_EXPORT v8::MaybeLocal<v8::Function> CreateNamedConstructorFunction(
+    ScriptState* script_state,
+    v8::FunctionCallback callback,
+    const char* func_name,
+    int func_length,
+    const WrapperTypeInfo* wrapper_type_info);
+
 CORE_EXPORT v8::Local<v8::Array> EnumerateIndexedProperties(
     v8::Isolate* isolate,
     uint32_t length);
diff --git a/third_party/blink/renderer/bindings/core/v8/v8_dom_configuration.cc b/third_party/blink/renderer/bindings/core/v8/v8_dom_configuration.cc
index 6809daa..53926f2 100644
--- a/third_party/blink/renderer/bindings/core/v8/v8_dom_configuration.cc
+++ b/third_party/blink/renderer/bindings/core/v8/v8_dom_configuration.cc
@@ -725,13 +725,13 @@
         static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontDelete),
         v8::Local<v8::AccessorSignature>(), v8::DEFAULT,
         v8::SideEffectType::kHasNoSideEffect,
-        v8::SideEffectType::kHasNoSideEffect);
+        v8::SideEffectType::kHasSideEffect);
     prototype_template->SetNativeDataProperty(
         name, constants[i].getter, nullptr, v8::Local<v8::Value>(),
         static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontDelete),
         v8::Local<v8::AccessorSignature>(), v8::DEFAULT,
         v8::SideEffectType::kHasNoSideEffect,
-        v8::SideEffectType::kHasNoSideEffect);
+        v8::SideEffectType::kHasSideEffect);
   }
 }
 
diff --git a/third_party/blink/renderer/bindings/generated_in_core.gni b/third_party/blink/renderer/bindings/generated_in_core.gni
index 0650640..2e98dff 100644
--- a/third_party/blink/renderer/bindings/generated_in_core.gni
+++ b/third_party/blink/renderer/bindings/generated_in_core.gni
@@ -63,12 +63,16 @@
   "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_native_scroll_behavior.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_navigation_type.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_navigation_type.h",
+  "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_operation_type.cc",
+  "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_operation_type.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_playback_direction.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_playback_direction.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_premultiply_alpha.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_premultiply_alpha.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_referrer_policy.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_referrer_policy.h",
+  "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_refresh_policy.cc",
+  "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_refresh_policy.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_replace_state.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_replace_state.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_request_cache.cc",
@@ -107,6 +111,8 @@
   "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_selection_mode.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_shadow_root_mode.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_shadow_root_mode.h",
+  "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_sign_request_data.cc",
+  "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_sign_request_data.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_slot_assignment_mode.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_slot_assignment_mode.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_supported_type.cc",
diff --git a/third_party/blink/renderer/bindings/generated_in_modules.gni b/third_party/blink/renderer/bindings/generated_in_modules.gni
index fac8c3d..997d5288 100644
--- a/third_party/blink/renderer/bindings/generated_in_modules.gni
+++ b/third_party/blink/renderer/bindings/generated_in_modules.gni
@@ -91,6 +91,8 @@
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_gpu_cull_mode.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_gpu_error_filter.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_gpu_error_filter.h",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_gpu_extension_name.cc",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_gpu_extension_name.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_gpu_filter_mode.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_gpu_filter_mode.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_gpu_front_face.cc",
@@ -185,6 +187,8 @@
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_orientation_type.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_oscillator_type.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_oscillator_type.h",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_otp_credential_transport_type.cc",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_otp_credential_transport_type.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_over_sample_type.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_over_sample_type.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_panning_model_type.cc",
@@ -219,6 +223,8 @@
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_rtc_bundle_policy.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_rtc_data_channel_state.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_rtc_data_channel_state.h",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_rtc_degradation_preference.cc",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_rtc_degradation_preference.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_rtc_dtls_transport_state.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_rtc_dtls_transport_state.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_rtc_error_detail_type.cc",
diff --git a/third_party/blink/renderer/bindings/scripts/bind_gen/codegen_context.py b/third_party/blink/renderer/bindings/scripts/bind_gen/codegen_context.py
index ae9defa4..7ef1ef1 100644
--- a/third_party/blink/renderer/bindings/scripts/bind_gen/codegen_context.py
+++ b/third_party/blink/renderer/bindings/scripts/bind_gen/codegen_context.py
@@ -71,6 +71,7 @@
             "constructor_group": None,
             "dict_member": None,
             "exposed_construct": None,
+            "is_named_constructor": False,
             "legacy_window_alias": None,
             "operation": None,
             "operation_group": None,
diff --git a/third_party/blink/renderer/bindings/scripts/bind_gen/interface.py b/third_party/blink/renderer/bindings/scripts/bind_gen/interface.py
index 9d2ce69..e00dbd2e 100644
--- a/third_party/blink/renderer/bindings/scripts/bind_gen/interface.py
+++ b/third_party/blink/renderer/bindings/scripts/bind_gen/interface.py
@@ -126,10 +126,15 @@
     elif cg_context.constant:
         kind = "Constant"
     elif cg_context.constructor_group:
-        property_name = ""
-        kind = "Constructor"
+        if cg_context.is_named_constructor:
+            kind = "NamedConstructor"
+        else:
+            property_name = ""
+            kind = "Constructor"
     elif cg_context.exposed_construct:
-        if cg_context.legacy_window_alias:
+        if cg_context.is_named_constructor:
+            kind = "NamedConstructorProperty"
+        elif cg_context.legacy_window_alias:
             kind = "LegacyWindowAlias"
         else:
             kind = "ExposedConstruct"
@@ -562,7 +567,10 @@
 
     func_name = backward_compatible_api_func(cg_context)
     if cg_context.constructor:
-        func_name = "Create"
+        if cg_context.is_named_constructor:
+            func_name = "CreateForJSConstructor"
+        else:
+            func_name = "Create"
     if "Reflect" in ext_attrs:  # [Reflect]
         func_name = _make_reflect_accessor_func_name(cg_context)
 
@@ -676,7 +684,7 @@
         event_name = "{}.{}".format(event_name, "get")
     elif cg_context.attribute_set:
         event_name = "{}.{}".format(event_name, "set")
-    elif cg_context.constructor_group:
+    elif cg_context.constructor_group and not cg_context.is_named_constructor:
         event_name = "{}.{}".format(cg_context.class_like.identifier,
                                     "constructor")
 
@@ -729,7 +737,7 @@
 
     T = TextNode
 
-    return SequenceNode([
+    node = SequenceNode([
         CxxUnlikelyIfNode(
             cond="!${info}.IsConstructCall()",
             body=T("${exception_state}.ThrowTypeError("
@@ -742,6 +750,11 @@
             body=T("bindings::V8SetReturnValue(${info}, ${v8_receiver});\n"
                    "return;")),
     ])
+    node.accumulate(
+        CodeGenAccumulator.require_include_headers([
+            "third_party/blink/renderer/platform/bindings/v8_object_constructor.h",
+        ]))
+    return node
 
 
 def make_check_receiver(cg_context):
@@ -1732,6 +1745,80 @@
     return func_def
 
 
+def make_named_constructor_property_callback_def(cg_context, function_name):
+    assert isinstance(cg_context, CodeGenContext)
+    assert isinstance(function_name, str)
+
+    func_def = _make_empty_callback_def(cg_context, function_name)
+    body = func_def.body
+
+    body.extend([
+        make_runtime_call_timer_scope(cg_context),
+        make_bindings_trace_event(cg_context),
+        make_report_deprecate_as(cg_context),
+        make_report_measure_as(cg_context),
+        make_log_activity(cg_context),
+        EmptyNode(),
+    ])
+
+    constructor_group = cg_context.exposed_construct
+    assert isinstance(constructor_group, web_idl.ConstructorGroup)
+    assert isinstance(constructor_group.owner, web_idl.Interface)
+    named_ctor_v8_bridge = v8_bridge_class_name(constructor_group.owner)
+    cgc = CodeGenContext(
+        interface=constructor_group.owner,
+        constructor_group=constructor_group,
+        is_named_constructor=True,
+        class_name=named_ctor_v8_bridge)
+    named_ctor_name = callback_function_name(cgc)
+    named_ctor_def = make_constructor_callback_def(cgc, named_ctor_name)
+
+    return_value_cache_return_early = """\
+static const V8PrivateProperty::SymbolKey kPrivatePropertyNamedConstructor;
+auto&& v8_private_named_constructor =
+    V8PrivateProperty::GetSymbol(${isolate}, kPrivatePropertyNamedConstructor);
+v8::Local<v8::Value> v8_named_constructor;
+if (v8_private_named_constructor.GetOrUndefined(${v8_receiver})
+        .ToLocal(&v8_named_constructor) &&
+    !v8_named_constructor->IsUndefined()) {
+  bindings::V8SetReturnValue(${info}, v8_named_constructor);
+  return;
+}
+"""
+
+    pattern = """\
+v8::Local<v8::Function> v8_function;
+if (!bindings::CreateNamedConstructorFunction(
+         ${script_state},
+         {callback},
+         "{func_name}",
+         {func_length},
+         {v8_bridge}::GetWrapperTypeInfo())
+     .ToLocal(&v8_function)) {
+  return;
+}
+bindings::V8SetReturnValue(${info}, v8_function);
+"""
+    create_named_constructor_function = _format(
+        pattern,
+        callback=named_ctor_name,
+        func_name=constructor_group.identifier,
+        func_length=constructor_group.min_num_of_required_arguments,
+        v8_bridge=named_ctor_v8_bridge)
+
+    return_value_cache_update_value = """\
+v8_private_named_constructor.Set(${v8_receiver}, v8_function);
+"""
+
+    body.extend([
+        TextNode(return_value_cache_return_early),
+        TextNode(create_named_constructor_function),
+        TextNode(return_value_cache_update_value),
+    ])
+
+    return SequenceNode([named_ctor_def, EmptyNode(), func_def])
+
+
 def make_operation_entry(cg_context):
     assert isinstance(cg_context, CodeGenContext)
 
@@ -3271,6 +3358,31 @@
                 exposed_construct=exposed_construct,
                 prop_callback_name=prop_callback_name))
 
+    def process_named_constructor_group(named_constructor_group,
+                                        is_context_dependent,
+                                        exposure_conditional, world):
+        cgc = cg_context.make_copy(
+            exposed_construct=named_constructor_group,
+            is_named_constructor=True,
+            for_world=world,
+            v8_callback_type=CodeGenContext.V8_ACCESSOR_NAME_GETTER_CALLBACK)
+        prop_callback_name = callback_function_name(cgc)
+        prop_callback_node = make_named_constructor_property_callback_def(
+            cgc, prop_callback_name)
+
+        callback_def_nodes.extend([
+            prop_callback_node,
+            EmptyNode(),
+        ])
+
+        exposed_construct_entries.append(
+            _PropEntryExposedConstruct(
+                is_context_dependent=is_context_dependent,
+                exposure_conditional=exposure_conditional,
+                world=world,
+                exposed_construct=named_constructor_group,
+                prop_callback_name=prop_callback_name))
+
     def process_operation_group(operation_group, is_context_dependent,
                                 exposure_conditional, world):
         cgc = cg_context.make_copy(
@@ -3318,6 +3430,12 @@
     iterate(interface.constructor_groups, process_constructor_group)
     iterate(interface.exposed_constructs, process_exposed_construct)
     iterate(interface.legacy_window_aliases, process_exposed_construct)
+    named_constructor_groups = [
+        group for construct in interface.exposed_constructs
+        for group in construct.named_constructor_groups
+        if construct.named_constructor_groups
+    ]
+    iterate(named_constructor_groups, process_named_constructor_group)
     iterate(interface.operation_groups, process_operation_group)
     if interface.stringifier:
         iterate([interface.stringifier.operation], process_stringifier)
@@ -4680,10 +4798,6 @@
     ])
     impl_source_node.accumulator.add_include_headers(
         _collect_include_headers(interface))
-    if interface.constructor_groups:
-        impl_source_node.accumulator.add_include_headers([
-            "third_party/blink/renderer/platform/bindings/v8_object_constructor.h",
-        ])
 
     # Assemble the parts.
     api_header_blink_ns.body.append(api_class_def)
diff --git a/third_party/blink/renderer/build/scripts/core/css/templates/cssom_keywords.cc.tmpl b/third_party/blink/renderer/build/scripts/core/css/templates/cssom_keywords.cc.tmpl
index f5a02e8..87caef2 100644
--- a/third_party/blink/renderer/build/scripts/core/css/templates/cssom_keywords.cc.tmpl
+++ b/third_party/blink/renderer/build/scripts/core/css/templates/cssom_keywords.cc.tmpl
@@ -10,6 +10,7 @@
 #include "third_party/blink/renderer/core/css/css_property_id_templates.h"
 #include "third_party/blink/renderer/core/css/cssom/css_keyword_value.h"
 #include "third_party/blink/renderer/platform/wtf/hash_map.h"
+#include "third_party/blink/renderer/core/css/properties/css_parsing_utils.h"
 
 namespace blink {
 
@@ -20,11 +21,8 @@
     return false;
   }
 
-  if (valueID == CSSValueID::kInherit || valueID == CSSValueID::kInitial ||
-      valueID == CSSValueID::kUnset) {
-    // These are css-wide keywords that are valid for all properties.
+  if (css_parsing_utils::IsCSSWideKeyword(valueID))
     return true;
-  }
 
   switch (id) {
   {% for property in properties if property.keywordIDs and 'Keyword' in property.typedom_types %}
diff --git a/third_party/blink/renderer/core/BUILD.gn b/third_party/blink/renderer/core/BUILD.gn
index e9ef6e2..c83e781 100644
--- a/third_party/blink/renderer/core/BUILD.gn
+++ b/third_party/blink/renderer/core/BUILD.gn
@@ -1047,6 +1047,7 @@
   sources = [
     "clipboard/clipboard_utilities_test.cc",
     "content_capture/content_capture_test.cc",
+    "css/css_revert_value_test.cc",
     "css/css_test_helpers.cc",
     "css/css_test_helpers.h",
     "css/css_uri_value_test.cc",
diff --git a/third_party/blink/renderer/core/animation/css_interpolation_type.cc b/third_party/blink/renderer/core/animation/css_interpolation_type.cc
index fcc56d7..efc95c5 100644
--- a/third_party/blink/renderer/core/animation/css_interpolation_type.cc
+++ b/third_party/blink/renderer/core/animation/css_interpolation_type.cc
@@ -12,6 +12,8 @@
 #include "third_party/blink/renderer/core/animation/string_keyframe.h"
 #include "third_party/blink/renderer/core/css/computed_style_css_value_mapping.h"
 #include "third_party/blink/renderer/core/css/css_custom_property_declaration.h"
+#include "third_party/blink/renderer/core/css/css_revert_value.h"
+#include "third_party/blink/renderer/core/css/css_unset_value.h"
 #include "third_party/blink/renderer/core/css/css_value.h"
 #include "third_party/blink/renderer/core/css/css_variable_reference_value.h"
 #include "third_party/blink/renderer/core/css/parser/css_tokenizer.h"
@@ -183,6 +185,11 @@
     value = resolved_value;
   }
 
+  if (value->IsRevertValue()) {
+    // TODO(andruud): Actually revert instead of treating as unset.
+    value = cssvalue::CSSUnsetValue::Create();
+  }
+
   bool is_inherited = CssProperty().IsInherited();
   if (value->IsInitialValue() || (value->IsUnsetValue() && !is_inherited)) {
     return MaybeConvertInitial(state, conversion_checkers);
diff --git a/third_party/blink/renderer/core/css/BUILD.gn b/third_party/blink/renderer/core/css/BUILD.gn
index 183fd3f..3851292 100644
--- a/third_party/blink/renderer/core/css/BUILD.gn
+++ b/third_party/blink/renderer/core/css/BUILD.gn
@@ -155,6 +155,8 @@
     "css_reflection_direction.h",
     "css_resolution_units.h",
     "css_resource_fetch_restriction.h",
+    "css_revert_value.cc",
+    "css_revert_value.h",
     "css_rule.cc",
     "css_rule.h",
     "css_rule_list.h",
@@ -626,6 +628,7 @@
     "cssom/inline_style_property_map_test.cc",
     "cssom/paint_worklet_style_property_map_test.cc",
     "cssom/prepopulated_computed_style_property_map_test.cc",
+    "cssom/style_property_map_test.cc",
     "drag_update_test.cc",
     "font_face_cache_test.cc",
     "invalidation/invalidation_set_test.cc",
@@ -643,6 +646,7 @@
     "parser/css_parser_local_context_test.cc",
     "parser/css_parser_token_stream_test.cc",
     "parser/css_parser_token_test.cc",
+    "parser/css_property_parser_helpers_test.cc",
     "parser/css_property_parser_test.cc",
     "parser/css_selector_parser_test.cc",
     "parser/css_supports_parser_test.cc",
diff --git a/third_party/blink/renderer/core/css/css_computed_style_declaration.cc b/third_party/blink/renderer/core/css/css_computed_style_declaration.cc
index 4df6327..f2b1f0d 100644
--- a/third_party/blink/renderer/core/css/css_computed_style_declaration.cc
+++ b/third_party/blink/renderer/core/css/css_computed_style_declaration.cc
@@ -132,7 +132,7 @@
     CSSPropertyID::kWillChange, CSSPropertyID::kWordBreak,
     CSSPropertyID::kWordSpacing, CSSPropertyID::kZIndex, CSSPropertyID::kZoom,
 
-    CSSPropertyID::kWebkitAppearance, CSSPropertyID::kBackfaceVisibility,
+    CSSPropertyID::kAppearance, CSSPropertyID::kBackfaceVisibility,
     CSSPropertyID::kWebkitBorderHorizontalSpacing,
     CSSPropertyID::kWebkitBorderImage,
     CSSPropertyID::kWebkitBorderVerticalSpacing, CSSPropertyID::kWebkitBoxAlign,
@@ -433,8 +433,7 @@
 String CSSComputedStyleDeclaration::GetPropertyValue(
     CSSPropertyID property_id) const {
   // allow_visited_style_ is true only for access from DevTools.
-  if (!allow_visited_style_ &&
-      property_id == CSSPropertyID::kWebkitAppearance) {
+  if (!allow_visited_style_ && property_id == CSSPropertyID::kAppearance) {
     UseCounter::Count(
         node_->GetDocument(),
         WebFeature::kGetComputedStyleForWebkitAppearanceExcludeDevTools);
@@ -559,7 +558,7 @@
 
 const CSSValue* CSSComputedStyleDeclaration::GetPropertyCSSValueInternal(
     CSSPropertyID property_id) {
-  if (property_id == CSSPropertyID::kWebkitAppearance && node_) {
+  if (property_id == CSSPropertyID::kAppearance && node_) {
     UseCounter::Count(node_->GetDocument(),
                       WebFeature::kGetComputedStyleWebkitAppearance);
   }
diff --git a/third_party/blink/renderer/core/css/css_custom_property_declaration.h b/third_party/blink/renderer/core/css/css_custom_property_declaration.h
index f7d09e70..b870b5b 100644
--- a/third_party/blink/renderer/core/css/css_custom_property_declaration.h
+++ b/third_party/blink/renderer/core/css/css_custom_property_declaration.h
@@ -8,6 +8,7 @@
 #include "base/memory/scoped_refptr.h"
 #include "third_party/blink/renderer/core/css/css_value.h"
 #include "third_party/blink/renderer/core/css/css_variable_data.h"
+#include "third_party/blink/renderer/core/css/properties/css_parsing_utils.h"
 #include "third_party/blink/renderer/core/css_value_keywords.h"
 #include "third_party/blink/renderer/platform/wtf/casting.h"
 #include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
@@ -21,8 +22,7 @@
         name_(name),
         value_(nullptr),
         value_id_(id) {
-    DCHECK(id == CSSValueID::kInherit || id == CSSValueID::kInitial ||
-           id == CSSValueID::kUnset);
+    DCHECK(css_parsing_utils::IsCSSWideKeyword(id));
   }
 
   CSSCustomPropertyDeclaration(const AtomicString& name,
diff --git a/third_party/blink/renderer/core/css/css_math_expression_node.cc b/third_party/blink/renderer/core/css/css_math_expression_node.cc
index b274e03..b60c35e4 100644
--- a/third_party/blink/renderer/core/css/css_math_expression_node.cc
+++ b/third_party/blink/renderer/core/css/css_math_expression_node.cc
@@ -229,10 +229,7 @@
     case kCalcAngle:
     case kCalcFrequency:
     case kCalcPercentLength:
-    case kCalcPercentNumber:
     case kCalcTime:
-    case kCalcLengthNumber:
-    case kCalcPercentLengthNumber:
     case kCalcOther:
       NOTREACHED();
       break;
@@ -285,43 +282,26 @@
 // ------ End of CSSMathExpressionNumericLiteral member functions
 
 static const CalculationCategory kAddSubtractResult[kCalcOther][kCalcOther] = {
-    /* CalcNumber */ {kCalcNumber, kCalcLengthNumber, kCalcPercentNumber,
-                      kCalcPercentNumber, kCalcOther, kCalcOther, kCalcOther,
-                      kCalcOther, kCalcLengthNumber, kCalcPercentLengthNumber},
+    /* CalcNumber */ {kCalcNumber, kCalcOther, kCalcOther, kCalcOther,
+                      kCalcOther, kCalcOther, kCalcOther},
     /* CalcLength */
-    {kCalcLengthNumber, kCalcLength, kCalcPercentLength, kCalcOther,
-     kCalcPercentLength, kCalcOther, kCalcOther, kCalcOther, kCalcLengthNumber,
-     kCalcPercentLengthNumber},
+    {kCalcOther, kCalcLength, kCalcPercentLength, kCalcPercentLength,
+     kCalcOther, kCalcOther, kCalcOther},
     /* CalcPercent */
-    {kCalcPercentNumber, kCalcPercentLength, kCalcPercent, kCalcPercentNumber,
-     kCalcPercentLength, kCalcOther, kCalcOther, kCalcOther,
-     kCalcPercentLengthNumber, kCalcPercentLengthNumber},
-    /* CalcPercentNumber */
-    {kCalcPercentNumber, kCalcPercentLengthNumber, kCalcPercentNumber,
-     kCalcPercentNumber, kCalcPercentLengthNumber, kCalcOther, kCalcOther,
-     kCalcOther, kCalcOther, kCalcPercentLengthNumber},
+    {kCalcOther, kCalcPercentLength, kCalcPercent, kCalcPercentLength,
+     kCalcOther, kCalcOther, kCalcOther},
     /* CalcPercentLength */
-    {kCalcPercentLengthNumber, kCalcPercentLength, kCalcPercentLength,
-     kCalcPercentLengthNumber, kCalcPercentLength, kCalcOther, kCalcOther,
-     kCalcOther, kCalcOther, kCalcPercentLengthNumber},
+    {kCalcOther, kCalcPercentLength, kCalcPercentLength, kCalcPercentLength,
+     kCalcOther, kCalcOther, kCalcOther},
     /* CalcAngle  */
-    {kCalcOther, kCalcOther, kCalcOther, kCalcOther, kCalcOther, kCalcAngle,
-     kCalcOther, kCalcOther, kCalcOther, kCalcOther},
+    {kCalcOther, kCalcOther, kCalcOther, kCalcOther, kCalcAngle, kCalcOther,
+     kCalcOther},
     /* CalcTime */
-    {kCalcOther, kCalcOther, kCalcOther, kCalcOther, kCalcOther, kCalcOther,
-     kCalcTime, kCalcOther, kCalcOther, kCalcOther},
+    {kCalcOther, kCalcOther, kCalcOther, kCalcOther, kCalcOther, kCalcTime,
+     kCalcOther},
     /* CalcFrequency */
     {kCalcOther, kCalcOther, kCalcOther, kCalcOther, kCalcOther, kCalcOther,
-     kCalcOther, kCalcFrequency, kCalcOther, kCalcOther},
-    /* CalcLengthNumber */
-    {kCalcLengthNumber, kCalcLengthNumber, kCalcPercentLengthNumber,
-     kCalcPercentLengthNumber, kCalcPercentLengthNumber, kCalcOther, kCalcOther,
-     kCalcOther, kCalcLengthNumber, kCalcPercentLengthNumber},
-    /* CalcPercentLengthNumber */
-    {kCalcPercentLengthNumber, kCalcPercentLengthNumber,
-     kCalcPercentLengthNumber, kCalcPercentLengthNumber,
-     kCalcPercentLengthNumber, kCalcOther, kCalcOther, kCalcOther,
-     kCalcPercentLengthNumber, kCalcPercentLengthNumber}};
+     kCalcFrequency}};
 
 static CalculationCategory DetermineCategory(
     const CSSMathExpressionNode& left_side,
@@ -729,9 +709,6 @@
     case kCalcFrequency:
       return CSSPrimitiveValue::UnitType::kHertz;
     case kCalcPercentLength:
-    case kCalcPercentNumber:
-    case kCalcLengthNumber:
-    case kCalcPercentLengthNumber:
     case kCalcOther:
       return CSSPrimitiveValue::UnitType::kUnknown;
   }
diff --git a/third_party/blink/renderer/core/css/css_math_expression_node.h b/third_party/blink/renderer/core/css/css_math_expression_node.h
index b1e932b6..1d1b76f 100644
--- a/third_party/blink/renderer/core/css/css_math_expression_node.h
+++ b/third_party/blink/renderer/core/css/css_math_expression_node.h
@@ -47,17 +47,14 @@
 // The order of this enum should not change since its elements are used as
 // indices in the addSubtractResult matrix.
 enum CalculationCategory {
-  kCalcNumber = 0,
+  kCalcNumber,
   kCalcLength,
   kCalcPercent,
-  kCalcPercentNumber,
   kCalcPercentLength,
   kCalcAngle,
   kCalcTime,
   kCalcFrequency,
-  kCalcLengthNumber,
-  kCalcPercentLengthNumber,
-  kCalcOther
+  kCalcOther,
 };
 
 class CORE_EXPORT CSSMathExpressionNode
@@ -121,9 +118,7 @@
 
   CalculationCategory Category() const { return category_; }
   bool HasPercentage() const {
-    return category_ == kCalcPercent || category_ == kCalcPercentNumber ||
-           category_ == kCalcPercentLength ||
-           category_ == kCalcPercentLengthNumber;
+    return category_ == kCalcPercent || category_ == kCalcPercentLength;
   }
 
   // Returns the unit type of the math expression *without doing any type
diff --git a/third_party/blink/renderer/core/css/css_properties.json5 b/third_party/blink/renderer/core/css/css_properties.json5
index 3f4c10be..5f83f4d 100644
--- a/third_party/blink/renderer/core/css/css_properties.json5
+++ b/third_party/blink/renderer/core/css/css_properties.json5
@@ -2634,7 +2634,6 @@
       field_template: "primitive",
       default_value: "0",
       type_name: "int",
-      computed_style_custom_functions: ["getter"],
       converter: "ConvertComputedLength<int>",
       typedom_types: ["Length"],
       valid_for_cue: true,
@@ -3732,8 +3731,8 @@
       converter: "ConvertLength",
     },
     {
-      name: "-webkit-appearance",
-      property_methods: ["CSSValueFromComputedStyleInternal"],
+      name: "appearance",
+      property_methods: ["ParseSingleValue", "CSSValueFromComputedStyleInternal"],
       field_group: "*",
       field_size: 5,
       field_template: "primitive",
@@ -3742,6 +3741,10 @@
       type_name: "ControlPart",
     },
     {
+      name: "-webkit-appearance",
+      alias_for: "appearance",
+    },
+    {
       name: "-webkit-app-region",
       property_methods: ["CSSValueFromComputedStyleInternal"],
       field_group: "*",
diff --git a/third_party/blink/renderer/core/css/css_revert_value.cc b/third_party/blink/renderer/core/css/css_revert_value.cc
new file mode 100644
index 0000000..e0d1b7b
--- /dev/null
+++ b/third_party/blink/renderer/core/css/css_revert_value.cc
@@ -0,0 +1,24 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/core/css/css_revert_value.h"
+
+#include "third_party/blink/renderer/core/css/css_value_pool.h"
+#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+namespace cssvalue {
+
+CSSRevertValue* CSSRevertValue::Create() {
+  DCHECK(RuntimeEnabledFeatures::CSSRevertEnabled());
+  return CssValuePool().RevertValue();
+}
+
+String CSSRevertValue::CustomCSSText() const {
+  return "revert";
+}
+
+}  // namespace cssvalue
+}  // namespace blink
diff --git a/third_party/blink/renderer/core/css/css_revert_value.h b/third_party/blink/renderer/core/css/css_revert_value.h
new file mode 100644
index 0000000..5630c91
--- /dev/null
+++ b/third_party/blink/renderer/core/css/css_revert_value.h
@@ -0,0 +1,44 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_CSS_CSS_REVERT_VALUE_H_
+#define THIRD_PARTY_BLINK_RENDERER_CORE_CSS_CSS_REVERT_VALUE_H_
+
+#include "base/memory/scoped_refptr.h"
+#include "base/util/type_safety/pass_key.h"
+#include "third_party/blink/renderer/core/css/css_value.h"
+#include "third_party/blink/renderer/platform/wtf/casting.h"
+
+namespace blink {
+
+class CSSValuePool;
+
+namespace cssvalue {
+
+class CORE_EXPORT CSSRevertValue : public CSSValue {
+ public:
+  static CSSRevertValue* Create();
+
+  explicit CSSRevertValue(util::PassKey<CSSValuePool>)
+      : CSSValue(kRevertClass) {}
+
+  String CustomCSSText() const;
+
+  bool Equals(const CSSRevertValue&) const { return true; }
+
+  void TraceAfterDispatch(blink::Visitor* visitor) {
+    CSSValue::TraceAfterDispatch(visitor);
+  }
+};
+
+}  // namespace cssvalue
+
+template <>
+struct DowncastTraits<cssvalue::CSSRevertValue> {
+  static bool AllowFrom(const CSSValue& value) { return value.IsRevertValue(); }
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_CSS_CSS_REVERT_VALUE_H_
diff --git a/third_party/blink/renderer/core/css/css_revert_value_test.cc b/third_party/blink/renderer/core/css/css_revert_value_test.cc
new file mode 100644
index 0000000..a3508c2
--- /dev/null
+++ b/third_party/blink/renderer/core/css/css_revert_value_test.cc
@@ -0,0 +1,36 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/core/css/css_revert_value.h"
+#include "third_party/blink/renderer/core/css/css_initial_value.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+using CSSRevertValue = cssvalue::CSSRevertValue;
+
+TEST(CSSRevertValueTest, IsCSSWideKeyword) {
+  ScopedCSSRevertForTest scoped_revert(true);
+  EXPECT_TRUE(CSSRevertValue::Create()->IsCSSWideKeyword());
+}
+
+TEST(CSSRevertValueTest, CssText) {
+  ScopedCSSRevertForTest scoped_revert(true);
+  EXPECT_EQ("revert", CSSRevertValue::Create()->CssText());
+}
+
+TEST(CSSRevertValueTest, Equals) {
+  ScopedCSSRevertForTest scoped_revert(true);
+  EXPECT_EQ(*CSSRevertValue::Create(), *CSSRevertValue::Create());
+}
+
+TEST(CSSRevertValueTest, NotEquals) {
+  ScopedCSSRevertForTest scoped_revert(true);
+  EXPECT_FALSE(*CSSRevertValue::Create() == *CSSInitialValue::Create());
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/core/css/css_style_declaration_test.cc b/third_party/blink/renderer/core/css/css_style_declaration_test.cc
index f4a1572..5bfe780 100644
--- a/third_party/blink/renderer/core/css/css_style_declaration_test.cc
+++ b/third_party/blink/renderer/core/css/css_style_declaration_test.cc
@@ -7,6 +7,8 @@
 #include "third_party/blink/renderer/core/css/css_rule_list.h"
 #include "third_party/blink/renderer/core/css/css_style_rule.h"
 #include "third_party/blink/renderer/core/css/css_test_helpers.h"
+#include "third_party/blink/renderer/core/dom/document.h"
+#include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
 
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -25,4 +27,62 @@
   EXPECT_EQ(AtomicString(), style->GetPropertyShorthand("padding"));
 }
 
+TEST(CSSStyleDeclarationTest, ParsingRevertWithFeatureEnabled) {
+  ScopedCSSRevertForTest scoped_revert(true);
+
+  css_test_helpers::TestStyleSheet sheet;
+  sheet.AddCSSRules("div { top: revert; --x: revert; }");
+  ASSERT_TRUE(sheet.CssRules());
+  ASSERT_EQ(1u, sheet.CssRules()->length());
+  CSSStyleRule* style_rule = To<CSSStyleRule>(sheet.CssRules()->item(0));
+  CSSStyleDeclaration* style = style_rule->style();
+  ASSERT_TRUE(style);
+  EXPECT_EQ("revert", style->getPropertyValue("top"));
+  EXPECT_EQ("revert", style->getPropertyValue("--x"));
+
+  // Test setProperty/getPropertyValue:
+
+  DummyExceptionStateForTesting exception_state;
+
+  style->SetPropertyInternal(CSSPropertyID::kLeft, "left", "revert", false,
+                             SecureContextMode::kSecureContext,
+                             exception_state);
+  style->SetPropertyInternal(CSSPropertyID::kVariable, "--y", " revert", false,
+                             SecureContextMode::kSecureContext,
+                             exception_state);
+
+  EXPECT_EQ("revert", style->getPropertyValue("left"));
+  EXPECT_EQ("revert", style->getPropertyValue("--y"));
+  EXPECT_FALSE(exception_state.HadException());
+}
+
+TEST(CSSStyleDeclarationTest, ParsingRevertWithFeatureDisabled) {
+  ScopedCSSRevertForTest scoped_revert(false);
+
+  css_test_helpers::TestStyleSheet sheet;
+  sheet.AddCSSRules("div { top: revert; --x: revert; }");
+  ASSERT_TRUE(sheet.CssRules());
+  ASSERT_EQ(1u, sheet.CssRules()->length());
+  CSSStyleRule* style_rule = To<CSSStyleRule>(sheet.CssRules()->item(0));
+  CSSStyleDeclaration* style = style_rule->style();
+  ASSERT_TRUE(style);
+  EXPECT_EQ("", style->getPropertyValue("top"));
+  EXPECT_EQ(" revert", style->getPropertyValue("--x"));
+
+  // Test setProperty/getPropertyValue:
+
+  DummyExceptionStateForTesting exception_state;
+
+  style->SetPropertyInternal(CSSPropertyID::kLeft, "left", "revert", false,
+                             SecureContextMode::kSecureContext,
+                             exception_state);
+  style->SetPropertyInternal(CSSPropertyID::kVariable, "--y", " revert", false,
+                             SecureContextMode::kSecureContext,
+                             exception_state);
+
+  EXPECT_EQ("", style->getPropertyValue("left"));
+  EXPECT_EQ(" revert", style->getPropertyValue("--y"));
+  EXPECT_FALSE(exception_state.HadException());
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/css/css_syntax_string_parser.cc b/third_party/blink/renderer/core/css/css_syntax_string_parser.cc
index 41c8165..a045ab4 100644
--- a/third_party/blink/renderer/core/css/css_syntax_string_parser.cc
+++ b/third_party/blink/renderer/core/css/css_syntax_string_parser.cc
@@ -150,7 +150,6 @@
 
 bool CSSSyntaxStringParser::ConsumeIdent(String& ident) {
   ident = ConsumeName(input_);
-  // TODO(crbug.com/579788): Implement 'revert'.
   // TODO(crbug.com/882285): Make 'default' invalid as <custom-ident>.
   return !css_property_parser_helpers::IsCSSWideKeyword(ident) &&
          !css_property_parser_helpers::IsRevertKeyword(ident) &&
diff --git a/third_party/blink/renderer/core/css/css_syntax_string_parser_test.cc b/third_party/blink/renderer/core/css/css_syntax_string_parser_test.cc
index 9dc46a5..fc8bf60 100644
--- a/third_party/blink/renderer/core/css/css_syntax_string_parser_test.cc
+++ b/third_party/blink/renderer/core/css/css_syntax_string_parser_test.cc
@@ -5,6 +5,7 @@
 #include "third_party/blink/renderer/core/css/css_syntax_string_parser.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/renderer/core/css/css_syntax_component.h"
+#include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
 
 namespace blink {
 
@@ -118,6 +119,15 @@
   EXPECT_FALSE(CSSSyntaxStringParser("initial").Parse());
   EXPECT_FALSE(CSSSyntaxStringParser("inherit").Parse());
   EXPECT_FALSE(CSSSyntaxStringParser("unset").Parse());
+  EXPECT_FALSE(CSSSyntaxStringParser("default").Parse());
+  EXPECT_FALSE(CSSSyntaxStringParser("revert").Parse());
+
+  // 'revert' is forbidden also with CSSRevert toggled.
+  {
+    ScopedCSSRevertForTest scoped_revert(
+        !RuntimeEnabledFeatures::CSSRevertEnabled());
+    EXPECT_FALSE(CSSSyntaxStringParser("revert").Parse());
+  }
 }
 
 TEST_F(CSSSyntaxStringParserTest, Combinator) {
diff --git a/third_party/blink/renderer/core/css/css_value.cc b/third_party/blink/renderer/core/css/css_value.cc
index d849ce27..ed0ac04c 100644
--- a/third_party/blink/renderer/core/css/css_value.cc
+++ b/third_party/blink/renderer/core/css/css_value.cc
@@ -65,6 +65,7 @@
 #include "third_party/blink/renderer/core/css/css_quad_value.h"
 #include "third_party/blink/renderer/core/css/css_ray_value.h"
 #include "third_party/blink/renderer/core/css/css_reflect_value.h"
+#include "third_party/blink/renderer/core/css/css_revert_value.h"
 #include "third_party/blink/renderer/core/css/css_shadow_value.h"
 #include "third_party/blink/renderer/core/css/css_string_value.h"
 #include "third_party/blink/renderer/core/css/css_timing_function_value.h"
@@ -212,6 +213,8 @@
         return CompareCSSValues<CSSInitialValue>(*this, other);
       case kUnsetClass:
         return CompareCSSValues<cssvalue::CSSUnsetValue>(*this, other);
+      case kRevertClass:
+        return CompareCSSValues<cssvalue::CSSRevertValue>(*this, other);
       case kGridAutoRepeatClass:
         return CompareCSSValues<cssvalue::CSSGridAutoRepeatValue>(*this, other);
       case kGridIntegerRepeatClass:
@@ -331,6 +334,8 @@
       return To<CSSInheritedValue>(this)->CustomCSSText();
     case kUnsetClass:
       return To<cssvalue::CSSUnsetValue>(this)->CustomCSSText();
+    case kRevertClass:
+      return To<cssvalue::CSSRevertValue>(this)->CustomCSSText();
     case kInitialClass:
       return To<CSSInitialValue>(this)->CustomCSSText();
     case kGridAutoRepeatClass:
@@ -475,6 +480,9 @@
     case kUnsetClass:
       To<cssvalue::CSSUnsetValue>(this)->~CSSUnsetValue();
       return;
+    case kRevertClass:
+      To<cssvalue::CSSRevertValue>(this)->~CSSRevertValue();
+      return;
     case kGridAutoRepeatClass:
       To<cssvalue::CSSGridAutoRepeatValue>(this)->~CSSGridAutoRepeatValue();
       return;
@@ -648,6 +656,9 @@
     case kUnsetClass:
       To<cssvalue::CSSUnsetValue>(this)->TraceAfterDispatch(visitor);
       return;
+    case kRevertClass:
+      To<cssvalue::CSSRevertValue>(this)->TraceAfterDispatch(visitor);
+      return;
     case kGridAutoRepeatClass:
       To<cssvalue::CSSGridAutoRepeatValue>(this)->TraceAfterDispatch(visitor);
       return;
diff --git a/third_party/blink/renderer/core/css/css_value.h b/third_party/blink/renderer/core/css/css_value.h
index db205280..d4bc1ac 100644
--- a/third_party/blink/renderer/core/css/css_value.h
+++ b/third_party/blink/renderer/core/css/css_value.h
@@ -109,8 +109,9 @@
   bool IsInheritedValue() const { return class_type_ == kInheritedClass; }
   bool IsInitialValue() const { return class_type_ == kInitialClass; }
   bool IsUnsetValue() const { return class_type_ == kUnsetClass; }
+  bool IsRevertValue() const { return class_type_ == kRevertClass; }
   bool IsCSSWideKeyword() const {
-    return class_type_ >= kInheritedClass && class_type_ <= kUnsetClass;
+    return class_type_ >= kInheritedClass && class_type_ <= kRevertClass;
   }
   bool IsLayoutFunctionValue() const {
     return class_type_ == kLayoutFunctionClass;
@@ -236,6 +237,7 @@
     kInheritedClass,
     kInitialClass,
     kUnsetClass,
+    kRevertClass,
 
     kReflectClass,
     kShadowClass,
diff --git a/third_party/blink/renderer/core/css/css_value_pool.cc b/third_party/blink/renderer/core/css/css_value_pool.cc
index 1d62b641..fa730f1 100644
--- a/third_party/blink/renderer/core/css/css_value_pool.cc
+++ b/third_party/blink/renderer/core/css/css_value_pool.cc
@@ -45,6 +45,7 @@
     : inherited_value_(MakeGarbageCollected<CSSInheritedValue>()),
       initial_value_(MakeGarbageCollected<CSSInitialValue>()),
       unset_value_(MakeGarbageCollected<CSSUnsetValue>(PassKey())),
+      revert_value_(MakeGarbageCollected<CSSRevertValue>(PassKey())),
       invalid_variable_value_(MakeGarbageCollected<CSSInvalidVariableValue>()),
       color_transparent_(
           MakeGarbageCollected<cssvalue::CSSColorValue>(Color::kTransparent)),
@@ -62,6 +63,7 @@
   visitor->Trace(inherited_value_);
   visitor->Trace(initial_value_);
   visitor->Trace(unset_value_);
+  visitor->Trace(revert_value_);
   visitor->Trace(invalid_variable_value_);
   visitor->Trace(color_transparent_);
   visitor->Trace(color_white_);
diff --git a/third_party/blink/renderer/core/css/css_value_pool.h b/third_party/blink/renderer/core/css/css_value_pool.h
index c2fb257..7d978f1c 100644
--- a/third_party/blink/renderer/core/css/css_value_pool.h
+++ b/third_party/blink/renderer/core/css/css_value_pool.h
@@ -39,6 +39,7 @@
 #include "third_party/blink/renderer/core/css/css_invalid_variable_value.h"
 #include "third_party/blink/renderer/core/css/css_numeric_literal_value.h"
 #include "third_party/blink/renderer/core/css/css_property_names.h"
+#include "third_party/blink/renderer/core/css/css_revert_value.h"
 #include "third_party/blink/renderer/core/css/css_unset_value.h"
 #include "third_party/blink/renderer/core/css/css_value_list.h"
 #include "third_party/blink/renderer/core/css_value_keywords.h"
@@ -55,6 +56,7 @@
   static const int kMaximumCacheableIntegerValue = 255;
   using CSSColorValue = cssvalue::CSSColorValue;
   using CSSUnsetValue = cssvalue::CSSUnsetValue;
+  using CSSRevertValue = cssvalue::CSSRevertValue;
   using ColorValueCache = HeapHashMap<unsigned, Member<CSSColorValue>>;
   static const unsigned kMaximumColorCacheSize = 512;
   using FontFaceValueCache =
@@ -71,6 +73,7 @@
   CSSInheritedValue* InheritedValue() { return inherited_value_; }
   CSSInitialValue* InitialValue() { return initial_value_; }
   CSSUnsetValue* UnsetValue() { return unset_value_; }
+  CSSRevertValue* RevertValue() { return revert_value_; }
   CSSInvalidVariableValue* InvalidVariableValue() {
     return invalid_variable_value_;
   }
@@ -134,6 +137,7 @@
   Member<CSSInheritedValue> inherited_value_;
   Member<CSSInitialValue> initial_value_;
   Member<CSSUnsetValue> unset_value_;
+  Member<CSSRevertValue> revert_value_;
   Member<CSSInvalidVariableValue> invalid_variable_value_;
   Member<CSSColorValue> color_transparent_;
   Member<CSSColorValue> color_white_;
diff --git a/third_party/blink/renderer/core/css/cssom/css_keyword_value.cc b/third_party/blink/renderer/core/css/cssom/css_keyword_value.cc
index 28df68d1..f7fbef3b 100644
--- a/third_party/blink/renderer/core/css/cssom/css_keyword_value.cc
+++ b/third_party/blink/renderer/core/css/cssom/css_keyword_value.cc
@@ -8,6 +8,7 @@
 #include "third_party/blink/renderer/core/css/css_identifier_value.h"
 #include "third_party/blink/renderer/core/css/css_inherited_value.h"
 #include "third_party/blink/renderer/core/css/css_initial_value.h"
+#include "third_party/blink/renderer/core/css/css_revert_value.h"
 #include "third_party/blink/renderer/core/css/css_unset_value.h"
 #include "third_party/blink/renderer/core/css/parser/css_property_parser.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
@@ -38,6 +39,10 @@
     return MakeGarbageCollected<CSSKeywordValue>(
         getValueName(CSSValueID::kUnset));
   }
+  if (value.IsRevertValue()) {
+    return MakeGarbageCollected<CSSKeywordValue>(
+        getValueName(CSSValueID::kRevert));
+  }
   if (auto* identifier_value = DynamicTo<CSSIdentifierValue>(value)) {
     return MakeGarbageCollected<CSSKeywordValue>(
         getValueName(identifier_value->GetValueID()));
@@ -86,6 +91,8 @@
       return CSSInitialValue::Create();
     case (CSSValueID::kUnset):
       return cssvalue::CSSUnsetValue::Create();
+    case (CSSValueID::kRevert):
+      return cssvalue::CSSRevertValue::Create();
     case (CSSValueID::kInvalid):
       return MakeGarbageCollected<CSSCustomIdentValue>(
           AtomicString(keyword_value_));
diff --git a/third_party/blink/renderer/core/css/cssom/style_property_map_test.cc b/third_party/blink/renderer/core/css/cssom/style_property_map_test.cc
new file mode 100644
index 0000000..25d5c42
--- /dev/null
+++ b/third_party/blink/renderer/core/css/cssom/style_property_map_test.cc
@@ -0,0 +1,100 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/core/css/cssom/style_property_map.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/core/css/cssom/css_keyword_value.h"
+#include "third_party/blink/renderer/core/css/cssom/inline_style_property_map.h"
+#include "third_party/blink/renderer/core/html/html_element.h"
+#include "third_party/blink/renderer/core/testing/page_test_base.h"
+#include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
+
+namespace blink {
+
+class StylePropertyMapTest : public PageTestBase {};
+
+TEST_F(StylePropertyMapTest, SetRevertWithFeatureEnabled) {
+  ScopedCSSRevertForTest scoped_revert(true);
+
+  DummyExceptionStateForTesting exception_state;
+
+  HeapVector<CSSStyleValueOrString> revert_string;
+  revert_string.push_back(CSSStyleValueOrString::FromString(" revert"));
+
+  HeapVector<CSSStyleValueOrString> revert_style_value;
+  revert_style_value.push_back(CSSStyleValueOrString::FromCSSStyleValue(
+      CSSKeywordValue::Create("revert", exception_state)));
+
+  auto* map =
+      MakeGarbageCollected<InlineStylePropertyMap>(GetDocument().body());
+
+  map->set(GetDocument().ToExecutionContext(), "top", revert_string,
+           exception_state);
+  map->set(GetDocument().ToExecutionContext(), "left", revert_style_value,
+           exception_state);
+
+  CSSStyleValue* top =
+      map->get(GetDocument().ToExecutionContext(), "top", exception_state);
+  CSSStyleValue* left =
+      map->get(GetDocument().ToExecutionContext(), "left", exception_state);
+
+  ASSERT_TRUE(DynamicTo<CSSKeywordValue>(top));
+  EXPECT_EQ(CSSValueID::kRevert,
+            DynamicTo<CSSKeywordValue>(top)->KeywordValueID());
+
+  ASSERT_TRUE(DynamicTo<CSSKeywordValue>(left));
+  EXPECT_EQ(CSSValueID::kRevert,
+            DynamicTo<CSSKeywordValue>(top)->KeywordValueID());
+
+  EXPECT_FALSE(exception_state.HadException());
+}
+
+TEST_F(StylePropertyMapTest, SetRevertWithFeatureDisabled) {
+  ScopedCSSRevertForTest scoped_revert(false);
+
+  HeapVector<CSSStyleValueOrString> revert_string;
+  revert_string.push_back(CSSStyleValueOrString::FromString(" revert"));
+
+  HeapVector<CSSStyleValueOrString> revert_style_value;
+
+  DummyExceptionStateForTesting exception_state;
+  revert_style_value.push_back(CSSStyleValueOrString::FromCSSStyleValue(
+      CSSKeywordValue::Create("revert", exception_state)));
+  EXPECT_FALSE(exception_state.HadException());
+
+  auto* map =
+      MakeGarbageCollected<InlineStylePropertyMap>(GetDocument().body());
+
+  {
+    DummyExceptionStateForTesting exception_state;
+    map->set(GetDocument().ToExecutionContext(), "top", revert_string,
+             exception_state);
+    EXPECT_TRUE(exception_state.HadException());
+  }
+  {
+    DummyExceptionStateForTesting exception_state;
+    map->set(GetDocument().ToExecutionContext(), "left", revert_style_value,
+             exception_state);
+    EXPECT_TRUE(exception_state.HadException());
+  }
+  {
+    DummyExceptionStateForTesting exception_state;
+    map->set(GetDocument().ToExecutionContext(), "--y", revert_style_value,
+             exception_state);
+    EXPECT_TRUE(exception_state.HadException());
+  }
+
+  CSSStyleValue* top =
+      map->get(GetDocument().ToExecutionContext(), "top", exception_state);
+  CSSStyleValue* left =
+      map->get(GetDocument().ToExecutionContext(), "left", exception_state);
+  CSSStyleValue* y =
+      map->get(GetDocument().ToExecutionContext(), "--y", exception_state);
+
+  EXPECT_FALSE(top);
+  EXPECT_FALSE(left);
+  EXPECT_FALSE(y);
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/core/css/mathml.css b/third_party/blink/renderer/core/css/mathml.css
index 5adc7b1..b1ea4c50 100644
--- a/third_party/blink/renderer/core/css/mathml.css
+++ b/third_party/blink/renderer/core/css/mathml.css
@@ -46,7 +46,7 @@
     outline: auto 1px -webkit-focus-ring-color;
 }
 
-maction, merror, mfrac, mphantom, mrow, mspace, mstyle
+maction, merror, mfrac, mphantom, mrow, mspace, mstyle, munder, mover, munderover
 {
     display: math;
 }
diff --git a/third_party/blink/renderer/core/css/parser/css_parser_fast_paths.cc b/third_party/blink/renderer/core/css/parser/css_parser_fast_paths.cc
index faf3e61..5b0119f 100644
--- a/third_party/blink/renderer/core/css/parser/css_parser_fast_paths.cc
+++ b/third_party/blink/renderer/core/css/parser/css_parser_fast_paths.cc
@@ -12,6 +12,7 @@
 #include "third_party/blink/renderer/core/css/css_initial_value.h"
 #include "third_party/blink/renderer/core/css/css_numeric_literal_value.h"
 #include "third_party/blink/renderer/core/css/css_primitive_value.h"
+#include "third_party/blink/renderer/core/css/css_revert_value.h"
 #include "third_party/blink/renderer/core/css/css_unset_value.h"
 #include "third_party/blink/renderer/core/css/parser/css_parser_idioms.h"
 #include "third_party/blink/renderer/core/css/parser/css_property_parser.h"
@@ -793,7 +794,7 @@
       return (value_id >= CSSValueID::kDrag &&
               value_id <= CSSValueID::kNoDrag) ||
              value_id == CSSValueID::kNone;
-    case CSSPropertyID::kWebkitAppearance:
+    case CSSPropertyID::kAppearance:
       return (value_id >= CSSValueID::kCheckbox &&
               value_id <= CSSValueID::kTextarea) ||
              value_id == CSSValueID::kNone || value_id == CSSValueID::kAuto;
@@ -1049,7 +1050,6 @@
     case CSSPropertyID::kVectorEffect:
     case CSSPropertyID::kVisibility:
     case CSSPropertyID::kWebkitAppRegion:
-    case CSSPropertyID::kWebkitAppearance:
     case CSSPropertyID::kBackfaceVisibility:
     case CSSPropertyID::kBorderBlockEndStyle:
     case CSSPropertyID::kBorderBlockStartStyle:
@@ -1107,13 +1107,15 @@
   DCHECK(!string.IsEmpty());
 
   if (!CSSParserFastPaths::IsKeywordPropertyID(property_id)) {
-    // All properties accept the values of "initial," "inherit" and "unset".
+    // All properties accept CSS-wide keywords.
     if (!EqualIgnoringASCIICase(string, "initial") &&
         !EqualIgnoringASCIICase(string, "inherit") &&
-        !EqualIgnoringASCIICase(string, "unset"))
+        !EqualIgnoringASCIICase(string, "unset") &&
+        (!RuntimeEnabledFeatures::CSSRevertEnabled() ||
+         !EqualIgnoringASCIICase(string, "revert")))
       return nullptr;
 
-    // Parse initial/inherit/unset shorthands using the CSSPropertyParser.
+    // Parse CSS-wide keyword shorthands using the CSSPropertyParser.
     if (shorthandForProperty(property_id).length())
       return nullptr;
 
@@ -1133,6 +1135,10 @@
     return CSSInitialValue::Create();
   if (value_id == CSSValueID::kUnset)
     return cssvalue::CSSUnsetValue::Create();
+  if (RuntimeEnabledFeatures::CSSRevertEnabled() &&
+      value_id == CSSValueID::kRevert) {
+    return cssvalue::CSSRevertValue::Create();
+  }
   if (CSSParserFastPaths::IsValidKeywordPropertyAndValue(property_id, value_id,
                                                          parser_mode))
     return CSSIdentifierValue::Create(value_id);
diff --git a/third_party/blink/renderer/core/css/parser/css_parser_fast_paths_test.cc b/third_party/blink/renderer/core/css/parser/css_parser_fast_paths_test.cc
index 5d01a442..a54b79c 100644
--- a/third_party/blink/renderer/core/css/parser/css_parser_fast_paths_test.cc
+++ b/third_party/blink/renderer/core/css/parser/css_parser_fast_paths_test.cc
@@ -8,6 +8,7 @@
 #include "third_party/blink/renderer/core/css/css_color_value.h"
 #include "third_party/blink/renderer/core/css/css_identifier_value.h"
 #include "third_party/blink/renderer/core/css/css_value_list.h"
+#include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
 
 namespace blink {
 
@@ -53,6 +54,46 @@
   ASSERT_EQ(nullptr, value);
 }
 
+TEST(CSSParserFastPathsTest, ParseRevert) {
+  // Revert enabled, IsKeywordPropertyID=false
+  {
+    DCHECK(!CSSParserFastPaths::IsKeywordPropertyID(CSSPropertyID::kMarginTop));
+    ScopedCSSRevertForTest scoped_revert(true);
+    CSSValue* value = CSSParserFastPaths::MaybeParseValue(
+        CSSPropertyID::kMarginTop, "revert", kHTMLStandardMode);
+    ASSERT_TRUE(value);
+    EXPECT_TRUE(value->IsRevertValue());
+  }
+
+  // Revert enabled, IsKeywordPropertyID=true
+  {
+    DCHECK(CSSParserFastPaths::IsKeywordPropertyID(CSSPropertyID::kDisplay));
+    ScopedCSSRevertForTest scoped_revert(true);
+    CSSValue* value = CSSParserFastPaths::MaybeParseValue(
+        CSSPropertyID::kDisplay, "revert", kHTMLStandardMode);
+    ASSERT_TRUE(value);
+    EXPECT_TRUE(value->IsRevertValue());
+  }
+
+  // Revert disabled, IsKeywordPropertyID=false
+  {
+    DCHECK(!CSSParserFastPaths::IsKeywordPropertyID(CSSPropertyID::kMarginTop));
+    ScopedCSSRevertForTest scoped_revert(false);
+    CSSValue* value = CSSParserFastPaths::MaybeParseValue(
+        CSSPropertyID::kMarginTop, "revert", kHTMLStandardMode);
+    EXPECT_FALSE(value);
+  }
+
+  // Revert disabled, IsKeywordPropertyID=true
+  {
+    DCHECK(CSSParserFastPaths::IsKeywordPropertyID(CSSPropertyID::kDisplay));
+    ScopedCSSRevertForTest scoped_revert(false);
+    CSSValue* value = CSSParserFastPaths::MaybeParseValue(
+        CSSPropertyID::kDisplay, "revert", kHTMLStandardMode);
+    EXPECT_FALSE(value);
+  }
+}
+
 TEST(CSSParserFastPathsTest, ParseTransform) {
   CSSValue* value = CSSParserFastPaths::MaybeParseValue(
       CSSPropertyID::kTransform, "translate(5.5px, 5px)", kHTMLStandardMode);
diff --git a/third_party/blink/renderer/core/css/parser/css_property_parser.cc b/third_party/blink/renderer/core/css/parser/css_property_parser.cc
index 1365fb4..b7d3024 100644
--- a/third_party/blink/renderer/core/css/parser/css_property_parser.cc
+++ b/third_party/blink/renderer/core/css/parser/css_property_parser.cc
@@ -7,6 +7,7 @@
 #include "third_party/blink/renderer/core/css/css_inherited_value.h"
 #include "third_party/blink/renderer/core/css/css_initial_value.h"
 #include "third_party/blink/renderer/core/css/css_pending_substitution_value.h"
+#include "third_party/blink/renderer/core/css/css_revert_value.h"
 #include "third_party/blink/renderer/core/css/css_unicode_range_value.h"
 #include "third_party/blink/renderer/core/css/css_unset_value.h"
 #include "third_party/blink/renderer/core/css/css_variable_reference_value.h"
@@ -45,6 +46,8 @@
     value = CSSInheritedValue::Create();
   if (id == CSSValueID::kUnset)
     value = cssvalue::CSSUnsetValue::Create();
+  if (RuntimeEnabledFeatures::CSSRevertEnabled() && id == CSSValueID::kRevert)
+    value = cssvalue::CSSRevertValue::Create();
 
   if (value)
     range = local_range;
diff --git a/third_party/blink/renderer/core/css/parser/css_property_parser_helpers.cc b/third_party/blink/renderer/core/css/parser/css_property_parser_helpers.cc
index c2034ea..b194927 100644
--- a/third_party/blink/renderer/core/css/parser/css_property_parser_helpers.cc
+++ b/third_party/blink/renderer/core/css/parser/css_property_parser_helpers.cc
@@ -29,7 +29,9 @@
 #include "third_party/blink/renderer/core/css/properties/css_property.h"
 #include "third_party/blink/renderer/core/css/properties/longhand.h"
 #include "third_party/blink/renderer/core/css/style_color.h"
+#include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/frame/web_feature.h"
+#include "third_party/blink/renderer/core/inspector/console_message.h"
 #include "third_party/blink/renderer/core/style_property_shorthand.h"
 #include "third_party/blink/renderer/platform/heap/heap.h"
 #include "third_party/blink/renderer/platform/instrumentation/use_counter.h"
@@ -468,18 +470,9 @@
 
 bool CanConsumeCalcValue(CalculationCategory category,
                          CSSParserMode css_parser_mode) {
-  if (category == kCalcLength || category == kCalcPercent ||
-      category == kCalcPercentLength)
-    return true;
-
-  if (css_parser_mode != kSVGAttributeMode)
-    return false;
-
-  if (category == kCalcNumber || category == kCalcPercentNumber ||
-      category == kCalcLengthNumber || category == kCalcPercentLengthNumber)
-    return true;
-
-  return false;
+  return category == kCalcLength || category == kCalcPercent ||
+         category == kCalcPercentLength ||
+         (css_parser_mode == kSVGAttributeMode && category == kCalcNumber);
 }
 
 CSSPrimitiveValue* ConsumeLengthOrPercent(CSSParserTokenRange& range,
@@ -510,16 +503,7 @@
            value->GetDoubleValue() != 0;
   }
   const auto& math_value = To<CSSMathFunctionValue>(*value);
-  switch (math_value.Category()) {
-    case kCalcNumber:
-      return math_value.DoubleValue() != 0;
-    case kCalcPercentNumber:
-    case kCalcLengthNumber:
-    case kCalcPercentLengthNumber:
-      return true;
-    default:
-      return false;
-  }
+  return math_value.Category() == kCalcNumber && math_value.DoubleValue() != 0;
 }
 
 }  // namespace
@@ -659,8 +643,6 @@
 
   if (EqualIgnoringASCIICase(token.Value(), "default"))
     context.Count(WebFeature::kDefaultInCustomIdent);
-  if (EqualIgnoringASCIICase(token.Value(), "revert"))
-    context.Count(WebFeature::kRevertInCustomIdent);
 
   return MakeGarbageCollected<CSSCustomIdentValue>(
       token.Value().ToAtomicString());
@@ -1868,7 +1850,9 @@
 bool IsCSSWideKeyword(StringView keyword) {
   return EqualIgnoringASCIICase(keyword, "initial") ||
          EqualIgnoringASCIICase(keyword, "inherit") ||
-         EqualIgnoringASCIICase(keyword, "unset");
+         EqualIgnoringASCIICase(keyword, "unset") ||
+         (RuntimeEnabledFeatures::CSSRevertEnabled() &&
+          EqualIgnoringASCIICase(keyword, "revert"));
 }
 
 // https://drafts.csswg.org/css-cascade/#default
@@ -1952,7 +1936,30 @@
   if (!context.IsUseCounterRecordingEnabled())
     return;
   switch (property) {
-    case CSSPropertyID::kWebkitAppearance: {
+    case CSSPropertyID::kAppearance:
+      if (value_id == CSSValueID::kInnerSpinButton ||
+          value_id == CSSValueID::kMediaSlider ||
+          value_id == CSSValueID::kMediaSliderthumb ||
+          value_id == CSSValueID::kMediaVolumeSlider ||
+          value_id == CSSValueID::kMediaVolumeSliderthumb ||
+          value_id == CSSValueID::kSliderVertical ||
+          value_id == CSSValueID::kSliderthumbHorizontal ||
+          value_id == CSSValueID::kSliderthumbVertical ||
+          value_id == CSSValueID::kSearchfieldCancelButton) {
+        if (const auto* document = context.GetDocument()) {
+          document->AddConsoleMessage(MakeGarbageCollected<ConsoleMessage>(
+              mojom::blink::ConsoleMessageSource::kOther,
+              mojom::blink::ConsoleMessageLevel::kWarning,
+              String("The keyword '") + getValueName(value_id) +
+                  "' specified to an 'appearance' property is not "
+                  "standardized. It will be removed in the future."));
+        }
+      }
+      FALLTHROUGH;
+      // This function distinguishes 'appearance' and '-webkit-appearance'
+      // though other property aliases are handles as their aliased properties.
+      // See Appearance::ParseSingleValue().
+    case CSSPropertyID::kAliasWebkitAppearance: {
       WebFeature feature;
       if (value_id == CSSValueID::kNone) {
         feature = WebFeature::kCSSValueAppearanceNone;
diff --git a/third_party/blink/renderer/core/css/parser/css_property_parser_helpers.h b/third_party/blink/renderer/core/css/parser/css_property_parser_helpers.h
index 45c628c..6c4ea21 100644
--- a/third_party/blink/renderer/core/css/parser/css_property_parser_helpers.h
+++ b/third_party/blink/renderer/core/css/parser/css_property_parser_helpers.h
@@ -150,7 +150,8 @@
 
 CSSValue* ConsumeAxis(CSSParserTokenRange&, const CSSParserContext& context);
 
-bool IsCSSWideKeyword(StringView);
+// See also css_parsing_utils::IsCSSWideKeyword.
+CORE_EXPORT bool IsCSSWideKeyword(StringView);
 bool IsRevertKeyword(StringView);
 bool IsDefaultKeyword(StringView);
 
diff --git a/third_party/blink/renderer/core/css/parser/css_property_parser_helpers_test.cc b/third_party/blink/renderer/core/css/parser/css_property_parser_helpers_test.cc
new file mode 100644
index 0000000..2eb068a
--- /dev/null
+++ b/third_party/blink/renderer/core/css/parser/css_property_parser_helpers_test.cc
@@ -0,0 +1,25 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/core/css/parser/css_property_parser_helpers.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+TEST(CSSPropertyParserHelpersTest, ParseRevert) {
+  {
+    ScopedCSSRevertForTest scoped_revert(true);
+    EXPECT_TRUE(css_property_parser_helpers::IsCSSWideKeyword("revert"));
+  }
+
+  {
+    ScopedCSSRevertForTest scoped_revert(false);
+    EXPECT_FALSE(css_property_parser_helpers::IsCSSWideKeyword("revert"));
+  }
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/core/css/parser/css_property_parser_test.cc b/third_party/blink/renderer/core/css/parser/css_property_parser_test.cc
index edd37e2..0d39231 100644
--- a/third_party/blink/renderer/core/css/parser/css_property_parser_test.cc
+++ b/third_party/blink/renderer/core/css/parser/css_property_parser_test.cc
@@ -638,20 +638,6 @@
   EXPECT_TRUE(IsCounted(feature));
 }
 
-TEST_F(CSSPropertyUseCounterTest, CSSPropertyRevertAnimationNameUseCount) {
-  WebFeature feature = WebFeature::kRevertInCustomIdent;
-  EXPECT_FALSE(IsCounted(feature));
-
-  ParseProperty(CSSPropertyID::kAnimationName, "initial");
-  EXPECT_FALSE(IsCounted(feature));
-
-  ParseProperty(CSSPropertyID::kAnimationName, "test");
-  EXPECT_FALSE(IsCounted(feature));
-
-  ParseProperty(CSSPropertyID::kAnimationName, "revert");
-  EXPECT_TRUE(IsCounted(feature));
-}
-
 TEST_F(CSSPropertyUseCounterTest, CSSPropertyContainStyleUseCount) {
   WebFeature feature = WebFeature::kCSSValueContainStyle;
   EXPECT_FALSE(IsCounted(feature));
@@ -725,4 +711,28 @@
             value->CssText());
 }
 
+TEST(CSSPropertyParserTest, ParseRevert) {
+  auto* context = MakeGarbageCollected<CSSParserContext>(
+      kHTMLStandardMode, SecureContextMode::kInsecureContext);
+
+  String string = " revert";
+  CSSTokenizer tokenizer(string);
+  const auto tokens = tokenizer.TokenizeToEOF();
+
+  {
+    ScopedCSSRevertForTest scoped_revert(true);
+    const CSSValue* value = CSSPropertyParser::ParseSingleValue(
+        CSSPropertyID::kMarginLeft, CSSParserTokenRange(tokens), context);
+    ASSERT_TRUE(value);
+    EXPECT_TRUE(value->IsRevertValue());
+  }
+
+  {
+    ScopedCSSRevertForTest scoped_revert(false);
+    const CSSValue* value = CSSPropertyParser::ParseSingleValue(
+        CSSPropertyID::kMarginLeft, CSSParserTokenRange(tokens), context);
+    EXPECT_FALSE(value);
+  }
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/css/parser/css_variable_parser.cc b/third_party/blink/renderer/core/css/parser/css_variable_parser.cc
index 61a8c02..d95059d9 100644
--- a/third_party/blink/renderer/core/css/parser/css_variable_parser.cc
+++ b/third_party/blink/renderer/core/css/parser/css_variable_parser.cc
@@ -7,6 +7,7 @@
 #include "third_party/blink/renderer/core/css/css_custom_property_declaration.h"
 #include "third_party/blink/renderer/core/css/css_variable_reference_value.h"
 #include "third_party/blink/renderer/core/css/parser/css_parser_token_range.h"
+#include "third_party/blink/renderer/core/css/properties/css_parsing_utils.h"
 #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
 
 namespace blink {
@@ -114,9 +115,7 @@
   range.ConsumeWhitespace();
   if (range.Peek().GetType() == kIdentToken) {
     CSSValueID id = range.ConsumeIncludingWhitespace().Id();
-    if (range.AtEnd() &&
-        (id == CSSValueID::kInherit || id == CSSValueID::kInitial ||
-         id == CSSValueID::kUnset))
+    if (range.AtEnd() && css_parsing_utils::IsCSSWideKeyword(id))
       return id;
   }
 
diff --git a/third_party/blink/renderer/core/css/properties/css_parsing_utils.cc b/third_party/blink/renderer/core/css/properties/css_parsing_utils.cc
index 4ede24d..c2e3c3a 100644
--- a/third_party/blink/renderer/core/css/properties/css_parsing_utils.cc
+++ b/third_party/blink/renderer/core/css/properties/css_parsing_utils.cc
@@ -458,6 +458,13 @@
   return IsContentPositionKeyword(id) || IsLeftOrRightKeyword(id);
 }
 
+bool IsCSSWideKeyword(CSSValueID id) {
+  return id == CSSValueID::kInherit || id == CSSValueID::kInitial ||
+         id == CSSValueID::kUnset ||
+         (RuntimeEnabledFeatures::CSSRevertEnabled() &&
+          (id == CSSValueID::kRevert));
+}
+
 CSSValue* ConsumeScrollOffset(CSSParserTokenRange& range,
                               const CSSParserContext& context) {
   range.ConsumeWhitespace();
diff --git a/third_party/blink/renderer/core/css/properties/css_parsing_utils.h b/third_party/blink/renderer/core/css/properties/css_parsing_utils.h
index 1ffeb84..6cf2745 100644
--- a/third_party/blink/renderer/core/css/properties/css_parsing_utils.h
+++ b/third_party/blink/renderer/core/css/properties/css_parsing_utils.h
@@ -5,6 +5,7 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_CSS_PROPERTIES_CSS_PARSING_UTILS_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_CSS_PROPERTIES_CSS_PARSING_UTILS_H_
 
+#include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/css/css_numeric_literal_value.h"
 #include "third_party/blink/renderer/core/css/parser/css_parser_mode.h"
 #include "third_party/blink/renderer/core/css/parser/css_parser_token_range.h"
@@ -46,6 +47,7 @@
 bool IsSelfPositionOrLeftOrRightKeyword(CSSValueID);
 bool IsContentPositionKeyword(CSSValueID);
 bool IsContentPositionOrLeftOrRightKeyword(CSSValueID);
+CORE_EXPORT bool IsCSSWideKeyword(CSSValueID);
 
 CSSValue* ConsumeScrollOffset(CSSParserTokenRange&, const CSSParserContext&);
 CSSValue* ConsumeSelfPositionOverflowPosition(CSSParserTokenRange&,
diff --git a/third_party/blink/renderer/core/css/properties/css_parsing_utils_test.cc b/third_party/blink/renderer/core/css/properties/css_parsing_utils_test.cc
index 8ac05ca..3e768f51 100644
--- a/third_party/blink/renderer/core/css/properties/css_parsing_utils_test.cc
+++ b/third_party/blink/renderer/core/css/properties/css_parsing_utils_test.cc
@@ -7,6 +7,7 @@
 #include "third_party/blink/renderer/core/html/html_html_element.h"
 #include "third_party/blink/renderer/core/testing/dummy_page_holder.h"
 #include "third_party/blink/renderer/platform/instrumentation/use_counter.h"
+#include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
 
 namespace blink {
 
@@ -21,4 +22,16 @@
   EXPECT_TRUE(document.IsUseCounted(feature));
 }
 
+TEST(CSSParsingUtilsTest, Revert) {
+  {
+    ScopedCSSRevertForTest scoped_revert(true);
+    EXPECT_TRUE(css_parsing_utils::IsCSSWideKeyword(CSSValueID::kRevert));
+  }
+
+  {
+    ScopedCSSRevertForTest scoped_revert(false);
+    EXPECT_FALSE(css_parsing_utils::IsCSSWideKeyword(CSSValueID::kRevert));
+  }
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc b/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc
index 2273c25..700ce58f 100644
--- a/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc
+++ b/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc
@@ -25,6 +25,7 @@
 #include "third_party/blink/renderer/core/css/css_value_list.h"
 #include "third_party/blink/renderer/core/css/css_value_pair.h"
 #include "third_party/blink/renderer/core/css/parser/css_parser_context.h"
+#include "third_party/blink/renderer/core/css/parser/css_parser_fast_paths.h"
 #include "third_party/blink/renderer/core/css/parser/css_parser_local_context.h"
 #include "third_party/blink/renderer/core/css/parser/css_parser_mode.h"
 #include "third_party/blink/renderer/core/css/parser/css_parser_token.h"
@@ -6834,7 +6835,24 @@
   state.GetDocument().SetHasAnnotatedRegions(true);
 }
 
-const CSSValue* WebkitAppearance::CSSValueFromComputedStyleInternal(
+const CSSValue* Appearance::ParseSingleValue(
+    CSSParserTokenRange& range,
+    const CSSParserContext& context,
+    const CSSParserLocalContext& local_context) const {
+  CSSValueID id = range.Peek().Id();
+  CSSPropertyID property = CSSPropertyID::kAppearance;
+  if (CSSParserFastPaths::IsValidKeywordPropertyAndValue(property, id,
+                                                         context.Mode())) {
+    if (local_context.UseAliasParsing())
+      property = CSSPropertyID::kAliasWebkitAppearance;
+    css_property_parser_helpers::CountKeywordOnlyPropertyUsage(property,
+                                                               context, id);
+    return css_property_parser_helpers::ConsumeIdent(range);
+  }
+  return nullptr;
+}
+
+const CSSValue* Appearance::CSSValueFromComputedStyleInternal(
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
diff --git a/third_party/blink/renderer/core/css/resolver/style_cascade.cc b/third_party/blink/renderer/core/css/resolver/style_cascade.cc
index c38edab6..8b06d79c 100644
--- a/third_party/blink/renderer/core/css/resolver/style_cascade.cc
+++ b/third_party/blink/renderer/core/css/resolver/style_cascade.cc
@@ -129,8 +129,8 @@
   ApplyMatchResult(resolver);
   ApplyInterpolations(resolver);
 
-  if (map_.Find(CSSPropertyName(CSSPropertyID::kWebkitAppearance)) &&
-      !resolver.filter_.Rejects(GetCSSPropertyWebkitAppearance()) &&
+  if (map_.Find(CSSPropertyName(CSSPropertyID::kAppearance)) &&
+      !resolver.filter_.Rejects(GetCSSPropertyAppearance()) &&
       state_.Style()->HasAppearance()) {
     state_.Style()->SetHasAuthorBackground(HasAuthorBackground());
     state_.Style()->SetHasAuthorBorder(HasAuthorBorder());
@@ -501,6 +501,9 @@
     return ResolveVariableReference(property, *v, resolver);
   if (const auto* v = DynamicTo<cssvalue::CSSPendingSubstitutionValue>(value))
     return ResolvePendingSubstitution(property, *v, resolver);
+  // TODO(andruud): Actually revert instead of treating as unset.
+  if (value.IsRevertValue())
+    return cssvalue::CSSUnsetValue::Create();
   return &value;
 }
 
diff --git a/third_party/blink/renderer/core/dom/document.cc b/third_party/blink/renderer/core/dom/document.cc
index ef44c779..b5cf9632 100644
--- a/third_party/blink/renderer/core/dom/document.cc
+++ b/third_party/blink/renderer/core/dom/document.cc
@@ -7238,7 +7238,7 @@
 }
 
 void Document::AddConsoleMessage(ConsoleMessage* message,
-                                 bool discard_duplicates) {
+                                 bool discard_duplicates) const {
   // Don't let non-attached Documents spam the console.
   if (domWindow())
     domWindow()->AddConsoleMessage(message, discard_duplicates);
diff --git a/third_party/blink/renderer/core/dom/document.h b/third_party/blink/renderer/core/dom/document.h
index b13f3c3..f8c96c1 100644
--- a/third_party/blink/renderer/core/dom/document.h
+++ b/third_party/blink/renderer/core/dom/document.h
@@ -680,6 +680,9 @@
     kRunPostLayoutTasksSynchronously,
   };
   void UpdateStyleAndLayoutForNode(const Node*, DocumentUpdateReason);
+  void IncLayoutCallsCounter() { ++layout_calls_counter_; }
+  void IncLayoutCallsCounterNG() { ++layout_calls_counter_ng_; }
+
   scoped_refptr<const ComputedStyle> StyleForPage(int page_index);
 
   // Ensures that location-based data will be valid for a given node.
@@ -1451,7 +1454,7 @@
   void DidAssociateFormControl(Element*);
 
   void AddConsoleMessage(ConsoleMessage* message,
-                         bool discard_duplicates = false);
+                         bool discard_duplicates = false) const;
   void AddInspectorIssue(InspectorIssue*);
 
   LocalFrame* ExecutingFrame();
@@ -2253,6 +2256,13 @@
   // The number of canvas elements on the document
   int num_canvases_ = 0;
 
+  // The number of LayoutObject::UpdateLayout() calls for both of the legacy
+  // layout and LayoutNG.
+  uint32_t layout_calls_counter_ = 0;
+
+  // The number of LayoutObject::UpdateLayout() calls for LayoutNG.
+  uint32_t layout_calls_counter_ng_ = 0;
+
   // Number of display locks in this document that block all activation.
   int display_lock_blocking_all_activation_count_ = 0;
   // Number of locked display locks in the document.
diff --git a/third_party/blink/renderer/core/layout/BUILD.gn b/third_party/blink/renderer/core/layout/BUILD.gn
index cb2f84eb..e9cb636 100644
--- a/third_party/blink/renderer/core/layout/BUILD.gn
+++ b/third_party/blink/renderer/core/layout/BUILD.gn
@@ -427,6 +427,8 @@
     "ng/mathml/ng_math_row_layout_algorithm.h",
     "ng/mathml/ng_math_space_layout_algorithm.cc",
     "ng/mathml/ng_math_space_layout_algorithm.h",
+    "ng/mathml/ng_math_under_over_layout_algorithm.cc",
+    "ng/mathml/ng_math_under_over_layout_algorithm.h",
     "ng/ng_absolute_utils.cc",
     "ng/ng_absolute_utils.h",
     "ng/ng_block_break_token.cc",
diff --git a/third_party/blink/renderer/core/layout/layout_box.cc b/third_party/blink/renderer/core/layout/layout_box.cc
index d10c1623..b155d33 100644
--- a/third_party/blink/renderer/core/layout/layout_box.cc
+++ b/third_party/blink/renderer/core/layout/layout_box.cc
@@ -296,8 +296,13 @@
   ShapeOutsideInfo::RemoveInfo(*this);
 
   if (!DocumentBeingDestroyed()) {
-    if (NGPaintFragment* first_inline_fragment = FirstInlineFragment())
-      first_inline_fragment->LayoutObjectWillBeDestroyed();
+    if (!RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()) {
+      if (NGPaintFragment* first_inline_fragment = FirstInlineFragment())
+        first_inline_fragment->LayoutObjectWillBeDestroyed();
+    } else if (FirstInlineFragmentItemIndex()) {
+      NGFragmentItems::LayoutObjectWillBeDestroyed(*this);
+      ClearFirstInlineFragmentItemIndex();
+    }
     for (auto result : layout_results_)
       result->PhysicalFragment().LayoutObjectWillBeDestroyed();
   }
@@ -886,6 +891,11 @@
   // Legacy, then NG again, NG won't use a stale layout result.
   if (IsOutOfFlowPositioned() && !IsLayoutNGObject())
     ClearLayoutResults();
+
+  Document& document = GetDocument();
+  document.IncLayoutCallsCounter();
+  if (IsLayoutNGObject())
+    document.IncLayoutCallsCounterNG();
 }
 
 bool LayoutBox::HasOverrideIntrinsicContentWidth() const {
@@ -2468,17 +2478,12 @@
     return inline_box_wrapper_;
   if (!RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled())
     return first_paint_fragment_;
-  // TODO(yosin): We should use |first_fragment_item_index_|.
-  NGInlineCursor cursor;
-  cursor.MoveTo(*this);
-  return cursor;
+  return first_fragment_item_index_;
 }
 
 void LayoutBox::SetFirstInlineFragment(NGPaintFragment* fragment) {
   CHECK(IsInLayoutNGInlineFormattingContext()) << *this;
-  // TODO(yosin): Once we remove |NGPaintFragment|, we should get rid of
-  // |!fragment|.
-  DCHECK(!fragment || !RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled());
+  DCHECK(!RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled());
   first_paint_fragment_ = fragment;
 }
 
@@ -2492,17 +2497,26 @@
   CHECK(IsInLayoutNGInlineFormattingContext()) << *this;
   DCHECK(RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled());
   DCHECK_NE(index, 0u);
-  // TDOO(yosin): Once we update all |LayoutObject::FirstInlineFragment()|,
-  // we should enable below.
-  // first_fragment_item_index_ = index;
+  first_fragment_item_index_ = index;
 }
 
 void LayoutBox::InLayoutNGInlineFormattingContextWillChange(bool new_value) {
-  DeleteLineBoxWrapper();
+  if (IsInLayoutNGInlineFormattingContext()) {
+    if (!RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()) {
+      SetFirstInlineFragment(nullptr);
+    } else {
+      ClearFirstInlineFragmentItemIndex();
+    }
+  } else {
+    DeleteLineBoxWrapper();
+  }
 
   // Because |first_paint_fragment_| and |inline_box_wrapper_| are union, when
   // one is deleted, the other should be initialized to nullptr.
-  DCHECK(new_value ? !first_paint_fragment_ : !inline_box_wrapper_);
+  DCHECK(new_value ? (RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()
+                          ? !first_fragment_item_index_
+                          : !first_paint_fragment_)
+                   : !inline_box_wrapper_);
 }
 
 void LayoutBox::SetCachedLayoutResult(
@@ -2524,11 +2538,40 @@
 void LayoutBox::AddLayoutResult(scoped_refptr<const NGLayoutResult> result,
                                 wtf_size_t index) {
   DCHECK_EQ(result->Status(), NGLayoutResult::kSuccess);
-  if (index != WTF::kNotFound)
-    ShrinkLayoutResults(index);
+  if (index != WTF::kNotFound && layout_results_.size() > index) {
+    if (layout_results_.size() > index + 1)
+      ShrinkLayoutResults(index + 1);
+    ReplaceLayoutResult(std::move(result), index);
+    return;
+  }
+  if (!index) {
+    if (const NGFragmentItems* items =
+            To<NGPhysicalBoxFragment>(result->PhysicalFragment()).Items())
+      items->AssociateWithLayoutObject();
+  }
   layout_results_.push_back(std::move(result));
 }
 
+void LayoutBox::ReplaceLayoutResult(scoped_refptr<const NGLayoutResult> result,
+                                    wtf_size_t index) {
+  DCHECK_LE(index, layout_results_.size());
+  const NGLayoutResult* old_result = layout_results_[index].get();
+  if (old_result == result.get())
+    return;
+  if (!index &&
+      &old_result->PhysicalFragment() != &result->PhysicalFragment()) {
+    if (const NGFragmentItems* old_items =
+            To<NGPhysicalBoxFragment>(old_result->PhysicalFragment()).Items()) {
+      InvalidateItems(*old_result);
+      old_items->ClearAssociatedFragments();
+    }
+    if (const NGFragmentItems* new_items =
+            To<NGPhysicalBoxFragment>(result->PhysicalFragment()).Items())
+      new_items->AssociateWithLayoutObject();
+  }
+  layout_results_[index] = std::move(result);
+}
+
 void LayoutBox::ClearLayoutResults() {
   if (measure_result_)
     InvalidateItems(*measure_result_);
@@ -2542,6 +2585,13 @@
   // Invalidate if inline |DisplayItemClient|s will be destroyed.
   for (wtf_size_t i = results_to_keep; i < layout_results_.size(); i++)
     InvalidateItems(*layout_results_[i]);
+  if (results_to_keep == 0 && !layout_results_.IsEmpty()) {
+    if (const NGFragmentItems* old_items =
+            To<NGPhysicalBoxFragment>(layout_results_[0]->PhysicalFragment())
+                .Items()) {
+      old_items->ClearAssociatedFragments();
+    }
+  }
   layout_results_.Shrink(results_to_keep);
 }
 
@@ -2563,6 +2613,7 @@
   const NGLayoutResult* result = layout_results_[0].get();
   if (result->IsSingleUse())
     return nullptr;
+  DCHECK(result->PhysicalFragment().IsAlive());
   DCHECK_EQ(layout_results_.size(), 1u);
   return result;
 }
diff --git a/third_party/blink/renderer/core/layout/layout_box.h b/third_party/blink/renderer/core/layout/layout_box.h
index a9484751..f0aa953 100644
--- a/third_party/blink/renderer/core/layout/layout_box.h
+++ b/third_party/blink/renderer/core/layout/layout_box.h
@@ -969,6 +969,8 @@
   // Store one layout result (with its physical fragment) at the specified
   // index, and delete all entries following it.
   void AddLayoutResult(scoped_refptr<const NGLayoutResult>, wtf_size_t index);
+  void ReplaceLayoutResult(scoped_refptr<const NGLayoutResult>,
+                           wtf_size_t index);
 
   void ShrinkLayoutResults(wtf_size_t results_to_keep);
   void ClearLayoutResults();
@@ -2001,11 +2003,10 @@
 inline NGPaintFragment* LayoutBox::FirstInlineFragment() const {
   if (!IsInLayoutNGInlineFormattingContext())
     return nullptr;
-  // TODO(yosin): Once we replace all usage of |FirstInlineFragment()| to
-  // |NGInlineCursor|, we should change this to |DCHECK()|.
-  if (RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled())
-    return nullptr;
-  return first_paint_fragment_;
+  if (!RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled())
+    return first_paint_fragment_;
+  NOTREACHED();
+  return nullptr;
 }
 
 inline wtf_size_t LayoutBox::FirstInlineFragmentItemIndex() const {
diff --git a/third_party/blink/renderer/core/layout/layout_button.cc b/third_party/blink/renderer/core/layout/layout_button.cc
index 28d3a96..29ddba1 100644
--- a/third_party/blink/renderer/core/layout/layout_button.cc
+++ b/third_party/blink/renderer/core/layout/layout_button.cc
@@ -20,6 +20,8 @@
 
 #include "third_party/blink/renderer/core/layout/layout_button.h"
 
+#include "third_party/blink/renderer/core/frame/web_feature.h"
+
 namespace blink {
 
 LayoutButton::LayoutButton(Element* element)
@@ -94,8 +96,12 @@
     return MarginRight() + Size().Width() - BorderLeft() - PaddingLeft() -
            VerticalScrollbarWidth();
   }
-  return LayoutFlexibleBox::BaselinePosition(baseline, first_line, direction,
-                                             line_position_mode);
+  LayoutUnit result_baseline = LayoutFlexibleBox::BaselinePosition(
+      baseline, first_line, direction, line_position_mode);
+  LayoutUnit correct_baseline = LayoutBlock::InlineBlockBaseline(direction);
+  if (correct_baseline != result_baseline)
+    UseCounter::Count(GetDocument(), WebFeature::kWrongBaselineOfButtonElement);
+  return result_baseline;
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/layout/layout_inline.cc b/third_party/blink/renderer/core/layout/layout_inline.cc
index a7dd0e5..57acf1f3 100644
--- a/third_party/blink/renderer/core/layout/layout_inline.cc
+++ b/third_party/blink/renderer/core/layout/layout_inline.cc
@@ -136,10 +136,15 @@
           box->Remove();
       }
     } else {
-      if (NGPaintFragment* first_inline_fragment = FirstInlineFragment())
-        first_inline_fragment->LayoutObjectWillBeDestroyed();
       if (Parent())
         Parent()->DirtyLinesFromChangedChild(this);
+      if (!RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()) {
+        if (NGPaintFragment* first_inline_fragment = FirstInlineFragment())
+          first_inline_fragment->LayoutObjectWillBeDestroyed();
+      } else if (FirstInlineFragmentItemIndex()) {
+        NGFragmentItems::LayoutObjectWillBeDestroyed(*this);
+        ClearFirstInlineFragmentItemIndex();
+      }
     }
   }
 
@@ -155,9 +160,7 @@
 
 void LayoutInline::SetFirstInlineFragment(NGPaintFragment* fragment) {
   CHECK(IsInLayoutNGInlineFormattingContext()) << *this;
-  // TODO(yosin): Once we remove |NGPaintFragment|, we should get rid of
-  // |!fragment|.
-  DCHECK(!fragment || !RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled());
+  DCHECK(!RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled());
   first_paint_fragment_ = fragment;
 }
 
@@ -171,9 +174,7 @@
   CHECK(IsInLayoutNGInlineFormattingContext()) << *this;
   DCHECK(RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled());
   DCHECK_NE(index, 0u);
-  // TDOO(yosin): Once we update all |LayoutObject::FirstInlineFragment()|,
-  // we should enable below.
-  // first_fragment_item_index_ = index;
+  first_fragment_item_index_ = index;
 }
 
 bool LayoutInline::HasInlineFragments() const {
@@ -181,18 +182,26 @@
     return FirstLineBox();
   if (!RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled())
     return first_paint_fragment_;
-  // TODO(yosin): We should use |first_fragment_item_index_|.
-  NGInlineCursor cursor;
-  cursor.MoveTo(*this);
-  return cursor;
+  return first_fragment_item_index_;
 }
 
 void LayoutInline::InLayoutNGInlineFormattingContextWillChange(bool new_value) {
-  DeleteLineBoxes();
+  if (IsInLayoutNGInlineFormattingContext()) {
+    if (!RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()) {
+      SetFirstInlineFragment(nullptr);
+    } else {
+      ClearFirstInlineFragmentItemIndex();
+    }
+  } else {
+    DeleteLineBoxes();
+  }
 
   // Because |first_paint_fragment_| and |line_boxes_| are union, when one is
   // deleted, the other should be initialized to nullptr.
-  DCHECK(new_value ? !first_paint_fragment_ : !line_boxes_.First());
+  DCHECK(new_value ? (RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()
+                          ? !first_fragment_item_index_
+                          : !first_paint_fragment_)
+                   : !line_boxes_.First());
 }
 
 LayoutInline* LayoutInline::InlineElementContinuation() const {
diff --git a/third_party/blink/renderer/core/layout/layout_inline.h b/third_party/blink/renderer/core/layout/layout_inline.h
index 448bf29ca..771303d 100644
--- a/third_party/blink/renderer/core/layout/layout_inline.h
+++ b/third_party/blink/renderer/core/layout/layout_inline.h
@@ -429,6 +429,7 @@
     return nullptr;
   // TODO(yosin): Once we replace all usage of |FirstInlineFragment()| to
   // |NGInlineCursor|, we should change this to |DCHECK()|.
+  DCHECK(!RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled());
   if (RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled())
     return nullptr;
   return first_paint_fragment_;
diff --git a/third_party/blink/renderer/core/layout/layout_object.h b/third_party/blink/renderer/core/layout/layout_object.h
index 714d124..3e65542 100644
--- a/third_party/blink/renderer/core/layout/layout_object.h
+++ b/third_party/blink/renderer/core/layout/layout_object.h
@@ -3490,7 +3490,11 @@
   if (IsInLayoutNGInlineFormattingContext() == new_value)
     return;
   InLayoutNGInlineFormattingContextWillChange(new_value);
+  // The association cache for inline fragments is in union. Make sure the
+  // cache is cleared before and after changing this flag.
+  DCHECK(!HasInlineFragments());
   bitfields_.SetIsInLayoutNGInlineFormattingContext(new_value);
+  DCHECK(!HasInlineFragments());
 }
 
 inline void LayoutObject::SetHasBoxDecorationBackground(bool b) {
diff --git a/third_party/blink/renderer/core/layout/layout_object_child_list.cc b/third_party/blink/renderer/core/layout/layout_object_child_list.cc
index 8a71bc3..63b241f3 100644
--- a/third_party/blink/renderer/core/layout/layout_object_child_list.cc
+++ b/third_party/blink/renderer/core/layout/layout_object_child_list.cc
@@ -31,6 +31,7 @@
 #include "third_party/blink/renderer/core/layout/layout_inline.h"
 #include "third_party/blink/renderer/core/layout/layout_object.h"
 #include "third_party/blink/renderer/core/layout/layout_view.h"
+#include "third_party/blink/renderer/core/layout/ng/inline/ng_fragment_items.h"
 #include "third_party/blink/renderer/core/paint/ng/ng_paint_fragment.h"
 #include "third_party/blink/renderer/core/paint/object_paint_invalidator.h"
 
@@ -59,9 +60,13 @@
   }
 
   if (RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()) {
-    // TODO(yosin): Tells |NGFragmentItem| about they become not to associate
-    // to layout object.
-    object->ClearFirstInlineFragmentItemIndex();
+    // This LayoutObject is not technically destroyed, but further access should
+    // be prohibited when moved to different parent as if it were destroyed.
+    if (object->FirstInlineFragmentItemIndex()) {
+      if (auto* text = ToLayoutTextOrNull(object))
+        text->DetachAbstractInlineTextBoxesIfNeeded();
+      NGFragmentItems::LayoutObjectWillBeMoved(*object);
+    }
   } else if (NGPaintFragment* fragment = object->FirstInlineFragment()) {
     // This LayoutObject is not technically destroyed, but further access should
     // be prohibited when moved to different parent as if it were destroyed.
diff --git a/third_party/blink/renderer/core/layout/layout_text.cc b/third_party/blink/renderer/core/layout/layout_text.cc
index b1ccf91..cc5509c2 100644
--- a/third_party/blink/renderer/core/layout/layout_text.cc
+++ b/third_party/blink/renderer/core/layout/layout_text.cc
@@ -153,10 +153,14 @@
 
 LayoutText::~LayoutText() {
 #if DCHECK_IS_ON()
-  if (IsInLayoutNGInlineFormattingContext())
-    DCHECK(!first_paint_fragment_);
-  else
+  if (IsInLayoutNGInlineFormattingContext()) {
+    if (!RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled())
+      DCHECK(!first_paint_fragment_);
+    else
+      DCHECK(!first_fragment_item_index_);
+  } else {
     text_boxes_.AssertIsEmpty();
+  }
 #endif
 }
 
@@ -223,20 +227,26 @@
       for (InlineTextBox* box : TextBoxes())
         box->Remove();
     } else {
+      if (Parent())
+        Parent()->DirtyLinesFromChangedChild(this);
       if (RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()) {
-        if (has_abstract_inline_text_box_)
+        if (FirstInlineFragmentItemIndex()) {
+          DetachAbstractInlineTextBoxesIfNeeded();
+          NGFragmentItems::LayoutObjectWillBeDestroyed(*this);
           ClearFirstInlineFragmentItemIndex();
+        }
       } else if (NGPaintFragment* first_inline_fragment =
                      FirstInlineFragment()) {
         first_inline_fragment->LayoutObjectWillBeDestroyed();
         SetFirstInlineFragment(nullptr);
       }
-      if (Parent())
-        Parent()->DirtyLinesFromChangedChild(this);
     }
   } else if (RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()) {
-    if (has_abstract_inline_text_box_)
+    if (FirstInlineFragmentItemIndex()) {
+      DetachAbstractInlineTextBoxesIfNeeded();
+      NGFragmentItems::LayoutObjectWillBeDestroyed(*this);
       ClearFirstInlineFragmentItemIndex();
+    }
   } else if (NGPaintFragment* first_inline_fragment = FirstInlineFragment()) {
     // Still do this to clear the global hash map in  NGAbstractInlineTextBox.
     SetFirstInlineFragment(nullptr);
@@ -298,10 +308,7 @@
 
 void LayoutText::SetFirstInlineFragment(NGPaintFragment* first_fragment) {
   CHECK(IsInLayoutNGInlineFormattingContext());
-  // TODO(yosin): Once we remove |NGPaintFragment|, we should get rid of
-  // |!fragment|.
-  DCHECK(!first_fragment ||
-         !RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled());
+  DCHECK(!RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled());
   DetachAbstractInlineTextBoxesIfNeeded();
   first_paint_fragment_ = first_fragment;
 }
@@ -319,17 +326,26 @@
   DCHECK(RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled());
   DCHECK_NE(index, 0u);
   DetachAbstractInlineTextBoxesIfNeeded();
-  // TDOO(yosin): Once we update all |LayoutObject::FirstInlineFragment()|,
-  // we should enable below.
-  // first_fragment_item_index_ = index;
+  first_fragment_item_index_ = index;
 }
 
 void LayoutText::InLayoutNGInlineFormattingContextWillChange(bool new_value) {
-  DeleteTextBoxes();
+  if (IsInLayoutNGInlineFormattingContext()) {
+    if (!RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()) {
+      SetFirstInlineFragment(nullptr);
+    } else {
+      ClearFirstInlineFragmentItemIndex();
+    }
+  } else {
+    DeleteTextBoxes();
+  }
 
   // Because |first_paint_fragment_| and |text_boxes_| are union, when one is
   // deleted, the other should be initialized to nullptr.
-  DCHECK(new_value ? !first_paint_fragment_ : !text_boxes_.First());
+  DCHECK(new_value ? (RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()
+                          ? !first_fragment_item_index_
+                          : !first_paint_fragment_)
+                   : !text_boxes_.First());
 
   // Because there are no inline boxes associated to this text, we should not
   // have abstract inline text boxes too.
@@ -414,11 +430,11 @@
 }
 
 bool LayoutText::HasInlineFragments() const {
-    if (IsInLayoutNGInlineFormattingContext()) {
-      NGInlineCursor cursor;
-      cursor.MoveTo(*this);
-      return cursor.IsNotNull();
-    }
+  if (IsInLayoutNGInlineFormattingContext()) {
+    if (!RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled())
+      return first_paint_fragment_;
+    return first_fragment_item_index_;
+  }
 
   return FirstTextBox();
 }
diff --git a/third_party/blink/renderer/core/layout/layout_text.h b/third_party/blink/renderer/core/layout/layout_text.h
index 17ca490..42b67a90 100644
--- a/third_party/blink/renderer/core/layout/layout_text.h
+++ b/third_party/blink/renderer/core/layout/layout_text.h
@@ -476,11 +476,10 @@
 inline NGPaintFragment* LayoutText::FirstInlineFragment() const {
   if (!IsInLayoutNGInlineFormattingContext())
     return nullptr;
-  // TODO(yosin): Once we replace all usage of |FirstInlineFragment()| to
-  // |NGInlineCursor|, we should change this to |DCHECK()|.
-  if (RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled())
-    return nullptr;
-  return first_paint_fragment_;
+  if (!RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled())
+    return first_paint_fragment_;
+  NOTREACHED();
+  return nullptr;
 }
 
 inline wtf_size_t LayoutText::FirstInlineFragmentItemIndex() const {
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item.cc
index 63a23df..f5e29f8ef 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item.cc
@@ -174,6 +174,21 @@
   return false;
 }
 
+void NGFragmentItem::LayoutObjectWillBeDestroyed() const {
+  const_cast<NGFragmentItem*>(this)->layout_object_ = nullptr;
+  if (const NGPhysicalBoxFragment* fragment = BoxFragment())
+    fragment->LayoutObjectWillBeDestroyed();
+}
+
+void NGFragmentItem::LayoutObjectWillBeMoved() const {
+  // When |Layoutobject| is moved out from the current IFC, we should not clear
+  // the association with it in |ClearAssociatedFragments|, because the
+  // |LayoutObject| may be moved to a different IFC and is already laid out
+  // before clearing this IFC. This happens e.g., when split inlines moves
+  // inline children into a child anonymous block.
+  const_cast<NGFragmentItem*>(this)->layout_object_ = nullptr;
+}
+
 inline const LayoutBox* NGFragmentItem::InkOverflowOwnerBox() const {
   if (Type() == kBox)
     return ToLayoutBoxOrNull(GetLayoutObject());
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item.h b/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item.h
index c07af90..c9eeea8c 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item.h
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item.h
@@ -117,6 +117,8 @@
   LayoutObject* GetMutableLayoutObject() const {
     return const_cast<LayoutObject*>(layout_object_);
   }
+  void LayoutObjectWillBeDestroyed() const;
+  void LayoutObjectWillBeMoved() const;
   Node* GetNode() const { return layout_object_->GetNode(); }
   Node* NodeForHitTest() const { return layout_object_->NodeForHitTest(); }
   bool IsSiblingOf(const NGFragmentItem& other) const;
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_items.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_items.cc
index 794995b..33afc917 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_items.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_items.cc
@@ -5,17 +5,28 @@
 #include "third_party/blink/renderer/core/layout/ng/inline/ng_fragment_items.h"
 
 #include "third_party/blink/renderer/core/layout/ng/inline/ng_fragment_items_builder.h"
+#include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.h"
 
 namespace blink {
 
+namespace {
+
+inline bool ShouldAssociateWithLayoutObject(const NGFragmentItem& item) {
+  return item.Type() != NGFragmentItem::kLine && !item.IsFloating();
+}
+
+}  // namespace
+
 NGFragmentItems::NGFragmentItems(NGFragmentItemsBuilder* builder)
     : items_(std::move(builder->items_)),
       text_content_(std::move(builder->text_content_)),
       first_line_text_content_(std::move(builder->first_line_text_content_)) {}
 
-// static
-void NGFragmentItems::AssociateWithLayoutObject(
-    Vector<std::unique_ptr<NGFragmentItem>>* items) {
+void NGFragmentItems::AssociateWithLayoutObject() const {
+  const Vector<std::unique_ptr<NGFragmentItem>>* items = &items_;
+  DCHECK(std::all_of(items->begin(), items->end(), [](const auto& item) {
+    return !item->DeltaToNextForSameLayoutObject();
+  }));
   // items_[0] can be:
   //  - kBox  for list marker, e.g. <li>abc</li>
   //  - kLine for line, e.g. <div>abc</div>
@@ -26,12 +37,13 @@
   HashMap<const LayoutObject*, wtf_size_t> last_fragment_map;
   for (wtf_size_t index = 1u; index < items->size(); ++index) {
     const NGFragmentItem& item = *(*items)[index];
-    if (item.Type() == NGFragmentItem::kLine)
+    if (!ShouldAssociateWithLayoutObject(item))
       continue;
     LayoutObject* const layout_object = item.GetMutableLayoutObject();
     DCHECK(layout_object->IsInLayoutNGInlineFormattingContext()) << item;
     auto insert_result = last_fragment_map.insert(layout_object, index);
     if (insert_result.is_new_entry) {
+      DCHECK_EQ(layout_object->FirstInlineFragmentItemIndex(), 0u);
       layout_object->SetFirstInlineFragmentItemIndex(index);
       continue;
     }
@@ -40,8 +52,51 @@
     DCHECK_GT(last_index, 0u) << item;
     DCHECK_LT(last_index, items->size());
     DCHECK_LT(last_index, index);
+    DCHECK_EQ((*items)[last_index]->DeltaToNextForSameLayoutObject(), 0u);
     (*items)[last_index]->SetDeltaToNextForSameLayoutObject(index - last_index);
   }
 }
 
+void NGFragmentItems::ClearAssociatedFragments() const {
+  DCHECK(items_.IsEmpty() || items_[0]->IsContainer());
+  if (items_.size() <= 1)
+    return;
+  LayoutObject* last_object = nullptr;
+  for (const auto& item : base::span<const std::unique_ptr<NGFragmentItem>>(
+           items_.begin() + 1, items_.end())) {
+    if (!ShouldAssociateWithLayoutObject(*item)) {
+      // These items are not associated and that no need to clear.
+      continue;
+    }
+    LayoutObject* object = item->GetMutableLayoutObject();
+    if (!object || object == last_object)
+      continue;
+    if (object->IsInLayoutNGInlineFormattingContext())
+      object->ClearFirstInlineFragmentItemIndex();
+    last_object = object;
+  }
+}
+
+// static
+void NGFragmentItems::LayoutObjectWillBeMoved(
+    const LayoutObject& layout_object) {
+  NGInlineCursor cursor;
+  cursor.MoveTo(layout_object);
+  for (; cursor; cursor.MoveToNextForSameLayoutObject()) {
+    const NGFragmentItem* item = cursor.Current().Item();
+    item->LayoutObjectWillBeMoved();
+  }
+}
+
+// static
+void NGFragmentItems::LayoutObjectWillBeDestroyed(
+    const LayoutObject& layout_object) {
+  NGInlineCursor cursor;
+  cursor.MoveTo(layout_object);
+  for (; cursor; cursor.MoveToNextForSameLayoutObject()) {
+    const NGFragmentItem* item = cursor.Current().Item();
+    item->LayoutObjectWillBeDestroyed();
+  }
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_items.h b/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_items.h
index 76c95f1..44386167 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_items.h
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_items.h
@@ -28,8 +28,13 @@
     return UNLIKELY(first_line) ? first_line_text_content_ : text_content_;
   }
 
-  static void AssociateWithLayoutObject(
-      Vector<std::unique_ptr<NGFragmentItem>>* items);
+  // Associate/disassociate |NGFragmentItem|s with |LayoutObject|s.
+  void AssociateWithLayoutObject() const;
+  void ClearAssociatedFragments() const;
+
+  // Notify when |LayoutObject| will be destroyed/moved.
+  static void LayoutObjectWillBeDestroyed(const LayoutObject& layout_object);
+  static void LayoutObjectWillBeMoved(const LayoutObject& layout_object);
 
  private:
   // TODO(kojii): inline capacity TBD.
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_items_builder.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_items_builder.cc
index 243c9d36..ec22f6f 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_items_builder.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_items_builder.cc
@@ -217,7 +217,6 @@
                                              const PhysicalSize& outer_size,
                                              void* data) {
   ConvertToPhysical(writing_mode, direction, outer_size);
-  NGFragmentItems::AssociateWithLayoutObject(&items_);
   new (data) NGFragmentItems(this);
 }
 
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.cc
index bb39e1b4..15030129 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.cc
@@ -902,6 +902,10 @@
     DCHECK(&root);
     SetRoot(root);
     if (!HasRoot()) {
+      if (RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()) {
+        MakeNull();
+        return;
+      }
       const auto fragments =
           NGPaintFragment::InlineFragmentsFor(&layout_object);
       if (!fragments.IsInLayoutNGInlineFormattingContext() ||
@@ -915,9 +919,8 @@
   if (fragment_items_) {
     const wtf_size_t item_index = layout_object.FirstInlineFragmentItemIndex();
     if (!item_index) {
-      // TODO(yosin): Once we update all |LayoutObject::FirstInlineFragment()|
-      // clients, we should replace to |return MakeNull()|
-      MoveToItem(SlowFirstItemIteratorFor(layout_object));
+      DCHECK(items_.end() == SlowFirstItemIteratorFor(layout_object));
+      MakeNull();
       return;
     }
     const unsigned span_index = SpanIndexFromItemIndex(item_index);
diff --git a/third_party/blink/renderer/core/layout/ng/mathml/ng_math_layout_utils.cc b/third_party/blink/renderer/core/layout/ng/mathml/ng_math_layout_utils.cc
index 44f6ed8..af33302b 100644
--- a/third_party/blink/renderer/core/layout/ng/mathml/ng_math_layout_utils.cc
+++ b/third_party/blink/renderer/core/layout/ng/mathml/ng_math_layout_utils.cc
@@ -10,6 +10,7 @@
 #include "third_party/blink/renderer/core/layout/ng/ng_length_utils.h"
 #include "third_party/blink/renderer/core/layout/ng/ng_space_utils.h"
 #include "third_party/blink/renderer/core/mathml/mathml_fraction_element.h"
+#include "third_party/blink/renderer/core/mathml/mathml_under_over_element.h"
 
 namespace blink {
 
@@ -69,6 +70,21 @@
   return InFlowChildCountIs(node, 2);
 }
 
+bool IsValidMathMLUnderOver(const NGBlockNode& node) {
+  auto* scripted =
+      DynamicTo<MathMLUnderOverElement>(node.GetLayoutBox()->GetNode());
+  switch (scripted->scriptType()) {
+    case MathMLUnderOverElement::ScriptType::kUnder:
+    case MathMLUnderOverElement::ScriptType::kOver:
+      return InFlowChildCountIs(node, 2);
+    case MathMLUnderOverElement::ScriptType::kUnderOver:
+      return InFlowChildCountIs(node, 3);
+    default:
+      NOTREACHED();
+      return false;
+  }
+}
+
 namespace {
 
 inline LayoutUnit DefaultFractionLineThickness(const ComputedStyle& style) {
diff --git a/third_party/blink/renderer/core/layout/ng/mathml/ng_math_layout_utils.h b/third_party/blink/renderer/core/layout/ng/mathml/ng_math_layout_utils.h
index 366e1a811..e08dc08 100644
--- a/third_party/blink/renderer/core/layout/ng/mathml/ng_math_layout_utils.h
+++ b/third_party/blink/renderer/core/layout/ng/mathml/ng_math_layout_utils.h
@@ -26,6 +26,7 @@
 NGLayoutInputNode NextSiblingInFlow(const NGBlockNode&);
 
 bool IsValidMathMLFraction(const NGBlockNode&);
+bool IsValidMathMLUnderOver(const NGBlockNode&);
 
 inline float RuleThicknessFallback(const ComputedStyle& style) {
   // This function returns a value for the default rule thickness (TeX's
diff --git a/third_party/blink/renderer/core/layout/ng/mathml/ng_math_under_over_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/mathml/ng_math_under_over_layout_algorithm.cc
new file mode 100644
index 0000000..f1b0a75
--- /dev/null
+++ b/third_party/blink/renderer/core/layout/ng/mathml/ng_math_under_over_layout_algorithm.cc
@@ -0,0 +1,290 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/core/layout/ng/mathml/ng_math_under_over_layout_algorithm.h"
+
+#include "third_party/blink/renderer/core/layout/ng/mathml/ng_math_layout_utils.h"
+#include "third_party/blink/renderer/core/layout/ng/ng_block_break_token.h"
+#include "third_party/blink/renderer/core/layout/ng/ng_box_fragment.h"
+#include "third_party/blink/renderer/core/layout/ng/ng_length_utils.h"
+#include "third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.h"
+#include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h"
+#include "third_party/blink/renderer/core/mathml/mathml_under_over_element.h"
+
+namespace blink {
+namespace {
+
+// Describes the amount to shift to apply to the under/over boxes.
+// Data is populated from the OpenType MATH table.
+// If the OpenType MATH table is not present fallback values are used.
+// https://mathml-refresh.github.io/mathml-core/#base-with-underscript
+// https://mathml-refresh.github.io/mathml-core/#base-with-overscript
+struct UnderOverVerticalParameters {
+  bool use_under_over_bar_fallback;
+  LayoutUnit under_gap_min;
+  LayoutUnit over_gap_min;
+  LayoutUnit under_shift_min;
+  LayoutUnit over_shift_min;
+  LayoutUnit under_extra_descender;
+  LayoutUnit over_extra_ascender;
+  LayoutUnit accent_base_height;
+};
+
+UnderOverVerticalParameters GetUnderOverVerticalParameters(
+    const ComputedStyle& style) {
+  UnderOverVerticalParameters parameters;
+
+  if (!OpenTypeMathSupport::HasMathData(
+          style.GetFont().PrimaryFont()->PlatformData().GetHarfBuzzFace())) {
+    // The MATH table specification does not really provide any suggestions,
+    // except for some underbar/overbar values and AccentBaseHeight.
+    LayoutUnit default_line_thickness =
+        LayoutUnit(RuleThicknessFallback(style));
+    parameters.under_gap_min = 3 * default_line_thickness;
+    parameters.over_gap_min = 3 * default_line_thickness;
+    parameters.under_extra_descender = default_line_thickness;
+    parameters.over_extra_ascender = default_line_thickness;
+    parameters.accent_base_height =
+        LayoutUnit(style.GetFont().PrimaryFont()->GetFontMetrics().XHeight());
+    parameters.use_under_over_bar_fallback = true;
+    return parameters;
+  }
+
+  // The base is a large operator so we read UpperLimit/LowerLimit constants
+  // from the MATH table.
+  parameters.under_gap_min = LayoutUnit(
+      MathConstant(style, OpenTypeMathSupport::MathConstants::kLowerLimitGapMin)
+          .value_or(0));
+  parameters.over_gap_min = LayoutUnit(
+      MathConstant(style, OpenTypeMathSupport::MathConstants::kUpperLimitGapMin)
+          .value_or(0));
+  parameters.under_shift_min = LayoutUnit(
+      MathConstant(
+          style, OpenTypeMathSupport::MathConstants::kLowerLimitBaselineDropMin)
+          .value_or(0));
+  parameters.over_shift_min = LayoutUnit(
+      MathConstant(
+          style, OpenTypeMathSupport::MathConstants::kUpperLimitBaselineRiseMin)
+          .value_or(0));
+  parameters.use_under_over_bar_fallback = false;
+  return parameters;
+}
+
+}  // namespace
+
+NGMathUnderOverLayoutAlgorithm::NGMathUnderOverLayoutAlgorithm(
+    const NGLayoutAlgorithmParams& params)
+    : NGLayoutAlgorithm(params),
+      border_scrollbar_padding_(params.fragment_geometry.border +
+                                params.fragment_geometry.padding +
+                                params.fragment_geometry.scrollbar) {
+  DCHECK(params.space.IsNewFormattingContext());
+  container_builder_.SetIsNewFormattingContext(
+      params.space.IsNewFormattingContext());
+  container_builder_.SetInitialFragmentGeometry(params.fragment_geometry);
+}
+
+void NGMathUnderOverLayoutAlgorithm::GatherChildren(NGBlockNode* base,
+                                                    NGBlockNode* over,
+                                                    NGBlockNode* under) {
+  auto script_type =
+      DynamicTo<MathMLUnderOverElement>(Node().GetLayoutBox()->GetNode())
+          ->scriptType();
+  for (NGLayoutInputNode child = Node().FirstChild(); child;
+       child = child.NextSibling()) {
+    NGBlockNode block_child = To<NGBlockNode>(child);
+    if (child.IsOutOfFlowPositioned()) {
+      container_builder_.AddOutOfFlowChildCandidate(
+          block_child, {border_scrollbar_padding_.inline_start,
+                        border_scrollbar_padding_.block_start});
+      continue;
+    }
+    if (!*base) {
+      *base = block_child;
+      continue;
+    }
+    switch (script_type) {
+      case MathMLUnderOverElement::ScriptType::kUnder:
+        DCHECK(!*under);
+        *under = block_child;
+        break;
+      case MathMLUnderOverElement::ScriptType::kOver:
+        DCHECK(!*over);
+        *over = block_child;
+        break;
+      case MathMLUnderOverElement::ScriptType::kUnderOver:
+        if (!*under) {
+          *under = block_child;
+          continue;
+        }
+        DCHECK(!*over);
+        *over = block_child;
+        break;
+      default:
+        NOTREACHED();
+    }
+  }
+}
+
+scoped_refptr<const NGLayoutResult> NGMathUnderOverLayoutAlgorithm::Layout() {
+  DCHECK(!BreakToken());
+  DCHECK(IsValidMathMLUnderOver(Node()));
+
+  NGBlockNode base = nullptr;
+  NGBlockNode over = nullptr;
+  NGBlockNode under = nullptr;
+  GatherChildren(&base, &over, &under);
+
+  const LogicalSize border_box_size = container_builder_.InitialBorderBoxSize();
+  auto child_available_size =
+      ShrinkAvailableSize(border_box_size, border_scrollbar_padding_);
+
+  LayoutUnit block_offset = border_scrollbar_padding_.block_start;
+  UnderOverVerticalParameters parameters =
+      GetUnderOverVerticalParameters(Style());
+  // TODO(rbuis): handle stretchy operators.
+  // TODO(rbuis): handle accent.
+
+  // All children are positioned centered relative to the container (and
+  // therefore centered relative to themselves).
+  if (over) {
+    auto over_space = CreateConstraintSpaceForMathChild(
+        Node(), child_available_size, ConstraintSpace(), over);
+    scoped_refptr<const NGLayoutResult> over_layout_result =
+        over.Layout(over_space);
+    NGBoxStrut over_margins =
+        ComputeMarginsFor(over_space, over.Style(), ConstraintSpace());
+    NGBoxFragment over_fragment(
+        ConstraintSpace().GetWritingMode(), ConstraintSpace().Direction(),
+        To<NGPhysicalBoxFragment>(over_layout_result->PhysicalFragment()));
+    block_offset += parameters.over_extra_ascender + over_margins.block_start;
+    LogicalOffset over_offset = {
+        border_scrollbar_padding_.inline_start + over_margins.inline_start +
+            (child_available_size.inline_size -
+             (over_fragment.InlineSize() + over_margins.InlineSum())) /
+                2,
+        block_offset};
+    container_builder_.AddChild(over_layout_result->PhysicalFragment(),
+                                over_offset);
+    over.StoreMargins(ConstraintSpace(), over_margins);
+    if (parameters.use_under_over_bar_fallback) {
+      block_offset += over_fragment.BlockSize();
+      block_offset += parameters.over_gap_min;
+    } else {
+      LayoutUnit over_ascent =
+          over_fragment.Baseline().value_or(over_fragment.BlockSize());
+      block_offset +=
+          std::max(over_fragment.BlockSize() + parameters.over_gap_min,
+                   over_ascent + parameters.over_shift_min);
+    }
+    block_offset += over_margins.block_end;
+  }
+
+  auto base_space = CreateConstraintSpaceForMathChild(
+      Node(), child_available_size, ConstraintSpace(), base);
+  auto base_layout_result = base.Layout(base_space);
+  auto base_margins =
+      ComputeMarginsFor(base_space, base.Style(), ConstraintSpace());
+
+  NGBoxFragment base_fragment(
+      ConstraintSpace().GetWritingMode(), ConstraintSpace().Direction(),
+      To<NGPhysicalBoxFragment>(base_layout_result->PhysicalFragment()));
+
+  block_offset += base_margins.block_start;
+  LogicalOffset base_offset = {
+      border_scrollbar_padding_.inline_start + base_margins.inline_start +
+          (child_available_size.inline_size -
+           (base_fragment.InlineSize() + base_margins.InlineSum())) /
+              2,
+      block_offset};
+  container_builder_.AddChild(base_layout_result->PhysicalFragment(),
+                              base_offset);
+  base.StoreMargins(ConstraintSpace(), base_margins);
+  block_offset += base_fragment.BlockSize() + base_margins.block_end;
+
+  if (under) {
+    auto under_space = CreateConstraintSpaceForMathChild(
+        Node(), child_available_size, ConstraintSpace(), under);
+    scoped_refptr<const NGLayoutResult> under_layout_result =
+        under.Layout(under_space);
+    NGBoxStrut under_margins =
+        ComputeMarginsFor(under_space, under.Style(), ConstraintSpace());
+    NGBoxFragment under_fragment(
+        ConstraintSpace().GetWritingMode(), ConstraintSpace().Direction(),
+        To<NGPhysicalBoxFragment>(under_layout_result->PhysicalFragment()));
+    block_offset += under_margins.block_start;
+    if (parameters.use_under_over_bar_fallback) {
+      block_offset += parameters.under_gap_min;
+    } else {
+      LayoutUnit under_ascent =
+          under_fragment.Baseline().value_or(under_fragment.BlockSize());
+      block_offset += std::max(parameters.under_gap_min,
+                               parameters.under_shift_min - under_ascent);
+    }
+    LogicalOffset under_offset = {
+        border_scrollbar_padding_.inline_start + under_margins.inline_start +
+            (child_available_size.inline_size -
+             (under_fragment.InlineSize() + under_margins.InlineSum())) /
+                2,
+        block_offset};
+    block_offset += under_fragment.BlockSize();
+    block_offset += parameters.under_extra_descender;
+    container_builder_.AddChild(under_layout_result->PhysicalFragment(),
+                                under_offset);
+    under.StoreMargins(ConstraintSpace(), under_margins);
+    block_offset += under_margins.block_end;
+  }
+
+  LayoutUnit base_ascent =
+      base_fragment.Baseline().value_or(base_fragment.BlockSize());
+  container_builder_.SetBaseline(base_offset.block_offset + base_ascent);
+
+  block_offset += border_scrollbar_padding_.block_end;
+
+  LayoutUnit block_size = ComputeBlockSizeForFragment(
+      ConstraintSpace(), Style(), border_scrollbar_padding_, block_offset);
+
+  container_builder_.SetIntrinsicBlockSize(block_offset);
+  container_builder_.SetBlockSize(block_size);
+
+  NGOutOfFlowLayoutPart(Node(), ConstraintSpace(), container_builder_.Borders(),
+                        &container_builder_)
+      .Run();
+
+  return container_builder_.ToBoxFragment();
+}
+
+base::Optional<MinMaxSizes> NGMathUnderOverLayoutAlgorithm::ComputeMinMaxSizes(
+    const MinMaxSizesInput& input) const {
+  DCHECK(IsValidMathMLUnderOver(Node()));
+
+  base::Optional<MinMaxSizes> sizes =
+      CalculateMinMaxSizesIgnoringChildren(Node(), border_scrollbar_padding_);
+  if (sizes)
+    return sizes;
+
+  sizes.emplace();
+  LayoutUnit child_percentage_resolution_block_size =
+      CalculateChildPercentageBlockSizeForMinMax(
+          ConstraintSpace(), Node(), border_scrollbar_padding_,
+          input.percentage_resolution_block_size);
+
+  MinMaxSizesInput child_input(child_percentage_resolution_block_size);
+
+  for (NGLayoutInputNode child = Node().FirstChild(); child;
+       child = child.NextSibling()) {
+    if (child.IsOutOfFlowPositioned())
+      continue;
+    auto child_sizes =
+        ComputeMinAndMaxContentContribution(Style(), child, child_input);
+    NGBoxStrut margins = ComputeMinMaxMargins(Style(), child);
+    child_sizes += margins.InlineSum();
+    sizes->Encompass(child_sizes);
+  }
+
+  *sizes += border_scrollbar_padding_.InlineSum();
+  return sizes;
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/core/layout/ng/mathml/ng_math_under_over_layout_algorithm.h b/third_party/blink/renderer/core/layout/ng/mathml/ng_math_under_over_layout_algorithm.h
new file mode 100644
index 0000000..57deb0f
--- /dev/null
+++ b/third_party/blink/renderer/core/layout/ng/mathml/ng_math_under_over_layout_algorithm.h
@@ -0,0 +1,36 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_MATHML_NG_MATH_UNDER_OVER_LAYOUT_ALGORITHM_H_
+#define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_MATHML_NG_MATH_UNDER_OVER_LAYOUT_ALGORITHM_H_
+
+#include "third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.h"
+#include "third_party/blink/renderer/core/layout/ng/ng_layout_algorithm.h"
+
+namespace blink {
+
+class CORE_EXPORT NGMathUnderOverLayoutAlgorithm
+    : public NGLayoutAlgorithm<NGBlockNode,
+                               NGBoxFragmentBuilder,
+                               NGBlockBreakToken> {
+ public:
+  explicit NGMathUnderOverLayoutAlgorithm(
+      const NGLayoutAlgorithmParams& params);
+
+  scoped_refptr<const NGLayoutResult> Layout() override;
+
+  base::Optional<MinMaxSizes> ComputeMinMaxSizes(
+      const MinMaxSizesInput&) const override;
+
+ private:
+  void GatherChildren(NGBlockNode* base,
+                      NGBlockNode* second,
+                      NGBlockNode* third);
+
+  const NGBoxStrut border_scrollbar_padding_;
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_MATHML_NG_MATH_UNDER_OVER_LAYOUT_ALGORITHM_H_
diff --git a/third_party/blink/renderer/core/layout/ng/ng_block_node.cc b/third_party/blink/renderer/core/layout/ng/ng_block_node.cc
index 868dbbc..11af02e 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_block_node.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_block_node.cc
@@ -33,6 +33,7 @@
 #include "third_party/blink/renderer/core/layout/ng/mathml/ng_math_layout_utils.h"
 #include "third_party/blink/renderer/core/layout/ng/mathml/ng_math_row_layout_algorithm.h"
 #include "third_party/blink/renderer/core/layout/ng/mathml/ng_math_space_layout_algorithm.h"
+#include "third_party/blink/renderer/core/layout/ng/mathml/ng_math_under_over_layout_algorithm.h"
 #include "third_party/blink/renderer/core/layout/ng/ng_block_break_token.h"
 #include "third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.h"
 #include "third_party/blink/renderer/core/layout/ng/ng_box_fragment.h"
@@ -55,6 +56,7 @@
 #include "third_party/blink/renderer/core/mathml/mathml_element.h"
 #include "third_party/blink/renderer/core/mathml/mathml_fraction_element.h"
 #include "third_party/blink/renderer/core/mathml/mathml_space_element.h"
+#include "third_party/blink/renderer/core/mathml/mathml_under_over_element.h"
 #include "third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h"
 #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
 #include "third_party/blink/renderer/platform/text/writing_mode.h"
@@ -64,6 +66,11 @@
 
 namespace {
 
+inline bool HasInlineChildren(LayoutBlockFlow* block_flow) {
+  auto* child = GetLayoutObjectForFirstChildNode(block_flow);
+  return child && AreNGBlockFlowChildrenInline(block_flow);
+}
+
 inline LayoutMultiColumnFlowThread* GetFlowThread(
     const LayoutBlockFlow* block_flow) {
   if (!block_flow)
@@ -99,6 +106,9 @@
   else if (IsA<MathMLFractionElement>(element) &&
            IsValidMathMLFraction(params.node))
     CreateAlgorithmAndRun<NGMathFractionLayoutAlgorithm>(params, callback);
+  else if (IsA<MathMLUnderOverElement>(element) &&
+           IsValidMathMLUnderOver(params.node))
+    CreateAlgorithmAndRun<NGMathUnderOverLayoutAlgorithm>(params, callback);
   else
     CreateAlgorithmAndRun<NGMathRowLayoutAlgorithm>(params, callback);
 }
@@ -628,9 +638,8 @@
     box_->SetCachedLayoutResult(layout_result);
 
   if (block_flow) {
-    auto* child = GetLayoutObjectForFirstChildNode(block_flow);
-    bool has_inline_children =
-        child && AreNGBlockFlowChildrenInline(block_flow);
+    const NGFragmentItems* items = physical_fragment.Items();
+    bool has_inline_children = items || HasInlineChildren(block_flow);
 
     // Don't consider display-locked objects as having any children.
     if (has_inline_children && box_->LayoutBlockedByDisplayLock(
@@ -656,15 +665,18 @@
             physical_fragment, physical_fragment.Size().width,
             Style().IsFlippedBlocksWritingMode());
         block_flow->SetPaintFragment(break_token, &physical_fragment);
-      } else {
-        CopyFragmentDataToLayoutBoxForInlineChildren(physical_fragment);
+      } else if (items) {
+        CopyFragmentItemsToLayoutBox(physical_fragment, *items);
       }
     } else {
       // We still need to clear paint fragments in case it had inline children,
       // and thus had NGPaintFragment.
       block_flow->ClearNGInlineNodeData();
-      block_flow->SetPaintFragment(break_token, nullptr);
+      if (!RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled())
+        block_flow->SetPaintFragment(break_token, nullptr);
     }
+  } else {
+    DCHECK(!physical_fragment.HasItems());
   }
 
   CopyFragmentDataToLayoutBox(constraint_space, *layout_result, break_token);
@@ -1140,14 +1152,13 @@
   }
 }
 
-void NGBlockNode::CopyFragmentDataToLayoutBoxForInlineChildren(
-    const NGPhysicalBoxFragment& container) {
+void NGBlockNode::CopyFragmentItemsToLayoutBox(
+    const NGPhysicalBoxFragment& container,
+    const NGFragmentItems& items) {
   DCHECK(RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled());
-  const NGFragmentItems* items = container.Items();
-  if (!items)
-    return;
+
   bool initial_container_is_flipped = Style().IsFlippedBlocksWritingMode();
-  for (NGInlineCursor cursor(*items); cursor; cursor.MoveToNext()) {
+  for (NGInlineCursor cursor(items); cursor; cursor.MoveToNext()) {
     if (const NGPhysicalBoxFragment* child = cursor.Current().BoxFragment()) {
       // Replaced elements and inline blocks need Location() set relative to
       // their block container.
diff --git a/third_party/blink/renderer/core/layout/ng/ng_block_node.h b/third_party/blink/renderer/core/layout/ng/ng_block_node.h
index 7ccb575..5e90d19 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_block_node.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_block_node.h
@@ -174,8 +174,8 @@
       const NGConstraintSpace&,
       const NGLayoutResult&,
       const NGBlockBreakToken* previous_break_token);
-  void CopyFragmentDataToLayoutBoxForInlineChildren(
-      const NGPhysicalBoxFragment& container);
+  void CopyFragmentItemsToLayoutBox(const NGPhysicalBoxFragment& container,
+                                    const NGFragmentItems& items);
   void CopyFragmentDataToLayoutBoxForInlineChildren(
       const NGPhysicalContainerFragment& container,
       LayoutUnit initial_container_width,
diff --git a/third_party/blink/renderer/core/loader/resource/font_resource.cc b/third_party/blink/renderer/core/loader/resource/font_resource.cc
index 794161c5..0a601bd 100644
--- a/third_party/blink/renderer/core/loader/resource/font_resource.cc
+++ b/third_party/blink/renderer/core/loader/resource/font_resource.cc
@@ -119,6 +119,8 @@
 
     if (!font_data_)
       SetStatus(ResourceStatus::kDecodeError);
+    else
+      ClearData();
   }
   return font_data_;
 }
diff --git a/third_party/blink/renderer/core/mathml/BUILD.gn b/third_party/blink/renderer/core/mathml/BUILD.gn
index 2204ea93..a78e6696 100644
--- a/third_party/blink/renderer/core/mathml/BUILD.gn
+++ b/third_party/blink/renderer/core/mathml/BUILD.gn
@@ -14,5 +14,7 @@
     "mathml_row_element.h",
     "mathml_space_element.cc",
     "mathml_space_element.h",
+    "mathml_under_over_element.cc",
+    "mathml_under_over_element.h",
   ]
 }
diff --git a/third_party/blink/renderer/core/mathml/mathml_tag_names.json5 b/third_party/blink/renderer/core/mathml/mathml_tag_names.json5
index 17fd2a645..2ecb7857 100644
--- a/third_party/blink/renderer/core/mathml/mathml_tag_names.json5
+++ b/third_party/blink/renderer/core/mathml/mathml_tag_names.json5
@@ -76,6 +76,18 @@
       interfaceName: "MathMLElement",
     },
     {
+      name: "mover",
+      interfaceName: "MathMLUnderOverElement",
+    },
+    {
+      name: "munder",
+      interfaceName: "MathMLUnderOverElement",
+    },
+    {
+      name: "munderover",
+      interfaceName: "MathMLUnderOverElement",
+    },
+    {
       name: "semantics",
       interfaceName: "MathMLRowElement",
     },
diff --git a/third_party/blink/renderer/core/mathml/mathml_under_over_element.cc b/third_party/blink/renderer/core/mathml/mathml_under_over_element.cc
new file mode 100644
index 0000000..f0ce234
--- /dev/null
+++ b/third_party/blink/renderer/core/mathml/mathml_under_over_element.cc
@@ -0,0 +1,35 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/core/mathml/mathml_under_over_element.h"
+
+#include "third_party/blink/renderer/core/layout/ng/mathml/layout_ng_mathml_block.h"
+#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
+
+namespace blink {
+
+static MathMLUnderOverElement::ScriptType scriptTypeOf(
+    const QualifiedName& tagName) {
+  if (tagName == mathml_names::kMunderTag)
+    return MathMLUnderOverElement::ScriptType::kUnder;
+  if (tagName == mathml_names::kMoverTag)
+    return MathMLUnderOverElement::ScriptType::kOver;
+  DCHECK_EQ(tagName, mathml_names::kMunderoverTag);
+  return MathMLUnderOverElement::ScriptType::kUnderOver;
+}
+
+MathMLUnderOverElement::MathMLUnderOverElement(const QualifiedName& tagName,
+                                               Document& document)
+    : MathMLElement(tagName, document), script_type_(scriptTypeOf(tagName)) {}
+
+LayoutObject* MathMLUnderOverElement::CreateLayoutObject(
+    const ComputedStyle& style,
+    LegacyLayout legacy) {
+  if (!RuntimeEnabledFeatures::MathMLCoreEnabled() ||
+      !style.IsDisplayMathType() || legacy == LegacyLayout::kForce)
+    return MathMLElement::CreateLayoutObject(style, legacy);
+  return new LayoutNGMathMLBlock(this);
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/core/mathml/mathml_under_over_element.h b/third_party/blink/renderer/core/mathml/mathml_under_over_element.h
new file mode 100644
index 0000000..50f3b690
--- /dev/null
+++ b/third_party/blink/renderer/core/mathml/mathml_under_over_element.h
@@ -0,0 +1,50 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_MATHML_MATHML_UNDER_OVER_ELEMENT_H_
+#define THIRD_PARTY_BLINK_RENDERER_CORE_MATHML_MATHML_UNDER_OVER_ELEMENT_H_
+
+#include "third_party/blink/renderer/core/mathml/mathml_element.h"
+
+namespace blink {
+
+class Document;
+
+class CORE_EXPORT MathMLUnderOverElement final : public MathMLElement {
+ public:
+  MathMLUnderOverElement(const QualifiedName& tagName, Document& document);
+
+  enum class ScriptType {
+    kUnder,
+    kOver,
+    kUnderOver,
+  };
+  ScriptType scriptType() const { return script_type_; }
+
+ private:
+  const ScriptType script_type_;
+  LayoutObject* CreateLayoutObject(const ComputedStyle&,
+                                   LegacyLayout legacy) override;
+};
+
+template <>
+inline bool IsElementOfType<const MathMLUnderOverElement>(const Node& node) {
+  return IsA<MathMLUnderOverElement>(node);
+}
+template <>
+struct DowncastTraits<MathMLUnderOverElement> {
+  static bool AllowFrom(const Node& node) {
+    auto* mathml_element = DynamicTo<MathMLElement>(node);
+    return mathml_element && AllowFrom(*mathml_element);
+  }
+  static bool AllowFrom(const MathMLElement& mathml_element) {
+    return mathml_element.HasTagName(mathml_names::kMunderTag) ||
+           mathml_element.HasTagName(mathml_names::kMoverTag) ||
+           mathml_element.HasTagName(mathml_names::kMunderoverTag);
+  }
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_MATHML_MATHML_UNDER_OVER_ELEMENT_H_
diff --git a/third_party/blink/renderer/core/page/chrome_client_impl.cc b/third_party/blink/renderer/core/page/chrome_client_impl.cc
index 1b430137..cf28300c 100644
--- a/third_party/blink/renderer/core/page/chrome_client_impl.cc
+++ b/third_party/blink/renderer/core/page/chrome_client_impl.cc
@@ -436,8 +436,17 @@
   if (client) {
     client->ConvertViewportToWindow(&screen_rect);
     WebRect view_rect = client->ViewRect();
-    screen_rect.x += view_rect.x;
-    screen_rect.y += view_rect.y;
+
+    base::CheckedNumeric<int> screen_rect_x = screen_rect.x;
+    base::CheckedNumeric<int> screen_rect_y = screen_rect.y;
+
+    screen_rect_x += view_rect.x;
+    screen_rect_y += view_rect.y;
+
+    screen_rect.x =
+        screen_rect_x.ValueOrDefault(std::numeric_limits<int>::max());
+    screen_rect.y =
+        screen_rect_y.ValueOrDefault(std::numeric_limits<int>::max());
   }
 
   return screen_rect;
diff --git a/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc b/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc
index 1cca3d0..82722d99 100644
--- a/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc
+++ b/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc
@@ -3007,6 +3007,7 @@
   // This will be used in the loop finding matching fragment from ancestor flow
   // threads after no matching from parent_fragments.
   LayoutUnit logical_top_in_containing_flow_thread;
+  bool crossed_flow_thread = false;
 
   if (object_.IsLayoutFlowThread()) {
     const auto& flow_thread = ToLayoutFlowThread(object_);
@@ -3025,6 +3026,7 @@
         return context;
       }
     }
+    crossed_flow_thread = true;
   } else {
     bool parent_is_under_same_flow_thread;
     auto* pagination_layer =
@@ -3057,6 +3059,7 @@
     }
 
     logical_top_in_containing_flow_thread = logical_top_in_flow_thread;
+    crossed_flow_thread = !parent_is_under_same_flow_thread;
   }
 
   // Found no matching parent fragment. Use parent_fragments[0] to inherit
@@ -3081,14 +3084,35 @@
     if (!container->FirstFragment().HasLocalBorderBoxProperties())
       continue;
 
-    for (const auto* fragment = &container->FirstFragment(); fragment;
-         fragment = fragment->NextFragment()) {
-      if (fragment->LogicalTopInFlowThread() ==
-          logical_top_in_containing_flow_thread) {
-        // Found a matching fragment in an ancestor container. Use the
-        // container's content clip as the clip state.
-        context.current.clip = &fragment->PostOverflowClip();
-        return context;
+    const FragmentData* container_fragment = &container->FirstFragment();
+    while (container_fragment->LogicalTopInFlowThread() <
+               logical_top_in_containing_flow_thread &&
+           container_fragment->NextFragment())
+      container_fragment = container_fragment->NextFragment();
+
+    if (container_fragment->LogicalTopInFlowThread() ==
+        logical_top_in_containing_flow_thread) {
+      // Found a matching fragment in an ancestor container. Use the
+      // container's content clip as the clip state.
+      context.current.clip = &container_fragment->PostOverflowClip();
+      return context;
+    }
+
+    // We didn't find corresponding fragment in the container because the
+    // fragment fully overflows the container. If the container has overflow
+    // clip, then this fragment should be under |container_fragment|.
+    // This works only when the current fragment and the overflow clip are under
+    // the same flow thread. In other cases, we just leave it broken, which will
+    // be fixed by LayoutNG block fragments hopefully.
+    if (!crossed_flow_thread) {
+      if (const auto* container_properties =
+              container_fragment->PaintProperties()) {
+        if (const auto* overflow_clip = container_properties->OverflowClip()) {
+          context.logical_top_in_flow_thread =
+              container_fragment->LogicalTopInFlowThread();
+          context.current.clip = overflow_clip;
+          return context;
+        }
       }
     }
 
@@ -3097,6 +3121,7 @@
           FragmentLogicalTopInParentFlowThread(
               ToLayoutFlowThread(*container),
               logical_top_in_containing_flow_thread);
+      crossed_flow_thread = true;
     }
   }
 
@@ -3181,8 +3206,16 @@
     }
 
     // Match to parent fragments from the same containing flow thread.
-    new_fragment_contexts.push_back(
-        ContextForFragment(fragment_clip, logical_top_in_flow_thread));
+    auto fragment_context =
+        ContextForFragment(fragment_clip, logical_top_in_flow_thread);
+    // ContextForFragment may override logical_top_in_flow_thread.
+    logical_top_in_flow_thread = fragment_context.logical_top_in_flow_thread;
+    // Avoid fragment with duplicated overridden logical_top_in_flow_thread.
+    if (new_fragment_contexts.size() &&
+        new_fragment_contexts.back().logical_top_in_flow_thread ==
+            logical_top_in_flow_thread)
+      break;
+    new_fragment_contexts.push_back(fragment_context);
 
     if (current_fragment_data) {
       if (!current_fragment_data->NextFragment())
diff --git a/third_party/blink/renderer/core/paint/paint_property_tree_builder_test.cc b/third_party/blink/renderer/core/paint/paint_property_tree_builder_test.cc
index e2fcffa..289444c 100644
--- a/third_party/blink/renderer/core/paint/paint_property_tree_builder_test.cc
+++ b/third_party/blink/renderer/core/paint/paint_property_tree_builder_test.cc
@@ -4322,6 +4322,33 @@
   EXPECT_EQ(1u, NumFragments(GetLayoutObjectByElementId("layer")));
 }
 
+TEST_P(PaintPropertyTreeBuilderTest, OverflowClipUnderMultiColumn) {
+  SetBodyInnerHTML(R"HTML(
+    <style>body { margin: 0; }</style>
+    <div style='columns: 4; height: 100px; column-fill: auto; column-gap: 0'>
+      <div id='clip' style='height: 200px; overflow: hidden'>
+        <div id='child1' style='height: 400px'></div>
+        <div id='child2' style='height: 400px'></div>
+      </div>
+    </div>
+  )HTML");
+
+  const auto* clip = GetLayoutObjectByElementId("clip");
+  ASSERT_EQ(2u, NumFragments(clip));
+  EXPECT_EQ(LayoutUnit(), FragmentAt(clip, 0).LogicalTopInFlowThread());
+  EXPECT_EQ(LayoutUnit(100), FragmentAt(clip, 1).LogicalTopInFlowThread());
+  const auto* child1 = GetLayoutObjectByElementId("child1");
+  ASSERT_EQ(2u, NumFragments(child1));
+  EXPECT_EQ(LayoutUnit(), FragmentAt(child1, 0).LogicalTopInFlowThread());
+  EXPECT_EQ(PhysicalOffset(), FragmentAt(child1, 0).PaintOffset());
+  EXPECT_EQ(LayoutUnit(100), FragmentAt(child1, 1).LogicalTopInFlowThread());
+  EXPECT_EQ(PhysicalOffset(200, -100), FragmentAt(child1, 1).PaintOffset());
+  const auto* child2 = GetLayoutObjectByElementId("child2");
+  ASSERT_EQ(1u, NumFragments(child2));
+  EXPECT_EQ(LayoutUnit(100), FragmentAt(child2, 0).LogicalTopInFlowThread());
+  EXPECT_EQ(PhysicalOffset(200, 300), FragmentAt(child2, 0).PaintOffset());
+}
+
 TEST_P(PaintPropertyTreeBuilderTest, CompositedUnderMultiColumn) {
   SetBodyInnerHTML(R"HTML(
     <style>body { margin: 0; }</style>
diff --git a/third_party/blink/renderer/core/style/computed_style.cc b/third_party/blink/renderer/core/style/computed_style.cc
index 10d3535..cd061dd 100644
--- a/third_party/blink/renderer/core/style/computed_style.cc
+++ b/third_party/blink/renderer/core/style/computed_style.cc
@@ -1072,7 +1072,7 @@
 }
 
 void ComputedStyle::AddCallbackSelector(const String& selector) {
-  if (!CallbackSelectorsInternal().Contains(selector))
+  if (!CallbackSelectors().Contains(selector))
     MutableCallbackSelectorsInternal().push_back(selector);
 }
 
diff --git a/third_party/blink/renderer/core/style/computed_style.h b/third_party/blink/renderer/core/style/computed_style.h
index 3896c54d0..790c5be 100644
--- a/third_party/blink/renderer/core/style/computed_style.h
+++ b/third_party/blink/renderer/core/style/computed_style.h
@@ -94,6 +94,7 @@
 
 namespace css_longhand {
 
+class Appearance;
 class BackgroundColor;
 class BorderBottomColor;
 class BorderLeftColor;
@@ -125,7 +126,6 @@
 class StopColor;
 class Stroke;
 class TextDecorationColor;
-class WebkitAppearance;
 class WebkitTapHighlightColor;
 class WebkitTextEmphasisColor;
 class WebkitTextFillColor;
@@ -238,7 +238,7 @@
   friend class LayoutTheme;
   friend class StyleAdjuster;
   friend class StyleCascade;
-  friend class css_longhand::WebkitAppearance;
+  friend class css_longhand::Appearance;
   // Editing has to only reveal unvisited info.
   friend class ApplyStyleCommand;
   // Editing has to only reveal unvisited info.
@@ -707,7 +707,7 @@
            OutlineColorIsCurrentColor() == other.OutlineColorIsCurrentColor() &&
            OutlineColor() == other.OutlineColor() &&
            OutlineStyle() == other.OutlineStyle() &&
-           OutlineOffsetInternal() == other.OutlineOffsetInternal() &&
+           OutlineOffset() == other.OutlineOffset() &&
            OutlineStyleIsAuto() == other.OutlineStyleIsAuto();
   }
 
@@ -727,11 +727,6 @@
   }
   void SetOutlineWidth(uint16_t v) { SetOutlineWidthInternal(LayoutUnit(v)); }
 
-  // outline-offset
-  int OutlineOffset() const {
-    return OutlineOffsetInternal();
-  }
-
   // -webkit-perspective-origin-x
   const Length& PerspectiveOriginX() const { return PerspectiveOrigin().X(); }
   void SetPerspectiveOriginX(const Length& v) {
@@ -1261,9 +1256,6 @@
   CSSTransitionData& AccessTransitions();
 
   // Callback selectors.
-  const Vector<String>& CallbackSelectors() const {
-    return CallbackSelectorsInternal();
-  }
   void AddCallbackSelector(const String& selector);
 
   // Non-property flags.
diff --git a/third_party/blink/renderer/core/style/computed_style_diff_functions.json5 b/third_party/blink/renderer/core/style/computed_style_diff_functions.json5
index cd6337f..1189ff2 100644
--- a/third_party/blink/renderer/core/style/computed_style_diff_functions.json5
+++ b/third_party/blink/renderer/core/style/computed_style_diff_functions.json5
@@ -81,7 +81,7 @@
     {
         name: "DiffNeedsFullLayoutAndPaintInvalidation",
         fields_to_diff: ["padding-top", "padding-left", "padding-right", 
-                "padding-bottom", "-webkit-appearance", "-webkit-line-clamp",
+                "padding-bottom", "appearance", "-webkit-line-clamp",
                 "text-overflow", "shape-margin", "order", "-webkit-highlight",
                 "text-indent", "text-align-last", "TextIndentLine", "-internal-effective-zoom",
                 "word-break", "overflow-wrap", "-webkit-line-break", 
diff --git a/third_party/blink/renderer/core/style/computed_style_extra_fields.json5 b/third_party/blink/renderer/core/style/computed_style_extra_fields.json5
index 9327fd0..16db031 100644
--- a/third_party/blink/renderer/core/style/computed_style_extra_fields.json5
+++ b/third_party/blink/renderer/core/style/computed_style_extra_fields.json5
@@ -638,7 +638,7 @@
       default_value: "Vector<String>()",
       include_paths: ["third_party/blink/renderer/platform/wtf/vector.h",
                       "third_party/blink/renderer/platform/wtf/text/wtf_string.h"],
-      computed_style_custom_functions: ["getter", "setter"],
+      computed_style_custom_functions: ["setter"],
     },
     {
       name: "PaintImages",
diff --git a/third_party/blink/renderer/core/svg/svg_length.cc b/third_party/blink/renderer/core/svg/svg_length.cc
index 9a0108d1..ffcafb4 100644
--- a/third_party/blink/renderer/core/svg/svg_length.cc
+++ b/third_party/blink/renderer/core/svg/svg_length.cc
@@ -165,10 +165,7 @@
     case kCalcLength:
     case kCalcNumber:
     case kCalcPercent:
-    case kCalcPercentNumber:
     case kCalcPercentLength:
-    case kCalcLengthNumber:
-    case kCalcPercentLengthNumber:
       return true;
     default:
       return false;
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object.cc b/third_party/blink/renderer/modules/accessibility/ax_object.cc
index 3d46994..52204d7 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_object.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_object.cc
@@ -3727,6 +3727,7 @@
     case ax::mojom::Role::kVideo:
     case ax::mojom::Role::kWebArea:
     case ax::mojom::Role::kWebView:
+    case ax::mojom::Role::kWindow:
       result = false;
       break;
 
@@ -3822,7 +3823,6 @@
     }
 
     case ax::mojom::Role::kUnknown:
-    case ax::mojom::Role::kMaxValue:
       LOG(ERROR) << "ax::mojom::Role::kUnknown for " << GetNode();
       NOTREACHED();
       break;
diff --git a/third_party/blink/renderer/modules/background_sync/periodic_sync_manager.cc b/third_party/blink/renderer/modules/background_sync/periodic_sync_manager.cc
index 097ab0c0..b3b4376 100644
--- a/third_party/blink/renderer/modules/background_sync/periodic_sync_manager.cc
+++ b/third_party/blink/renderer/modules/background_sync/periodic_sync_manager.cc
@@ -19,7 +19,9 @@
 PeriodicSyncManager::PeriodicSyncManager(
     ServiceWorkerRegistration* registration,
     scoped_refptr<base::SequencedTaskRunner> task_runner)
-    : registration_(registration), task_runner_(std::move(task_runner)) {
+    : registration_(registration),
+      task_runner_(std::move(task_runner)),
+      background_sync_service_(registration_->GetExecutionContext()) {
   DCHECK(registration_);
 }
 
@@ -89,13 +91,13 @@
   return promise;
 }
 
-const mojo::Remote<mojom::blink::PeriodicBackgroundSyncService>&
+mojom::blink::PeriodicBackgroundSyncService*
 PeriodicSyncManager::GetBackgroundSyncServiceRemote() {
   if (!background_sync_service_.is_bound()) {
     Platform::Current()->GetBrowserInterfaceBroker()->GetInterface(
-        background_sync_service_.BindNewPipeAndPassReceiver());
+        background_sync_service_.BindNewPipeAndPassReceiver(task_runner_));
   }
-  return background_sync_service_;
+  return background_sync_service_.get();
 }
 
 void PeriodicSyncManager::RegisterCallback(
@@ -186,6 +188,7 @@
 
 void PeriodicSyncManager::Trace(Visitor* visitor) {
   visitor->Trace(registration_);
+  visitor->Trace(background_sync_service_);
   ScriptWrappable::Trace(visitor);
 }
 
diff --git a/third_party/blink/renderer/modules/background_sync/periodic_sync_manager.h b/third_party/blink/renderer/modules/background_sync/periodic_sync_manager.h
index b76e055..0b7111a3 100644
--- a/third_party/blink/renderer/modules/background_sync/periodic_sync_manager.h
+++ b/third_party/blink/renderer/modules/background_sync/periodic_sync_manager.h
@@ -7,9 +7,10 @@
 
 #include "base/memory/scoped_refptr.h"
 #include "base/sequenced_task_runner.h"
-#include "mojo/public/cpp/bindings/remote.h"
 #include "third_party/blink/public/mojom/background_sync/background_sync.mojom-blink.h"
 #include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
+#include "third_party/blink/renderer/platform/mojo/heap_mojo_remote.h"
+#include "third_party/blink/renderer/platform/mojo/heap_mojo_wrapper_mode.h"
 #include "third_party/blink/renderer/platform/wtf/vector.h"
 
 namespace blink {
@@ -43,8 +44,7 @@
   // mojo::Remote<mojom::blink::PeriodicBackgroundSyncService>. A connection
   // with the the browser's BackgroundSyncService is created the first time this
   // method is called.
-  const mojo::Remote<mojom::blink::PeriodicBackgroundSyncService>&
-  GetBackgroundSyncServiceRemote();
+  mojom::blink::PeriodicBackgroundSyncService* GetBackgroundSyncServiceRemote();
 
   // Callbacks
   void RegisterCallback(ScriptPromiseResolver* resolver,
@@ -59,7 +59,8 @@
 
   Member<ServiceWorkerRegistration> registration_;
   scoped_refptr<base::SequencedTaskRunner> task_runner_;
-  mojo::Remote<mojom::blink::PeriodicBackgroundSyncService>
+  HeapMojoRemote<mojom::blink::PeriodicBackgroundSyncService,
+                 HeapMojoWrapperMode::kWithoutContextObserver>
       background_sync_service_;
 };
 
diff --git a/third_party/blink/renderer/modules/cache_storage/cache_storage.cc b/third_party/blink/renderer/modules/cache_storage/cache_storage.cc
index 76483de..29cd652 100644
--- a/third_party/blink/renderer/modules/cache_storage/cache_storage.cc
+++ b/third_party/blink/renderer/modules/cache_storage/cache_storage.cc
@@ -104,7 +104,7 @@
 
   // The context may be destroyed and the mojo connection unbound. However the
   // object may live on, reject any requests after the context is destroyed.
-  if (!cache_storage_remote_) {
+  if (!cache_storage_remote_.is_bound()) {
     resolver->Reject(MakeGarbageCollected<DOMException>(
         DOMExceptionCode::kInvalidStateError));
     return promise;
@@ -177,7 +177,7 @@
 
   // The context may be destroyed and the mojo connection unbound. However the
   // object may live on, reject any requests after the context is destroyed.
-  if (!cache_storage_remote_) {
+  if (!cache_storage_remote_.is_bound()) {
     resolver->Reject(MakeGarbageCollected<DOMException>(
         DOMExceptionCode::kInvalidStateError));
     return promise;
@@ -237,7 +237,7 @@
 
   // The context may be destroyed and the mojo connection unbound. However the
   // object may live on, reject any requests after the context is destroyed.
-  if (!cache_storage_remote_) {
+  if (!cache_storage_remote_.is_bound()) {
     resolver->Reject(MakeGarbageCollected<DOMException>(
         DOMExceptionCode::kInvalidStateError));
     return promise;
@@ -297,7 +297,7 @@
 
   // The context may be destroyed and the mojo connection unbound. However the
   // object may live on, reject any requests after the context is destroyed.
-  if (!cache_storage_remote_) {
+  if (!cache_storage_remote_.is_bound()) {
     resolver->Reject(MakeGarbageCollected<DOMException>(
         DOMExceptionCode::kInvalidStateError));
     return promise;
@@ -374,7 +374,7 @@
 
   // The context may be destroyed and the mojo connection unbound. However the
   // object may live on, reject any requests after the context is destroyed.
-  if (!cache_storage_remote_) {
+  if (!cache_storage_remote_.is_bound()) {
     resolver->Reject(MakeGarbageCollected<DOMException>(
         DOMExceptionCode::kInvalidStateError));
     return promise;
@@ -457,9 +457,10 @@
 
 CacheStorage::CacheStorage(ExecutionContext* context,
                            GlobalFetch::ScopedFetcher* fetcher)
-    : ExecutionContextLifecycleObserver(context),
+    : ExecutionContextClient(context),
       scoped_fetcher_(fetcher),
       blob_client_list_(MakeGarbageCollected<CacheStorageBlobClientList>()),
+      cache_storage_remote_(context),
       ever_used_(false) {
   // See https://bit.ly/2S0zRAS for task types.
   scoped_refptr<base::SingleThreadTaskRunner> task_runner =
@@ -471,8 +472,9 @@
     mojo::PendingRemote<mojom::blink::CacheStorage> info =
         service_worker->TakeCacheStorage();
     if (info) {
-      cache_storage_remote_ = mojo::Remote<mojom::blink::CacheStorage>(
-          std::move(info), task_runner);
+      cache_storage_remote_ =
+          HeapMojoRemote<mojom::blink::CacheStorage>(context);
+      cache_storage_remote_.Bind(std::move(info), task_runner);
       return;
     }
   }
@@ -497,8 +499,9 @@
 void CacheStorage::Trace(Visitor* visitor) {
   visitor->Trace(scoped_fetcher_);
   visitor->Trace(blob_client_list_);
+  visitor->Trace(cache_storage_remote_);
   ScriptWrappable::Trace(visitor);
-  ExecutionContextLifecycleObserver::Trace(visitor);
+  ExecutionContextClient::Trace(visitor);
 }
 
 bool CacheStorage::IsAllowed(ScriptState* script_state) {
@@ -509,8 +512,4 @@
   return allowed_.value();
 }
 
-void CacheStorage::ContextDestroyed() {
-  cache_storage_remote_.reset();
-}
-
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/cache_storage/cache_storage.h b/third_party/blink/renderer/modules/cache_storage/cache_storage.h
index 87a4563e..00fb1777 100644
--- a/third_party/blink/renderer/modules/cache_storage/cache_storage.h
+++ b/third_party/blink/renderer/modules/cache_storage/cache_storage.h
@@ -8,7 +8,6 @@
 #include <memory>
 #include "base/macros.h"
 #include "base/optional.h"
-#include "mojo/public/cpp/bindings/remote.h"
 #include "third_party/blink/public/mojom/cache_storage/cache_storage.mojom-blink-forward.h"
 #include "third_party/blink/renderer/bindings/core/v8/active_script_wrappable.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
@@ -17,6 +16,7 @@
 #include "third_party/blink/renderer/modules/cache_storage/cache.h"
 #include "third_party/blink/renderer/platform/bindings/script_state.h"
 #include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
+#include "third_party/blink/renderer/platform/mojo/heap_mojo_remote.h"
 #include "third_party/blink/renderer/platform/wtf/forward.h"
 #include "third_party/blink/renderer/platform/wtf/hash_map.h"
 
@@ -27,7 +27,7 @@
 
 class CacheStorage final : public ScriptWrappable,
                            public ActiveScriptWrappable<CacheStorage>,
-                           public ExecutionContextLifecycleObserver {
+                           public ExecutionContextClient {
   DEFINE_WRAPPERTYPEINFO();
   USING_GARBAGE_COLLECTED_MIXIN(CacheStorage);
 
@@ -46,7 +46,6 @@
 
   bool HasPendingActivity() const override;
   void Trace(Visitor*) override;
-  void ContextDestroyed() override;
 
  private:
   ScriptPromise MatchImpl(ScriptState*,
@@ -58,7 +57,7 @@
   Member<GlobalFetch::ScopedFetcher> scoped_fetcher_;
   Member<CacheStorageBlobClientList> blob_client_list_;
 
-  mojo::Remote<mojom::blink::CacheStorage> cache_storage_remote_;
+  HeapMojoRemote<mojom::blink::CacheStorage> cache_storage_remote_;
   base::Optional<bool> allowed_;
   bool ever_used_;
 
diff --git a/third_party/blink/renderer/modules/keyboard/keyboard_layout.cc b/third_party/blink/renderer/modules/keyboard/keyboard_layout.cc
index 6d2c8842..e6982582 100644
--- a/third_party/blink/renderer/modules/keyboard/keyboard_layout.cc
+++ b/third_party/blink/renderer/modules/keyboard/keyboard_layout.cc
@@ -10,6 +10,7 @@
 #include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
 #include "third_party/blink/renderer/core/dom/dom_exception.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
+#include "third_party/blink/renderer/core/frame/local_dom_window.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
 #include "third_party/blink/renderer/platform/heap/heap.h"
@@ -31,7 +32,7 @@
 }  // namespace
 
 KeyboardLayout::KeyboardLayout(ExecutionContext* context)
-    : ExecutionContextClient(context) {}
+    : ExecutionContextClient(context), service_(context) {}
 
 ScriptPromise KeyboardLayout::GetKeyboardLayoutMap(
     ScriptState* script_state,
@@ -75,14 +76,15 @@
 }
 
 bool KeyboardLayout::EnsureServiceConnected() {
-  if (!service_) {
+  if (!service_.is_bound()) {
     LocalFrame* frame = GetFrame();
     if (!frame) {
       return false;
     }
     frame->GetBrowserInterfaceBroker().GetInterface(
-        service_.BindNewPipeAndPassReceiver());
-    DCHECK(service_);
+        service_.BindNewPipeAndPassReceiver(
+            frame->GetTaskRunner(TaskType::kMiscPlatformAPI)));
+    DCHECK(service_.is_bound());
   }
   return true;
 }
@@ -116,6 +118,7 @@
 
 void KeyboardLayout::Trace(Visitor* visitor) {
   visitor->Trace(script_promise_resolver_);
+  visitor->Trace(service_);
   ExecutionContextClient::Trace(visitor);
 }
 
diff --git a/third_party/blink/renderer/modules/keyboard/keyboard_layout.h b/third_party/blink/renderer/modules/keyboard/keyboard_layout.h
index 4307f83..526fc43 100644
--- a/third_party/blink/renderer/modules/keyboard/keyboard_layout.h
+++ b/third_party/blink/renderer/modules/keyboard/keyboard_layout.h
@@ -6,12 +6,13 @@
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_KEYBOARD_KEYBOARD_LAYOUT_H_
 
 #include "base/macros.h"
-#include "mojo/public/cpp/bindings/remote.h"
 #include "third_party/blink/public/mojom/keyboard_lock/keyboard_lock.mojom-blink.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context_lifecycle_observer.h"
 #include "third_party/blink/renderer/modules/keyboard/keyboard_layout_map.h"
+#include "third_party/blink/renderer/platform/mojo/heap_mojo_remote.h"
+#include "third_party/blink/renderer/platform/mojo/heap_mojo_wrapper_mode.h"
 
 namespace blink {
 
@@ -45,7 +46,9 @@
 
   Member<ScriptPromiseResolver> script_promise_resolver_;
 
-  mojo::Remote<mojom::blink::KeyboardLockService> service_;
+  HeapMojoRemote<mojom::blink::KeyboardLockService,
+                 HeapMojoWrapperMode::kWithoutContextObserver>
+      service_;
 
   DISALLOW_COPY_AND_ASSIGN(KeyboardLayout);
 };
diff --git a/third_party/blink/renderer/modules/push_messaging/push_provider.cc b/third_party/blink/renderer/modules/push_messaging/push_provider.cc
index 082166d..06d82392 100644
--- a/third_party/blink/renderer/modules/push_messaging/push_provider.cc
+++ b/third_party/blink/renderer/modules/push_messaging/push_provider.cc
@@ -13,6 +13,7 @@
 #include "third_party/blink/renderer/modules/push_messaging/push_messaging_utils.h"
 #include "third_party/blink/renderer/modules/push_messaging/push_subscription.h"
 #include "third_party/blink/renderer/modules/push_messaging/push_subscription_options.h"
+#include "third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h"
 #include "third_party/blink/renderer/platform/weborigin/kurl.h"
 #include "third_party/blink/renderer/platform/wtf/functional.h"
 
@@ -22,7 +23,8 @@
 const char PushProvider::kSupplementName[] = "PushProvider";
 
 PushProvider::PushProvider(ServiceWorkerRegistration& registration)
-    : Supplement<ServiceWorkerRegistration>(registration) {}
+    : Supplement<ServiceWorkerRegistration>(registration),
+      push_messaging_manager_(nullptr) {}
 
 // static
 PushProvider* PushProvider::From(ServiceWorkerRegistration* registration) {
@@ -41,9 +43,11 @@
 
 // static
 mojom::blink::PushMessaging* PushProvider::GetPushMessagingRemote() {
-  if (!push_messaging_manager_) {
+  if (!push_messaging_manager_.is_bound()) {
     Platform::Current()->GetBrowserInterfaceBroker()->GetInterface(
-        push_messaging_manager_.BindNewPipeAndPassReceiver());
+        push_messaging_manager_.BindNewPipeAndPassReceiver(
+            GetSupplementable()->GetExecutionContext()->GetTaskRunner(
+                TaskType::kMiscPlatformAPI)));
   }
 
   return push_messaging_manager_.get();
@@ -122,6 +126,11 @@
                 WTF::Passed(std::move(callbacks))));
 }
 
+void PushProvider::Trace(Visitor* visitor) {
+  visitor->Trace(push_messaging_manager_);
+  Supplement::Trace(visitor);
+}
+
 void PushProvider::DidGetSubscription(
     std::unique_ptr<PushSubscriptionCallbacks> callbacks,
     mojom::blink::PushGetRegistrationStatus status,
diff --git a/third_party/blink/renderer/modules/push_messaging/push_provider.h b/third_party/blink/renderer/modules/push_messaging/push_provider.h
index 4ac49e8..7f528d19 100644
--- a/third_party/blink/renderer/modules/push_messaging/push_provider.h
+++ b/third_party/blink/renderer/modules/push_messaging/push_provider.h
@@ -11,11 +11,12 @@
 #include "base/macros.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/single_thread_task_runner.h"
-#include "mojo/public/cpp/bindings/remote.h"
 #include "third_party/blink/public/mojom/push_messaging/push_messaging.mojom-blink.h"
 #include "third_party/blink/public/mojom/push_messaging/push_messaging_status.mojom-blink-forward.h"
 #include "third_party/blink/renderer/modules/push_messaging/push_subscription_callbacks.h"
 #include "third_party/blink/renderer/modules/service_worker/service_worker_registration.h"
+#include "third_party/blink/renderer/platform/mojo/heap_mojo_remote.h"
+#include "third_party/blink/renderer/platform/mojo/heap_mojo_wrapper_mode.h"
 #include "third_party/blink/renderer/platform/supplementable.h"
 #include "third_party/blink/renderer/platform/wtf/vector.h"
 
@@ -46,6 +47,8 @@
   void Unsubscribe(std::unique_ptr<PushUnsubscribeCallbacks> callbacks);
   void GetSubscription(std::unique_ptr<PushSubscriptionCallbacks> callbacks);
 
+  void Trace(Visitor*) override;
+
  private:
   // Returns an initialized PushMessaging service. A connection will be
   // established after the first call to this method.
@@ -64,7 +67,9 @@
                           mojom::blink::PushGetRegistrationStatus status,
                           mojom::blink::PushSubscriptionPtr subscription);
 
-  mojo::Remote<mojom::blink::PushMessaging> push_messaging_manager_;
+  HeapMojoRemote<mojom::blink::PushMessaging,
+                 HeapMojoWrapperMode::kWithoutContextObserver>
+      push_messaging_manager_;
 
   DISALLOW_COPY_AND_ASSIGN(PushProvider);
 };
diff --git a/third_party/blink/renderer/modules/serial/serial.cc b/third_party/blink/renderer/modules/serial/serial.cc
index 4051e1d1..00ab0eb 100644
--- a/third_party/blink/renderer/modules/serial/serial.cc
+++ b/third_party/blink/renderer/modules/serial/serial.cc
@@ -42,7 +42,9 @@
 }  // namespace
 
 Serial::Serial(ExecutionContext& execution_context)
-    : ExecutionContextLifecycleObserver(&execution_context) {}
+    : ExecutionContextLifecycleObserver(&execution_context),
+      service_(&execution_context),
+      receiver_(this, &execution_context) {}
 
 ExecutionContext* Serial::GetExecutionContext() const {
   return ExecutionContextLifecycleObserver::GetExecutionContext();
@@ -155,10 +157,6 @@
   return resolver->Promise();
 }
 
-void Serial::Dispose() {
-  receiver_.reset();
-}
-
 void Serial::GetPort(
     const base::UnguessableToken& token,
     mojo::PendingReceiver<device::mojom::blink::SerialPort> receiver) {
@@ -167,6 +165,8 @@
 }
 
 void Serial::Trace(Visitor* visitor) {
+  visitor->Trace(service_);
+  visitor->Trace(receiver_);
   visitor->Trace(get_ports_promises_);
   visitor->Trace(request_port_promises_);
   visitor->Trace(port_cache_);
@@ -196,7 +196,7 @@
 void Serial::EnsureServiceConnection() {
   DCHECK(GetExecutionContext());
 
-  if (service_)
+  if (service_.is_bound())
     return;
 
   auto task_runner =
@@ -206,7 +206,7 @@
   service_.set_disconnect_handler(
       WTF::Bind(&Serial::OnServiceConnectionError, WrapWeakPersistent(this)));
 
-  service_->SetClient(receiver_.BindNewPipeAndPassRemote());
+  service_->SetClient(receiver_.BindNewPipeAndPassRemote(task_runner));
 }
 
 void Serial::OnServiceConnectionError() {
diff --git a/third_party/blink/renderer/modules/serial/serial.h b/third_party/blink/renderer/modules/serial/serial.h
index bd3115f..52a4664 100644
--- a/third_party/blink/renderer/modules/serial/serial.h
+++ b/third_party/blink/renderer/modules/serial/serial.h
@@ -5,8 +5,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_SERIAL_SERIAL_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_SERIAL_SERIAL_H_
 
-#include "mojo/public/cpp/bindings/receiver.h"
-#include "mojo/public/cpp/bindings/remote.h"
 #include "third_party/blink/public/mojom/serial/serial.mojom-blink.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
 #include "third_party/blink/renderer/core/dom/events/event_target.h"
@@ -14,6 +12,9 @@
 #include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
 #include "third_party/blink/renderer/platform/heap/garbage_collected.h"
 #include "third_party/blink/renderer/platform/heap/handle.h"
+#include "third_party/blink/renderer/platform/mojo/heap_mojo_receiver.h"
+#include "third_party/blink/renderer/platform/mojo/heap_mojo_remote.h"
+#include "third_party/blink/renderer/platform/mojo/heap_mojo_wrapper_mode.h"
 #include "third_party/blink/renderer/platform/wtf/functional.h"
 
 namespace blink {
@@ -29,7 +30,6 @@
                      public mojom::blink::SerialServiceClient {
   DEFINE_WRAPPERTYPEINFO();
   USING_GARBAGE_COLLECTED_MIXIN(Serial);
-  USING_PRE_FINALIZER(Serial, Dispose);
 
  public:
   explicit Serial(ExecutionContext&);
@@ -53,7 +53,6 @@
                             const SerialPortRequestOptions*,
                             ExceptionState&);
 
-  void Dispose();
   void GetPort(
       const base::UnguessableToken& token,
       mojo::PendingReceiver<device::mojom::blink::SerialPort> receiver);
@@ -72,8 +71,12 @@
                   Vector<mojom::blink::SerialPortInfoPtr>);
   void OnRequestPort(ScriptPromiseResolver*, mojom::blink::SerialPortInfoPtr);
 
-  mojo::Remote<mojom::blink::SerialService> service_;
-  mojo::Receiver<mojom::blink::SerialServiceClient> receiver_{this};
+  HeapMojoRemote<mojom::blink::SerialService,
+                 HeapMojoWrapperMode::kWithoutContextObserver>
+      service_;
+  HeapMojoReceiver<mojom::blink::SerialServiceClient,
+                   HeapMojoWrapperMode::kWithoutContextObserver>
+      receiver_;
   HeapHashSet<Member<ScriptPromiseResolver>> get_ports_promises_;
   HeapHashSet<Member<ScriptPromiseResolver>> request_port_promises_;
   HeapHashMap<String, WeakMember<SerialPort>> port_cache_;
diff --git a/third_party/blink/renderer/modules/serial/serial_port.cc b/third_party/blink/renderer/modules/serial/serial_port.cc
index 574011b..fd971dbe 100644
--- a/third_party/blink/renderer/modules/serial/serial_port.cc
+++ b/third_party/blink/renderer/modules/serial/serial_port.cc
@@ -159,7 +159,10 @@
 }  // namespace
 
 SerialPort::SerialPort(Serial* parent, mojom::blink::SerialPortInfoPtr info)
-    : info_(std::move(info)), parent_(parent) {}
+    : info_(std::move(info)),
+      parent_(parent),
+      port_(GetExecutionContext()),
+      client_receiver_(this, GetExecutionContext()) {}
 
 SerialPort::~SerialPort() = default;
 
@@ -173,7 +176,7 @@
     return ScriptPromise();
   }
 
-  if (port_) {
+  if (port_.is_bound()) {
     exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
                                       "The port is already open.");
     return ScriptPromise();
@@ -262,7 +265,10 @@
   }
 
   mojo::PendingRemote<device::mojom::blink::SerialPortClient> client;
-  parent_->GetPort(info_->token, port_.BindNewPipeAndPassReceiver());
+  parent_->GetPort(
+      info_->token,
+      port_.BindNewPipeAndPassReceiver(
+          GetExecutionContext()->GetTaskRunner(TaskType::kMiscPlatformAPI)));
   port_.set_disconnect_handler(
       WTF::Bind(&SerialPort::OnConnectionError, WrapWeakPersistent(this)));
 
@@ -282,7 +288,7 @@
   if (readable_)
     return readable_;
 
-  if (!port_ || open_resolver_ || closing_ || read_fatal_)
+  if (!port_.is_bound() || open_resolver_ || closing_ || read_fatal_)
     return nullptr;
 
   mojo::ScopedDataPipeConsumerHandle readable_pipe;
@@ -303,7 +309,7 @@
   if (writable_)
     return writable_;
 
-  if (!port_ || open_resolver_ || closing_ || write_fatal_)
+  if (!port_.is_bound() || open_resolver_ || closing_ || write_fatal_)
     return nullptr;
 
   mojo::ScopedDataPipeProducerHandle writable_pipe;
@@ -321,7 +327,7 @@
 
 ScriptPromise SerialPort::getSignals(ScriptState* script_state,
                                      ExceptionState& exception_state) {
-  if (!port_) {
+  if (!port_.is_bound()) {
     exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
                                       kPortClosed);
     return ScriptPromise();
@@ -338,7 +344,7 @@
 ScriptPromise SerialPort::setSignals(ScriptState* script_state,
                                      const SerialOutputSignals* signals,
                                      ExceptionState& exception_state) {
-  if (!port_) {
+  if (!port_.is_bound()) {
     exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
                                       kPortClosed);
     return ScriptPromise();
@@ -369,7 +375,7 @@
 
 ScriptPromise SerialPort::close(ScriptState* script_state,
                                 ExceptionState& exception_state) {
-  if (!port_) {
+  if (!port_.is_bound()) {
     exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
                                       "The port is already closed.");
     return ScriptPromise();
@@ -415,7 +421,7 @@
   DCHECK(!writable_);
   DCHECK(!close_resolver_);
 
-  if (!port_)
+  if (!port_.is_bound())
     return ScriptPromise::CastUndefined(script_state);
 
   close_resolver_ = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
@@ -445,6 +451,8 @@
 
 void SerialPort::Trace(Visitor* visitor) {
   visitor->Trace(parent_);
+  visitor->Trace(port_);
+  visitor->Trace(client_receiver_);
   visitor->Trace(readable_);
   visitor->Trace(underlying_source_);
   visitor->Trace(writable_);
@@ -455,12 +463,6 @@
   ScriptWrappable::Trace(visitor);
 }
 
-void SerialPort::Dispose() {
-  // The binding holds a raw pointer to this object which must be released when
-  // it becomes garbage.
-  client_receiver_.reset();
-}
-
 ExecutionContext* SerialPort::GetExecutionContext() const {
   return parent_->GetExecutionContext();
 }
@@ -564,7 +566,9 @@
   ScriptState::Scope scope(script_state);
   InitializeReadableStream(script_state, std::move(readable_pipe));
   InitializeWritableStream(script_state, std::move(writable_pipe));
-  client_receiver_.Bind(std::move(client_receiver));
+  client_receiver_.Bind(
+      std::move(client_receiver),
+      GetExecutionContext()->GetTaskRunner(TaskType::kMiscPlatformAPI));
   open_resolver_->Resolve();
   open_resolver_ = nullptr;
 }
diff --git a/third_party/blink/renderer/modules/serial/serial_port.h b/third_party/blink/renderer/modules/serial/serial_port.h
index 06f923b..a7701f75 100644
--- a/third_party/blink/renderer/modules/serial/serial_port.h
+++ b/third_party/blink/renderer/modules/serial/serial_port.h
@@ -6,8 +6,6 @@
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_SERIAL_SERIAL_PORT_H_
 
 #include "mojo/public/cpp/bindings/pending_receiver.h"
-#include "mojo/public/cpp/bindings/receiver.h"
-#include "mojo/public/cpp/bindings/remote.h"
 #include "services/device/public/mojom/serial.mojom-blink-forward.h"
 #include "third_party/blink/public/mojom/serial/serial.mojom-blink.h"
 #include "third_party/blink/renderer/bindings/core/v8/active_script_wrappable.h"
@@ -15,6 +13,9 @@
 #include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
 #include "third_party/blink/renderer/platform/heap/handle.h"
 #include "third_party/blink/renderer/platform/heap/heap_allocator.h"
+#include "third_party/blink/renderer/platform/mojo/heap_mojo_receiver.h"
+#include "third_party/blink/renderer/platform/mojo/heap_mojo_remote.h"
+#include "third_party/blink/renderer/platform/mojo/heap_mojo_wrapper_mode.h"
 
 namespace base {
 class UnguessableToken;
@@ -37,7 +38,6 @@
                          public device::mojom::blink::SerialPortClient {
   DEFINE_WRAPPERTYPEINFO();
   USING_GARBAGE_COLLECTED_MIXIN(SerialPort);
-  USING_PRE_FINALIZER(SerialPort, Dispose);
 
  public:
   explicit SerialPort(Serial* parent, mojom::blink::SerialPortInfoPtr info);
@@ -64,7 +64,6 @@
 
   void ContextDestroyed();
   void Trace(Visitor*) override;
-  void Dispose();
 
   // ActiveScriptWrappable
   ExecutionContext* GetExecutionContext() const;
@@ -95,8 +94,10 @@
   const Member<Serial> parent_;
 
   uint32_t buffer_size_ = 0;
-  mojo::Remote<device::mojom::blink::SerialPort> port_;
-  mojo::Receiver<device::mojom::blink::SerialPortClient> client_receiver_{this};
+  HeapMojoRemote<device::mojom::blink::SerialPort> port_;
+  HeapMojoReceiver<device::mojom::blink::SerialPortClient,
+                   HeapMojoWrapperMode::kWithoutContextObserver>
+      client_receiver_;
 
   Member<ReadableStream> readable_;
   Member<SerialPortUnderlyingSource> underlying_source_;
diff --git a/third_party/blink/renderer/modules/wake_lock/wake_lock.cc b/third_party/blink/renderer/modules/wake_lock/wake_lock.cc
index e7dfc4a..db9cdb7 100644
--- a/third_party/blink/renderer/modules/wake_lock/wake_lock.cc
+++ b/third_party/blink/renderer/modules/wake_lock/wake_lock.cc
@@ -29,6 +29,7 @@
 WakeLock::WakeLock(Document& document)
     : ExecutionContextLifecycleObserver(&document),
       PageVisibilityObserver(document.GetPage()),
+      permission_service_(document.ToExecutionContext()),
       managers_{
           MakeGarbageCollected<WakeLockManager>(document.ToExecutionContext(),
                                                 WakeLockType::kScreen),
@@ -42,6 +43,7 @@
 WakeLock::WakeLock(DedicatedWorkerGlobalScope& worker_scope)
     : ExecutionContextLifecycleObserver(&worker_scope),
       PageVisibilityObserver(nullptr),
+      permission_service_(&worker_scope),
       managers_{MakeGarbageCollected<WakeLockManager>(&worker_scope,
                                                       WakeLockType::kScreen),
                 MakeGarbageCollected<WakeLockManager>(&worker_scope,
@@ -271,10 +273,11 @@
 }
 
 PermissionService* WakeLock::GetPermissionService() {
-  if (!permission_service_) {
+  if (!permission_service_.is_bound()) {
     ConnectToPermissionService(
         GetExecutionContext(),
-        permission_service_.BindNewPipeAndPassReceiver());
+        permission_service_.BindNewPipeAndPassReceiver(
+            GetExecutionContext()->GetTaskRunner(TaskType::kMiscPlatformAPI)));
   }
   return permission_service_.get();
 }
@@ -282,6 +285,7 @@
 void WakeLock::Trace(Visitor* visitor) {
   for (const WakeLockManager* manager : managers_)
     visitor->Trace(manager);
+  visitor->Trace(permission_service_);
   PageVisibilityObserver::Trace(visitor);
   ExecutionContextLifecycleObserver::Trace(visitor);
   ScriptWrappable::Trace(visitor);
diff --git a/third_party/blink/renderer/modules/wake_lock/wake_lock.h b/third_party/blink/renderer/modules/wake_lock/wake_lock.h
index a2379f07..27b1daf 100644
--- a/third_party/blink/renderer/modules/wake_lock/wake_lock.h
+++ b/third_party/blink/renderer/modules/wake_lock/wake_lock.h
@@ -17,6 +17,8 @@
 #include "third_party/blink/renderer/modules/wake_lock/wake_lock_type.h"
 #include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
 #include "third_party/blink/renderer/platform/heap/handle.h"
+#include "third_party/blink/renderer/platform/mojo/heap_mojo_remote.h"
+#include "third_party/blink/renderer/platform/mojo/heap_mojo_wrapper_mode.h"
 
 namespace WTF {
 
@@ -68,7 +70,9 @@
       base::OnceCallback<void(mojom::blink::PermissionStatus)> callback);
   mojom::blink::PermissionService* GetPermissionService();
 
-  mojo::Remote<mojom::blink::PermissionService> permission_service_;
+  HeapMojoRemote<mojom::blink::PermissionService,
+                 HeapMojoWrapperMode::kWithoutContextObserver>
+      permission_service_;
 
   // https://w3c.github.io/wake-lock/#concepts-and-state-record
   // Each platform wake lock (one per wake lock type) has an associated state
diff --git a/third_party/blink/renderer/modules/webmidi/midi_access_initializer.cc b/third_party/blink/renderer/modules/webmidi/midi_access_initializer.cc
index 39120ec..d0ceda2 100644
--- a/third_party/blink/renderer/modules/webmidi/midi_access_initializer.cc
+++ b/third_party/blink/renderer/modules/webmidi/midi_access_initializer.cc
@@ -30,16 +30,16 @@
 
 MIDIAccessInitializer::MIDIAccessInitializer(ScriptState* script_state,
                                              const MIDIOptions* options)
-    : ScriptPromiseResolver(script_state), options_(options) {}
+    : ScriptPromiseResolver(script_state),
+      options_(options),
+      permission_service_(GetExecutionContext()) {}
 
 void MIDIAccessInitializer::Dispose() {
   dispatcher_.reset();
-  permission_service_.reset();
 }
 
 void MIDIAccessInitializer::ContextDestroyed() {
   dispatcher_.reset();
-  permission_service_.reset();
 
   ScriptPromiseResolver::ContextDestroyed();
 }
@@ -126,6 +126,7 @@
 
 void MIDIAccessInitializer::Trace(Visitor* visitor) {
   visitor->Trace(options_);
+  visitor->Trace(permission_service_);
   ScriptPromiseResolver::Trace(visitor);
 }
 
diff --git a/third_party/blink/renderer/modules/webmidi/midi_access_initializer.h b/third_party/blink/renderer/modules/webmidi/midi_access_initializer.h
index 8ae5e42..e5489990 100644
--- a/third_party/blink/renderer/modules/webmidi/midi_access_initializer.h
+++ b/third_party/blink/renderer/modules/webmidi/midi_access_initializer.h
@@ -7,7 +7,6 @@
 
 #include <memory>
 #include "media/midi/midi_service.mojom-blink-forward.h"
-#include "mojo/public/cpp/bindings/remote.h"
 #include "third_party/blink/public/mojom/permissions/permission.mojom-blink.h"
 #include "third_party/blink/public/mojom/permissions/permission_status.mojom-blink-forward.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
@@ -16,6 +15,7 @@
 #include "third_party/blink/renderer/modules/modules_export.h"
 #include "third_party/blink/renderer/modules/webmidi/midi_dispatcher.h"
 #include "third_party/blink/renderer/modules/webmidi/midi_port.h"
+#include "third_party/blink/renderer/platform/mojo/heap_mojo_remote.h"
 #include "third_party/blink/renderer/platform/wtf/vector.h"
 
 namespace blink {
@@ -101,7 +101,7 @@
   Vector<PortDescriptor> port_descriptors_;
   Member<const MIDIOptions> options_;
 
-  mojo::Remote<mojom::blink::PermissionService> permission_service_;
+  HeapMojoRemote<mojom::blink::PermissionService> permission_service_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index 898e130..ba5f4c63 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -508,6 +508,10 @@
       status: "test",
     },
     {
+      name: "CSSRevert",
+      depends_on: ["CSSCascade"],
+    },
+    {
       name: "CSSSnapSize",
       status: "experimental",
     },
diff --git a/third_party/blink/renderer/platform/wtf/linked_hash_set_test.cc b/third_party/blink/renderer/platform/wtf/linked_hash_set_test.cc
index bcfb0be1..784fccc 100644
--- a/third_party/blink/renderer/platform/wtf/linked_hash_set_test.cc
+++ b/third_party/blink/renderer/platform/wtf/linked_hash_set_test.cc
@@ -11,17 +11,26 @@
 
 namespace WTF {
 
+template <typename T>
+int* const ValueInstanceCount<T>::kDeletedValue =
+    reinterpret_cast<int*>(static_cast<uintptr_t>(-1));
+
 TEST(NewLinkedHashSetTest, CopyConstructAndAssignInt) {
-  NewLinkedHashSet<int> set1;
+  using Set = NewLinkedHashSet<ValueInstanceCount<int>>;
+  // Declare the counters before the set, because they have to outlive teh set.
+  int counter1 = 0;
+  int counter2 = 0;
+  int counter3 = 0;
+  Set set1;
   EXPECT_EQ(set1.size(), 0u);
   EXPECT_TRUE(set1.IsEmpty());
-  set1.insert(1);
-  set1.insert(2);
-  set1.insert(3);
+  set1.insert(ValueInstanceCount<int>(&counter1, 1));
+  set1.insert(ValueInstanceCount<int>(&counter2, 2));
+  set1.insert(ValueInstanceCount<int>(&counter3, 3));
   EXPECT_EQ(set1.size(), 3u);
-  NewLinkedHashSet<int> set2(set1);
+  Set set2(set1);
   EXPECT_EQ(set2.size(), 3u);
-  NewLinkedHashSet<int> set3;
+  Set set3;
   EXPECT_EQ(set3.size(), 0u);
   set3 = set2;
   EXPECT_EQ(set3.size(), 3u);
@@ -29,17 +38,24 @@
   auto it2 = set2.begin();
   auto it3 = set3.begin();
   for (int i = 0; i < 3; i++) {
-    EXPECT_EQ(*it1, i + 1);
-    EXPECT_EQ(*it2, i + 1);
-    EXPECT_EQ(*it3, i + 1);
+    EXPECT_EQ(it1->Value(), i + 1);
+    EXPECT_EQ(it2->Value(), i + 1);
+    EXPECT_EQ(it3->Value(), i + 1);
     ++it1;
     ++it2;
     ++it3;
   }
+
+  // Each object is now in all 3 sets.
+  // Count 2x because each set uses hash map and vector.
+  EXPECT_EQ(counter1, 6);
+  EXPECT_EQ(counter2, 6);
+  EXPECT_EQ(counter3, 6);
 }
 
 TEST(NewLinkedHashSetTest, CopyConstructAndAssignIntPtr) {
-  NewLinkedHashSet<int*> set1;
+  using Set = NewLinkedHashSet<int*>;
+  Set set1;
   EXPECT_EQ(set1.size(), 0u);
   EXPECT_TRUE(set1.IsEmpty());
   std::unique_ptr<int> int1 = std::make_unique<int>(1);
@@ -49,9 +65,9 @@
   set1.insert(int2.get());
   set1.insert(int3.get());
   EXPECT_EQ(set1.size(), 3u);
-  NewLinkedHashSet<int*> set2(set1);
+  Set set2(set1);
   EXPECT_EQ(set2.size(), 3u);
-  NewLinkedHashSet<int*> set3;
+  Set set3;
   EXPECT_EQ(set3.size(), 0u);
   set3 = set2;
   EXPECT_EQ(set3.size(), 3u);
@@ -67,6 +83,7 @@
     ++it3;
   }
 
+  // Changing the pointed values in one set should change it in all sets.
   for (int* ptr : set1)
     *ptr += 1000;
   it1 = set1.begin();
@@ -83,16 +100,17 @@
 }
 
 TEST(NewLinkedHashSetTest, CopyConstructAndAssignString) {
-  NewLinkedHashSet<String> set1;
+  using Set = NewLinkedHashSet<String>;
+  Set set1;
   EXPECT_EQ(set1.size(), 0u);
   EXPECT_TRUE(set1.IsEmpty());
   set1.insert("1");
   set1.insert("2");
   set1.insert("3");
   EXPECT_EQ(set1.size(), 3u);
-  NewLinkedHashSet<String> set2(set1);
+  Set set2(set1);
   EXPECT_EQ(set2.size(), 3u);
-  NewLinkedHashSet<String> set3;
+  Set set3;
   EXPECT_EQ(set3.size(), 0u);
   set3 = set2;
   EXPECT_EQ(set3.size(), 3u);
@@ -107,22 +125,40 @@
     ++it2;
     ++it3;
   }
+
+  // Changing one set should not affect the others.
+  set1.clear();
+  set1.insert("11");
+  set1.insert("12");
+  set1.insert("13");
+  it1 = set1.begin();
+  it2 = set2.begin();
+  it3 = set3.begin();
+  for (int i = 0; i < 3; i++) {
+    EXPECT_EQ(*it1, String(Vector<UChar>({'1', '1' + i})));
+    EXPECT_EQ(*it2, String(Vector<UChar>({'1' + i})));
+    EXPECT_EQ(*it3, String(Vector<UChar>({'1' + i})));
+    ++it1;
+    ++it2;
+    ++it3;
+  }
 }
 
 TEST(NewLinkedHashSetTest, MoveConstructAndAssignInt) {
-  NewLinkedHashSet<ValueInstanceCount<int>> set1;
-  EXPECT_EQ(set1.size(), 0u);
-  EXPECT_TRUE(set1.IsEmpty());
+  using Set = NewLinkedHashSet<ValueInstanceCount<int>>;
   int counter1 = 0;
   int counter2 = 0;
   int counter3 = 0;
+  Set set1;
+  EXPECT_EQ(set1.size(), 0u);
+  EXPECT_TRUE(set1.IsEmpty());
   set1.insert(ValueInstanceCount<int>(&counter1, 1));
   set1.insert(ValueInstanceCount<int>(&counter2, 2));
   set1.insert(ValueInstanceCount<int>(&counter3, 3));
   EXPECT_EQ(set1.size(), 3u);
-  NewLinkedHashSet<ValueInstanceCount<int>> set2(std::move(set1));
+  Set set2(std::move(set1));
   EXPECT_EQ(set2.size(), 3u);
-  NewLinkedHashSet<ValueInstanceCount<int>> set3;
+  Set set3;
   EXPECT_EQ(set3.size(), 0u);
   set3 = std::move(set2);
   EXPECT_EQ(set3.size(), 3u);
@@ -138,7 +174,7 @@
   EXPECT_EQ(counter2, 2);
   EXPECT_EQ(counter3, 2);
 
-  NewLinkedHashSet<ValueInstanceCount<int>> set4(set3);
+  Set set4(set3);
   // Copy constructor was used, each object is in set3 and set4.
   EXPECT_EQ(counter1, 4);
   EXPECT_EQ(counter2, 4);
@@ -146,19 +182,20 @@
 }
 
 TEST(NewLinkedHashSetTest, MoveConstructAndAssignString) {
-  NewLinkedHashSet<ValueInstanceCount<String>> set1;
-  EXPECT_EQ(set1.size(), 0u);
-  EXPECT_TRUE(set1.IsEmpty());
+  using Set = NewLinkedHashSet<ValueInstanceCount<String>>;
   int counter1 = 0;
   int counter2 = 0;
   int counter3 = 0;
+  Set set1;
+  EXPECT_EQ(set1.size(), 0u);
+  EXPECT_TRUE(set1.IsEmpty());
   set1.insert(ValueInstanceCount<String>(&counter1, "1"));
   set1.insert(ValueInstanceCount<String>(&counter2, "2"));
   set1.insert(ValueInstanceCount<String>(&counter3, "3"));
   EXPECT_EQ(set1.size(), 3u);
-  NewLinkedHashSet<ValueInstanceCount<String>> set2(std::move(set1));
+  Set set2(std::move(set1));
   EXPECT_EQ(set2.size(), 3u);
-  NewLinkedHashSet<ValueInstanceCount<String>> set3;
+  Set set3;
   EXPECT_EQ(set3.size(), 0u);
   set3 = std::move(set2);
   EXPECT_EQ(set3.size(), 3u);
@@ -174,7 +211,7 @@
   EXPECT_EQ(counter2, 2);
   EXPECT_EQ(counter3, 2);
 
-  NewLinkedHashSet<ValueInstanceCount<String>> set4(set3);
+  Set set4(set3);
   // Copy constructor was used, each object is in set3 and set4.
   EXPECT_EQ(counter1, 4);
   EXPECT_EQ(counter2, 4);
@@ -372,7 +409,9 @@
 
   Set::const_iterator it = set.begin();
   ++it;
+  EXPECT_TRUE(set.Contains(2));
   set.erase(it);
+  EXPECT_FALSE(set.Contains(2));
   it = set.begin();
   EXPECT_EQ(*it, 1);
   ++it;
@@ -382,7 +421,9 @@
   ++it;
   EXPECT_EQ(*it, 5);
 
+  EXPECT_TRUE(set.Contains(3));
   set.erase(3);
+  EXPECT_FALSE(set.Contains(3));
   it = set.begin();
   EXPECT_EQ(*it, 1);
   ++it;
diff --git a/third_party/blink/renderer/platform/wtf/vector_backed_linked_list.h b/third_party/blink/renderer/platform/wtf/vector_backed_linked_list.h
index 60383b8a..ef44f8f6 100644
--- a/third_party/blink/renderer/platform/wtf/vector_backed_linked_list.h
+++ b/third_party/blink/renderer/platform/wtf/vector_backed_linked_list.h
@@ -79,16 +79,6 @@
       VectorTraits<ValueType>::kCanCopyWithMemcpy;
   static const bool kCanMoveWithMemcpy =
       VectorTraits<ValueType>::kCanMoveWithMemcpy;
-
-  // Needs to be shadowing because |VectorTraitsBase::IsDeletedValue| uses call
-  // by value, which means we need to define copy constructor of
-  // |VectorBackedLinkedList|. We can remove this function if we change
-  // |VectorTraitsBase::IsDeletedValue| to use call by reference.
-  static bool IsDeletedValue(
-      const VectorBackedLinkedListNode<ValueType, Allocator>& node) {
-    NOTREACHED();
-    return false;
-  }
 };
 
 // VectorBackedLinkedList maintains a linked list through its contents such that
diff --git a/third_party/blink/renderer/platform/wtf/vector_traits.h b/third_party/blink/renderer/platform/wtf/vector_traits.h
index 0efa4ccf..25c0c7b7 100644
--- a/third_party/blink/renderer/platform/wtf/vector_traits.h
+++ b/third_party/blink/renderer/platform/wtf/vector_traits.h
@@ -66,7 +66,7 @@
 
   // Vectors do not support deleting values.
   static constexpr bool kCanHaveDeletedValue = false;
-  static bool IsDeletedValue(T value) { return false; }
+  static bool IsDeletedValue(const T& value) { return false; }
 };
 
 template <typename T>
diff --git a/third_party/blink/renderer/platform/wtf/wtf_test_helper.h b/third_party/blink/renderer/platform/wtf/wtf_test_helper.h
index 35b1e73..3e09b94 100644
--- a/third_party/blink/renderer/platform/wtf/wtf_test_helper.h
+++ b/third_party/blink/renderer/platform/wtf/wtf_test_helper.h
@@ -182,26 +182,30 @@
 template <typename T>
 class ValueInstanceCount final {
  public:
+  static int* const kDeletedValue;
+
   ValueInstanceCount() : counter_(nullptr), value_(T()) {}
   explicit ValueInstanceCount(int* counter, T value = T())
       : counter_(counter), value_(value) {
-    DCHECK(counter_);
-    *counter = 1;
+    if (counter_ && counter_ != kDeletedValue)
+      ++*counter_;
   }
   ValueInstanceCount(const ValueInstanceCount& other)
       : counter_(other.counter_), value_(other.value_) {
-    if (counter_)
+    if (counter_ && counter_ != kDeletedValue)
       ++*counter_;
   }
   ValueInstanceCount& operator=(const ValueInstanceCount& other) {
+    if (counter_ && counter_ != kDeletedValue)
+      --*counter_;
     counter_ = other.counter_;
     value_ = other.value_;
-    if (counter_)
+    if (counter_ && counter_ != kDeletedValue)
       ++*counter_;
     return *this;
   }
   ~ValueInstanceCount() {
-    if (counter_)
+    if (counter_ && counter_ != kDeletedValue)
       --*counter_;
   }
 
@@ -221,9 +225,11 @@
   static bool IsEmptyValue(const ValueInstanceCount<T>& value) {
     return !value.Counter();
   }
-  static void ConstructDeletedValue(ValueInstanceCount<T>& slot, bool) {}
+  static void ConstructDeletedValue(ValueInstanceCount<T>& slot, bool) {
+    slot = ValueInstanceCount<T>(ValueInstanceCount<T>::kDeletedValue);
+  }
   static bool IsDeletedValue(const ValueInstanceCount<T>& value) {
-    return false;
+    return value.Counter() == ValueInstanceCount<T>::kDeletedValue;
   }
 };
 
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 6ff43aa..a6932c6 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -177,6 +177,8 @@
 virtual/composite-after-paint/scrollingcoordinator/* [ Pass ]
 # --- End CompositeAfterPaint Tests --
 
+crbug.com/1067174 wpt_internal/display-lock/css-subtree-visibility/subtree-visibility-051.html [ Failure ]
+
 # Subpixel differences
 crbug.com/771643 external/wpt/css/css-backgrounds/background-attachment-local/attachment-local-clipping-color-5.html [ Failure ]
 crbug.com/771643 external/wpt/css/css-backgrounds/background-attachment-local/attachment-local-clipping-image-5.html [ Failure ]
@@ -311,10 +313,6 @@
 crbug.com/918155 virtual/prefer_compositing_to_lcd_text/scrollbars/overlay-scrollbar-over-child-layer-nested-2.html [ Failure ]
 crbug.com/918155 virtual/prefer_compositing_to_lcd_text/scrollbars/overlay-scrollbar-over-child-layer-nested.html [ Failure ]
 
-crbug.com/981719 external/wpt/css/css-ui/appearance-textfield-001.html [ Failure ]
-crbug.com/981719 [ Linux ] external/wpt/css/css-ui/webkit-appearance-textfield-001.html [ Failure ]
-crbug.com/981719 [ Win ] external/wpt/css/css-ui/webkit-appearance-textfield-001.html [ Failure ]
-
 # WebGPU tests are only run on GPU bots, so they are skipped by default and run
 # separately from other Web Tests.
 external/wpt/webgpu/* [ Skip ]
@@ -1038,6 +1036,7 @@
 crbug.com/591099 virtual/layout_ng_block_frag/external/wpt/css/css-multicol/multicol-nested-margin-004.xht [ Failure ]
 crbug.com/591099 virtual/layout_ng_block_frag/external/wpt/css/css-multicol/multicol-nested-margin-005.xht [ Failure ]
 crbug.com/591099 virtual/layout_ng_block_frag/external/wpt/css/css-multicol/multicol-overflow-000.xht [ Failure ]
+crbug.com/591099 virtual/layout_ng_block_frag/external/wpt/css/css-multicol/multicol-overflow-clip.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_block_frag/external/wpt/css/css-multicol/multicol-overflowing-001.xht [ Failure ]
 crbug.com/591099 virtual/layout_ng_block_frag/external/wpt/css/css-multicol/multicol-reduce-000.xht [ Failure ]
 crbug.com/591099 virtual/layout_ng_block_frag/external/wpt/css/css-multicol/multicol-rule-000.xht [ Failure ]
@@ -1631,6 +1630,7 @@
 crbug.com/6606 external/wpt/mathml/presentation-markup/operators/mo-form-dynamic.html [ Failure ]
 crbug.com/6606 external/wpt/mathml/presentation-markup/operators/mo-form-minus-plus.html [ Failure ]
 crbug.com/6606 external/wpt/mathml/presentation-markup/operators/mo-form.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/presentation-markup/operators/mo-movablelimits.html [ Failure ]
 crbug.com/6606 external/wpt/mathml/presentation-markup/operators/mo-paint-lspace-rspace.html [ Failure ]
 crbug.com/6606 external/wpt/mathml/presentation-markup/operators/operator-dictionary-001.html [ Failure ]
 crbug.com/6606 external/wpt/mathml/presentation-markup/operators/operator-dictionary-002.html [ Failure ]
@@ -1641,8 +1641,6 @@
 crbug.com/6606 external/wpt/mathml/presentation-markup/scripts/subsup-5.html [ Failure ]
 crbug.com/6606 external/wpt/mathml/presentation-markup/scripts/subsup-parameters-1.html [ Failure ]
 crbug.com/6606 external/wpt/mathml/presentation-markup/scripts/subsup-parameters-2.html [ Failure ]
-crbug.com/6606 external/wpt/mathml/presentation-markup/scripts/underover-1.html [ Failure ]
-crbug.com/6606 external/wpt/mathml/presentation-markup/scripts/underover-parameters-1.html [ Failure ]
 crbug.com/6606 external/wpt/mathml/presentation-markup/scripts/underover-parameters-2.html [ Failure ]
 crbug.com/6606 external/wpt/mathml/presentation-markup/scripts/underover-parameters-3.html [ Failure ]
 crbug.com/6606 external/wpt/mathml/presentation-markup/scripts/underover-parameters-4.html [ Failure ]
@@ -1681,9 +1679,6 @@
 crbug.com/6606 external/wpt/mathml/relations/css-styling/mathvariant-script.html [ Failure ]
 crbug.com/6606 external/wpt/mathml/relations/css-styling/mathvariant-stretched.html [ Failure ]
 crbug.com/6606 external/wpt/mathml/relations/css-styling/mathvariant-tailed.html [ Failure ]
-crbug.com/6606 external/wpt/mathml/relations/css-styling/padding-border-margin/border-002.html [ Failure ]
-crbug.com/6606 external/wpt/mathml/relations/css-styling/padding-border-margin/margin-002.html [ Failure ]
-crbug.com/6606 external/wpt/mathml/relations/css-styling/padding-border-margin/padding-002.html [ Failure ]
 crbug.com/6606 external/wpt/mathml/relations/css-styling/visibility-003.html [ Failure ]
 crbug.com/6606 external/wpt/mathml/relations/css-styling/width-height-001.html [ Failure ]
 crbug.com/6606 external/wpt/mathml/relations/html5-tree/href-click-1.html [ Failure ]
@@ -3122,9 +3117,6 @@
 # FontFace object failures detected by WPT test
 crbug.com/965409 external/wpt/css/css-font-loading/fontface-descriptor-updates.html [ Failure ]
 
-# Various menulist appearance failures in WPT
-crbug.com/968164 external/wpt/css/css-ui/appearance-menulist-button-002.html [ Failure ]
-
 # Implement text-decoration-thickness and text-decoration-offset
 crbug.com/785230 external/wpt/css/css-text-decor/text-underline-offset-002.html [ Failure ]
 crbug.com/785230 external/wpt/css/css-text-decor/text-decoration-thickness-001.html [ Failure ]
@@ -3166,6 +3158,7 @@
 crbug.com/947951 [ Win ] external/wpt/pointerevents/pointerevent_touch-action-inherit_child-auto-child-none_touch.html [ Pass Timeout ]
 
 # ====== New tests from wpt-importer added here ======
+crbug.com/626703 [ Mac10.10 ] external/wpt/fetch/origin/assorted.window.html [ Failure Timeout ]
 crbug.com/626703 [ Linux ] external/wpt/css/css-content/element-replacement-on-replaced-element.tentative.html [ Failure ]
 crbug.com/626703 [ Mac ] external/wpt/css/css-content/element-replacement-on-replaced-element.tentative.html [ Failure ]
 crbug.com/626703 [ Win ] external/wpt/css/css-content/element-replacement-on-replaced-element.tentative.html [ Failure ]
@@ -3432,7 +3425,6 @@
 crbug.com/626703 external/wpt/webrtc/RTCRtpTransceiver.https.html [ Crash Timeout ]
 crbug.com/626703 external/wpt/css/css-writing-modes/text-combine-upright-all-001-manual.html [ Skip ]
 crbug.com/626703 external/wpt/css/css-writing-modes/text-combine-upright-digits-002-manual.html [ Skip ]
-crbug.com/626703 external/wpt/css/css-ui/webkit-appearance-button-bevel-001.html [ Failure ]
 crbug.com/626703 external/wpt/css/css-writing-modes/text-combine-upright-all-002-manual.html [ Skip ]
 crbug.com/626703 virtual/streaming-preload/external/wpt/html/semantics/scripting-1/the-script-element/json-module/parse-error.tentative.html [ Timeout ]
 crbug.com/967018 virtual/streaming-preload/external/wpt/html/semantics/scripting-1/the-script-element/css-module/* [ Failure ]
diff --git a/third_party/blink/web_tests/animations/svg-attribute-interpolation/svg-calc-interpolation.html b/third_party/blink/web_tests/animations/svg-attribute-interpolation/svg-calc-interpolation.html
index 2db968d..448aa01 100644
--- a/third_party/blink/web_tests/animations/svg-attribute-interpolation/svg-calc-interpolation.html
+++ b/third_party/blink/web_tests/animations/svg-attribute-interpolation/svg-calc-interpolation.html
@@ -40,13 +40,13 @@
   from: '0%',
   to: '100'
 }, [
-  {at: -0.25, is: 'calc(0% + -25)'},
+  {at: -0.25, is: 'calc(0% + -25px)'},
   {at: 0, is: '0%'},
-  {at: 0.25, is: 'calc(0% + 25)'},
-  {at: 0.5, is: 'calc(0% + 50)'},
-  {at: 0.75, is: 'calc(0% + 75)'},
+  {at: 0.25, is: 'calc(0% + 25px)'},
+  {at: 0.5, is: 'calc(0% + 50px)'},
+  {at: 0.75, is: 'calc(0% + 75px)'},
   {at: 1, is: '100px'},
-  {at: 1.25, is: 'calc(0% + 125)'}
+  {at: 1.25, is: 'calc(0% + 125px)'}
 ]);
 assertAttributeInterpolation({
   property: 'cy',
diff --git a/third_party/blink/web_tests/css-parser/appearance-with-nonstandard-keywords-expected.txt b/third_party/blink/web_tests/css-parser/appearance-with-nonstandard-keywords-expected.txt
new file mode 100644
index 0000000..9a569ede
--- /dev/null
+++ b/third_party/blink/web_tests/css-parser/appearance-with-nonstandard-keywords-expected.txt
@@ -0,0 +1,13 @@
+CONSOLE WARNING: line 32: The keyword 'inner-spin-button' specified to an 'appearance' property is not standardized. It will be removed in the future.
+CONSOLE WARNING: line 32: The keyword 'media-slider' specified to an 'appearance' property is not standardized. It will be removed in the future.
+CONSOLE WARNING: line 32: The keyword 'media-sliderthumb' specified to an 'appearance' property is not standardized. It will be removed in the future.
+CONSOLE WARNING: line 32: The keyword 'media-volume-slider' specified to an 'appearance' property is not standardized. It will be removed in the future.
+CONSOLE WARNING: line 32: The keyword 'media-volume-sliderthumb' specified to an 'appearance' property is not standardized. It will be removed in the future.
+CONSOLE WARNING: line 32: The keyword 'slider-vertical' specified to an 'appearance' property is not standardized. It will be removed in the future.
+CONSOLE WARNING: line 32: The keyword 'sliderthumb-horizontal' specified to an 'appearance' property is not standardized. It will be removed in the future.
+CONSOLE WARNING: line 32: The keyword 'sliderthumb-vertical' specified to an 'appearance' property is not standardized. It will be removed in the future.
+CONSOLE WARNING: line 32: The keyword 'searchfield-cancel-button' specified to an 'appearance' property is not standardized. It will be removed in the future.
+This is a testharness.js-based test.
+FAIL There should be consle warnings about non-standard appearance keywords assert_true: This should FAIL to have -expected.txt expected true got false
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/css-parser/appearance-with-nonstandard-keywords.html b/third_party/blink/web_tests/css-parser/appearance-with-nonstandard-keywords.html
new file mode 100644
index 0000000..4a74b34
--- /dev/null
+++ b/third_party/blink/web_tests/css-parser/appearance-with-nonstandard-keywords.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<script src="../resources/testharness.js"></script>
+<script src="../resources/testharnessreport.js"></script>
+<style>
+/* See https://drafts.csswg.org/css-ui-4/#appearance-switching */
+.t {
+  appearance: auto;
+  appearance: checkbox;
+  appearance: radio;
+  appearance: push-button;
+  appearance: square-button;
+  appearance: button;
+  appearance: inner-spin-button; /* should be warned */
+  appearance: listbox;
+  appearance: media-slider; /* should be warned */
+  appearance: media-sliderthumb; /* should be warned */
+  appearance: media-volume-slider; /* should be warned */
+  appearance: media-volume-sliderthumb; /* should be warned */
+  appearance: menulist;
+  appearance: menulist-button;
+  appearance: meter;
+  appearance: progress-bar;
+  appearance: slider-horizontal;
+  appearance: slider-vertical; /* should be warned */
+  appearance: sliderthumb-horizontal; /* should be warned */
+  appearance: sliderthumb-vertical; /* should be warned */
+  appearance: searchfield;
+  appearance: searchfield-cancel-button; /* should be warned */
+  appearance: textfield;
+  appearance: textarea;
+}
+</style>
+<script>
+test(() => {
+  assert_true(false, 'This should FAIL to have -expected.txt');
+}, 'There should be consle warnings about non-standard appearance keywords');
+</script>
diff --git a/third_party/blink/web_tests/css3/flexbox/stretch-input-in-column.html b/third_party/blink/web_tests/css3/flexbox/stretch-input-in-column.html
deleted file mode 100644
index ffa0632..0000000
--- a/third_party/blink/web_tests/css3/flexbox/stretch-input-in-column.html
+++ /dev/null
@@ -1,30 +0,0 @@
-<!DOCTYPE html>
-<html>
-<link rel="stylesheet" href="resources/flexbox.css">
-<style>
-.flexbox {
-    background-color: grey;
-}
-</style>
-<body>
-<p>Form controls that are stretched in a column flexbox should not overflow the flexbox.</p>
-<div class="flexbox column">
-    <input>
-</div>
-<div class="flexbox column align-content-flex-start">
-    <textarea class="align-self-stretch"></textarea>
-</div>
-<div class="flexbox column wrap">
-    <input type="button">
-</div>
-<div class="flexbox column">
-    <select></select>
-</div>
-<div class="flexbox column">
-    <legend style="border: 2px solid black">legend</legend>
-</div>
-<div class="flexbox column wrap">
-    <div type="border: 4px solid black; padding: 10px;">
-</div>
-</body>
-</html>
diff --git a/third_party/blink/web_tests/css3/flexbox/undefined-min-width.html b/third_party/blink/web_tests/css3/flexbox/undefined-min-width.html
deleted file mode 100644
index ea3a5ee8..0000000
--- a/third_party/blink/web_tests/css3/flexbox/undefined-min-width.html
+++ /dev/null
@@ -1,15 +0,0 @@
-<!DOCTYPE html>
-<html>
-<link href="resources/flexbox.css" rel="stylesheet">
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/check-layout-th.js"></script>
-<body onload="checkLayout('.flexbox')">
-<div id=log></div>
-
-Test to make sure that we do not assert on this code.
-
-<div class="flexbox column" style="max-height: 0; overflow: hidden; line-height: 13px;" data-expected-height="0">
-    <div style="min-height: 100%;" data-expected-height="0">This is a flex item.</div>
-    <div style="flex: none;" data-expected-height="13">Inflexible</div>
-</div>
diff --git a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_7.json b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_7.json
index 5261d35f..76204545 100644
--- a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_7.json
+++ b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_7.json
@@ -49773,6 +49773,18 @@
      {}
     ]
    ],
+   "css/css-flexbox/stretch-input-in-column.html": [
+    [
+     "css/css-flexbox/stretch-input-in-column.html",
+     [
+      [
+       "/css/css-flexbox/reference/stretch-input-in-column-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
    "css/css-flexbox/stretch-obeys-min-max-001.html": [
     [
      "css/css-flexbox/stretch-obeys-min-max-001.html",
@@ -57123,6 +57135,62 @@
      }
     ]
    ],
+   "css/css-images/image-orientation/svg-image-orientation-aspect-ratio.html": [
+    [
+     "css/css-images/image-orientation/svg-image-orientation-aspect-ratio.html",
+     [
+      [
+       "/css/css-images/image-orientation/reference/svg-image-orientation-aspect-ratio-ref.html",
+       "=="
+      ]
+     ],
+     {
+      "fuzzy": [
+       [
+        null,
+        [
+         [
+          10,
+          10
+         ],
+         [
+          100,
+          100
+         ]
+        ]
+       ]
+      ]
+     }
+    ]
+   ],
+   "css/css-images/image-orientation/svg-image-orientation.html": [
+    [
+     "css/css-images/image-orientation/svg-image-orientation.html",
+     [
+      [
+       "/css/css-images/image-orientation/reference/svg-image-orientation-ref.html",
+       "=="
+      ]
+     ],
+     {
+      "fuzzy": [
+       [
+        null,
+        [
+         [
+          10,
+          10
+         ],
+         [
+          100,
+          100
+         ]
+        ]
+       ]
+      ]
+     }
+    ]
+   ],
    "css/css-images/image-set/image-set-rendering.html": [
     [
      "css/css-images/image-set/image-set-rendering.html",
@@ -63183,6 +63251,18 @@
      {}
     ]
    ],
+   "css/css-multicol/multicol-overflow-clip.html": [
+    [
+     "css/css-multicol/multicol-overflow-clip.html",
+     [
+      [
+       "/css/css-multicol/multicol-overflow-clip-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
    "css/css-multicol/multicol-overflowing-001.xht": [
     [
      "css/css-multicol/multicol-overflowing-001.xht",
@@ -94255,18 +94335,6 @@
      {}
     ]
    ],
-   "css/css-ui/appearance-button-bevel-001.html": [
-    [
-     "css/css-ui/appearance-button-bevel-001.html",
-     [
-      [
-       "/css/css-ui/appearance-auto-ref.html",
-       "=="
-      ]
-     ],
-     {}
-    ]
-   ],
    "css/css-ui/appearance-checkbox-001.html": [
     [
      "css/css-ui/appearance-checkbox-001.html",
@@ -95431,18 +95499,6 @@
      {}
     ]
    ],
-   "css/css-ui/webkit-appearance-button-bevel-001.html": [
-    [
-     "css/css-ui/webkit-appearance-button-bevel-001.html",
-     [
-      [
-       "/css/css-ui/appearance-auto-ref.html",
-       "=="
-      ]
-     ],
-     {}
-    ]
-   ],
    "css/css-ui/webkit-appearance-checkbox-001.html": [
     [
      "css/css-ui/webkit-appearance-checkbox-001.html",
@@ -142791,6 +142847,9 @@
    "css/css-flexbox/reference/position-fixed-001-ref.html": [
     []
    ],
+   "css/css-flexbox/reference/stretch-input-in-column-ref.html": [
+    []
+   ],
    "css/css-flexbox/reference/stretching-orthogonal-flows-ref.html": [
     []
    ],
@@ -148863,6 +148922,12 @@
    "css/css-images/image-orientation/reference/image-orientation-none-ref.html": [
     []
    ],
+   "css/css-images/image-orientation/reference/svg-image-orientation-aspect-ratio-ref.html": [
+    []
+   ],
+   "css/css-images/image-orientation/reference/svg-image-orientation-ref.html": [
+    []
+   ],
    "css/css-images/image-orientation/support/exif-orientation-1-ul-pre-rotated.jpg": [
     []
    ],
@@ -149937,6 +150002,9 @@
    "css/css-multicol/multicol-overflow-clip-positioned-ref.html": [
     []
    ],
+   "css/css-multicol/multicol-overflow-clip-ref.html": [
+    []
+   ],
    "css/css-multicol/multicol-overflowing-001-ref.xht": [
     []
    ],
@@ -155919,18 +155987,6 @@
    "css/css-ui/appearance-cssom-001-expected.txt": [
     []
    ],
-   "css/css-ui/appearance-initial-value-001-expected.txt": [
-    []
-   ],
-   "css/css-ui/appearance-parsing-expected.txt": [
-    []
-   ],
-   "css/css-ui/appearance-property-expected.txt": [
-    []
-   ],
-   "css/css-ui/appearance-serialization-expected.txt": [
-    []
-   ],
    "css/css-ui/appearance-textfield-001-ref.html": [
     []
    ],
@@ -156783,15 +156839,6 @@
    "css/css-ui/tools/appearance-build-webkit-reftests.py": [
     []
    ],
-   "css/css-ui/webkit-appearance-parsing-expected.txt": [
-    []
-   ],
-   "css/css-ui/webkit-appearance-property-expected.txt": [
-    []
-   ],
-   "css/css-ui/webkit-appearance-serialization-expected.txt": [
-    []
-   ],
    "css/css-values/META.yml": [
     []
    ],
@@ -166941,9 +166988,6 @@
    "html/cross-origin-opener-policy/coep-navigate-popup.https.html.headers": [
     []
    ],
-   "html/cross-origin-opener-policy/coep-redirect.https-expected.txt": [
-    []
-   ],
    "html/cross-origin-opener-policy/coep-redirect.https.html.headers": [
     []
    ],
@@ -174093,9 +174137,6 @@
    "infrastructure/testdriver/file_upload_data.txt": [
     []
    ],
-   "infrastructure/testdriver/send_keys-expected.txt": [
-    []
-   ],
    "infrastructure/webdriver/tests/conftest.py": [
     []
    ],
@@ -176385,6 +176426,12 @@
    "offscreen-canvas/text/2d.text.draw.stroke.basic.png": [
     []
    ],
+   "offscreen-canvas/text/2d.text.measure.width.space-expected.txt": [
+    []
+   ],
+   "offscreen-canvas/text/2d.text.measure.width.space.worker-expected.txt": [
+    []
+   ],
    "offscreen-canvas/the-offscreen-canvas/2d.getcontext.extraargs-expected.txt": [
     []
    ],
@@ -187194,12 +187241,6 @@
    "webauthn/OWNERS": [
     []
    ],
-   "webauthn/createcredential-passing.https-expected.txt": [
-    []
-   ],
-   "webauthn/getcredential-extensions.https-expected.txt": [
-    []
-   ],
    "webauthn/helpers.js": [
     []
    ],
@@ -187776,6 +187817,12 @@
    "webrtc-identity/idlharness.https.window-expected.txt": [
     []
    ],
+   "webrtc-insertable-streams/resources/blank.html": [
+    []
+   ],
+   "webrtc-insertable-streams/resources/serviceworker-failure.js": [
+    []
+   ],
    "webrtc-quic/META.yml": [
     []
    ],
@@ -187995,6 +188042,12 @@
    "webrtc/resources/RTCCertificate-postMessage-iframe.html": [
     []
    ],
+   "webrtc/simulcast/.eslintignore": [
+    []
+   ],
+   "webrtc/simulcast/simulcast.js": [
+    []
+   ],
    "webrtc/third_party/README.md": [
     []
    ],
@@ -219363,6 +219416,12 @@
      {}
     ]
    ],
+   "css/css-flexbox/flex-minimum-size-002.html": [
+    [
+     "css/css-flexbox/flex-minimum-size-002.html",
+     {}
+    ]
+   ],
    "css/css-flexbox/flex-minimum-width-flex-items-014.html": [
     [
      "css/css-flexbox/flex-minimum-width-flex-items-014.html",
@@ -219387,6 +219446,12 @@
      {}
     ]
    ],
+   "css/css-flexbox/flexbox-lines-must-be-stretched-by-default.html": [
+    [
+     "css/css-flexbox/flexbox-lines-must-be-stretched-by-default.html",
+     {}
+    ]
+   ],
    "css/css-flexbox/flexbox_first-letter.html": [
     [
      "css/css-flexbox/flexbox_first-letter.html",
@@ -220155,6 +220220,18 @@
      {}
     ]
    ],
+   "css/css-flexbox/percentage-heights-012.html": [
+    [
+     "css/css-flexbox/percentage-heights-012.html",
+     {}
+    ]
+   ],
+   "css/css-flexbox/percentage-margins-001.html": [
+    [
+     "css/css-flexbox/percentage-margins-001.html",
+     {}
+    ]
+   ],
    "css/css-flexbox/percentage-padding-001.html": [
     [
      "css/css-flexbox/percentage-padding-001.html",
@@ -220191,6 +220268,18 @@
      {}
     ]
    ],
+   "css/css-flexbox/radiobutton-min-size.html": [
+    [
+     "css/css-flexbox/radiobutton-min-size.html",
+     {}
+    ]
+   ],
+   "css/css-flexbox/relayout-align-items.html": [
+    [
+     "css/css-flexbox/relayout-align-items.html",
+     {}
+    ]
+   ],
    "css/css-flexbox/shrinking-column-flexbox.html": [
     [
      "css/css-flexbox/shrinking-column-flexbox.html",
@@ -324024,6 +324113,18 @@
      }
     ]
    ],
+   "webrtc-insertable-streams/RTCEncodedAudioFrame-serviceworker-failure.https.html": [
+    [
+     "webrtc-insertable-streams/RTCEncodedAudioFrame-serviceworker-failure.https.html",
+     {}
+    ]
+   ],
+   "webrtc-insertable-streams/RTCEncodedVideoFrame-serviceworker-failure.https.html": [
+    [
+     "webrtc-insertable-streams/RTCEncodedVideoFrame-serviceworker-failure.https.html",
+     {}
+    ]
+   ],
    "webrtc-quic/RTCQuicStream.https.html": [
     [
      "webrtc-quic/RTCQuicStream.https.html",
@@ -324717,8 +324818,13 @@
        [
         "script",
         "./RTCPeerConnection-helper.js"
+       ],
+       [
+        "timeout",
+        "long"
        ]
-      ]
+      ],
+      "timeout": "long"
      }
     ]
    ],
@@ -324856,6 +324962,12 @@
      {}
     ]
    ],
+   "webrtc/simulcast/basic.https.html": [
+    [
+     "webrtc/simulcast/basic.https.html",
+     {}
+    ]
+   ],
    "websockets/Close-1000-reason.any.js": [
     [
      "websockets/Close-1000-reason.any.html",
@@ -382640,7 +382752,7 @@
    "testharness"
   ],
   "css/css-flexbox/change-column-flex-width.html": [
-   "bea59bc2444758c778bad914d3c18c4f2eb1cf7c",
+   "8dc370ac9d3e0da7837d10250a868c177c78af38",
    "testharness"
   ],
   "css/css-flexbox/column-flex-child-with-overflow-scroll.html": [
@@ -383239,6 +383351,10 @@
    "c2eea80ca5b79818f6b7a9a36ab1ff64826b5218",
    "testharness"
   ],
+  "css/css-flexbox/flex-minimum-size-002.html": [
+   "41a8cd705d0c3bbd970f923b0b1dbfdbcff2de57",
+   "testharness"
+  ],
   "css/css-flexbox/flex-minimum-width-flex-items-001.xht": [
    "cd18483ba414160c46e30bc282dec0c2fcd2f418",
    "reftest"
@@ -383427,6 +383543,10 @@
    "850f1a42d4e48b1cb8e5a10bee24dfa3966ad7df",
    "reftest"
   ],
+  "css/css-flexbox/flexbox-lines-must-be-stretched-by-default.html": [
+   "f705a3882b456244e6b916898ab1342a3cce5207",
+   "testharness"
+  ],
   "css/css-flexbox/flexbox-order-from-lowest.html": [
    "34bc0276019d03c0dcf4121f412b3eb4a50260d1",
    "visual"
@@ -384844,7 +384964,7 @@
    "reftest"
   ],
   "css/css-flexbox/flexbox_width-change-and-relayout-children.html": [
-   "a5f086d181454d67c5123cc0f9ff0525d21fa72c",
+   "06342679b62777213483442128b6429290f091d1",
    "testharness"
   ],
   "css/css-flexbox/flexbox_width-overflow.html": [
@@ -385887,6 +386007,14 @@
    "fda9c9b4f306adbfc8cd0eeb06f700ad2a5e60a9",
    "testharness"
   ],
+  "css/css-flexbox/percentage-heights-012.html": [
+   "63f63aa8ec91b441ebae0754153260cc7b8d6d37",
+   "testharness"
+  ],
+  "css/css-flexbox/percentage-margins-001.html": [
+   "b98bfa1af875b6961b4819fa0e7f82a24fcf783d",
+   "testharness"
+  ],
   "css/css-flexbox/percentage-max-height-001.html": [
    "8e6dd227a038c8f237c769dad8e92976fe9ca832",
    "reftest"
@@ -385939,6 +386067,10 @@
    "966f39f173952f01268dcd26a9a5892bf4a689a9",
    "testharness"
   ],
+  "css/css-flexbox/radiobutton-min-size.html": [
+   "5703861bf7333b765c3edce6627db5f64e741626",
+   "testharness"
+  ],
   "css/css-flexbox/reference/Flexible-order-ref.html": [
    "12e94ba598e74cd253f0f91daf7e88f44884b7fc",
    "support"
@@ -386123,6 +386255,10 @@
    "4943f537f747e6bf10c4e6c91ad8ae1fb58b72c6",
    "support"
   ],
+  "css/css-flexbox/reference/stretch-input-in-column-ref.html": [
+   "85d2a49e3ec6576dd3d0599c13e56ec453000606",
+   "support"
+  ],
   "css/css-flexbox/reference/stretching-orthogonal-flows-ref.html": [
    "3a3efaad5dd9abc98f1da0c37ddb33f6aba45594",
    "support"
@@ -386171,6 +386307,10 @@
    "369de990ff2190ea0e4592d4dfc354a8a8fcae16",
    "support"
   ],
+  "css/css-flexbox/relayout-align-items.html": [
+   "0569d5143981259400bac7f5998e56338e617ef1",
+   "testharness"
+  ],
   "css/css-flexbox/scrollbars-auto-ref.html": [
    "590b533d8d25ac45dbeb1e7eab7cd02f3c1e8b5b",
    "support"
@@ -386211,6 +386351,10 @@
    "918ac3818553b3e9a276da5b98114719b1a400c1",
    "testharness"
   ],
+  "css/css-flexbox/stretch-input-in-column.html": [
+   "4136c45d17a6bf946a4f788d95458c04ac6c6a6d",
+   "reftest"
+  ],
   "css/css-flexbox/stretch-obeys-min-max-001.html": [
    "4b7eb5a1f76a39fdad6956953270ab9bdf8a14c1",
    "reftest"
@@ -398771,6 +398915,14 @@
    "c4d140f1533b0aa31a7bcee6884f9b5f0fd206f5",
    "support"
   ],
+  "css/css-images/image-orientation/reference/svg-image-orientation-aspect-ratio-ref.html": [
+   "728bfb202f22da7d155536d8d0e2fb88869cc384",
+   "support"
+  ],
+  "css/css-images/image-orientation/reference/svg-image-orientation-ref.html": [
+   "345d6698fcf376051a0ffe33cd6c5496b4b362c8",
+   "support"
+  ],
   "css/css-images/image-orientation/support/exif-orientation-1-ul-pre-rotated.jpg": [
    "33abbd152a775cfed8802fd3084167328533ac87",
    "support"
@@ -398843,6 +398995,14 @@
    "7d41aead39e38d1377ec9b1732ca53b0fbb1906c",
    "support"
   ],
+  "css/css-images/image-orientation/svg-image-orientation-aspect-ratio.html": [
+   "ee053442d0d65ad48e0ee03cd7f08a4da5b8555e",
+   "reftest"
+  ],
+  "css/css-images/image-orientation/svg-image-orientation.html": [
+   "3b8255f2d41407e634ac127adb5c6d2809231b7c",
+   "reftest"
+  ],
   "css/css-images/image-set/image-set-parsing-expected.txt": [
    "47b6b85f06cb0c1ff02c97788e0afce7c13407d7",
    "support"
@@ -403003,6 +403163,14 @@
    "184bfc7f18dadbade32192b777c6b21f345882af",
    "reftest"
   ],
+  "css/css-multicol/multicol-overflow-clip-ref.html": [
+   "c061a1d6114572c307d058f545e4828e4910250a",
+   "support"
+  ],
+  "css/css-multicol/multicol-overflow-clip.html": [
+   "af59ff92d4f740ee71c6e3e1378704dd6a1ebe86",
+   "reftest"
+  ],
   "css/css-multicol/multicol-overflowing-001-ref.xht": [
    "d3a6c6e03c58c17f0dfa4f2e6d9b945e352216af",
    "support"
@@ -411860,15 +412028,15 @@
    "testharness"
   ],
   "css/css-text-decor/parsing/text-underline-position-computed.html": [
-   "9019a58fa4830ae5541b3882bf42f385504d0bd4",
+   "a1bf54b068bc848a9e253500f692eeff692f4e61",
    "testharness"
   ],
   "css/css-text-decor/parsing/text-underline-position-invalid.html": [
-   "5feea8ed531de3de31b0692603ae48c065056819",
+   "d3d0fe8838b15c25e341cb4622c34e6cf85ffb84",
    "testharness"
   ],
   "css/css-text-decor/parsing/text-underline-position-valid.html": [
-   "fa05448f09e40f34de18ac6896d81269c1103dfa",
+   "ee238f075e5474604e89dcc55cf40cf4361a822e",
    "testharness"
   ],
   "css/css-text-decor/reference/line-through-vertical-ref.html": [
@@ -425216,7 +425384,7 @@
    "testharness"
   ],
   "css/css-transitions/KeyframeEffect-setKeyframes.tentative-expected.txt": [
-   "bef7b146dd95e4ee968c1fb7e54ef9afd847cb59",
+   "f45b2f68dac9f201a01d779515d4bf6ec66f07c4",
    "support"
   ],
   "css/css-transitions/KeyframeEffect-setKeyframes.tentative.html": [
@@ -427695,26 +427863,18 @@
    "5c5cdb42e7078b18ed1e4f58f34747ce76d50c02",
    "support"
   ],
-  "css/css-ui/appearance-button-bevel-001.html": [
-   "03748528154acc19f6d9dc8fb00ee8423a2c60bf",
-   "reftest"
-  ],
   "css/css-ui/appearance-checkbox-001.html": [
    "54ebef94f24cd207f1e7a3f40f45cc825698df1c",
    "reftest"
   ],
   "css/css-ui/appearance-cssom-001-expected.txt": [
-   "30c70bbdb39d95c2ade23318f0903cf6005a970a",
+   "3c5f1f08b844f2915f1acad7cf75a879d7f605a4",
    "support"
   ],
   "css/css-ui/appearance-cssom-001.html": [
    "f7dd4d1d761ed57831ae7842280f47164c6c080e",
    "testharness"
   ],
-  "css/css-ui/appearance-initial-value-001-expected.txt": [
-   "7b676a1cf2630657c56d5a40d3582828a03ff0d5",
-   "support"
-  ],
   "css/css-ui/appearance-initial-value-001.html": [
    "6e7d3002676d7c9a7bde572e903e5a148c5f34d5",
    "testharness"
@@ -427739,10 +427899,6 @@
    "da6f1ffa6cacd3ac0e77b343ffaa33ab44f4c2c2",
    "reftest"
   ],
-  "css/css-ui/appearance-parsing-expected.txt": [
-   "c748b8aeb1f562e25ea64ef11239f4b670fc0759",
-   "support"
-  ],
   "css/css-ui/appearance-parsing.html": [
    "8d6973231c0cb33fa0bd1ff83f54f98487d844e8",
    "testharness"
@@ -427751,10 +427907,6 @@
    "1573f69911566fc4410f6d49959f83dad140ce3e",
    "reftest"
   ],
-  "css/css-ui/appearance-property-expected.txt": [
-   "90dcd4612b5f0193432bd81d19e7e89c790a9305",
-   "support"
-  ],
   "css/css-ui/appearance-property.html": [
    "53b98ba6055f81b7dd8510cd5be5f85b1ff4a01f",
    "testharness"
@@ -427771,10 +427923,6 @@
    "78741411551c667c3f3f317eeb475db883d9d7d4",
    "reftest"
   ],
-  "css/css-ui/appearance-serialization-expected.txt": [
-   "edcbb8b50929b54000a972029bf7c292cf4b5f9d",
-   "support"
-  ],
   "css/css-ui/appearance-serialization.html": [
    "ed968afee609b4beab0a67d7bc9fc11c686ecb33",
    "testharness"
@@ -428420,11 +428568,11 @@
    "manual"
   ],
   "css/css-ui/inheritance-expected.txt": [
-   "eb57674e25b8db519d05bb2b4b229db8e50740bf",
+   "548a76a3407ac8a48f9f108559957cf061f20a19",
    "support"
   ],
   "css/css-ui/inheritance.html": [
-   "6315316d8224b7c2f3d4f575e2e48d9527a22223",
+   "c2aab07a23b9502353c7c3bbe798963442c651fe",
    "testharness"
   ],
   "css/css-ui/nav-dir-001.html": [
@@ -428948,7 +429096,7 @@
    "testharness"
   ],
   "css/css-ui/parsing/outline-offset-computed.html": [
-   "feb7732df35583849fb5fce78e02c8afce0de376",
+   "3ba35217ce1905b2e99f1a3055b169e46d4a9af3",
    "testharness"
   ],
   "css/css-ui/parsing/outline-offset-invalid.html": [
@@ -430391,10 +430539,6 @@
    "fb0261b020f6fc2b1e3bfccb3da7d899f0337f79",
    "reftest"
   ],
-  "css/css-ui/webkit-appearance-button-bevel-001.html": [
-   "3c860f23ae12ca204a2c76d7e181cb12c4de63c5",
-   "reftest"
-  ],
   "css/css-ui/webkit-appearance-checkbox-001.html": [
    "09dd3d76cd66d59522b02d1b6a0c0126bb5eda2a",
    "reftest"
@@ -430419,10 +430563,6 @@
    "05fb5ce25f24df1bf8fd2c72323a9deea9d0e112",
    "reftest"
   ],
-  "css/css-ui/webkit-appearance-parsing-expected.txt": [
-   "c748b8aeb1f562e25ea64ef11239f4b670fc0759",
-   "support"
-  ],
   "css/css-ui/webkit-appearance-parsing.html": [
    "0f08eab222493a123fd5a44afce3689385a42390",
    "testharness"
@@ -430431,10 +430571,6 @@
    "b989502902d2b8716f7fcc0d8f18e124ec8cb94e",
    "reftest"
   ],
-  "css/css-ui/webkit-appearance-property-expected.txt": [
-   "d0e76b0a11629e39dc97e7e3ac72e1f14132b827",
-   "support"
-  ],
   "css/css-ui/webkit-appearance-property.html": [
    "fb3f7df2d4885c801711f91f7d1e1f7ba09a3951",
    "testharness"
@@ -430451,10 +430587,6 @@
    "8abd91e3c8faa9f5e02a8af6d2f4e66e1a0c6c29",
    "reftest"
   ],
-  "css/css-ui/webkit-appearance-serialization-expected.txt": [
-   "3ff4fe3e611739b95b9d38a2acffbd3559d044f2",
-   "support"
-  ],
   "css/css-ui/webkit-appearance-serialization.html": [
    "b325fd5d6c647063c880046804afbac23c617d7f",
    "testharness"
@@ -458388,11 +458520,11 @@
    "testharness"
   ],
   "fetch/origin/assorted.window-expected.txt": [
-   "b5fa6b585fb29ed98beba6edbaf498c20d32544f",
+   "6b30c4e43e6a70892310392dada07e458e588443",
    "support"
   ],
   "fetch/origin/assorted.window.js": [
-   "cc37dbd8ed990c60a0e9347f0f25336f2c9f70d3",
+   "fc6dd1a02870e29f43531821a8958b59ed6cfc1d",
    "testharness"
   ],
   "fetch/origin/resources/redirect-and-stash.py": [
@@ -461592,7 +461724,7 @@
    "testharness"
   ],
   "html/browsers/history/the-location-interface/location-protocol-setter-non-broken-expected.txt": [
-   "4d1f8a9abd4d46f445ceb66ec1f65fe65425aabd",
+   "73507dc26a7a6935592a606ac49ce62bbaf4ff8c",
    "support"
   ],
   "html/browsers/history/the-location-interface/location-protocol-setter-non-broken-weird-expected.txt": [
@@ -463395,10 +463527,6 @@
    "63b60e490f47f4db77d33d7a4ca2f5b9a4181de8",
    "support"
   ],
-  "html/cross-origin-opener-policy/coep-redirect.https-expected.txt": [
-   "78adc45f03aa0ba3bdc35cee22fe107ae8ce73d5",
-   "support"
-  ],
   "html/cross-origin-opener-policy/coep-redirect.https.html": [
    "83f8f8a33d4b2c9c7f23c02011bcc568836e62ea",
    "testharness"
@@ -470888,7 +471016,7 @@
    "testharness"
   ],
   "html/rendering/widgets/appearance/default-styles-expected.txt": [
-   "c5d868fd31d5b8bdcccad46343ee8083dfc7fd5d",
+   "7062e8d5a4ec4581085be7f9157382b64b046235",
    "support"
   ],
   "html/rendering/widgets/appearance/default-styles.html": [
@@ -483311,10 +483439,6 @@
    "168c9e9956f9efae57d64948a34d7a24aa5bb44f",
    "testharness"
   ],
-  "infrastructure/testdriver/send_keys-expected.txt": [
-   "fbd705a2a1403a1c6e14a3fdfb6f7cd6a7af0193",
-   "support"
-  ],
   "infrastructure/testdriver/send_keys.html": [
    "2170347c9729564f7e492009b3d20b3267422c1d",
    "testharness"
@@ -495124,67 +495248,75 @@
    "testharness"
   ],
   "offscreen-canvas/text/2d.text.measure.actualBoundingBox.html": [
-   "9b2e5eaeed34454fc77cd35a137db69a957620c6",
+   "8c3606be19d563c816e590e7346f8df611442863",
    "testharness"
   ],
   "offscreen-canvas/text/2d.text.measure.actualBoundingBox.worker.js": [
-   "de9370c7d5fa225cf88f085c383a316da61ab9b8",
+   "4b4d859dc30ea5111335bdae491a9e6c205b34ac",
    "testharness"
   ],
   "offscreen-canvas/text/2d.text.measure.advances.html": [
-   "aa26e529d8bc8186f113885ac6bb7a651af140a5",
+   "2c35113c47f934b7f1648d4b0e402130e450d2b7",
    "testharness"
   ],
   "offscreen-canvas/text/2d.text.measure.advances.worker.js": [
-   "85fcd38089569215da97b616ed2271669865de17",
+   "bc111e2c3219d556c24d5a16dc665811b7257a01",
    "testharness"
   ],
   "offscreen-canvas/text/2d.text.measure.baselines.html": [
-   "a09e9508a7c8e4338715fbd7f2b51f5542f90f36",
+   "2d27995407f34f00cefc2e53423fd5733c3dc242",
    "testharness"
   ],
   "offscreen-canvas/text/2d.text.measure.baselines.worker.js": [
-   "8dbaa3b01c93e517cf5c958486e8d056aa987889",
+   "902e9d69549de8278c1219ec0551a99864b947e8",
    "testharness"
   ],
   "offscreen-canvas/text/2d.text.measure.emHeights.html": [
-   "cadbce72bae4b8a972c374e6145a49cc0a8616b8",
+   "aadf53d7aee4e7a92d21783a4a4792e285911106",
    "testharness"
   ],
   "offscreen-canvas/text/2d.text.measure.emHeights.worker.js": [
-   "db217e190af4c66acf9d4e17eed1300e08f2bd9a",
+   "86a79b5fd2709dbfcddce63c5851401a2a776e00",
    "testharness"
   ],
   "offscreen-canvas/text/2d.text.measure.fontBoundingBox.html": [
-   "b1075b015a10a8037f532fc1afd2a1b8bbd0684e",
+   "c5d50acfe52129f52ba60b29c15ac0753f14d6a2",
    "testharness"
   ],
   "offscreen-canvas/text/2d.text.measure.fontBoundingBox.worker.js": [
-   "de97ef641f1d8e959a53f44baf4dea66cb064bca",
+   "e3d8d88892f878902a8e365978b2e8ff82981c45",
    "testharness"
   ],
   "offscreen-canvas/text/2d.text.measure.width.basic.html": [
-   "c7edb163d311e1dd4d245b2f37023c24374690a3",
+   "678a059e37288cf8f3d027b1c1156456d2fa8dcc",
    "testharness"
   ],
   "offscreen-canvas/text/2d.text.measure.width.basic.worker.js": [
-   "11b36c082b1ec7a1e68f7d4e2262a2aeb5e302c6",
+   "3e1454fc87e746552863a26eaaf5761a24134458",
    "testharness"
   ],
   "offscreen-canvas/text/2d.text.measure.width.empty.html": [
-   "b1044623b2441842c151f44a5171f3e53e27d773",
+   "304d6b2248e8aedc0b30c6f15bbbec96cc4c9559",
    "testharness"
   ],
   "offscreen-canvas/text/2d.text.measure.width.empty.worker.js": [
-   "124353c5afc1d5e273a23e3ddd13d67929ef5b4d",
+   "7dd1677b96497bfeacb2c81d74d628af6c0f2ea0",
    "testharness"
   ],
+  "offscreen-canvas/text/2d.text.measure.width.space-expected.txt": [
+   "fa0b0b36605c4be0235f178a9fe354aa7068acd4",
+   "support"
+  ],
   "offscreen-canvas/text/2d.text.measure.width.space.html": [
-   "b3cb2fc4420d69692effc54b19e788391e9e28ef",
+   "b452fc98b6b567dd7ee4a286d3bbce99cddc076e",
    "testharness"
   ],
+  "offscreen-canvas/text/2d.text.measure.width.space.worker-expected.txt": [
+   "fa0b0b36605c4be0235f178a9fe354aa7068acd4",
+   "support"
+  ],
   "offscreen-canvas/text/2d.text.measure.width.space.worker.js": [
-   "a1232dd4f79d29380b0cf93a0cc08bffde00e352",
+   "3d78110f7e636cdeb02dc75d3f35d1410fd4c0c2",
    "testharness"
   ],
   "offscreen-canvas/the-canvas-state/2d.state.saverestore.bitmap.html": [
@@ -495796,7 +495928,7 @@
    "support"
   ],
   "offscreen-canvas/tools/tests2d.yaml": [
-   "1d2e1210ed180903ab40a6bbf205ca076d65835d",
+   "846dc8b3eedc80171b4ea103b936eaa863b81d6c",
    "support"
   ],
   "offscreen-canvas/transformations/2d.transformation.getTransform.html": [
@@ -506000,7 +506132,7 @@
    "reftest"
   ],
   "scroll-animations/constructor-expected.txt": [
-   "dd1ea00be1f1616790a4ac6aed26d6817246a942",
+   "f52fddfbf134a724704b4ddc63dd27289bb6ebd2",
    "support"
   ],
   "scroll-animations/constructor-no-document.html": [
@@ -506008,7 +506140,7 @@
    "testharness"
   ],
   "scroll-animations/constructor.html": [
-   "5d13035f6a1f527c2467f5768908e6b27eda6995",
+   "7cb7013c00685aa07f2a5a4acee22365a5d08293",
    "testharness"
   ],
   "scroll-animations/current-time-nan.html": [
@@ -507856,11 +507988,11 @@
    "testharness"
   ],
   "service-workers/service-worker/fetch-event-handled.https-expected.txt": [
-   "42484619e33308d010376fec1eda99c0bdea03c3",
+   "9b071f9be6e590d23cd8d645aa12789985137c74",
    "support"
   ],
   "service-workers/service-worker/fetch-event-handled.https.html": [
-   "2d6f6c86d876daf40ea8f41be01bca2c549bcc41",
+   "89f3d79a681a22cfd6889b3c0603f7388d460472",
    "testharness"
   ],
   "service-workers/service-worker/fetch-event-is-history-backward-navigation-manual.https.html": [
@@ -508796,7 +508928,7 @@
    "support"
   ],
   "service-workers/service-worker/resources/fetch-event-handled-worker.js": [
-   "4af58e20d05c902d74511579e6b74894731d1239",
+   "0dc6de005dba08cda134aa657e56191362cf28a8",
    "support"
   ],
   "service-workers/service-worker/resources/fetch-event-network-error-controllee-iframe.html": [
@@ -523672,7 +523804,7 @@
    "support"
   ],
   "web-animations/animation-model/animation-types/accumulation-per-property-expected.txt": [
-   "9b5d78ab275ad7a9946a2462afa077a40de644bb",
+   "0ad89a6b96968712b74d6590f7ab1b1c434073d8",
    "support"
   ],
   "web-animations/animation-model/animation-types/accumulation-per-property.html": [
@@ -523680,7 +523812,7 @@
    "testharness"
   ],
   "web-animations/animation-model/animation-types/addition-per-property-expected.txt": [
-   "665b9fb2227ee2b685c9e9a0fab39f8702a807aa",
+   "270f0db112d0b70def52c58034a834e35933e317",
    "support"
   ],
   "web-animations/animation-model/animation-types/addition-per-property.html": [
@@ -523692,7 +523824,7 @@
    "testharness"
   ],
   "web-animations/animation-model/animation-types/interpolation-per-property-expected.txt": [
-   "b17015724a58e39da178dbc17300a7d8ed29ad0d",
+   "265d5bc2d09edeaa2b1fa96f960cd0647e05d403",
    "support"
   ],
   "web-animations/animation-model/animation-types/interpolation-per-property.html": [
@@ -525615,12 +525747,8 @@
    "46cab3051b4924a2cb9cf1d7a2df46046b7b2f23",
    "testharness"
   ],
-  "webauthn/createcredential-passing.https-expected.txt": [
-   "39540b29c138f85bbe878dd7382664eff254734b",
-   "support"
-  ],
   "webauthn/createcredential-passing.https.html": [
-   "a94be363da20214ac84950b2bb701d428372dd10",
+   "30fef13bbdc9efb5659460652054d119399ce819",
    "testharness"
   ],
   "webauthn/createcredential-pubkeycredparams.https.html": [
@@ -525639,12 +525767,8 @@
    "7f8571586e4094e663399b79f473676cdfb0a660",
    "testharness"
   ],
-  "webauthn/getcredential-extensions.https-expected.txt": [
-   "402ef6fc2d0de20ff07ad071dab8377e304eb657",
-   "support"
-  ],
   "webauthn/getcredential-extensions.https.html": [
-   "820f9b529f16e5644d3d288f406db6e71a72cf34",
+   "af03086ee048567c32338636560ff2d76351892b",
    "testharness"
   ],
   "webauthn/getcredential-passing.https.html": [
@@ -527519,6 +527643,22 @@
    "8c7bf665ef69487f396519e9e6b45a010854bb3a",
    "testharness"
   ],
+  "webrtc-insertable-streams/RTCEncodedAudioFrame-serviceworker-failure.https.html": [
+   "fda89f36dcb52668e28d8c017112f6efb1f169a8",
+   "testharness"
+  ],
+  "webrtc-insertable-streams/RTCEncodedVideoFrame-serviceworker-failure.https.html": [
+   "93b9a6a0f36098e3d20762adc9e1d30b61fc7b15",
+   "testharness"
+  ],
+  "webrtc-insertable-streams/resources/blank.html": [
+   "a3c3a4689a62b45b1e429f6b7a94690e556a1259",
+   "support"
+  ],
+  "webrtc-insertable-streams/resources/serviceworker-failure.js": [
+   "e7aa8e11be396cc32eda592ae9618391ad6fee40",
+   "support"
+  ],
   "webrtc-quic/META.yml": [
    "740c8d2fe84beafbcb7f98cef29aff2378721132",
    "support"
@@ -528220,7 +528360,7 @@
    "support"
   ],
   "webrtc/idlharness.https.window.js": [
-   "93341278ac628f7ecc514283f01ad6d9ad90bc28",
+   "ae18cc6353831a8c18928f1e68e6a80ac400bb0d",
    "testharness"
   ],
   "webrtc/legacy/README.txt": [
@@ -528323,6 +528463,18 @@
    "291437a526582d13db6e04fa39c152ce9ed5859a",
    "testharness"
   ],
+  "webrtc/simulcast/.eslintignore": [
+   "90fee69f044fc5adfcf49bac0fdb6dcd638556e1",
+   "support"
+  ],
+  "webrtc/simulcast/basic.https.html": [
+   "ae2e7eec11b85bb0f5b0444e6e6cf7123e7e26e0",
+   "testharness"
+  ],
+  "webrtc/simulcast/simulcast.js": [
+   "ed08ddca44866367bd66ea3b5a7c3a6f58a85f03",
+   "support"
+  ],
   "webrtc/third_party/README.md": [
    "56a2295dd103db76836d17fa513f56d09891c586",
    "support"
diff --git a/third_party/blink/web_tests/external/wpt/css/css-flexbox/flex-minimum-size-002.html b/third_party/blink/web_tests/external/wpt/css/css-flexbox/flex-minimum-size-002.html
new file mode 100644
index 0000000..41a8cd7
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-flexbox/flex-minimum-size-002.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<title>CSS Flexbox: min-size when the child has a percentage min-size</title>
+<link rel="help" href="https://drafts.csswg.org/css-flexbox/#algo-main-item">
+<meta name="assert" content="This test ensures that min-size can not be negative when the child has a percentage min-size.">
+<link href="support/flexbox.css" rel="stylesheet">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/check-layout-th.js"></script>
+<body onload="checkLayout('.flexbox')">
+<div id=log></div>
+
+<div class="flexbox column" style="max-height: 0; overflow: hidden; line-height: 13px;" data-expected-height="0">
+    <div style="min-height: 100%;" data-expected-height="0">This is a flex item.</div>
+    <div style="flex: none;" data-expected-height="13">Inflexible</div>
+</div>
diff --git a/third_party/blink/web_tests/css3/flexbox/stretch-input-in-column-expected.html b/third_party/blink/web_tests/external/wpt/css/css-flexbox/reference/stretch-input-in-column-ref.html
similarity index 77%
rename from third_party/blink/web_tests/css3/flexbox/stretch-input-in-column-expected.html
rename to third_party/blink/web_tests/external/wpt/css/css-flexbox/reference/stretch-input-in-column-ref.html
index bdf8519..85d2a49e 100644
--- a/third_party/blink/web_tests/css3/flexbox/stretch-input-in-column-expected.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-flexbox/reference/stretch-input-in-column-ref.html
@@ -1,6 +1,6 @@
 <!DOCTYPE html>
 <html>
-<link rel="stylesheet" href="resources/flexbox.css">
+<link href="../support/flexbox.css" rel="stylesheet">
 <style>
 .flexbox {
     background-color: grey;
@@ -10,7 +10,7 @@
 }
 </style>
 <body>
-<p>Form controls that are stretched in a column flexbox should not overflow the flexbox.</p>
+<p>This test passes if none of the form controls overflow.</p>
 <div class="flexbox">
     <input>
 </div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-flexbox/stretch-input-in-column.html b/third_party/blink/web_tests/external/wpt/css/css-flexbox/stretch-input-in-column.html
new file mode 100644
index 0000000..4136c45
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-flexbox/stretch-input-in-column.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<html>
+<title>CSS Flexbox: Stretch input form controls in flexbox column</title>
+<link rel="help" href="https://drafts.csswg.org/css-flexbox/#flex-property">
+<link rel="help" href="https://drafts.csswg.org/css-flexbox/#align-items-property">
+<link rel="match" href="reference/stretch-input-in-column-ref.html">
+<link href="support/flexbox.css" rel="stylesheet">
+<meta name="assert" content="This test ensures that input form controls that are stretched in
+a column flexbox should not overflow the flexbox.">
+<style>
+.flexbox {
+    background-color: grey;
+}
+</style>
+<body>
+<p>This test passes if none of the form controls overflow.</p>
+<div class="flexbox column">
+    <input>
+</div>
+<div class="flexbox column align-content-flex-start">
+    <textarea class="align-self-stretch"></textarea>
+</div>
+<div class="flexbox column wrap">
+    <input type="button">
+</div>
+<div class="flexbox column">
+    <select></select>
+</div>
+<div class="flexbox column">
+    <legend style="border: 2px solid black">legend</legend>
+</div>
+<div class="flexbox column wrap">
+    <div type="border: 4px solid black; padding: 10px;">
+</div>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-multicol/multicol-overflow-clip-ref.html b/third_party/blink/web_tests/external/wpt/css/css-multicol/multicol-overflow-clip-ref.html
new file mode 100644
index 0000000..c061a1d6
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-multicol/multicol-overflow-clip-ref.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<style>
+.multicol {
+  column-count: 3;
+}
+.parent {
+  background: green;
+  height: 50px;
+}
+</style>
+<div class="multicol">
+  <div class="parent"></div>
+  <div class="parent"></div>
+  <div class="parent"></div>
+</div>
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-multicol/multicol-overflow-clip.html b/third_party/blink/web_tests/external/wpt/css/css-multicol/multicol-overflow-clip.html
new file mode 100644
index 0000000..af59ff9
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-multicol/multicol-overflow-clip.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<title>CSS Multi-column Layout Test: multicol with overflow-clipped content</title>
+<link rel="help" href="https://www.w3.org/TR/css-multicol-1/">
+<link rel="match" href="multicol-overflow-clip-ref.html">
+<meta name="assert" content="Overflow clip should work under multicol.">
+<style>
+.multicol {
+  column-count: 3;
+}
+.parent {
+  background: green;
+  height: 50px;
+  overflow: hidden;
+}
+.child2 {
+  margin-top: 50px;
+  background: darkred;
+  color: red;
+  height: 100px;
+}
+</style>
+<div class="multicol">
+  <div class="parent">
+    <div class="child2">This should be hidden.</div>
+  </div>
+  <div class="parent">
+    <div class="child2">This should be hidden.</div>
+  </div>
+  <div class="parent">
+    <div class="child2">This should be hidden.</div>
+  </div>
+</div>
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-ui/appearance-button-bevel-001.html b/third_party/blink/web_tests/external/wpt/css/css-ui/appearance-button-bevel-001.html
deleted file mode 100644
index 0374852..0000000
--- a/third_party/blink/web_tests/external/wpt/css/css-ui/appearance-button-bevel-001.html
+++ /dev/null
@@ -1,28 +0,0 @@
-<!DOCTYPE html>
-<meta charset="utf-8">
-<title>CSS Basic User Interface Test: appearance: button-bevel</title>
-<link rel="help" href="https://drafts.csswg.org/css-ui-4/#appearance-switching">
-<meta name="assert" content="button-bevel is an alias to auto.">
-<link rel="match" href="appearance-auto-ref.html">
-<style>
- #container { width: 500px; }
- #container > * { appearance: none; appearance: button-bevel; }
-</style>
-<div id="container">
-	<a>a</a>
-	<button>button</button>
-	<input type="text" value="input-text">
-	<input type="search" value="input-search">
-	<textarea>textarea</textarea>
-	<input type="button" value="input-button">
-	<input type="submit" value="input-submit">
-	<input type="reset" value="input-reset">
-	<input type="range">
-	<input type="checkbox">
-	<input type="radio">
-	<input type="color">
-	<select><option>select</option></select>
-	<select multiple><option>select-multiple</option></select>
-	<meter value=0.5></meter>
-	<progress value=0.5></progress>
-</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-ui/appearance-cssom-001-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-ui/appearance-cssom-001-expected.txt
index 30c70bbd..3c5f1f0 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-ui/appearance-cssom-001-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/css/css-ui/appearance-cssom-001-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 342 tests; 170 PASS, 172 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 342 tests; 324 PASS, 18 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS -webkit-appearance: none
 PASS -webkit-appearance: auto
 PASS -webkit-appearance: button
@@ -163,169 +163,169 @@
 PASS -webkit-appearance: -moz-window-frame-right (invalid)
 PASS -webkit-appearance: -moz-window-titlebar (invalid)
 PASS -webkit-appearance: -moz-window-titlebar-maximized (invalid)
-FAIL appearance: none assert_equals: style.appearance expected (string) "none" but got (undefined) undefined
-FAIL appearance: auto assert_equals: style.appearance expected (string) "auto" but got (undefined) undefined
-FAIL appearance: button assert_equals: style.appearance expected (string) "button" but got (undefined) undefined
-FAIL appearance: checkbox assert_equals: style.appearance expected (string) "checkbox" but got (undefined) undefined
-FAIL appearance: listbox assert_equals: style.appearance expected (string) "listbox" but got (undefined) undefined
-FAIL appearance: menulist assert_equals: style.appearance expected (string) "menulist" but got (undefined) undefined
-FAIL appearance: menulist-button assert_equals: style.appearance expected (string) "menulist-button" but got (undefined) undefined
-FAIL appearance: meter assert_equals: style.appearance expected (string) "meter" but got (undefined) undefined
-FAIL appearance: progress-bar assert_equals: style.appearance expected (string) "progress-bar" but got (undefined) undefined
-FAIL appearance: push-button assert_equals: style.appearance expected (string) "push-button" but got (undefined) undefined
-FAIL appearance: radio assert_equals: style.appearance expected (string) "radio" but got (undefined) undefined
-FAIL appearance: searchfield assert_equals: style.appearance expected (string) "searchfield" but got (undefined) undefined
-FAIL appearance: slider-horizontal assert_equals: style.appearance expected (string) "slider-horizontal" but got (undefined) undefined
-FAIL appearance: square-button assert_equals: style.appearance expected (string) "square-button" but got (undefined) undefined
-FAIL appearance: textarea assert_equals: style.appearance expected (string) "textarea" but got (undefined) undefined
-FAIL appearance: textfield assert_equals: style.appearance expected (string) "textfield" but got (undefined) undefined
-FAIL appearance: bogus-button (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: attachment (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: button-bevel (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: borderless-attachment (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: button-arrow-down (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: button-arrow-next (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: button-arrow-previous (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: button-arrow-up (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: button-focus (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: caps-lock-indicator (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: caret (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: checkbox-container (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: checkbox-label (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: checkmenuitem (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: color-well (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: continuous-capacity-level-indicator (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: default-button (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: discrete-capacity-level-indicator (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: dualbutton (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: groupbox (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: image-controls-button (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: inner-spin-button (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: list-button (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: listitem (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: media-controls-background (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: media-controls-dark-bar-background (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: media-controls-fullscreen-background (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: media-controls-light-bar-background (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: media-current-time-display (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: media-enter-fullscreen-button (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: media-exit-fullscreen-button (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: media-fullscreen-volume-slider (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: media-fullscreen-volume-slider-thumb (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: media-mute-button (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: media-overlay-play-button (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: media-play-button (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: media-return-to-realtime-button (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: media-rewind-button (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: media-seek-back-button (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: media-seek-forward-button (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: media-slider (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: media-sliderthumb (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: media-time-remaining-display (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: media-toggle-closed-captions-button (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: media-volume-slider (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: media-volume-slider-container (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: media-volume-slider-mute-button (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: media-volume-sliderthumb (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: menuarrow (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: menubar (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: menucheckbox (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: menuimage (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: menuitem (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: menuitemtext (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: menulist-text (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: menulist-textfield (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: menupopup (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: menuradio (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: menuseparator (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: meterbar (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: meterchunk (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: number-input (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: progress-bar-value (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: progressbar (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: progressbar-vertical (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: progresschunk (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: progresschunk-vertical (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: radio-container (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: radio-label (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: radiomenuitem (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: range (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: range-thumb (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: rating-level-indicator (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: relevancy-level-indicator (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: resizer (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: resizerpanel (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: scale-horizontal (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: scale-vertical (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: scalethumb-horizontal (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: scalethumb-vertical (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: scalethumbend (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: scalethumbstart (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: scalethumbtick (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: scrollbarbutton-down (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: scrollbarbutton-left (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: scrollbarbutton-right (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: scrollbarbutton-up (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: scrollbarthumb-horizontal (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: scrollbarthumb-vertical (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: scrollbartrack-horizontal (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: scrollbartrack-vertical (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: searchfield-cancel-button (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: searchfield-decoration (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: searchfield-results-button (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: searchfield-results-decoration (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: separator (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: sheet (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: slider-vertical (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: sliderthumb-horizontal (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: sliderthumb-vertical (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: snapshotted-plugin-overlay (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: spinner (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: spinner-downbutton (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: spinner-textfield (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: spinner-upbutton (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: splitter (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: statusbar (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: statusbarpanel (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: tab (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: tab-scroll-arrow-back (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: tab-scroll-arrow-forward  (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: tabpanel (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: tabpanels (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: textfield-multiline (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: toolbar (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: toolbarbutton (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: toolbarbutton-dropdown (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: toolbargripper (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: toolbox (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: tooltip (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: treeheader (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: treeheadercell (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: treeheadersortarrow (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: treeitem (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: treeline (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: treetwisty (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: treetwistyopen (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: treeview (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: -apple-pay-button (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: -moz-win-borderless-glass (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: -moz-win-browsertabbar-toolbox (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: -moz-win-communications-toolbox (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: -moz-win-communicationstext (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: -moz-win-exclude-glass (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: -moz-win-glass (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: -moz-win-media-toolbox (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: -moz-window-button-box (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: -moz-window-button-box-maximized (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: -moz-window-button-close (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: -moz-window-button-maximize (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: -moz-window-button-minimize (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: -moz-window-button-restore (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: -moz-window-frame-bottom (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: -moz-window-frame-left (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: -moz-window-frame-right (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: -moz-window-titlebar (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
-FAIL appearance: -moz-window-titlebar-maximized (invalid) assert_equals: style.appearance expected (string) "" but got (undefined) undefined
+PASS appearance: none
+PASS appearance: auto
+PASS appearance: button
+PASS appearance: checkbox
+PASS appearance: listbox
+PASS appearance: menulist
+PASS appearance: menulist-button
+PASS appearance: meter
+PASS appearance: progress-bar
+PASS appearance: push-button
+PASS appearance: radio
+PASS appearance: searchfield
+PASS appearance: slider-horizontal
+PASS appearance: square-button
+PASS appearance: textarea
+PASS appearance: textfield
+PASS appearance: bogus-button (invalid)
+PASS appearance: attachment (invalid)
+PASS appearance: button-bevel (invalid)
+PASS appearance: borderless-attachment (invalid)
+PASS appearance: button-arrow-down (invalid)
+PASS appearance: button-arrow-next (invalid)
+PASS appearance: button-arrow-previous (invalid)
+PASS appearance: button-arrow-up (invalid)
+PASS appearance: button-focus (invalid)
+PASS appearance: caps-lock-indicator (invalid)
+PASS appearance: caret (invalid)
+PASS appearance: checkbox-container (invalid)
+PASS appearance: checkbox-label (invalid)
+PASS appearance: checkmenuitem (invalid)
+PASS appearance: color-well (invalid)
+PASS appearance: continuous-capacity-level-indicator (invalid)
+PASS appearance: default-button (invalid)
+PASS appearance: discrete-capacity-level-indicator (invalid)
+PASS appearance: dualbutton (invalid)
+PASS appearance: groupbox (invalid)
+PASS appearance: image-controls-button (invalid)
+FAIL appearance: inner-spin-button (invalid) assert_equals: style.appearance expected "" but got "inner-spin-button"
+PASS appearance: list-button (invalid)
+PASS appearance: listitem (invalid)
+PASS appearance: media-controls-background (invalid)
+PASS appearance: media-controls-dark-bar-background (invalid)
+PASS appearance: media-controls-fullscreen-background (invalid)
+PASS appearance: media-controls-light-bar-background (invalid)
+PASS appearance: media-current-time-display (invalid)
+PASS appearance: media-enter-fullscreen-button (invalid)
+PASS appearance: media-exit-fullscreen-button (invalid)
+PASS appearance: media-fullscreen-volume-slider (invalid)
+PASS appearance: media-fullscreen-volume-slider-thumb (invalid)
+PASS appearance: media-mute-button (invalid)
+PASS appearance: media-overlay-play-button (invalid)
+PASS appearance: media-play-button (invalid)
+PASS appearance: media-return-to-realtime-button (invalid)
+PASS appearance: media-rewind-button (invalid)
+PASS appearance: media-seek-back-button (invalid)
+PASS appearance: media-seek-forward-button (invalid)
+FAIL appearance: media-slider (invalid) assert_equals: style.appearance expected "" but got "media-slider"
+FAIL appearance: media-sliderthumb (invalid) assert_equals: style.appearance expected "" but got "media-sliderthumb"
+PASS appearance: media-time-remaining-display (invalid)
+PASS appearance: media-toggle-closed-captions-button (invalid)
+FAIL appearance: media-volume-slider (invalid) assert_equals: style.appearance expected "" but got "media-volume-slider"
+PASS appearance: media-volume-slider-container (invalid)
+PASS appearance: media-volume-slider-mute-button (invalid)
+FAIL appearance: media-volume-sliderthumb (invalid) assert_equals: style.appearance expected "" but got "media-volume-sliderthumb"
+PASS appearance: menuarrow (invalid)
+PASS appearance: menubar (invalid)
+PASS appearance: menucheckbox (invalid)
+PASS appearance: menuimage (invalid)
+PASS appearance: menuitem (invalid)
+PASS appearance: menuitemtext (invalid)
+PASS appearance: menulist-text (invalid)
+PASS appearance: menulist-textfield (invalid)
+PASS appearance: menupopup (invalid)
+PASS appearance: menuradio (invalid)
+PASS appearance: menuseparator (invalid)
+PASS appearance: meterbar (invalid)
+PASS appearance: meterchunk (invalid)
+PASS appearance: number-input (invalid)
+PASS appearance: progress-bar-value (invalid)
+PASS appearance: progressbar (invalid)
+PASS appearance: progressbar-vertical (invalid)
+PASS appearance: progresschunk (invalid)
+PASS appearance: progresschunk-vertical (invalid)
+PASS appearance: radio-container (invalid)
+PASS appearance: radio-label (invalid)
+PASS appearance: radiomenuitem (invalid)
+PASS appearance: range (invalid)
+PASS appearance: range-thumb (invalid)
+PASS appearance: rating-level-indicator (invalid)
+PASS appearance: relevancy-level-indicator (invalid)
+PASS appearance: resizer (invalid)
+PASS appearance: resizerpanel (invalid)
+PASS appearance: scale-horizontal (invalid)
+PASS appearance: scale-vertical (invalid)
+PASS appearance: scalethumb-horizontal (invalid)
+PASS appearance: scalethumb-vertical (invalid)
+PASS appearance: scalethumbend (invalid)
+PASS appearance: scalethumbstart (invalid)
+PASS appearance: scalethumbtick (invalid)
+PASS appearance: scrollbarbutton-down (invalid)
+PASS appearance: scrollbarbutton-left (invalid)
+PASS appearance: scrollbarbutton-right (invalid)
+PASS appearance: scrollbarbutton-up (invalid)
+PASS appearance: scrollbarthumb-horizontal (invalid)
+PASS appearance: scrollbarthumb-vertical (invalid)
+PASS appearance: scrollbartrack-horizontal (invalid)
+PASS appearance: scrollbartrack-vertical (invalid)
+FAIL appearance: searchfield-cancel-button (invalid) assert_equals: style.appearance expected "" but got "searchfield-cancel-button"
+PASS appearance: searchfield-decoration (invalid)
+PASS appearance: searchfield-results-button (invalid)
+PASS appearance: searchfield-results-decoration (invalid)
+PASS appearance: separator (invalid)
+PASS appearance: sheet (invalid)
+FAIL appearance: slider-vertical (invalid) assert_equals: style.appearance expected "" but got "slider-vertical"
+FAIL appearance: sliderthumb-horizontal (invalid) assert_equals: style.appearance expected "" but got "sliderthumb-horizontal"
+FAIL appearance: sliderthumb-vertical (invalid) assert_equals: style.appearance expected "" but got "sliderthumb-vertical"
+PASS appearance: snapshotted-plugin-overlay (invalid)
+PASS appearance: spinner (invalid)
+PASS appearance: spinner-downbutton (invalid)
+PASS appearance: spinner-textfield (invalid)
+PASS appearance: spinner-upbutton (invalid)
+PASS appearance: splitter (invalid)
+PASS appearance: statusbar (invalid)
+PASS appearance: statusbarpanel (invalid)
+PASS appearance: tab (invalid)
+PASS appearance: tab-scroll-arrow-back (invalid)
+PASS appearance: tab-scroll-arrow-forward  (invalid)
+PASS appearance: tabpanel (invalid)
+PASS appearance: tabpanels (invalid)
+PASS appearance: textfield-multiline (invalid)
+PASS appearance: toolbar (invalid)
+PASS appearance: toolbarbutton (invalid)
+PASS appearance: toolbarbutton-dropdown (invalid)
+PASS appearance: toolbargripper (invalid)
+PASS appearance: toolbox (invalid)
+PASS appearance: tooltip (invalid)
+PASS appearance: treeheader (invalid)
+PASS appearance: treeheadercell (invalid)
+PASS appearance: treeheadersortarrow (invalid)
+PASS appearance: treeitem (invalid)
+PASS appearance: treeline (invalid)
+PASS appearance: treetwisty (invalid)
+PASS appearance: treetwistyopen (invalid)
+PASS appearance: treeview (invalid)
+PASS appearance: -apple-pay-button (invalid)
+PASS appearance: -moz-win-borderless-glass (invalid)
+PASS appearance: -moz-win-browsertabbar-toolbox (invalid)
+PASS appearance: -moz-win-communications-toolbox (invalid)
+PASS appearance: -moz-win-communicationstext (invalid)
+PASS appearance: -moz-win-exclude-glass (invalid)
+PASS appearance: -moz-win-glass (invalid)
+PASS appearance: -moz-win-media-toolbox (invalid)
+PASS appearance: -moz-window-button-box (invalid)
+PASS appearance: -moz-window-button-box-maximized (invalid)
+PASS appearance: -moz-window-button-close (invalid)
+PASS appearance: -moz-window-button-maximize (invalid)
+PASS appearance: -moz-window-button-minimize (invalid)
+PASS appearance: -moz-window-button-restore (invalid)
+PASS appearance: -moz-window-frame-bottom (invalid)
+PASS appearance: -moz-window-frame-left (invalid)
+PASS appearance: -moz-window-frame-right (invalid)
+PASS appearance: -moz-window-titlebar (invalid)
+PASS appearance: -moz-window-titlebar-maximized (invalid)
 PASS -ms-appearance (should not be supported)
 PASS mso-appearance (should not be supported)
 PASS -moz-appearance (should not be supported)
diff --git a/third_party/blink/web_tests/external/wpt/css/css-ui/appearance-initial-value-001-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-ui/appearance-initial-value-001-expected.txt
deleted file mode 100644
index 7b676a1..0000000
--- a/third_party/blink/web_tests/external/wpt/css/css-ui/appearance-initial-value-001-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-FAIL support for appearance assert_equals: expected "button" but got ""
-FAIL initial value for appearance assert_equals: expected "none" but got ""
-PASS support for -webkit-appearance
-PASS initial value for -webkit-appearance
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/css/css-ui/appearance-parsing-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-ui/appearance-parsing-expected.txt
deleted file mode 100644
index c748b8a..0000000
--- a/third_party/blink/web_tests/external/wpt/css/css-ui/appearance-parsing-expected.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-This is a testharness.js-based test.
-FAIL parsing via attribute change steps of CSS declaration block's owner node assert_equals: expected "none" but got ""
-FAIL parsing via modification of cssText assert_equals: expected "none" but got ""
-FAIL parsing via creation of CSS declaration block assert_equals: expected "none" but got ""
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/css/css-ui/appearance-property-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-ui/appearance-property-expected.txt
deleted file mode 100644
index 90dcd46..0000000
--- a/third_party/blink/web_tests/external/wpt/css/css-ui/appearance-property-expected.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-This is a testharness.js-based test.
-FAIL setProperty assert_equals: expected (string) "none" but got (undefined) undefined
-FAIL removeProperty assert_equals: expected (string) "" but got (undefined) undefined
-PASS property assignment
-FAIL getPropertyValue assert_equals: expected "none" but got ""
-FAIL property access assert_equals: expected (string) "none" but got (undefined) undefined
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/css/css-ui/appearance-serialization-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-ui/appearance-serialization-expected.txt
deleted file mode 100644
index edcbb8b..0000000
--- a/third_party/blink/web_tests/external/wpt/css/css-ui/appearance-serialization-expected.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-This is a testharness.js-based test.
-FAIL serialization via CSSStyleDeclaration assert_equals: expected "appearance: none;" but got ""
-FAIL serialization via CSSStyleRule assert_equals: expected "#foo { appearance: none; }" but got "#foo { }"
-FAIL serialization via CSSMediaRule assert_equals: expected "@media print {\n  #foo { appearance: none; }\n}" but got "@media print {\n  #foo { }\n}"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/css/css-ui/inheritance-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-ui/inheritance-expected.txt
index eb57674..548a76a 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-ui/inheritance-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/css/css-ui/inheritance-expected.txt
@@ -1,6 +1,6 @@
 This is a testharness.js-based test.
-FAIL Property appearance has initial value auto assert_true: appearance doesn't seem to be supported in the computed style expected true got false
-FAIL Property appearance does not inherit assert_true: expected true got false
+PASS Property appearance has initial value none
+PASS Property appearance does not inherit
 PASS Property caret-color has initial value rgb(0, 255, 0)
 PASS Property caret-color inherits
 FAIL Property caret-shape has initial value auto assert_true: caret-shape doesn't seem to be supported in the computed style expected true got false
diff --git a/third_party/blink/web_tests/external/wpt/css/css-ui/inheritance.html b/third_party/blink/web_tests/external/wpt/css/css-ui/inheritance.html
index 6315316..c2aab07 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-ui/inheritance.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-ui/inheritance.html
@@ -33,7 +33,7 @@
 const mediumWidth = getComputedStyle(reference).borderTopWidth; // e.g. 3px
 const currentColor = getComputedStyle(reference).color;
 
-assert_not_inherited('appearance', 'auto', 'none');
+assert_not_inherited('appearance', 'none', 'auto');
 assert_inherited('caret-color', currentColor, 'rgba(42, 53, 64, 0.75)');
 assert_inherited('caret-shape', 'auto', 'bar');
 assert_inherited('cursor', 'auto', 'pointer');
diff --git a/third_party/blink/web_tests/external/wpt/css/css-ui/webkit-appearance-button-bevel-001.html b/third_party/blink/web_tests/external/wpt/css/css-ui/webkit-appearance-button-bevel-001.html
deleted file mode 100644
index 3c860f23..0000000
--- a/third_party/blink/web_tests/external/wpt/css/css-ui/webkit-appearance-button-bevel-001.html
+++ /dev/null
@@ -1,32 +0,0 @@
-<!-- DO NOT EDIT THIS FILE.
-Edit the appearance-* file instead and then run:
-    ./tools/appearance-build-webkit-reftests.py
--->
-<!DOCTYPE html>
-<meta charset="utf-8">
-<title>CSS Basic User Interface Test: -webkit-appearance: button-bevel</title>
-<link rel="help" href="https://drafts.csswg.org/css-ui-4/#appearance-switching">
-<meta name="assert" content="button-bevel is an alias to auto.">
-<link rel="match" href="appearance-auto-ref.html">
-<style>
- #container { width: 500px; }
- #container > * { -webkit-appearance: none; -webkit-appearance: button-bevel; }
-</style>
-<div id="container">
-	<a>a</a>
-	<button>button</button>
-	<input type="text" value="input-text">
-	<input type="search" value="input-search">
-	<textarea>textarea</textarea>
-	<input type="button" value="input-button">
-	<input type="submit" value="input-submit">
-	<input type="reset" value="input-reset">
-	<input type="range">
-	<input type="checkbox">
-	<input type="radio">
-	<input type="color">
-	<select><option>select</option></select>
-	<select multiple><option>select-multiple</option></select>
-	<meter value=0.5></meter>
-	<progress value=0.5></progress>
-</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-ui/webkit-appearance-parsing-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-ui/webkit-appearance-parsing-expected.txt
deleted file mode 100644
index c748b8a..0000000
--- a/third_party/blink/web_tests/external/wpt/css/css-ui/webkit-appearance-parsing-expected.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-This is a testharness.js-based test.
-FAIL parsing via attribute change steps of CSS declaration block's owner node assert_equals: expected "none" but got ""
-FAIL parsing via modification of cssText assert_equals: expected "none" but got ""
-FAIL parsing via creation of CSS declaration block assert_equals: expected "none" but got ""
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/css/css-ui/webkit-appearance-property-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-ui/webkit-appearance-property-expected.txt
deleted file mode 100644
index d0e76b0..0000000
--- a/third_party/blink/web_tests/external/wpt/css/css-ui/webkit-appearance-property-expected.txt
+++ /dev/null
@@ -1,18 +0,0 @@
-This is a testharness.js-based test.
-FAIL setProperty - CSS property name assert_equals: expected (string) "none" but got (undefined) undefined
-FAIL setProperty - camel-cased property name (ignored) assert_equals: expected (string) "" but got (undefined) undefined
-FAIL setProperty - webkit-cased property name (ignored) assert_equals: expected (string) "" but got (undefined) undefined
-FAIL removeProperty - CSS property name assert_equals: expected (string) "" but got (undefined) undefined
-FAIL removeProperty - camel-cased property name (ignored) assert_equals: expected (string) "none" but got (undefined) undefined
-FAIL removeProperty - webkit-cased property name (ignored) assert_equals: expected (string) "none" but got (undefined) undefined
-FAIL property assignment - CSS property name assert_equals: expected (string) "none" but got (undefined) undefined
-FAIL property assignment - camel-cased property name assert_equals: expected (string) "none" but got (undefined) undefined
-FAIL property assignment - webkit-cased property name assert_equals: expected (string) "none" but got (undefined) undefined
-FAIL getPropertyValue - CSS property name assert_equals: expected "none" but got ""
-PASS getPropertyValue - camel-cased property name (ignored)
-PASS getPropertyValue - webkit-cased property name (ignored)
-FAIL property access - CSS property name assert_equals: expected "none" but got ""
-FAIL property access - camel-cased property name assert_equals: expected "none" but got ""
-FAIL property access - webkit-cased property name assert_equals: expected "none" but got ""
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/css/css-ui/webkit-appearance-serialization-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-ui/webkit-appearance-serialization-expected.txt
deleted file mode 100644
index 3ff4fe3e..0000000
--- a/third_party/blink/web_tests/external/wpt/css/css-ui/webkit-appearance-serialization-expected.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-This is a testharness.js-based test.
-FAIL serialization via CSSStyleDeclaration assert_equals: expected "appearance: none;" but got "-webkit-appearance: none;"
-FAIL serialization via CSSStyleRule assert_equals: expected "#foo { appearance: none; }" but got "#foo { -webkit-appearance: none; }"
-FAIL serialization via CSSMediaRule assert_equals: expected "@media print {\n  #foo { appearance: none; }\n}" but got "@media print {\n  #foo { -webkit-appearance: none; }\n}"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/fetch/origin/assorted.window-expected.txt b/third_party/blink/web_tests/external/wpt/fetch/origin/assorted.window-expected.txt
index b5fa6b5..6b30c4e4 100644
--- a/third_party/blink/web_tests/external/wpt/fetch/origin/assorted.window-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/fetch/origin/assorted.window-expected.txt
@@ -4,33 +4,43 @@
 PASS Origin header and POST navigation
 PASS Origin header and POST same-origin navigation with Referrer-Policy no-referrer
 FAIL Origin header and POST same-origin fetch no-cors mode with Referrer-Policy no-referrer assert_equals: expected "null" but got "http://web-platform.test:8001"
-PASS Origin header and POST same-origin fetch cors mode with Referrer-Policy no-referrer
+FAIL Origin header and POST same-origin fetch cors mode with Referrer-Policy no-referrer assert_equals: expected "null" but got "http://web-platform.test:8001"
+PASS Origin header and GET same-origin fetch cors mode with Referrer-Policy no-referrer
 PASS Origin header and POST cross-origin navigation with Referrer-Policy no-referrer
 FAIL Origin header and POST cross-origin fetch no-cors mode with Referrer-Policy no-referrer assert_equals: expected "null" but got "http://web-platform.test:8001"
 PASS Origin header and POST cross-origin fetch cors mode with Referrer-Policy no-referrer
+PASS Origin header and GET cross-origin fetch cors mode with Referrer-Policy no-referrer
 PASS Origin header and POST same-origin navigation with Referrer-Policy same-origin
 PASS Origin header and POST same-origin fetch no-cors mode with Referrer-Policy same-origin
 PASS Origin header and POST same-origin fetch cors mode with Referrer-Policy same-origin
+PASS Origin header and GET same-origin fetch cors mode with Referrer-Policy same-origin
 PASS Origin header and POST cross-origin navigation with Referrer-Policy same-origin
 FAIL Origin header and POST cross-origin fetch no-cors mode with Referrer-Policy same-origin assert_equals: expected "null" but got "http://web-platform.test:8001"
 PASS Origin header and POST cross-origin fetch cors mode with Referrer-Policy same-origin
+PASS Origin header and GET cross-origin fetch cors mode with Referrer-Policy same-origin
 PASS Origin header and POST same-origin navigation with Referrer-Policy origin-when-cross-origin
 PASS Origin header and POST same-origin fetch no-cors mode with Referrer-Policy origin-when-cross-origin
 PASS Origin header and POST same-origin fetch cors mode with Referrer-Policy origin-when-cross-origin
+PASS Origin header and GET same-origin fetch cors mode with Referrer-Policy origin-when-cross-origin
 PASS Origin header and POST cross-origin navigation with Referrer-Policy origin-when-cross-origin
 PASS Origin header and POST cross-origin fetch no-cors mode with Referrer-Policy origin-when-cross-origin
 PASS Origin header and POST cross-origin fetch cors mode with Referrer-Policy origin-when-cross-origin
+PASS Origin header and GET cross-origin fetch cors mode with Referrer-Policy origin-when-cross-origin
 PASS Origin header and POST same-origin navigation with Referrer-Policy no-referrer-when-downgrade
 PASS Origin header and POST same-origin fetch no-cors mode with Referrer-Policy no-referrer-when-downgrade
 PASS Origin header and POST same-origin fetch cors mode with Referrer-Policy no-referrer-when-downgrade
+PASS Origin header and GET same-origin fetch cors mode with Referrer-Policy no-referrer-when-downgrade
 PASS Origin header and POST cross-origin navigation with Referrer-Policy no-referrer-when-downgrade
 PASS Origin header and POST cross-origin fetch no-cors mode with Referrer-Policy no-referrer-when-downgrade
 PASS Origin header and POST cross-origin fetch cors mode with Referrer-Policy no-referrer-when-downgrade
+PASS Origin header and GET cross-origin fetch cors mode with Referrer-Policy no-referrer-when-downgrade
 PASS Origin header and POST same-origin navigation with Referrer-Policy unsafe-url
 PASS Origin header and POST same-origin fetch no-cors mode with Referrer-Policy unsafe-url
 PASS Origin header and POST same-origin fetch cors mode with Referrer-Policy unsafe-url
+PASS Origin header and GET same-origin fetch cors mode with Referrer-Policy unsafe-url
 PASS Origin header and POST cross-origin navigation with Referrer-Policy unsafe-url
 PASS Origin header and POST cross-origin fetch no-cors mode with Referrer-Policy unsafe-url
 PASS Origin header and POST cross-origin fetch cors mode with Referrer-Policy unsafe-url
+PASS Origin header and GET cross-origin fetch cors mode with Referrer-Policy unsafe-url
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/external/wpt/fetch/origin/assorted.window.js b/third_party/blink/web_tests/external/wpt/fetch/origin/assorted.window.js
index cc37dbd..fc6dd1a 100644
--- a/third_party/blink/web_tests/external/wpt/fetch/origin/assorted.window.js
+++ b/third_party/blink/web_tests/external/wpt/fetch/origin/assorted.window.js
@@ -115,7 +115,7 @@
   };
 }
 
-function fetchReferrerPolicy(referrerPolicy, destination, fetchMode, expectedOrigin) {
+function fetchReferrerPolicy(referrerPolicy, destination, fetchMode, expectedOrigin, httpMethod) {
   return async function () {
     const stash = token();
     const redirectPath = "/fetch/origin/resources/redirect-and-stash.py";
@@ -125,7 +125,7 @@
                                        : origins.HTTP_REMOTE_ORIGIN) +
         redirectPath + "?stash=" + stash;
 
-    await fetch(fetchUrl, { mode: fetchMode, method: "POST" , "referrerPolicy": referrerPolicy});
+    await fetch(fetchUrl, { mode: fetchMode, method: httpMethod , "referrerPolicy": referrerPolicy});
 
     const json = await (await fetch(redirectPath + "?dump&stash=" + stash)).json();
 
@@ -133,8 +133,8 @@
   };
 }
 
-function referrerPolicyTestString(referrerPolicy, destination) {
-  return "Origin header and POST " + destination + " with Referrer-Policy " +
+function referrerPolicyTestString(referrerPolicy, method, destination) {
+  return "Origin header and " + method + " " + destination + " with Referrer-Policy " +
          referrerPolicy;
 }
 
@@ -179,23 +179,33 @@
     promise_test(navigationReferrerPolicy(testObj.policy,
                                           destination.name,
                                           destination.expectedOrigin),
-                 referrerPolicyTestString(testObj.policy,
+                 referrerPolicyTestString(testObj.policy, "POST",
                                           destination.name + " navigation"));
     // Test fetch
     promise_test(fetchReferrerPolicy(testObj.policy,
                                      destination.name,
                                      "no-cors",
-                                     destination.expectedOrigin),
-                 referrerPolicyTestString(testObj.policy,
+                                     destination.expectedOrigin,
+                                     "POST"),
+                 referrerPolicyTestString(testObj.policy, "POST",
                                           destination.name + " fetch no-cors mode"));
 
-    // When we're dealing with CORS (mode is "cors"), we shouldn't take the
-    // Referrer-Policy into account
+    // Test cors mode POST
     promise_test(fetchReferrerPolicy(testObj.policy,
                                      destination.name,
                                      "cors",
-                                     origins.HTTP_ORIGIN),
-                 referrerPolicyTestString(testObj.policy,
+                                     (destination.name == "same-origin") ? destination.expectedOrigin : origins.HTTP_ORIGIN,
+                                     "POST"),
+                 referrerPolicyTestString(testObj.policy, "POST",
+                                          destination.name + " fetch cors mode"));
+
+    // Test cors mode GET
+    promise_test(fetchReferrerPolicy(testObj.policy,
+                                     destination.name,
+                                     "cors",
+                                     (destination.name == "same-origin") ? "no Origin header" : origins.HTTP_ORIGIN,
+                                     "GET"),
+                 referrerPolicyTestString(testObj.policy, "GET",
                                           destination.name + " fetch cors mode"));
   });
 });
diff --git a/third_party/blink/web_tests/external/wpt/html/rendering/widgets/appearance/default-styles-expected.txt b/third_party/blink/web_tests/external/wpt/html/rendering/widgets/appearance/default-styles-expected.txt
index c5d868f..7062e8d 100644
--- a/third_party/blink/web_tests/external/wpt/html/rendering/widgets/appearance/default-styles-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/html/rendering/widgets/appearance/default-styles-expected.txt
@@ -28,21 +28,21 @@
 FAIL <textarea> assert_equals: -webkit-appearance expected "auto" but got "textarea"
 FAIL <meter> assert_equals: -webkit-appearance expected "auto" but got "meter"
 FAIL <progress> assert_equals: -webkit-appearance expected "auto" but got "progress-bar"
-FAIL <input type="hidden"> assert_equals: appearance (no prefix) expected "none" but got ""
-FAIL <input type="HIDDEN"> assert_equals: appearance (no prefix) expected "none" but got ""
-FAIL <input type="file"> assert_equals: appearance (no prefix) expected "none" but got ""
-FAIL <input type="image"> assert_equals: appearance (no prefix) expected "none" but got ""
-FAIL <div> assert_equals: appearance (no prefix) expected "none" but got ""
-FAIL <details> assert_equals: appearance (no prefix) expected "none" but got ""
-FAIL <summary> assert_equals: appearance (no prefix) expected "none" but got ""
-FAIL <video> assert_equals: appearance (no prefix) expected "none" but got ""
-FAIL <video controls=""> assert_equals: appearance (no prefix) expected "none" but got ""
-FAIL <menuitem> assert_equals: appearance (no prefix) expected "none" but got ""
-FAIL <marquee> assert_equals: appearance (no prefix) expected "none" but got ""
-FAIL <keygen> assert_equals: appearance (no prefix) expected "none" but got ""
-FAIL <input> (namespace: null) assert_equals: appearance (no prefix) expected "none" but got ""
-FAIL <input> (namespace: http://www.w3.org/2000/svg) assert_equals: appearance (no prefix) expected "none" but got ""
-FAIL The html element assert_equals: appearance (no prefix) expected "none" but got ""
-FAIL The body element assert_equals: appearance (no prefix) expected "none" but got ""
+PASS <input type="hidden">
+PASS <input type="HIDDEN">
+PASS <input type="file">
+PASS <input type="image">
+PASS <div>
+PASS <details>
+PASS <summary>
+PASS <video>
+PASS <video controls="">
+PASS <menuitem>
+PASS <marquee>
+PASS <keygen>
+PASS <input> (namespace: null)
+PASS <input> (namespace: http://www.w3.org/2000/svg)
+PASS The html element
+PASS The body element
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/padding-border-margin/border-002-expected.txt b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/padding-border-margin/border-002-expected.txt
new file mode 100644
index 0000000..fd33b8b
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/padding-border-margin/border-002-expected.txt
@@ -0,0 +1,52 @@
+This is a testharness.js-based test.
+PASS Border properties on maction
+PASS Border properties on maction (rtl)
+PASS Border properties on menclose
+PASS Border properties on menclose (rtl)
+PASS Border properties on merror
+PASS Border properties on merror (rtl)
+PASS Border properties on mfrac
+PASS Border properties on mfrac (rtl)
+PASS Border properties on mi
+PASS Border properties on mi (rtl)
+PASS Border properties on mmultiscripts
+PASS Border properties on mmultiscripts (rtl)
+PASS Border properties on mn
+PASS Border properties on mn (rtl)
+PASS Border properties on mo
+PASS Border properties on mo (rtl)
+PASS Border properties on mover
+PASS Border properties on mover (rtl)
+PASS Border properties on mpadded
+PASS Border properties on mpadded (rtl)
+PASS Border properties on mphantom
+PASS Border properties on mphantom (rtl)
+FAIL Border properties on mroot assert_true: mroot is supported expected true got false
+FAIL Border properties on mroot (rtl) assert_true: mroot is supported expected true got false
+PASS Border properties on mrow
+PASS Border properties on mrow (rtl)
+PASS Border properties on ms
+PASS Border properties on ms (rtl)
+PASS Border properties on mspace
+FAIL Border properties on msqrt assert_true: msqrt is supported expected true got false
+FAIL Border properties on msqrt (rtl) assert_true: msqrt is supported expected true got false
+PASS Border properties on mstyle
+PASS Border properties on mstyle (rtl)
+PASS Border properties on msub
+PASS Border properties on msub (rtl)
+PASS Border properties on msubsup
+PASS Border properties on msubsup (rtl)
+PASS Border properties on msup
+PASS Border properties on msup (rtl)
+PASS Border properties on mtable
+PASS Border properties on mtable (rtl)
+PASS Border properties on mtext
+PASS Border properties on mtext (rtl)
+PASS Border properties on munder
+PASS Border properties on munder (rtl)
+PASS Border properties on munderover
+PASS Border properties on munderover (rtl)
+PASS Border properties on semantics
+PASS Border properties on semantics (rtl)
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/padding-border-margin/margin-002-expected.txt b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/padding-border-margin/margin-002-expected.txt
new file mode 100644
index 0000000..12188ba61
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/padding-border-margin/margin-002-expected.txt
@@ -0,0 +1,77 @@
+This is a testharness.js-based test.
+Found 73 tests; 67 PASS, 6 FAIL, 0 TIMEOUT, 0 NOTRUN.
+PASS Margin properties on maction
+PASS Margin properties on maction (rtl)
+PASS Margin properties on maction (no margin-collapsing)
+PASS Margin properties on menclose
+PASS Margin properties on menclose (rtl)
+PASS Margin properties on menclose (no margin-collapsing)
+PASS Margin properties on merror
+PASS Margin properties on merror (rtl)
+PASS Margin properties on merror (no margin-collapsing)
+PASS Margin properties on mfrac
+PASS Margin properties on mfrac (rtl)
+PASS Margin properties on mfrac (no margin-collapsing)
+PASS Margin properties on mi
+PASS Margin properties on mi (rtl)
+PASS Margin properties on mi (no margin-collapsing)
+PASS Margin properties on mmultiscripts
+PASS Margin properties on mmultiscripts (rtl)
+PASS Margin properties on mmultiscripts (no margin-collapsing)
+PASS Margin properties on mn
+PASS Margin properties on mn (rtl)
+PASS Margin properties on mn (no margin-collapsing)
+PASS Margin properties on mo
+PASS Margin properties on mo (rtl)
+PASS Margin properties on mo (no margin-collapsing)
+PASS Margin properties on mover
+PASS Margin properties on mover (rtl)
+PASS Margin properties on mover (no margin-collapsing)
+PASS Margin properties on mpadded
+PASS Margin properties on mpadded (rtl)
+PASS Margin properties on mpadded (no margin-collapsing)
+PASS Margin properties on mphantom
+PASS Margin properties on mphantom (rtl)
+PASS Margin properties on mphantom (no margin-collapsing)
+FAIL Margin properties on mroot assert_true: mroot is supported expected true got false
+FAIL Margin properties on mroot (rtl) assert_true: mroot is supported expected true got false
+FAIL Margin properties on mroot (no margin-collapsing) assert_true: mroot is supported expected true got false
+PASS Margin properties on mrow
+PASS Margin properties on mrow (rtl)
+PASS Margin properties on mrow (no margin-collapsing)
+PASS Margin properties on ms
+PASS Margin properties on ms (rtl)
+PASS Margin properties on ms (no margin-collapsing)
+PASS Margin properties on mspace
+FAIL Margin properties on msqrt assert_true: msqrt is supported expected true got false
+FAIL Margin properties on msqrt (rtl) assert_true: msqrt is supported expected true got false
+FAIL Margin properties on msqrt (no margin-collapsing) assert_true: msqrt is supported expected true got false
+PASS Margin properties on mstyle
+PASS Margin properties on mstyle (rtl)
+PASS Margin properties on mstyle (no margin-collapsing)
+PASS Margin properties on msub
+PASS Margin properties on msub (rtl)
+PASS Margin properties on msub (no margin-collapsing)
+PASS Margin properties on msubsup
+PASS Margin properties on msubsup (rtl)
+PASS Margin properties on msubsup (no margin-collapsing)
+PASS Margin properties on msup
+PASS Margin properties on msup (rtl)
+PASS Margin properties on msup (no margin-collapsing)
+PASS Margin properties on mtable
+PASS Margin properties on mtable (rtl)
+PASS Margin properties on mtable (no margin-collapsing)
+PASS Margin properties on mtext
+PASS Margin properties on mtext (rtl)
+PASS Margin properties on mtext (no margin-collapsing)
+PASS Margin properties on munder
+PASS Margin properties on munder (rtl)
+PASS Margin properties on munder (no margin-collapsing)
+PASS Margin properties on munderover
+PASS Margin properties on munderover (rtl)
+PASS Margin properties on munderover (no margin-collapsing)
+PASS Margin properties on semantics
+PASS Margin properties on semantics (rtl)
+PASS Margin properties on semantics (no margin-collapsing)
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/padding-border-margin/margin-003-expected.txt b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/padding-border-margin/margin-003-expected.txt
new file mode 100644
index 0000000..005624f
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/padding-border-margin/margin-003-expected.txt
@@ -0,0 +1,19 @@
+This is a testharness.js-based test.
+PASS Margin properties on the children of menclose
+PASS Margin properties on the children of merror
+PASS Margin properties on the children of mfrac
+FAIL Margin properties on the children of mmultiscripts assert_approx_equals: block size expected 345 +/- 1 but got 305
+PASS Margin properties on the children of mover
+PASS Margin properties on the children of mpadded
+PASS Margin properties on the children of mphantom
+FAIL Margin properties on the children of mroot assert_true: mroot is supported expected true got false
+PASS Margin properties on the children of mrow
+FAIL Margin properties on the children of msqrt assert_true: msqrt is supported expected true got false
+PASS Margin properties on the children of mstyle
+FAIL Margin properties on the children of msub assert_approx_equals: block size expected 230 +/- 1 but got 210
+FAIL Margin properties on the children of msubsup assert_approx_equals: block size expected 345 +/- 1 but got 305
+FAIL Margin properties on the children of msup assert_approx_equals: block size expected 230 +/- 1 but got 210
+PASS Margin properties on the children of munder
+PASS Margin properties on the children of munderover
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/padding-border-margin/margin-003.html b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/padding-border-margin/margin-003.html
new file mode 100644
index 0000000..5f0dbf4
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/padding-border-margin/margin-003.html
@@ -0,0 +1,93 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>margin</title>
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#layout-algorithms">
+<meta name="assert" content="Verify that margin is taken into account on children.">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/mathml/support/feature-detection.js"></script>
+<script src="/mathml/support/mathml-fragments.js"></script>
+<script src="/mathml/support/layout-comparison.js"></script>
+<script>
+  var epsilon = 1;
+
+  setup({ explicit_done: true });
+  window.addEventListener("load", runTests);
+
+  function runTests() {
+
+    for (tag in MathMLFragments) {
+        if (!FragmentHelper.isValidChildOfMrow(tag) ||
+            FragmentHelper.isEmpty(tag) ||
+            FragmentHelper.isTokenElement(tag) ||
+            tag == "semantics" ||
+            tag == "maction" ||
+            tag == "mtable")
+            continue;
+
+        test(function() {
+            assert_true(MathMLFeatureDetection[`has_${tag}`](), `${tag} is supported`);
+
+            document.body.insertAdjacentHTML("beforeend", `<hr/><div>\
+<div style="display: inline-block; border: 1px dashed blue;"><math>${MathMLFragments[tag]}</math></div><br/>\
+<div style="display: inline-block; border: 1px dashed green;"><math>${MathMLFragments[tag]}</math></div>\
+</div>`);
+
+            var div = document.body.lastElementChild;
+            var elementShrinkWrapContainer = div.firstElementChild;
+            var element = elementShrinkWrapContainer.firstElementChild.firstElementChild;
+            var elementContainer = div.firstElementChild;
+            var referenceShrinkWrapContainer = div.lastElementChild;
+            var reference = referenceShrinkWrapContainer.firstElementChild.firstElementChild;
+
+            FragmentHelper.forceNonEmptyElement(element);
+            FragmentHelper.forceNonEmptyElement(reference);
+
+            var mspaceWidth = 20, mspaceHeight = 40, mspaceDepth = 30;
+            var marginLeft = 10, marginRight = 15, marginTop = 20, marginBottom = 25;
+            Array.from(element.children).forEach(mrow => {
+                mrow.outerHTML = `<mspace width="${mspaceWidth}px" height="${mspaceHeight}px" depth='${mspaceDepth}px' style='background: blue; margin-left: ${marginLeft}px; margin-right: ${marginRight}px;  margin-top: ${marginTop}px; margin-bottom: ${marginBottom}px;'></mspace>`;
+            });
+
+            Array.from(reference.children).forEach(mrow => {
+                mrow.outerHTML = `<mspace width="${marginLeft+mspaceWidth+marginRight}px" height="${mspaceHeight+marginTop}px" depth='${mspaceDepth+marginBottom}px' style='background: green;'></mspace>`;
+            });
+
+            // Compare sizes.
+            compareSize(element, reference, epsilon);
+
+            // Compare children positions.
+            var elementBox = element.getBoundingClientRect();
+            var referenceBox = reference.getBoundingClientRect();
+            for (var i = 0; i < element.children.length; i++) {
+                var childBox = element.children[i].getBoundingClientRect();
+                var referenceChildBox = reference.children[i].getBoundingClientRect();
+                assert_approx_equals(childBox.width + marginLeft + marginRight, referenceChildBox.width, epsilon, "inline size (child ${i})");
+                assert_approx_equals(childBox.height + marginTop + marginBottom, referenceChildBox.height, epsilon, "block size (child ${i})");
+
+                assert_approx_equals(childBox.left - marginLeft - elementBox.left,
+                                     referenceChildBox.left - referenceBox.left,
+                                     epsilon,
+                                     `inline position (child ${i})`);
+                assert_approx_equals(childBox.top - marginTop - elementBox.top,
+                                     referenceChildBox.top - referenceBox.top,
+                                     epsilon,
+                                     `block position (child ${i})`);
+            }
+
+            // Compare preferred widths.
+            assert_approx_equals(elementShrinkWrapContainer.offsetWidth, referenceShrinkWrapContainer.offsetWidth, epsilon, "preferred width");
+
+        }, `Margin properties on the children of ${tag}`);
+    }
+
+    done();
+  }
+</script>
+</head>
+<body>
+  <div id="log"></div>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/padding-border-margin/padding-002-expected.txt b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/padding-border-margin/padding-002-expected.txt
new file mode 100644
index 0000000..31be557
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/padding-border-margin/padding-002-expected.txt
@@ -0,0 +1,52 @@
+This is a testharness.js-based test.
+PASS Padding properties on maction
+PASS Padding properties on maction (rtl)
+PASS Padding properties on menclose
+PASS Padding properties on menclose (rtl)
+PASS Padding properties on merror
+PASS Padding properties on merror (rtl)
+PASS Padding properties on mfrac
+PASS Padding properties on mfrac (rtl)
+PASS Padding properties on mi
+PASS Padding properties on mi (rtl)
+PASS Padding properties on mmultiscripts
+PASS Padding properties on mmultiscripts (rtl)
+PASS Padding properties on mn
+PASS Padding properties on mn (rtl)
+PASS Padding properties on mo
+PASS Padding properties on mo (rtl)
+PASS Padding properties on mover
+PASS Padding properties on mover (rtl)
+PASS Padding properties on mpadded
+PASS Padding properties on mpadded (rtl)
+PASS Padding properties on mphantom
+PASS Padding properties on mphantom (rtl)
+FAIL Padding properties on mroot assert_true: mroot is supported expected true got false
+FAIL Padding properties on mroot (rtl) assert_true: mroot is supported expected true got false
+PASS Padding properties on mrow
+PASS Padding properties on mrow (rtl)
+PASS Padding properties on ms
+PASS Padding properties on ms (rtl)
+PASS Padding properties on mspace
+FAIL Padding properties on msqrt assert_true: msqrt is supported expected true got false
+FAIL Padding properties on msqrt (rtl) assert_true: msqrt is supported expected true got false
+PASS Padding properties on mstyle
+PASS Padding properties on mstyle (rtl)
+PASS Padding properties on msub
+PASS Padding properties on msub (rtl)
+PASS Padding properties on msubsup
+PASS Padding properties on msubsup (rtl)
+PASS Padding properties on msup
+PASS Padding properties on msup (rtl)
+PASS Padding properties on mtable
+PASS Padding properties on mtable (rtl)
+PASS Padding properties on mtext
+PASS Padding properties on mtext (rtl)
+PASS Padding properties on munder
+PASS Padding properties on munder (rtl)
+PASS Padding properties on munderover
+PASS Padding properties on munderover (rtl)
+PASS Padding properties on semantics
+PASS Padding properties on semantics (rtl)
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/mathml/support/mathml-fragments.js b/third_party/blink/web_tests/external/wpt/mathml/support/mathml-fragments.js
index 8376ccf..7e2113e 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/support/mathml-fragments.js
+++ b/third_party/blink/web_tests/external/wpt/mathml/support/mathml-fragments.js
@@ -132,6 +132,14 @@
                  tag == "mtd");
     },
 
+    isTokenElement: function(tag) {
+        return (tag == "mi" ||
+                tag == "mtext" ||
+                tag == "mo" ||
+                tag == "mn" ||
+                tag == "ms")
+    },
+
     isEmpty: function(tag) {
         return tag === "mspace" || tag == "mprescripts" || tag == "none";
     },
diff --git a/third_party/blink/web_tests/external/wpt/service-workers/service-worker/fetch-event-handled.https-expected.txt b/third_party/blink/web_tests/external/wpt/service-workers/service-worker/fetch-event-handled.https-expected.txt
index 42484619..9b071f9 100644
--- a/third_party/blink/web_tests/external/wpt/service-workers/service-worker/fetch-event-handled.https-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/service-workers/service-worker/fetch-event-handled.https-expected.txt
@@ -1,6 +1,7 @@
 This is a testharness.js-based test.
 PASS global setup
-FAIL FetchEvent.handled should resolve when respondWith() is not called assert_equals: expected "RESOLVED" but got "FAILED"
+FAIL FetchEvent.handled should resolve when respondWith() is not called for a navigation request assert_equals: expected "RESOLVED" but got "FAILED"
+FAIL FetchEvent.handled should resolve when respondWith() is not called for a sub-resource request assert_equals: expected "RESOLVED" but got "FAILED"
 FAIL FetchEvent.handled should reject when respondWith() is not called and the event is canceled assert_equals: expected "REJECTED" but got "FAILED"
 FAIL FetchEvent.handled should resolve when the promise provided to respondWith() is resolved assert_equals: expected "RESOLVED" but got "FAILED"
 FAIL FetchEvent.handled should reject when the promise provided to respondWith() is resolved to an invalid response assert_equals: expected "REJECTED" but got "FAILED"
diff --git a/third_party/blink/web_tests/external/wpt/service-workers/service-worker/fetch-event-handled.https.html b/third_party/blink/web_tests/external/wpt/service-workers/service-worker/fetch-event-handled.https.html
index 2d6f6c86..89f3d79a 100644
--- a/third_party/blink/web_tests/external/wpt/service-workers/service-worker/fetch-event-handled.https.html
+++ b/third_party/blink/web_tests/external/wpt/service-workers/service-worker/fetch-event-handled.https.html
@@ -30,14 +30,21 @@
       await service_worker_unregister_and_register(t, script, scope);
   worker = registration.installing;
   await wait_for_state(t, worker, 'activated');
-  frame = await with_iframe(scope);
 }, 'global setup');
 
 promise_test(async (t) => {
+  frame = await with_iframe(scope);
+  const message = await wait_for_message_from_worker();
+  assert_equals(message, 'RESOLVED');
+}, 'FetchEvent.handled should resolve when respondWith() is not called for a' +
+    ' navigation request');
+
+promise_test(async (t) => {
   frame.contentWindow.fetch('dummy.txt?respondWith-not-called');
   const message = await wait_for_message_from_worker();
   assert_equals(message, 'RESOLVED');
-}, 'FetchEvent.handled should resolve when respondWith() is not called');
+}, 'FetchEvent.handled should resolve when respondWith() is not called for a' +
+    ' sub-resource request');
 
 promise_test(async (t) => {
   frame.contentWindow.fetch(
diff --git a/third_party/blink/web_tests/external/wpt/service-workers/service-worker/resources/fetch-event-handled-worker.js b/third_party/blink/web_tests/external/wpt/service-workers/service-worker/resources/fetch-event-handled-worker.js
index 4af58e2..0dc6de0 100644
--- a/third_party/blink/web_tests/external/wpt/service-workers/service-worker/resources/fetch-event-handled-worker.js
+++ b/third_party/blink/web_tests/external/wpt/service-workers/service-worker/resources/fetch-event-handled-worker.js
@@ -9,7 +9,9 @@
 }
 
 self.addEventListener('fetch', function(event) {
-  const clientId = event.clientId;
+  const clientId = (event.request.mode === 'navigate') ?
+      event.resultingClientId : event.clientId;
+
   try {
     event.handled.then(() => {
       send_message_to_client('RESOLVED', clientId);
diff --git a/third_party/blink/web_tests/external/wpt/svg/animations/svglength-additive-by-8.html b/third_party/blink/web_tests/external/wpt/svg/animations/svglength-additive-by-8.html
index c61cb65..459fc73 100644
--- a/third_party/blink/web_tests/external/wpt/svg/animations/svglength-additive-by-8.html
+++ b/third_party/blink/web_tests/external/wpt/svg/animations/svglength-additive-by-8.html
@@ -13,8 +13,8 @@
 <!-- an1: Change width from 10 to 50 in 4s -->
 <!-- an2: Change width from 10 to 100 in 4s starting at 5s -->
 <rect width="10" height="100" fill="green">
-    <animate id="an1" attributeType="XML" attributeName="width" fill="remove" by="calc(4% + 8)" begin="0s" dur="4s"/>
-        <animate id="an2" attributeType="XML" attributeName="width" additive="replace" fill="freeze" by="calc(10% + 10)" begin="5s" dur="4s"/>
+    <animate id="an1" attributeType="XML" attributeName="width" fill="remove" by="calc(4% + 8px)" begin="0s" dur="4s"/>
+        <animate id="an2" attributeType="XML" attributeName="width" additive="replace" fill="freeze" by="calc(10% + 10px)" begin="5s" dur="4s"/>
 </rect>
 
 </svg>
@@ -73,4 +73,4 @@
 
 window.animationStartsImmediately = true;
 
-</script>
\ No newline at end of file
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/svg/painting/parsing/stroke-dasharray-invalid.svg b/third_party/blink/web_tests/external/wpt/svg/painting/parsing/stroke-dasharray-invalid.svg
index 0c356b6..53a9640c 100644
--- a/third_party/blink/web_tests/external/wpt/svg/painting/parsing/stroke-dasharray-invalid.svg
+++ b/third_party/blink/web_tests/external/wpt/svg/painting/parsing/stroke-dasharray-invalid.svg
@@ -17,6 +17,9 @@
 test_invalid_value("stroke-dasharray", "none 10px");
 test_invalid_value("stroke-dasharray", "20px / 30px");
 test_invalid_value("stroke-dasharray", "-40px");
+test_invalid_value("stroke-dasharray", "calc(2px + 3)");
+test_invalid_value("stroke-dasharray", "calc(10% + 5)");
+test_invalid_value("stroke-dasharray", "calc(40 + calc(3px + 6%))");
 
   ]]></script>
 </svg>
diff --git a/third_party/blink/web_tests/external/wpt/svg/painting/parsing/stroke-dasharray-valid.svg b/third_party/blink/web_tests/external/wpt/svg/painting/parsing/stroke-dasharray-valid.svg
index e47ebc62..9326118c 100644
--- a/third_party/blink/web_tests/external/wpt/svg/painting/parsing/stroke-dasharray-valid.svg
+++ b/third_party/blink/web_tests/external/wpt/svg/painting/parsing/stroke-dasharray-valid.svg
@@ -23,6 +23,9 @@
 test_valid_value("stroke-dasharray", "10pt 20% 30pc 40in", "10pt, 20%, 30pc, 40in");
 test_valid_value("stroke-dasharray", "10vmin, 20vmax, 30em, 40ex");
 test_valid_value("stroke-dasharray", "0, 5", ["0, 5", "0px, 5px"]); // Edge/Safari serialize numbers as lengths.
+test_valid_value("stroke-dasharray", "calc(3)");
+test_valid_value("stroke-dasharray", "calc(2 + 1)", "calc(3)");
+test_valid_value("stroke-dasharray", "calc(2 + (7 - 5))", "calc(4)");
 
   ]]></script>
 </svg>
diff --git a/third_party/blink/web_tests/external/wpt/svg/painting/parsing/stroke-dashoffset-invalid.svg b/third_party/blink/web_tests/external/wpt/svg/painting/parsing/stroke-dashoffset-invalid.svg
index 64e2eec7..2040355 100644
--- a/third_party/blink/web_tests/external/wpt/svg/painting/parsing/stroke-dashoffset-invalid.svg
+++ b/third_party/blink/web_tests/external/wpt/svg/painting/parsing/stroke-dashoffset-invalid.svg
@@ -17,6 +17,9 @@
 test_invalid_value("stroke-dashoffset", "-10.px");
 test_invalid_value("stroke-dashoffset", "30deg");
 test_invalid_value("stroke-dashoffset", "40px 50%");
+test_invalid_value("stroke-dashoffset", "calc(2px + 3)");
+test_invalid_value("stroke-dashoffset", "calc(10% + 5)");
+test_invalid_value("stroke-dashoffset", "calc(40 + calc(3px + 6%))");
 
   ]]></script>
 </svg>
diff --git a/third_party/blink/web_tests/external/wpt/svg/painting/parsing/stroke-dashoffset-valid-expected.txt b/third_party/blink/web_tests/external/wpt/svg/painting/parsing/stroke-dashoffset-valid-expected.txt
index a683e2f..2de3aab 100644
--- a/third_party/blink/web_tests/external/wpt/svg/painting/parsing/stroke-dashoffset-valid-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/svg/painting/parsing/stroke-dashoffset-valid-expected.txt
@@ -5,5 +5,8 @@
 FAIL e.style['stroke-dashoffset'] = "30" should set the property value assert_equals: serialization should be canonical expected "30px" but got "30"
 PASS e.style['stroke-dashoffset'] = "40Q" should set the property value
 PASS e.style['stroke-dashoffset'] = "calc(2em + 3ex)" should set the property value
+PASS e.style['stroke-dashoffset'] = "calc(3)" should set the property value
+PASS e.style['stroke-dashoffset'] = "calc(2 + 1)" should set the property value
+PASS e.style['stroke-dashoffset'] = "calc(2 + (7 - 5))" should set the property value
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/external/wpt/svg/painting/parsing/stroke-dashoffset-valid.svg b/third_party/blink/web_tests/external/wpt/svg/painting/parsing/stroke-dashoffset-valid.svg
index f34774e..fe7ba12c 100644
--- a/third_party/blink/web_tests/external/wpt/svg/painting/parsing/stroke-dashoffset-valid.svg
+++ b/third_party/blink/web_tests/external/wpt/svg/painting/parsing/stroke-dashoffset-valid.svg
@@ -19,6 +19,9 @@
 test_valid_value("stroke-dashoffset", "30", "30px");
 test_valid_value("stroke-dashoffset", "40Q", "40q");
 test_valid_value("stroke-dashoffset", "calc(2em + 3ex)");
+test_valid_value("stroke-dashoffset", "calc(3)");
+test_valid_value("stroke-dashoffset", "calc(2 + 1)", "calc(3)");
+test_valid_value("stroke-dashoffset", "calc(2 + (7 - 5))", "calc(4)");
 
   ]]></script>
 </svg>
diff --git a/third_party/blink/web_tests/external/wpt/svg/painting/parsing/stroke-width-invalid.svg b/third_party/blink/web_tests/external/wpt/svg/painting/parsing/stroke-width-invalid.svg
index 0d3f63d..2111e37 100644
--- a/third_party/blink/web_tests/external/wpt/svg/painting/parsing/stroke-width-invalid.svg
+++ b/third_party/blink/web_tests/external/wpt/svg/painting/parsing/stroke-width-invalid.svg
@@ -17,6 +17,9 @@
 test_invalid_value("stroke-width", "10px 20px");
 test_invalid_value("stroke-width", "-1px");
 test_invalid_value("stroke-width", "-10%");
+test_invalid_value("stroke-width", "calc(2px + 3)");
+test_invalid_value("stroke-width", "calc(10% + 5)");
+test_invalid_value("stroke-width", "calc(40 + calc(3px + 6%))");
 
   ]]></script>
 </svg>
diff --git a/third_party/blink/web_tests/external/wpt/svg/painting/parsing/stroke-width-valid-expected.txt b/third_party/blink/web_tests/external/wpt/svg/painting/parsing/stroke-width-valid-expected.txt
index 9ec8028..e9b028d 100644
--- a/third_party/blink/web_tests/external/wpt/svg/painting/parsing/stroke-width-valid-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/svg/painting/parsing/stroke-width-valid-expected.txt
@@ -6,5 +6,8 @@
 PASS e.style['stroke-width'] = "4%" should set the property value
 PASS e.style['stroke-width'] = "5vmin" should set the property value
 PASS e.style['stroke-width'] = "calc(50% + 60px)" should set the property value
+PASS e.style['stroke-width'] = "calc(3)" should set the property value
+PASS e.style['stroke-width'] = "calc(2 + 1)" should set the property value
+PASS e.style['stroke-width'] = "calc(2 + (7 - 5))" should set the property value
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/external/wpt/svg/painting/parsing/stroke-width-valid.svg b/third_party/blink/web_tests/external/wpt/svg/painting/parsing/stroke-width-valid.svg
index f9078128..1ee0449 100644
--- a/third_party/blink/web_tests/external/wpt/svg/painting/parsing/stroke-width-valid.svg
+++ b/third_party/blink/web_tests/external/wpt/svg/painting/parsing/stroke-width-valid.svg
@@ -20,6 +20,9 @@
 test_valid_value("stroke-width", "4%");
 test_valid_value("stroke-width", "5vmin");
 test_valid_value("stroke-width", "calc(50% + 60px)");
+test_valid_value("stroke-width", "calc(3)");
+test_valid_value("stroke-width", "calc(2 + 1)", "calc(3)");
+test_valid_value("stroke-width", "calc(2 + (7 - 5))", "calc(4)");
 
   ]]></script>
 </svg>
diff --git a/third_party/blink/web_tests/external/wpt/webrtc/simulcast/.eslintignore b/third_party/blink/web_tests/external/wpt/webrtc/simulcast/.eslintignore
new file mode 100644
index 0000000..90fee69
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webrtc/simulcast/.eslintignore
@@ -0,0 +1 @@
+sdp/
diff --git a/third_party/blink/web_tests/external/wpt/webrtc/simulcast/basic.https.html b/third_party/blink/web_tests/external/wpt/webrtc/simulcast/basic.https.html
new file mode 100644
index 0000000..ae2e7ee
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webrtc/simulcast/basic.https.html
@@ -0,0 +1,56 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>RTCPeerConnection Simulcast Tests</title>
+<script src="../third_party/sdp/sdp.js"></script>
+<script src="simulcast.js"></script>
+<script src="../RTCPeerConnection-helper.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+promise_test(async t => {
+  const rids = [0, 1, 2];
+  const pc1 = new RTCPeerConnection();
+  t.add_cleanup(() => pc1.close());
+  const pc2 = new RTCPeerConnection();
+  t.add_cleanup(() => pc2.close());
+
+  exchangeIceCandidates(pc1, pc2);
+
+  const metadataToBeLoaded = [];
+  pc2.ontrack = (e) => {
+    const stream = e.streams[0];
+    const v = document.createElement('video');
+    v.autoplay = true;
+    v.srcObject = stream;
+    v.id = stream.id
+    metadataToBeLoaded.push(new Promise((resolve) => {
+        v.addEventListener('loadedmetadata', () => {
+            resolve();
+        });
+    }));
+  };
+
+  // use getUserMedia as getNoiseStream does not have enough entropy to ramp-up.
+  const stream = await navigator.mediaDevices.getUserMedia({video: {width: 1280, height: 720}});
+  t.add_cleanup(() => stream.getTracks().forEach(track => track.stop()));
+  pc1.addTransceiver(stream.getVideoTracks()[0], {
+    streams: [stream],
+    sendEncodings: rids.map(rid => {rid}),
+  });
+
+  const offer = await pc1.createOffer();
+  await pc1.setLocalDescription(offer),
+  await pc2.setRemoteDescription({
+    type: 'offer',
+    sdp: swapRidAndMidExtensionsInSimulcastOffer(offer, rids),
+  });
+  const answer = await pc2.createAnswer();
+  await pc2.setLocalDescription(answer);
+  await pc1.setRemoteDescription({
+    type: 'answer',
+    sdp: swapRidAndMidExtensionsInSimulcastAnswer(answer, pc1.localDescription, rids),
+  });
+  assert_equals(metadataToBeLoaded.length, 3);
+  return Promise.all(metadataToBeLoaded);
+}, 'Basic simulcast setup with three spatial layers');
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/webrtc/simulcast/simulcast.js b/third_party/blink/web_tests/external/wpt/webrtc/simulcast/simulcast.js
new file mode 100644
index 0000000..ed08ddc
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webrtc/simulcast/simulcast.js
@@ -0,0 +1,77 @@
+'use strict';
+/* Helper functions to munge SDP and split the sending track into
+ * separate tracks on the receiving end. This can be done in a number
+ * of ways, the one used here uses the fact that the MID and RID header
+ * extensions which are used for packet routing share the same wire
+ * format. The receiver interprets the rids from the sender as mids
+ * which allows receiving the different spatial resolutions on separate
+ * m-lines and tracks.
+ */
+const extensionsToFilter = [
+  'urn:ietf:params:rtp-hdrext:sdes:mid',
+  'urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id',
+  'urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id',
+];
+
+function swapRidAndMidExtensionsInSimulcastOffer(offer, rids) {
+  const sections = SDPUtils.splitSections(offer.sdp);
+  const dtls = SDPUtils.getDtlsParameters(sections[1], sections[0]);
+  const ice = SDPUtils.getIceParameters(sections[1], sections[0]);
+  const rtpParameters = SDPUtils.parseRtpParameters(sections[1]);
+
+  // The gist of this hack is that rid and mid have the same wire format.
+  const rid = rtpParameters.headerExtensions.find(ext => ext.uri === 'urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id');
+  rtpParameters.headerExtensions = rtpParameters.headerExtensions.filter(ext => {
+    return !extensionsToFilter.includes(ext.uri);
+  });
+  // This tells the other side that the RID packets are actually mids.
+  rtpParameters.headerExtensions.push({id: rid.id, uri: 'urn:ietf:params:rtp-hdrext:sdes:mid', direction: 'sendrecv'});
+
+  // Filter rtx as we have no way to (re)interpret rrid.
+  // Not doing this makes probing use RTX, it's not understood and ramp-up is slower.
+  rtpParameters.codecs = rtpParameters.codecs.filter(c => c.name.toUpperCase() !== 'RTX');
+
+  let sdp = SDPUtils.writeSessionBoilerplate() +
+    SDPUtils.writeDtlsParameters(dtls, 'actpass') +
+    SDPUtils.writeIceParameters(ice) +
+    'a=group:BUNDLE ' + rids.join(' ') + '\r\n';
+  const baseRtpDescription = SDPUtils.writeRtpDescription('video', rtpParameters);
+  rids.forEach(rid => {
+    sdp += baseRtpDescription +
+        'a=mid:' + rid + '\r\n' +
+        'a=msid:rid-' + rid + ' rid-' + rid + '\r\n';
+  });
+  return sdp;
+}
+
+function swapRidAndMidExtensionsInSimulcastAnswer(answer, localDescription, rids) {
+  const sections = SDPUtils.splitSections(answer.sdp);
+  const dtls = SDPUtils.getDtlsParameters(sections[1], sections[0]);
+  const ice = SDPUtils.getIceParameters(sections[1], sections[0]);
+  const rtpParameters = SDPUtils.parseRtpParameters(sections[1]);
+
+  rtpParameters.headerExtensions = rtpParameters.headerExtensions.filter(ext => {
+    return !extensionsToFilter.includes(ext.uri);
+  });
+  const localMid = SDPUtils.getMid(SDPUtils.splitSections(localDescription.sdp)[1]);
+  let sdp = SDPUtils.writeSessionBoilerplate() +
+    SDPUtils.writeDtlsParameters(dtls, 'active') +
+    SDPUtils.writeIceParameters(ice) +
+    'a=group:BUNDLE ' + localMid + '\r\n';
+  sdp += SDPUtils.writeRtpDescription('video', rtpParameters);
+  sdp += 'a=mid:' + localMid + '\r\n';
+
+  rids.forEach(rid => {
+    sdp += 'a=rid:' + rid + ' recv\r\n';
+  });
+  sdp += 'a=simulcast:recv ' + rids.join(';') + '\r\n';
+
+  // Re-add headerextensions we filtered.
+  const headerExtensions = SDPUtils.parseRtpParameters(SDPUtils.splitSections(localDescription.sdp)[1]).headerExtensions;
+  headerExtensions.forEach(ext => {
+    if (extensionsToFilter.includes(ext.uri)) {
+      sdp += 'a=extmap:' + ext.id + ' ' + ext.uri + '\r\n';
+    }
+  });
+  return sdp;
+}
diff --git a/third_party/blink/web_tests/fast/css/getComputedStyle/computed-style-listing-expected.txt b/third_party/blink/web_tests/fast/css/getComputedStyle/computed-style-listing-expected.txt
index 82851a25..f945b4f 100644
--- a/third_party/blink/web_tests/fast/css/getComputedStyle/computed-style-listing-expected.txt
+++ b/third_party/blink/web_tests/fast/css/getComputedStyle/computed-style-listing-expected.txt
@@ -3,7 +3,6 @@
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
 -webkit-app-region: none
--webkit-appearance: none
 -webkit-border-horizontal-spacing: 0px
 -webkit-border-image: none
 -webkit-border-vertical-spacing: 0px
@@ -63,6 +62,7 @@
 animation-name: none
 animation-play-state: running
 animation-timing-function: ease
+appearance: none
 backdrop-filter: none
 backface-visibility: visible
 background-attachment: scroll
diff --git a/third_party/blink/web_tests/fast/css/getComputedStyle/computed-style-without-renderer-listing-expected.txt b/third_party/blink/web_tests/fast/css/getComputedStyle/computed-style-without-renderer-listing-expected.txt
index e814287..14ef84b5 100644
--- a/third_party/blink/web_tests/fast/css/getComputedStyle/computed-style-without-renderer-listing-expected.txt
+++ b/third_party/blink/web_tests/fast/css/getComputedStyle/computed-style-without-renderer-listing-expected.txt
@@ -3,7 +3,6 @@
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
 -webkit-app-region: none
--webkit-appearance: none
 -webkit-border-horizontal-spacing: 0px
 -webkit-border-image: none
 -webkit-border-vertical-spacing: 0px
@@ -63,6 +62,7 @@
 animation-name: none
 animation-play-state: running
 animation-timing-function: ease
+appearance: none
 backdrop-filter: none
 backface-visibility: visible
 background-attachment: scroll
diff --git a/third_party/blink/web_tests/payments/payment-request-interface.html b/third_party/blink/web_tests/payments/payment-request-interface.html
index 556ec028..d3fcae0c 100644
--- a/third_party/blink/web_tests/payments/payment-request-interface.html
+++ b/third_party/blink/web_tests/payments/payment-request-interface.html
@@ -273,84 +273,84 @@
     return promise_rejects_dom(t, 'InvalidStateError', new PaymentRequest([{'supportedMethods': 'foo'}], buildDetails()).abort());
 }, 'abort() without show() should reject with error');
 
-generate_tests(assert_throws, [
-    ['PaymentRequest constructor should throw for incorrect parameter types.', new TypeError(), function() {
+generate_tests(assert_throws_js, [
+    ['PaymentRequest constructor should throw for incorrect parameter types.', TypeError, function() {
         new PaymentRequest('', '', '')
     }],
-    ['PaymentRequest constructor should throw for undefined required parameters.', new TypeError(), function() {
+    ['PaymentRequest constructor should throw for undefined required parameters.', TypeError, function() {
         new PaymentRequest(undefined, undefined)
     }],
-    ['PaymentRequest constructor should throw for null required parameter.', new TypeError(), function() {
+    ['PaymentRequest constructor should throw for null required parameter.', TypeError, function() {
         new PaymentRequest(null, null)
     }],
-    ['Empty list of supported payment method identifiers should throw TypeError.', new TypeError(), function() {
+    ['Empty list of supported payment method identifiers should throw TypeError.', TypeError, function() {
         new PaymentRequest([], buildDetails())
     }],
-    ['Empty supported payment method identifier should throw RangeError.', new RangeError(), function() {
+    ['Empty supported payment method identifier should throw RangeError.', RangeError, function() {
         new PaymentRequest([{'supportedMethods': ''}], buildDetails())
     }],
-    ['Absence of total should throw TypeError.', new TypeError(), function() {
+    ['Absence of total should throw TypeError.', TypeError, function() {
         new PaymentRequest([{'supportedMethods': 'foo'}], {'displayItems': [buildItem()]})
     }],
-    ['Negative total value should throw a TypeError.', new TypeError(), function() {
+    ['Negative total value should throw a TypeError.', TypeError, function() {
         new PaymentRequest([{'supportedMethods': 'foo'}], buildDetails('total', {'value': '-0.01'}))
     }],
-    ['Negative total value in PaymentDetailsModifier should throw a TypeError.', new TypeError(), function() {
+    ['Negative total value in PaymentDetailsModifier should throw a TypeError.', TypeError, function() {
         new PaymentRequest([{'supportedMethods': 'foo'}], {'total': buildItem(), 'modifiers': [{'supportedMethods': 'foo', 'total': buildItem({'value': '-0.01'})}]})
     }],
-    ['Undefined supportedMethods in modifiers should throw TypeError.', new TypeError(), function() {
+    ['Undefined supportedMethods in modifiers should throw TypeError.', TypeError, function() {
         new PaymentRequest([{'supportedMethods': 'foo'}], {'total': buildItem(), 'modifiers': [{'supportedMethods': undefined}]})
     }],
-    ['Empty supportedMethods in modifiers should throw RangeError.', new RangeError(), function() {
+    ['Empty supportedMethods in modifiers should throw RangeError.', RangeError, function() {
         new PaymentRequest([{'supportedMethods': 'foo'}], {'total': buildItem(), 'modifiers': [{'supportedMethods': ''}]})
     }],
-    ['Absence of supportedMethods in modifiers should throw TypeError.', new TypeError(), function() {
+    ['Absence of supportedMethods in modifiers should throw TypeError.', TypeError, function() {
         new PaymentRequest([{'supportedMethods': 'foo'}], {'total': buildItem(), 'modifiers': [{'total': buildItem()}]})
     }],
-    ['Empty details should throw', new TypeError(), function() {
+    ['Empty details should throw', TypeError, function() {
         new PaymentRequest([{'supportedMethods': 'foo'}], {})
     }],
-    ['Null items should throw', new TypeError(), function() {
+    ['Null items should throw', TypeError, function() {
         new PaymentRequest([{'supportedMethods': 'foo'}], {'total': buildItem(), 'displayItems': null});
     }],
-    ['Null shipping options should throw', new TypeError(), function() {
+    ['Null shipping options should throw', TypeError, function() {
         new PaymentRequest([{'supportedMethods': 'foo'}], {'total': buildItem(), 'displayItems': [buildItem()], 'shippingOptions': null});
     }],
-    ['Undefined PaymentShippingType value for shppingType should throw a TypeError', new TypeError(), function() {
+    ['Undefined PaymentShippingType value for shppingType should throw a TypeError', TypeError, function() {
         var request = new PaymentRequest([{'supportedMethods': 'foo'}], buildDetails(), {'requestShipping': true, 'shippingType': 'invalid'});
     }],
-    ['Null for shppingType should throw a TypeError', new TypeError(), function() {
+    ['Null for shppingType should throw a TypeError', TypeError, function() {
         var request = new PaymentRequest([{'supportedMethods': 'foo'}], buildDetails(), {'requestShipping': true, 'shippingType': null});
     }],
-    ['Array value for shppingType should throw a TypeError', new TypeError(), function() {
+    ['Array value for shppingType should throw a TypeError', TypeError, function() {
         var request = new PaymentRequest([{'supportedMethods': 'foo'}], buildDetails(), {'requestShipping': true, 'shippingType': []});
     }],
-    ['Object value for shppingType should throw a TypeError', new TypeError(), function() {
+    ['Object value for shppingType should throw a TypeError', TypeError, function() {
         var request = new PaymentRequest([{'supportedMethods': 'foo'}], buildDetails(), {'requestShipping': true, 'shippingType': {}});
     }],
-    ['Numeric value for shppingType should throw a TypeError', new TypeError(), function() {
+    ['Numeric value for shppingType should throw a TypeError', TypeError, function() {
         var request = new PaymentRequest([{'supportedMethods': 'foo'}], buildDetails(), {'requestShipping': true, 'shippingType': 0});
     }],
 
     // Payment method specific data should be a JSON-serializable object.
-    ['String value for payment method specific data parameter should throw', new TypeError(), function() {
+    ['String value for payment method specific data parameter should throw', TypeError, function() {
         new PaymentRequest([{'supportedMethods': 'foo', 'data': 'foo'}], buildDetails(), {})
     }],
-    ['Numeric value for payment method specific data parameter should throw', new TypeError(), function() {
+    ['Numeric value for payment method specific data parameter should throw', TypeError, function() {
         new PaymentRequest([{'supportedMethods': 'foo', 'data': 42}], buildDetails(), {})
     }],
-    ['Infinite JSON value for one of the payment method specific data pieces should throw', new TypeError(), function() {
+    ['Infinite JSON value for one of the payment method specific data pieces should throw', TypeError, function() {
         var infiniteData = {'foo': {}};
         infiniteData.foo = infiniteData;
         new PaymentRequest([{'supportedMethods': 'foo', 'data': infiniteData}], buildDetails())
     }],
-    ['Null for payment method specific data parameter should throw', new TypeError(), function() {
+    ['Null for payment method specific data parameter should throw', TypeError, function() {
         new PaymentRequest([{'supportedMethods': 'foo', 'data': null}], buildDetails())
     }],
-    ['Empty string for payment method specific data parameter should throw', new TypeError(), function() {
+    ['Empty string for payment method specific data parameter should throw', TypeError, function() {
         new PaymentRequest([{'supportedMethods': 'foo', 'data': ''}], buildDetails())
     }],
-    ['PaymentRequest constructor should throw for invalid "basic-card" parameters.', new TypeError(), function() {
+    ['PaymentRequest constructor should throw for invalid "basic-card" parameters.', TypeError, function() {
         new PaymentRequest([{'supportedMethods': 'basic-card', 'data': {'supportedTypes': 0, 'supportedNetworks': 'foo'}}], buildDetails())
     }]
 ]);
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/external/wpt/fetch/origin/assorted.window-expected.txt b/third_party/blink/web_tests/platform/mac-mac10.10/external/wpt/fetch/origin/assorted.window-expected.txt
new file mode 100644
index 0000000..5f71302
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.10/external/wpt/fetch/origin/assorted.window-expected.txt
@@ -0,0 +1,49 @@
+Content-Type: text/plain
+This is a testharness.js-based test.
+PASS Origin header and 308 redirect
+PASS Origin header and GET navigation
+PASS Origin header and POST navigation
+PASS Origin header and POST same-origin navigation with Referrer-Policy no-referrer
+FAIL Origin header and POST same-origin fetch no-cors mode with Referrer-Policy no-referrer assert_equals: expected "null" but got "http://web-platform.test:8001"
+FAIL Origin header and POST same-origin fetch cors mode with Referrer-Policy no-referrer assert_equals: expected "null" but got "http://web-platform.test:8001"
+PASS Origin header and GET same-origin fetch cors mode with Referrer-Policy no-referrer
+PASS Origin header and POST cross-origin navigation with Referrer-Policy no-referrer
+FAIL Origin header and POST cross-origin fetch no-cors mode with Referrer-Policy no-referrer assert_equals: expected "null" but got "http://web-platform.test:8001"
+PASS Origin header and POST cross-origin fetch cors mode with Referrer-Policy no-referrer
+PASS Origin header and GET cross-origin fetch cors mode with Referrer-Policy no-referrer
+PASS Origin header and POST same-origin navigation with Referrer-Policy same-origin
+PASS Origin header and POST same-origin fetch no-cors mode with Referrer-Policy same-origin
+PASS Origin header and POST same-origin fetch cors mode with Referrer-Policy same-origin
+PASS Origin header and GET same-origin fetch cors mode with Referrer-Policy same-origin
+PASS Origin header and POST cross-origin navigation with Referrer-Policy same-origin
+FAIL Origin header and POST cross-origin fetch no-cors mode with Referrer-Policy same-origin assert_equals: expected "null" but got "http://web-platform.test:8001"
+PASS Origin header and POST cross-origin fetch cors mode with Referrer-Policy same-origin
+PASS Origin header and GET cross-origin fetch cors mode with Referrer-Policy same-origin
+PASS Origin header and POST same-origin navigation with Referrer-Policy origin-when-cross-origin
+PASS Origin header and POST same-origin fetch no-cors mode with Referrer-Policy origin-when-cross-origin
+PASS Origin header and POST same-origin fetch cors mode with Referrer-Policy origin-when-cross-origin
+PASS Origin header and GET same-origin fetch cors mode with Referrer-Policy origin-when-cross-origin
+PASS Origin header and POST cross-origin navigation with Referrer-Policy origin-when-cross-origin
+PASS Origin header and POST cross-origin fetch no-cors mode with Referrer-Policy origin-when-cross-origin
+PASS Origin header and POST cross-origin fetch cors mode with Referrer-Policy origin-when-cross-origin
+PASS Origin header and GET cross-origin fetch cors mode with Referrer-Policy origin-when-cross-origin
+PASS Origin header and POST same-origin navigation with Referrer-Policy no-referrer-when-downgrade
+PASS Origin header and POST same-origin fetch no-cors mode with Referrer-Policy no-referrer-when-downgrade
+PASS Origin header and POST same-origin fetch cors mode with Referrer-Policy no-referrer-when-downgrade
+PASS Origin header and GET same-origin fetch cors mode with Referrer-Policy no-referrer-when-downgrade
+PASS Origin header and POST cross-origin navigation with Referrer-Policy no-referrer-when-downgrade
+PASS Origin header and POST cross-origin fetch no-cors mode with Referrer-Policy no-referrer-when-downgrade
+PASS Origin header and POST cross-origin fetch cors mode with Referrer-Policy no-referrer-when-downgrade
+PASS Origin header and GET cross-origin fetch cors mode with Referrer-Policy no-referrer-when-downgrade
+PASS Origin header and POST same-origin navigation with Referrer-Policy unsafe-url
+PASS Origin header and POST same-origin fetch no-cors mode with Referrer-Policy unsafe-url
+PASS Origin header and POST same-origin fetch cors mode with Referrer-Policy unsafe-url
+PASS Origin header and GET same-origin fetch cors mode with Referrer-Policy unsafe-url
+PASS Origin header and POST cross-origin navigation with Referrer-Policy unsafe-url
+PASS Origin header and POST cross-origin fetch no-cors mode with Referrer-Policy unsafe-url
+PASS Origin header and POST cross-origin fetch cors mode with Referrer-Policy unsafe-url
+PASS Origin header and GET cross-origin fetch cors mode with Referrer-Policy unsafe-url
+Harness: the test ran to completion.
+
+#EOF
+#EOF
diff --git a/third_party/blink/web_tests/platform/mac/editing/selection/continuations-with-move-caret-to-boundary-expected.txt b/third_party/blink/web_tests/platform/mac/editing/selection/continuations-with-move-caret-to-boundary-expected.txt
index a45b3538..2ca0d7c 100644
--- a/third_party/blink/web_tests/platform/mac/editing/selection/continuations-with-move-caret-to-boundary-expected.txt
+++ b/third_party/blink/web_tests/platform/mac/editing/selection/continuations-with-move-caret-to-boundary-expected.txt
@@ -1,6 +1,6 @@
 This is a testharness.js-based test.
 PASS Continuations across a block -20
-FAIL Continuations across a block -15 resources/testharness.js:2024:25)
+FAIL Continuations across a block -15 resources/testharness.js:1867:25)
 	 expected <style>* { font: 20px Ahem; }</style><p>^AAAAA</p><p>|BBBBB</p>,
 	 but got  <style>* { font: 20px Ahem; }</style><p>^AAAAA</p><p>BB|BBB</p>,
 	 sameupto <style>* { font: 20px Ahem; }</style><p>^AAAAA</p><p>
diff --git a/third_party/blink/web_tests/resources/idlharness.js b/third_party/blink/web_tests/resources/idlharness.js
index a430a34b..18b11ec 100644
--- a/third_party/blink/web_tests/resources/idlharness.js
+++ b/third_party/blink/web_tests/resources/idlharness.js
@@ -84,16 +84,33 @@
     .reduce(function(m, n) { return Math.min(m, n); });
 }
 
+// A helper to get the global of a Function object.  This is needed to determine
+// which global exceptions the function throws will come from.
+function globalOf(func)
+{
+    try {
+        // Use the fact that .constructor for a Function object is normally the
+        // Function constructor, which can be used to mint a new function in the
+        // right global.
+        return func.constructor("return this;")();
+    } catch (e) {
+    }
+    // If the above fails, because someone gave us a non-function, or a function
+    // with a weird proto chain or weird .constructor property, just fall back
+    // to 'self'.
+    return self;
+}
+
 function throwOrReject(a_test, operation, fn, obj, args, message, cb)
 {
     if (operation.idlType.generic !== "Promise") {
-        assert_throws_js(TypeError, function() {
+        assert_throws_js(globalOf(fn).TypeError, function() {
             fn.apply(obj, args);
         }, message);
         cb();
     } else {
         try {
-            promise_rejects(a_test, new TypeError(), fn.apply(obj, args), message).then(cb, cb);
+            promise_rejects_js(a_test, TypeError, fn.apply(obj, args), message).then(cb, cb);
         } catch (e){
             a_test.step(function() {
                 assert_unreached("Throws \"" + e + "\" instead of rejecting promise");
@@ -193,6 +210,12 @@
     this["implements"] = {};
     this["includes"] = {};
     this["inheritance"] = {};
+
+    /**
+     * Record of skipped IDL items, in case we later realize that they are a
+     * dependency (to retroactively process them).
+     */
+    this.skipped = new Map();
 };
 
 IdlArray.prototype.add_idls = function(raw_idls, options)
@@ -230,7 +253,11 @@
 
 IdlArray.prototype.add_dependency_idls = function(raw_idls, options)
 {
-    const parsed_idls = WebIDL2.parse(raw_idls);
+    return this.internal_add_dependency_idls(WebIDL2.parse(raw_idls), options);
+};
+
+IdlArray.prototype.internal_add_dependency_idls = function(parsed_idls, options)
+{
     const new_options = { only: [] }
 
     const all_deps = new Set();
@@ -244,22 +271,50 @@
         all_deps.add(k);
         this.includes[k].forEach(v => all_deps.add(v));
     });
-    this.partials.map(p => p.name).forEach(v => all_deps.add(v));
-    // Add the attribute idlTypes of all the nested members of all tested idls.
-    for (const obj of [this.members, this.partials]) {
-        const tested = Object.values(obj).filter(m => !m.untested && m.members);
-        for (const parsed of tested) {
-            for (const attr of Object.values(parsed.members).filter(m => !m.untested && m.type === 'attribute')) {
-                all_deps.add(attr.idlType.idlType);
-            }
+    this.partials.forEach(p => all_deps.add(p.name));
+    // Add 'TypeOfType' for each "typedef TypeOfType MyType;" entry.
+    Object.entries(this.members).forEach(([k, v]) => {
+        if (v instanceof IdlTypedef) {
+            let defs = v.idlType.union
+                ? v.idlType.idlType.map(t => t.idlType)
+                : [v.idlType.idlType];
+            defs.forEach(d => all_deps.add(d));
         }
-    }
+    });
+
+    // Add the attribute idlTypes of all the nested members of idls.
+    const attrDeps = parsedIdls => {
+        return parsedIdls.reduce((deps, parsed) => {
+            if (parsed.members) {
+                for (const attr of Object.values(parsed.members).filter(m => m.type === 'attribute')) {
+                    let attrType = attr.idlType;
+                    // Check for generic members (e.g. FrozenArray<MyType>)
+                    if (attrType.generic) {
+                        deps.add(attrType.generic);
+                        attrType = attrType.idlType;
+                    }
+                    deps.add(attrType.idlType);
+                }
+            }
+            if (parsed.base in this.members) {
+                attrDeps([this.members[parsed.base]]).forEach(dep => deps.add(dep));
+            }
+            return deps;
+        }, new Set());
+    };
+
+    const testedMembers = Object.values(this.members).filter(m => !m.untested && m.members);
+    attrDeps(testedMembers).forEach(dep => all_deps.add(dep));
+
+    const testedPartials = this.partials.filter(m => !m.untested && m.members);
+    attrDeps(testedPartials).forEach(dep => all_deps.add(dep));
+
 
     if (options && options.except && options.only) {
         throw new IdlHarnessError("The only and except options can't be used together.");
     }
 
-    const should_skip = name => {
+    const defined_or_untested = name => {
         // NOTE: Deps are untested, so we're lenient, and skip re-encountered definitions.
         // e.g. for 'idl' containing A:B, B:C, C:D
         //      array.add_idls(idl, {only: ['A','B']}).
@@ -268,9 +323,7 @@
         return name in this.members
             || this.is_excluded_by_options(name, options);
     }
-    // Record of skipped items, in case we later determine they are a dependency.
     // Maps name -> [parsed_idl, ...]
-    const skipped = new Map();
     const process = function(parsed) {
         var deps = [];
         if (parsed.name) {
@@ -284,13 +337,15 @@
         }
 
         deps = deps.filter(function(name) {
-            if (!name || should_skip(name) || !all_deps.has(name)) {
+            if (!name
+                || name === parsed.name && defined_or_untested(name)
+                || !all_deps.has(name)) {
                 // Flag as skipped, if it's not already processed, so we can
                 // come back to it later if we retrospectively call it a dep.
                 if (name && !(name in this.members)) {
-                    skipped.has(name)
-                        ? skipped.get(name).push(parsed)
-                        : skipped.set(name, [parsed]);
+                    this.skipped.has(name)
+                        ? this.skipped.get(name).push(parsed)
+                        : this.skipped.set(name, [parsed]);
                 }
                 return false;
             }
@@ -328,9 +383,9 @@
             }
 
             for (const deferred of follow_up) {
-                if (skipped.has(deferred)) {
-                    const next = skipped.get(deferred);
-                    skipped.delete(deferred);
+                if (this.skipped.has(deferred)) {
+                    const next = this.skipped.get(deferred);
+                    this.skipped.delete(deferred);
                     next.forEach(process);
                 }
             }
@@ -463,8 +518,7 @@
             break;
 
         case "callback":
-            // TODO
-            console.log("callback not yet supported");
+            this.members[parsed_idl.name] = new IdlCallback(parsed_idl);
             break;
 
         case "enum":
@@ -586,7 +640,7 @@
 
     // sequence types
     if (type.generic == "sequence" || type.generic == "FrozenArray") {
-        return this.is_json_type(idlType);
+        return this.is_json_type(idlType[0]);
     }
 
     if (typeof idlType != "string") { throw new Error("Unexpected type " + JSON.stringify(idlType)); }
@@ -694,11 +748,11 @@
         result = new Set(result);
     }
     if (exposed && exposed.length) {
-        var set = exposed[0].rhs.value;
+        const { rhs } = exposed[0];
         // Could be a list or a string.
-        if (typeof set == "string") {
-            set = [ set ];
-        }
+        const set = rhs.type === "identifier-list" ?
+            rhs.value.map(id => id.value) :
+            [ rhs.value ];
         result = new Set(set);
     }
     if (result && result.has("Worker")) {
@@ -771,10 +825,20 @@
             if (!(this.members[lhs] instanceof IdlInterface)) throw errStr + lhs + " is not an interface.";
             if (!(rhs in this.members)) throw errStr + rhs + " is undefined.";
             if (!(this.members[rhs] instanceof IdlInterface)) throw errStr + rhs + " is not an interface.";
-            this.members[rhs].members.forEach(function(member)
-            {
-                this.members[lhs].members.push(new IdlInterfaceMember(member));
-            }.bind(this));
+
+            if (this.members[rhs].members.length) {
+                test(function () {
+                    var clash = this.members[rhs].members.find(function(member) {
+                        return this.members[lhs].members.find(function(m) {
+                            return this.are_duplicate_members(m, member);
+                        }.bind(this));
+                    }.bind(this));
+                    this.members[rhs].members.forEach(function(member) {
+                        this.members[lhs].members.push(new IdlInterfaceMember(member));
+                    }.bind(this));
+                    assert_true(!clash, "member " + (clash && clash.name) + " is unique");
+                }.bind(this), lhs + " implements " + rhs + ": member names are unique");
+            }
         }.bind(this));
     }
     this["implements"] = {};
@@ -788,10 +852,23 @@
             if (!(this.members[lhs] instanceof IdlInterface)) throw errStr + lhs + " is not an interface.";
             if (!(rhs in this.members)) throw errStr + rhs + " is undefined.";
             if (!(this.members[rhs] instanceof IdlInterface)) throw errStr + rhs + " is not an interface.";
-            this.members[rhs].members.forEach(function(member)
-            {
-                this.members[lhs].members.push(new IdlInterfaceMember(member));
-            }.bind(this));
+
+            if (this.members[rhs].members.length) {
+                test(function () {
+                    var clash = this.members[rhs].members.find(function(member) {
+                        return this.members[lhs].members.find(function(m) {
+                            return this.are_duplicate_members(m, member);
+                        }.bind(this));
+                    }.bind(this));
+                    this.members[rhs].members.forEach(function(member) {
+                        assert_true(
+                            this.members[lhs].members.every(m => !this.are_duplicate_members(m, member)),
+                            "member " + member.name + " is unique");
+                        this.members[lhs].members.push(new IdlInterfaceMember(member));
+                    }.bind(this));
+                    assert_true(!clash, "member " + (clash && clash.name) + " is unique");
+                }.bind(this), lhs + " includes " + rhs + ": member names are unique");
+            }
         }.bind(this));
     }
     this["includes"] = {};
@@ -853,24 +930,27 @@
                 || this.members[parsed_idl.name] instanceof IdlDictionary
                 || this.members[parsed_idl.name] instanceof IdlNamespace);
 
+        // Ensure unique test name in case of multiple partials.
         let partialTestName = parsed_idl.name;
-        if (!parsed_idl.untested) {
-            // Ensure unique test name in case of multiple partials.
-            let partialTestCount = 1;
-            if (testedPartials.has(parsed_idl.name)) {
-                partialTestCount += testedPartials.get(parsed_idl.name);
-                partialTestName = `${partialTestName}[${partialTestCount}]`;
-            }
-            testedPartials.set(parsed_idl.name, partialTestCount);
+        let partialTestCount = 1;
+        if (testedPartials.has(parsed_idl.name)) {
+            partialTestCount += testedPartials.get(parsed_idl.name);
+            partialTestName = `${partialTestName}[${partialTestCount}]`;
+        }
+        testedPartials.set(parsed_idl.name, partialTestCount);
 
+        if (!parsed_idl.untested) {
             test(function () {
                 assert_true(originalExists, `Original ${parsed_idl.type} should be defined`);
 
-                var expected = IdlInterface;
+                var expected;
                 switch (parsed_idl.type) {
-                    case 'interface': expected = IdlInterface; break;
                     case 'dictionary': expected = IdlDictionary; break;
                     case 'namespace': expected = IdlNamespace; break;
+                    case 'interface':
+                    case 'interface mixin':
+                    default:
+                        expected = IdlInterface; break;
                 }
                 assert_true(
                     expected.prototype.isPrototypeOf(this.members[parsed_idl.name]),
@@ -916,14 +996,36 @@
                 this.members[parsed_idl.name].extAttrs.push(extAttr);
             }.bind(this));
         }
-        parsed_idl.members.forEach(function(member)
-        {
-            this.members[parsed_idl.name].members.push(new IdlInterfaceMember(member));
-        }.bind(this));
+        if (parsed_idl.members.length) {
+            test(function () {
+                var clash = parsed_idl.members.find(function(member) {
+                    return this.members[parsed_idl.name].members.find(function(m) {
+                        return this.are_duplicate_members(m, member);
+                    }.bind(this));
+                }.bind(this));
+                parsed_idl.members.forEach(function(member)
+                {
+                    this.members[parsed_idl.name].members.push(new IdlInterfaceMember(member));
+                }.bind(this));
+                assert_true(!clash, "member " + (clash && clash.name) + " is unique");
+            }.bind(this), `Partial ${parsed_idl.type} ${partialTestName}: member names are unique`);
+        }
     }.bind(this));
     this.partials = [];
 }
 
+IdlArray.prototype.are_duplicate_members = function(m1, m2) {
+    if (m1.name !== m2.name) {
+        return false;
+    }
+    if (m1.type === 'operation' && m2.type === 'operation'
+        && m1.arguments.length !== m2.arguments.length) {
+        // Method overload. TODO: Deep comparison of arguments.
+        return false;
+    }
+    return true;
+}
+
 IdlArray.prototype.assert_type_is = function(value, type)
 {
     if (type.idlType in this.members
@@ -984,7 +1086,7 @@
             // Nothing we can do.
             return;
         }
-        this.assert_type_is(value[0], type.idlType);
+        this.assert_type_is(value[0], type.idlType[0]);
         return;
     }
 
@@ -1004,11 +1106,11 @@
             // Nothing we can do.
             return;
         }
-        this.assert_type_is(value[0], type.idlType);
+        this.assert_type_is(value[0], type.idlType[0]);
         return;
     }
 
-    type = type.idlType;
+    type = Array.isArray(type.idlType) ? type.idlType[0] : type.idlType;
 
     switch(type)
     {
@@ -1105,6 +1207,10 @@
             assert_regexp_match(value, /^([\x00-\ud7ff\ue000-\uffff]|[\ud800-\udbff][\udc00-\udfff])*$/);
             return;
 
+        case "DataView":
+            assert_equals(typeof value, "DataView");
+            return;
+
         case "object":
             assert_in_array(typeof value, ["object", "function"], "wrong type: not object or function");
             return;
@@ -1138,9 +1244,13 @@
     {
         // TODO: Test when we actually have something to test this on
     }
+    else if (this.members[type] instanceof IdlCallback)
+    {
+        assert_equals(typeof value, "function");
+    }
     else
     {
-        throw new IdlHarnessError("Type " + type + " isn't an interface or dictionary");
+        throw new IdlHarnessError("Type " + type + " isn't an interface, callback or dictionary");
     }
 };
 
@@ -1225,7 +1335,7 @@
     this.members = obj.members.map(function(m){return new IdlInterfaceMember(m); });
     if (this.has_extended_attribute("Unforgeable")) {
         this.members
-            .filter(function(m) { return !m["static"] && (m.type == "attribute" || m.type == "operation"); })
+            .filter(function(m) { return m.special !== "static" && (m.type == "attribute" || m.type == "operation"); })
             .forEach(function(m) { return m.isUnforgeable = true; });
     }
 
@@ -1376,7 +1486,7 @@
         if (I.has_default_to_json_regular_operation()) {
             isDefault = true;
             I.members.forEach(function(m) {
-                if (!m.static && m.type == "attribute" && I.array.is_json_type(m.idlType)) {
+                if (m.special !== "static" && m.type == "attribute" && I.array.is_json_type(m.idlType)) {
                     map.set(m.name, m.idlType);
                 }
             });
@@ -1470,6 +1580,17 @@
     this.test_members();
 };
 
+// This supports both Constructor extended attributes and constructor
+// operations until all idl fragments have been updated.
+IdlInterface.prototype.constructors = function()
+{
+    var extendedAttributes = this.extAttrs
+        .filter(function(attr) { return attr.name == "Constructor"; });
+    var operations = this.members
+        .filter(function(m) { return m.type == "constructor"; });
+    return extendedAttributes.concat(operations);
+}
+
 IdlInterface.prototype.test_self = function()
 {
     subsetTestByKey(this.name, test, function()
@@ -1557,17 +1678,17 @@
                           "prototype of self's property " + format_value(this.name) + " is not Function.prototype");
         }
 
-        if (!this.has_extended_attribute("Constructor")) {
+        if (!this.constructors().length) {
             // "The internal [[Call]] method of the interface object behaves as
             // follows . . .
             //
             // "If I was not declared with a [Constructor] extended attribute,
             // then throw a TypeError."
             var interface_object = this.get_interface_object();
-            assert_throws_js(TypeError, function() {
+            assert_throws_js(globalOf(interface_object).TypeError, function() {
                 interface_object();
             }, "interface object didn't throw TypeError when called as a function");
-            assert_throws_js(TypeError, function() {
+            assert_throws_js(globalOf(interface_object).TypeError, function() {
                 new interface_object();
             }, "interface object didn't throw TypeError when called as a constructor");
         }
@@ -1592,8 +1713,7 @@
             assert_false(desc.enumerable, this.name + ".length should not be enumerable");
             assert_true(desc.configurable, this.name + ".length should be configurable");
 
-            var constructors = this.extAttrs
-                .filter(function(attr) { return attr.name == "Constructor"; });
+            var constructors = this.constructors();
             var expected_length = minOverloadLength(constructors);
             assert_equals(this.get_interface_object().length, expected_length, "wrong value for " + this.name + ".length");
         }.bind(this), this.name + " interface object length");
@@ -1647,7 +1767,7 @@
             }
             var aliases;
             if (rhs.type === "identifier-list") {
-                aliases = rhs.value;
+                aliases = rhs.value.map(id => id.value);
             } else { // rhs.type === identifier
                 aliases = [ rhs.value ];
             }
@@ -1785,7 +1905,7 @@
             var args = constructor.arguments.map(function(arg) {
                 return create_suitable_object(arg.idlType);
             });
-            assert_throws_js(TypeError, function() {
+            assert_throws_js(globalOf(self[name]).TypeError, function() {
                 self[name](...args);
             }.bind(this));
         }.bind(this), this.name + " interface: named constructor without 'new'");
@@ -2048,7 +2168,7 @@
         var newValue = Object.create(null);
 
         t.add_cleanup(function() {
-            var setter = Object.getOwnPropertyDescriptor(
+            let setter = Object.getOwnPropertyDescriptor(
                 Object.prototype, '__proto__'
             ).set;
 
@@ -2057,7 +2177,22 @@
             } catch (err) {}
         });
 
-        assert_throws_js(TypeError, function() {
+        // We need to find the actual setter for the '__proto__' property, so we
+        // can determine the right global for it.  Walk up the prototype chain
+        // looking for that property until we find it.
+        let setter;
+        {
+            let cur = obj;
+            while (cur) {
+                const desc = Object.getOwnPropertyDescriptor(cur, "__proto__");
+                if (desc) {
+                    setter = desc.set;
+                    break;
+                }
+                cur = Object.getPrototypeOf(cur);
+            }
+        }
+        assert_throws_js(globalOf(setter).TypeError, function() {
             obj.__proto__ = newValue;
         });
 
@@ -2190,7 +2325,7 @@
         assert_own_property(this.get_interface_object(), "prototype",
                             'interface "' + this.name + '" does not have own property "prototype"');
 
-        if (member["static"]) {
+        if (member.special === "static") {
             assert_own_property(this.get_interface_object(), member.name,
                 "The interface object must have a property " +
                 format_value(member.name));
@@ -2238,6 +2373,7 @@
 
             if (!member.has_extended_attribute("LenientThis")) {
                 if (member.idlType.generic !== "Promise") {
+                    // this.get_interface_object() returns a thing in our global
                     assert_throws_js(TypeError, function() {
                         this.get_interface_object().prototype[member.name];
                     }.bind(this), "getting property on prototype object must throw TypeError");
@@ -2245,7 +2381,7 @@
                     // do, since it will call done() on a_test.
                     this.do_interface_attribute_asserts(this.get_interface_object().prototype, member, a_test);
                 } else {
-                    promise_rejects(a_test, new TypeError(),
+                    promise_rejects_js(a_test, TypeError,
                                     this.get_interface_object().prototype[member.name])
                         .then(function() {
                             // do_interface_attribute_asserts must be the last
@@ -2307,7 +2443,7 @@
         var memberHolderObject;
         // "* If the operation is static, then the property exists on the
         //    interface object."
-        if (member["static"]) {
+        if (member.special === "static") {
             assert_own_property(this.get_interface_object(), member.name,
                     "interface object missing static operation");
             memberHolderObject = this.get_interface_object();
@@ -2400,7 +2536,7 @@
     // check for globals, since otherwise we'll invoke window.close().  And we
     // have to skip this test for anything that on the proto chain of "self",
     // since that does in fact have implicit-this behavior.
-    if (!member["static"]) {
+    if (member.special !== "static") {
         var cb;
         if (!this.is_global() &&
             memberHolderObject[member.name] != self[member.name])
@@ -2522,7 +2658,7 @@
             "property has wrong .length");
 
         // "Let O be the result of calling ToObject on the this value."
-        assert_throws_js(TypeError, function() {
+        assert_throws_js(globalOf(interfacePrototypeObject.toString).TypeError, function() {
             interfacePrototypeObject.toString.apply(null, []);
         }, "calling stringifier with this = null didn't throw TypeError");
 
@@ -2531,7 +2667,7 @@
         //
         // TODO: Test a platform object that implements some other
         // interface.  (Have to be sure to get inheritance right.)
-        assert_throws_js(TypeError, function() {
+        assert_throws_js(globalOf(interfacePrototypeObject.toString).TypeError, function() {
             interfacePrototypeObject.toString.apply({}, []);
         }, "calling stringifier with this = {} didn't throw TypeError");
     }.bind(this), this.name + " interface: stringifier");
@@ -2571,7 +2707,7 @@
             {
                 this.test_member_attribute(member);
             }
-            if (member.stringifier) {
+            if (member.special === "stringifier") {
                 this.test_member_stringifier(member);
             }
             break;
@@ -2586,7 +2722,7 @@
                 {
                     this.test_member_operation(member);
                 }
-            } else if (member.stringifier) {
+            } else if (member.special === "stringifier") {
                 this.test_member_stringifier(member);
             }
             break;
@@ -2756,7 +2892,7 @@
             {
                 assert_equals(exception, null, "Unexpected exception when evaluating object");
                 assert_equals(typeof obj, expected_typeof, "wrong typeof object");
-                if (!member["static"]) {
+                if (member.special !== "static") {
                     if (!this.is_global()) {
                         assert_inherits(obj, member.name);
                     } else {
@@ -2806,7 +2942,7 @@
                 assert_equals(exception, null, "Unexpected exception when evaluating object");
                 assert_equals(typeof obj, expected_typeof, "wrong typeof object");
                 var fn;
-                if (!member["static"]) {
+                if (member.special !== "static") {
                     if (!this.is_global() && !member.isUnforgeable) {
                         assert_inherits(obj, member.name);
                     } else {
@@ -2849,7 +2985,7 @@
         // default stringifer
         return true;
     }
-    if (this.members.some(function(member) { return member.stringifier; })) {
+    if (this.members.some(function(member) { return member.special === "stringifier"; })) {
         return true;
     }
     if (this.base &&
@@ -2903,19 +3039,19 @@
     assert_equals(typeof desc.get, "function", "getter must be Function");
 
     // "If the attribute is a regular attribute, then:"
-    if (!member["static"]) {
+    if (member.special !== "static") {
         // "If O is not a platform object that implements I, then:
         // "If the attribute was specified with the [LenientThis] extended
         // attribute, then return undefined.
         // "Otherwise, throw a TypeError."
         if (!member.has_extended_attribute("LenientThis")) {
             if (member.idlType.generic !== "Promise") {
-                assert_throws_js(TypeError, function() {
+                assert_throws_js(globalOf(desc.get).TypeError, function() {
                     desc.get.call({});
                 }.bind(this), "calling getter on wrong object type must throw TypeError");
             } else {
                 pendingPromises.push(
-                    promise_rejects(a_test, new TypeError(), desc.get.call({}),
+                    promise_rejects_js(a_test, TypeError, desc.get.call({}),
                                     "calling getter on wrong object type must reject the return promise with TypeError"));
             }
         } else {
@@ -2953,7 +3089,7 @@
         assert_equals(typeof desc.set, "function", "setter must be function for PutForwards, Replaceable, or non-readonly attributes");
 
         // "If the attribute is a regular attribute, then:"
-        if (!member["static"]) {
+        if (member.special !== "static") {
             // "If /validThis/ is false and the attribute was not specified
             // with the [LenientThis] extended attribute, then throw a
             // TypeError."
@@ -2961,7 +3097,7 @@
             // attribute, then: ..."
             // "If validThis is false, then return."
             if (!member.has_extended_attribute("LenientThis")) {
-                assert_throws_js(TypeError, function() {
+                assert_throws_js(globalOf(desc.set).TypeError, function() {
                     desc.set.call({});
                 }.bind(this), "calling setter on wrong object type must throw TypeError");
             } else {
@@ -2991,7 +3127,7 @@
      * We just forward all properties to this object without modification,
      * except for special extAttrs handling.
      */
-    for (var k in obj)
+    for (var k in obj.toJSON())
     {
         this[k] = obj[k];
     }
@@ -3006,8 +3142,12 @@
 
 IdlInterfaceMember.prototype = Object.create(IdlObject.prototype);
 
+IdlInterfaceMember.prototype.toJSON = function() {
+    return this;
+};
+
 IdlInterfaceMember.prototype.is_to_json_regular_operation = function() {
-    return this.type == "operation" && !this.static && this.name == "toJSON";
+    return this.type == "operation" && this.special !== "static" && this.name == "toJSON";
 };
 
 /// Internal helper functions ///
@@ -3067,6 +3207,24 @@
 
 IdlEnum.prototype = Object.create(IdlObject.prototype);
 
+/// IdlCallback ///
+// Used for IdlArray.prototype.assert_type_is
+function IdlCallback(obj)
+{
+    /**
+     * obj is an object produced by the WebIDLParser.js "callback"
+     * production.
+     */
+
+    /** Self-explanatory. */
+    this.name = obj.name;
+
+    /** Arguments for the callback. */
+    this.arguments = obj.arguments;
+}
+
+IdlCallback.prototype = Object.create(IdlObject.prototype);
+
 /// IdlTypedef ///
 // Used for IdlArray.prototype.assert_type_is
 function IdlTypedef(obj)
@@ -3228,16 +3386,30 @@
         srcs = (srcs instanceof Array) ? srcs : [srcs] || [];
         deps = (deps instanceof Array) ? deps : [deps] || [];
         var setup_error = null;
+        const validationIgnored = [
+            "constructor-member",
+            "dict-arg-default",
+            "require-exposed"
+        ];
         return Promise.all(
-            srcs.concat(deps).map(function(spec) {
-                return fetch_spec(spec);
-            }))
-            .then(function(idls) {
+            srcs.concat(deps).map(fetch_spec))
+            .then(function(results) {
+                const astArray = results.map(result =>
+                    WebIDL2.parse(result.idl, { sourceName: result.spec })
+                );
+                test(() => {
+                    const validations = WebIDL2.validate(astArray)
+                        .filter(v => !validationIgnored.includes(v.ruleName));
+                    if (validations.length) {
+                        const message = validations.map(v => v.message).join("\n\n");
+                        throw new Error(message);
+                    }
+                }, "idl_test validation");
                 for (var i = 0; i < srcs.length; i++) {
-                    idl_array.add_idls(idls[i]);
+                    idl_array.internal_add_idls(astArray[i]);
                 }
                 for (var i = srcs.length; i < srcs.length + deps.length; i++) {
-                    idl_array.add_dependency_idls(idls[i]);
+                    idl_array.internal_add_dependency_idls(astArray[i]);
                 }
             })
             .then(function() {
@@ -3272,6 +3444,6 @@
             throw new IdlHarnessError("Error fetching " + url + ".");
         }
         return r.text();
-    });
+    }).then(idl => ({ spec, idl }));
 }
 // vim: set expandtab shiftwidth=4 tabstop=4 foldmarker=@{,@} foldmethod=marker:
diff --git a/third_party/blink/web_tests/resources/testharness.js b/third_party/blink/web_tests/resources/testharness.js
index e183e8cf..9d31d0b6 100644
--- a/third_party/blink/web_tests/resources/testharness.js
+++ b/third_party/blink/web_tests/resources/testharness.js
@@ -638,29 +638,23 @@
         });
     }
 
-    function promise_rejects(test, expected, promise, description) {
+    function promise_rejects_js(test, constructor, promise, description) {
         return promise.then(test.unreached_func("Should have rejected: " + description)).catch(function(e) {
-            assert_throws(expected, function() { throw e }, description);
-        });
-    }
-
-    function promise_rejects_js(test, expected, promise, description) {
-        return promise.then(test.unreached_func("Should have rejected: " + description)).catch(function(e) {
-            assert_throws_js_impl(expected, function() { throw e },
+            assert_throws_js_impl(constructor, function() { throw e },
                                   description, "promise_reject_js");
         });
     }
 
-    function promise_rejects_dom(test, expected, promise, description) {
+    function promise_rejects_dom(test, type, promise, description) {
         return promise.then(test.unreached_func("Should have rejected: " + description)).catch(function(e) {
-            assert_throws_dom_impl(expected, function() { throw e },
+            assert_throws_dom_impl(type, function() { throw e },
                                    description, "promise_rejects_dom");
         });
     }
 
-    function promise_rejects_exactly(test, expected, promise, description) {
+    function promise_rejects_exactly(test, exception, promise, description) {
         return promise.then(test.unreached_func("Should have rejected: " + description)).catch(function(e) {
-            assert_throws_exactly_impl(expected, function() { throw e },
+            assert_throws_exactly_impl(exception, function() { throw e },
                                        description, "promise_rejects_exactly");
         });
     }
@@ -849,7 +843,7 @@
             // descriptive than the generic message defined here.
             if (tests.status.status === null) {
                 tests.status.status = tests.status.ERROR;
-                tests.status.message = "done() was called without first defining any tests: " + new Error().stack;
+                tests.status.message = "done() was called without first defining any tests";
             }
 
             tests.complete();
@@ -898,7 +892,6 @@
     expose(test, 'test');
     expose(async_test, 'async_test');
     expose(promise_test, 'promise_test');
-    expose(promise_rejects, 'promise_rejects');
     expose(promise_rejects_js, 'promise_rejects_js');
     expose(promise_rejects_dom, 'promise_rejects_dom');
     expose(promise_rejects_exactly, 'promise_rejects_exactly');
@@ -1468,156 +1461,6 @@
     expose(assert_readonly, "assert_readonly");
 
     /**
-     * Assert an Exception with the expected code is thrown.
-     *
-     * @param {object|number|string} code The expected exception code.
-     * @param {Function} func Function which should throw.
-     * @param {string} description Error description for the case that the error is not thrown.
-     */
-    function assert_throws(code, func, description)
-    {
-        try {
-            func.call(this);
-            assert(false, "assert_throws", description,
-                   "${func} did not throw", {func:func});
-        } catch (e) {
-            if (e instanceof AssertionError) {
-                throw e;
-            }
-
-            assert(typeof e === "object",
-                   "assert_throws", description,
-                   "${func} threw ${e} with type ${type}, not an object",
-                   {func:func, e:e, type:typeof e});
-
-            assert(e !== null,
-                   "assert_throws", description,
-                   "${func} threw null, not an object",
-                   {func:func});
-
-            if (code === null) {
-                throw new AssertionError('Test bug: need to pass exception to assert_throws()');
-            }
-            if (typeof code === "object") {
-                assert("name" in e && e.name == code.name,
-                       "assert_throws", description,
-                       "${func} threw ${actual} (${actual_name}) expected ${expected} (${expected_name})",
-                                    {func:func, actual:e, actual_name:e.name,
-                                     expected:code,
-                                     expected_name:code.name});
-                return;
-            }
-
-            var code_name_map = {
-                INDEX_SIZE_ERR: 'IndexSizeError',
-                HIERARCHY_REQUEST_ERR: 'HierarchyRequestError',
-                WRONG_DOCUMENT_ERR: 'WrongDocumentError',
-                INVALID_CHARACTER_ERR: 'InvalidCharacterError',
-                NO_MODIFICATION_ALLOWED_ERR: 'NoModificationAllowedError',
-                NOT_FOUND_ERR: 'NotFoundError',
-                NOT_SUPPORTED_ERR: 'NotSupportedError',
-                INUSE_ATTRIBUTE_ERR: 'InUseAttributeError',
-                INVALID_STATE_ERR: 'InvalidStateError',
-                SYNTAX_ERR: 'SyntaxError',
-                INVALID_MODIFICATION_ERR: 'InvalidModificationError',
-                NAMESPACE_ERR: 'NamespaceError',
-                INVALID_ACCESS_ERR: 'InvalidAccessError',
-                TYPE_MISMATCH_ERR: 'TypeMismatchError',
-                SECURITY_ERR: 'SecurityError',
-                NETWORK_ERR: 'NetworkError',
-                ABORT_ERR: 'AbortError',
-                URL_MISMATCH_ERR: 'URLMismatchError',
-                QUOTA_EXCEEDED_ERR: 'QuotaExceededError',
-                TIMEOUT_ERR: 'TimeoutError',
-                INVALID_NODE_TYPE_ERR: 'InvalidNodeTypeError',
-                DATA_CLONE_ERR: 'DataCloneError'
-            };
-
-            var name = code in code_name_map ? code_name_map[code] : code;
-
-            var name_code_map = {
-                IndexSizeError: 1,
-                HierarchyRequestError: 3,
-                WrongDocumentError: 4,
-                InvalidCharacterError: 5,
-                NoModificationAllowedError: 7,
-                NotFoundError: 8,
-                NotSupportedError: 9,
-                InUseAttributeError: 10,
-                InvalidStateError: 11,
-                SyntaxError: 12,
-                InvalidModificationError: 13,
-                NamespaceError: 14,
-                InvalidAccessError: 15,
-                TypeMismatchError: 17,
-                SecurityError: 18,
-                NetworkError: 19,
-                AbortError: 20,
-                URLMismatchError: 21,
-                QuotaExceededError: 22,
-                TimeoutError: 23,
-                InvalidNodeTypeError: 24,
-                DataCloneError: 25,
-
-                EncodingError: 0,
-                NotReadableError: 0,
-                UnknownError: 0,
-                ConstraintError: 0,
-                DataError: 0,
-                TransactionInactiveError: 0,
-                ReadOnlyError: 0,
-                VersionError: 0,
-                OperationError: 0,
-                NotAllowedError: 0
-            };
-
-            var code_name_map = {};
-            for (var key in name_code_map) {
-                if (name_code_map[key] > 0) {
-                    code_name_map[name_code_map[key]] = key;
-                }
-            }
-
-            var required_props = { code: code };
-
-            if (typeof code === "number") {
-                if (code === 0) {
-                    throw new AssertionError('Test bug: ambiguous DOMException code 0 passed to assert_throws()');
-                } else if (!(code in code_name_map)) {
-                    throw new AssertionError('Test bug: unrecognized DOMException code "' + code + '" passed to assert_throws()');
-                }
-                name = code_name_map[code];
-            } else if (typeof code === "string") {
-                if (!(name in name_code_map)) {
-                    throw new AssertionError('Test bug: unrecognized DOMException code "' + code + '" passed to assert_throws()');
-                }
-                required_props.code = name_code_map[name];
-            }
-
-            if (required_props.code === 0 ||
-               ("name" in e &&
-                e.name !== e.name.toUpperCase() &&
-                e.name !== "DOMException")) {
-                // New style exception: also test the name property.
-                required_props.name = name;
-            }
-
-            //We'd like to test that e instanceof the appropriate interface,
-            //but we can't, because we don't know what window it was created
-            //in.  It might be an instanceof the appropriate interface on some
-            //unknown other window.  TODO: Work around this somehow?
-
-            for (var prop in required_props) {
-                assert(prop in e && e[prop] == required_props[prop],
-                       "assert_throws", description,
-                       "${func} threw ${e} that is not a DOMException " + code + ": property ${prop} is equal to ${actual}, expected ${expected}",
-                       {func:func, e:e, prop:prop, actual:e[prop], expected:required_props[prop]});
-            }
-        }
-    }
-    expose(assert_throws, "assert_throws");
-
-    /**
      * Assert a JS Error with the expected constructor is thrown.
      *
      * @param {object} constructor The expected exception constructor.
diff --git a/third_party/blink/web_tests/resources/webidl2.js b/third_party/blink/web_tests/resources/webidl2.js
index ef519c0..ea30d6e 100644
--- a/third_party/blink/web_tests/resources/webidl2.js
+++ b/third_party/blink/web_tests/resources/webidl2.js
@@ -1,970 +1,2873 @@
+(function webpackUniversalModuleDefinition(root, factory) {
+    if(typeof exports === 'object' && typeof module === 'object')
+        module.exports = factory();
+    else if(typeof define === 'function' && define.amd)
+        define([], factory);
+    else if(typeof exports === 'object')
+        exports["WebIDL2"] = factory();
+    else
+        root["WebIDL2"] = factory();
+})(this, function() {
+return /******/ (function(modules) { // webpackBootstrap
+/******/     // The module cache
+/******/     var installedModules = {};
+/******/
+/******/     // The require function
+/******/     function __webpack_require__(moduleId) {
+/******/
+/******/         // Check if module is in cache
+/******/         if(installedModules[moduleId]) {
+/******/             return installedModules[moduleId].exports;
+/******/         }
+/******/         // Create a new module (and put it into the cache)
+/******/         var module = installedModules[moduleId] = {
+/******/             i: moduleId,
+/******/             l: false,
+/******/             exports: {}
+/******/         };
+/******/
+/******/         // Execute the module function
+/******/         modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
+/******/
+/******/         // Flag the module as loaded
+/******/         module.l = true;
+/******/
+/******/         // Return the exports of the module
+/******/         return module.exports;
+/******/     }
+/******/
+/******/
+/******/     // expose the modules object (__webpack_modules__)
+/******/     __webpack_require__.m = modules;
+/******/
+/******/     // expose the module cache
+/******/     __webpack_require__.c = installedModules;
+/******/
+/******/     // define getter function for harmony exports
+/******/     __webpack_require__.d = function(exports, name, getter) {
+/******/         if(!__webpack_require__.o(exports, name)) {
+/******/             Object.defineProperty(exports, name, { enumerable: true, get: getter });
+/******/         }
+/******/     };
+/******/
+/******/     // define __esModule on exports
+/******/     __webpack_require__.r = function(exports) {
+/******/         if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
+/******/             Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
+/******/         }
+/******/         Object.defineProperty(exports, '__esModule', { value: true });
+/******/     };
+/******/
+/******/     // create a fake namespace object
+/******/     // mode & 1: value is a module id, require it
+/******/     // mode & 2: merge all properties of value into the ns
+/******/     // mode & 4: return value when already ns object
+/******/     // mode & 8|1: behave like require
+/******/     __webpack_require__.t = function(value, mode) {
+/******/         if(mode & 1) value = __webpack_require__(value);
+/******/         if(mode & 8) return value;
+/******/         if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
+/******/         var ns = Object.create(null);
+/******/         __webpack_require__.r(ns);
+/******/         Object.defineProperty(ns, 'default', { enumerable: true, value: value });
+/******/         if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
+/******/         return ns;
+/******/     };
+/******/
+/******/     // getDefaultExport function for compatibility with non-harmony modules
+/******/     __webpack_require__.n = function(module) {
+/******/         var getter = module && module.__esModule ?
+/******/             function getDefault() { return module['default']; } :
+/******/             function getModuleExports() { return module; };
+/******/         __webpack_require__.d(getter, 'a', getter);
+/******/         return getter;
+/******/     };
+/******/
+/******/     // Object.prototype.hasOwnProperty.call
+/******/     __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
+/******/
+/******/     // __webpack_public_path__
+/******/     __webpack_require__.p = "";
+/******/
+/******/
+/******/     // Load entry module and return exports
+/******/     return __webpack_require__(__webpack_require__.s = 0);
+/******/ })
+/************************************************************************/
+/******/ ([
+/* 0 */
+/***/ (function(module, __webpack_exports__, __webpack_require__) {
+
 "use strict";
+__webpack_require__.r(__webpack_exports__);
+/* harmony import */ var _lib_webidl2_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1);
+/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "parse", function() { return _lib_webidl2_js__WEBPACK_IMPORTED_MODULE_0__["parse"]; });
 
-(() => {
-  // These regular expressions use the sticky flag so they will only match at
-  // the current location (ie. the offset of lastIndex).
-  const tokenRe = {
-    // This expression uses a lookahead assertion to catch false matches
-    // against integers early.
-    "float": /-?(?=[0-9]*\.|[0-9]+[eE])(([0-9]+\.[0-9]*|[0-9]*\.[0-9]+)([Ee][-+]?[0-9]+)?|[0-9]+[Ee][-+]?[0-9]+)/y,
-    "integer": /-?(0([Xx][0-9A-Fa-f]+|[0-7]*)|[1-9][0-9]*)/y,
-    "identifier": /_?[A-Za-z][0-9A-Z_a-z-]*/y,
-    "string": /"[^"]*"/y,
-    "whitespace": /[\t\n\r ]+/y,
-    "comment": /((\/(\/.*|\*([^*]|\*[^\/])*\*\/)[\t\n\r ]*)+)/y,
-    "other": /[^\t\n\r 0-9A-Za-z]/y
-  };
+/* harmony import */ var _lib_writer_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(30);
+/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "write", function() { return _lib_writer_js__WEBPACK_IMPORTED_MODULE_1__["write"]; });
 
-  const stringTypes = [
-    "ByteString",
-    "DOMString",
-    "USVString"
-  ];
+/* harmony import */ var _lib_validator_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(31);
+/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "validate", function() { return _lib_validator_js__WEBPACK_IMPORTED_MODULE_2__["validate"]; });
 
-  const argumentNameKeywords = [
-    "attribute",
-    "callback",
-    "const",
-    "deleter",
-    "dictionary",
-    "enum",
-    "getter",
-    "includes",
-    "inherit",
-    "interface",
-    "iterable",
-    "maplike",
-    "namespace",
-    "partial",
-    "required",
-    "setlike",
-    "setter",
-    "static",
-    "stringifier",
-    "typedef",
-    "unrestricted"
-  ];
 
-  const nonRegexTerminals = [
-    "FrozenArray",
-    "Infinity",
-    "NaN",
-    "Promise",
-    "boolean",
-    "byte",
-    "double",
-    "false",
-    "float",
-    "implements",
-    "legacyiterable",
-    "long",
-    "mixin",
-    "null",
-    "octet",
-    "optional",
-    "or",
-    "readonly",
-    "record",
-    "sequence",
-    "short",
-    "true",
-    "unsigned",
-    "void"
-  ].concat(argumentNameKeywords, stringTypes);
 
-  const punctuations = [
-    "(",
-    ")",
-    ",",
-    "-Infinity",
-    "...",
-    ":",
-    ";",
-    "<",
-    "=",
-    ">",
-    "?",
-    "[",
-    "]",
-    "{",
-    "}"
-  ];
 
-  function tokenise(str) {
-    const tokens = [];
-    let lastIndex = 0;
-    let trivia = "";
-    while (lastIndex < str.length) {
-      const nextChar = str.charAt(lastIndex);
-      let result = -1;
 
-      if (/[\t\n\r ]/.test(nextChar)) {
-        result = attemptTokenMatch("whitespace", { noFlushTrivia: true });
-      } else if (nextChar === '/') {
-        result = attemptTokenMatch("comment", { noFlushTrivia: true });
+
+/***/ }),
+/* 1 */
+/***/ (function(module, __webpack_exports__, __webpack_require__) {
+
+"use strict";
+__webpack_require__.r(__webpack_exports__);
+/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "parse", function() { return parse; });
+/* harmony import */ var _tokeniser_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(2);
+/* harmony import */ var _productions_enum_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(15);
+/* harmony import */ var _productions_includes_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(16);
+/* harmony import */ var _productions_extended_attributes_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(10);
+/* harmony import */ var _productions_typedef_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(17);
+/* harmony import */ var _productions_callback_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(18);
+/* harmony import */ var _productions_interface_js__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(19);
+/* harmony import */ var _productions_mixin_js__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(25);
+/* harmony import */ var _productions_dictionary_js__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(26);
+/* harmony import */ var _productions_namespace_js__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(28);
+/* harmony import */ var _productions_callback_interface_js__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(29);
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/**
+ * @param {Tokeniser} tokeniser
+ * @param {object} options
+ * @param {boolean} [options.concrete]
+ */
+function parseByTokens(tokeniser, options) {
+  const source = tokeniser.source;
+
+  function error(str) {
+    tokeniser.error(str);
+  }
+
+  function consume(...candidates) {
+    return tokeniser.consume(...candidates);
+  }
+
+  function callback() {
+    const callback = consume("callback");
+    if (!callback) return;
+    if (tokeniser.probe("interface")) {
+      return _productions_callback_interface_js__WEBPACK_IMPORTED_MODULE_10__["CallbackInterface"].parse(tokeniser, callback);
+    }
+    return _productions_callback_js__WEBPACK_IMPORTED_MODULE_5__["CallbackFunction"].parse(tokeniser, callback);
+  }
+
+  function interface_(opts) {
+    const base = consume("interface");
+    if (!base) return;
+    const ret = _productions_mixin_js__WEBPACK_IMPORTED_MODULE_7__["Mixin"].parse(tokeniser, base, opts) ||
+      _productions_interface_js__WEBPACK_IMPORTED_MODULE_6__["Interface"].parse(tokeniser, base, opts) ||
+      error("Interface has no proper body");
+    return ret;
+  }
+
+  function partial() {
+    const partial = consume("partial");
+    if (!partial) return;
+    return _productions_dictionary_js__WEBPACK_IMPORTED_MODULE_8__["Dictionary"].parse(tokeniser, { partial }) ||
+      interface_({ partial }) ||
+      _productions_namespace_js__WEBPACK_IMPORTED_MODULE_9__["Namespace"].parse(tokeniser, { partial }) ||
+      error("Partial doesn't apply to anything");
+  }
+
+  function definition() {
+    return callback() ||
+      interface_() ||
+      partial() ||
+      _productions_dictionary_js__WEBPACK_IMPORTED_MODULE_8__["Dictionary"].parse(tokeniser) ||
+      _productions_enum_js__WEBPACK_IMPORTED_MODULE_1__["Enum"].parse(tokeniser) ||
+      _productions_typedef_js__WEBPACK_IMPORTED_MODULE_4__["Typedef"].parse(tokeniser) ||
+      _productions_includes_js__WEBPACK_IMPORTED_MODULE_2__["Includes"].parse(tokeniser) ||
+      _productions_namespace_js__WEBPACK_IMPORTED_MODULE_9__["Namespace"].parse(tokeniser);
+  }
+
+  function definitions() {
+    if (!source.length) return [];
+    const defs = [];
+    while (true) {
+      const ea = _productions_extended_attributes_js__WEBPACK_IMPORTED_MODULE_3__["ExtendedAttributes"].parse(tokeniser);
+      const def = definition();
+      if (!def) {
+        if (ea.length) error("Stray extended attributes");
+        break;
       }
+      def.extAttrs = ea;
+      defs.push(def);
+    }
+    const eof = consume("eof");
+    if (options.concrete) {
+      defs.push(eof);
+    }
+    return defs;
+  }
+  const res = definitions();
+  if (tokeniser.position < source.length) error("Unrecognised tokens");
+  return res;
+}
 
-      if (result !== -1) {
-        trivia += tokens.pop().value;
-      } else if (/[-0-9.]/.test(nextChar)) {
-        result = attemptTokenMatch("float");
-        if (result === -1) {
-          result = attemptTokenMatch("integer");
-        }
-      } else if (/[A-Z_a-z]/.test(nextChar)) {
+function parse(str, options = {}) {
+  const tokeniser = new _tokeniser_js__WEBPACK_IMPORTED_MODULE_0__["Tokeniser"](str);
+  if (typeof options.sourceName !== "undefined") {
+    tokeniser.source.name = options.sourceName;
+  }
+  return parseByTokens(tokeniser, options);
+}
+
+
+/***/ }),
+/* 2 */
+/***/ (function(module, __webpack_exports__, __webpack_require__) {
+
+"use strict";
+__webpack_require__.r(__webpack_exports__);
+/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "stringTypes", function() { return stringTypes; });
+/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "argumentNameKeywords", function() { return argumentNameKeywords; });
+/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Tokeniser", function() { return Tokeniser; });
+/* harmony import */ var _error_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(3);
+/* harmony import */ var _productions_helpers_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(4);
+
+
+
+// These regular expressions use the sticky flag so they will only match at
+// the current location (ie. the offset of lastIndex).
+const tokenRe = {
+  // This expression uses a lookahead assertion to catch false matches
+  // against integers early.
+  "decimal": /-?(?=[0-9]*\.|[0-9]+[eE])(([0-9]+\.[0-9]*|[0-9]*\.[0-9]+)([Ee][-+]?[0-9]+)?|[0-9]+[Ee][-+]?[0-9]+)/y,
+  "integer": /-?(0([Xx][0-9A-Fa-f]+|[0-7]*)|[1-9][0-9]*)/y,
+  "identifier": /[_-]?[A-Za-z][0-9A-Z_a-z-]*/y,
+  "string": /"[^"]*"/y,
+  "whitespace": /[\t\n\r ]+/y,
+  "comment": /((\/(\/.*|\*([^*]|\*[^/])*\*\/)[\t\n\r ]*)+)/y,
+  "other": /[^\t\n\r 0-9A-Za-z]/y
+};
+
+const stringTypes = [
+  "ByteString",
+  "DOMString",
+  "USVString"
+];
+
+const argumentNameKeywords = [
+  "async",
+  "attribute",
+  "callback",
+  "const",
+  "constructor",
+  "deleter",
+  "dictionary",
+  "enum",
+  "getter",
+  "includes",
+  "inherit",
+  "interface",
+  "iterable",
+  "maplike",
+  "namespace",
+  "partial",
+  "required",
+  "setlike",
+  "setter",
+  "static",
+  "stringifier",
+  "typedef",
+  "unrestricted"
+];
+
+const nonRegexTerminals = [
+  "-Infinity",
+  "FrozenArray",
+  "Infinity",
+  "NaN",
+  "Promise",
+  "async",
+  "boolean",
+  "byte",
+  "constructor",
+  "double",
+  "false",
+  "float",
+  "long",
+  "mixin",
+  "null",
+  "octet",
+  "optional",
+  "or",
+  "readonly",
+  "record",
+  "sequence",
+  "short",
+  "true",
+  "unsigned",
+  "void"
+].concat(argumentNameKeywords, stringTypes);
+
+const punctuations = [
+  "(",
+  ")",
+  ",",
+  "...",
+  ":",
+  ";",
+  "<",
+  "=",
+  ">",
+  "?",
+  "[",
+  "]",
+  "{",
+  "}"
+];
+
+const reserved = [
+  // "constructor" is now a keyword
+  "_constructor",
+  "toString",
+  "_toString",
+];
+
+/**
+ * @param {string} str
+ */
+function tokenise(str) {
+  const tokens = [];
+  let lastCharIndex = 0;
+  let trivia = "";
+  let line = 1;
+  let index = 0;
+  while (lastCharIndex < str.length) {
+    const nextChar = str.charAt(lastCharIndex);
+    let result = -1;
+
+    if (/[\t\n\r ]/.test(nextChar)) {
+      result = attemptTokenMatch("whitespace", { noFlushTrivia: true });
+    } else if (nextChar === '/') {
+      result = attemptTokenMatch("comment", { noFlushTrivia: true });
+    }
+
+    if (result !== -1) {
+      const currentTrivia = tokens.pop().value;
+      line += (currentTrivia.match(/\n/g) || []).length;
+      trivia += currentTrivia;
+      index -= 1;
+    } else if (/[-0-9.A-Z_a-z]/.test(nextChar)) {
+      result = attemptTokenMatch("decimal");
+      if (result === -1) {
+        result = attemptTokenMatch("integer");
+      }
+      if (result === -1) {
         result = attemptTokenMatch("identifier");
-        const token = tokens[tokens.length - 1];
-        if (result !== -1 && nonRegexTerminals.includes(token.value)) {
-          token.type = token.value;
-        }
-      } else if (nextChar === '"') {
-        result = attemptTokenMatch("string");
-      }
-
-      for (const punctuation of punctuations) {
-        if (str.startsWith(punctuation, lastIndex)) {
-          tokens.push({ type: punctuation, value: punctuation, trivia });
-          trivia = "";
-          lastIndex += punctuation.length;
-          result = lastIndex;
-          break;
-        }
-      }
-
-      // other as the last try
-      if (result === -1) {
-        result = attemptTokenMatch("other");
-      }
-      if (result === -1) {
-        throw new Error("Token stream not progressing");
-      }
-      lastIndex = result;
-    }
-    return tokens;
-
-    function attemptTokenMatch(type, { noFlushTrivia } = {}) {
-      const re = tokenRe[type];
-      re.lastIndex = lastIndex;
-      const result = re.exec(str);
-      if (result) {
-        tokens.push({ type, value: result[0], trivia });
-        if (!noFlushTrivia) {
-          trivia = "";
-        }
-        return re.lastIndex;
-      }
-      return -1;
-    }
-  }
-
-  class WebIDLParseError {
-    constructor(str, line, input, tokens) {
-      this.message = str;
-      this.line = line;
-      this.input = input;
-      this.tokens = tokens;
-    }
-
-    toString() {
-      const escapedInput = JSON.stringify(this.input);
-      const tokens = JSON.stringify(this.tokens, null, 4);
-      return `${this.message}, line ${this.line} (tokens: ${escapedInput})\n${tokens}`;
-    }
-  }
-
-  function parse(tokens) {
-    let line = 1;
-    tokens = tokens.slice();
-    const names = new Map();
-    let current = null;
-
-    const FLOAT = "float";
-    const INT = "integer";
-    const ID = "identifier";
-    const STR = "string";
-    const OTHER = "other";
-
-    const EMPTY_OPERATION = Object.freeze({
-      type: "operation",
-      getter: false,
-      setter: false,
-      deleter: false,
-      static: false,
-      stringifier: false
-    });
-
-    const EMPTY_IDLTYPE = Object.freeze({
-      generic: null,
-      nullable: false,
-      union: false,
-      idlType: null,
-      extAttrs: []
-    });
-
-    function error(str) {
-      const maxTokens = 5;
-      const tok = tokens
-        .slice(consume_position, consume_position + maxTokens)
-        .map(t => t.trivia + t.value).join("");
-      // Count newlines preceding the actual erroneous token
-      if (tokens.length) {
-        line += count(tokens[consume_position].trivia, "\n");
-      }
-
-      let message;
-      if (current) {
-        message = `Got an error during or right after parsing \`${current.partial ? "partial " : ""}${current.type} ${current.name}\`: ${str}`
-      }
-      else {
-        // throwing before any valid definition
-        message = `Got an error before parsing any named definition: ${str}`;
-      }
-
-      throw new WebIDLParseError(message, line, tok, tokens.slice(0, maxTokens));
-    }
-
-    function sanitize_name(name, type) {
-      if (names.has(name)) {
-        error(`The name "${name}" of type "${names.get(name)}" is already seen`);
-      }
-      names.set(name, type);
-      return name;
-    }
-
-    let consume_position = 0;
-
-    function probe(type) {
-      return tokens.length > consume_position && tokens[consume_position].type === type;
-    }
-
-    function consume(...candidates) {
-      // TODO: use const when Servo updates its JS engine
-      for (let type of candidates) {
-        if (!probe(type)) continue;
-        const token = tokens[consume_position];
-        consume_position++;
-        line += count(token.trivia, "\n");
-        return token;
-      }
-    }
-
-    function unescape(identifier) {
-      return identifier.startsWith('_') ? identifier.slice(1) : identifier;
-    }
-
-    function unconsume(position) {
-      while (consume_position > position) {
-        consume_position--;
-        line -= count(tokens[consume_position].trivia, "\n");
-      }
-    }
-
-    function count(str, char) {
-      let total = 0;
-      for (let i = str.indexOf(char); i !== -1; i = str.indexOf(char, i + 1)) {
-        ++total;
-      }
-      return total;
-    }
-
-    function integer_type() {
-      let ret = "";
-      if (consume("unsigned")) ret = "unsigned ";
-      if (consume("short")) return ret + "short";
-      if (consume("long")) {
-        ret += "long";
-        if (consume("long")) return ret + " long";
-        return ret;
-      }
-      if (ret) error("Failed to parse integer type");
-    }
-
-    function float_type() {
-      let ret = "";
-      if (consume("unrestricted")) ret = "unrestricted ";
-      if (consume("float")) return ret + "float";
-      if (consume("double")) return ret + "double";
-      if (ret) error("Failed to parse float type");
-    }
-
-    function primitive_type() {
-      const num_type = integer_type() || float_type();
-      if (num_type) return num_type;
-      if (consume("boolean")) return "boolean";
-      if (consume("byte")) return "byte";
-      if (consume("octet")) return "octet";
-    }
-
-    function const_value() {
-      if (consume("true")) return { type: "boolean", value: true };
-      if (consume("false")) return { type: "boolean", value: false };
-      if (consume("null")) return { type: "null" };
-      if (consume("Infinity")) return { type: "Infinity", negative: false };
-      if (consume("-Infinity")) return { type: "Infinity", negative: true };
-      if (consume("NaN")) return { type: "NaN" };
-      const ret = consume(FLOAT, INT);
-      if (ret) return { type: "number", value: ret.value };
-    }
-
-    function type_suffix(obj) {
-      obj.nullable = !!consume("?");
-      if (probe("?")) error("Can't nullable more than once");
-    }
-
-    function generic_type(typeName) {
-      const name = consume("FrozenArray", "Promise", "sequence", "record");
-      if (!name) {
-        return;
-      }
-      const ret = { generic: name.type };
-      consume("<") || error(`No opening bracket after ${name.type}`);
-      switch (name.type) {
-        case "Promise":
-          if (probe("[")) error("Promise type cannot have extended attribute");
-          ret.idlType = return_type(typeName);
-          break;
-        case "sequence":
-        case "FrozenArray":
-          ret.idlType = type_with_extended_attributes(typeName);
-          break;
-        case "record":
-          if (probe("[")) error("Record key cannot have extended attribute");
-          ret.idlType = [];
-          const keyType = consume(...stringTypes);
-          if (!keyType) error(`Record key must be a string type`);
-          ret.idlType.push(Object.assign({ type: typeName }, EMPTY_IDLTYPE, { idlType: keyType.value }));
-          consume(",") || error("Missing comma after record key type");
-          const valueType = type_with_extended_attributes(typeName) || error("Error parsing generic type record");
-          ret.idlType.push(valueType);
-          break;
-      }
-      if (!ret.idlType) error(`Error parsing generic type ${name.type}`);
-      consume(">") || error(`Missing closing bracket after ${name.type}`);
-      if (name.type === "Promise" && probe("?")) {
-        error("Promise type cannot be nullable");
-      }
-      type_suffix(ret);
-      return ret;
-    }
-
-    function single_type(typeName) {
-      const ret = Object.assign({ type: typeName || null }, EMPTY_IDLTYPE);
-      const generic = generic_type(typeName);
-      if (generic) {
-        return Object.assign(ret, generic);
-      }
-      const prim = primitive_type();
-      let name;
-      if (prim) {
-        ret.idlType = prim;
-      } else if (name = consume(ID, ...stringTypes)) {
-        ret.idlType = name.value;
-        if (probe("<")) error(`Unsupported generic type ${name.value}`);
-      } else {
-        return;
-      }
-      type_suffix(ret);
-      if (ret.nullable && ret.idlType === "any") error("Type any cannot be made nullable");
-      return ret;
-    }
-
-    function union_type(typeName) {
-      if (!consume("(")) return;
-      const ret = Object.assign({ type: typeName || null }, EMPTY_IDLTYPE, { union: true, idlType: [] });
-      do {
-        const typ = type_with_extended_attributes() || error("No type after open parenthesis or 'or' in union type");
-        ret.idlType.push(typ);
-      } while (consume("or"));
-      if (ret.idlType.length < 2) {
-        error("At least two types are expected in a union type but found less");
-      }
-      if (!consume(")")) error("Unterminated union type");
-      type_suffix(ret);
-      return ret;
-    }
-
-    function type(typeName) {
-      return single_type(typeName) || union_type(typeName);
-    }
-
-    function type_with_extended_attributes(typeName) {
-      const extAttrs = extended_attrs();
-      const ret = single_type(typeName) || union_type(typeName);
-      if (extAttrs.length && ret) ret.extAttrs = extAttrs;
-      return ret;
-    }
-
-    function argument() {
-      const start_position = consume_position;
-      const ret = { optional: false, variadic: false, default: null };
-      ret.extAttrs = extended_attrs();
-      const opt_token = consume("optional");
-      if (opt_token) {
-        ret.optional = true;
-      }
-      ret.idlType = type_with_extended_attributes("argument-type");
-      if (!ret.idlType) {
-        unconsume(start_position);
-        return;
-      }
-      if (!ret.optional && consume("...")) {
-        ret.variadic = true;
-      }
-      const name = consume(ID, ...argumentNameKeywords);
-      if (!name) {
-        unconsume(start_position);
-        return;
-      }
-      ret.name = unescape(name.value);
-      ret.escapedName = name.value;
-      if (ret.optional) {
-        ret.default = default_() || null;
-      }
-      return ret;
-    }
-
-    function argument_list() {
-      const ret = [];
-      const arg = argument();
-      if (!arg) return ret;
-      ret.push(arg);
-      while (true) {
-        if (!consume(",")) return ret;
-        const nxt = argument() || error("Trailing comma in arguments list");
-        ret.push(nxt);
-      }
-    }
-
-    function simple_extended_attr() {
-      const name = consume(ID);
-      if (!name) return;
-      const ret = {
-        name: name.value,
-        arguments: null,
-        type: "extended-attribute",
-        rhs: null
-      };
-      const eq = consume("=");
-      if (eq) {
-        ret.rhs = consume(ID, FLOAT, INT, STR);
-        if (ret.rhs) {
-          // No trivia exposure yet
-          ret.rhs.trivia = undefined;
-        }
-      }
-      if (consume("(")) {
-        if (eq && !ret.rhs) {
-          // [Exposed=(Window,Worker)]
-          ret.rhs = {
-            type: "identifier-list",
-            value: identifiers()
-          };
-        }
-        else {
-          // [NamedConstructor=Audio(DOMString src)] or [Constructor(DOMString str)]
-          ret.arguments = argument_list();
-        }
-        consume(")") || error("Unexpected token in extended attribute argument list");
-      }
-      if (eq && !ret.rhs) error("No right hand side to extended attribute assignment");
-      return ret;
-    }
-
-    // Note: we parse something simpler than the official syntax. It's all that ever
-    // seems to be used
-    function extended_attrs() {
-      const eas = [];
-      if (!consume("[")) return eas;
-      eas[0] = simple_extended_attr() || error("Extended attribute with not content");
-      while (consume(",")) {
-        eas.push(simple_extended_attr() || error("Trailing comma in extended attribute"));
-      }
-      consume("]") || error("No end of extended attribute");
-      return eas;
-    }
-
-    function default_() {
-      if (consume("=")) {
-        const def = const_value();
-        if (def) {
-          return def;
-        } else if (consume("[")) {
-          if (!consume("]")) error("Default sequence value must be empty");
-          return { type: "sequence", value: [] };
-        } else {
-          const str = consume(STR) || error("No value for default");
-          str.value = str.value.slice(1, -1);
-          // No trivia exposure yet
-          str.trivia = undefined;
-          return str;
-        }
-      }
-    }
-
-    function const_() {
-      if (!consume("const")) return;
-      const ret = { type: "const", nullable: false };
-      let typ = primitive_type();
-      if (!typ) {
-        typ = consume(ID) || error("No type for const");
-        typ = typ.value;
-      }
-      ret.idlType = Object.assign({ type: "const-type" }, EMPTY_IDLTYPE, { idlType: typ });
-      type_suffix(ret);
-      const name = consume(ID) || error("No name for const");
-      ret.name = name.value;
-      consume("=") || error("No value assignment for const");
-      const cnt = const_value();
-      if (cnt) ret.value = cnt;
-      else error("No value for const");
-      consume(";") || error("Unterminated const");
-      return ret;
-    }
-
-    function inheritance() {
-      if (consume(":")) {
-        const inh = consume(ID) || error("No type in inheritance");
-        return inh.value;
-      }
-    }
-
-    function operation_rest(ret) {
-      if (!ret) ret = {};
-      const name = consume(ID);
-      ret.name = name ? unescape(name.value) : null;
-      ret.escapedName = name ? name.value : null;
-      consume("(") || error("Invalid operation");
-      ret.arguments = argument_list();
-      consume(")") || error("Unterminated operation");
-      consume(";") || error("Unterminated operation");
-      return ret;
-    }
-
-    function callback() {
-      let ret;
-      if (!consume("callback")) return;
-      const tok = consume("interface");
-      if (tok) {
-        ret = interface_rest(false, "callback interface");
-        return ret;
-      }
-      const name = consume(ID) || error("No name for callback");
-      ret = current = { type: "callback", name: sanitize_name(name.value, "callback") };
-      consume("=") || error("No assignment in callback");
-      ret.idlType = return_type() || error("Missing return type");
-      consume("(") || error("No arguments in callback");
-      ret.arguments = argument_list();
-      consume(")") || error("Unterminated callback");
-      consume(";") || error("Unterminated callback");
-      return ret;
-    }
-
-    function attribute({ noInherit = false, readonly = false } = {}) {
-      const start_position = consume_position;
-      const ret = {
-        type: "attribute",
-        static: false,
-        stringifier: false,
-        inherit: false,
-        readonly: false
-      };
-      if (!noInherit && consume("inherit")) {
-        ret.inherit = true;
-      }
-      if (consume("readonly")) {
-        ret.readonly = true;
-      } else if (readonly && probe("attribute")) {
-        error("Attributes must be readonly in this context");
-      }
-      const rest = attribute_rest(ret);
-      if (!rest) {
-        unconsume(start_position);
-      }
-      return rest;
-    }
-
-    function attribute_rest(ret) {
-      if (!consume("attribute")) {
-        return;
-      }
-      ret.idlType = type_with_extended_attributes("attribute-type") || error("No type in attribute");
-      if (ret.idlType.generic === "sequence") error("Attributes cannot accept sequence types");
-      if (ret.idlType.generic === "record") error("Attributes cannot accept record types");
-      const name = consume(ID, "required") || error("No name in attribute");
-      ret.name = unescape(name.value);
-      ret.escapedName = name.value;
-      consume(";") || error("Unterminated attribute");
-      return ret;
-    }
-
-    function return_type(typeName) {
-      const typ = type(typeName || "return-type");
-      if (typ) {
-        return typ;
-      }
-      if (consume("void")) {
-        return Object.assign({ type: "return-type" }, EMPTY_IDLTYPE, { idlType: "void" });
-      }
-    }
-
-    function operation({ regular = false } = {}) {
-      const ret = Object.assign({}, EMPTY_OPERATION);
-      while (!regular) {
-        if (consume("getter")) ret.getter = true;
-        else if (consume("setter")) ret.setter = true;
-        else if (consume("deleter")) ret.deleter = true;
-        else break;
-      }
-      ret.idlType = return_type() || error("Missing return type");
-      operation_rest(ret);
-      return ret;
-    }
-
-    function static_member() {
-      if (!consume("static")) return;
-      const member = attribute({ noInherit: true }) ||
-        operation({ regular: true }) ||
-        error("No body in static member");
-      member.static = true;
-      return member;
-    }
-
-    function stringifier() {
-      if (!consume("stringifier")) return;
-      if (consume(";")) {
-        return Object.assign({}, EMPTY_OPERATION, { stringifier: true });
-      }
-      const member = attribute({ noInherit: true }) ||
-        operation({ regular: true }) ||
-        error("Unterminated stringifier");
-      member.stringifier = true;
-      return member;
-    }
-
-    function identifiers() {
-      const arr = [];
-      const id = consume(ID);
-      if (id) {
-        arr.push(id.value);
-      }
-      else error("Expected identifiers but not found");
-      while (true) {
-        if (consume(",")) {
-          const name = consume(ID) || error("Trailing comma in identifiers list");
-          arr.push(name.value);
-        } else break;
-      }
-      return arr;
-    }
-
-    function iterable_type() {
-      if (consume("iterable")) return "iterable";
-      else if (consume("legacyiterable")) return "legacyiterable";
-      else if (consume("maplike")) return "maplike";
-      else if (consume("setlike")) return "setlike";
-      else return;
-    }
-
-    function readonly_iterable_type() {
-      if (consume("maplike")) return "maplike";
-      else if (consume("setlike")) return "setlike";
-      else return;
-    }
-
-    function iterable() {
-      const start_position = consume_position;
-      const ret = { type: null, idlType: null, readonly: false };
-      if (consume("readonly")) {
-        ret.readonly = true;
-      }
-      const consumeItType = ret.readonly ? readonly_iterable_type : iterable_type;
-
-      const ittype = consumeItType();
-      if (!ittype) {
-        unconsume(start_position);
-        return;
-      }
-
-      const secondTypeRequired = ittype === "maplike";
-      const secondTypeAllowed = secondTypeRequired || ittype === "iterable";
-      ret.type = ittype;
-      if (ret.type !== 'maplike' && ret.type !== 'setlike')
-        delete ret.readonly;
-      if (consume("<")) {
-        ret.idlType = [type_with_extended_attributes()] || error(`Error parsing ${ittype} declaration`);
-        if (secondTypeAllowed) {
-          if (consume(",")) {
-            ret.idlType.push(type_with_extended_attributes());
+        const lastIndex = tokens.length - 1;
+        const token = tokens[lastIndex];
+        if (result !== -1) {
+          if (reserved.includes(token.value)) {
+            const message = `${Object(_productions_helpers_js__WEBPACK_IMPORTED_MODULE_1__["unescape"])(token.value)} is a reserved identifier and must not be used.`;
+            throw new WebIDLParseError(Object(_error_js__WEBPACK_IMPORTED_MODULE_0__["syntaxError"])(tokens, lastIndex, null, message));
+          } else if (nonRegexTerminals.includes(token.value)) {
+            token.type = token.value;
           }
-          else if (secondTypeRequired)
-            error(`Missing second type argument in ${ittype} declaration`);
         }
-        if (!consume(">")) error(`Unterminated ${ittype} declaration`);
-        if (!consume(";")) error(`Missing semicolon after ${ittype} declaration`);
-      } else
-        error(`Error parsing ${ittype} declaration`);
-
-      return ret;
+      }
+    } else if (nextChar === '"') {
+      result = attemptTokenMatch("string");
     }
 
-    function interface_rest(isPartial, typeName = "interface") {
-      const name = consume(ID) || error("No name for interface");
-      const mems = [];
-      const ret = current = {
-        type: typeName,
-        name: isPartial ? name.value : sanitize_name(name.value, "interface"),
-        partial: isPartial,
-        members: mems
-      };
-      if (!isPartial) ret.inheritance = inheritance() || null;
-      consume("{") || error("Bodyless interface");
-      while (true) {
-        if (consume("}")) {
-          consume(";") || error("Missing semicolon after interface");
-          return ret;
-        }
-        const ea = extended_attrs();
-        const mem = const_() ||
-          static_member() ||
-          stringifier() ||
-          iterable() ||
-          attribute() ||
-          operation() ||
-          error("Unknown member");
-        mem.extAttrs = ea;
-        ret.members.push(mem);
+    for (const punctuation of punctuations) {
+      if (str.startsWith(punctuation, lastCharIndex)) {
+        tokens.push({ type: punctuation, value: punctuation, trivia, line, index });
+        trivia = "";
+        lastCharIndex += punctuation.length;
+        result = lastCharIndex;
+        break;
       }
     }
 
-    function mixin_rest(isPartial) {
-      if (!consume("mixin")) return;
-      const name = consume(ID) || error("No name for interface mixin");
-      const mems = [];
-      const ret = current = {
-        type: "interface mixin",
-        name: isPartial ? name.value : sanitize_name(name.value, "interface mixin"),
-        partial: isPartial,
-        members: mems
-      };
-      consume("{") || error("Bodyless interface mixin");
-      while (true) {
-        if (consume("}")) {
-          consume(";") || error("Missing semicolon after interface mixin");
-          return ret;
-        }
-        const ea = extended_attrs();
-        const mem = const_() ||
-          stringifier() ||
-          attribute({ noInherit: true }) ||
-          operation({ regular: true }) ||
-          error("Unknown member");
-        mem.extAttrs = ea;
-        ret.members.push(mem);
-      }
+    // other as the last try
+    if (result === -1) {
+      result = attemptTokenMatch("other");
     }
-
-    function interface_(isPartial) {
-      if (!consume("interface")) return;
-      return mixin_rest(isPartial) ||
-        interface_rest(isPartial) ||
-        error("Interface has no proper body");
+    if (result === -1) {
+      throw new Error("Token stream not progressing");
     }
-
-    function namespace(isPartial) {
-      if (!consume("namespace")) return;
-      const name = consume(ID) || error("No name for namespace");
-      const mems = [];
-      const ret = current = {
-        type: "namespace",
-        name: isPartial ? name.value : sanitize_name(name.value, "namespace"),
-        partial: isPartial,
-        members: mems
-      };
-      consume("{") || error("Bodyless namespace");
-      while (true) {
-        if (consume("}")) {
-          consume(";") || error("Missing semicolon after namespace");
-          return ret;
-        }
-        const ea = extended_attrs();
-        const mem = attribute({ noInherit: true, readonly: true }) ||
-          operation({ regular: true }) ||
-          error("Unknown member");
-        mem.extAttrs = ea;
-        ret.members.push(mem);
-      }
-    }
-
-    function partial() {
-      if (!consume("partial")) return;
-      const thing = dictionary(true) ||
-        interface_(true) ||
-        namespace(true) ||
-        error("Partial doesn't apply to anything");
-      return thing;
-    }
-
-    function dictionary(isPartial) {
-      if (!consume("dictionary")) return;
-      const name = consume(ID) || error("No name for dictionary");
-      const mems = [];
-      const ret = current = {
-        type: "dictionary",
-        name: isPartial ? name.value : sanitize_name(name.value, "dictionary"),
-        partial: isPartial,
-        members: mems
-      };
-      if (!isPartial) ret.inheritance = inheritance() || null;
-      consume("{") || error("Bodyless dictionary");
-      while (true) {
-        if (consume("}")) {
-          consume(";") || error("Missing semicolon after dictionary");
-          return ret;
-        }
-        const ea = extended_attrs();
-        const required = consume("required");
-        const typ = type_with_extended_attributes("dictionary-type") || error("No type for dictionary member");
-        const name = consume(ID) || error("No name for dictionary member");
-        const dflt = default_() || null;
-        if (required && dflt) error("Required member must not have a default");
-        const member = {
-          type: "field",
-          name: unescape(name.value),
-          escapedName: name.value,
-          required: !!required,
-          idlType: typ,
-          extAttrs: ea,
-          default: dflt
-        };
-        ret.members.push(member);
-        consume(";") || error("Unterminated dictionary member");
-      }
-    }
-
-    function enum_() {
-      if (!consume("enum")) return;
-      const name = consume(ID) || error("No name for enum");
-      const vals = [];
-      const ret = current = {
-        type: "enum",
-        name: sanitize_name(name.value, "enum"),
-        values: vals
-      };
-      consume("{") || error("No curly for enum");
-      let value_expected = true;
-      while (true) {
-        if (consume("}")) {
-          if (!ret.values.length) error("No value in enum");
-          consume(";") || error("No semicolon after enum");
-          return ret;
-        }
-        else if (!value_expected) {
-          error("No comma between enum values");
-        }
-        const val = consume(STR) || error("Unexpected value in enum");
-        val.value = val.value.slice(1, -1);
-        // No trivia exposure yet
-        val.trivia = undefined;
-        ret.values.push(val);
-        value_expected = !!consume(",");
-      }
-    }
-
-    function typedef() {
-      if (!consume("typedef")) return;
-      const ret = {
-        type: "typedef"
-      };
-      ret.idlType = type_with_extended_attributes("typedef-type") || error("No type in typedef");
-      const name = consume(ID) || error("No name in typedef");
-      ret.name = sanitize_name(name.value, "typedef");
-      current = ret;
-      consume(";") || error("Unterminated typedef");
-      return ret;
-    }
-
-    function implements_() {
-      const start_position = consume_position;
-      const target = consume(ID);
-      if (!target) return;
-      if (consume("implements")) {
-        const ret = {
-          type: "implements",
-          target: target.value
-        };
-        const imp = consume(ID) || error("Incomplete implements statement");
-        ret.implements = imp.value;
-        consume(";") || error("No terminating ; for implements statement");
-        return ret;
-      } else {
-        // rollback
-        unconsume(start_position);
-      }
-    }
-
-    function includes() {
-      const start_position = consume_position;
-      const target = consume(ID);
-      if (!target) return;
-      if (consume("includes")) {
-        const ret = {
-          type: "includes",
-          target: target.value
-        };
-        const imp = consume(ID) || error("Incomplete includes statement");
-        ret.includes = imp.value;
-        consume(";") || error("No terminating ; for includes statement");
-        return ret;
-      } else {
-        // rollback
-        unconsume(start_position);
-      }
-    }
-
-    function definition() {
-      return callback() ||
-        interface_(false) ||
-        partial() ||
-        dictionary(false) ||
-        enum_() ||
-        typedef() ||
-        implements_() ||
-        includes() ||
-        namespace(false);
-    }
-
-    function definitions() {
-      if (!tokens.length) return [];
-      const defs = [];
-      while (true) {
-        const ea = extended_attrs();
-        const def = definition();
-        if (!def) {
-          if (ea.length) error("Stray extended attributes");
-          break;
-        }
-        def.extAttrs = ea;
-        defs.push(def);
-      }
-      return defs;
-    }
-    const res = definitions();
-    if (consume_position < tokens.length) error("Unrecognised tokens");
-    return res;
+    lastCharIndex = result;
+    index += 1;
   }
 
-  const obj = {
-    parse(str) {
-      const tokens = tokenise(str);
-      return parse(tokens);
+  // remaining trivia as eof
+  tokens.push({
+    type: "eof",
+    value: "",
+    trivia
+  });
+
+  return tokens;
+
+  /**
+   * @param {keyof tokenRe} type
+   * @param {object} [options]
+   * @param {boolean} [options.noFlushTrivia]
+   */
+  function attemptTokenMatch(type, { noFlushTrivia } = {}) {
+    const re = tokenRe[type];
+    re.lastIndex = lastCharIndex;
+    const result = re.exec(str);
+    if (result) {
+      tokens.push({ type, value: result[0], trivia, line, index });
+      if (!noFlushTrivia) {
+        trivia = "";
+      }
+      return re.lastIndex;
+    }
+    return -1;
+  }
+}
+
+class Tokeniser {
+  /**
+   * @param {string} idl
+   */
+  constructor(idl) {
+    this.source = tokenise(idl);
+    this.position = 0;
+  }
+
+  /**
+   * @param {string} message
+   */
+  error(message) {
+    throw new WebIDLParseError(Object(_error_js__WEBPACK_IMPORTED_MODULE_0__["syntaxError"])(this.source, this.position, this.current, message));
+  }
+
+  /**
+   * @param {string} type
+   */
+  probe(type) {
+    return this.source.length > this.position && this.source[this.position].type === type;
+  }
+
+  /**
+   * @param  {...string} candidates
+   */
+  consume(...candidates) {
+    for (const type of candidates) {
+      if (!this.probe(type)) continue;
+      const token = this.source[this.position];
+      this.position++;
+      return token;
+    }
+  }
+
+  /**
+   * @param {number} position
+   */
+  unconsume(position) {
+    this.position = position;
+  }
+}
+
+class WebIDLParseError extends Error {
+  constructor({ message, bareMessage, context, line, sourceName, input, tokens }) {
+    super(message);
+
+    this.name = "WebIDLParseError"; // not to be mangled
+    this.bareMessage = bareMessage;
+    this.context = context;
+    this.line = line;
+    this.sourceName = sourceName;
+    this.input = input;
+    this.tokens = tokens;
+  }
+}
+
+
+/***/ }),
+/* 3 */
+/***/ (function(module, __webpack_exports__, __webpack_require__) {
+
+"use strict";
+__webpack_require__.r(__webpack_exports__);
+/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "syntaxError", function() { return syntaxError; });
+/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "validationError", function() { return validationError; });
+/**
+ * @param {string} text
+ */
+function lastLine(text) {
+  const splitted = text.split("\n");
+  return splitted[splitted.length - 1];
+}
+
+/**
+ * @typedef {object} WebIDL2ErrorOptions
+ * @property {"error" | "warning"} level
+ * @property {Function} autofix
+ *
+ * @param {string} message error message
+ * @param {"Syntax" | "Validation"} kind error type
+ * @param {WebIDL2ErrorOptions} [options]
+ */
+function error(source, position, current, message, kind, { level = "error", autofix, ruleName } = {}) {
+  /**
+   * @param {number} count
+   */
+  function sliceTokens(count) {
+    return count > 0 ?
+      source.slice(position, position + count) :
+      source.slice(Math.max(position + count, 0), position);
+  }
+
+  function tokensToText(inputs, { precedes } = {}) {
+    const text = inputs.map(t => t.trivia + t.value).join("");
+    const nextToken = source[position];
+    if (nextToken.type === "eof") {
+      return text;
+    }
+    if (precedes) {
+      return text + nextToken.trivia;
+    }
+    return text.slice(nextToken.trivia.length);
+  }
+
+  const maxTokens = 5; // arbitrary but works well enough
+  const line =
+    source[position].type !== "eof" ? source[position].line :
+    source.length > 1 ? source[position - 1].line :
+    1;
+
+  const precedingLastLine = lastLine(
+    tokensToText(sliceTokens(-maxTokens), { precedes: true })
+  );
+
+  const subsequentTokens = sliceTokens(maxTokens);
+  const subsequentText = tokensToText(subsequentTokens);
+  const subsequentFirstLine = subsequentText.split("\n")[0];
+
+  const spaced = " ".repeat(precedingLastLine.length) + "^";
+  const sourceContext = precedingLastLine + subsequentFirstLine + "\n" + spaced;
+
+  const contextType = kind === "Syntax" ? "since" : "inside";
+  const inSourceName = source.name ? ` in ${source.name}` : "";
+  const grammaticalContext = (current && current.name) ? `, ${contextType} \`${current.partial ? "partial " : ""}${current.type} ${current.name}\`` : "";
+  const context = `${kind} error at line ${line}${inSourceName}${grammaticalContext}:\n${sourceContext}`;
+  return {
+    message: `${context} ${message}`,
+    bareMessage: message,
+    context,
+    line,
+    sourceName: source.name,
+    level,
+    ruleName,
+    autofix,
+    input: subsequentText,
+    tokens: subsequentTokens
+  };
+}
+
+/**
+ * @param {string} message error message
+ */
+function syntaxError(source, position, current, message) {
+  return error(source, position, current, message, "Syntax");
+}
+
+/**
+ * @param {string} message error message
+ * @param {WebIDL2ErrorOptions} [options]
+ */
+function validationError(token, current, ruleName, message, options = {}) {
+  options.ruleName = ruleName;
+  return error(current.source, token.index, current, message, "Validation", options);
+}
+
+
+/***/ }),
+/* 4 */
+/***/ (function(module, __webpack_exports__, __webpack_require__) {
+
+"use strict";
+__webpack_require__.r(__webpack_exports__);
+/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "unescape", function() { return unescape; });
+/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "list", function() { return list; });
+/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "const_value", function() { return const_value; });
+/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "const_data", function() { return const_data; });
+/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "primitive_type", function() { return primitive_type; });
+/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "identifiers", function() { return identifiers; });
+/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "argument_list", function() { return argument_list; });
+/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "type_with_extended_attributes", function() { return type_with_extended_attributes; });
+/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "return_type", function() { return return_type; });
+/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "stringifier", function() { return stringifier; });
+/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "getLastIndentation", function() { return getLastIndentation; });
+/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "getMemberIndentation", function() { return getMemberIndentation; });
+/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "autofixAddExposedWindow", function() { return autofixAddExposedWindow; });
+/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "getFirstToken", function() { return getFirstToken; });
+/* harmony import */ var _type_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(5);
+/* harmony import */ var _argument_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(8);
+/* harmony import */ var _token_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(12);
+/* harmony import */ var _extended_attributes_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(10);
+/* harmony import */ var _operation_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(13);
+/* harmony import */ var _attribute_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(14);
+/* harmony import */ var _tokeniser_js__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(2);
+
+
+
+
+
+
+
+
+/**
+ * @param {string} identifier
+ */
+function unescape(identifier) {
+  return identifier.startsWith('_') ? identifier.slice(1) : identifier;
+}
+
+/**
+ * Parses comma-separated list
+ * @param {import("../tokeniser").Tokeniser} tokeniser
+ * @param {object} args
+ * @param {Function} args.parser parser function for each item
+ * @param {boolean} [args.allowDangler] whether to allow dangling comma
+ * @param {string} [args.listName] the name to be shown on error messages
+ */
+function list(tokeniser, { parser, allowDangler, listName = "list" }) {
+  const first = parser(tokeniser);
+  if (!first) {
+    return [];
+  }
+  first.tokens.separator = tokeniser.consume(",");
+  const items = [first];
+  while (first.tokens.separator) {
+    const item = parser(tokeniser);
+    if (!item) {
+      if (!allowDangler) {
+        tokeniser.error(`Trailing comma in ${listName}`);
+      }
+      break;
+    }
+    item.tokens.separator = tokeniser.consume(",");
+    items.push(item);
+    if (!item.tokens.separator) break;
+  }
+  return items;
+}
+
+/**
+ * @param {import("../tokeniser").Tokeniser} tokeniser
+ */
+function const_value(tokeniser) {
+  return tokeniser.consume("true", "false", "Infinity", "-Infinity", "NaN", "decimal", "integer");
+}
+
+/**
+ * @param {object} token
+ * @param {string} token.type
+ * @param {string} token.value
+ */
+function const_data({ type, value }) {
+  switch (type) {
+    case "true":
+    case "false":
+      return { type: "boolean", value: type === "true" };
+    case "Infinity":
+    case "-Infinity":
+      return { type: "Infinity", negative: type.startsWith("-") };
+    case "[":
+      return { type: "sequence", value: [] };
+    case "{":
+      return { type: "dictionary" };
+    case "decimal":
+    case "integer":
+      return { type: "number", value };
+    case "string":
+      return { type: "string", value: value.slice(1, -1) };
+    default:
+      return { type };
+  }
+}
+
+/**
+ * @param {import("../tokeniser").Tokeniser} tokeniser
+ */
+function primitive_type(tokeniser) {
+  function integer_type() {
+    const prefix = tokeniser.consume("unsigned");
+    const base = tokeniser.consume("short", "long");
+    if (base) {
+      const postfix = tokeniser.consume("long");
+      return new _type_js__WEBPACK_IMPORTED_MODULE_0__["Type"]({ source, tokens: { prefix, base, postfix } });
+    }
+    if (prefix) tokeniser.error("Failed to parse integer type");
+  }
+
+  function decimal_type() {
+    const prefix = tokeniser.consume("unrestricted");
+    const base = tokeniser.consume("float", "double");
+    if (base) {
+      return new _type_js__WEBPACK_IMPORTED_MODULE_0__["Type"]({ source, tokens: { prefix, base } });
+    }
+    if (prefix) tokeniser.error("Failed to parse float type");
+  }
+
+  const { source } = tokeniser;
+  const num_type = integer_type(tokeniser) || decimal_type(tokeniser);
+  if (num_type) return num_type;
+  const base = tokeniser.consume("boolean", "byte", "octet");
+  if (base) {
+    return new _type_js__WEBPACK_IMPORTED_MODULE_0__["Type"]({ source, tokens: { base } });
+  }
+}
+
+/**
+ * @param {import("../tokeniser").Tokeniser} tokeniser
+ */
+function identifiers(tokeniser) {
+  const ids = list(tokeniser, { parser: _token_js__WEBPACK_IMPORTED_MODULE_2__["Token"].parser(tokeniser, "identifier"), listName: "identifier list" });
+  if (!ids.length) {
+    tokeniser.error("Expected identifiers but none found");
+  }
+  return ids;
+}
+
+/**
+ * @param {import("../tokeniser").Tokeniser} tokeniser
+ */
+function argument_list(tokeniser) {
+  return list(tokeniser, { parser: _argument_js__WEBPACK_IMPORTED_MODULE_1__["Argument"].parse, listName: "arguments list" });
+}
+
+/**
+ * @param {import("../tokeniser").Tokeniser} tokeniser
+ * @param {string} typeName
+ */
+function type_with_extended_attributes(tokeniser, typeName) {
+  const extAttrs = _extended_attributes_js__WEBPACK_IMPORTED_MODULE_3__["ExtendedAttributes"].parse(tokeniser);
+  const ret = _type_js__WEBPACK_IMPORTED_MODULE_0__["Type"].parse(tokeniser, typeName);
+  if (ret) ret.extAttrs = extAttrs;
+  return ret;
+}
+
+/**
+ * @param {import("../tokeniser").Tokeniser} tokeniser
+ * @param {string} typeName
+ */
+function return_type(tokeniser, typeName) {
+  const typ = _type_js__WEBPACK_IMPORTED_MODULE_0__["Type"].parse(tokeniser, typeName || "return-type");
+  if (typ) {
+    return typ;
+  }
+  const voidToken = tokeniser.consume("void");
+  if (voidToken) {
+    const ret = new _type_js__WEBPACK_IMPORTED_MODULE_0__["Type"]({ source: tokeniser.source, tokens: { base: voidToken } });
+    ret.type = "return-type";
+    return ret;
+  }
+}
+
+/**
+ * @param {import("../tokeniser").Tokeniser} tokeniser
+ */
+function stringifier(tokeniser) {
+  const special = tokeniser.consume("stringifier");
+  if (!special) return;
+  const member = _attribute_js__WEBPACK_IMPORTED_MODULE_5__["Attribute"].parse(tokeniser, { special }) ||
+    _operation_js__WEBPACK_IMPORTED_MODULE_4__["Operation"].parse(tokeniser, { special }) ||
+    tokeniser.error("Unterminated stringifier");
+  return member;
+}
+
+/**
+ * @param {string} str
+ */
+function getLastIndentation(str) {
+  const lines = str.split("\n");
+  // the first line visually binds to the preceding token
+  if (lines.length) {
+    const match = lines[lines.length - 1].match(/^\s+/);
+    if (match) {
+      return match[0];
+    }
+  }
+  return "";
+}
+
+/**
+ * @param {string} parentTrivia
+ */
+function getMemberIndentation(parentTrivia) {
+  const indentation = getLastIndentation(parentTrivia);
+  const indentCh = indentation.includes("\t") ? "\t" : "  ";
+  return indentation + indentCh;
+}
+
+/**
+ * @param {object} def
+ * @param {import("./extended-attributes.js").ExtendedAttributes} def.extAttrs
+ */
+function autofixAddExposedWindow(def) {
+  return () => {
+    if (def.extAttrs.length){
+      const tokeniser = new _tokeniser_js__WEBPACK_IMPORTED_MODULE_6__["Tokeniser"]("Exposed=Window,");
+      const exposed = _extended_attributes_js__WEBPACK_IMPORTED_MODULE_3__["SimpleExtendedAttribute"].parse(tokeniser);
+      exposed.tokens.separator = tokeniser.consume(",");
+      const existing = def.extAttrs[0];
+      if (!/^\s/.test(existing.tokens.name.trivia)) {
+        existing.tokens.name.trivia = ` ${existing.tokens.name.trivia}`;
+      }
+      def.extAttrs.unshift(exposed);
+    } else {
+      def.extAttrs = _extended_attributes_js__WEBPACK_IMPORTED_MODULE_3__["ExtendedAttributes"].parse(new _tokeniser_js__WEBPACK_IMPORTED_MODULE_6__["Tokeniser"]("[Exposed=Window]"));
+      const trivia = def.tokens.base.trivia;
+      def.extAttrs.tokens.open.trivia = trivia;
+      def.tokens.base.trivia = `\n${getLastIndentation(trivia)}`;
     }
   };
+}
 
-  if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') {
-    module.exports = obj;
-  } else if (typeof define === 'function' && define.amd) {
-    define([], () => obj);
-  } else {
-    (self || window).WebIDL2 = obj;
+/**
+ * Get the first syntax token for the given IDL object.
+ * @param {*} data
+ */
+function getFirstToken(data) {
+  if (data.extAttrs.length) {
+    return data.extAttrs.tokens.open;
   }
-})();
+  if (data.type === "operation") {
+    return getFirstToken(data.idlType);
+  }
+  const tokens = Object.values(data.tokens).sort((x, y) => x.index - y.index);
+  return tokens[0];
+}
+
+
+/***/ }),
+/* 5 */
+/***/ (function(module, __webpack_exports__, __webpack_require__) {
+
+"use strict";
+__webpack_require__.r(__webpack_exports__);
+/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Type", function() { return Type; });
+/* harmony import */ var _base_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(6);
+/* harmony import */ var _helpers_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(4);
+/* harmony import */ var _tokeniser_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(2);
+/* harmony import */ var _error_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(3);
+/* harmony import */ var _validators_helpers_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(7);
+
+
+
+
+
+
+/**
+ * @param {import("../tokeniser").Tokeniser} tokeniser
+ * @param {string} typeName
+ */
+function generic_type(tokeniser, typeName) {
+  const base = tokeniser.consume("FrozenArray", "Promise", "sequence", "record");
+  if (!base) {
+    return;
+  }
+  const ret = new Type({ source: tokeniser.source, tokens: { base } });
+  ret.tokens.open = tokeniser.consume("<") || tokeniser.error(`No opening bracket after ${base.type}`);
+  switch (base.type) {
+    case "Promise": {
+      if (tokeniser.probe("[")) tokeniser.error("Promise type cannot have extended attribute");
+      const subtype = Object(_helpers_js__WEBPACK_IMPORTED_MODULE_1__["return_type"])(tokeniser, typeName) || tokeniser.error("Missing Promise subtype");
+      ret.subtype.push(subtype);
+      break;
+    }
+    case "sequence":
+    case "FrozenArray": {
+      const subtype = Object(_helpers_js__WEBPACK_IMPORTED_MODULE_1__["type_with_extended_attributes"])(tokeniser, typeName) || tokeniser.error(`Missing ${base.type} subtype`);
+      ret.subtype.push(subtype);
+      break;
+    }
+    case "record": {
+      if (tokeniser.probe("[")) tokeniser.error("Record key cannot have extended attribute");
+      const keyType = tokeniser.consume(..._tokeniser_js__WEBPACK_IMPORTED_MODULE_2__["stringTypes"]) || tokeniser.error(`Record key must be one of: ${_tokeniser_js__WEBPACK_IMPORTED_MODULE_2__["stringTypes"].join(", ")}`);
+      const keyIdlType = new Type({ source: tokeniser.source, tokens: { base: keyType }});
+      keyIdlType.tokens.separator = tokeniser.consume(",") || tokeniser.error("Missing comma after record key type");
+      keyIdlType.type = typeName;
+      const valueType = Object(_helpers_js__WEBPACK_IMPORTED_MODULE_1__["type_with_extended_attributes"])(tokeniser, typeName) || tokeniser.error("Error parsing generic type record");
+      ret.subtype.push(keyIdlType, valueType);
+      break;
+    }
+  }
+  if (!ret.idlType) tokeniser.error(`Error parsing generic type ${base.type}`);
+  ret.tokens.close = tokeniser.consume(">") || tokeniser.error(`Missing closing bracket after ${base.type}`);
+  return ret;
+}
+
+/**
+ * @param {import("../tokeniser").Tokeniser} tokeniser
+ */
+function type_suffix(tokeniser, obj) {
+  const nullable = tokeniser.consume("?");
+  if (nullable) {
+    obj.tokens.nullable = nullable;
+  }
+  if (tokeniser.probe("?")) tokeniser.error("Can't nullable more than once");
+}
+
+/**
+ * @param {import("../tokeniser").Tokeniser} tokeniser
+ * @param {string} typeName
+ */
+function single_type(tokeniser, typeName) {
+  let ret = generic_type(tokeniser, typeName) || Object(_helpers_js__WEBPACK_IMPORTED_MODULE_1__["primitive_type"])(tokeniser);
+  if (!ret) {
+    const base = tokeniser.consume("identifier", ..._tokeniser_js__WEBPACK_IMPORTED_MODULE_2__["stringTypes"]);
+    if (!base) {
+      return;
+    }
+    ret = new Type({ source: tokeniser.source, tokens: { base } });
+    if (tokeniser.probe("<")) tokeniser.error(`Unsupported generic type ${base.value}`);
+  }
+  if (ret.generic === "Promise" && tokeniser.probe("?")) {
+    tokeniser.error("Promise type cannot be nullable");
+  }
+  ret.type = typeName || null;
+  type_suffix(tokeniser, ret);
+  if (ret.nullable && ret.idlType === "any") tokeniser.error("Type `any` cannot be made nullable");
+  return ret;
+}
+
+/**
+ * @param {import("../tokeniser").Tokeniser} tokeniser
+ * @param {string} type
+ */
+function union_type(tokeniser, type) {
+  const tokens = {};
+  tokens.open = tokeniser.consume("(");
+  if (!tokens.open) return;
+  const ret = new Type({ source: tokeniser.source, tokens });
+  ret.type = type || null;
+  while (true) {
+    const typ = Object(_helpers_js__WEBPACK_IMPORTED_MODULE_1__["type_with_extended_attributes"])(tokeniser) || tokeniser.error("No type after open parenthesis or 'or' in union type");
+    if (typ.idlType === "any") tokeniser.error("Type `any` cannot be included in a union type");
+    ret.subtype.push(typ);
+    const or = tokeniser.consume("or");
+    if (or) {
+      typ.tokens.separator = or;
+    }
+    else break;
+  }
+  if (ret.idlType.length < 2) {
+    tokeniser.error("At least two types are expected in a union type but found less");
+  }
+  tokens.close = tokeniser.consume(")") || tokeniser.error("Unterminated union type");
+  type_suffix(tokeniser, ret);
+  return ret;
+}
+
+class Type extends _base_js__WEBPACK_IMPORTED_MODULE_0__["Base"] {
+  /**
+   * @param {import("../tokeniser").Tokeniser} tokeniser
+   * @param {string} typeName
+   */
+  static parse(tokeniser, typeName) {
+    return single_type(tokeniser, typeName) || union_type(tokeniser, typeName);
+  }
+
+  constructor({ source, tokens }) {
+    super({ source, tokens });
+    Object.defineProperty(this, "subtype", { value: [] });
+    this.extAttrs = [];
+  }
+
+  get generic() {
+    if (this.subtype.length && this.tokens.base) {
+      return this.tokens.base.value;
+    }
+    return "";
+  }
+  get nullable() {
+    return Boolean(this.tokens.nullable);
+  }
+  get union() {
+    return Boolean(this.subtype.length) && !this.tokens.base;
+  }
+  get idlType() {
+    if (this.subtype.length) {
+      return this.subtype;
+    }
+    // Adding prefixes/postfixes for "unrestricted float", etc.
+    const name = [
+      this.tokens.prefix,
+      this.tokens.base,
+      this.tokens.postfix
+    ].filter(t => t).map(t => t.value).join(" ");
+    return Object(_helpers_js__WEBPACK_IMPORTED_MODULE_1__["unescape"])(name);
+  }
+
+  *validate(defs) {
+    /*
+     * If a union is nullable, its subunions cannot include a dictionary
+     * If not, subunions may include dictionaries if each union is not nullable
+     */
+    const typedef = !this.union && defs.unique.get(this.idlType);
+    const target =
+      this.union ? this :
+      (typedef && typedef.type === "typedef") ? typedef.idlType :
+      undefined;
+    if (target && this.nullable) {
+      // do not allow any dictionary
+      const reference = Object(_validators_helpers_js__WEBPACK_IMPORTED_MODULE_4__["idlTypeIncludesDictionary"])(target, defs);
+      if (reference) {
+        const targetToken = (this.union ? reference : this).tokens.base;
+        const message = `Nullable union cannot include a dictionary type`;
+        yield Object(_error_js__WEBPACK_IMPORTED_MODULE_3__["validationError"])(targetToken, this, "no-nullable-union-dict", message);
+      }
+    } else {
+      // allow some dictionary
+      for (const subtype of this.subtype) {
+        yield* subtype.validate(defs);
+      }
+    }
+  }
+}
+
+
+/***/ }),
+/* 6 */
+/***/ (function(module, __webpack_exports__, __webpack_require__) {
+
+"use strict";
+__webpack_require__.r(__webpack_exports__);
+/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Base", function() { return Base; });
+class Base {
+  constructor({ source, tokens }) {
+    Object.defineProperties(this, {
+      source: { value: source },
+      tokens: { value: tokens }
+    });
+  }
+
+  toJSON() {
+    const json = { type: undefined, name: undefined, inheritance: undefined };
+    let proto = this;
+    while (proto !== Object.prototype) {
+      const descMap = Object.getOwnPropertyDescriptors(proto);
+      for (const [key, value] of Object.entries(descMap)) {
+        if (value.enumerable || value.get) {
+          json[key] = this[key];
+        }
+      }
+      proto = Object.getPrototypeOf(proto);
+    }
+    return json;
+  }
+}
+
+
+/***/ }),
+/* 7 */
+/***/ (function(module, __webpack_exports__, __webpack_require__) {
+
+"use strict";
+__webpack_require__.r(__webpack_exports__);
+/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "idlTypeIncludesDictionary", function() { return idlTypeIncludesDictionary; });
+/**
+ * @param {*} idlType
+ * @param {*[]} defs
+ * @param {object} [options]
+ * @param {boolean} [options.useNullableInner] use when the input idlType is nullable and you want to use its inner type
+ * @return the type reference that ultimately includes dictionary.
+ */
+function idlTypeIncludesDictionary(idlType, defs, { useNullableInner } = {}) {
+  if (!idlType.union) {
+    const def = defs.unique.get(idlType.idlType);
+    if (!def) {
+      return;
+    }
+    if (def.type === "typedef") {
+      const { typedefIncludesDictionary} = defs.cache;
+      if (typedefIncludesDictionary.has(def)) {
+        // Note that this also halts when it met indeterminate state
+        // to prevent infinite recursion
+        return typedefIncludesDictionary.get(def);
+      }
+      defs.cache.typedefIncludesDictionary.set(def, undefined); // indeterminate state
+      const result = idlTypeIncludesDictionary(def.idlType, defs);
+      defs.cache.typedefIncludesDictionary.set(def, result);
+      if (result) {
+        return idlType;
+      }
+    }
+    if (def.type === "dictionary" && (useNullableInner || !idlType.nullable)) {
+      return idlType;
+    }
+  }
+  for (const subtype of idlType.subtype) {
+    const result = idlTypeIncludesDictionary(subtype, defs);
+    if (result) {
+      if (subtype.union) {
+        return result;
+      }
+      return subtype;
+    }
+  }
+}
+
+
+/***/ }),
+/* 8 */
+/***/ (function(module, __webpack_exports__, __webpack_require__) {
+
+"use strict";
+__webpack_require__.r(__webpack_exports__);
+/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Argument", function() { return Argument; });
+/* harmony import */ var _base_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(6);
+/* harmony import */ var _default_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(9);
+/* harmony import */ var _extended_attributes_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(10);
+/* harmony import */ var _helpers_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(4);
+/* harmony import */ var _tokeniser_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(2);
+/* harmony import */ var _error_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(3);
+/* harmony import */ var _validators_helpers_js__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(7);
+
+
+
+
+
+
+
+
+class Argument extends _base_js__WEBPACK_IMPORTED_MODULE_0__["Base"] {
+  /**
+   * @param {import("../tokeniser").Tokeniser} tokeniser
+   */
+  static parse(tokeniser) {
+    const start_position = tokeniser.position;
+    const tokens = {};
+    const ret = new Argument({ source: tokeniser.source, tokens });
+    ret.extAttrs = _extended_attributes_js__WEBPACK_IMPORTED_MODULE_2__["ExtendedAttributes"].parse(tokeniser);
+    tokens.optional = tokeniser.consume("optional");
+    ret.idlType = Object(_helpers_js__WEBPACK_IMPORTED_MODULE_3__["type_with_extended_attributes"])(tokeniser, "argument-type");
+    if (!ret.idlType) {
+      return tokeniser.unconsume(start_position);
+    }
+    if (!tokens.optional) {
+      tokens.variadic = tokeniser.consume("...");
+    }
+    tokens.name = tokeniser.consume("identifier", ..._tokeniser_js__WEBPACK_IMPORTED_MODULE_4__["argumentNameKeywords"]);
+    if (!tokens.name) {
+      return tokeniser.unconsume(start_position);
+    }
+    ret.default = tokens.optional ? _default_js__WEBPACK_IMPORTED_MODULE_1__["Default"].parse(tokeniser) : null;
+    return ret;
+  }
+
+  get type() {
+    return "argument";
+  }
+  get optional() {
+    return !!this.tokens.optional;
+  }
+  get variadic() {
+    return !!this.tokens.variadic;
+  }
+  get name() {
+    return Object(_helpers_js__WEBPACK_IMPORTED_MODULE_3__["unescape"])(this.tokens.name.value);
+  }
+
+  *validate(defs) {
+    yield* this.idlType.validate(defs);
+    if (Object(_validators_helpers_js__WEBPACK_IMPORTED_MODULE_6__["idlTypeIncludesDictionary"])(this.idlType, defs, { useNullableInner: true })) {
+      if (this.idlType.nullable) {
+        const message = `Dictionary arguments cannot be nullable.`;
+        yield Object(_error_js__WEBPACK_IMPORTED_MODULE_5__["validationError"])(this.tokens.name, this, "no-nullable-dict-arg", message);
+      } else if (this.optional && !this.default) {
+        const message = `Optional dictionary arguments must have a default value of \`{}\`.`;
+        yield Object(_error_js__WEBPACK_IMPORTED_MODULE_5__["validationError"])(this.tokens.name, this, "dict-arg-default", message, {
+          autofix: autofixOptionalDictionaryDefaultValue(this)
+        });
+      }
+    }
+  }
+}
+
+/**
+ * @param {Argument} arg
+ */
+function autofixOptionalDictionaryDefaultValue(arg) {
+  return () => {
+    arg.default = _default_js__WEBPACK_IMPORTED_MODULE_1__["Default"].parse(new _tokeniser_js__WEBPACK_IMPORTED_MODULE_4__["Tokeniser"](" = {}"));
+  };
+}
+
+
+/***/ }),
+/* 9 */
+/***/ (function(module, __webpack_exports__, __webpack_require__) {
+
+"use strict";
+__webpack_require__.r(__webpack_exports__);
+/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Default", function() { return Default; });
+/* harmony import */ var _base_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(6);
+/* harmony import */ var _helpers_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(4);
+
+
+
+class Default extends _base_js__WEBPACK_IMPORTED_MODULE_0__["Base"] {
+  /**
+   * @param {import("../tokeniser").Tokeniser} tokeniser
+   */
+  static parse(tokeniser) {
+    const assign = tokeniser.consume("=");
+    if (!assign) {
+      return null;
+    }
+    const def = Object(_helpers_js__WEBPACK_IMPORTED_MODULE_1__["const_value"])(tokeniser) || tokeniser.consume("string", "null", "[", "{") || tokeniser.error("No value for default");
+    const expression = [def];
+    if (def.type === "[") {
+      const close = tokeniser.consume("]") || tokeniser.error("Default sequence value must be empty");
+      expression.push(close);
+    } else if (def.type === "{") {
+      const close = tokeniser.consume("}") || tokeniser.error("Default dictionary value must be empty");
+      expression.push(close);
+    }
+    return new Default({ source: tokeniser.source, tokens: { assign }, expression });
+  }
+
+  constructor({ source, tokens, expression }) {
+    super({ source, tokens });
+    Object.defineProperty(this, "expression", { value: expression });
+  }
+
+  get type() {
+    return Object(_helpers_js__WEBPACK_IMPORTED_MODULE_1__["const_data"])(this.expression[0]).type;
+  }
+  get value() {
+    return Object(_helpers_js__WEBPACK_IMPORTED_MODULE_1__["const_data"])(this.expression[0]).value;
+  }
+  get negative() {
+    return Object(_helpers_js__WEBPACK_IMPORTED_MODULE_1__["const_data"])(this.expression[0]).negative;
+  }
+}
+
+
+/***/ }),
+/* 10 */
+/***/ (function(module, __webpack_exports__, __webpack_require__) {
+
+"use strict";
+__webpack_require__.r(__webpack_exports__);
+/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "SimpleExtendedAttribute", function() { return SimpleExtendedAttribute; });
+/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ExtendedAttributes", function() { return ExtendedAttributes; });
+/* harmony import */ var _base_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(6);
+/* harmony import */ var _array_base_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(11);
+/* harmony import */ var _helpers_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(4);
+/* harmony import */ var _error_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(3);
+
+
+
+
+
+class ExtendedAttributeParameters extends _base_js__WEBPACK_IMPORTED_MODULE_0__["Base"] {
+  /**
+   * @param {import("../tokeniser").Tokeniser} tokeniser
+   */
+  static parse(tokeniser) {
+    const tokens = { assign: tokeniser.consume("=") };
+    const ret = new ExtendedAttributeParameters({ source: tokeniser.source, tokens });
+    if (tokens.assign) {
+      tokens.secondaryName = tokeniser.consume("identifier", "decimal", "integer", "string");
+    }
+    tokens.open = tokeniser.consume("(");
+    if (tokens.open) {
+      ret.list = ret.rhsType === "identifier-list" ?
+        // [Exposed=(Window,Worker)]
+        Object(_helpers_js__WEBPACK_IMPORTED_MODULE_2__["identifiers"])(tokeniser) :
+        // [NamedConstructor=Audio(DOMString src)] or [Constructor(DOMString str)]
+        Object(_helpers_js__WEBPACK_IMPORTED_MODULE_2__["argument_list"])(tokeniser);
+      tokens.close = tokeniser.consume(")") || tokeniser.error("Unexpected token in extended attribute argument list");
+    } else if (ret.hasRhs && !tokens.secondaryName) {
+      tokeniser.error("No right hand side to extended attribute assignment");
+    }
+    return ret;
+  }
+
+  get rhsType() {
+    return !this.tokens.assign ? null :
+      !this.tokens.secondaryName ? "identifier-list" :
+        this.tokens.secondaryName.type;
+  }
+}
+
+class SimpleExtendedAttribute extends _base_js__WEBPACK_IMPORTED_MODULE_0__["Base"] {
+  /**
+   * @param {import("../tokeniser").Tokeniser} tokeniser
+   */
+  static parse(tokeniser) {
+    const name = tokeniser.consume("identifier");
+    if (name) {
+      return new SimpleExtendedAttribute({
+        source: tokeniser.source,
+        tokens: { name },
+        params: ExtendedAttributeParameters.parse(tokeniser)
+      });
+    }
+  }
+
+  constructor({ source, tokens, params }) {
+    super({ source, tokens });
+    Object.defineProperty(this, "params", { value: params });
+  }
+
+  get type() {
+    return "extended-attribute";
+  }
+  get name() {
+    return this.tokens.name.value;
+  }
+  get rhs() {
+    const { rhsType: type, tokens, list } = this.params;
+    if (!type) {
+      return null;
+    }
+    const value = type === "identifier-list" ? list : tokens.secondaryName.value;
+    return { type, value };
+  }
+  get arguments() {
+    const { rhsType, list } = this.params;
+    if (!list || rhsType === "identifier-list") {
+      return [];
+    }
+    return list;
+  }
+
+  *validate(defs) {
+    if (this.name === "NoInterfaceObject") {
+      const message = `\`[NoInterfaceObject]\` extended attribute is an \
+undesirable feature that may be removed from Web IDL in the future. Refer to the \
+[relevant upstream PR](https://github.com/heycam/webidl/pull/609) for more \
+information.`;
+      yield Object(_error_js__WEBPACK_IMPORTED_MODULE_3__["validationError"])(this.tokens.name, this, "no-nointerfaceobject", message, { level: "warning" });
+    }
+    for (const arg of this.arguments) {
+      yield* arg.validate(defs);
+    }
+  }
+}
+
+// Note: we parse something simpler than the official syntax. It's all that ever
+// seems to be used
+class ExtendedAttributes extends _array_base_js__WEBPACK_IMPORTED_MODULE_1__["ArrayBase"] {
+  /**
+   * @param {import("../tokeniser").Tokeniser} tokeniser
+   */
+  static parse(tokeniser) {
+    const tokens = {};
+    tokens.open = tokeniser.consume("[");
+    if (!tokens.open) return new ExtendedAttributes({});
+    const ret = new ExtendedAttributes({ source: tokeniser.source, tokens });
+    ret.push(...Object(_helpers_js__WEBPACK_IMPORTED_MODULE_2__["list"])(tokeniser, {
+      parser: SimpleExtendedAttribute.parse,
+      listName: "extended attribute"
+    }));
+    tokens.close = tokeniser.consume("]") || tokeniser.error("Unexpected closing token of extended attribute");
+    if (!ret.length) {
+      tokeniser.error("Found an empty extended attribute");
+    }
+    if (tokeniser.probe("[")) {
+      tokeniser.error("Illegal double extended attribute lists, consider merging them");
+    }
+    return ret;
+  }
+
+  *validate(defs) {
+    for (const extAttr of this) {
+      yield* extAttr.validate(defs);
+    }
+  }
+}
+
+
+/***/ }),
+/* 11 */
+/***/ (function(module, __webpack_exports__, __webpack_require__) {
+
+"use strict";
+__webpack_require__.r(__webpack_exports__);
+/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ArrayBase", function() { return ArrayBase; });
+class ArrayBase extends Array {
+  constructor({ source, tokens }) {
+    super();
+    Object.defineProperties(this, {
+      source: { value: source },
+      tokens: { value: tokens }
+    });
+  }
+}
+
+
+/***/ }),
+/* 12 */
+/***/ (function(module, __webpack_exports__, __webpack_require__) {
+
+"use strict";
+__webpack_require__.r(__webpack_exports__);
+/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Token", function() { return Token; });
+/* harmony import */ var _base_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(6);
+
+
+class Token extends _base_js__WEBPACK_IMPORTED_MODULE_0__["Base"] {
+  /**
+   * @param {import("../tokeniser").Tokeniser} tokeniser
+   * @param {string} type
+   */
+  static parser(tokeniser, type) {
+    return () => {
+      const value = tokeniser.consume(type);
+      if (value) {
+        return new Token({ source: tokeniser.source, tokens: { value } });
+      }
+    };
+  }
+
+  get value() {
+    return this.tokens.value.value;
+  }
+}
+
+
+/***/ }),
+/* 13 */
+/***/ (function(module, __webpack_exports__, __webpack_require__) {
+
+"use strict";
+__webpack_require__.r(__webpack_exports__);
+/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Operation", function() { return Operation; });
+/* harmony import */ var _base_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(6);
+/* harmony import */ var _helpers_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(4);
+/* harmony import */ var _error_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(3);
+
+
+
+
+class Operation extends _base_js__WEBPACK_IMPORTED_MODULE_0__["Base"] {
+  /**
+   * @param {import("../tokeniser.js").Tokeniser} tokeniser
+   */
+  static parse(tokeniser, { special, regular } = {}) {
+    const tokens = { special };
+    const ret = new Operation({ source: tokeniser.source, tokens });
+    if (special && special.value === "stringifier") {
+      tokens.termination = tokeniser.consume(";");
+      if (tokens.termination) {
+        ret.arguments = [];
+        return ret;
+      }
+    }
+    if (!special && !regular) {
+      tokens.special = tokeniser.consume("getter", "setter", "deleter");
+    }
+    ret.idlType = Object(_helpers_js__WEBPACK_IMPORTED_MODULE_1__["return_type"])(tokeniser) || tokeniser.error("Missing return type");
+    tokens.name = tokeniser.consume("identifier", "includes");
+    tokens.open = tokeniser.consume("(") || tokeniser.error("Invalid operation");
+    ret.arguments = Object(_helpers_js__WEBPACK_IMPORTED_MODULE_1__["argument_list"])(tokeniser);
+    tokens.close = tokeniser.consume(")") || tokeniser.error("Unterminated operation");
+    tokens.termination = tokeniser.consume(";") || tokeniser.error("Unterminated operation, expected `;`");
+    return ret;
+  }
+
+  get type() {
+    return "operation";
+  }
+  get name() {
+    const { name } = this.tokens;
+    if (!name) {
+      return "";
+    }
+    return Object(_helpers_js__WEBPACK_IMPORTED_MODULE_1__["unescape"])(name.value);
+  }
+  get special() {
+    if (!this.tokens.special) {
+      return "";
+    }
+    return this.tokens.special.value;
+  }
+
+  *validate(defs) {
+    if (!this.name && ["", "static"].includes(this.special)) {
+      const message = `Regular or static operations must have both a return type and an identifier.`;
+      yield Object(_error_js__WEBPACK_IMPORTED_MODULE_2__["validationError"])(this.tokens.open, this, "incomplete-op", message);
+    }
+    if (this.idlType) {
+      yield* this.idlType.validate(defs);
+    }
+    for (const argument of this.arguments) {
+      yield* argument.validate(defs);
+    }
+  }
+}
+
+
+/***/ }),
+/* 14 */
+/***/ (function(module, __webpack_exports__, __webpack_require__) {
+
+"use strict";
+__webpack_require__.r(__webpack_exports__);
+/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Attribute", function() { return Attribute; });
+/* harmony import */ var _base_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(6);
+/* harmony import */ var _helpers_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(4);
+
+
+
+class Attribute extends _base_js__WEBPACK_IMPORTED_MODULE_0__["Base"] {
+  /**
+   * @param {import("../tokeniser.js").Tokeniser} tokeniser
+   */
+  static parse(tokeniser, { special, noInherit = false, readonly = false } = {}) {
+    const start_position = tokeniser.position;
+    const tokens = { special };
+    const ret = new Attribute({ source: tokeniser.source, tokens });
+    if (!special && !noInherit) {
+      tokens.special = tokeniser.consume("inherit");
+    }
+    if (ret.special === "inherit" && tokeniser.probe("readonly")) {
+      tokeniser.error("Inherited attributes cannot be read-only");
+    }
+    tokens.readonly = tokeniser.consume("readonly");
+    if (readonly && !tokens.readonly && tokeniser.probe("attribute")) {
+      tokeniser.error("Attributes must be readonly in this context");
+    }
+    tokens.base = tokeniser.consume("attribute");
+    if (!tokens.base) {
+      tokeniser.unconsume(start_position);
+      return;
+    }
+    ret.idlType = Object(_helpers_js__WEBPACK_IMPORTED_MODULE_1__["type_with_extended_attributes"])(tokeniser, "attribute-type") || tokeniser.error("Attribute lacks a type");
+    switch (ret.idlType.generic) {
+      case "sequence":
+      case "record": tokeniser.error(`Attributes cannot accept ${ret.idlType.generic} types`);
+    }
+    tokens.name = tokeniser.consume("identifier", "async", "required") || tokeniser.error("Attribute lacks a name");
+    tokens.termination = tokeniser.consume(";") || tokeniser.error("Unterminated attribute, expected `;`");
+    return ret;
+  }
+
+  get type() {
+    return "attribute";
+  }
+  get special() {
+    if (!this.tokens.special) {
+      return "";
+    }
+    return this.tokens.special.value;
+  }
+  get readonly() {
+    return !!this.tokens.readonly;
+  }
+  get name() {
+    return Object(_helpers_js__WEBPACK_IMPORTED_MODULE_1__["unescape"])(this.tokens.name.value);
+  }
+
+  *validate(defs) {
+    yield* this.idlType.validate(defs);
+  }
+}
+
+
+/***/ }),
+/* 15 */
+/***/ (function(module, __webpack_exports__, __webpack_require__) {
+
+"use strict";
+__webpack_require__.r(__webpack_exports__);
+/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Enum", function() { return Enum; });
+/* harmony import */ var _helpers_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4);
+/* harmony import */ var _token_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(12);
+/* harmony import */ var _base_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(6);
+
+
+
+
+class EnumValue extends _token_js__WEBPACK_IMPORTED_MODULE_1__["Token"] {
+  /**
+   * @param {import("../tokeniser").Tokeniser} tokeniser
+   */
+  static parse(tokeniser) {
+    const value = tokeniser.consume("string");
+    if (value) {
+      return new EnumValue({ source: tokeniser.source, tokens: { value } });
+    }
+  }
+
+  get type() {
+    return "enum-value";
+  }
+  get value() {
+    return super.value.slice(1, -1);
+  }
+}
+
+class Enum extends _base_js__WEBPACK_IMPORTED_MODULE_2__["Base"] {
+  /**
+   * @param {import("../tokeniser").Tokeniser} tokeniser
+   */
+  static parse(tokeniser) {
+    const tokens = {};
+    tokens.base = tokeniser.consume("enum");
+    if (!tokens.base) {
+      return;
+    }
+    tokens.name = tokeniser.consume("identifier") || tokeniser.error("No name for enum");
+    const ret = tokeniser.current = new Enum({ source: tokeniser.source, tokens });
+    tokens.open = tokeniser.consume("{") || tokeniser.error("Bodyless enum");
+    ret.values = Object(_helpers_js__WEBPACK_IMPORTED_MODULE_0__["list"])(tokeniser, {
+      parser: EnumValue.parse,
+      allowDangler: true,
+      listName: "enumeration"
+    });
+    if (tokeniser.probe("string")) {
+      tokeniser.error("No comma between enum values");
+    }
+    tokens.close = tokeniser.consume("}") || tokeniser.error("Unexpected value in enum");
+    if (!ret.values.length) {
+      tokeniser.error("No value in enum");
+    }
+    tokens.termination = tokeniser.consume(";") || tokeniser.error("No semicolon after enum");
+    return ret;
+  }
+
+  get type() {
+    return "enum";
+  }
+  get name() {
+    return Object(_helpers_js__WEBPACK_IMPORTED_MODULE_0__["unescape"])(this.tokens.name.value);
+  }
+}
+
+
+/***/ }),
+/* 16 */
+/***/ (function(module, __webpack_exports__, __webpack_require__) {
+
+"use strict";
+__webpack_require__.r(__webpack_exports__);
+/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Includes", function() { return Includes; });
+/* harmony import */ var _base_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(6);
+/* harmony import */ var _helpers_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(4);
+
+
+
+class Includes extends _base_js__WEBPACK_IMPORTED_MODULE_0__["Base"] {
+  /**
+   * @param {import("../tokeniser").Tokeniser} tokeniser
+   */
+  static parse(tokeniser) {
+    const target = tokeniser.consume("identifier");
+    if (!target) {
+      return;
+    }
+    const tokens = { target };
+    tokens.includes = tokeniser.consume("includes");
+    if (!tokens.includes) {
+      tokeniser.unconsume(target.index);
+      return;
+    }
+    tokens.mixin = tokeniser.consume("identifier") || tokeniser.error("Incomplete includes statement");
+    tokens.termination = tokeniser.consume(";") || tokeniser.error("No terminating ; for includes statement");
+    return new Includes({ source: tokeniser.source, tokens });
+  }
+
+  get type() {
+    return "includes";
+  }
+  get target() {
+    return Object(_helpers_js__WEBPACK_IMPORTED_MODULE_1__["unescape"])(this.tokens.target.value);
+  }
+  get includes() {
+    return Object(_helpers_js__WEBPACK_IMPORTED_MODULE_1__["unescape"])(this.tokens.mixin.value);
+  }
+}
+
+
+/***/ }),
+/* 17 */
+/***/ (function(module, __webpack_exports__, __webpack_require__) {
+
+"use strict";
+__webpack_require__.r(__webpack_exports__);
+/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Typedef", function() { return Typedef; });
+/* harmony import */ var _base_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(6);
+/* harmony import */ var _helpers_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(4);
+
+
+
+class Typedef extends _base_js__WEBPACK_IMPORTED_MODULE_0__["Base"] {
+  /**
+   * @param {import("../tokeniser").Tokeniser} tokeniser
+   */
+  static parse(tokeniser) {
+    const tokens = {};
+    const ret = new Typedef({ source: tokeniser.source, tokens });
+    tokens.base = tokeniser.consume("typedef");
+    if (!tokens.base) {
+      return;
+    }
+    ret.idlType = Object(_helpers_js__WEBPACK_IMPORTED_MODULE_1__["type_with_extended_attributes"])(tokeniser, "typedef-type") || tokeniser.error("Typedef lacks a type");
+    tokens.name = tokeniser.consume("identifier") || tokeniser.error("Typedef lacks a name");
+    tokeniser.current = ret;
+    tokens.termination = tokeniser.consume(";") || tokeniser.error("Unterminated typedef, expected `;`");
+    return ret;
+  }
+
+  get type() {
+    return "typedef";
+  }
+  get name() {
+    return Object(_helpers_js__WEBPACK_IMPORTED_MODULE_1__["unescape"])(this.tokens.name.value);
+  }
+
+  *validate(defs) {
+    yield* this.idlType.validate(defs);
+  }
+}
+
+
+/***/ }),
+/* 18 */
+/***/ (function(module, __webpack_exports__, __webpack_require__) {
+
+"use strict";
+__webpack_require__.r(__webpack_exports__);
+/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "CallbackFunction", function() { return CallbackFunction; });
+/* harmony import */ var _base_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(6);
+/* harmony import */ var _helpers_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(4);
+
+
+
+class CallbackFunction extends _base_js__WEBPACK_IMPORTED_MODULE_0__["Base"] {
+  /**
+   * @param {import("../tokeniser.js").Tokeniser} tokeniser
+   */
+  static parse(tokeniser, base) {
+    const tokens = { base };
+    const ret = new CallbackFunction({ source: tokeniser.source, tokens });
+    tokens.name = tokeniser.consume("identifier") || tokeniser.error("Callback lacks a name");
+    tokeniser.current = ret;
+    tokens.assign = tokeniser.consume("=") || tokeniser.error("Callback lacks an assignment");
+    ret.idlType = Object(_helpers_js__WEBPACK_IMPORTED_MODULE_1__["return_type"])(tokeniser) || tokeniser.error("Callback lacks a return type");
+    tokens.open = tokeniser.consume("(") || tokeniser.error("Callback lacks parentheses for arguments");
+    ret.arguments = Object(_helpers_js__WEBPACK_IMPORTED_MODULE_1__["argument_list"])(tokeniser);
+    tokens.close = tokeniser.consume(")") || tokeniser.error("Unterminated callback");
+    tokens.termination = tokeniser.consume(";") || tokeniser.error("Unterminated callback, expected `;`");
+    return ret;
+  }
+
+  get type() {
+    return "callback";
+  }
+  get name() {
+    return Object(_helpers_js__WEBPACK_IMPORTED_MODULE_1__["unescape"])(this.tokens.name.value);
+  }
+
+  *validate(defs) {
+    yield* this.idlType.validate(defs);
+  }
+}
+
+
+/***/ }),
+/* 19 */
+/***/ (function(module, __webpack_exports__, __webpack_require__) {
+
+"use strict";
+__webpack_require__.r(__webpack_exports__);
+/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Interface", function() { return Interface; });
+/* harmony import */ var _container_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(20);
+/* harmony import */ var _attribute_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(14);
+/* harmony import */ var _operation_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(13);
+/* harmony import */ var _constant_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(21);
+/* harmony import */ var _iterable_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(22);
+/* harmony import */ var _helpers_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(4);
+/* harmony import */ var _error_js__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(3);
+/* harmony import */ var _validators_interface_js__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(23);
+/* harmony import */ var _constructor_js__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(24);
+/* harmony import */ var _tokeniser_js__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(2);
+
+
+
+
+
+
+
+
+
+
+
+/**
+ * @param {import("../tokeniser").Tokeniser} tokeniser
+ */
+function static_member(tokeniser) {
+  const special = tokeniser.consume("static");
+  if (!special) return;
+  const member = _attribute_js__WEBPACK_IMPORTED_MODULE_1__["Attribute"].parse(tokeniser, { special }) ||
+    _operation_js__WEBPACK_IMPORTED_MODULE_2__["Operation"].parse(tokeniser, { special }) ||
+    tokeniser.error("No body in static member");
+  return member;
+}
+
+class Interface extends _container_js__WEBPACK_IMPORTED_MODULE_0__["Container"] {
+  /**
+   * @param {import("../tokeniser").Tokeniser} tokeniser
+   */
+  static parse(tokeniser, base, { partial = null } = {}) {
+    const tokens = { partial, base };
+    return _container_js__WEBPACK_IMPORTED_MODULE_0__["Container"].parse(tokeniser, new Interface({ source: tokeniser.source, tokens }), {
+      type: "interface",
+      inheritable: !partial,
+      allowedMembers: [
+        [_constant_js__WEBPACK_IMPORTED_MODULE_3__["Constant"].parse],
+        [_constructor_js__WEBPACK_IMPORTED_MODULE_8__["Constructor"].parse],
+        [static_member],
+        [_helpers_js__WEBPACK_IMPORTED_MODULE_5__["stringifier"]],
+        [_iterable_js__WEBPACK_IMPORTED_MODULE_4__["IterableLike"].parse],
+        [_attribute_js__WEBPACK_IMPORTED_MODULE_1__["Attribute"].parse],
+        [_operation_js__WEBPACK_IMPORTED_MODULE_2__["Operation"].parse]
+      ]
+    });
+  }
+
+  get type() {
+    return "interface";
+  }
+
+  *validate(defs) {
+    yield* this.extAttrs.validate(defs);
+    if (
+      !this.partial &&
+      this.extAttrs.every(extAttr => extAttr.name !== "Exposed") &&
+      this.extAttrs.every(extAttr => extAttr.name !== "NoInterfaceObject")
+    ) {
+      const message = `Interfaces must have \`[Exposed]\` extended attribute. \
+To fix, add, for example, \`[Exposed=Window]\`. Please also consider carefully \
+if your interface should also be exposed in a Worker scope. Refer to the \
+[WebIDL spec section on Exposed](https://heycam.github.io/webidl/#Exposed) \
+for more information.`;
+      yield Object(_error_js__WEBPACK_IMPORTED_MODULE_6__["validationError"])(this.tokens.name, this, "require-exposed", message, {
+        autofix: Object(_helpers_js__WEBPACK_IMPORTED_MODULE_5__["autofixAddExposedWindow"])(this)
+      });
+    }
+    const constructors = this.extAttrs.filter(extAttr => extAttr.name === "Constructor");
+    for (const constructor of constructors) {
+      const message = `Constructors should now be represented as a \`constructor()\` operation on the interface \
+instead of \`[Constructor]\` extended attribute. Refer to the \
+[WebIDL spec section on constructor operations](https://heycam.github.io/webidl/#idl-constructors) \
+for more information.`;
+      yield Object(_error_js__WEBPACK_IMPORTED_MODULE_6__["validationError"])(constructor.tokens.name, this, "constructor-member", message, {
+        autofix: autofixConstructor(this, constructor)
+      });
+    }
+
+    yield* super.validate(defs);
+    if (!this.partial) {
+      yield* Object(_validators_interface_js__WEBPACK_IMPORTED_MODULE_7__["checkInterfaceMemberDuplication"])(defs, this);
+    }
+  }
+}
+
+function autofixConstructor(interfaceDef, constructorExtAttr) {
+  return () => {
+    const indentation = Object(_helpers_js__WEBPACK_IMPORTED_MODULE_5__["getLastIndentation"])(interfaceDef.extAttrs.tokens.open.trivia);
+    const memberIndent = interfaceDef.members.length ?
+      Object(_helpers_js__WEBPACK_IMPORTED_MODULE_5__["getLastIndentation"])(Object(_helpers_js__WEBPACK_IMPORTED_MODULE_5__["getFirstToken"])(interfaceDef.members[0]).trivia) :
+      Object(_helpers_js__WEBPACK_IMPORTED_MODULE_5__["getMemberIndentation"])(indentation);
+    const constructorOp = _constructor_js__WEBPACK_IMPORTED_MODULE_8__["Constructor"].parse(new _tokeniser_js__WEBPACK_IMPORTED_MODULE_9__["Tokeniser"](`\n${memberIndent}constructor();`));
+    constructorOp.extAttrs = [];
+    constructorOp.arguments = constructorExtAttr.arguments;
+
+    const existingIndex = interfaceDef.members.findIndex(m => m.type === "constructor");
+    interfaceDef.members.splice(existingIndex + 1, 0, constructorOp);
+
+    const { close }  = interfaceDef.tokens;
+    if (!close.trivia.includes("\n")) {
+      close.trivia += `\n${indentation}`;
+    }
+
+    const { extAttrs } = interfaceDef;
+    const index = extAttrs.indexOf(constructorExtAttr);
+    const removed = extAttrs.splice(index, 1);
+    if (!extAttrs.length) {
+      extAttrs.tokens.open = extAttrs.tokens.close = undefined;
+    } else if (extAttrs.length === index) {
+      extAttrs[index - 1].tokens.separator = undefined;
+    } else if (!extAttrs[index].tokens.name.trivia.trim()) {
+      extAttrs[index].tokens.name.trivia = removed[0].tokens.name.trivia;
+    }
+  };
+}
+
+
+/***/ }),
+/* 20 */
+/***/ (function(module, __webpack_exports__, __webpack_require__) {
+
+"use strict";
+__webpack_require__.r(__webpack_exports__);
+/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Container", function() { return Container; });
+/* harmony import */ var _base_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(6);
+/* harmony import */ var _extended_attributes_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(10);
+/* harmony import */ var _helpers_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(4);
+
+
+
+
+/**
+ * @param {import("../tokeniser.js").Tokeniser} tokeniser
+ */
+function inheritance(tokeniser) {
+  const colon = tokeniser.consume(":");
+  if (!colon) {
+    return {};
+  }
+  const inheritance = tokeniser.consume("identifier") || tokeniser.error("Inheritance lacks a type");
+  return { colon, inheritance };
+}
+
+class Container extends _base_js__WEBPACK_IMPORTED_MODULE_0__["Base"] {
+    /**
+     * @param {import("../tokeniser.js").Tokeniser} tokeniser
+     * @param {*} instance
+     * @param {*} args
+     */
+    static parse(tokeniser, instance, { type, inheritable, allowedMembers }) {
+      const { tokens } = instance;
+      tokens.name = tokeniser.consume("identifier") || tokeniser.error(`Missing name in ${instance.type}`);
+      tokeniser.current = instance;
+      if (inheritable) {
+        Object.assign(tokens, inheritance(tokeniser));
+      }
+      tokens.open = tokeniser.consume("{") || tokeniser.error(`Bodyless ${type}`);
+      instance.members = [];
+      while (true) {
+        tokens.close = tokeniser.consume("}");
+        if (tokens.close) {
+          tokens.termination = tokeniser.consume(";") || tokeniser.error(`Missing semicolon after ${type}`);
+          return instance;
+        }
+        const ea = _extended_attributes_js__WEBPACK_IMPORTED_MODULE_1__["ExtendedAttributes"].parse(tokeniser);
+        let mem;
+        for (const [parser, ...args] of allowedMembers) {
+          mem = parser(tokeniser, ...args);
+          if (mem) {
+            break;
+          }
+        }
+        if (!mem) {
+          tokeniser.error("Unknown member");
+        }
+        mem.extAttrs = ea;
+        instance.members.push(mem);
+      }
+    }
+
+    get partial() {
+      return !!this.tokens.partial;
+    }
+    get name() {
+      return Object(_helpers_js__WEBPACK_IMPORTED_MODULE_2__["unescape"])(this.tokens.name.value);
+    }
+    get inheritance() {
+      if (!this.tokens.inheritance) {
+        return null;
+      }
+      return Object(_helpers_js__WEBPACK_IMPORTED_MODULE_2__["unescape"])(this.tokens.inheritance.value);
+    }
+
+    *validate(defs) {
+      for (const member of this.members) {
+        if (member.validate) {
+          yield* member.validate(defs);
+        }
+      }
+    }
+  }
+
+
+/***/ }),
+/* 21 */
+/***/ (function(module, __webpack_exports__, __webpack_require__) {
+
+"use strict";
+__webpack_require__.r(__webpack_exports__);
+/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Constant", function() { return Constant; });
+/* harmony import */ var _base_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(6);
+/* harmony import */ var _type_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(5);
+/* harmony import */ var _helpers_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(4);
+
+
+
+
+class Constant extends _base_js__WEBPACK_IMPORTED_MODULE_0__["Base"] {
+  /**
+   * @param {import("../tokeniser.js").Tokeniser} tokeniser
+   */
+  static parse(tokeniser) {
+    const tokens = {};
+    tokens.base = tokeniser.consume("const");
+    if (!tokens.base) {
+      return;
+    }
+    let idlType = Object(_helpers_js__WEBPACK_IMPORTED_MODULE_2__["primitive_type"])(tokeniser);
+    if (!idlType) {
+      const base = tokeniser.consume("identifier") || tokeniser.error("Const lacks a type");
+      idlType = new _type_js__WEBPACK_IMPORTED_MODULE_1__["Type"]({ source: tokeniser.source, tokens: { base } });
+    }
+    if (tokeniser.probe("?")) {
+      tokeniser.error("Unexpected nullable constant type");
+    }
+    idlType.type = "const-type";
+    tokens.name = tokeniser.consume("identifier") || tokeniser.error("Const lacks a name");
+    tokens.assign = tokeniser.consume("=") || tokeniser.error("Const lacks value assignment");
+    tokens.value = Object(_helpers_js__WEBPACK_IMPORTED_MODULE_2__["const_value"])(tokeniser) || tokeniser.error("Const lacks a value");
+    tokens.termination = tokeniser.consume(";") || tokeniser.error("Unterminated const, expected `;`");
+    const ret = new Constant({ source: tokeniser.source, tokens });
+    ret.idlType = idlType;
+    return ret;
+  }
+
+  get type() {
+    return "const";
+  }
+  get name() {
+    return unescape(this.tokens.name.value);
+  }
+  get value() {
+    return Object(_helpers_js__WEBPACK_IMPORTED_MODULE_2__["const_data"])(this.tokens.value);
+  }
+}
+
+
+/***/ }),
+/* 22 */
+/***/ (function(module, __webpack_exports__, __webpack_require__) {
+
+"use strict";
+__webpack_require__.r(__webpack_exports__);
+/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "IterableLike", function() { return IterableLike; });
+/* harmony import */ var _base_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(6);
+/* harmony import */ var _helpers_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(4);
+
+
+
+class IterableLike extends _base_js__WEBPACK_IMPORTED_MODULE_0__["Base"] {
+  /**
+   * @param {import("../tokeniser.js").Tokeniser} tokeniser
+   */
+  static parse(tokeniser) {
+    const start_position = tokeniser.position;
+    const tokens = {};
+    const ret = new IterableLike({ source: tokeniser.source, tokens });
+    tokens.readonly = tokeniser.consume("readonly");
+    if (!tokens.readonly) {
+      tokens.async = tokeniser.consume("async");
+    }
+    tokens.base =
+      tokens.readonly ? tokeniser.consume("maplike", "setlike") :
+      tokens.async ? tokeniser.consume("iterable") :
+      tokeniser.consume("iterable", "maplike", "setlike");
+    if (!tokens.base) {
+      tokeniser.unconsume(start_position);
+      return;
+    }
+
+    const { type } = ret;
+    const secondTypeRequired = type === "maplike" || ret.async;
+    const secondTypeAllowed = secondTypeRequired || type === "iterable";
+
+    tokens.open = tokeniser.consume("<") || tokeniser.error(`Missing less-than sign \`<\` in ${type} declaration`);
+    const first = Object(_helpers_js__WEBPACK_IMPORTED_MODULE_1__["type_with_extended_attributes"])(tokeniser) || tokeniser.error(`Missing a type argument in ${type} declaration`);
+    ret.idlType = [first];
+    if (secondTypeAllowed) {
+      first.tokens.separator = tokeniser.consume(",");
+      if (first.tokens.separator) {
+        ret.idlType.push(Object(_helpers_js__WEBPACK_IMPORTED_MODULE_1__["type_with_extended_attributes"])(tokeniser));
+      }
+      else if (secondTypeRequired) {
+        tokeniser.error(`Missing second type argument in ${type} declaration`);
+      }
+    }
+    tokens.close = tokeniser.consume(">") || tokeniser.error(`Missing greater-than sign \`>\` in ${type} declaration`);
+    tokens.termination = tokeniser.consume(";") || tokeniser.error(`Missing semicolon after ${type} declaration`);
+
+    return ret;
+  }
+
+  get type() {
+    return this.tokens.base.value;
+  }
+  get readonly() {
+    return !!this.tokens.readonly;
+  }
+  get async() {
+    return !!this.tokens.async;
+  }
+}
+
+
+/***/ }),
+/* 23 */
+/***/ (function(module, __webpack_exports__, __webpack_require__) {
+
+"use strict";
+__webpack_require__.r(__webpack_exports__);
+/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "checkInterfaceMemberDuplication", function() { return checkInterfaceMemberDuplication; });
+/* harmony import */ var _error_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(3);
+
+
+function* checkInterfaceMemberDuplication(defs, i) {
+  const opNames = new Set(getOperations(i).map(op => op.name));
+  const partials = defs.partials.get(i.name) || [];
+  const mixins = defs.mixinMap.get(i.name) || [];
+  for (const ext of [...partials, ...mixins]) {
+    const additions = getOperations(ext);
+    yield* forEachExtension(additions, opNames, ext, i);
+    for (const addition of additions) {
+      opNames.add(addition.name);
+    }
+  }
+
+  function* forEachExtension(additions, existings, ext, base) {
+    for (const addition of additions) {
+      const { name } = addition;
+      if (name && existings.has(name)) {
+        const message = `The operation "${name}" has already been defined for the base interface "${base.name}" either in itself or in a mixin`;
+        yield Object(_error_js__WEBPACK_IMPORTED_MODULE_0__["validationError"])(addition.tokens.name, ext, "no-cross-overload", message);
+      }
+    }
+  }
+
+  function getOperations(i) {
+    return i.members
+      .filter(({type}) => type === "operation");
+  }
+}
+
+
+/***/ }),
+/* 24 */
+/***/ (function(module, __webpack_exports__, __webpack_require__) {
+
+"use strict";
+__webpack_require__.r(__webpack_exports__);
+/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Constructor", function() { return Constructor; });
+/* harmony import */ var _base_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(6);
+/* harmony import */ var _helpers_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(4);
+
+
+
+class Constructor extends _base_js__WEBPACK_IMPORTED_MODULE_0__["Base"] {
+  /**
+   * @param {import("../tokeniser").Tokeniser} tokeniser
+   */
+  static parse(tokeniser) {
+    const base = tokeniser.consume("constructor");
+    if (!base) {
+      return;
+    }
+    const tokens = { base };
+    tokens.open = tokeniser.consume("(") || tokeniser.error("No argument list in constructor");
+    const args = Object(_helpers_js__WEBPACK_IMPORTED_MODULE_1__["argument_list"])(tokeniser);
+    tokens.close = tokeniser.consume(")") || tokeniser.error("Unterminated constructor");
+    tokens.termination = tokeniser.consume(";") || tokeniser.error("No semicolon after constructor");
+    const ret = new Constructor({ tokens });
+    ret.arguments = args;
+    return ret;
+  }
+
+  get type() {
+    return "constructor";
+  }
+
+  *validate(defs) {
+    if (this.idlType) {
+      yield* this.idlType.validate(defs);
+    }
+    for (const argument of this.arguments) {
+      yield* argument.validate(defs);
+    }
+  }
+}
+
+
+/***/ }),
+/* 25 */
+/***/ (function(module, __webpack_exports__, __webpack_require__) {
+
+"use strict";
+__webpack_require__.r(__webpack_exports__);
+/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Mixin", function() { return Mixin; });
+/* harmony import */ var _container_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(20);
+/* harmony import */ var _constant_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(21);
+/* harmony import */ var _attribute_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(14);
+/* harmony import */ var _operation_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(13);
+/* harmony import */ var _helpers_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(4);
+
+
+
+
+
+
+class Mixin extends _container_js__WEBPACK_IMPORTED_MODULE_0__["Container"] {
+  /**
+   * @param {import("../tokeniser").Tokeniser} tokeniser
+   */
+  static parse(tokeniser, base, { partial } = {}) {
+    const tokens = { partial, base };
+    tokens.mixin = tokeniser.consume("mixin");
+    if (!tokens.mixin) {
+      return;
+    }
+    return _container_js__WEBPACK_IMPORTED_MODULE_0__["Container"].parse(tokeniser, new Mixin({ source: tokeniser.source, tokens }), {
+      type: "interface mixin",
+      allowedMembers: [
+        [_constant_js__WEBPACK_IMPORTED_MODULE_1__["Constant"].parse],
+        [_helpers_js__WEBPACK_IMPORTED_MODULE_4__["stringifier"]],
+        [_attribute_js__WEBPACK_IMPORTED_MODULE_2__["Attribute"].parse, { noInherit: true }],
+        [_operation_js__WEBPACK_IMPORTED_MODULE_3__["Operation"].parse, { regular: true }]
+      ]
+    });
+  }
+
+  get type() {
+    return "interface mixin";
+  }
+}
+
+
+/***/ }),
+/* 26 */
+/***/ (function(module, __webpack_exports__, __webpack_require__) {
+
+"use strict";
+__webpack_require__.r(__webpack_exports__);
+/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Dictionary", function() { return Dictionary; });
+/* harmony import */ var _container_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(20);
+/* harmony import */ var _field_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(27);
+
+
+
+class Dictionary extends _container_js__WEBPACK_IMPORTED_MODULE_0__["Container"] {
+  /**
+   * @param {import("../tokeniser").Tokeniser} tokeniser
+   */
+  static parse(tokeniser, { partial } = {}) {
+    const tokens = { partial };
+    tokens.base = tokeniser.consume("dictionary");
+    if (!tokens.base) {
+      return;
+    }
+    return _container_js__WEBPACK_IMPORTED_MODULE_0__["Container"].parse(tokeniser, new Dictionary({ source: tokeniser.source, tokens }), {
+      type: "dictionary",
+      inheritable: !partial,
+      allowedMembers: [
+        [_field_js__WEBPACK_IMPORTED_MODULE_1__["Field"].parse],
+      ]
+    });
+  }
+
+  get type() {
+    return "dictionary";
+  }
+}
+
+
+/***/ }),
+/* 27 */
+/***/ (function(module, __webpack_exports__, __webpack_require__) {
+
+"use strict";
+__webpack_require__.r(__webpack_exports__);
+/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Field", function() { return Field; });
+/* harmony import */ var _base_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(6);
+/* harmony import */ var _helpers_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(4);
+/* harmony import */ var _extended_attributes_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(10);
+/* harmony import */ var _default_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(9);
+
+
+
+
+
+class Field extends _base_js__WEBPACK_IMPORTED_MODULE_0__["Base"] {
+  /**
+   * @param {import("../tokeniser").Tokeniser} tokeniser
+   */
+  static parse(tokeniser) {
+    const tokens = {};
+    const ret = new Field({ source: tokeniser.source, tokens });
+    ret.extAttrs = _extended_attributes_js__WEBPACK_IMPORTED_MODULE_2__["ExtendedAttributes"].parse(tokeniser);
+    tokens.required = tokeniser.consume("required");
+    ret.idlType = Object(_helpers_js__WEBPACK_IMPORTED_MODULE_1__["type_with_extended_attributes"])(tokeniser, "dictionary-type") || tokeniser.error("Dictionary member lacks a type");
+    tokens.name = tokeniser.consume("identifier") || tokeniser.error("Dictionary member lacks a name");
+    ret.default = _default_js__WEBPACK_IMPORTED_MODULE_3__["Default"].parse(tokeniser);
+    if (tokens.required && ret.default) tokeniser.error("Required member must not have a default");
+    tokens.termination = tokeniser.consume(";") || tokeniser.error("Unterminated dictionary member, expected `;`");
+    return ret;
+  }
+
+  get type() {
+    return "field";
+  }
+  get name() {
+    return Object(_helpers_js__WEBPACK_IMPORTED_MODULE_1__["unescape"])(this.tokens.name.value);
+  }
+  get required() {
+    return !!this.tokens.required;
+  }
+
+  *validate(defs) {
+    yield* this.idlType.validate(defs);
+  }
+}
+
+
+/***/ }),
+/* 28 */
+/***/ (function(module, __webpack_exports__, __webpack_require__) {
+
+"use strict";
+__webpack_require__.r(__webpack_exports__);
+/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Namespace", function() { return Namespace; });
+/* harmony import */ var _container_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(20);
+/* harmony import */ var _attribute_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(14);
+/* harmony import */ var _operation_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(13);
+/* harmony import */ var _error_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(3);
+/* harmony import */ var _helpers_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(4);
+
+
+
+
+
+
+class Namespace extends _container_js__WEBPACK_IMPORTED_MODULE_0__["Container"] {
+  /**
+   * @param {import("../tokeniser").Tokeniser} tokeniser
+   */
+  static parse(tokeniser, { partial } = {}) {
+    const tokens = { partial };
+    tokens.base = tokeniser.consume("namespace");
+    if (!tokens.base) {
+      return;
+    }
+    return _container_js__WEBPACK_IMPORTED_MODULE_0__["Container"].parse(tokeniser, new Namespace({ source: tokeniser.source, tokens }), {
+      type: "namespace",
+      allowedMembers: [
+        [_attribute_js__WEBPACK_IMPORTED_MODULE_1__["Attribute"].parse, { noInherit: true, readonly: true }],
+        [_operation_js__WEBPACK_IMPORTED_MODULE_2__["Operation"].parse, { regular: true }]
+      ]
+    });
+  }
+
+  get type() {
+    return "namespace";
+  }
+
+  *validate(defs) {
+    if (!this.partial && this.extAttrs.every(extAttr => extAttr.name !== "Exposed")) {
+      const message = `Namespaces must have [Exposed] extended attribute. \
+To fix, add, for example, [Exposed=Window]. Please also consider carefully \
+if your namespace should also be exposed in a Worker scope. Refer to the \
+[WebIDL spec section on Exposed](https://heycam.github.io/webidl/#Exposed) \
+for more information.`;
+      yield Object(_error_js__WEBPACK_IMPORTED_MODULE_3__["validationError"])(this.tokens.name, this, "require-exposed", message, {
+        autofix: Object(_helpers_js__WEBPACK_IMPORTED_MODULE_4__["autofixAddExposedWindow"])(this)
+      });
+    }
+    yield* super.validate(defs);
+  }
+}
+
+
+/***/ }),
+/* 29 */
+/***/ (function(module, __webpack_exports__, __webpack_require__) {
+
+"use strict";
+__webpack_require__.r(__webpack_exports__);
+/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "CallbackInterface", function() { return CallbackInterface; });
+/* harmony import */ var _container_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(20);
+/* harmony import */ var _operation_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(13);
+/* harmony import */ var _constant_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(21);
+
+
+
+
+
+class CallbackInterface extends _container_js__WEBPACK_IMPORTED_MODULE_0__["Container"] {
+  /**
+   * @param {import("../tokeniser").Tokeniser} tokeniser
+   */
+  static parse(tokeniser, callback, { partial = null } = {}) {
+    const tokens = { callback };
+    tokens.base = tokeniser.consume("interface");
+    if (!tokens.base) {
+      return;
+    }
+    return _container_js__WEBPACK_IMPORTED_MODULE_0__["Container"].parse(tokeniser, new CallbackInterface({ source: tokeniser.source, tokens }), {
+      type: "callback interface",
+      inheritable: !partial,
+      allowedMembers: [
+        [_constant_js__WEBPACK_IMPORTED_MODULE_2__["Constant"].parse],
+        [_operation_js__WEBPACK_IMPORTED_MODULE_1__["Operation"].parse, { regular: true }]
+      ]
+    });
+  }
+
+  get type() {
+    return "callback interface";
+  }
+}
+
+
+/***/ }),
+/* 30 */
+/***/ (function(module, __webpack_exports__, __webpack_require__) {
+
+"use strict";
+__webpack_require__.r(__webpack_exports__);
+/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "write", function() { return write; });
+
+
+function noop(arg) {
+  return arg;
+}
+
+const templates = {
+  wrap: items => items.join(""),
+  trivia: noop,
+  name: noop,
+  reference: noop,
+  type: noop,
+  generic: noop,
+  nameless: noop,
+  inheritance: noop,
+  definition: noop,
+  extendedAttribute: noop,
+  extendedAttributeReference: noop
+};
+
+function write(ast, { templates: ts = templates } = {}) {
+  ts = Object.assign({}, templates, ts);
+
+  function reference(raw, { unescaped, context }) {
+    if (!unescaped) {
+      unescaped = raw.startsWith("_") ? raw.slice(1) : raw;
+    }
+    return ts.reference(raw, unescaped, context);
+  }
+
+  function token(t, wrapper = noop, ...args) {
+    if (!t) {
+      return "";
+    }
+    const value = wrapper(t.value, ...args);
+    return ts.wrap([ts.trivia(t.trivia), value]);
+  }
+
+  function reference_token(t, context) {
+    return token(t, reference, { context });
+  }
+
+  function name_token(t, arg) {
+    return token(t, ts.name, arg);
+  }
+
+  function type_body(it) {
+    if (it.union || it.generic) {
+      return ts.wrap([
+        token(it.tokens.base, ts.generic),
+        token(it.tokens.open),
+        ...it.subtype.map(type),
+        token(it.tokens.close)
+      ]);
+    }
+    const firstToken = it.tokens.prefix || it.tokens.base;
+    const prefix = it.tokens.prefix ? [
+      it.tokens.prefix.value,
+      ts.trivia(it.tokens.base.trivia)
+    ] : [];
+    const ref = reference(ts.wrap([
+      ...prefix,
+      it.tokens.base.value,
+      token(it.tokens.postfix)
+    ]), { unescaped: it.idlType, context: it });
+    return ts.wrap([ts.trivia(firstToken.trivia), ref]);
+  }
+  function type(it) {
+    return ts.wrap([
+      extended_attributes(it.extAttrs),
+      type_body(it),
+      token(it.tokens.nullable),
+      token(it.tokens.separator)
+    ]);
+  }
+  function default_(def) {
+    if (!def) {
+      return "";
+    }
+    return ts.wrap([
+      token(def.tokens.assign),
+      ...def.expression.map(t => token(t))
+    ]);
+  }
+  function argument(arg) {
+    return ts.wrap([
+      extended_attributes(arg.extAttrs),
+      token(arg.tokens.optional),
+      ts.type(type(arg.idlType)),
+      token(arg.tokens.variadic),
+      name_token(arg.tokens.name, { data: arg }),
+      default_(arg.default),
+      token(arg.tokens.separator)
+    ]);
+  }
+  function identifier(id, context) {
+    return ts.wrap([
+      reference_token(id.tokens.value, context),
+      token(id.tokens.separator)
+    ]);
+  }
+  function make_ext_at(it) {
+    const { rhsType } = it.params;
+    return ts.wrap([
+      ts.trivia(it.tokens.name.trivia),
+      ts.extendedAttribute(ts.wrap([
+        ts.extendedAttributeReference(it.name),
+        token(it.params.tokens.assign),
+        reference_token(it.params.tokens.secondaryName, it),
+        token(it.params.tokens.open),
+        ...!it.params.list ? [] :
+          it.params.list.map(
+            rhsType === "identifier-list" ? id => identifier(id, it) : argument
+          ),
+        token(it.params.tokens.close)
+      ])),
+      token(it.tokens.separator)
+    ]);
+  }
+  function extended_attributes(eats) {
+    if (!eats.length) return "";
+    return ts.wrap([
+      token(eats.tokens.open),
+      ...eats.map(make_ext_at),
+      token(eats.tokens.close)
+    ]);
+  }
+
+  function operation(it, parent) {
+    const body = it.idlType ? [
+      ts.type(type(it.idlType)),
+      name_token(it.tokens.name, { data: it, parent }),
+      token(it.tokens.open),
+      ts.wrap(it.arguments.map(argument)),
+      token(it.tokens.close),
+    ] : [];
+    return ts.definition(ts.wrap([
+      extended_attributes(it.extAttrs),
+      it.tokens.name ? token(it.tokens.special) : token(it.tokens.special, ts.nameless, { data: it, parent }),
+      ...body,
+      token(it.tokens.termination)
+    ]), { data: it, parent });
+  }
+
+  function attribute(it, parent) {
+    return ts.definition(ts.wrap([
+      extended_attributes(it.extAttrs),
+      token(it.tokens.special),
+      token(it.tokens.readonly),
+      token(it.tokens.base),
+      ts.type(type(it.idlType)),
+      name_token(it.tokens.name, { data: it, parent }),
+      token(it.tokens.termination)
+    ]), { data: it, parent });
+  }
+
+  function constructor(it, parent) {
+    return ts.definition(ts.wrap([
+      extended_attributes(it.extAttrs),
+      token(it.tokens.base, ts.nameless, { data: it, parent }),
+      token(it.tokens.open),
+      ts.wrap(it.arguments.map(argument)),
+      token(it.tokens.close),
+      token(it.tokens.termination)
+    ]), { data: it, parent });
+  }
+
+  function inheritance(inh) {
+    if (!inh.tokens.inheritance) {
+      return "";
+    }
+    return ts.wrap([
+      token(inh.tokens.colon),
+      ts.trivia(inh.tokens.inheritance.trivia),
+      ts.inheritance(reference(inh.tokens.inheritance.value, { context: inh }))
+    ]);
+  }
+
+  function container(it) {
+    return ts.definition(ts.wrap([
+      extended_attributes(it.extAttrs),
+      token(it.tokens.callback),
+      token(it.tokens.partial),
+      token(it.tokens.base),
+      token(it.tokens.mixin),
+      name_token(it.tokens.name, { data: it }),
+      inheritance(it),
+      token(it.tokens.open),
+      iterate(it.members, it),
+      token(it.tokens.close),
+      token(it.tokens.termination)
+    ]), { data: it });
+  }
+
+  function field(it, parent) {
+    return ts.definition(ts.wrap([
+      extended_attributes(it.extAttrs),
+      token(it.tokens.required),
+      ts.type(type(it.idlType)),
+      name_token(it.tokens.name, { data: it, parent }),
+      default_(it.default),
+      token(it.tokens.termination)
+    ]), { data: it, parent });
+  }
+  function const_(it, parent) {
+    return ts.definition(ts.wrap([
+      extended_attributes(it.extAttrs),
+      token(it.tokens.base),
+      ts.type(type(it.idlType)),
+      name_token(it.tokens.name, { data: it, parent }),
+      token(it.tokens.assign),
+      token(it.tokens.value),
+      token(it.tokens.termination)
+    ]), { data: it, parent });
+  }
+  function typedef(it) {
+    return ts.definition(ts.wrap([
+      extended_attributes(it.extAttrs),
+      token(it.tokens.base),
+      ts.type(type(it.idlType)),
+      name_token(it.tokens.name, { data: it }),
+      token(it.tokens.termination)
+    ]), { data: it });
+  }
+  function includes(it) {
+    return ts.definition(ts.wrap([
+      extended_attributes(it.extAttrs),
+      reference_token(it.tokens.target, it),
+      token(it.tokens.includes),
+      reference_token(it.tokens.mixin, it),
+      token(it.tokens.termination)
+    ]), { data: it });
+  }
+  function callback(it) {
+    return ts.definition(ts.wrap([
+      extended_attributes(it.extAttrs),
+      token(it.tokens.base),
+      name_token(it.tokens.name, { data: it }),
+      token(it.tokens.assign),
+      ts.type(type(it.idlType)),
+      token(it.tokens.open),
+      ...it.arguments.map(argument),
+      token(it.tokens.close),
+      token(it.tokens.termination),
+    ]), { data: it });
+  }
+  function enum_(it) {
+    return ts.definition(ts.wrap([
+      extended_attributes(it.extAttrs),
+      token(it.tokens.base),
+      name_token(it.tokens.name, { data: it }),
+      token(it.tokens.open),
+      iterate(it.values, it),
+      token(it.tokens.close),
+      token(it.tokens.termination)
+    ]), { data: it });
+  }
+  function enum_value(v, parent) {
+    return ts.wrap([
+      ts.trivia(v.tokens.value.trivia),
+      ts.definition(
+        ts.wrap(['"', ts.name(v.value, { data: v, parent }), '"']),
+        { data: v, parent }
+      ),
+      token(v.tokens.separator)
+    ]);
+  }
+  function iterable_like(it, parent) {
+    return ts.definition(ts.wrap([
+      extended_attributes(it.extAttrs),
+      token(it.tokens.readonly),
+      token(it.tokens.async),
+      token(it.tokens.base, ts.generic),
+      token(it.tokens.open),
+      ts.wrap(it.idlType.map(type)),
+      token(it.tokens.close),
+      token(it.tokens.termination)
+    ]), { data: it, parent });
+  }
+  function eof(it) {
+    return ts.trivia(it.trivia);
+  }
+
+  const table = {
+    interface: container,
+    "interface mixin": container,
+    namespace: container,
+    operation,
+    attribute,
+    constructor,
+    dictionary: container,
+    field,
+    const: const_,
+    typedef,
+    includes,
+    callback,
+    enum: enum_,
+    "enum-value": enum_value,
+    iterable: iterable_like,
+    maplike: iterable_like,
+    setlike: iterable_like,
+    "callback interface": container,
+    eof
+  };
+  function dispatch(it, parent) {
+    const dispatcher = table[it.type];
+    if (!dispatcher) {
+      throw new Error(`Type "${it.type}" is unsupported`);
+    }
+    return table[it.type](it, parent);
+  }
+  function iterate(things, parent) {
+    if (!things) return;
+    const results = things.map(thing => dispatch(thing, parent));
+    return ts.wrap(results);
+  }
+  return iterate(ast);
+}
+
+
+/***/ }),
+/* 31 */
+/***/ (function(module, __webpack_exports__, __webpack_require__) {
+
+"use strict";
+__webpack_require__.r(__webpack_exports__);
+/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "validate", function() { return validate; });
+/* harmony import */ var _error_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(3);
+
+
+
+
+function getMixinMap(all, unique) {
+  const map = new Map();
+  const includes = all.filter(def => def.type === "includes");
+  for (const include of includes) {
+    const mixin = unique.get(include.includes);
+    if (!mixin) {
+      continue;
+    }
+    const array = map.get(include.target);
+    if (array) {
+      array.push(mixin);
+    } else {
+      map.set(include.target, [mixin]);
+    }
+  }
+  return map;
+}
+
+function groupDefinitions(all) {
+  const unique = new Map();
+  const duplicates = new Set();
+  const partials = new Map();
+  for (const def of all) {
+    if (def.partial) {
+      const array = partials.get(def.name);
+      if (array) {
+        array.push(def);
+      } else {
+        partials.set(def.name, [def]);
+      }
+      continue;
+    }
+    if (!def.name) {
+      continue;
+    }
+    if (!unique.has(def.name)) {
+      unique.set(def.name, def);
+    } else {
+      duplicates.add(def);
+    }
+  }
+  return {
+    all,
+    unique,
+    partials,
+    duplicates,
+    mixinMap: getMixinMap(all, unique),
+    cache: {
+      typedefIncludesDictionary: new WeakMap()
+    },
+  };
+}
+
+function* checkDuplicatedNames({ unique, duplicates }) {
+  for (const dup of duplicates) {
+    const { name } = dup;
+    const message = `The name "${name}" of type "${unique.get(name).type}" was already seen`;
+    yield Object(_error_js__WEBPACK_IMPORTED_MODULE_0__["validationError"])(dup.tokens.name, dup, "no-duplicate", message);
+  }
+}
+
+function* validateIterable(ast) {
+  const defs = groupDefinitions(ast);
+  for (const def of defs.all) {
+    if (def.validate) {
+      yield* def.validate(defs);
+    }
+  }
+  yield* checkDuplicatedNames(defs);
+}
+
+// Remove this once all of our support targets expose `.flat()` by default
+function flatten(array) {
+  if (array.flat) {
+    return array.flat();
+  }
+  return [].concat(...array);
+}
+
+/**
+ * @param {*} ast AST or array of ASTs
+ */
+function validate(ast) {
+  return [...validateIterable(flatten(ast))];
+}
+
+
+/***/ })
+/******/ ]);
+});
+//# sourceMappingURL=webidl2.js.map
diff --git a/third_party/blink/web_tests/svg/css/getComputedStyle-listing-expected.txt b/third_party/blink/web_tests/svg/css/getComputedStyle-listing-expected.txt
index ff25b12b..9d01491 100644
--- a/third_party/blink/web_tests/svg/css/getComputedStyle-listing-expected.txt
+++ b/third_party/blink/web_tests/svg/css/getComputedStyle-listing-expected.txt
@@ -3,7 +3,6 @@
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
 -webkit-app-region: none
--webkit-appearance: none
 -webkit-border-horizontal-spacing: 0px
 -webkit-border-image: none
 -webkit-border-vertical-spacing: 0px
@@ -63,6 +62,7 @@
 animation-name: none
 animation-play-state: running
 animation-timing-function: ease
+appearance: none
 backdrop-filter: none
 backface-visibility: visible
 background-attachment: scroll
diff --git a/third_party/blink/web_tests/svg/custom/calc-expression-with-zoom.html b/third_party/blink/web_tests/svg/custom/calc-expression-with-zoom.html
index 9a76d709..d983f5d 100644
--- a/third_party/blink/web_tests/svg/custom/calc-expression-with-zoom.html
+++ b/third_party/blink/web_tests/svg/custom/calc-expression-with-zoom.html
@@ -3,6 +3,6 @@
    body { zoom: 200%; }
 </style>
 <svg id="svg" width="500" height="500" viewBox='0 0 1000 1000'>
-    <rect width='calc(50px + 50)' height='100' fill='green'/>
+    <rect width='calc(50px + 50px)' height='100' fill='green'/>
     <rect x='110' width='calc(50 + 50)' height='100' fill='green'/>
 </svg> 
diff --git a/third_party/blink/web_tests/svg/custom/rect-calc-corner-radii.svg b/third_party/blink/web_tests/svg/custom/rect-calc-corner-radii.svg
index fe98961..55e9c79 100644
--- a/third_party/blink/web_tests/svg/custom/rect-calc-corner-radii.svg
+++ b/third_party/blink/web_tests/svg/custom/rect-calc-corner-radii.svg
@@ -1,5 +1,5 @@
 <svg xmlns="http://www.w3.org/2000/svg" width="500" height="500">
     <rect  x="50" y="50"  width="400" height="400" rx="calc(-50)"  ry="calc(-50)"  fill="#00ff00" />
-    <rect x="150" y="100" width="200" height="300" rx="calc(20% - 50)" ry="50" fill="#0000ff" />
+    <rect x="150" y="100" width="200" height="300" rx="calc(20% - 50px)" ry="50" fill="#0000ff" />
     <rect x="200" y="150" width="100" height="200" rx="50" ry="-100" fill="#ff0000" />
 </svg>
diff --git a/third_party/blink/web_tests/svg/dom/SVGLength-calc-in-attr.html b/third_party/blink/web_tests/svg/dom/SVGLength-calc-in-attr.html
index 3d4a704..7b718975 100644
--- a/third_party/blink/web_tests/svg/dom/SVGLength-calc-in-attr.html
+++ b/third_party/blink/web_tests/svg/dom/SVGLength-calc-in-attr.html
@@ -82,10 +82,10 @@
     assert_calc_expression("calc(10mm + 10mm)", (20 * cssPixelsPerMillimeter));
     assert_calc_expression("calc(20mm)", (20 * cssPixelsPerMillimeter));
     assert_calc_expression("calc(10 + 10)", 20);
-    assert_calc_expression("calc(10mm + 10)", (10 * cssPixelsPerMillimeter) + 10);
-    assert_calc_expression("calc(10% + 10)", (10 * viewportWidthPercent()) + 10);
-    assert_calc_expression("calc(1cm + 2in + 1cm + 2)", (2 * cssPixelsPerInch) + (2 * cssPixelsPerCentimeter) + 2);
-    assert_calc_expression("calc(1cm + 2 + 1cm + 2in)", (2 * cssPixelsPerInch) + (2 * cssPixelsPerCentimeter) + 2);
-    assert_calc_expression("calc(10% + 10 + 2% + 10pc)", (12 * viewportWidthPercent()) + 10 + (10 * cssPixelsPerPica));
+    assert_calc_expression("calc(10mm + 10px)", (10 * cssPixelsPerMillimeter) + 10);
+    assert_calc_expression("calc(10% + 10px)", (10 * viewportWidthPercent()) + 10);
+    assert_calc_expression("calc(1cm + 2in + 1cm + 2px)", (2 * cssPixelsPerInch) + (2 * cssPixelsPerCentimeter) + 2);
+    assert_calc_expression("calc(1cm + 2px + 1cm + 2in)", (2 * cssPixelsPerInch) + (2 * cssPixelsPerCentimeter) + 2);
+    assert_calc_expression("calc(10% + 10px + 2% + 10pc)", (12 * viewportWidthPercent()) + 10 + (10 * cssPixelsPerPica));
 }, "Tests calc() on presentation and non-presentation attr in svgLength");
 </script>
diff --git a/third_party/blink/web_tests/virtual/force-defer-script/defer-script/async-script-expected.txt b/third_party/blink/web_tests/virtual/force-defer-script/defer-script/async-script-expected.txt
index 794f008..9160632 100644
--- a/third_party/blink/web_tests/virtual/force-defer-script/defer-script/async-script-expected.txt
+++ b/third_party/blink/web_tests/virtual/force-defer-script/defer-script/async-script-expected.txt
@@ -1,4 +1,4 @@
-CONSOLE ERROR: line 3503: Uncaught Error: assert_equals: Normal script execution order comparison expected "Inline1;Sync1;EndOfBody;DOMContentLoaded;Async1;" but got "EndOfBody;Inline1;Sync1;DOMContentLoaded;Async1;"
+CONSOLE ERROR: line 3346: Uncaught Error: assert_equals: Normal script execution order comparison expected "Inline1;Sync1;EndOfBody;DOMContentLoaded;Async1;" but got "EndOfBody;Inline1;Sync1;DOMContentLoaded;Async1;"
 This is a testharness.js-based test.
 FAIL Async Script Execution Order (wrt possibly deferred Synchronous Script) Uncaught Error: assert_equals: Normal script execution order comparison expected "Inline1;Sync1;EndOfBody;DOMContentLoaded;Async1;" but got "EndOfBody;Inline1;Sync1;DOMContentLoaded;Async1;"
 Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/virtual/force-defer-script/defer-script/defer-script-expected.txt b/third_party/blink/web_tests/virtual/force-defer-script/defer-script/defer-script-expected.txt
index ec885da..2f9f63a 100644
--- a/third_party/blink/web_tests/virtual/force-defer-script/defer-script/defer-script-expected.txt
+++ b/third_party/blink/web_tests/virtual/force-defer-script/defer-script/defer-script-expected.txt
@@ -1,4 +1,4 @@
-CONSOLE ERROR: line 3503: Uncaught Error: assert_equals: Normal defer script execution order comparison expected "Inline1;Sync1;Inline2;Sync2;EndOfBody;Defer1;Defer2;DOMContentLoaded;" but got "EndOfBody;Inline1;Sync1;Inline2;Sync2;Defer1;Defer2;DOMContentLoaded;"
+CONSOLE ERROR: line 3346: Uncaught Error: assert_equals: Normal defer script execution order comparison expected "Inline1;Sync1;Inline2;Sync2;EndOfBody;Defer1;Defer2;DOMContentLoaded;" but got "EndOfBody;Inline1;Sync1;Inline2;Sync2;Defer1;Defer2;DOMContentLoaded;"
 This is a testharness.js-based test.
 FAIL Defer Script Execution Order Uncaught Error: assert_equals: Normal defer script execution order comparison expected "Inline1;Sync1;Inline2;Sync2;EndOfBody;Defer1;Defer2;DOMContentLoaded;" but got "EndOfBody;Inline1;Sync1;Inline2;Sync2;Defer1;Defer2;DOMContentLoaded;"
 Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/virtual/omt-worker-fetch/external/wpt/fetch/origin/assorted.window-expected.txt b/third_party/blink/web_tests/virtual/omt-worker-fetch/external/wpt/fetch/origin/assorted.window-expected.txt
new file mode 100644
index 0000000..6b30c4e4
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/omt-worker-fetch/external/wpt/fetch/origin/assorted.window-expected.txt
@@ -0,0 +1,46 @@
+This is a testharness.js-based test.
+PASS Origin header and 308 redirect
+PASS Origin header and GET navigation
+PASS Origin header and POST navigation
+PASS Origin header and POST same-origin navigation with Referrer-Policy no-referrer
+FAIL Origin header and POST same-origin fetch no-cors mode with Referrer-Policy no-referrer assert_equals: expected "null" but got "http://web-platform.test:8001"
+FAIL Origin header and POST same-origin fetch cors mode with Referrer-Policy no-referrer assert_equals: expected "null" but got "http://web-platform.test:8001"
+PASS Origin header and GET same-origin fetch cors mode with Referrer-Policy no-referrer
+PASS Origin header and POST cross-origin navigation with Referrer-Policy no-referrer
+FAIL Origin header and POST cross-origin fetch no-cors mode with Referrer-Policy no-referrer assert_equals: expected "null" but got "http://web-platform.test:8001"
+PASS Origin header and POST cross-origin fetch cors mode with Referrer-Policy no-referrer
+PASS Origin header and GET cross-origin fetch cors mode with Referrer-Policy no-referrer
+PASS Origin header and POST same-origin navigation with Referrer-Policy same-origin
+PASS Origin header and POST same-origin fetch no-cors mode with Referrer-Policy same-origin
+PASS Origin header and POST same-origin fetch cors mode with Referrer-Policy same-origin
+PASS Origin header and GET same-origin fetch cors mode with Referrer-Policy same-origin
+PASS Origin header and POST cross-origin navigation with Referrer-Policy same-origin
+FAIL Origin header and POST cross-origin fetch no-cors mode with Referrer-Policy same-origin assert_equals: expected "null" but got "http://web-platform.test:8001"
+PASS Origin header and POST cross-origin fetch cors mode with Referrer-Policy same-origin
+PASS Origin header and GET cross-origin fetch cors mode with Referrer-Policy same-origin
+PASS Origin header and POST same-origin navigation with Referrer-Policy origin-when-cross-origin
+PASS Origin header and POST same-origin fetch no-cors mode with Referrer-Policy origin-when-cross-origin
+PASS Origin header and POST same-origin fetch cors mode with Referrer-Policy origin-when-cross-origin
+PASS Origin header and GET same-origin fetch cors mode with Referrer-Policy origin-when-cross-origin
+PASS Origin header and POST cross-origin navigation with Referrer-Policy origin-when-cross-origin
+PASS Origin header and POST cross-origin fetch no-cors mode with Referrer-Policy origin-when-cross-origin
+PASS Origin header and POST cross-origin fetch cors mode with Referrer-Policy origin-when-cross-origin
+PASS Origin header and GET cross-origin fetch cors mode with Referrer-Policy origin-when-cross-origin
+PASS Origin header and POST same-origin navigation with Referrer-Policy no-referrer-when-downgrade
+PASS Origin header and POST same-origin fetch no-cors mode with Referrer-Policy no-referrer-when-downgrade
+PASS Origin header and POST same-origin fetch cors mode with Referrer-Policy no-referrer-when-downgrade
+PASS Origin header and GET same-origin fetch cors mode with Referrer-Policy no-referrer-when-downgrade
+PASS Origin header and POST cross-origin navigation with Referrer-Policy no-referrer-when-downgrade
+PASS Origin header and POST cross-origin fetch no-cors mode with Referrer-Policy no-referrer-when-downgrade
+PASS Origin header and POST cross-origin fetch cors mode with Referrer-Policy no-referrer-when-downgrade
+PASS Origin header and GET cross-origin fetch cors mode with Referrer-Policy no-referrer-when-downgrade
+PASS Origin header and POST same-origin navigation with Referrer-Policy unsafe-url
+PASS Origin header and POST same-origin fetch no-cors mode with Referrer-Policy unsafe-url
+PASS Origin header and POST same-origin fetch cors mode with Referrer-Policy unsafe-url
+PASS Origin header and GET same-origin fetch cors mode with Referrer-Policy unsafe-url
+PASS Origin header and POST cross-origin navigation with Referrer-Policy unsafe-url
+PASS Origin header and POST cross-origin fetch no-cors mode with Referrer-Policy unsafe-url
+PASS Origin header and POST cross-origin fetch cors mode with Referrer-Policy unsafe-url
+PASS Origin header and GET cross-origin fetch cors mode with Referrer-Policy unsafe-url
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/virtual/stable/webexposed/css-properties-as-js-properties-expected.txt b/third_party/blink/web_tests/virtual/stable/webexposed/css-properties-as-js-properties-expected.txt
index 2524912..9b2d07d 100644
--- a/third_party/blink/web_tests/virtual/stable/webexposed/css-properties-as-js-properties-expected.txt
+++ b/third_party/blink/web_tests/virtual/stable/webexposed/css-properties-as-js-properties-expected.txt
@@ -15,6 +15,7 @@
 animationName
 animationPlayState
 animationTimingFunction
+appearance
 backdropFilter
 backfaceVisibility
 background
diff --git a/third_party/blink/web_tests/virtual/stable/webexposed/css-property-listing-expected.txt b/third_party/blink/web_tests/virtual/stable/webexposed/css-property-listing-expected.txt
index ce48d5ac..ef35f66 100644
--- a/third_party/blink/web_tests/virtual/stable/webexposed/css-property-listing-expected.txt
+++ b/third_party/blink/web_tests/virtual/stable/webexposed/css-property-listing-expected.txt
@@ -3,7 +3,6 @@
 
 [LONGHANDS]
     -webkit-app-region
-    -webkit-appearance
     -webkit-border-horizontal-spacing
     -webkit-border-image
     -webkit-border-vertical-spacing
@@ -71,6 +70,7 @@
     animation-name
     animation-play-state
     animation-timing-function
+    appearance
     backdrop-filter
     backface-visibility
     background-attachment
@@ -677,6 +677,8 @@
         animation-play-state
     -webkit-animation-timing-function
         animation-timing-function
+    -webkit-appearance
+        appearance
     -webkit-backface-visibility
         backface-visibility
     -webkit-background-clip
diff --git a/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/simulcast/basic.https-expected.txt b/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/simulcast/basic.https-expected.txt
new file mode 100644
index 0000000..5f33fd4
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/simulcast/basic.https-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+FAIL Basic simulcast setup with three spatial layers promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/webexposed/css-properties-as-js-properties-expected.txt b/third_party/blink/web_tests/webexposed/css-properties-as-js-properties-expected.txt
index 6efcd28..2ffd5d1 100644
--- a/third_party/blink/web_tests/webexposed/css-properties-as-js-properties-expected.txt
+++ b/third_party/blink/web_tests/webexposed/css-properties-as-js-properties-expected.txt
@@ -15,6 +15,7 @@
 animationName
 animationPlayState
 animationTimingFunction
+appearance
 aspectRatio
 backdropFilter
 backfaceVisibility
diff --git a/third_party/blink/web_tests/webexposed/css-property-listing-expected.txt b/third_party/blink/web_tests/webexposed/css-property-listing-expected.txt
index 563ba73..7abedf1 100644
--- a/third_party/blink/web_tests/webexposed/css-property-listing-expected.txt
+++ b/third_party/blink/web_tests/webexposed/css-property-listing-expected.txt
@@ -3,7 +3,6 @@
 
 [LONGHANDS]
     -webkit-app-region
-    -webkit-appearance
     -webkit-border-horizontal-spacing
     -webkit-border-image
     -webkit-border-vertical-spacing
@@ -71,6 +70,7 @@
     animation-name
     animation-play-state
     animation-timing-function
+    appearance
     aspect-ratio
     backdrop-filter
     backface-visibility
@@ -753,6 +753,8 @@
         animation-play-state
     -webkit-animation-timing-function
         animation-timing-function
+    -webkit-appearance
+        appearance
     -webkit-backface-visibility
         backface-visibility
     -webkit-background-clip
diff --git a/third_party/blink/web_tests/wpt_internal/display-lock/css-subtree-visibility/subtree-visibility-032.html b/third_party/blink/web_tests/wpt_internal/display-lock/css-subtree-visibility/subtree-visibility-032.html
index 940395b..b2704536 100644
--- a/third_party/blink/web_tests/wpt_internal/display-lock/css-subtree-visibility/subtree-visibility-032.html
+++ b/third_party/blink/web_tests/wpt_internal/display-lock/css-subtree-visibility/subtree-visibility-032.html
@@ -40,11 +40,13 @@
   const target = document.getElementById("target");
   let axTarget = axElementById("target");
   t.step(() => { assert_false(axTarget.isIgnored); });
-
-  // Expect 7 children: 4 text nodes ("target" and whitespace between the divs) and 3 div children.
+  // Expect 7 children: 4 text nodes ("target" text and whitespace between the divs) and 3 div children.
+  // This is because #target is locked, and thus is saved as an AXNodeObject instead of an AXLayoutObject,
+  // so we have the separate <newline> text children because AXNodeObject does not do whitespace
+  // collapsing (though this might change in the future).
   // TODO(rakina): Make display:none, visiblity:hidden be ignored/not included in tree even when
   // display target, and make sure whitespace collapsing happen.
-  t.step(() => { assert_equals(axTarget.childrenCount, 7, "Child count after acquire"); });
+  t.step(() => { assert_equals(axTarget.childrenCount, 7, "Children count when locked"); });
 
   // These children are special because their "isVisible" should be false.
   const displayNoneIndex = 3;
@@ -63,27 +65,14 @@
     }
   }
 
-  axTarget = axElementById("target");
-  t.step(() => { assert_equals(axTarget.childrenCount, 7, "Child count after update"); });
-
-  // Should stay the same as after acquiring.
-  for (let i = 0; i < axTarget.childrenCount; ++i) {
-    const axChild = axTarget.childAtIndex(i);
-    t.step(() => { assert_false(axChild.isIgnored, "After update, isIgnored is false on child #" + i); });
-    if (i == displayNoneIndex || i == visibilityHiddenIndex) {
-      t.step(() => { assert_false(axChild.isVisible, "After update, isVisible is false on child #" + i); });
-    } else {
-      t.step(() => { assert_true(axChild.isVisible, "After update, isVisible is true on child #" + i); });
-    }
-  }
-
-  // Remove subtree-visibility and force a layout recalc.
+  // Remove subtree-visibility and force a layout recalc, causing #target to get unlocked.
   target.classList.remove("auto");
   requestAnimationFrame(() => requestAnimationFrame(() => {
     target.getBoundingClientRect();
     axTarget = axElementById("target");
-    // Expect 4 children: the text node "target" and 3 div children (#child, #displayNone, and #hidden).
-    t.step(() => { assert_equals(axTarget.childrenCount, 4, "Child count after commit"); });
+    // Now that #target is not locked, it is saved as a normal AXLayout object and has  4 children:
+    // the text node "target" and 3 div children (#child, #displayNone, and #hidden).
+    t.step(() => { assert_equals(axTarget.childrenCount, 4, "Children count when not locked"); });
 
     for (let i = 0; i < axTarget.childrenCount; ++i) {
       const axChild = axTarget.childAtIndex(i);
diff --git a/third_party/blink/web_tests/wpt_internal/display-lock/css-subtree-visibility/subtree-visibility-033.html b/third_party/blink/web_tests/wpt_internal/display-lock/css-subtree-visibility/subtree-visibility-033.html
index 1eac930..7886ded 100644
--- a/third_party/blink/web_tests/wpt_internal/display-lock/css-subtree-visibility/subtree-visibility-033.html
+++ b/third_party/blink/web_tests/wpt_internal/display-lock/css-subtree-visibility/subtree-visibility-033.html
@@ -32,14 +32,14 @@
   let target = document.getElementById("target");
   let axTarget = axElementById("target");
   t.step(() => { assert_false(axTarget.isIgnored); });
-  t.step(() => { assert_equals(axTarget.childrenCount, 0); });
+  t.step(() => { assert_equals(axTarget.childrenCount, 0, "When locked, nodes in hidden subtree are ignored"); });
 
   target.classList.remove("hidden");
   requestAnimationFrame(() => requestAnimationFrame(() => {
     // The ax object for #target got replaced since the layout object changed, so use the new ax object.
     axTarget = axElementById("target");
-    t.step(() => { assert_equals(axTarget.childrenCount, 2, "After commit, nodes in target subtree are not ignored"); });
+    t.step(() => { assert_equals(axTarget.childrenCount, 2, "After getting unlocked, nodes in hidden subtree are not ignored"); });
     t.done();
   }));
-}, "Nodes in target non-activatable tree are not exposed to accessibility tree");
+}, "Nodes in hidden non-activatable tree are not exposed to accessibility tree");
 </script>
diff --git a/third_party/blink/web_tests/wpt_internal/display-lock/css-subtree-visibility/subtree-visibility-051.html b/third_party/blink/web_tests/wpt_internal/display-lock/css-subtree-visibility/subtree-visibility-051.html
index c407c6b..c282a9a 100644
--- a/third_party/blink/web_tests/wpt_internal/display-lock/css-subtree-visibility/subtree-visibility-051.html
+++ b/third_party/blink/web_tests/wpt_internal/display-lock/css-subtree-visibility/subtree-visibility-051.html
@@ -4,12 +4,14 @@
 <title>Subtree Visibility: accessibility focus</title>
 <link rel="author" title="Rakina Zata Amni" href="mailto:rakina@chromium.org">
 <link rel="help" href="https://github.com/WICG/display-locking">
-<link rel="match" href="container-ref.html">
 <meta name="assert" content="subtree-visibility auto subtrees are exposed by accessibility focus">
 
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 
+<div style="height:10000px;">
+  spacer so that everything below will be offscreen (and won't get viewport-activated)
+</div>
 <div id="hidden" style="subtree-visibility: auto">
   foo
   <div id="child" tabindex="0">
@@ -23,25 +25,25 @@
 }
 
 async_test(async(t) => {
-  t.step(() => {
-    let axChild = axElementById("child");
-    assert_false(Boolean(axChild.backgroundColor), "Background color after activation");
-  });
+  const hiddenEl = document.getElementById("hidden");
+  let axHidden = axElementById("hidden");
+  // #hidden is locked, and thus is saved as an AXNodeObject instead of an AXLayoutObject,
+  // and has 3 child nodes: "foo" text, "<newline>" text, and #child node.
+  // Note that we have the separate <newline> text because it's an AXNodeObject and does
+  // not do whitespace collapsing (though this might change in the future).
+  t.step(() => { assert_equals(axHidden.childrenCount, 3, "Children count when locked"); });
 
-  axElementById("child").takeFocus();  // Use accessibility to set the focus.
-
-  // Wait for a few frames for the ax tree to be refreshed.
+  // Focus to #child, causing #hidden to get unlocked.
+  axElementById("child").takeFocus();
+  // Wait for the next frame for the ax object to be recreated.
   requestAnimationFrame(() => {
     requestAnimationFrame(() => {
--     requestAnimationFrame(() => {
-  -     t.step(() => {
-          let axChild = axElementById("child");
-          assert_equals(axChild.backgroundColor, 0xFFFFFFFF, "Background color after activation");
-        });
-        t.done();
-      });
+      axHidden = axElementById("hidden");
+      // #hidden is now unlocked and saved as a normal AXLayoutObject with 2 child nodes:
+      // "foo" text and #child node.
+      t.step(() => { assert_equals(axHidden.childrenCount, 2, "Children count after activation"); });
+      t.done();
     });
   });
-}, "Accessiblility focus causes activatable hidden tree to activate, and thus accessibility details such as background color become available");
-
+}, "Accessiblility focus causes activatable hidden tree to activate");
 </script>
diff --git a/third_party/blink/web_tests/wpt_internal/display-lock/css-subtree-visibility/subtree-visibility-052.html b/third_party/blink/web_tests/wpt_internal/display-lock/css-subtree-visibility/subtree-visibility-052.html
index 27f6080e..dd2e315 100644
--- a/third_party/blink/web_tests/wpt_internal/display-lock/css-subtree-visibility/subtree-visibility-052.html
+++ b/third_party/blink/web_tests/wpt_internal/display-lock/css-subtree-visibility/subtree-visibility-052.html
@@ -4,18 +4,14 @@
 <title>Subtree Visibility: accessibility press</title>
 <link rel="author" title="Rakina Zata Amni" href="mailto:rakina@chromium.org">
 <link rel="help" href="https://github.com/WICG/display-locking">
-<link rel="match" href="container-ref.html">
 <meta name="assert" content="subtree-visibility auto subtrees are exposed by accessibility press">
 
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 
-<style>
-.auto {
-  subtree-visibility: auto;
-}
-</style>
-
+<div style="height:10000px;">
+  spacer so that everything below will be offscreen (and won't get viewport-activated)
+</div>
 <div id="hidden" style="subtree-visibility: auto">
   foo
   <div id="child" tabindex="0">
@@ -28,18 +24,24 @@
   return accessibilityController.accessibleElementById(id);
 }
 
+// #hidden is locked, and thus is saved as an AXNodeObject instead of an AXLayoutObject,
+// and has 3 child nodes: "foo" text, "<newline>" text, and #child node.
+// Note that we have the separate <newline> text because it's an AXNodeObject and does
+// not do whitespace collapsing (though this might change in the future).
 async_test(async(t) => {
   const hiddenEl = document.getElementById("hidden");
   let axHidden = axElementById("hidden");
-  t.step(() => { assert_equals(axHidden.childrenCount, 3, "Child count after acquire"); });
+  t.step(() => { assert_equals(axHidden.childrenCount, 3, "Children count when locked"); });
 
+  // This should unlock #hidden.
   axElementById("child").press();
-
   // Wait for the next frame for the ax object to be recreated.
   requestAnimationFrame(() => {
     requestAnimationFrame(() => {
       axHidden = axElementById("hidden");
-      t.step(() => { assert_equals(axHidden.childrenCount, 2, "Child count after activation"); });
+      // #hidden is now unlocked and saved as a normal AXLayoutObject with 2 child nodes:
+      // "foo" text and #child node.
+      t.step(() => { assert_equals(axHidden.childrenCount, 2, "Children count after activation"); });
       t.done();
     });
   });
diff --git a/third_party/blink/web_tests/wpt_internal/display-lock/css-subtree-visibility/subtree-visibility-053.html b/third_party/blink/web_tests/wpt_internal/display-lock/css-subtree-visibility/subtree-visibility-053.html
index 85dcb71..9dca6b56 100644
--- a/third_party/blink/web_tests/wpt_internal/display-lock/css-subtree-visibility/subtree-visibility-053.html
+++ b/third_party/blink/web_tests/wpt_internal/display-lock/css-subtree-visibility/subtree-visibility-053.html
@@ -9,17 +9,10 @@
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 
-<style>
-.spacer {
-  height: 3000px;
-}
-.auto {
-  subtree-visibility: auto;
-}
-</style>
-
-<div class=spacer></div>
-<div id="hidden" class=auto>
+<div style="height:10000px;">
+  spacer so that everything below will be offscreen (and won't get viewport-activated)
+</div>
+<div id="hidden" style="subtree-visibility: auto">
   foo
   <div id="child" tabindex="0">
     bar
@@ -35,8 +28,13 @@
 async_test(async(t) => {
   const hiddenEl = document.getElementById("hidden");
   let axHidden = axElementById("hidden");
-  t.step(() => { assert_equals(axHidden.childrenCount, 3, "Child count after acquire"); });
+  // #hidden is locked, and thus is saved as an AXNodeObject instead of an AXLayoutObject,
+  // and has 3 child nodes: "foo" text, "<newline>" text, and #child node.
+  // Note that we have the separate <newline> text because it's an AXNodeObject and does
+  // not do whitespace collapsing (though this might change in the future)..
+  t.step(() => { assert_equals(axHidden.childrenCount, 3, "Children count when locked"); });
 
+  // This should unlock #hidden.
   axElementById("child").scrollToMakeVisible();
 
   // If this is frame 1, then during frame 2, we will determine that we're
diff --git a/third_party/bouncycastle/README.chromium b/third_party/bouncycastle/README.chromium
index 6bedd00..b0f55a4 100644
--- a/third_party/bouncycastle/README.chromium
+++ b/third_party/bouncycastle/README.chromium
@@ -1,6 +1,7 @@
 Name: Bouncy Castle
 URL: https://www.bouncycastle.org/java.html
 Version: 1.46
+CPEPrefix: cpe:/a:bouncycastle:fips_java_api:1.46
 License: MIT
 License File: NOT_SHIPPED
 Security Critical: no
diff --git a/third_party/checkstyle/README.chromium b/third_party/checkstyle/README.chromium
index d2d234a..0938fa1 100644
--- a/third_party/checkstyle/README.chromium
+++ b/third_party/checkstyle/README.chromium
@@ -3,6 +3,7 @@
 Short Name: checkstyle
 URL: https://github.com/checkstyle/checkstyle
 Version: 8.15
+CPEPrefix: cpe:/a:checkstyle:checkstyle:8.15
 License: LGPL 2.1
 License File: NOT_SHIPPED
 Security Critical: no
diff --git a/third_party/d3/README.chromium b/third_party/d3/README.chromium
index d3fcf14..a5f9893 100644
--- a/third_party/d3/README.chromium
+++ b/third_party/d3/README.chromium
@@ -2,6 +2,7 @@
 Short Name: d3
 URL: https://github.com/d3/d3
 Version: 5.7.0
+CPEPrefix: cpe:/a:d3.js_project:d3.js:5.7.0::~~~node.js~~
 Date: Fri Aug 24 5:46 PM 2018 EDT
 Revision: e35d1f74f9337a2eee4aa704f38025621c8ae9d0
 License: BSD 3-Clause
diff --git a/third_party/devscripts/README.chromium b/third_party/devscripts/README.chromium
index cae5d0a..5019458 100644
--- a/third_party/devscripts/README.chromium
+++ b/third_party/devscripts/README.chromium
@@ -1,6 +1,7 @@
 Name: devscripts
 URL: http://anonscm.debian.org/gitweb/?p=devscripts/devscripts.git
 Version: 2.12.4
+CPEPrefix: cpe:/a:debian:devscripts:2.12.4
 Security Critical: no
 License: GPL 2.0
 License File: NOT_SHIPPED
diff --git a/third_party/fontconfig/README.chromium b/third_party/fontconfig/README.chromium
index 0527e45..2355a647 100644
--- a/third_party/fontconfig/README.chromium
+++ b/third_party/fontconfig/README.chromium
@@ -1,6 +1,7 @@
 Name: fontconfig
 URL: http://www.freedesktop.org/wiki/Software/fontconfig/
 Version: 452be8125f0e2a18a7dfef469e05d19374d36307
+CPEPrefix: cpe:/a:fontconfig_project:fontconfig:2.13.91
 License: MIT
 License File: src/COPYING
 Security Critical: yes
diff --git a/third_party/freetype/README.chromium b/third_party/freetype/README.chromium
index 129e70e..26cf50ca 100644
--- a/third_party/freetype/README.chromium
+++ b/third_party/freetype/README.chromium
@@ -2,6 +2,7 @@
 URL: http://www.freetype.org/
 Version: VER-2-10-1-113-g13c0df80d
 Revision: 13c0df80dca59ce2ef3ec125b08c5b6ea485535c
+CPEPrefix: cpe:/a:freetype:freetype:2.10.1
 License: Custom license "inspired by the BSD, Artistic, and IJG (Independent
          JPEG Group) licenses"
 License File: src/docs/FTL.TXT
diff --git a/third_party/guava/README.chromium b/third_party/guava/README.chromium
index 12ec68e3..f31dc74 100644
--- a/third_party/guava/README.chromium
+++ b/third_party/guava/README.chromium
@@ -2,6 +2,7 @@
 Short Name: guava
 URL: https://github.com/google/guava
 Version: 23.0
+CPEPrefix: cpe:/a:google:guava:23.0:rc1
 License: Apache 2.0
 License File: NOT_SHIPPED
 Security Critical: no
diff --git a/third_party/hunspell/README.chromium b/third_party/hunspell/README.chromium
index cb0eb94..1bb154a50 100644
--- a/third_party/hunspell/README.chromium
+++ b/third_party/hunspell/README.chromium
@@ -1,6 +1,7 @@
 Name: hunspell
 URL: http://hunspell.sourceforge.net/
 Version: 1.6.0
+CPEPrefix: cpe:/a:hunspell_project:hunspell:1.6.0
 License: MPL 1.1/GPL 2.0/LGPL 2.1
 License File: COPYING.MPL
 Security Critical: yes
diff --git a/third_party/jdk/README.chromium b/third_party/jdk/README.chromium
index cdfca5b4..8481c3a 100644
--- a/third_party/jdk/README.chromium
+++ b/third_party/jdk/README.chromium
@@ -2,6 +2,7 @@
 Short Name: JDK
 URL: https://www.java.com/
 Version: 11.0.4
+CPEPrefix: cpe:/a:oracle:jdk:11.0.4
 License: GPL v2
 License File: NOT_SHIPPED
 Security Critical: no
diff --git a/third_party/jinja2/README.chromium b/third_party/jinja2/README.chromium
index 5246c2f..9b8311a 100644
--- a/third_party/jinja2/README.chromium
+++ b/third_party/jinja2/README.chromium
@@ -2,6 +2,7 @@
 Short Name: jinja2
 URL: http://jinja.pocoo.org/
 Version: 2.10
+CPEPrefix: cpe:/a:pocoo:jinja2:2.10
 License: BSD 3-Clause
 License File: LICENSE
 Security Critical: no
diff --git a/third_party/jsoncpp/README.chromium b/third_party/jsoncpp/README.chromium
index 48bc543..da90c30 100644
--- a/third_party/jsoncpp/README.chromium
+++ b/third_party/jsoncpp/README.chromium
@@ -1,6 +1,7 @@
 Name: jsoncpp
 URL: https://github.com/open-source-parsers/jsoncpp
-Version: f572e8e42e22cfcf5ab0aea26574f408943edfa4
+Version: 645250b6690785be60ab6780ce4b58698d884d11
+CPEPrefix: cpe:/a:jsoncpp_project:jsoncpp:1.9.1
 License: MIT
 License File: LICENSE
 Security Critical: yes
diff --git a/third_party/junit/README.chromium b/third_party/junit/README.chromium
index ddd5a553..26fead7 100644
--- a/third_party/junit/README.chromium
+++ b/third_party/junit/README.chromium
@@ -1,6 +1,7 @@
 Name: JUnit
 URL: http://junit.org
 Version: 4.12
+CPEPrefix: cpe:/a:jenkins:junit:4.12::~~~jenkins~~
 License: Common Public License 1.0
 License File: NOT_SHIPPED
 Security Critical: no
diff --git a/third_party/liblouis/README.chromium b/third_party/liblouis/README.chromium
index cbbbd61..0fcb3cd0 100644
--- a/third_party/liblouis/README.chromium
+++ b/third_party/liblouis/README.chromium
@@ -2,6 +2,7 @@
 Short Name: liblouis
 URL: https://github.com/liblouis/liblouis
 Version: 3.12.0
+CPEPrefix: cpe:/a:liblouis:liblouis:3.12.0
 License: LGPL3 and GPL3
 License Android Compatible: No
 License File: LICENSE
diff --git a/third_party/libusb/README.chromium b/third_party/libusb/README.chromium
index d2301774..071a981 100644
--- a/third_party/libusb/README.chromium
+++ b/third_party/libusb/README.chromium
@@ -1,6 +1,7 @@
 Name: libusbx
 URL: http://libusb.org
 Version: 1.0.17
+CPEPrefix: cpe:/a:libusb:libusb:1.0.17:rc1
 License: LGPL 2.1
 License File: src/COPYING
 Security Critical: yes
diff --git a/third_party/libwebm/README.chromium b/third_party/libwebm/README.chromium
index 7d3b804..6a3fcd52 100644
--- a/third_party/libwebm/README.chromium
+++ b/third_party/libwebm/README.chromium
@@ -2,6 +2,7 @@
 Short Name: libwebm
 URL: http://www.webmproject.org/code/
 Version: unknown
+CPEPrefix: cpe:/a:webmproject:libwebm:1.0.0.27
 License: BSD
 License File: source/LICENSE.TXT
 Security Critical: yes
diff --git a/third_party/libwebp/README.chromium b/third_party/libwebp/README.chromium
index 0a911d0..768d6de 100644
--- a/third_party/libwebp/README.chromium
+++ b/third_party/libwebp/README.chromium
@@ -2,6 +2,7 @@
 Short Name: libwebp
 URL: http://developers.google.com/speed/webp
 Version: v1.1.0
+CPEPrefix: cpe:/a:webmproject:libwebp:1.1.0
 License: BSD
 License File: LICENSE
 Security Critical: Yes
diff --git a/third_party/libxml/README.chromium b/third_party/libxml/README.chromium
index 2aa8772..bbe3179 100644
--- a/third_party/libxml/README.chromium
+++ b/third_party/libxml/README.chromium
@@ -1,6 +1,7 @@
 Name: libxml
 URL: http://xmlsoft.org
 Version: a7fe7ee45938c53a8dd028dd40baa461191a2fd2
+CPEPrefix: cpe:/a:xmlsoft:libxml2:2.9.9
 License: MIT
 License File: src/Copyright
 Security Critical: yes
diff --git a/third_party/logilab/logilab/common/README.chromium b/third_party/logilab/logilab/common/README.chromium
index fdc9991..8283ad6 100644
--- a/third_party/logilab/logilab/common/README.chromium
+++ b/third_party/logilab/logilab/common/README.chromium
@@ -1,6 +1,7 @@
 URL: http://www.logilab.org/project/logilab-common
 Name: logilab-common
 Version: 0.63.2
+CPEPrefix: cpe:/a:logilab:logilab-common:0.63.2
 License: GPL v2
 License File: LICENSE.txt
 Security Critical: No
diff --git a/third_party/markdown/README.chromium b/third_party/markdown/README.chromium
index a376550..4e292a0 100644
--- a/third_party/markdown/README.chromium
+++ b/third_party/markdown/README.chromium
@@ -2,6 +2,7 @@
 Short Name: markdown
 URL: https://pypi.python.org/pypi/Markdown
 Version: 2.3.1
+CPEPrefix: cpe:/a:cebe:markdown:2.3.1
 Date: March 22, 2013
 Revision: commit 0ea0bac85a749a13381b721cde44214d9193406c
 License: BSD 3-Clause
diff --git a/third_party/mesa_headers/README.chromium b/third_party/mesa_headers/README.chromium
index 03f9ee52..a5ed85a 100644
--- a/third_party/mesa_headers/README.chromium
+++ b/third_party/mesa_headers/README.chromium
@@ -1,5 +1,6 @@
 Name: mesa_headers
 Version: 9.0.3
+CPEPrefix: cpe:/a:mesa3d:mesa:9.0.3
 URL: http://www.mesa3d.org/
 License: MIT and SGI Free Software B License Version 2.0
 Security Critical: Yes
diff --git a/third_party/minizip/README.chromium b/third_party/minizip/README.chromium
index cc7f42b..3a30e02 100644
--- a/third_party/minizip/README.chromium
+++ b/third_party/minizip/README.chromium
@@ -2,6 +2,7 @@
 Short name: minizip
 URL: https://github.com/nmoinvaz/minizip
 Version: 2.8.1
+CPEPrefix: cpe:/a:minizip_project:minizip:2.8.1
 Revision: 1ff40343b55e738d941abb51c70eddb803db16e2
 Security critical: yes
 License: Custom license
diff --git a/third_party/mozilla/README.chromium b/third_party/mozilla/README.chromium
index 9ea000df..085e486 100644
--- a/third_party/mozilla/README.chromium
+++ b/third_party/mozilla/README.chromium
@@ -2,6 +2,7 @@
 Short Name: camino
 URL: http://caminobrowser.org/
 Version: unknown
+CPEPrefix: unknown
 License: MPL 1.1/GPL 2.0/LGPL 2.1
 Security Critical: yes
 
diff --git a/third_party/openh264/README.chromium b/third_party/openh264/README.chromium
index 3d17eda..4f5b9fd 100644
--- a/third_party/openh264/README.chromium
+++ b/third_party/openh264/README.chromium
@@ -2,6 +2,7 @@
 Short Name: openh264
 URL: http://www.openh264.org/
 Version: unknown
+CPEPrefix: cpe:/a:cisco:openh264:1.9.0
 (Cut at 6f26bce0b1c4e8ce0e13332f7c0083788def5fdf, which is between 1.9.0 and
 1.10.0)
 License: 2-Clause BSD
diff --git a/third_party/protobuf/README.chromium b/third_party/protobuf/README.chromium
index a42e244..dc47470 100644
--- a/third_party/protobuf/README.chromium
+++ b/third_party/protobuf/README.chromium
@@ -4,6 +4,7 @@
 License: BSD
 License File: LICENSE
 Version: 3.9.0
+CPEPrefix: cpe:/a:google:protobuf:3.9.0
 Revision: cf242503ec157a7dda8a6eda48712dd26c81d2e6
 Security Critical: yes
 
diff --git a/third_party/r8/README.chromium b/third_party/r8/README.chromium
index 58f46ae..5d6ff82 100644
--- a/third_party/r8/README.chromium
+++ b/third_party/r8/README.chromium
@@ -10,25 +10,14 @@
 R8 is a proguard-like optimizer that also has the ability to dex.
 
 Local Modifications:
-Turning off vertical and horizontal class merging, as described in
-https://issuetracker.google.com/122902374#comment11. We should no longer make
-this modification once the linked bug is fixed.
-
-Turning off outlining because it caused issues when synchronized proguarding
-Trichrome due to illegal dex references (crbug.com/956839). Will only be used
-for android_apk() targets that set `disable_r8_outlining = true`):
-
-Added --feature-jar flag for doing dex splitting.
-
-local_modifications.diff created via:
-git diff >local_modifications.diff
+* Refer to commit descriptions within "patches" directory.
 
 Update Instructions:
 # Download R8:
 git clone https://r8.googlesource.com/r8
 cd r8
-# Apply the patch:
-git apply $CHROMIUM_SRC/third_party/r8/local_modifications.diff
+# Apply patches:
+git am $CHROMIUM_SRC/third_party/r8/patches *.patch
 # Build:
 tools/gradle.py r8
 # Shrink (improves r8/d8 launch time):
@@ -44,3 +33,7 @@
 # Manually update:
 * README.chromium (version number via "java -jar lib/r8.jar --version")
 * //DEPS (instance ID output by cipd create)
+
+# To create a new patch:
+* Make commits to the r8 repro
+* Run: git format-patch $R8_REVISION_BEFORE_PATCHES
diff --git a/third_party/r8/local_modifications.diff b/third_party/r8/local_modifications.diff
deleted file mode 100644
index 8b695e0..0000000
--- a/third_party/r8/local_modifications.diff
+++ /dev/null
@@ -1,120 +0,0 @@
-diff --git a/src/main/java/com/android/tools/r8/D8CommandParser.java b/src/main/java/com/android/tools/r8/D8CommandParser.java
-index f66fff8f3..c63f4f566 100644
---- a/src/main/java/com/android/tools/r8/D8CommandParser.java
-+++ b/src/main/java/com/android/tools/r8/D8CommandParser.java
-@@ -205,7 +205,7 @@ public class D8CommandParser extends BaseCompilerCommandParser<D8Command, D8Comm
-         }
-         compilationMode = CompilationMode.RELEASE;
-       } else if (arg.equals("--file-per-class")) {
--        outputMode = OutputMode.DexFilePerClass;
-+        outputMode = OutputMode.DexFilePerClassFile;
-       } else if (arg.equals("--output")) {
-         if (outputPath != null) {
-           builder.error(
-diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java
-index 4051f9cef..6d034d5b8 100644
---- a/src/main/java/com/android/tools/r8/R8Command.java
-+++ b/src/main/java/com/android/tools/r8/R8Command.java
-@@ -42,6 +42,8 @@ import java.util.function.BiPredicate;
- import java.util.function.Consumer;
- import java.util.function.Function;
- 
-+import com.android.tools.r8.DexIndexedConsumer.DirectoryConsumer;
-+
- /**
-  * Immutable command structure for an invocation of the {@link R8} compiler.
-  *
-@@ -234,6 +236,15 @@ public final class R8Command extends BaseCompilerCommand {
-       return self();
-     }
- 
-+    public Builder addFeatureJar(Path inputJarPath, Path outputPath) {
-+      addFeatureSplit(splitBuilder ->
-+          splitBuilder
-+            .addProgramResourceProvider(ArchiveProgramResourceProvider.fromArchive(inputJarPath))
-+            .setProgramConsumer(new DirectoryConsumer(outputPath))
-+            .build());
-+      return self();
-+    }
-+
-     /**
-      * Set a consumer for receiving the proguard-map content.
-      *
-diff --git a/src/main/java/com/android/tools/r8/R8CommandParser.java b/src/main/java/com/android/tools/r8/R8CommandParser.java
-index 22ebb5e42..b53e4c59e 100644
---- a/src/main/java/com/android/tools/r8/R8CommandParser.java
-+++ b/src/main/java/com/android/tools/r8/R8CommandParser.java
-@@ -24,6 +24,7 @@ public class R8CommandParser extends BaseCompilerCommandParser<R8Command, R8Comm
-           "--min-api",
-           "--main-dex-rules",
-           "--main-dex-list",
-+          "--feature-jar",
-           "--main-dex-list-output",
-           "--pg-conf",
-           "--pg-map-output",
-@@ -205,6 +206,11 @@ public class R8CommandParser extends BaseCompilerCommandParser<R8Command, R8Comm
-         builder.setDisableDesugaring(true);
-       } else if (arg.equals("--main-dex-rules")) {
-         builder.addMainDexRulesFiles(Paths.get(nextArg));
-+      } else if (arg.equals("--feature-jar")) {
-+        String[] argParts = nextArg.split(":");
-+        String featureJarInputPath = argParts[0];
-+        String featureJarOutputPath = argParts[1];
-+        builder.addFeatureJar(Paths.get(featureJarInputPath), Paths.get(featureJarOutputPath));
-       } else if (arg.equals("--main-dex-list")) {
-         builder.addMainDexListFiles(Paths.get(nextArg));
-       } else if (arg.equals("--main-dex-list-output")) {
-diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
-index 0831e6d93..49c1a202b 100644
---- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
-+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
-@@ -206,7 +206,7 @@ public final class LambdaClass {
-   }
- 
-   public final boolean isStateless() {
--    return descriptor.isStateless();
-+    return false;  // descriptor.isStateless();
-   }
- 
-   void addSynthesizedFrom(DexProgramClass clazz) {
-diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
-index 232b54a54..fd45dfe36 100644
---- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
-+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
-@@ -198,8 +198,8 @@ public class InternalOptions {
-   public boolean enableFieldAssignmentTracker = true;
-   public boolean enableFieldBitAccessAnalysis =
-       System.getProperty("com.android.tools.r8.fieldBitAccessAnalysis") != null;
--  public boolean enableHorizontalClassMerging = true;
--  public boolean enableVerticalClassMerging = true;
-+  public boolean enableHorizontalClassMerging = System.getProperty("com.android.tools.r8.enableHorizontalClassMerging") != null;
-+  public boolean enableVerticalClassMerging = System.getProperty("com.android.tools.r8.enableVerticalClassMerging") != null;
-   public boolean enableArgumentRemoval = true;
-   public boolean enableUnusedArgumentRemoval = true;
-   public boolean enableUnusedInterfaceRemoval = true;
-@@ -956,7 +956,7 @@ public class InternalOptions {
-     public static final String CLASS_NAME = "com.android.tools.r8.GeneratedOutlineSupport";
-     public static final String METHOD_PREFIX = "outline";
- 
--    public boolean enabled = true;
-+    public boolean enabled = System.getProperty("com.android.tools.r8.disableOutlining") == null;
-     public int minSize = 3;
-     public int maxSize = 99;
-     public int threshold = 20;
-diff --git a/src/main/keep.txt b/src/main/keep.txt
-index b0c58c916..5e4405175 100644
---- a/src/main/keep.txt
-+++ b/src/main/keep.txt
-@@ -27,4 +27,10 @@
- -keep public class com.android.tools.r8.compatproguard.CompatProguard { public static void main(java.lang.String[]); }
- 
- # Compatibility command line program used by in google3.
---keep public class com.android.tools.r8.compatdx.CompatDx { public static void main(java.lang.String[]); }
-\ No newline at end of file
-+-keep public class com.android.tools.r8.compatdx.CompatDx { public static void main(java.lang.String[]); }
-+
-+-keep public class com.android.tools.r8.SwissArmyKnife { *; }
-+
-+# Chrome: We want to maintain stack traces from the R8 tool, so disable
-+# optimizations.
-+-dontoptimize
diff --git a/third_party/r8/patches/0001-Chrome-Change-meaning-of-file-per-class.patch b/third_party/r8/patches/0001-Chrome-Change-meaning-of-file-per-class.patch
new file mode 100644
index 0000000..be387522
--- /dev/null
+++ b/third_party/r8/patches/0001-Chrome-Change-meaning-of-file-per-class.patch
@@ -0,0 +1,29 @@
+From 0cc1077ef5dcdccafc5f526e682dff88d46c67ff Mon Sep 17 00:00:00 2001
+From: Andrew Grieve <agrieve@chromium.org>
+Date: Wed, 1 Apr 2020 09:59:30 -0400
+Subject: [PATCH 1/5] Chrome: Change meaning of --file-per-class
+
+Make it mean OutputMode.DexFilePerClassFile instead of
+OutputMode.DexFilePerClass.
+
+Fixes Chrome's incremental dexing logic.
+---
+ src/main/java/com/android/tools/r8/D8CommandParser.java | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/src/main/java/com/android/tools/r8/D8CommandParser.java b/src/main/java/com/android/tools/r8/D8CommandParser.java
+index f66fff8f3..c63f4f566 100644
+--- a/src/main/java/com/android/tools/r8/D8CommandParser.java
++++ b/src/main/java/com/android/tools/r8/D8CommandParser.java
+@@ -205,7 +205,7 @@ public class D8CommandParser extends BaseCompilerCommandParser<D8Command, D8Comm
+         }
+         compilationMode = CompilationMode.RELEASE;
+       } else if (arg.equals("--file-per-class")) {
+-        outputMode = OutputMode.DexFilePerClass;
++        outputMode = OutputMode.DexFilePerClassFile;
+       } else if (arg.equals("--output")) {
+         if (outputPath != null) {
+           builder.error(
+-- 
+2.26.0.rc2.310.g2932bb562d-goog
+
diff --git a/third_party/r8/patches/0002-Adds-feature-jar-to-R8-command.patch b/third_party/r8/patches/0002-Adds-feature-jar-to-R8-command.patch
new file mode 100644
index 0000000..1120f38
--- /dev/null
+++ b/third_party/r8/patches/0002-Adds-feature-jar-to-R8-command.patch
@@ -0,0 +1,67 @@
+From 497bfde1cf89ca66777dcb3314c4eef331d3e7ab Mon Sep 17 00:00:00 2001
+From: Andrew Grieve <agrieve@chromium.org>
+Date: Wed, 1 Apr 2020 10:03:06 -0400
+Subject: [PATCH 2/5] Adds --feature-jar to R8 command
+
+Chrome needs this to expose feature splits via command-line
+---
+ src/main/java/com/android/tools/r8/R8Command.java     | 11 +++++++++++
+ .../java/com/android/tools/r8/R8CommandParser.java    |  6 ++++++
+ 2 files changed, 17 insertions(+)
+
+diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java
+index 4051f9cef..6d034d5b8 100644
+--- a/src/main/java/com/android/tools/r8/R8Command.java
++++ b/src/main/java/com/android/tools/r8/R8Command.java
+@@ -42,6 +42,8 @@ import java.util.function.BiPredicate;
+ import java.util.function.Consumer;
+ import java.util.function.Function;
+ 
++import com.android.tools.r8.DexIndexedConsumer.DirectoryConsumer;
++
+ /**
+  * Immutable command structure for an invocation of the {@link R8} compiler.
+  *
+@@ -234,6 +236,15 @@ public final class R8Command extends BaseCompilerCommand {
+       return self();
+     }
+ 
++    public Builder addFeatureJar(Path inputJarPath, Path outputPath) {
++      addFeatureSplit(splitBuilder ->
++          splitBuilder
++            .addProgramResourceProvider(ArchiveProgramResourceProvider.fromArchive(inputJarPath))
++            .setProgramConsumer(new DirectoryConsumer(outputPath))
++            .build());
++      return self();
++    }
++
+     /**
+      * Set a consumer for receiving the proguard-map content.
+      *
+diff --git a/src/main/java/com/android/tools/r8/R8CommandParser.java b/src/main/java/com/android/tools/r8/R8CommandParser.java
+index 22ebb5e42..b53e4c59e 100644
+--- a/src/main/java/com/android/tools/r8/R8CommandParser.java
++++ b/src/main/java/com/android/tools/r8/R8CommandParser.java
+@@ -24,6 +24,7 @@ public class R8CommandParser extends BaseCompilerCommandParser<R8Command, R8Comm
+           "--min-api",
+           "--main-dex-rules",
+           "--main-dex-list",
++          "--feature-jar",
+           "--main-dex-list-output",
+           "--pg-conf",
+           "--pg-map-output",
+@@ -205,6 +206,11 @@ public class R8CommandParser extends BaseCompilerCommandParser<R8Command, R8Comm
+         builder.setDisableDesugaring(true);
+       } else if (arg.equals("--main-dex-rules")) {
+         builder.addMainDexRulesFiles(Paths.get(nextArg));
++      } else if (arg.equals("--feature-jar")) {
++        String[] argParts = nextArg.split(":");
++        String featureJarInputPath = argParts[0];
++        String featureJarOutputPath = argParts[1];
++        builder.addFeatureJar(Paths.get(featureJarInputPath), Paths.get(featureJarOutputPath));
+       } else if (arg.equals("--main-dex-list")) {
+         builder.addMainDexListFiles(Paths.get(nextArg));
+       } else if (arg.equals("--main-dex-list-output")) {
+-- 
+2.26.0.rc2.310.g2932bb562d-goog
+
diff --git a/third_party/r8/patches/0003-Desugaring-Make-all-lambdas-stateless-fix-naming-sch.patch b/third_party/r8/patches/0003-Desugaring-Make-all-lambdas-stateless-fix-naming-sch.patch
new file mode 100644
index 0000000..e57b97b
--- /dev/null
+++ b/third_party/r8/patches/0003-Desugaring-Make-all-lambdas-stateless-fix-naming-sch.patch
@@ -0,0 +1,39 @@
+From 6c3009d363b1e50adc83279dba27b4e0a350ead7 Mon Sep 17 00:00:00 2001
+From: Andrew Grieve <agrieve@chromium.org>
+Date: Wed, 1 Apr 2020 10:03:46 -0400
+Subject: [PATCH 3/5] Desugaring: Make all lambdas stateless & fix naming
+ scheme of method references
+
+Statefull lambdas regress dex size.
+
+Bug: b/129997269
+---
+ .../java/com/android/tools/r8/ir/desugar/LambdaClass.java    | 5 +++--
+ 1 file changed, 3 insertions(+), 2 deletions(-)
+
+diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
+index 0831e6d93..0abb97180 100644
+--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
++++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
+@@ -128,7 +128,8 @@ public final class LambdaClass {
+     // If the lambda class should match 1:1 the class it is accessed from, we
+     // just add the name of this type to make lambda class name unique.
+     // It also helps link the class lambda originated from in some cases.
+-    if (match.delegatesToLambdaImplMethod() || match.needsAccessor(accessedFrom)) {
++    if (true || match.delegatesToLambdaImplMethod() || match.needsAccessor(accessedFrom)) {
++      // Chrome: This class name is required for supersize to map the symbol to source .java file.
+       lambdaClassDescriptor.append(accessedFrom.getName()).append('$');
+     }
+ 
+@@ -206,7 +207,7 @@ public final class LambdaClass {
+   }
+ 
+   public final boolean isStateless() {
+-    return descriptor.isStateless();
++    return false;  // descriptor.isStateless();
+   }
+ 
+   void addSynthesizedFrom(DexProgramClass clazz) {
+-- 
+2.26.0.rc2.310.g2932bb562d-goog
+
diff --git a/third_party/r8/patches/0004-Make-class-merging-and-outlining-settable-via-comman.patch b/third_party/r8/patches/0004-Make-class-merging-and-outlining-settable-via-comman.patch
new file mode 100644
index 0000000..d5037d68
--- /dev/null
+++ b/third_party/r8/patches/0004-Make-class-merging-and-outlining-settable-via-comman.patch
@@ -0,0 +1,37 @@
+From c090e841899461437cc91adbdfc7c74ef8091a53 Mon Sep 17 00:00:00 2001
+From: Andrew Grieve <agrieve@chromium.org>
+Date: Wed, 1 Apr 2020 10:06:15 -0400
+Subject: [PATCH 4/5] Make class merging and outlining settable via
+ command-line
+
+---
+ .../java/com/android/tools/r8/utils/InternalOptions.java    | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+index 232b54a54..fd45dfe36 100644
+--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
++++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+@@ -198,8 +198,8 @@ public class InternalOptions {
+   public boolean enableFieldAssignmentTracker = true;
+   public boolean enableFieldBitAccessAnalysis =
+       System.getProperty("com.android.tools.r8.fieldBitAccessAnalysis") != null;
+-  public boolean enableHorizontalClassMerging = true;
+-  public boolean enableVerticalClassMerging = true;
++  public boolean enableHorizontalClassMerging = System.getProperty("com.android.tools.r8.enableHorizontalClassMerging") != null;
++  public boolean enableVerticalClassMerging = System.getProperty("com.android.tools.r8.enableVerticalClassMerging") != null;
+   public boolean enableArgumentRemoval = true;
+   public boolean enableUnusedArgumentRemoval = true;
+   public boolean enableUnusedInterfaceRemoval = true;
+@@ -956,7 +956,7 @@ public class InternalOptions {
+     public static final String CLASS_NAME = "com.android.tools.r8.GeneratedOutlineSupport";
+     public static final String METHOD_PREFIX = "outline";
+ 
+-    public boolean enabled = true;
++    public boolean enabled = System.getProperty("com.android.tools.r8.disableOutlining") == null;
+     public int minSize = 3;
+     public int maxSize = 99;
+     public int threshold = 20;
+-- 
+2.26.0.rc2.310.g2932bb562d-goog
+
diff --git a/third_party/r8/patches/0005-Fix-up-proguard-keeps-for-Chrome.patch b/third_party/r8/patches/0005-Fix-up-proguard-keeps-for-Chrome.patch
new file mode 100644
index 0000000..f42ab8ae
--- /dev/null
+++ b/third_party/r8/patches/0005-Fix-up-proguard-keeps-for-Chrome.patch
@@ -0,0 +1,29 @@
+From fe0cf2feb23d4cdcf4dc287bddba9ea07538aa35 Mon Sep 17 00:00:00 2001
+From: Andrew Grieve <agrieve@chromium.org>
+Date: Wed, 1 Apr 2020 10:06:57 -0400
+Subject: [PATCH 5/5] Fix up proguard keeps for Chrome
+
+---
+ src/main/keep.txt | 8 +++++++-
+ 1 file changed, 7 insertions(+), 1 deletion(-)
+
+diff --git a/src/main/keep.txt b/src/main/keep.txt
+index b0c58c916..5e4405175 100644
+--- a/src/main/keep.txt
++++ b/src/main/keep.txt
+@@ -27,4 +27,10 @@
+ -keep public class com.android.tools.r8.compatproguard.CompatProguard { public static void main(java.lang.String[]); }
+ 
+ # Compatibility command line program used by in google3.
+--keep public class com.android.tools.r8.compatdx.CompatDx { public static void main(java.lang.String[]); }
+\ No newline at end of file
++-keep public class com.android.tools.r8.compatdx.CompatDx { public static void main(java.lang.String[]); }
++
++-keep public class com.android.tools.r8.SwissArmyKnife { *; }
++
++# Chrome: We want to maintain stack traces from the R8 tool, so disable
++# optimizations.
++-dontoptimize
+-- 
+2.26.0.rc2.310.g2932bb562d-goog
+
diff --git a/third_party/requests/README.chromium b/third_party/requests/README.chromium
index ac14377..f9e12871 100644
--- a/third_party/requests/README.chromium
+++ b/third_party/requests/README.chromium
@@ -1,6 +1,7 @@
 Name: Requests
 URL: http://docs.python-requests.org/en/latest/
 Version: 2.5.0
+CPEPrefix: cpe:/a:python-requests:requests:2.5.0
 License: Apache Version 2.0
 License File: NOT_SHIPPED
 Security Critical: no
diff --git a/third_party/simplejson/README.chromium b/third_party/simplejson/README.chromium
index 1301c6a..6a1313e2 100644
--- a/third_party/simplejson/README.chromium
+++ b/third_party/simplejson/README.chromium
@@ -1,6 +1,7 @@
 Name: simplejson
 URL: https://github.com/simplejson/simplejson
 Version: 2.6.2
+CPEPrefix: cpe:/a:simplejson_project:simplejson:2.6.2::~~~python~~
 License: MIT
 License File: LICENSE.txt
 Security Critical: no
diff --git a/third_party/snappy/README.chromium b/third_party/snappy/README.chromium
index b7d03c4..896f2aaa 100644
--- a/third_party/snappy/README.chromium
+++ b/third_party/snappy/README.chromium
@@ -2,6 +2,7 @@
 Short Name: snappy
 URL: http://google.github.io/snappy/
 Version: 1.1.7.git.3f194acb57e0487531c96b97af61dcbd025a78a3
+CPEPrefix: cpe:/a:google:snappy:1.1.7
 License: New BSD
 License File: src/COPYING
 Security Critical: yes
diff --git a/third_party/sqlite/README.chromium b/third_party/sqlite/README.chromium
index 455fd0fc..4eabc10 100644
--- a/third_party/sqlite/README.chromium
+++ b/third_party/sqlite/README.chromium
@@ -1,6 +1,7 @@
 Name: sqlite
 URL: https://sqlite.org/
 Version: 3.31.1
+CPEPrefix: cpe:/a:sqlite:sqlite:3.31.1
 Included In Release: Yes
 Security Critical: Yes
 License: Public domain
diff --git a/third_party/tlslite/README.chromium b/third_party/tlslite/README.chromium
index 2725734..b189e12 100644
--- a/third_party/tlslite/README.chromium
+++ b/third_party/tlslite/README.chromium
@@ -1,6 +1,7 @@
 Name: tlslite
 URL: http://trevp.net/tlslite/
 Version: 0.4.8
+CPEPrefix: cpe:/a:tlslite_project:tlslite:0.4.8::~~~python~~
 Security Critical: No
 License: Public domain and BSD
 
diff --git a/third_party/wayland/README.chromium b/third_party/wayland/README.chromium
index cebc3a5..f577a0cc 100644
--- a/third_party/wayland/README.chromium
+++ b/third_party/wayland/README.chromium
@@ -1,6 +1,7 @@
 Name: wayland
 URL: http://wayland.freedesktop.org/
 Version: 1.17.0
+CPEPrefix: cpe:/a:wayland:wayland:1.17.0
 License: MIT
 License File: src/COPYING
 Security Critical: yes
diff --git a/third_party/xstream/README.chromium b/third_party/xstream/README.chromium
index 6568f65f..1e0397590 100644
--- a/third_party/xstream/README.chromium
+++ b/third_party/xstream/README.chromium
@@ -1,6 +1,7 @@
 Name: Xstream
 URL: https://x-stream.github.io/
 Version: 1.4.8
+CPEPrefix: cpe:/a:xstream_project:xstream:1.4.8
 License: BSD 3-Clause
 License File: NOT_SHIPPED
 Security Critical: no
diff --git a/third_party/zlib/README.chromium b/third_party/zlib/README.chromium
index 3d90f79..c3c1ef6 100644
--- a/third_party/zlib/README.chromium
+++ b/third_party/zlib/README.chromium
@@ -2,6 +2,7 @@
 Short Name: zlib
 URL: http://zlib.net/
 Version: 1.2.11
+CPEPrefix: cpe:/a:zlib:zlib:1.2.11
 Security Critical: yes
 License: Custom license
 License File: LICENSE
diff --git a/tools/binary_size/libsupersize/apkanalyzer.py b/tools/binary_size/libsupersize/apkanalyzer.py
index 460b9b8..9eba8fb 100644
--- a/tools/binary_size/libsupersize/apkanalyzer.py
+++ b/tools/binary_size/libsupersize/apkanalyzer.py
@@ -7,6 +7,7 @@
 Assumes that apk_path.mapping and apk_path.jar.info is available.
 """
 
+import collections
 import logging
 import os
 import subprocess
@@ -135,6 +136,13 @@
   return nodes
 
 
+def _TruncateFrom(value, delimiter, rfind=False):
+  idx = value.rfind(delimiter) if rfind else value.find(delimiter)
+  if idx != -1:
+    return value[:idx]
+  return value
+
+
 def CreateDexSymbols(apk_path, mapping_path, size_info_prefix):
   source_map = _ParseJarInfoFile(size_info_prefix + '.jar.info')
 
@@ -154,10 +162,33 @@
   # We have more than 100KB of ids for methods, strings
   id_metadata_overhead_size = dex_expected_size - total_node_size
   symbols = []
+  lambda_by_class_counter = collections.defaultdict(int)
+  lambda_name_to_nested_number = {}
   for _, name, node_size in nodes:
-    package = name.split(' ', 1)[0]
-    class_path = package.split('$')[0]
-    source_path = source_map.get(class_path, '')
+    package = _TruncateFrom(name, ' ')
+    # Make d8 desugared lambdas look the same as Desugar ones.
+    # D8 lambda: -$$Lambda$Promise$Nested1$kjevdDQ8V2zqCrdieLqWLHzk.dex
+    # Desugar lambda: Promise$Nested1$$Lambda$0
+    # 1) Need to prefix with proper class name so that they will show as nested.
+    # 2) Need to suffix with number so that they diff better.
+    # Original name will be kept as "object_path".
+    is_d8_lambda = '-$$Lambda$' in package
+    class_path = package
+    if is_d8_lambda:
+      class_path = package.replace('-$$Lambda$', '')
+      base_name = _TruncateFrom(class_path, '$', rfind=True)
+      # Map all methods of the lambda class to the same nested number.
+      lambda_number = lambda_name_to_nested_number.get(class_path)
+      if lambda_number is None:
+        # First time we've seen this lambda, increment nested class count.
+        lambda_number = lambda_by_class_counter[base_name]
+        lambda_name_to_nested_number[class_path] = lambda_number
+        lambda_by_class_counter[base_name] = lambda_number + 1
+
+      name = '{}$$Lambda${}{}'.format(base_name, lambda_number,
+                                      name[len(package):])
+    # Map all nested classes to outer class.
+    source_path = source_map.get(_TruncateFrom(class_path, '$'), '')
     if source_path:
       object_path = package
     elif package == _TOTAL_NODE_NAME:
diff --git a/tools/binary_size/libsupersize/diff.py b/tools/binary_size/libsupersize/diff.py
index 47e42f1..09aafe43e 100644
--- a/tools/binary_size/libsupersize/diff.py
+++ b/tools/binary_size/libsupersize/diff.py
@@ -25,6 +25,7 @@
   name = _STRIP_NUMBERS_PATTERN.sub('', s.full_name)
   # Prefer source_path over object_path since object_path for native files have
   # the target_name in it (which can get renamed).
+  # Also because object_path of Java lambdas symbols contains a hash.
   path = s.source_path or s.object_path
   # Use section rather than section_name since clang & gcc use
   # .data.rel.ro vs. .data.rel.ro.local.
diff --git a/tools/binary_size/libsupersize/testdata/Archive_Apk.golden b/tools/binary_size/libsupersize/testdata/Archive_Apk.golden
index 85e22d6..8b15dab 100644
--- a/tools/binary_size/libsupersize/testdata/Archive_Apk.golden
+++ b/tools/binary_size/libsupersize/testdata/Archive_Apk.golden
@@ -51,15 +51,15 @@
 * 6 have a component assigned. Accounts for 524520 bytes (100.0%).
 * 0 symbols have shared ownership.
 * 3 symbols are from generated sources. Accounts for 232 bytes (0.0%).
-Section .dex: has 100.0% of 8365003 bytes accounted for from 93 symbols. 0 bytes are unaccounted for.
+Section .dex: has 100.0% of 8364863 bytes accounted for from 100 symbols. 0 bytes are unaccounted for.
 * Padding accounts for 0 bytes (0.0%)
-* 83 have source paths. Accounts for 926 bytes (0.0%).
+* 90 have source paths. Accounts for 1806 bytes (0.0%).
 * 0 have a component assigned. Accounts for 0 bytes (0.0%).
 * 1 placeholders exist (symbols that start with **). Accounts for 4616803 bytes (55.2%).
 * 0 symbols have shared ownership.
-Section .dex.method: has 100.0% of 23605 bytes accounted for from 100 symbols. 0 bytes are unaccounted for.
+Section .dex.method: has 100.0% of 23745 bytes accounted for from 104 symbols. 0 bytes are unaccounted for.
 * Padding accounts for 0 bytes (0.0%)
-* 90 have source paths. Accounts for 21154 bytes (89.6%).
+* 94 have source paths. Accounts for 21294 bytes (89.7%).
 * 0 have a component assigned. Accounts for 0 bytes (0.0%).
 * 0 symbols have shared ownership.
 Section .pak.translations: has 100.0% of 6821 bytes accounted for from 208 symbols. 0 bytes are unaccounted for.
@@ -180,7 +180,14 @@
 .dex@0(size_without_padding=10,padding=0,full_name=org.chromium.chrome.browser.compositor.layouts.phone.stack.StackAnimation#mWidth: float,object_path=org.chromium.chrome.browser.compositor.layouts.phone.stack.StackAnimation,source_path=chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/stack/StackAnimation.java,flags={},num_aliases=1,component=)
 .dex@0(size_without_padding=40,padding=0,full_name=org.chromium.chrome.browser.compositor.layouts.phone.stack.StackAnimation,object_path=org.chromium.chrome.browser.compositor.layouts.phone.stack.StackAnimation,source_path=chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/stack/StackAnimation.java,flags={},num_aliases=1,component=)
 .dex@0(size_without_padding=104,padding=0,full_name=org.chromium.chrome.browser.compositor.layouts.phone.stack.StackAnimationLandscape,object_path=$APK/org/chromium/chrome/browser/compositor/layouts/phone/stack/StackAnimationLandscape,source_path=,flags={},num_aliases=1,component=)
-.dex@0(size_without_padding=8586,padding=0,full_name=org.chromium.chrome.browser.compositor.layouts.phone.stack,object_path=$APK/org/chromium/chrome/browser/compositor/layouts/phone/stack,source_path=,flags={},num_aliases=1,component=)
+.dex@0(size_without_padding=10,padding=0,full_name=org.chromium.chrome.browser.compositor.layouts.phone.stack.StackAnimation$Nested1$$Lambda$0#$instance: Object,object_path=org.chromium.chrome.browser.compositor.layouts.phone.stack.-$$Lambda$StackAnimation$Nested1$kjevdDQ8V2zqCrdieLqWLHzk.dex,source_path=chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/stack/StackAnimation.java,flags={},num_aliases=1,component=)
+.dex@0(size_without_padding=210,padding=0,full_name=org.chromium.chrome.browser.compositor.layouts.phone.stack.StackAnimation$Nested1$$Lambda$0,object_path=org.chromium.chrome.browser.compositor.layouts.phone.stack.-$$Lambda$StackAnimation$Nested1$kjevdDQ8V2zqCrdieLqWLHzk.dex,source_path=chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/stack/StackAnimation.java,flags={},num_aliases=1,component=)
+.dex@0(size_without_padding=220,padding=0,full_name=org.chromium.chrome.browser.compositor.layouts.phone.stack.StackAnimation$Nested1$$Lambda$1,object_path=org.chromium.chrome.browser.compositor.layouts.phone.stack.-$$Lambda$StackAnimation$Nested1$kjevdDQ8V2zqCrdieLqWLHzkB.dex,source_path=chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/stack/StackAnimation.java,flags={},num_aliases=1,component=)
+.dex@0(size_without_padding=10,padding=0,full_name=org.chromium.chrome.browser.compositor.layouts.phone.stack.StackAnimation$Nested2$$Lambda$0#$instance: Object,object_path=org.chromium.chrome.browser.compositor.layouts.phone.stack.-$$Lambda$StackAnimation$Nested2$kjevdDQ8V2zqCrdieLqWLHzk.dex,source_path=chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/stack/StackAnimation.java,flags={},num_aliases=1,component=)
+.dex@0(size_without_padding=210,padding=0,full_name=org.chromium.chrome.browser.compositor.layouts.phone.stack.StackAnimation$Nested2$$Lambda$0,object_path=org.chromium.chrome.browser.compositor.layouts.phone.stack.-$$Lambda$StackAnimation$Nested2$kjevdDQ8V2zqCrdieLqWLHzk.dex,source_path=chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/stack/StackAnimation.java,flags={},num_aliases=1,component=)
+.dex@0(size_without_padding=10,padding=0,full_name=org.chromium.chrome.browser.compositor.layouts.phone.stack.StackAnimation$$Lambda$0#$instance: Object,object_path=org.chromium.chrome.browser.compositor.layouts.phone.stack.-$$Lambda$StackAnimation$kjevdDQ8V2zqCrdieLqWLHzk.dex,source_path=chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/stack/StackAnimation.java,flags={},num_aliases=1,component=)
+.dex@0(size_without_padding=210,padding=0,full_name=org.chromium.chrome.browser.compositor.layouts.phone.stack.StackAnimation$$Lambda$0,object_path=org.chromium.chrome.browser.compositor.layouts.phone.stack.-$$Lambda$StackAnimation$kjevdDQ8V2zqCrdieLqWLHzk.dex,source_path=chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/stack/StackAnimation.java,flags={},num_aliases=1,component=)
+.dex@0(size_without_padding=7566,padding=0,full_name=org.chromium.chrome.browser.compositor.layouts.phone.stack,object_path=$APK/org/chromium/chrome/browser/compositor/layouts/phone/stack,source_path=,flags={},num_aliases=1,component=)
 .dex@0(size_without_padding=23896,padding=0,full_name=org.chromium.chrome.browser.compositor.layouts.phone,object_path=$APK/org/chromium/chrome/browser/compositor/layouts/phone,source_path=,flags={},num_aliases=1,component=)
 .dex@0(size_without_padding=58449,padding=0,full_name=org.chromium.chrome.browser.compositor.layouts,object_path=$APK/org/chromium/chrome/browser/compositor/layouts,source_path=,flags={},num_aliases=1,component=)
 .dex@0(size_without_padding=111986,padding=0,full_name=org.chromium.chrome.browser.compositor,object_path=$APK/org/chromium/chrome/browser/compositor,source_path=,flags={},num_aliases=1,component=)
@@ -289,6 +296,10 @@
 .dex.method@0(size_without_padding=79,padding=0,full_name=org.chromium.chrome.browser.compositor.layouts.phone.stack.StackAnimationLandscape#getScreenPositionInScrollDirection(org.chromium.chrome.browser.compositor.layouts.phone.stack.StackTab): float,object_path=$APK/org/chromium/chrome/browser/compositor/layouts/phone/stack/StackAnimationLandscape,source_path=,flags={},num_aliases=1,component=)
 .dex.method@0(size_without_padding=54,padding=0,full_name=org.chromium.chrome.browser.compositor.layouts.phone.stack.StackAnimationLandscape#getScreenSizeInScrollDirection(): float,object_path=$APK/org/chromium/chrome/browser/compositor/layouts/phone/stack/StackAnimationLandscape,source_path=,flags={},num_aliases=1,component=)
 .dex.method@0(size_without_padding=52,padding=0,full_name=org.chromium.chrome.browser.compositor.layouts.phone.stack.StackAnimationLandscape#isDefaultDiscardDirectionPositive(): boolean,object_path=$APK/org/chromium/chrome/browser/compositor/layouts/phone/stack/StackAnimationLandscape,source_path=,flags={},num_aliases=1,component=)
+.dex.method@0(size_without_padding=35,padding=0,full_name=org.chromium.chrome.browser.compositor.layouts.phone.stack.StackAnimation$Nested1$$Lambda$0#<init>(),object_path=org.chromium.chrome.browser.compositor.layouts.phone.stack.-$$Lambda$StackAnimation$Nested1$kjevdDQ8V2zqCrdieLqWLHzk.dex,source_path=chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/stack/StackAnimation.java,flags={},num_aliases=1,component=)
+.dex.method@0(size_without_padding=35,padding=0,full_name=org.chromium.chrome.browser.compositor.layouts.phone.stack.StackAnimation$Nested1$$Lambda$1#<init>(),object_path=org.chromium.chrome.browser.compositor.layouts.phone.stack.-$$Lambda$StackAnimation$Nested1$kjevdDQ8V2zqCrdieLqWLHzkB.dex,source_path=chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/stack/StackAnimation.java,flags={},num_aliases=1,component=)
+.dex.method@0(size_without_padding=35,padding=0,full_name=org.chromium.chrome.browser.compositor.layouts.phone.stack.StackAnimation$Nested2$$Lambda$0#<init>(),object_path=org.chromium.chrome.browser.compositor.layouts.phone.stack.-$$Lambda$StackAnimation$Nested2$kjevdDQ8V2zqCrdieLqWLHzk.dex,source_path=chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/stack/StackAnimation.java,flags={},num_aliases=1,component=)
+.dex.method@0(size_without_padding=35,padding=0,full_name=org.chromium.chrome.browser.compositor.layouts.phone.stack.StackAnimation$$Lambda$0#<init>(),object_path=org.chromium.chrome.browser.compositor.layouts.phone.stack.-$$Lambda$StackAnimation$kjevdDQ8V2zqCrdieLqWLHzk.dex,source_path=chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/stack/StackAnimation.java,flags={},num_aliases=1,component=)
 .other@0(size_without_padding=60,padding=0,full_name=** ELF Section: .ARM.attributes,object_path=,source_path=,flags={},num_aliases=1,component=)
 .other@0(size_without_padding=28,padding=0,full_name=** ELF Section: .note.gnu.gold-version,object_path=,source_path=,flags={},num_aliases=1,component=)
 .other@0(size_without_padding=436,padding=0,full_name=** ELF Section: .shstrtab,object_path=,source_path=,flags={},num_aliases=1,component=)
diff --git a/tools/binary_size/libsupersize/testdata/Archive_MinimalApks.golden b/tools/binary_size/libsupersize/testdata/Archive_MinimalApks.golden
index 76239933..c77aa61 100644
--- a/tools/binary_size/libsupersize/testdata/Archive_MinimalApks.golden
+++ b/tools/binary_size/libsupersize/testdata/Archive_MinimalApks.golden
@@ -52,15 +52,15 @@
 * 6 have a component assigned. Accounts for 524520 bytes (100.0%).
 * 0 symbols have shared ownership.
 * 3 symbols are from generated sources. Accounts for 232 bytes (0.0%).
-Section .dex: has 100.0% of 8365003 bytes accounted for from 93 symbols. 0 bytes are unaccounted for.
+Section .dex: has 100.0% of 8364863 bytes accounted for from 100 symbols. 0 bytes are unaccounted for.
 * Padding accounts for 0 bytes (0.0%)
-* 83 have source paths. Accounts for 926 bytes (0.0%).
+* 90 have source paths. Accounts for 1806 bytes (0.0%).
 * 0 have a component assigned. Accounts for 0 bytes (0.0%).
 * 1 placeholders exist (symbols that start with **). Accounts for 4616803 bytes (55.2%).
 * 0 symbols have shared ownership.
-Section .dex.method: has 100.0% of 23605 bytes accounted for from 100 symbols. 0 bytes are unaccounted for.
+Section .dex.method: has 100.0% of 23745 bytes accounted for from 104 symbols. 0 bytes are unaccounted for.
 * Padding accounts for 0 bytes (0.0%)
-* 90 have source paths. Accounts for 21154 bytes (89.6%).
+* 94 have source paths. Accounts for 21294 bytes (89.7%).
 * 0 have a component assigned. Accounts for 0 bytes (0.0%).
 * 0 symbols have shared ownership.
 Section .pak.translations: has 100.0% of 6821 bytes accounted for from 208 symbols. 0 bytes are unaccounted for.
@@ -181,7 +181,14 @@
 .dex@0(size_without_padding=10,padding=0,full_name=org.chromium.chrome.browser.compositor.layouts.phone.stack.StackAnimation#mWidth: float,object_path=org.chromium.chrome.browser.compositor.layouts.phone.stack.StackAnimation,source_path=chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/stack/StackAnimation.java,flags={},num_aliases=1,component=)
 .dex@0(size_without_padding=40,padding=0,full_name=org.chromium.chrome.browser.compositor.layouts.phone.stack.StackAnimation,object_path=org.chromium.chrome.browser.compositor.layouts.phone.stack.StackAnimation,source_path=chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/stack/StackAnimation.java,flags={},num_aliases=1,component=)
 .dex@0(size_without_padding=104,padding=0,full_name=org.chromium.chrome.browser.compositor.layouts.phone.stack.StackAnimationLandscape,object_path=$APK/org/chromium/chrome/browser/compositor/layouts/phone/stack/StackAnimationLandscape,source_path=,flags={},num_aliases=1,component=)
-.dex@0(size_without_padding=8586,padding=0,full_name=org.chromium.chrome.browser.compositor.layouts.phone.stack,object_path=$APK/org/chromium/chrome/browser/compositor/layouts/phone/stack,source_path=,flags={},num_aliases=1,component=)
+.dex@0(size_without_padding=10,padding=0,full_name=org.chromium.chrome.browser.compositor.layouts.phone.stack.StackAnimation$Nested1$$Lambda$0#$instance: Object,object_path=org.chromium.chrome.browser.compositor.layouts.phone.stack.-$$Lambda$StackAnimation$Nested1$kjevdDQ8V2zqCrdieLqWLHzk.dex,source_path=chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/stack/StackAnimation.java,flags={},num_aliases=1,component=)
+.dex@0(size_without_padding=210,padding=0,full_name=org.chromium.chrome.browser.compositor.layouts.phone.stack.StackAnimation$Nested1$$Lambda$0,object_path=org.chromium.chrome.browser.compositor.layouts.phone.stack.-$$Lambda$StackAnimation$Nested1$kjevdDQ8V2zqCrdieLqWLHzk.dex,source_path=chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/stack/StackAnimation.java,flags={},num_aliases=1,component=)
+.dex@0(size_without_padding=220,padding=0,full_name=org.chromium.chrome.browser.compositor.layouts.phone.stack.StackAnimation$Nested1$$Lambda$1,object_path=org.chromium.chrome.browser.compositor.layouts.phone.stack.-$$Lambda$StackAnimation$Nested1$kjevdDQ8V2zqCrdieLqWLHzkB.dex,source_path=chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/stack/StackAnimation.java,flags={},num_aliases=1,component=)
+.dex@0(size_without_padding=10,padding=0,full_name=org.chromium.chrome.browser.compositor.layouts.phone.stack.StackAnimation$Nested2$$Lambda$0#$instance: Object,object_path=org.chromium.chrome.browser.compositor.layouts.phone.stack.-$$Lambda$StackAnimation$Nested2$kjevdDQ8V2zqCrdieLqWLHzk.dex,source_path=chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/stack/StackAnimation.java,flags={},num_aliases=1,component=)
+.dex@0(size_without_padding=210,padding=0,full_name=org.chromium.chrome.browser.compositor.layouts.phone.stack.StackAnimation$Nested2$$Lambda$0,object_path=org.chromium.chrome.browser.compositor.layouts.phone.stack.-$$Lambda$StackAnimation$Nested2$kjevdDQ8V2zqCrdieLqWLHzk.dex,source_path=chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/stack/StackAnimation.java,flags={},num_aliases=1,component=)
+.dex@0(size_without_padding=10,padding=0,full_name=org.chromium.chrome.browser.compositor.layouts.phone.stack.StackAnimation$$Lambda$0#$instance: Object,object_path=org.chromium.chrome.browser.compositor.layouts.phone.stack.-$$Lambda$StackAnimation$kjevdDQ8V2zqCrdieLqWLHzk.dex,source_path=chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/stack/StackAnimation.java,flags={},num_aliases=1,component=)
+.dex@0(size_without_padding=210,padding=0,full_name=org.chromium.chrome.browser.compositor.layouts.phone.stack.StackAnimation$$Lambda$0,object_path=org.chromium.chrome.browser.compositor.layouts.phone.stack.-$$Lambda$StackAnimation$kjevdDQ8V2zqCrdieLqWLHzk.dex,source_path=chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/stack/StackAnimation.java,flags={},num_aliases=1,component=)
+.dex@0(size_without_padding=7566,padding=0,full_name=org.chromium.chrome.browser.compositor.layouts.phone.stack,object_path=$APK/org/chromium/chrome/browser/compositor/layouts/phone/stack,source_path=,flags={},num_aliases=1,component=)
 .dex@0(size_without_padding=23896,padding=0,full_name=org.chromium.chrome.browser.compositor.layouts.phone,object_path=$APK/org/chromium/chrome/browser/compositor/layouts/phone,source_path=,flags={},num_aliases=1,component=)
 .dex@0(size_without_padding=58449,padding=0,full_name=org.chromium.chrome.browser.compositor.layouts,object_path=$APK/org/chromium/chrome/browser/compositor/layouts,source_path=,flags={},num_aliases=1,component=)
 .dex@0(size_without_padding=111986,padding=0,full_name=org.chromium.chrome.browser.compositor,object_path=$APK/org/chromium/chrome/browser/compositor,source_path=,flags={},num_aliases=1,component=)
@@ -290,6 +297,10 @@
 .dex.method@0(size_without_padding=79,padding=0,full_name=org.chromium.chrome.browser.compositor.layouts.phone.stack.StackAnimationLandscape#getScreenPositionInScrollDirection(org.chromium.chrome.browser.compositor.layouts.phone.stack.StackTab): float,object_path=$APK/org/chromium/chrome/browser/compositor/layouts/phone/stack/StackAnimationLandscape,source_path=,flags={},num_aliases=1,component=)
 .dex.method@0(size_without_padding=54,padding=0,full_name=org.chromium.chrome.browser.compositor.layouts.phone.stack.StackAnimationLandscape#getScreenSizeInScrollDirection(): float,object_path=$APK/org/chromium/chrome/browser/compositor/layouts/phone/stack/StackAnimationLandscape,source_path=,flags={},num_aliases=1,component=)
 .dex.method@0(size_without_padding=52,padding=0,full_name=org.chromium.chrome.browser.compositor.layouts.phone.stack.StackAnimationLandscape#isDefaultDiscardDirectionPositive(): boolean,object_path=$APK/org/chromium/chrome/browser/compositor/layouts/phone/stack/StackAnimationLandscape,source_path=,flags={},num_aliases=1,component=)
+.dex.method@0(size_without_padding=35,padding=0,full_name=org.chromium.chrome.browser.compositor.layouts.phone.stack.StackAnimation$Nested1$$Lambda$0#<init>(),object_path=org.chromium.chrome.browser.compositor.layouts.phone.stack.-$$Lambda$StackAnimation$Nested1$kjevdDQ8V2zqCrdieLqWLHzk.dex,source_path=chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/stack/StackAnimation.java,flags={},num_aliases=1,component=)
+.dex.method@0(size_without_padding=35,padding=0,full_name=org.chromium.chrome.browser.compositor.layouts.phone.stack.StackAnimation$Nested1$$Lambda$1#<init>(),object_path=org.chromium.chrome.browser.compositor.layouts.phone.stack.-$$Lambda$StackAnimation$Nested1$kjevdDQ8V2zqCrdieLqWLHzkB.dex,source_path=chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/stack/StackAnimation.java,flags={},num_aliases=1,component=)
+.dex.method@0(size_without_padding=35,padding=0,full_name=org.chromium.chrome.browser.compositor.layouts.phone.stack.StackAnimation$Nested2$$Lambda$0#<init>(),object_path=org.chromium.chrome.browser.compositor.layouts.phone.stack.-$$Lambda$StackAnimation$Nested2$kjevdDQ8V2zqCrdieLqWLHzk.dex,source_path=chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/stack/StackAnimation.java,flags={},num_aliases=1,component=)
+.dex.method@0(size_without_padding=35,padding=0,full_name=org.chromium.chrome.browser.compositor.layouts.phone.stack.StackAnimation$$Lambda$0#<init>(),object_path=org.chromium.chrome.browser.compositor.layouts.phone.stack.-$$Lambda$StackAnimation$kjevdDQ8V2zqCrdieLqWLHzk.dex,source_path=chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/stack/StackAnimation.java,flags={},num_aliases=1,component=)
 .other@0(size_without_padding=60,padding=0,full_name=** ELF Section: .ARM.attributes,object_path=,source_path=,flags={},num_aliases=1,component=)
 .other@0(size_without_padding=28,padding=0,full_name=** ELF Section: .note.gnu.gold-version,object_path=,source_path=,flags={},num_aliases=1,component=)
 .other@0(size_without_padding=436,padding=0,full_name=** ELF Section: .shstrtab,object_path=,source_path=,flags={},num_aliases=1,component=)
diff --git a/tools/binary_size/libsupersize/testdata/mock_sdk/tools/bin/apkanalyzer.output b/tools/binary_size/libsupersize/testdata/mock_sdk/tools/bin/apkanalyzer.output
index 6197c60c..822a9b4 100644
--- a/tools/binary_size/libsupersize/testdata/mock_sdk/tools/bin/apkanalyzer.output
+++ b/tools/binary_size/libsupersize/testdata/mock_sdk/tools/bin/apkanalyzer.output
@@ -191,3 +191,14 @@
 M d 1	1	79	org.chromium.chrome.browser.compositor.layouts.phone.stack.StackAnimationLandscape float getScreenPositionInScrollDirection(org.chromium.chrome.browser.compositor.layouts.phone.stack.StackTab)
 M d 1	1	54	org.chromium.chrome.browser.compositor.layouts.phone.stack.StackAnimationLandscape float getScreenSizeInScrollDirection()
 M d 1	1	52	org.chromium.chrome.browser.compositor.layouts.phone.stack.StackAnimationLandscape boolean isDefaultDiscardDirectionPositive()
+C d 10	12	255	org.chromium.chrome.browser.compositor.layouts.phone.stack.-$$Lambda$StackAnimation$Nested1$kjevdDQ8V2zqCrdieLqWLHzk.dex
+M d 1	1	35	org.chromium.chrome.browser.compositor.layouts.phone.stack.-$$Lambda$StackAnimation$Nested1$kjevdDQ8V2zqCrdieLqWLHzk.dex <init>()
+F d 0	0	10	org.chromium.chrome.browser.compositor.layouts.phone.stack.-$$Lambda$StackAnimation$Nested1$kjevdDQ8V2zqCrdieLqWLHzk.dex Object $instance
+C d 10	12	255	org.chromium.chrome.browser.compositor.layouts.phone.stack.-$$Lambda$StackAnimation$Nested1$kjevdDQ8V2zqCrdieLqWLHzkB.dex
+M d 1	1	35	org.chromium.chrome.browser.compositor.layouts.phone.stack.-$$Lambda$StackAnimation$Nested1$kjevdDQ8V2zqCrdieLqWLHzkB.dex <init>()
+C d 10	12	255	org.chromium.chrome.browser.compositor.layouts.phone.stack.-$$Lambda$StackAnimation$Nested2$kjevdDQ8V2zqCrdieLqWLHzk.dex
+M d 1	1	35	org.chromium.chrome.browser.compositor.layouts.phone.stack.-$$Lambda$StackAnimation$Nested2$kjevdDQ8V2zqCrdieLqWLHzk.dex <init>()
+F d 0	0	10	org.chromium.chrome.browser.compositor.layouts.phone.stack.-$$Lambda$StackAnimation$Nested2$kjevdDQ8V2zqCrdieLqWLHzk.dex Object $instance
+C d 10	12	255	org.chromium.chrome.browser.compositor.layouts.phone.stack.-$$Lambda$StackAnimation$kjevdDQ8V2zqCrdieLqWLHzk.dex
+M d 1	1	35	org.chromium.chrome.browser.compositor.layouts.phone.stack.-$$Lambda$StackAnimation$kjevdDQ8V2zqCrdieLqWLHzk.dex <init>()
+F d 0	0	10	org.chromium.chrome.browser.compositor.layouts.phone.stack.-$$Lambda$StackAnimation$kjevdDQ8V2zqCrdieLqWLHzk.dex Object $instance
diff --git a/tools/clang/scripts/build.py b/tools/clang/scripts/build.py
index f7e984d8..293558ade 100755
--- a/tools/clang/scripts/build.py
+++ b/tools/clang/scripts/build.py
@@ -473,6 +473,13 @@
       '-DLLVM_INCLUDE_GO_TESTS=OFF',
   ]
 
+  if sys.platform == 'darwin':
+    # For libc++, we only want the headers.
+    base_cmake_args.extend([
+        '-DLIBCXX_ENABLE_SHARED=OFF', '-DLIBCXX_ENABLE_STATIC=OFF',
+        '-DLIBCXX_INCLUDE_TESTS=OFF'
+    ])
+
   if args.gcc_toolchain:
     # Don't use the custom gcc toolchain when building compiler-rt tests; those
     # tests are built with the just-built Clang, and target both i386 and x86_64
@@ -489,12 +496,6 @@
     cxxflags.append('-I' + zlib_dir)
     ldflags.append('-LIBPATH:' + zlib_dir)
 
-  if sys.platform == 'darwin':
-    # Use the system libc++abi.
-    # TODO(hans): use https://reviews.llvm.org/D62060 instead
-    base_cmake_args.append('-DLIBCXX_CXX_ABI=libcxxabi')
-    base_cmake_args.append('-DLIBCXX_CXX_ABI_SYSTEM=1')
-
   if sys.platform != 'win32':
     # libxml2 is required by the Win manifest merging tool used in cross-builds.
     base_cmake_args.append('-DLLVM_ENABLE_LIBXML2=FORCE_ON')
diff --git a/tools/licenses.py b/tools/licenses.py
index b61ee97..feeaf77 100755
--- a/tools/licenses.py
+++ b/tools/licenses.py
@@ -365,173 +365,175 @@
 
 
 class LicenseError(Exception):
-    """We raise this exception when a directory's licensing info isn't
+  """We raise this exception when a directory's licensing info isn't
     fully filled out."""
-    pass
+  pass
+
 
 def AbsolutePath(path, filename, root):
-    """Convert a path in README.chromium to be absolute based on the source
+  """Convert a path in README.chromium to be absolute based on the source
     root."""
-    if filename.startswith('/'):
-        # Absolute-looking paths are relative to the source root
-        # (which is the directory we're run from).
-        absolute_path = os.path.join(root, filename[1:])
-    else:
-        absolute_path = os.path.join(root, path, filename)
-    if os.path.exists(absolute_path):
-        return absolute_path
-    return None
+  if filename.startswith('/'):
+    # Absolute-looking paths are relative to the source root
+    # (which is the directory we're run from).
+    absolute_path = os.path.join(root, filename[1:])
+  else:
+    absolute_path = os.path.join(root, path, filename)
+  if os.path.exists(absolute_path):
+    return absolute_path
+  return None
+
 
 def ParseDir(path, root, require_license_file=True, optional_keys=None):
-    """Examine a third_party/foo component and extract its metadata."""
-    # Parse metadata fields out of README.chromium.
-    # We examine "LICENSE" for the license file by default.
-    metadata = {
-        "License File": "LICENSE",  # Relative path to license text.
-        "Name": None,               # Short name (for header on about:credits).
-        "URL": None,                # Project home page.
-        "License": None,            # Software license.
-        }
+  """Examine a third_party/foo component and extract its metadata."""
+  # Parse metadata fields out of README.chromium.
+  # We examine "LICENSE" for the license file by default.
+  metadata = {
+      "License File": "LICENSE",  # Relative path to license text.
+      "Name": None,  # Short name (for header on about:credits).
+      "URL": None,  # Project home page.
+      "License": None,  # Software license.
+  }
 
-    if optional_keys is None:
-        optional_keys = []
+  if optional_keys is None:
+    optional_keys = []
 
-    if path in SPECIAL_CASES:
-        metadata.update(SPECIAL_CASES[path])
-    else:
-        # Try to find README.chromium.
-        readme_path = os.path.join(root, path, 'README.chromium')
-        if not os.path.exists(readme_path):
-            raise LicenseError("missing README.chromium or licenses.py "
-                               "SPECIAL_CASES entry in %s\n" % path)
+  if path in SPECIAL_CASES:
+    metadata.update(SPECIAL_CASES[path])
+  else:
+    # Try to find README.chromium.
+    readme_path = os.path.join(root, path, 'README.chromium')
+    if not os.path.exists(readme_path):
+      raise LicenseError("missing README.chromium or licenses.py "
+                         "SPECIAL_CASES entry in %s\n" % path)
 
-        for line in open(readme_path):
-            line = line.strip()
-            if not line:
-                break
-            for key in list(metadata.keys()) + optional_keys:
-                field = key + ": "
-                if line.startswith(field):
-                    metadata[key] = line[len(field):]
+    for line in open(readme_path):
+      line = line.strip()
+      if not line:
+        break
+      for key in list(metadata.keys()) + optional_keys:
+        field = key + ": "
+        if line.startswith(field):
+          metadata[key] = line[len(field):]
 
-    # Check that all expected metadata is present.
-    errors = []
-    for key, value in metadata.items():
-        if not value:
-            errors.append("couldn't find '" + key + "' line "
-                          "in README.chromium or licences.py "
-                          "SPECIAL_CASES")
+  # Check that all expected metadata is present.
+  errors = []
+  for key, value in metadata.items():
+    if not value:
+      errors.append("couldn't find '" + key + "' line "
+                    "in README.chromium or licences.py "
+                    "SPECIAL_CASES")
 
-    # Special-case modules that aren't in the shipping product, so don't need
-    # their license in about:credits.
-    if metadata["License File"] != NOT_SHIPPED:
-        # Check that the license file exists.
-        for filename in (metadata["License File"], "COPYING"):
-            license_path = AbsolutePath(path, filename, root)
-            if license_path is not None:
-                break
+  # Special-case modules that aren't in the shipping product, so don't need
+  # their license in about:credits.
+  if metadata["License File"] != NOT_SHIPPED:
+    # Check that the license file exists.
+    for filename in (metadata["License File"], "COPYING"):
+      license_path = AbsolutePath(path, filename, root)
+      if license_path is not None:
+        break
 
-        if require_license_file and not license_path:
-            errors.append("License file not found. "
-                          "Either add a file named LICENSE, "
-                          "import upstream's COPYING if available, "
-                          "or add a 'License File:' line to "
-                          "README.chromium with the appropriate path.")
-        metadata["License File"] = license_path
+    if require_license_file and not license_path:
+      errors.append("License file not found. "
+                    "Either add a file named LICENSE, "
+                    "import upstream's COPYING if available, "
+                    "or add a 'License File:' line to "
+                    "README.chromium with the appropriate path.")
+    metadata["License File"] = license_path
 
-    if errors:
-        raise LicenseError("Errors in %s:\n %s\n" % (path, ";\n ".join(errors)))
-    return metadata
+  if errors:
+    raise LicenseError("Errors in %s:\n %s\n" % (path, ";\n ".join(errors)))
+  return metadata
 
 
 def ContainsFiles(path, root):
-    """Determines whether any files exist in a directory or in any of its
+  """Determines whether any files exist in a directory or in any of its
     subdirectories."""
-    for _, dirs, files in os.walk(os.path.join(root, path)):
-        if files:
-            return True
-        for vcs_metadata in VCS_METADATA_DIRS:
-            if vcs_metadata in dirs:
-                dirs.remove(vcs_metadata)
-    return False
+  for _, dirs, files in os.walk(os.path.join(root, path)):
+    if files:
+      return True
+    for vcs_metadata in VCS_METADATA_DIRS:
+      if vcs_metadata in dirs:
+        dirs.remove(vcs_metadata)
+  return False
 
 
 def FilterDirsWithFiles(dirs_list, root):
-    # If a directory contains no files, assume it's a DEPS directory for a
-    # project not used by our current configuration and skip it.
-    return [x for x in dirs_list if ContainsFiles(x, root)]
+  # If a directory contains no files, assume it's a DEPS directory for a
+  # project not used by our current configuration and skip it.
+  return [x for x in dirs_list if ContainsFiles(x, root)]
 
 
 def FindThirdPartyDirs(prune_paths, root):
-    """Find all third_party directories underneath the source root."""
-    third_party_dirs = set()
-    for path, dirs, files in os.walk(root):
-        path = path[len(root)+1:]  # Pretty up the path.
+  """Find all third_party directories underneath the source root."""
+  third_party_dirs = set()
+  for path, dirs, files in os.walk(root):
+    path = path[len(root) + 1:]  # Pretty up the path.
 
-        if path in prune_paths:
-            dirs[:] = []
-            continue
+    if path in prune_paths:
+      dirs[:] = []
+      continue
 
-        # Prune out directories we want to skip.
-        # (Note that we loop over PRUNE_DIRS so we're not iterating over a
-        # list that we're simultaneously mutating.)
-        for skip in PRUNE_DIRS:
-            if skip in dirs:
-                dirs.remove(skip)
+    # Prune out directories we want to skip.
+    # (Note that we loop over PRUNE_DIRS so we're not iterating over a
+    # list that we're simultaneously mutating.)
+    for skip in PRUNE_DIRS:
+      if skip in dirs:
+        dirs.remove(skip)
 
-        if os.path.basename(path) == 'third_party':
-            # Add all subdirectories that are not marked for skipping.
-            for dir in dirs:
-                dirpath = os.path.join(path, dir)
-                additional_paths_file = os.path.join(
-                    root, dirpath, ADDITIONAL_PATHS_FILENAME)
-                if dirpath not in prune_paths:
-                    third_party_dirs.add(dirpath)
-                if os.path.exists(additional_paths_file):
-                    with open(additional_paths_file) as paths_file:
-                        extra_paths = json.load(paths_file)
-                        third_party_dirs.update([
-                                os.path.join(dirpath, p) for p in extra_paths])
+    if os.path.basename(path) == 'third_party':
+      # Add all subdirectories that are not marked for skipping.
+      for dir in dirs:
+        dirpath = os.path.join(path, dir)
+        additional_paths_file = os.path.join(root, dirpath,
+                                             ADDITIONAL_PATHS_FILENAME)
+        if dirpath not in prune_paths:
+          third_party_dirs.add(dirpath)
+        if os.path.exists(additional_paths_file):
+          with open(additional_paths_file) as paths_file:
+            extra_paths = json.load(paths_file)
+            third_party_dirs.update(
+                [os.path.join(dirpath, p) for p in extra_paths])
 
-            # Don't recurse into any subdirs from here.
-            dirs[:] = []
-            continue
+      # Don't recurse into any subdirs from here.
+      dirs[:] = []
+      continue
 
-        # Don't recurse into paths in ADDITIONAL_PATHS, like we do with regular
-        # third_party/foo paths.
-        if path in ADDITIONAL_PATHS:
-            dirs[:] = []
+    # Don't recurse into paths in ADDITIONAL_PATHS, like we do with regular
+    # third_party/foo paths.
+    if path in ADDITIONAL_PATHS:
+      dirs[:] = []
 
-    for dir in ADDITIONAL_PATHS:
-        if dir not in prune_paths:
-            third_party_dirs.add(dir)
+  for dir in ADDITIONAL_PATHS:
+    if dir not in prune_paths:
+      third_party_dirs.add(dir)
 
-    return third_party_dirs
+  return third_party_dirs
 
 
 def FindThirdPartyDirsWithFiles(root):
-    third_party_dirs = FindThirdPartyDirs(PRUNE_PATHS, root)
-    return FilterDirsWithFiles(third_party_dirs, root)
+  third_party_dirs = FindThirdPartyDirs(PRUNE_PATHS, root)
+  return FilterDirsWithFiles(third_party_dirs, root)
 
 
 # Many builders do not contain 'gn' in their PATH, so use the GN binary from
 # //buildtools.
 def _GnBinary():
-    exe = 'gn'
-    if sys.platform.startswith('linux'):
-        subdir = 'linux64'
-    elif sys.platform == 'darwin':
-        subdir = 'mac'
-    elif sys.platform == 'win32':
-        subdir, exe = 'win', 'gn.exe'
-    else:
-        raise RuntimeError("Unsupported platform '%s'." % sys.platform)
+  exe = 'gn'
+  if sys.platform.startswith('linux'):
+    subdir = 'linux64'
+  elif sys.platform == 'darwin':
+    subdir = 'mac'
+  elif sys.platform == 'win32':
+    subdir, exe = 'win', 'gn.exe'
+  else:
+    raise RuntimeError("Unsupported platform '%s'." % sys.platform)
 
-    return os.path.join(_REPOSITORY_ROOT, 'buildtools', subdir, exe)
+  return os.path.join(_REPOSITORY_ROOT, 'buildtools', subdir, exe)
 
 
 def GetThirdPartyDepsFromGNDepsOutput(gn_deps, target_os):
-    """Returns third_party/foo directories given the output of "gn desc deps".
+  """Returns third_party/foo directories given the output of "gn desc deps".
 
     Note that it always returns the direct sub-directory of third_party
     where README.chromium and LICENSE files are, so that it can be passed to
@@ -540,269 +542,265 @@
 
     It returns relative paths from _REPOSITORY_ROOT, not absolute paths.
     """
-    third_party_deps = set()
-    for absolute_build_dep in gn_deps.split():
-        relative_build_dep = os.path.relpath(
-            absolute_build_dep, _REPOSITORY_ROOT)
-        m = re.search(
-            r'^((.+[/\\])?third_party[/\\][^/\\]+[/\\])(.+[/\\])?BUILD\.gn$',
-            relative_build_dep)
-        if not m:
-            continue
-        third_party_path = m.group(1)
-        if any(third_party_path.startswith(p + os.sep) for p in PRUNE_PATHS):
-            continue
-        if (target_os == 'ios' and
-            any(third_party_path.startswith(p + os.sep)
-                for p in KNOWN_NON_IOS_LIBRARIES)):
-            # Skip over files that are known not to be used on iOS.
-            continue
-        third_party_deps.add(third_party_path[:-1])
-    return third_party_deps
+  third_party_deps = set()
+  for absolute_build_dep in gn_deps.split():
+    relative_build_dep = os.path.relpath(absolute_build_dep, _REPOSITORY_ROOT)
+    m = re.search(
+        r'^((.+[/\\])?third_party[/\\][^/\\]+[/\\])(.+[/\\])?BUILD\.gn$',
+        relative_build_dep)
+    if not m:
+      continue
+    third_party_path = m.group(1)
+    if any(third_party_path.startswith(p + os.sep) for p in PRUNE_PATHS):
+      continue
+    if (target_os == 'ios' and any(
+        third_party_path.startswith(p + os.sep)
+        for p in KNOWN_NON_IOS_LIBRARIES)):
+      # Skip over files that are known not to be used on iOS.
+      continue
+    third_party_deps.add(third_party_path[:-1])
+  return third_party_deps
 
 
 def FindThirdPartyDeps(gn_out_dir, gn_target, target_os):
-    if not gn_out_dir:
-        raise RuntimeError("--gn-out-dir is required if --gn-target is used.")
+  if not gn_out_dir:
+    raise RuntimeError("--gn-out-dir is required if --gn-target is used.")
 
-    # Generate gn project in temp directory and use it to find dependencies.
-    # Current gn directory cannot be used when we run this script in a gn action
-    # rule, because gn doesn't allow recursive invocations due to potential side
-    # effects.
-    tmp_dir = None
-    try:
-        tmp_dir = tempfile.mkdtemp(dir = gn_out_dir)
-        shutil.copy(os.path.join(gn_out_dir, "args.gn"), tmp_dir)
-        subprocess.check_output([_GnBinary(), "gen", tmp_dir])
-        gn_deps = subprocess.check_output([
-            _GnBinary(), "desc", tmp_dir, gn_target,
-            "deps", "--as=buildfile", "--all"])
-        if isinstance(gn_deps, bytes):
-            gn_deps = gn_deps.decode("utf-8")
-    finally:
-        if tmp_dir and os.path.exists(tmp_dir):
-            shutil.rmtree(tmp_dir)
+  # Generate gn project in temp directory and use it to find dependencies.
+  # Current gn directory cannot be used when we run this script in a gn action
+  # rule, because gn doesn't allow recursive invocations due to potential side
+  # effects.
+  tmp_dir = None
+  try:
+    tmp_dir = tempfile.mkdtemp(dir=gn_out_dir)
+    shutil.copy(os.path.join(gn_out_dir, "args.gn"), tmp_dir)
+    subprocess.check_output([_GnBinary(), "gen", tmp_dir])
+    gn_deps = subprocess.check_output([
+        _GnBinary(), "desc", tmp_dir, gn_target, "deps", "--as=buildfile",
+        "--all"
+    ])
+    if isinstance(gn_deps, bytes):
+      gn_deps = gn_deps.decode("utf-8")
+  finally:
+    if tmp_dir and os.path.exists(tmp_dir):
+      shutil.rmtree(tmp_dir)
 
-    return GetThirdPartyDepsFromGNDepsOutput(gn_deps, target_os)
+  return GetThirdPartyDepsFromGNDepsOutput(gn_deps, target_os)
 
 
 def ScanThirdPartyDirs(root=None):
-    """Scan a list of directories and report on any problems we find."""
-    if root is None:
-      root = os.getcwd()
-    third_party_dirs = FindThirdPartyDirsWithFiles(root)
+  """Scan a list of directories and report on any problems we find."""
+  if root is None:
+    root = os.getcwd()
+  third_party_dirs = FindThirdPartyDirsWithFiles(root)
 
-    errors = []
-    for path in sorted(third_party_dirs):
-        try:
-            metadata = ParseDir(path, root)
-        except LicenseError as e:
-            errors.append((path, e.args[0]))
-            continue
+  errors = []
+  for path in sorted(third_party_dirs):
+    try:
+      metadata = ParseDir(path, root)
+    except LicenseError as e:
+      errors.append((path, e.args[0]))
+      continue
 
-    for path, error in sorted(errors):
-        print(path + ": " + error)
+  for path, error in sorted(errors):
+    print(path + ": " + error)
 
-    return len(errors) == 0
+  return len(errors) == 0
 
 
 def GenerateCredits(
         file_template_file, entry_template_file, output_file, target_os,
         gn_out_dir, gn_target, depfile=None):
-    """Generate about:credits."""
+  """Generate about:credits."""
 
-    def EvaluateTemplate(template, env, escape=True):
-        """Expand a template with variables like {{foo}} using a
+  def EvaluateTemplate(template, env, escape=True):
+    """Expand a template with variables like {{foo}} using a
         dictionary of expansions."""
-        for key, val in env.items():
-            if escape:
-                val = html.escape(val)
-            template = template.replace('{{%s}}' % key, val)
-        return template
+    for key, val in env.items():
+      if escape:
+        val = html.escape(val)
+      template = template.replace('{{%s}}' % key, val)
+    return template
 
-    def MetadataToTemplateEntry(metadata, entry_template):
-        env = {
-            'name': metadata['Name'],
-            'url': metadata['URL'],
-            'license': open(metadata['License File']).read(),
-        }
-        return {
-            'name': metadata['Name'],
-            'content': EvaluateTemplate(entry_template, env),
-            'license_file': metadata['License File'],
-        }
+  def MetadataToTemplateEntry(metadata, entry_template):
+    env = {
+        'name': metadata['Name'],
+        'url': metadata['URL'],
+        'license': open(metadata['License File']).read(),
+    }
+    return {
+        'name': metadata['Name'],
+        'content': EvaluateTemplate(entry_template, env),
+        'license_file': metadata['License File'],
+    }
 
-    if gn_target:
-        third_party_dirs = FindThirdPartyDeps(gn_out_dir, gn_target, target_os)
+  if gn_target:
+    third_party_dirs = FindThirdPartyDeps(gn_out_dir, gn_target, target_os)
 
-        # Sanity-check to raise a build error if invalid gn_... settings are
-        # somehow passed to this script.
-        if not third_party_dirs:
-            raise RuntimeError("No deps found.")
-    else:
-        third_party_dirs = FindThirdPartyDirs(PRUNE_PATHS, _REPOSITORY_ROOT)
+    # Sanity-check to raise a build error if invalid gn_... settings are
+    # somehow passed to this script.
+    if not third_party_dirs:
+      raise RuntimeError("No deps found.")
+  else:
+    third_party_dirs = FindThirdPartyDirs(PRUNE_PATHS, _REPOSITORY_ROOT)
 
-    if not file_template_file:
-        file_template_file = os.path.join(_REPOSITORY_ROOT, 'components',
-                                          'about_ui', 'resources',
-                                          'about_credits.tmpl')
-    if not entry_template_file:
-        entry_template_file = os.path.join(_REPOSITORY_ROOT, 'components',
-                                           'about_ui', 'resources',
-                                           'about_credits_entry.tmpl')
+  if not file_template_file:
+    file_template_file = os.path.join(_REPOSITORY_ROOT, 'components',
+                                      'about_ui', 'resources',
+                                      'about_credits.tmpl')
+  if not entry_template_file:
+    entry_template_file = os.path.join(_REPOSITORY_ROOT, 'components',
+                                       'about_ui', 'resources',
+                                       'about_credits_entry.tmpl')
 
-    entry_template = open(entry_template_file).read()
-    entries = []
-    # Start from Chromium's LICENSE file
-    chromium_license_metadata = {
-        'Name': 'The Chromium Project',
-        'URL': 'http://www.chromium.org',
-        'License File': os.path.join(_REPOSITORY_ROOT, 'LICENSE') }
-    entries.append(MetadataToTemplateEntry(chromium_license_metadata,
-        entry_template))
+  entry_template = open(entry_template_file).read()
+  entries = []
+  # Start from Chromium's LICENSE file
+  chromium_license_metadata = {
+      'Name': 'The Chromium Project',
+      'URL': 'http://www.chromium.org',
+      'License File': os.path.join(_REPOSITORY_ROOT, 'LICENSE')
+  }
+  entries.append(
+      MetadataToTemplateEntry(chromium_license_metadata, entry_template))
 
-    for path in third_party_dirs:
-        try:
-            metadata = ParseDir(path, _REPOSITORY_ROOT)
-        except LicenseError:
-            # TODO(phajdan.jr): Convert to fatal error (http://crbug.com/39240).
-            continue
-        if metadata['License File'] == NOT_SHIPPED:
-            continue
-        if target_os == 'ios' and not gn_target:
-            # Skip over files that are known not to be used on iOS. But
-            # skipping is unnecessary if GN was used to query the actual
-            # dependencies.
-            # TODO(lambroslambrou): Remove this step once the iOS build is
-            # updated to provide --gn-target to this script.
-            if path in KNOWN_NON_IOS_LIBRARIES:
-                continue
-        entries.append(MetadataToTemplateEntry(metadata, entry_template))
+  for path in third_party_dirs:
+    try:
+      metadata = ParseDir(path, _REPOSITORY_ROOT)
+    except LicenseError:
+      # TODO(phajdan.jr): Convert to fatal error (http://crbug.com/39240).
+      continue
+    if metadata['License File'] == NOT_SHIPPED:
+      continue
+    if target_os == 'ios' and not gn_target:
+      # Skip over files that are known not to be used on iOS. But
+      # skipping is unnecessary if GN was used to query the actual
+      # dependencies.
+      # TODO(lambroslambrou): Remove this step once the iOS build is
+      # updated to provide --gn-target to this script.
+      if path in KNOWN_NON_IOS_LIBRARIES:
+        continue
+    entries.append(MetadataToTemplateEntry(metadata, entry_template))
 
-    entries.sort(key=lambda entry: (entry['name'].lower(), entry['content']))
-    for entry_id, entry in enumerate(entries):
-        entry['content'] = entry['content'].replace('{{id}}', str(entry_id))
+  entries.sort(key=lambda entry: (entry['name'].lower(), entry['content']))
+  for entry_id, entry in enumerate(entries):
+    entry['content'] = entry['content'].replace('{{id}}', str(entry_id))
 
-    entries_contents = '\n'.join([entry['content'] for entry in entries])
-    file_template = open(file_template_file).read()
-    template_contents = "<!-- Generated by licenses.py; do not edit. -->"
-    template_contents += EvaluateTemplate(file_template,
-                                          {'entries': entries_contents},
-                                          escape=False)
+  entries_contents = '\n'.join([entry['content'] for entry in entries])
+  file_template = open(file_template_file).read()
+  template_contents = "<!-- Generated by licenses.py; do not edit. -->"
+  template_contents += EvaluateTemplate(
+      file_template, {'entries': entries_contents}, escape=False)
 
-    if output_file:
-      changed = True
-      try:
-        old_output = open(output_file, 'r').read()
-        if old_output == template_contents:
-          changed = False
-      except:
-        pass
-      if changed:
-        with open(output_file, 'w') as output:
-          output.write(template_contents)
-    else:
-      print(template_contents)
+  if output_file:
+    changed = True
+    try:
+      old_output = open(output_file, 'r').read()
+      if old_output == template_contents:
+        changed = False
+    except:
+      pass
+    if changed:
+      with open(output_file, 'w') as output:
+        output.write(template_contents)
+  else:
+    print(template_contents)
 
-    if depfile:
-      assert output_file
-      # Add in build.ninja so that the target will be considered dirty whenever
-      # gn gen is run. Otherwise, it will fail to notice new files being added.
-      # This is still no perfect, as it will fail if no build files are changed,
-      # but a new README.chromium / LICENSE is added. This shouldn't happen in
-      # practice however.
-      license_file_list = (entry['license_file'] for entry in entries)
-      license_file_list = (os.path.relpath(p) for p in license_file_list)
-      license_file_list = sorted(set(license_file_list))
-      build_utils.WriteDepfile(depfile, output_file,
-                               license_file_list + ['build.ninja'])
+  if depfile:
+    assert output_file
+    # Add in build.ninja so that the target will be considered dirty whenever
+    # gn gen is run. Otherwise, it will fail to notice new files being added.
+    # This is still no perfect, as it will fail if no build files are changed,
+    # but a new README.chromium / LICENSE is added. This shouldn't happen in
+    # practice however.
+    license_file_list = (entry['license_file'] for entry in entries)
+    license_file_list = (os.path.relpath(p) for p in license_file_list)
+    license_file_list = sorted(set(license_file_list))
+    build_utils.WriteDepfile(depfile, output_file,
+                             license_file_list + ['build.ninja'])
 
-    return True
+  return True
 
 
 def _ReadFile(path):
-    """Reads a file from disk.
+  """Reads a file from disk.
     Args:
       path: The path of the file to read, relative to the root of the
       repository.
     Returns:
       The contents of the file as a string.
     """
-    with codecs.open(os.path.join(_REPOSITORY_ROOT, path), 'r', 'utf-8') as f:
-        return f.read()
+  with codecs.open(os.path.join(_REPOSITORY_ROOT, path), 'r', 'utf-8') as f:
+    return f.read()
 
 
 def GenerateLicenseFile(output_file, gn_out_dir, gn_target, target_os):
-    """Generate a plain-text LICENSE file which can be used when you ship a part
+  """Generate a plain-text LICENSE file which can be used when you ship a part
     of Chromium code (specified by gn_target) as a stand-alone library
     (e.g., //ios/web_view).
 
     The LICENSE file contains licenses of both Chromium and third-party
     libraries which gn_target depends on. """
 
-    third_party_dirs = FindThirdPartyDeps(gn_out_dir, gn_target, target_os)
+  third_party_dirs = FindThirdPartyDeps(gn_out_dir, gn_target, target_os)
 
-    # Start with Chromium's LICENSE file.
-    content = [_ReadFile('LICENSE')]
+  # Start with Chromium's LICENSE file.
+  content = [_ReadFile('LICENSE')]
 
-    # Add necessary third_party.
-    for directory in sorted(third_party_dirs):
-        metadata = ParseDir(
-            directory, _REPOSITORY_ROOT, require_license_file=True)
-        content.append('-' * 20)
-        content.append(directory.split(os.sep)[-1])
-        content.append('-' * 20)
-        license_file = metadata['License File']
-        if license_file and license_file != NOT_SHIPPED:
-            content.append(_ReadFile(license_file))
+  # Add necessary third_party.
+  for directory in sorted(third_party_dirs):
+    metadata = ParseDir(directory, _REPOSITORY_ROOT, require_license_file=True)
+    content.append('-' * 20)
+    content.append(directory.split(os.sep)[-1])
+    content.append('-' * 20)
+    license_file = metadata['License File']
+    if license_file and license_file != NOT_SHIPPED:
+      content.append(_ReadFile(license_file))
 
-    content_text = '\n'.join(content)
+  content_text = '\n'.join(content)
 
-    if output_file:
-        with codecs.open(output_file, 'w', 'utf-8') as output:
-            output.write(content_text)
-    else:
-        print(content_text)
+  if output_file:
+    with codecs.open(output_file, 'w', 'utf-8') as output:
+      output.write(content_text)
+  else:
+    print(content_text)
 
 
 
 def main():
-    parser = argparse.ArgumentParser()
-    parser.add_argument('--file-template',
-                        help='Template HTML to use for the license page.')
-    parser.add_argument('--entry-template',
-                        help='Template HTML to use for each license.')
-    parser.add_argument('--target-os',
-                        help='OS that this build is targeting.')
-    parser.add_argument('--gn-out-dir',
-                        help='GN output directory for scanning dependencies.')
-    parser.add_argument('--gn-target',
-                        help='GN target to scan for dependencies.')
-    parser.add_argument('command',
-                        choices=['help', 'scan', 'credits', 'license_file'])
-    parser.add_argument('output_file', nargs='?')
-    build_utils.AddDepfileOption(parser)
-    args = parser.parse_args()
+  parser = argparse.ArgumentParser()
+  parser.add_argument(
+      '--file-template', help='Template HTML to use for the license page.')
+  parser.add_argument(
+      '--entry-template', help='Template HTML to use for each license.')
+  parser.add_argument('--target-os', help='OS that this build is targeting.')
+  parser.add_argument(
+      '--gn-out-dir', help='GN output directory for scanning dependencies.')
+  parser.add_argument('--gn-target', help='GN target to scan for dependencies.')
+  parser.add_argument(
+      'command', choices=['help', 'scan', 'credits', 'license_file'])
+  parser.add_argument('output_file', nargs='?')
+  build_utils.AddDepfileOption(parser)
+  args = parser.parse_args()
 
-    if args.command == 'scan':
-        if not ScanThirdPartyDirs():
-            return 1
-    elif args.command == 'credits':
-        if not GenerateCredits(args.file_template, args.entry_template,
-                               args.output_file, args.target_os,
-                               args.gn_out_dir, args.gn_target, args.depfile):
-            return 1
-    elif args.command == 'license_file':
-        try:
-            GenerateLicenseFile(
-                args.output_file, args.gn_out_dir, args.gn_target,
-                args.target_os)
-        except LicenseError as e:
-            print("Failed to parse README.chromium: {}".format(e))
-            return 1
-    else:
-        print(__doc__)
-        return 1
+  if args.command == 'scan':
+    if not ScanThirdPartyDirs():
+      return 1
+  elif args.command == 'credits':
+    if not GenerateCredits(args.file_template, args.entry_template,
+                           args.output_file, args.target_os, args.gn_out_dir,
+                           args.gn_target, args.depfile):
+      return 1
+  elif args.command == 'license_file':
+    try:
+      GenerateLicenseFile(args.output_file, args.gn_out_dir, args.gn_target,
+                          args.target_os)
+    except LicenseError as e:
+      print("Failed to parse README.chromium: {}".format(e))
+      return 1
+  else:
+    print(__doc__)
+    return 1
 
 
 if __name__ == '__main__':
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index d2805db..aaecf6f0 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -26576,7 +26576,7 @@
   <int value="2887"
       label="V8RTCRtpReceiver_JitterBufferDelayHint_AttributeSetter"/>
   <int value="2888" label="MediaCapabilitiesDecodingInfoWithKeySystemConfig"/>
-  <int value="2889" label="RevertInCustomIdent"/>
+  <int value="2889" label="OBSOLETE_RevertInCustomIdent"/>
   <int value="2890" label="UnoptimizedImagePolicies"/>
   <int value="2891" label="VTTCueParser"/>
   <int value="2892" label="MediaElementTextTrackContainer"/>
@@ -26899,6 +26899,7 @@
   <int value="3198" label="CrossOriginOpenerPolicySameOriginAllowPopups"/>
   <int value="3199" label="CrossOriginEmbedderPolicyRequireCorp"/>
   <int value="3200" label="CoopAndCoepIsolated"/>
+  <int value="3201" label="WrongBaselineOfButtonElement"/>
 </enum>
 
 <enum name="FeaturePolicyAllowlistType">
@@ -33106,6 +33107,23 @@
   <int value="20" label="Duplicate entry"/>
 </enum>
 
+<enum name="ICUCreateInstanceCode">
+  <summary>Recorded when ICU create instance of key objects.</summary>
+  <int value="0" label="Character Break Iterator"/>
+  <int value="1" label="Word Break Iterator"/>
+  <int value="2" label="Line Break Iterator"/>
+  <int value="3" label="Line Break Iterator (type=loose)"/>
+  <int value="4" label="Line Break Iterator (type=normal)"/>
+  <int value="5" label="Line Break Iterator (type=strict)"/>
+  <int value="6" label="Sentence Break Iterator"/>
+  <int value="7" label="Title Break Iterator"/>
+  <int value="8" label="Thai Break Engine"/>
+  <int value="9" label="Lao Break Engine"/>
+  <int value="10" label="Burmese Break Engine"/>
+  <int value="11" label="Khmer Break Engine"/>
+  <int value="12" label="Chinese/Japanese Break Engine"/>
+</enum>
+
 <enum name="ICUDataFileCode">
   <summary>
     Hash values for strings of the ICU built-in data file names.
@@ -41994,7 +42012,7 @@
   <int value="172" label="alias-webkit-animation-name"/>
   <int value="173" label="alias-webkit-animation-play-state"/>
   <int value="174" label="alias-webkit-animation-timing-function"/>
-  <int value="175" label="webkit-appearance"/>
+  <int value="175" label="alias-webkit-appearance"/>
   <int value="176" label="webkit-aspect-ratio"/>
   <int value="177" label="alias-webkit-backface-visibility"/>
   <int value="178" label="alias-webkit-background-clip"/>
@@ -42477,6 +42495,7 @@
   <int value="655" label="subtree-visibility"/>
   <int value="656" label="math-style"/>
   <int value="657" label="aspect-ratio"/>
+  <int value="658" label="appearance"/>
 </enum>
 
 <enum name="MappedEditingCommands">
@@ -50233,6 +50252,9 @@
 </enum>
 
 <enum name="PasswordGenerationPresaveConflict">
+  <obsolete>
+    Deprecated as of 2020-04.
+  </obsolete>
   <int value="0" label="No username conflict"/>
   <int value="1" label="No conflict with empty username"/>
   <int value="2" label="Conflict with empty username"/>
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index b8ff2fb..b70dd3f 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -56558,7 +56558,7 @@
 </histogram>
 
 <histogram name="FCMInvalidations.ResetClientIDStatus" enum="InstanceIDResult"
-    expires_after="M83">
+    expires_after="M85">
   <owner>tschumann@chromium.org</owner>
   <owner>treib@chromium.org</owner>
   <summary>
@@ -58578,7 +58578,10 @@
 </histogram>
 
 <histogram name="GCM.GetInstanceIDData.ClientStarted" enum="GCMClientResult"
-    expires_after="M83">
+    expires_after="2020-04-02">
+  <obsolete>
+    Removed in April 2020.
+  </obsolete>
   <owner>tschumann@chromium.org</owner>
   <owner>treib@chromium.org</owner>
   <summary>
@@ -58598,7 +58601,7 @@
 </histogram>
 
 <histogram base="true" name="GCM.IgnoredWriteResult" enum="BooleanSuccess"
-    expires_after="M83">
+    expires_after="M85">
 <!-- Name completed by histogram_suffixes name="IgnoredWriteResultOperation" -->
 
   <owner>tschumann@chromium.org</owner>
@@ -58606,8 +58609,8 @@
   <summary>
     Result of the outcome of persisting a change in the GCMStore. This histogram
     is to meausure the frequency of such events and assess their severity. For
-    example, failing to write when calling AdddInstanceIDData() likely results
-    in leaked FCM registrations.
+    example, failing to write when calling AddInstanceIDData() likely results in
+    leaked FCM registrations.
   </summary>
 </histogram>
 
@@ -64225,6 +64228,17 @@
   <summary>The time it takes to open a hyphenation dictionary file.</summary>
 </histogram>
 
+<histogram name="ICU.CreateInstance" enum="ICUCreateInstanceCode"
+    expires_after="2020-10-15">
+  <owner>ftang@chromium.org</owner>
+  <owner>intl-libs-team@google.com</owner>
+  <summary>
+    This metric logs the creation of key ICU objects. These object creation
+    called directly from Chrome code or indirectly from depending library such
+    as blink and v8.
+  </summary>
+</histogram>
+
 <histogram name="ICU.DataFile" enum="ICUDataFileCode"
     expires_after="2020-10-15">
   <owner>ftang@chromium.org</owner>
@@ -65889,7 +65903,7 @@
 </histogram>
 
 <histogram name="InstanceID.GeneratedNewID" enum="BooleanHit"
-    expires_after="M83">
+    expires_after="M85">
   <owner>tschumann@chromium.org</owner>
   <owner>treib@chromium.org</owner>
   <summary>
@@ -70646,8 +70660,9 @@
 </histogram>
 
 <histogram name="Media.Audio.Capture.HighLatencyCallbackError"
-    enum="BooleanError" expires_after="2020-04-05">
-  <owner>maxmorin@chromium.org</owner>
+    enum="BooleanError" expires_after="2021-04-05">
+  <owner>guidou@chromium.org</owner>
+  <owner>olka@chromium.org</owner>
   <summary>
     A boolean that reflects whether or not an error was reported during audio
     capture for a high latency stream.
@@ -70655,8 +70670,9 @@
 </histogram>
 
 <histogram name="Media.Audio.Capture.InputStreamDuration" units="ms"
-    expires_after="M85">
-  <owner>maxmorin@chromium.org</owner>
+    expires_after="2021-04-05">
+  <owner>guidou@chromium.org</owner>
+  <owner>olka@chromium.org</owner>
   <summary>
     This histogram measures the duration of every input stream, as seen from the
     renderer. It counts from when it has been successfully started, until it is
@@ -70688,8 +70704,9 @@
 </histogram>
 
 <histogram name="Media.Audio.Capture.LowLatencyCallbackError"
-    enum="BooleanError" expires_after="2020-04-05">
-  <owner>maxmorin@chromium.org</owner>
+    enum="BooleanError" expires_after="2021-04-05">
+  <owner>guidou@chromium.org</owner>
+  <owner>olka@chromium.org</owner>
   <summary>
     A boolean that reflects whether or not an error was reported during audio
     capture for a low latency stream.
@@ -114915,6 +114932,9 @@
 
 <histogram name="PasswordGeneration.PresaveConflict"
     enum="PasswordGenerationPresaveConflict" expires_after="M83">
+  <obsolete>
+    Removed 2020-04.
+  </obsolete>
   <owner>vasilii@chromium.org</owner>
   <owner>jdoerrie@chromium.org</owner>
   <summary>
@@ -116967,6 +116987,9 @@
 
 <histogram name="PasswordManager.PresavedUpdateUIDismissalReason"
     enum="PasswordManagerUIDismissalReason" expires_after="M83">
+  <obsolete>
+    Removed 2020-04.
+  </obsolete>
   <owner>vasilii@chromium.org</owner>
   <owner>jdoerrie@chromium.org</owner>
   <summary>
@@ -187814,6 +187837,7 @@
       label="From app list query context menu."/>
   <suffix name="FromAppListRecommendation"
       label="From app list recommendation."/>
+  <suffix name="FromArc" label="From ARC."/>
   <suffix name="FromChromeInternal" label="From Chrome Internal."/>
   <suffix name="FromFileManager" label="From FileManager."/>
   <suffix name="FromInstalledNotification"
@@ -199352,6 +199376,13 @@
   <suffix name="PulseIntervalMedium" label="Medium pulse interval"/>
   <suffix name="PulseIntervalShort" label="Short pulse interval"/>
   <affected-histogram name="Sharing.SendMessageResult"/>
+  <affected-histogram name="Sharing.SendMessageResult.Android"/>
+  <affected-histogram name="Sharing.SendMessageResult.ChromeOS"/>
+  <affected-histogram name="Sharing.SendMessageResult.iOS"/>
+  <affected-histogram name="Sharing.SendMessageResult.Linux"/>
+  <affected-histogram name="Sharing.SendMessageResult.Mac"/>
+  <affected-histogram name="Sharing.SendMessageResult.Unknown"/>
+  <affected-histogram name="Sharing.SendMessageResult.Windows"/>
 </histogram_suffixes>
 
 <histogram_suffixes name="SharingSendMessageResult" separator=".">
diff --git a/ui/base/cocoa/text_services_context_menu.cc b/ui/base/cocoa/text_services_context_menu.cc
index e4de932..d149dd8 100644
--- a/ui/base/cocoa/text_services_context_menu.cc
+++ b/ui/base/cocoa/text_services_context_menu.cc
@@ -17,6 +17,22 @@
 
 namespace {
 
+enum MenuCommands {
+  // These must not overlap with the command IDs used by other menus that
+  // incorporate text services.
+  // TODO(ellyjones): This is an ugly global dependency, especially on
+  // //ui/views. What can we do about this? Can we get rid of the global
+  // implicit namespace of command IDs?
+  kSpeechMenu = 100,
+  kSpeechStartSpeaking,
+  kSpeechStopSpeaking,
+
+  kWritingDirectionMenu,
+  kWritingDirectionDefault,
+  kWritingDirectionLtr,
+  kWritingDirectionRtl,
+};
+
 // The speech channel used for speaking. This is shared to check if a speech
 // channel is currently speaking.
 SpeechChannel g_speech_channel;
@@ -25,11 +41,11 @@
 // |command_id|.
 base::i18n::TextDirection GetTextDirectionFromCommandId(int command_id) {
   switch (command_id) {
-    case IDS_CONTENT_CONTEXT_WRITING_DIRECTION_DEFAULT:
+    case kWritingDirectionDefault:
       return base::i18n::UNKNOWN_DIRECTION;
-    case IDS_CONTENT_CONTEXT_WRITING_DIRECTION_LTR:
+    case kWritingDirectionLtr:
       return base::i18n::LEFT_TO_RIGHT;
-    case IDS_CONTENT_CONTEXT_WRITING_DIRECTION_RTL:
+    case kWritingDirectionRtl:
       return base::i18n::RIGHT_TO_LEFT;
     default:
       NOTREACHED();
@@ -47,20 +63,17 @@
       delegate_(delegate) {
   DCHECK(delegate);
 
-  speech_submenu_model_.AddItemWithStringId(IDS_SPEECH_START_SPEAKING_MAC,
+  speech_submenu_model_.AddItemWithStringId(kSpeechStartSpeaking,
                                             IDS_SPEECH_START_SPEAKING_MAC);
-  speech_submenu_model_.AddItemWithStringId(IDS_SPEECH_STOP_SPEAKING_MAC,
+  speech_submenu_model_.AddItemWithStringId(kSpeechStopSpeaking,
                                             IDS_SPEECH_STOP_SPEAKING_MAC);
 
   bidi_submenu_model_.AddCheckItemWithStringId(
-      IDS_CONTENT_CONTEXT_WRITING_DIRECTION_DEFAULT,
-      IDS_CONTENT_CONTEXT_WRITING_DIRECTION_DEFAULT);
+      kWritingDirectionDefault, IDS_CONTENT_CONTEXT_WRITING_DIRECTION_DEFAULT);
   bidi_submenu_model_.AddCheckItemWithStringId(
-      IDS_CONTENT_CONTEXT_WRITING_DIRECTION_LTR,
-      IDS_CONTENT_CONTEXT_WRITING_DIRECTION_LTR);
+      kWritingDirectionLtr, IDS_CONTENT_CONTEXT_WRITING_DIRECTION_LTR);
   bidi_submenu_model_.AddCheckItemWithStringId(
-      IDS_CONTENT_CONTEXT_WRITING_DIRECTION_RTL,
-      IDS_CONTENT_CONTEXT_WRITING_DIRECTION_RTL);
+      kWritingDirectionRtl, IDS_CONTENT_CONTEXT_WRITING_DIRECTION_RTL);
 }
 
 void TextServicesContextMenu::SpeakText(const base::string16& text) {
@@ -88,7 +101,7 @@
 
 void TextServicesContextMenu::AppendToContextMenu(SimpleMenuModel* model) {
   model->AddSeparator(NORMAL_SEPARATOR);
-  model->AddSubMenuWithStringId(IDS_SPEECH_MAC, IDS_SPEECH_MAC,
+  model->AddSubMenuWithStringId(kSpeechMenu, IDS_SPEECH_MAC,
                                 &speech_submenu_model_);
 }
 
@@ -96,20 +109,20 @@
   // MacOS provides a contextual menu to set writing direction for BiDi
   // languages. This functionality is exposed as a keyboard shortcut on
   // Windows and Linux.
-  model->AddSubMenuWithStringId(IDS_CONTENT_CONTEXT_WRITING_DIRECTION_MENU,
+  model->AddSubMenuWithStringId(kWritingDirectionMenu,
                                 IDS_CONTENT_CONTEXT_WRITING_DIRECTION_MENU,
                                 &bidi_submenu_model_);
 }
 
 bool TextServicesContextMenu::SupportsCommand(int command_id) const {
   switch (command_id) {
-    case IDS_CONTENT_CONTEXT_WRITING_DIRECTION_MENU:
-    case IDS_CONTENT_CONTEXT_WRITING_DIRECTION_DEFAULT:
-    case IDS_CONTENT_CONTEXT_WRITING_DIRECTION_LTR:
-    case IDS_CONTENT_CONTEXT_WRITING_DIRECTION_RTL:
-    case IDS_SPEECH_MAC:
-    case IDS_SPEECH_START_SPEAKING_MAC:
-    case IDS_SPEECH_STOP_SPEAKING_MAC:
+    case kWritingDirectionMenu:
+    case kWritingDirectionDefault:
+    case kWritingDirectionLtr:
+    case kWritingDirectionRtl:
+    case kSpeechMenu:
+    case kSpeechStartSpeaking:
+    case kSpeechStopSpeaking:
       return true;
   }
 
@@ -118,13 +131,13 @@
 
 bool TextServicesContextMenu::IsCommandIdChecked(int command_id) const {
   switch (command_id) {
-    case IDS_CONTENT_CONTEXT_WRITING_DIRECTION_DEFAULT:
-    case IDS_CONTENT_CONTEXT_WRITING_DIRECTION_LTR:
-    case IDS_CONTENT_CONTEXT_WRITING_DIRECTION_RTL:
+    case kWritingDirectionDefault:
+    case kWritingDirectionLtr:
+    case kWritingDirectionRtl:
       return delegate_->IsTextDirectionChecked(
           GetTextDirectionFromCommandId(command_id));
-    case IDS_SPEECH_START_SPEAKING_MAC:
-    case IDS_SPEECH_STOP_SPEAKING_MAC:
+    case kSpeechStartSpeaking:
+    case kSpeechStopSpeaking:
       return false;
   }
 
@@ -134,17 +147,17 @@
 
 bool TextServicesContextMenu::IsCommandIdEnabled(int command_id) const {
   switch (command_id) {
-    case IDS_SPEECH_MAC:
-    case IDS_CONTENT_CONTEXT_WRITING_DIRECTION_MENU:
+    case kSpeechMenu:
+    case kWritingDirectionMenu:
       return true;
-    case IDS_CONTENT_CONTEXT_WRITING_DIRECTION_DEFAULT:
-    case IDS_CONTENT_CONTEXT_WRITING_DIRECTION_LTR:
-    case IDS_CONTENT_CONTEXT_WRITING_DIRECTION_RTL:
+    case kWritingDirectionDefault:
+    case kWritingDirectionLtr:
+    case kWritingDirectionRtl:
       return delegate_->IsTextDirectionEnabled(
           GetTextDirectionFromCommandId(command_id));
-    case IDS_SPEECH_START_SPEAKING_MAC:
+    case kSpeechStartSpeaking:
       return true;
-    case IDS_SPEECH_STOP_SPEAKING_MAC:
+    case kSpeechStopSpeaking:
       return IsSpeaking();
   }
 
@@ -154,15 +167,15 @@
 
 void TextServicesContextMenu::ExecuteCommand(int command_id, int event_flags) {
   switch (command_id) {
-    case IDS_CONTENT_CONTEXT_WRITING_DIRECTION_DEFAULT:
-    case IDS_CONTENT_CONTEXT_WRITING_DIRECTION_LTR:
-    case IDS_CONTENT_CONTEXT_WRITING_DIRECTION_RTL:
+    case kWritingDirectionDefault:
+    case kWritingDirectionLtr:
+    case kWritingDirectionRtl:
       delegate_->UpdateTextDirection(GetTextDirectionFromCommandId(command_id));
       break;
-    case IDS_SPEECH_START_SPEAKING_MAC:
+    case kSpeechStartSpeaking:
       SpeakText(delegate_->GetSelectedText());
       break;
-    case IDS_SPEECH_STOP_SPEAKING_MAC:
+    case kSpeechStopSpeaking:
       StopSpeaking();
       break;
     default:
@@ -170,4 +183,4 @@
   }
 }
 
-}  // namespace ui
\ No newline at end of file
+}  // namespace ui
diff --git a/ui/base/ime/win/tsf_text_store.cc b/ui/base/ime/win/tsf_text_store.cc
index 2a8cde4..22f43ff7 100644
--- a/ui/base/ime/win/tsf_text_store.cc
+++ b/ui/base/ime/win/tsf_text_store.cc
@@ -1415,28 +1415,29 @@
   // any style or not.
   span->thickness = attribute.fBoldLine ? ImeTextSpan::Thickness::kThick
                                         : ImeTextSpan::Thickness::kThin;
-  span->underline_style = ImeTextSpan::UnderlineStyle::kSolid;
-  if (attribute.lsStyle != TF_LS_NONE) {
-    switch (attribute.lsStyle) {
-      case TF_LS_SOLID: {
-        span->underline_style = ImeTextSpan::UnderlineStyle::kSolid;
-        break;
-      }
-      case TF_LS_DOT: {
-        span->underline_style = ImeTextSpan::UnderlineStyle::kDot;
-        break;
-      }
-      case TF_LS_DASH: {
-        span->underline_style = ImeTextSpan::UnderlineStyle::kDash;
-        break;
-      }
-      case TF_LS_SQUIGGLE: {
-        span->underline_style = ImeTextSpan::UnderlineStyle::kSquiggle;
-        break;
-      }
-      default: {
-        span->underline_style = ImeTextSpan::UnderlineStyle::kSolid;
-      }
+  switch (attribute.lsStyle) {
+    case TF_LS_SOLID: {
+      span->underline_style = ImeTextSpan::UnderlineStyle::kSolid;
+      break;
+    }
+    case TF_LS_DOT: {
+      span->underline_style = ImeTextSpan::UnderlineStyle::kDot;
+      break;
+    }
+    case TF_LS_DASH: {
+      span->underline_style = ImeTextSpan::UnderlineStyle::kDash;
+      break;
+    }
+    case TF_LS_SQUIGGLE: {
+      span->underline_style = ImeTextSpan::UnderlineStyle::kSquiggle;
+      break;
+    }
+    case TF_LS_NONE: {
+      span->underline_style = ImeTextSpan::UnderlineStyle::kNone;
+      break;
+    }
+    default: {
+      span->underline_style = ImeTextSpan::UnderlineStyle::kSolid;
     }
   }
   if (attribute.crText.type != TF_CT_NONE) {
diff --git a/ui/base/models/simple_menu_model.cc b/ui/base/models/simple_menu_model.cc
index 6a55c0f..d5d058f 100644
--- a/ui/base/models/simple_menu_model.cc
+++ b/ui/base/models/simple_menu_model.cc
@@ -80,6 +80,16 @@
 }
 
 void SimpleMenuModel::AddItemWithStringId(int command_id, int string_id) {
+  // Prevent this dangerous pattern:
+  //   model->AddItemWithStringId(IDS_FOO, IDS_FOO);
+  // This conflates string IDs with command IDs, which are separate namespaces.
+  // Sometimes this is an accident where this is meant:
+  //   model->AddItemWithStringId(IDC_FOO, IDS_FOO);
+  // but sometimes it is deliberate, usually in situations where there is no
+  // matching IDC constant or the matching IDC constant is not available.
+  // Using IDS constants for command IDs can cause confusion elsewhere, since
+  // command IDs are usually either IDC values or strictly local constants.
+  DCHECK_NE(command_id, string_id);
   AddItem(command_id, l10n_util::GetStringUTF16(string_id));
 }
 
diff --git a/ui/base/pointer/touch_editing_controller.h b/ui/base/pointer/touch_editing_controller.h
index e735129b..e55fb3c9 100644
--- a/ui/base/pointer/touch_editing_controller.h
+++ b/ui/base/pointer/touch_editing_controller.h
@@ -19,6 +19,15 @@
 // using touch.
 class UI_BASE_EXPORT TouchEditable : public ui::SimpleMenuModel::Delegate {
  public:
+  // Commands that all TouchEditables support:
+  enum MenuCommands {
+    // Don't use command ID 0 - a lot of tests use 0 for "no command".
+    kCut = 1,
+    kCopy,
+    kPaste,
+    kLastTouchEditableCommandId = kPaste,
+  };
+
   // TODO(mohsen): Consider switching from local coordinates to screen
   // coordinates in this interface and see if it will simplify things.
 
diff --git a/ui/base/x/BUILD.gn b/ui/base/x/BUILD.gn
index 76584b2..5d4060d 100644
--- a/ui/base/x/BUILD.gn
+++ b/ui/base/x/BUILD.gn
@@ -80,3 +80,18 @@
     "//ui/platform_window/common",
   ]
 }
+
+source_set("gl") {
+  sources = [
+    "x11_gl_egl_utility.cc",
+    "x11_gl_egl_utility.h",
+  ]
+
+  configs += [ "//third_party/khronos:khronos_headers" ]
+  deps = [
+    ":x",
+    "//base",
+    "//third_party/angle:includes",
+    "//ui/gl",
+  ]
+}
diff --git a/ui/base/x/DEPS b/ui/base/x/DEPS
index edeb0030..4a0553f 100644
--- a/ui/base/x/DEPS
+++ b/ui/base/x/DEPS
@@ -1,3 +1,5 @@
 include_rules = [
   "+ui/platform_window/common/platform_window_defaults.h",
+  "+ui/gl/gl_surface_egl.h",
+  "+third_party/khronos/EGL/egl.h",
 ]
diff --git a/ui/base/x/x11_gl_egl_utility.cc b/ui/base/x/x11_gl_egl_utility.cc
new file mode 100644
index 0000000..2a7fd03
--- /dev/null
+++ b/ui/base/x/x11_gl_egl_utility.cc
@@ -0,0 +1,47 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/base/x/x11_gl_egl_utility.h"
+
+#include "ui/base/x/x11_util_internal.h"
+#include "ui/gfx/x/x11.h"
+#include "ui/gl/gl_surface_egl.h"
+
+#ifndef EGL_ANGLE_x11_visual
+#define EGL_ANGLE_x11_visual 1
+#define EGL_X11_VISUAL_ID_ANGLE 0x33A3
+#endif /* EGL_ANGLE_x11_visual */
+
+#ifndef EGL_ANGLE_platform_angle_null
+#define EGL_ANGLE_platform_angle_null 1
+#define EGL_PLATFORM_ANGLE_TYPE_NULL_ANGLE 0x33AE
+#endif /* EGL_ANGLE_platform_angle_null */
+
+namespace ui {
+
+void GetPlatformExtraDisplayAttribs(EGLenum platform_type,
+                                    std::vector<EGLAttrib>* attributes) {
+  // ANGLE_NULL doesn't use the visual, and may run without X11 where we can't
+  // get it anyway.
+  if (platform_type != EGL_PLATFORM_ANGLE_TYPE_NULL_ANGLE) {
+    Visual* visual;
+    ui::XVisualManager::GetInstance()->ChooseVisualForWindow(
+        true, &visual, nullptr, nullptr, nullptr);
+    attributes->push_back(EGL_X11_VISUAL_ID_ANGLE);
+    attributes->push_back(static_cast<EGLAttrib>(XVisualIDFromVisual(visual)));
+  }
+}
+
+void ChoosePlatformCustomAlphaAndBufferSize(EGLint* alpha_size,
+                                            EGLint* buffer_size) {
+  // If we're using ANGLE_NULL, we may not have a display, in which case we
+  // can't use XVisualManager.
+  if (gl::GLSurfaceEGL::GetNativeDisplay() != EGL_DEFAULT_DISPLAY) {
+    ui::XVisualManager::GetInstance()->ChooseVisualForWindow(
+        true, nullptr, buffer_size, nullptr, nullptr);
+    *alpha_size = *buffer_size == 32 ? 8 : 0;
+  }
+}
+
+}  // namespace ui
diff --git a/ui/base/x/x11_gl_egl_utility.h b/ui/base/x/x11_gl_egl_utility.h
new file mode 100644
index 0000000..eb3ca99a
--- /dev/null
+++ b/ui/base/x/x11_gl_egl_utility.h
@@ -0,0 +1,24 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_BASE_X_X11_GL_EGL_UTILITY_H_
+#define UI_BASE_X_X11_GL_EGL_UTILITY_H_
+
+#include <vector>
+
+#include "third_party/khronos/EGL/egl.h"
+
+namespace ui {
+
+// Returns display attributes for the given |platform_type|.
+void GetPlatformExtraDisplayAttribs(EGLenum platform_type,
+                                    std::vector<EGLAttrib>* attributes);
+
+// Sets custom alpha and buffer size.
+void ChoosePlatformCustomAlphaAndBufferSize(EGLint* alpha_size,
+                                            EGLint* buffer_size);
+
+}  // namespace ui
+
+#endif  // UI_BASE_X_X11_GL_EGL_UTILITY_H_
\ No newline at end of file
diff --git a/ui/file_manager/file_manager/foreground/js/toolbar_controller.js b/ui/file_manager/file_manager/foreground/js/toolbar_controller.js
index 8181200..d797d8f8 100644
--- a/ui/file_manager/file_manager/foreground/js/toolbar_controller.js
+++ b/ui/file_manager/file_manager/foreground/js/toolbar_controller.js
@@ -261,7 +261,6 @@
    * @private
    */
   onDeleteButtonClicked_() {
-    this.deleteButton_.blur();
     this.deleteCommand_.canExecuteChange(this.listContainer_.currentList);
     this.deleteCommand_.execute(this.listContainer_.currentList);
   }
diff --git a/ui/gl/BUILD.gn b/ui/gl/BUILD.gn
index 8fac30ee..2c9aba9 100644
--- a/ui/gl/BUILD.gn
+++ b/ui/gl/BUILD.gn
@@ -204,6 +204,8 @@
       "gl_bindings_autogen_egl.h",
       "gl_context_egl.cc",
       "gl_context_egl.h",
+      "gl_display_egl_util.cc",
+      "gl_display_egl_util.h",
       "gl_egl_api_implementation.cc",
       "gl_egl_api_implementation.h",
       "gl_fence_egl.cc",
@@ -255,6 +257,8 @@
 
   if (use_x11) {
     sources += [
+      "gl_surface_egl_x11.cc",
+      "gl_surface_egl_x11.h",
       "gl_surface_egl_x11_gles2.cc",
       "gl_surface_egl_x11_gles2.h",
       "gl_surface_glx_x11.cc",
diff --git a/ui/gl/gl_display_egl_util.cc b/ui/gl/gl_display_egl_util.cc
new file mode 100644
index 0000000..923203a
--- /dev/null
+++ b/ui/gl/gl_display_egl_util.cc
@@ -0,0 +1,52 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/gl/gl_display_egl_util.h"
+
+#include "base/no_destructor.h"
+
+namespace gl {
+
+namespace {
+
+static GLDisplayEglUtil* g_instance = nullptr;
+
+class GLDisplayEglUtilStub : public GLDisplayEglUtil {
+ public:
+  static GLDisplayEglUtilStub* GetInstance() {
+    static base::NoDestructor<GLDisplayEglUtilStub> instance;
+    return instance.get();
+  }
+  void GetPlatformExtraDisplayAttribs(
+      EGLenum platform_type,
+      std::vector<EGLAttrib>* attributes) override {}
+
+  void ChoosePlatformCustomAlphaAndBufferSize(EGLint* alpha_size,
+                                              EGLint* buffer_size) override {}
+
+ private:
+  friend base::NoDestructor<GLDisplayEglUtilStub>;
+
+  GLDisplayEglUtilStub() = default;
+  ~GLDisplayEglUtilStub() override = default;
+  GLDisplayEglUtilStub(const GLDisplayEglUtilStub& util) = delete;
+  GLDisplayEglUtilStub& operator=(const GLDisplayEglUtilStub& util) = delete;
+};
+
+}  // namespace
+
+// static
+GLDisplayEglUtil* GLDisplayEglUtil::GetInstance() {
+  // If a platform specific impl is not set, create a stub instance.
+  if (!g_instance)
+    SetInstance(GLDisplayEglUtilStub::GetInstance());
+  return g_instance;
+}
+
+// static
+void GLDisplayEglUtil::SetInstance(GLDisplayEglUtil* gl_display_util) {
+  g_instance = gl_display_util;
+}
+
+}  // namespace gl
diff --git a/ui/gl/gl_display_egl_util.h b/ui/gl/gl_display_egl_util.h
new file mode 100644
index 0000000..1566704
--- /dev/null
+++ b/ui/gl/gl_display_egl_util.h
@@ -0,0 +1,47 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_GL_GL_DISPLAY_EGL_UTIL_H_
+#define UI_GL_GL_DISPLAY_EGL_UTIL_H_
+
+#include <vector>
+
+#include "third_party/khronos/EGL/egl.h"
+#include "ui/gl/gl_export.h"
+#include "ui/gl/gl_surface_egl.h"
+
+namespace gl {
+
+// Utility singleton class that helps to set additional egl properties. This
+// class should be implemented by each platform except Ozone. In case of Ozone,
+// there is a common implementation that forwards calls to a public interface of
+// a platform.
+// The reason why it is defined here in ui/gl is that ui/gl cannot depend on
+// ozone and we have to provide an interface here. ui/gl/init will provide an
+// implementation for this utility class upon initialization of gl.
+class GL_EXPORT GLDisplayEglUtil {
+ public:
+  // Returns either set instance or stub instance.
+  static GLDisplayEglUtil* GetInstance();
+
+  static void SetInstance(GLDisplayEglUtil* gl_display_util);
+
+  // Returns display attributes for the given |platform_type|. Each platform can
+  // have different attributes.
+  virtual void GetPlatformExtraDisplayAttribs(
+      EGLenum platform_type,
+      std::vector<EGLAttrib>* attributes) = 0;
+
+  // Sets custom alpha and buffer size for a given platform. By default, the
+  // values are not modified.
+  virtual void ChoosePlatformCustomAlphaAndBufferSize(EGLint* alpha_size,
+                                                      EGLint* buffer_size) = 0;
+
+ protected:
+  virtual ~GLDisplayEglUtil() = default;
+};
+
+}  // namespace gl
+
+#endif  // UI_GL_GL_DISPLAY_EGL_UTIL_H_
diff --git a/ui/gl/gl_surface_egl.cc b/ui/gl/gl_surface_egl.cc
index 750f3ede2..5e4f30b8 100644
--- a/ui/gl/gl_surface_egl.cc
+++ b/ui/gl/gl_surface_egl.cc
@@ -20,7 +20,6 @@
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_split.h"
 #include "base/system/sys_info.h"
-#include "base/threading/thread_task_runner_handle.h"
 #include "base/trace_event/trace_event.h"
 #include "build/build_config.h"
 #include "ui/events/platform/platform_event_dispatcher.h"
@@ -30,6 +29,7 @@
 #include "ui/gl/egl_util.h"
 #include "ui/gl/gl_context.h"
 #include "ui/gl/gl_context_egl.h"
+#include "ui/gl/gl_display_egl_util.h"
 #include "ui/gl/gl_image.h"
 #include "ui/gl/gl_implementation.h"
 #include "ui/gl/gl_surface_presentation_helper.h"
@@ -204,37 +204,6 @@
 static base::LazyInstance<TraceSwapEventsInitializer>::Leaky
     g_trace_swap_enabled = LAZY_INSTANCE_INITIALIZER;
 
-#if defined(USE_X11)
-class XrandrIntervalOnlyVSyncProvider : public gfx::VSyncProvider {
- public:
-  XrandrIntervalOnlyVSyncProvider(Display* display)
-      : display_(display), interval_(base::TimeDelta::FromSeconds(1 / 60.)) {}
-
-  void GetVSyncParameters(UpdateVSyncCallback callback) override {
-    if (++calls_since_last_update_ >= kCallsBetweenUpdates) {
-      calls_since_last_update_ = 0;
-      interval_ = ui::GetPrimaryDisplayRefreshIntervalFromXrandr(display_);
-    }
-    base::ThreadTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE,
-        base::BindOnce(std::move(callback), base::TimeTicks(), interval_));
-  }
-
-  bool GetVSyncParametersIfAvailable(base::TimeTicks* timebase,
-                                     base::TimeDelta* interval) override {
-    return false;
-  }
-  bool SupportGetVSyncParametersIfAvailable() const override { return false; }
-  bool IsHWClock() const override { return false; }
-
- private:
-  Display* const display_ = nullptr;
-  base::TimeDelta interval_;
-  static const int kCallsBetweenUpdates = 100;
-  int calls_since_last_update_ = kCallsBetweenUpdates;
-};
-#endif
-
 class EGLSyncControlVSyncProvider : public SyncControlVSyncProvider {
  public:
   explicit EGLSyncControlVSyncProvider(EGLSurface surface)
@@ -313,18 +282,8 @@
   display_attribs.push_back(EGL_PLATFORM_ANGLE_TYPE_ANGLE);
   display_attribs.push_back(static_cast<EGLAttrib>(platform_type));
 
-#if defined(USE_X11)
-  // ANGLE_NULL doesn't use the visual, and may run without X11 where we can't
-  // get it anyway.
-  if (platform_type != EGL_PLATFORM_ANGLE_TYPE_NULL_ANGLE) {
-    Visual* visual;
-    ui::XVisualManager::GetInstance()->ChooseVisualForWindow(
-        true, &visual, nullptr, nullptr, nullptr);
-    display_attribs.push_back(EGL_X11_VISUAL_ID_ANGLE);
-    display_attribs.push_back(
-        static_cast<EGLAttrib>(XVisualIDFromVisual(visual)));
-  }
-#endif
+  GLDisplayEglUtil::GetInstance()->GetPlatformExtraDisplayAttribs(
+      platform_type, &display_attribs);
 
   std::vector<const char*> enabled_features_attribs =
       GetAttribArrayFromStringVector(enabled_features);
@@ -574,15 +533,10 @@
   EGLint stencil_size = format.GetStencilBits();
   EGLint samples = format.GetSamples();
 
-#if defined(USE_X11)
-  // If we're using ANGLE_NULL, we may not have a display, in which case we
-  // can't use XVisualManager.
-  if (g_native_display.GetDisplay() != EGL_DEFAULT_DISPLAY) {
-    ui::XVisualManager::GetInstance()->ChooseVisualForWindow(
-        true, nullptr, &buffer_size, nullptr, nullptr);
-    alpha_size = buffer_size == 32 ? 8 : 0;
-  }
-#endif
+  // Some platforms (eg. X11) may want to set custom values for alpha and buffer
+  // sizes.
+  GLDisplayEglUtil::GetInstance()->ChoosePlatformCustomAlphaAndBufferSize(
+      &alpha_size, &buffer_size);
 
   EGLint surface_type =
       (surfaceless ? EGL_DONT_CARE : EGL_WINDOW_BIT | EGL_PBUFFER_BIT);
@@ -1372,34 +1326,8 @@
         std::make_unique<EGLSyncControlVSyncProvider>(surface_);
   }
 
-#if defined(USE_X11)
-  // Query all child windows and store them. ANGLE creates a child window when
-  // eglCreateWidnowSurface is called on X11 and expose events from this window
-  // need to be received by this class.
-  Display* x11_display = GetNativeDisplay();
-  Window root = 0;
-  Window parent = 0;
-  Window* children = nullptr;
-  unsigned num_children = 0;
-  if (XQueryTree(x11_display, window_, &root, &parent, &children,
-                 &num_children)) {
-    for (unsigned int i = 0; i < num_children; ++i) {
-      children_.push_back(children[i]);
-    }
-    if (num_children > 0) {
-      XFree(children);
-    }
-  }
-
-  if (ui::X11EventSource::HasInstance()) {
-    ui::X11EventSource::GetInstance()->AddXEventDispatcher(this);
-  }
-
-  if (!vsync_provider_external_ && !vsync_provider_internal_) {
-    vsync_provider_internal_ =
-        std::make_unique<XrandrIntervalOnlyVSyncProvider>(x11_display);
-  }
-#endif
+  if (!vsync_provider_external_ && !vsync_provider_internal_)
+    vsync_provider_internal_ = CreateVsyncProviderInternal();
 
   presentation_helper_ =
       std::make_unique<GLSurfacePresentationHelper>(GetVSyncProvider());
@@ -1496,11 +1424,6 @@
   vsync_provider_internal_ = nullptr;
 
   if (surface_) {
-#if defined(USE_X11)
-    if (ui::X11EventSource::HasInstance()) {
-      ui::X11EventSource::GetInstance()->RemoveXEventDispatcher(this);
-    }
-#endif
     if (!eglDestroySurface(GetDisplay(), surface_)) {
       LOG(ERROR) << "eglDestroySurface failed with error "
                  << GetLastEGLErrorString();
@@ -1544,17 +1467,6 @@
     UpdateSwapEvents(new_frame_id, new_frame_id_is_valid);
   }
 
-#if defined(USE_X11)
-  // We need to restore the background pixel that we set to WhitePixel on
-  // views::DesktopWindowTreeHostX11::InitX11Window back to None for the
-  // XWindow associated to this surface after the first SwapBuffers has
-  // happened, to avoid showing a weird white background while resizing.
-  if (GetNativeDisplay() && !has_swapped_buffers_) {
-    XSetWindowBackgroundPixmap(GetNativeDisplay(), window_, 0);
-    has_swapped_buffers_ = true;
-  }
-#endif
-
   return scoped_swap_buffers.result();
 }
 
@@ -1673,6 +1585,11 @@
   }
 }
 
+std::unique_ptr<gfx::VSyncProvider>
+NativeViewGLSurfaceEGL::CreateVsyncProviderInternal() {
+  return nullptr;
+}
+
 gfx::Size NativeViewGLSurfaceEGL::GetSize() {
   EGLint width;
   EGLint height;
@@ -1981,25 +1898,6 @@
   return success;
 }
 
-#if defined(USE_X11)
-bool NativeViewGLSurfaceEGL::DispatchXEvent(XEvent* x_event) {
-  // When ANGLE is used for EGL, it creates an X11 child window. Expose events
-  // from this window need to be forwarded to this class.
-  bool can_dispatch = x_event->type == Expose &&
-                      std::find(children_.begin(), children_.end(),
-                                x_event->xexpose.window) != children_.end();
-
-  if (!can_dispatch)
-    return false;
-
-  x_event->xexpose.window = window_;
-  Display* x11_display = GetNativeDisplay();
-  XSendEvent(x11_display, window_, x11::False, ExposureMask, x_event);
-  XFlush(x11_display);
-  return true;
-}
-#endif
-
 PbufferGLSurfaceEGL::PbufferGLSurfaceEGL(const gfx::Size& size)
     : size_(size),
       surface_(NULL) {
diff --git a/ui/gl/gl_surface_egl.h b/ui/gl/gl_surface_egl.h
index 10099277..191d055 100644
--- a/ui/gl/gl_surface_egl.h
+++ b/ui/gl/gl_surface_egl.h
@@ -27,10 +27,6 @@
 #include "ui/gl/gl_surface.h"
 #include "ui/gl/gl_surface_overlay.h"
 
-#if defined(USE_X11)
-#include "ui/events/platform/x11/x11_event_source.h"  // nogncheck
-#endif
-
 namespace gl {
 
 class EGLDisplayPlatform {
@@ -136,9 +132,6 @@
 
 // Encapsulates an EGL surface bound to a view.
 class GL_EXPORT NativeViewGLSurfaceEGL : public GLSurfaceEGL,
-#if defined(USE_X11)
-                                         public ui::XEventDispatcher,
-#endif
                                          public EGLTimestampClient {
  public:
   NativeViewGLSurfaceEGL(EGLNativeWindowType window,
@@ -194,7 +187,6 @@
   ~NativeViewGLSurfaceEGL() override;
 
   EGLNativeWindowType window_ = 0;
-  std::vector<EGLNativeWindowType> children_;
   gfx::Size size_ = gfx::Size(1, 1);
   bool enable_fixed_size_angle_ = true;
 
@@ -213,20 +205,14 @@
   void UpdateSwapEvents(EGLuint64KHR newFrameId, bool newFrameIdIsValid);
   void TraceSwapEvents(EGLuint64KHR oldFrameId);
 
-#if defined(USE_X11)
-  // XEventDispatcher:
-  bool DispatchXEvent(XEvent* xev) override;
-#endif
+  // Some platforms may provide a custom implementation of vsync provider.
+  virtual std::unique_ptr<gfx::VSyncProvider> CreateVsyncProviderInternal();
 
   EGLSurface surface_ = nullptr;
   bool supports_post_sub_buffer_ = false;
   bool supports_swap_buffer_with_damage_ = false;
   gfx::SurfaceOrigin surface_origin_ = gfx::SurfaceOrigin::kBottomLeft;
 
-#if defined(USE_X11)
-  bool has_swapped_buffers_ = false;
-#endif
-
   std::unique_ptr<gfx::VSyncProvider> vsync_provider_external_;
   std::unique_ptr<gfx::VSyncProvider> vsync_provider_internal_;
 
diff --git a/ui/gl/gl_surface_egl_x11.cc b/ui/gl/gl_surface_egl_x11.cc
new file mode 100644
index 0000000..fbb53bf
--- /dev/null
+++ b/ui/gl/gl_surface_egl_x11.cc
@@ -0,0 +1,128 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/gl/gl_surface_egl_x11.h"
+
+#include "base/threading/thread_task_runner_handle.h"
+#include "ui/base/x/x11_display_util.h"
+#include "ui/gfx/x/x11.h"
+#include "ui/gl/egl_util.h"
+
+namespace gl {
+
+namespace {
+
+class XrandrIntervalOnlyVSyncProvider : public gfx::VSyncProvider {
+ public:
+  explicit XrandrIntervalOnlyVSyncProvider(Display* display)
+      : display_(display), interval_(base::TimeDelta::FromSeconds(1 / 60.)) {}
+
+  void GetVSyncParameters(UpdateVSyncCallback callback) override {
+    if (++calls_since_last_update_ >= kCallsBetweenUpdates) {
+      calls_since_last_update_ = 0;
+      interval_ = ui::GetPrimaryDisplayRefreshIntervalFromXrandr(display_);
+    }
+    base::ThreadTaskRunnerHandle::Get()->PostTask(
+        FROM_HERE,
+        base::BindOnce(std::move(callback), base::TimeTicks(), interval_));
+  }
+
+  bool GetVSyncParametersIfAvailable(base::TimeTicks* timebase,
+                                     base::TimeDelta* interval) override {
+    return false;
+  }
+  bool SupportGetVSyncParametersIfAvailable() const override { return false; }
+  bool IsHWClock() const override { return false; }
+
+ private:
+  Display* const display_ = nullptr;
+  base::TimeDelta interval_;
+  static const int kCallsBetweenUpdates = 100;
+  int calls_since_last_update_ = kCallsBetweenUpdates;
+};
+
+}  // namespace
+
+NativeViewGLSurfaceEGLX11::NativeViewGLSurfaceEGLX11(EGLNativeWindowType window)
+    : NativeViewGLSurfaceEGL(window, nullptr) {}
+
+bool NativeViewGLSurfaceEGLX11::Initialize(GLSurfaceFormat format) {
+  if (!NativeViewGLSurfaceEGL::Initialize(format))
+    return false;
+
+  // Query all child windows and store them. ANGLE creates a child window when
+  // eglCreateWidnowSurface is called on X11 and expose events from this window
+  // need to be received by this class.
+  Display* x11_display = GetNativeDisplay();
+  Window root = 0;
+  Window parent = 0;
+  Window* children = nullptr;
+  unsigned num_children = 0;
+  if (XQueryTree(x11_display, window_, &root, &parent, &children,
+                 &num_children)) {
+    for (unsigned int i = 0; i < num_children; ++i) {
+      children_.push_back(children[i]);
+    }
+    if (num_children > 0) {
+      XFree(children);
+    }
+  }
+
+  if (ui::X11EventSource::HasInstance()) {
+    dispatcher_set_ = true;
+    ui::X11EventSource::GetInstance()->AddXEventDispatcher(this);
+  }
+  return true;
+}
+
+void NativeViewGLSurfaceEGLX11::Destroy() {
+  NativeViewGLSurfaceEGL::Destroy();
+  if (dispatcher_set_ && ui::X11EventSource::HasInstance())
+    ui::X11EventSource::GetInstance()->RemoveXEventDispatcher(this);
+}
+
+gfx::SwapResult NativeViewGLSurfaceEGLX11::SwapBuffers(
+    PresentationCallback callback) {
+  auto result = NativeViewGLSurfaceEGL::SwapBuffers(std::move(callback));
+  if (result == gfx::SwapResult::SWAP_FAILED)
+    return result;
+
+  // We need to restore the background pixel that we set to WhitePixel on
+  // views::DesktopWindowTreeHostX11::InitX11Window back to None for the
+  // XWindow associated to this surface after the first SwapBuffers has
+  // happened, to avoid showing a weird white background while resizing.
+  if (GetNativeDisplay() && !has_swapped_buffers_) {
+    XSetWindowBackgroundPixmap(GetNativeDisplay(), window_, 0);
+    has_swapped_buffers_ = true;
+  }
+  return result;
+}
+
+NativeViewGLSurfaceEGLX11::~NativeViewGLSurfaceEGLX11() {
+  Destroy();
+}
+
+std::unique_ptr<gfx::VSyncProvider>
+NativeViewGLSurfaceEGLX11::CreateVsyncProviderInternal() {
+  return std::make_unique<XrandrIntervalOnlyVSyncProvider>(GetNativeDisplay());
+}
+
+bool NativeViewGLSurfaceEGLX11::DispatchXEvent(XEvent* x_event) {
+  // When ANGLE is used for EGL, it creates an X11 child window. Expose events
+  // from this window need to be forwarded to this class.
+  bool can_dispatch = x_event->type == Expose &&
+                      std::find(children_.begin(), children_.end(),
+                                x_event->xexpose.window) != children_.end();
+
+  if (!can_dispatch)
+    return false;
+
+  x_event->xexpose.window = window_;
+  Display* x11_display = GetNativeDisplay();
+  XSendEvent(x11_display, window_, x11::False, ExposureMask, x_event);
+  XFlush(x11_display);
+  return true;
+}
+
+}  // namespace gl
diff --git a/ui/gl/gl_surface_egl_x11.h b/ui/gl/gl_surface_egl_x11.h
new file mode 100644
index 0000000..925b35c
--- /dev/null
+++ b/ui/gl/gl_surface_egl_x11.h
@@ -0,0 +1,53 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_GL_GL_SURFACE_EGL_X11_H_
+#define UI_GL_GL_SURFACE_EGL_X11_H_
+
+#include <stdint.h>
+
+#include <string>
+
+#include "base/macros.h"
+#include "ui/events/platform/x11/x11_event_source.h"
+#include "ui/gl/gl_export.h"
+#include "ui/gl/gl_surface_egl.h"
+
+namespace gl {
+
+// Encapsulates an EGL surface bound to a view using the X Window System.
+class GL_EXPORT NativeViewGLSurfaceEGLX11 : public NativeViewGLSurfaceEGL,
+                                            public ui::XEventDispatcher {
+ public:
+  explicit NativeViewGLSurfaceEGLX11(EGLNativeWindowType window);
+  NativeViewGLSurfaceEGLX11(const NativeViewGLSurfaceEGLX11& other) = delete;
+  NativeViewGLSurfaceEGLX11& operator=(const NativeViewGLSurfaceEGLX11& rhs) =
+      delete;
+
+  // NativeViewGLSurfaceEGL overrides.
+  bool Initialize(GLSurfaceFormat format) override;
+  void Destroy() override;
+  gfx::SwapResult SwapBuffers(PresentationCallback callback) override;
+
+ protected:
+  ~NativeViewGLSurfaceEGLX11() override;
+
+ private:
+  // NativeViewGLSurfaceEGL overrides:
+  std::unique_ptr<gfx::VSyncProvider> CreateVsyncProviderInternal() override;
+
+  // XEventDispatcher:
+  bool DispatchXEvent(XEvent* xev) override;
+
+  std::vector<Window> children_;
+
+  // Indicates if the dispatcher has been set.
+  bool dispatcher_set_ = false;
+
+  bool has_swapped_buffers_ = false;
+};
+
+}  // namespace gl
+
+#endif  // UI_GL_GL_SURFACE_EGL_X11_H_
diff --git a/ui/gl/gl_surface_egl_x11_gles2.cc b/ui/gl/gl_surface_egl_x11_gles2.cc
index 684eb63..c68a18f 100644
--- a/ui/gl/gl_surface_egl_x11_gles2.cc
+++ b/ui/gl/gl_surface_egl_x11_gles2.cc
@@ -14,7 +14,7 @@
 
 NativeViewGLSurfaceEGLX11GLES2::NativeViewGLSurfaceEGLX11GLES2(
     EGLNativeWindowType window)
-    : NativeViewGLSurfaceEGL(0, nullptr), parent_window_(window) {}
+    : NativeViewGLSurfaceEGLX11(0), parent_window_(window) {}
 
 bool NativeViewGLSurfaceEGLX11GLES2::InitializeNativeWindow() {
   Display* x11_display = GetNativeDisplay();
@@ -46,7 +46,7 @@
 }
 
 void NativeViewGLSurfaceEGLX11GLES2::Destroy() {
-  NativeViewGLSurfaceEGL::Destroy();
+  NativeViewGLSurfaceEGLX11::Destroy();
 
   if (window_) {
     Display* x11_display = GetNativeDisplay();
diff --git a/ui/gl/gl_surface_egl_x11_gles2.h b/ui/gl/gl_surface_egl_x11_gles2.h
index fcf17dc..7039b34d 100644
--- a/ui/gl/gl_surface_egl_x11_gles2.h
+++ b/ui/gl/gl_surface_egl_x11_gles2.h
@@ -11,12 +11,13 @@
 
 #include "base/macros.h"
 #include "ui/gl/gl_export.h"
-#include "ui/gl/gl_surface_egl.h"
+#include "ui/gl/gl_surface_egl_x11.h"
 
 namespace gl {
 
 // Encapsulates an EGL surface bound to a view using the X Window System.
-class GL_EXPORT NativeViewGLSurfaceEGLX11GLES2 : public NativeViewGLSurfaceEGL {
+class GL_EXPORT NativeViewGLSurfaceEGLX11GLES2
+    : public NativeViewGLSurfaceEGLX11 {
  public:
   explicit NativeViewGLSurfaceEGLX11GLES2(EGLNativeWindowType window);
 
diff --git a/ui/gl/init/BUILD.gn b/ui/gl/init/BUILD.gn
index 921e567..f2708299 100644
--- a/ui/gl/init/BUILD.gn
+++ b/ui/gl/init/BUILD.gn
@@ -58,13 +58,23 @@
     libs = [ "OpenGL.framework" ]
   } else if (use_x11) {
     sources += [
+      "gl_display_egl_util_x11.cc",
+      "gl_display_egl_util_x11.h",
       "gl_factory_x11.cc",
       "gl_initializer_x11.cc",
     ]
 
-    deps += [ "//ui/gfx/x" ]
+    configs += [ "//build/config/linux:x11" ]
+
+    deps += [
+      "//ui/base/x",
+      "//ui/base/x:gl",
+      "//ui/gfx/x",
+    ]
   } else if (use_ozone) {
     sources += [
+      "gl_display_egl_util_ozone.cc",
+      "gl_display_egl_util_ozone.h",
       "gl_factory_ozone.cc",
       "gl_initializer_ozone.cc",
       "ozone_util.h",
diff --git a/ui/gl/init/gl_display_egl_util_ozone.cc b/ui/gl/init/gl_display_egl_util_ozone.cc
new file mode 100644
index 0000000..fd00349
--- /dev/null
+++ b/ui/gl/init/gl_display_egl_util_ozone.cc
@@ -0,0 +1,38 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/gl/init/gl_display_egl_util_ozone.h"
+
+#include "ui/ozone/public/ozone_platform.h"
+#include "ui/ozone/public/platform_gl_egl_utility.h"
+
+namespace gl {
+
+// static
+GLDisplayEglUtilOzone* GLDisplayEglUtilOzone::GetInstance() {
+  static base::NoDestructor<GLDisplayEglUtilOzone> instance;
+  return instance.get();
+}
+
+void GLDisplayEglUtilOzone::GetPlatformExtraDisplayAttribs(
+    EGLenum platform_type,
+    std::vector<EGLAttrib>* attributes) {
+  auto* utility = ui::OzonePlatform::GetInstance()->GetPlatformGLEGLUtility();
+  if (utility)
+    utility->GetAdditionalEGLAttributes(platform_type, attributes);
+}
+
+void GLDisplayEglUtilOzone::ChoosePlatformCustomAlphaAndBufferSize(
+    EGLint* alpha_size,
+    EGLint* buffer_size) {
+  auto* utility = ui::OzonePlatform::GetInstance()->GetPlatformGLEGLUtility();
+  if (utility)
+    utility->ChooseEGLAlphaAndBufferSize(alpha_size, buffer_size);
+}
+
+GLDisplayEglUtilOzone::GLDisplayEglUtilOzone() = default;
+
+GLDisplayEglUtilOzone::~GLDisplayEglUtilOzone() = default;
+
+}  // namespace gl
diff --git a/ui/gl/init/gl_display_egl_util_ozone.h b/ui/gl/init/gl_display_egl_util_ozone.h
new file mode 100644
index 0000000..32ecce6
--- /dev/null
+++ b/ui/gl/init/gl_display_egl_util_ozone.h
@@ -0,0 +1,39 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_GL_INIT_GL_DISPLAY_EGL_UTIL_OZONE_H_
+#define UI_GL_INIT_GL_DISPLAY_EGL_UTIL_OZONE_H_
+
+#include <vector>
+
+#include "base/no_destructor.h"
+#include "ui/gl/gl_display_egl_util.h"
+
+namespace gl {
+
+// Forwards calls to PlatformGLEGLUtility. It might be implemented by some
+// platforms.
+class GLDisplayEglUtilOzone : public GLDisplayEglUtil {
+ public:
+  static GLDisplayEglUtilOzone* GetInstance();
+
+  // GLDisplayEglUtil overrides:
+  void GetPlatformExtraDisplayAttribs(
+      EGLenum platform_type,
+      std::vector<EGLAttrib>* attributes) override;
+  void ChoosePlatformCustomAlphaAndBufferSize(EGLint* alpha_size,
+                                              EGLint* buffer_size) override;
+
+ private:
+  friend base::NoDestructor<GLDisplayEglUtilOzone>;
+
+  GLDisplayEglUtilOzone();
+  ~GLDisplayEglUtilOzone() override;
+  GLDisplayEglUtilOzone(const GLDisplayEglUtilOzone& util) = delete;
+  GLDisplayEglUtilOzone& operator=(const GLDisplayEglUtilOzone& util) = delete;
+};
+
+}  // namespace gl
+
+#endif  // UI_GL_INIT_GL_DISPLAY_EGL_UTIL_OZONE_H_
diff --git a/ui/gl/init/gl_display_egl_util_x11.cc b/ui/gl/init/gl_display_egl_util_x11.cc
new file mode 100644
index 0000000..182291b8
--- /dev/null
+++ b/ui/gl/init/gl_display_egl_util_x11.cc
@@ -0,0 +1,33 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/gl/init/gl_display_egl_util_x11.h"
+
+#include "ui/base/x/x11_gl_egl_utility.h"
+
+namespace gl {
+
+// static
+GLDisplayEglUtilX11* GLDisplayEglUtilX11::GetInstance() {
+  static base::NoDestructor<GLDisplayEglUtilX11> instance;
+  return instance.get();
+}
+
+void GLDisplayEglUtilX11::GetPlatformExtraDisplayAttribs(
+    EGLenum platform_type,
+    std::vector<EGLAttrib>* attributes) {
+  ui::GetPlatformExtraDisplayAttribs(platform_type, attributes);
+}
+
+void GLDisplayEglUtilX11::ChoosePlatformCustomAlphaAndBufferSize(
+    EGLint* alpha_size,
+    EGLint* buffer_size) {
+  ui::ChoosePlatformCustomAlphaAndBufferSize(alpha_size, buffer_size);
+}
+
+GLDisplayEglUtilX11::GLDisplayEglUtilX11() = default;
+
+GLDisplayEglUtilX11::~GLDisplayEglUtilX11() = default;
+
+}  // namespace gl
\ No newline at end of file
diff --git a/ui/gl/init/gl_display_egl_util_x11.h b/ui/gl/init/gl_display_egl_util_x11.h
new file mode 100644
index 0000000..290de7f
--- /dev/null
+++ b/ui/gl/init/gl_display_egl_util_x11.h
@@ -0,0 +1,36 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_GL_INIT_GL_DISPLAY_EGL_UTIL_X11_H_
+#define UI_GL_INIT_GL_DISPLAY_EGL_UTIL_X11_H_
+
+#include <vector>
+
+#include "base/no_destructor.h"
+#include "ui/gl/gl_display_egl_util.h"
+
+namespace gl {
+
+class GLDisplayEglUtilX11 : public GLDisplayEglUtil {
+ public:
+  static GLDisplayEglUtilX11* GetInstance();
+
+  // GLDisplayEglUtil overrides:
+  void GetPlatformExtraDisplayAttribs(
+      EGLenum platform_type,
+      std::vector<EGLAttrib>* attributes) override;
+  void ChoosePlatformCustomAlphaAndBufferSize(EGLint* alpha_size,
+                                              EGLint* buffer_size) override;
+
+ private:
+  friend base::NoDestructor<GLDisplayEglUtilX11>;
+  GLDisplayEglUtilX11();
+  ~GLDisplayEglUtilX11() override;
+  GLDisplayEglUtilX11(const GLDisplayEglUtilX11& util) = delete;
+  GLDisplayEglUtilX11& operator=(const GLDisplayEglUtilX11& util) = delete;
+};
+
+}  // namespace gl
+
+#endif  // UI_GL_INIT_GL_DISPLAY_EGL_UTIL_X11_H_
diff --git a/ui/gl/init/gl_factory_x11.cc b/ui/gl/init/gl_factory_x11.cc
index 75c9f0e..4bebef1 100644
--- a/ui/gl/init/gl_factory_x11.cc
+++ b/ui/gl/init/gl_factory_x11.cc
@@ -14,7 +14,7 @@
 #include "ui/gl/gl_implementation.h"
 #include "ui/gl/gl_share_group.h"
 #include "ui/gl/gl_surface.h"
-#include "ui/gl/gl_surface_egl.h"
+#include "ui/gl/gl_surface_egl_x11.h"
 #include "ui/gl/gl_surface_egl_x11_gles2.h"
 #include "ui/gl/gl_surface_glx.h"
 #include "ui/gl/gl_surface_glx_x11.h"
@@ -83,7 +83,7 @@
       return InitializeGLSurface(new NativeViewGLSurfaceEGLX11GLES2(window));
     case kGLImplementationEGLANGLE:
       DCHECK(window != gfx::kNullAcceleratedWidget);
-      return InitializeGLSurface(new NativeViewGLSurfaceEGL(window, nullptr));
+      return InitializeGLSurface(new NativeViewGLSurfaceEGLX11(window));
     case kGLImplementationMockGL:
     case kGLImplementationStubGL:
       return new GLSurfaceStub;
diff --git a/ui/gl/init/gl_initializer_ozone.cc b/ui/gl/init/gl_initializer_ozone.cc
index 754b36c..4dfcebd 100644
--- a/ui/gl/init/gl_initializer_ozone.cc
+++ b/ui/gl/init/gl_initializer_ozone.cc
@@ -8,14 +8,18 @@
 #include "ui/gl/gl_bindings.h"
 #include "ui/gl/gl_gl_api_implementation.h"
 #include "ui/gl/gl_surface.h"
+#include "ui/gl/init/gl_display_egl_util_ozone.h"
 #include "ui/gl/init/ozone_util.h"
+#include "ui/ozone/public/ozone_platform.h"
 
 namespace gl {
 namespace init {
 
 bool InitializeGLOneOffPlatform() {
-  if (HasGLOzone())
+  if (HasGLOzone()) {
+    gl::GLDisplayEglUtil::SetInstance(gl::GLDisplayEglUtilOzone::GetInstance());
     return GetGLOzone()->InitializeGLOneOffPlatform();
+  }
 
   switch (GetGLImplementation()) {
     case kGLImplementationMockGL:
diff --git a/ui/gl/init/gl_initializer_x11.cc b/ui/gl/init/gl_initializer_x11.cc
index d9190a95..d76d146 100644
--- a/ui/gl/init/gl_initializer_x11.cc
+++ b/ui/gl/init/gl_initializer_x11.cc
@@ -20,6 +20,7 @@
 #include "ui/gl/gl_surface_egl.h"
 #include "ui/gl/gl_surface_glx.h"
 #include "ui/gl/gl_switches.h"
+#include "ui/gl/init/gl_display_egl_util_x11.h"
 
 namespace gl {
 namespace init {
@@ -152,6 +153,8 @@
     case kGLImplementationSwiftShaderGL:
     case kGLImplementationEGLGLES2:
     case kGLImplementationEGLANGLE:
+      // Set utility class that helps to initialize egl platform.
+      gl::GLDisplayEglUtil::SetInstance(gl::GLDisplayEglUtilX11::GetInstance());
       if (!GLSurfaceEGL::InitializeOneOff(
               EGLDisplayPlatform(gfx::GetXDisplay()))) {
         LOG(ERROR) << "GLSurfaceEGL::InitializeOneOff failed.";
diff --git a/ui/ozone/BUILD.gn b/ui/ozone/BUILD.gn
index 7e53cb2a..3e2260f 100644
--- a/ui/ozone/BUILD.gn
+++ b/ui/ozone/BUILD.gn
@@ -85,6 +85,7 @@
     "public/ozone_switches.cc",
     "public/ozone_switches.h",
     "public/platform_clipboard.h",
+    "public/platform_gl_egl_utility.h",
     "public/platform_screen.cc",
     "public/platform_screen.h",
     "public/platform_window_surface.h",
@@ -136,6 +137,8 @@
     "//ui/base/ime/chromeos/*",
   ]
 
+  configs += [ "//third_party/khronos:khronos_headers" ]
+
   # Out of tree platforms can depend on this.
   visibility += ozone_external_platform_visibility
 }
diff --git a/ui/ozone/platform/x11/BUILD.gn b/ui/ozone/platform/x11/BUILD.gn
index f1941921..10b7bdb 100644
--- a/ui/ozone/platform/x11/BUILD.gn
+++ b/ui/ozone/platform/x11/BUILD.gn
@@ -12,6 +12,8 @@
   sources = [
     "client_native_pixmap_factory_x11.cc",
     "client_native_pixmap_factory_x11.h",
+    "gl_egl_utility_x11.cc",
+    "gl_egl_utility_x11.h",
     "gl_ozone_glx.cc",
     "gl_ozone_glx.h",
     "gl_surface_egl_ozone_x11.cc",
@@ -50,6 +52,7 @@
     "//ui/base/clipboard:clipboard_types",
     "//ui/base/ime",
     "//ui/base/x",
+    "//ui/base/x:gl",
     "//ui/display/fake",
     "//ui/events",
     "//ui/events/devices",
diff --git a/ui/ozone/platform/x11/gl_egl_utility_x11.cc b/ui/ozone/platform/x11/gl_egl_utility_x11.cc
new file mode 100644
index 0000000..5b6673be5
--- /dev/null
+++ b/ui/ozone/platform/x11/gl_egl_utility_x11.cc
@@ -0,0 +1,25 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/ozone/platform/x11/gl_egl_utility_x11.h"
+
+#include "ui/base/x/x11_gl_egl_utility.h"
+
+namespace ui {
+
+GLEGLUtilityX11::GLEGLUtilityX11() = default;
+GLEGLUtilityX11::~GLEGLUtilityX11() = default;
+
+void GLEGLUtilityX11::GetAdditionalEGLAttributes(
+    EGLenum platform_type,
+    std::vector<EGLAttrib>* display_attributes) {
+  GetPlatformExtraDisplayAttribs(platform_type, display_attributes);
+}
+
+void GLEGLUtilityX11::ChooseEGLAlphaAndBufferSize(EGLint* alpha_size,
+                                                  EGLint* buffer_size) {
+  ChoosePlatformCustomAlphaAndBufferSize(alpha_size, buffer_size);
+}
+
+}  // namespace ui
diff --git a/ui/ozone/platform/x11/gl_egl_utility_x11.h b/ui/ozone/platform/x11/gl_egl_utility_x11.h
new file mode 100644
index 0000000..e9a9981b
--- /dev/null
+++ b/ui/ozone/platform/x11/gl_egl_utility_x11.h
@@ -0,0 +1,30 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_PLATFORM_X11_GL_EGL_UTILITY_X11_H_
+#define UI_OZONE_PLATFORM_X11_GL_EGL_UTILITY_X11_H_
+
+#include "ui/ozone/public/platform_gl_egl_utility.h"
+
+namespace ui {
+
+// Allows EGL to ask platforms for platform specific EGL attributes.
+class GLEGLUtilityX11 : public PlatformGLEGLUtility {
+ public:
+  GLEGLUtilityX11();
+  ~GLEGLUtilityX11() override;
+  GLEGLUtilityX11(const GLEGLUtilityX11& util) = delete;
+  GLEGLUtilityX11& operator=(const GLEGLUtilityX11& util) = delete;
+
+  // PlatformGLEGLUtility overrides:
+  void GetAdditionalEGLAttributes(
+      EGLenum platform_type,
+      std::vector<EGLAttrib>* display_attributes) override;
+  void ChooseEGLAlphaAndBufferSize(EGLint* alpha_size,
+                                   EGLint* buffer_size) override;
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_X11_GL_EGL_UTILITY_X11_H_
diff --git a/ui/ozone/platform/x11/ozone_platform_x11.cc b/ui/ozone/platform/x11/ozone_platform_x11.cc
index 78ea2a9..a53c88d 100644
--- a/ui/ozone/platform/x11/ozone_platform_x11.cc
+++ b/ui/ozone/platform/x11/ozone_platform_x11.cc
@@ -21,6 +21,7 @@
 #include "ui/gfx/x/x11_connection.h"
 #include "ui/gfx/x/x11_types.h"
 #include "ui/ozone/common/stub_overlay_manager.h"
+#include "ui/ozone/platform/x11/gl_egl_utility_x11.h"
 #include "ui/ozone/platform/x11/x11_clipboard_ozone.h"
 #include "ui/ozone/platform/x11/x11_cursor_factory_ozone.h"
 #include "ui/ozone/platform/x11/x11_screen_ozone.h"
@@ -122,6 +123,10 @@
     return clipboard_.get();
   }
 
+  PlatformGLEGLUtility* GetPlatformGLEGLUtility() override {
+    return gl_egl_utility_.get();
+  }
+
   std::unique_ptr<InputMethod> CreateInputMethod(
       internal::InputMethodDelegate* delegate,
       gfx::AcceleratedWidget) override {
@@ -180,6 +185,7 @@
       CreatePlatformEventSource();
 
     surface_factory_ozone_ = std::make_unique<X11SurfaceFactory>();
+    gl_egl_utility_ = std::make_unique<GLEGLUtilityX11>();
   }
 
  private:
@@ -228,6 +234,7 @@
 
   // Objects in the GPU process.
   std::unique_ptr<X11SurfaceFactory> surface_factory_ozone_;
+  std::unique_ptr<GLEGLUtilityX11> gl_egl_utility_;
 
   // Objects in both UI and GPU process.
   std::unique_ptr<X11EventSource> event_source_;
diff --git a/ui/ozone/public/ozone_platform.cc b/ui/ozone/public/ozone_platform.cc
index 35bae77..c160e43e 100644
--- a/ui/ozone/public/ozone_platform.cc
+++ b/ui/ozone/public/ozone_platform.cc
@@ -87,6 +87,10 @@
   return nullptr;
 }
 
+PlatformGLEGLUtility* OzonePlatform::GetPlatformGLEGLUtility() {
+  return nullptr;
+}
+
 bool OzonePlatform::IsNativePixmapConfigSupported(
     gfx::BufferFormat format,
     gfx::BufferUsage usage) const {
diff --git a/ui/ozone/public/ozone_platform.h b/ui/ozone/public/ozone_platform.h
index c1d82b8b..2d9b665 100644
--- a/ui/ozone/public/ozone_platform.h
+++ b/ui/ozone/public/ozone_platform.h
@@ -36,6 +36,7 @@
 class SurfaceFactoryOzone;
 class SystemInputInjector;
 class PlatformClipboard;
+class PlatformGLEGLUtility;
 
 namespace internal {
 class InputMethodDelegate;
@@ -152,6 +153,7 @@
   virtual std::unique_ptr<InputMethod> CreateInputMethod(
       internal::InputMethodDelegate* delegate,
       gfx::AcceleratedWidget widget) = 0;
+  virtual PlatformGLEGLUtility* GetPlatformGLEGLUtility();
 
   // Returns true if the specified buffer format is supported.
   virtual bool IsNativePixmapConfigSupported(gfx::BufferFormat format,
diff --git a/ui/ozone/public/platform_gl_egl_utility.h b/ui/ozone/public/platform_gl_egl_utility.h
new file mode 100644
index 0000000..704dc178
--- /dev/null
+++ b/ui/ozone/public/platform_gl_egl_utility.h
@@ -0,0 +1,34 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_PUBLIC_PLATFORM_GL_EGL_UTILITY_H_
+#define UI_OZONE_PUBLIC_PLATFORM_GL_EGL_UTILITY_H_
+
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include "base/component_export.h"
+#include "third_party/khronos/EGL/egl.h"
+
+namespace ui {
+
+// Provides platform specific EGL attributes/configs.
+class COMPONENT_EXPORT(OZONE_BASE) PlatformGLEGLUtility {
+ public:
+  virtual ~PlatformGLEGLUtility() = default;
+
+  // Gets additional display attributes based on |platform_type|.
+  virtual void GetAdditionalEGLAttributes(
+      EGLenum platform_type,
+      std::vector<EGLAttrib>* display_attributes) = 0;
+
+  // Chooses alpha and buffer size values.
+  virtual void ChooseEGLAlphaAndBufferSize(EGLint* alpha_size,
+                                           EGLint* buffer_size) = 0;
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PUBLIC_PLATFORM_GL_EGL_UTILITY_H_
diff --git a/ui/views/controls/label.cc b/ui/views/controls/label.cc
index c15c27a..f389c326 100644
--- a/ui/views/controls/label.cc
+++ b/ui/views/controls/label.cc
@@ -895,9 +895,9 @@
 
 bool Label::IsCommandIdEnabled(int command_id) const {
   switch (command_id) {
-    case IDS_APP_COPY:
+    case MenuCommands::kCopy:
       return HasSelection() && !GetObscured();
-    case IDS_APP_SELECT_ALL:
+    case MenuCommands::kSelectAll:
       return GetRenderTextForSelectionController() && !GetText().empty();
   }
   return false;
@@ -905,10 +905,10 @@
 
 void Label::ExecuteCommand(int command_id, int event_flags) {
   switch (command_id) {
-    case IDS_APP_COPY:
+    case MenuCommands::kCopy:
       CopyToClipboard();
       break;
-    case IDS_APP_SELECT_ALL:
+    case MenuCommands::kSelectAll:
       SelectAll();
       DCHECK(HasSelection());
       UpdateSelectionClipboard();
@@ -921,11 +921,11 @@
 bool Label::GetAcceleratorForCommandId(int command_id,
                                        ui::Accelerator* accelerator) const {
   switch (command_id) {
-    case IDS_APP_COPY:
+    case MenuCommands::kCopy:
       *accelerator = ui::Accelerator(ui::VKEY_C, ui::EF_CONTROL_DOWN);
       return true;
 
-    case IDS_APP_SELECT_ALL:
+    case MenuCommands::kSelectAll:
       *accelerator = ui::Accelerator(ui::VKEY_A, ui::EF_CONTROL_DOWN);
       return true;
 
@@ -1101,8 +1101,8 @@
 }
 
 void Label::BuildContextMenuContents() {
-  context_menu_contents_.AddItemWithStringId(IDS_APP_COPY, IDS_APP_COPY);
-  context_menu_contents_.AddItemWithStringId(IDS_APP_SELECT_ALL,
+  context_menu_contents_.AddItemWithStringId(MenuCommands::kCopy, IDS_APP_COPY);
+  context_menu_contents_.AddItemWithStringId(MenuCommands::kSelectAll,
                                              IDS_APP_SELECT_ALL);
 }
 
diff --git a/ui/views/controls/label.h b/ui/views/controls/label.h
index 4cb533c..a95ecdf9 100644
--- a/ui/views/controls/label.h
+++ b/ui/views/controls/label.h
@@ -35,6 +35,12 @@
  public:
   METADATA_HEADER(Label);
 
+  enum MenuCommands {
+    kCopy = 1,
+    kSelectAll,
+    kLastCommandId = kSelectAll,
+  };
+
   // Helper to construct a Label that doesn't use the views typography spec.
   // Using this causes Label to obtain colors from ui::NativeTheme and line
   // spacing from gfx::FontList::GetHeight().
diff --git a/ui/views/controls/label_unittest.cc b/ui/views/controls/label_unittest.cc
index d3f01a1..763a169 100644
--- a/ui/views/controls/label_unittest.cc
+++ b/ui/views/controls/label_unittest.cc
@@ -1384,38 +1384,38 @@
   label()->SetText(ASCIIToUTF16("Label context menu"));
   label()->SizeToPreferredSize();
 
-  // A non-selectable label would not show a context menu and both COPY and
-  // SELECT_ALL context menu items should be disabled for it.
-  EXPECT_FALSE(IsMenuCommandEnabled(IDS_APP_COPY));
-  EXPECT_FALSE(IsMenuCommandEnabled(IDS_APP_SELECT_ALL));
+  // A non-selectable label should not show a context menu and both copy and
+  // select-all context menu items should be disabled for it.
+  EXPECT_FALSE(IsMenuCommandEnabled(Label::MenuCommands::kCopy));
+  EXPECT_FALSE(IsMenuCommandEnabled(Label::MenuCommands::kSelectAll));
 
-  // For a selectable label with no selection, only SELECT_ALL should be
+  // For a selectable label with no selection, only kSelectAll should be
   // enabled.
   ASSERT_TRUE(label()->SetSelectable(true));
-  EXPECT_FALSE(IsMenuCommandEnabled(IDS_APP_COPY));
-  EXPECT_TRUE(IsMenuCommandEnabled(IDS_APP_SELECT_ALL));
+  EXPECT_FALSE(IsMenuCommandEnabled(Label::MenuCommands::kCopy));
+  EXPECT_TRUE(IsMenuCommandEnabled(Label::MenuCommands::kSelectAll));
 
-  // For a selectable label with a selection, both COPY and SELECT_ALL should be
-  // enabled.
+  // For a selectable label with a selection, both copy and select-all should
+  // be enabled.
   label()->SelectRange(gfx::Range(0, 4));
-  EXPECT_TRUE(IsMenuCommandEnabled(IDS_APP_COPY));
-  EXPECT_TRUE(IsMenuCommandEnabled(IDS_APP_SELECT_ALL));
-  // Ensure unsupported commands like PASTE are not enabled.
-  EXPECT_FALSE(IsMenuCommandEnabled(IDS_APP_PASTE));
+  EXPECT_TRUE(IsMenuCommandEnabled(Label::MenuCommands::kCopy));
+  EXPECT_TRUE(IsMenuCommandEnabled(Label::MenuCommands::kSelectAll));
+  // Ensure unsupported commands are not enabled.
+  EXPECT_FALSE(IsMenuCommandEnabled(Label::MenuCommands::kLastCommandId + 1));
 
-  // An obscured label would not show a context menu and both COPY and
-  // SELECT_ALL should be disabled for it.
+  // An obscured label would not show a context menu and both copy and
+  // select-all should be disabled for it.
   label()->SetObscured(true);
   EXPECT_FALSE(label()->GetSelectable());
-  EXPECT_FALSE(IsMenuCommandEnabled(IDS_APP_COPY));
-  EXPECT_FALSE(IsMenuCommandEnabled(IDS_APP_SELECT_ALL));
+  EXPECT_FALSE(IsMenuCommandEnabled(Label::MenuCommands::kCopy));
+  EXPECT_FALSE(IsMenuCommandEnabled(Label::MenuCommands::kSelectAll));
   label()->SetObscured(false);
 
-  // For an empty label, both COPY and SELECT_ALL should be disabled.
+  // For an empty label, both copy and select-all should be disabled.
   label()->SetText(base::string16());
   ASSERT_TRUE(label()->SetSelectable(true));
-  EXPECT_FALSE(IsMenuCommandEnabled(IDS_APP_COPY));
-  EXPECT_FALSE(IsMenuCommandEnabled(IDS_APP_SELECT_ALL));
+  EXPECT_FALSE(IsMenuCommandEnabled(Label::MenuCommands::kCopy));
+  EXPECT_FALSE(IsMenuCommandEnabled(Label::MenuCommands::kSelectAll));
 }
 
 }  // namespace views
diff --git a/ui/views/controls/textfield/textfield.cc b/ui/views/controls/textfield/textfield.cc
index 61b11e53..c1d5e3e4 100644
--- a/ui/views/controls/textfield/textfield.cc
+++ b/ui/views/controls/textfield/textfield.cc
@@ -215,20 +215,20 @@
 ui::TextEditCommand GetTextEditCommandFromMenuCommand(int command_id,
                                                       bool has_selection) {
   switch (command_id) {
-    case IDS_APP_UNDO:
+    case Textfield::kUndo:
       return ui::TextEditCommand::UNDO;
-    case IDS_APP_CUT:
+    case Textfield::kCut:
       return ui::TextEditCommand::CUT;
-    case IDS_APP_COPY:
+    case Textfield::kCopy:
       return ui::TextEditCommand::COPY;
-    case IDS_APP_PASTE:
+    case Textfield::kPaste:
       return ui::TextEditCommand::PASTE;
-    case IDS_APP_DELETE:
+    case Textfield::kDelete:
       // The DELETE menu action only works in case of an active selection.
       if (has_selection)
         return ui::TextEditCommand::DELETE_FORWARD;
       break;
-    case IDS_APP_SELECT_ALL:
+    case Textfield::kSelectAll:
       return ui::TextEditCommand::SELECT_ALL;
   }
   return ui::TextEditCommand::INVALID_COMMAND;
@@ -1377,23 +1377,23 @@
 bool Textfield::GetAcceleratorForCommandId(int command_id,
                                            ui::Accelerator* accelerator) const {
   switch (command_id) {
-    case IDS_APP_UNDO:
+    case kUndo:
       *accelerator = ui::Accelerator(ui::VKEY_Z, ui::EF_PLATFORM_ACCELERATOR);
       return true;
 
-    case IDS_APP_CUT:
+    case kCut:
       *accelerator = ui::Accelerator(ui::VKEY_X, ui::EF_PLATFORM_ACCELERATOR);
       return true;
 
-    case IDS_APP_COPY:
+    case kCopy:
       *accelerator = ui::Accelerator(ui::VKEY_C, ui::EF_PLATFORM_ACCELERATOR);
       return true;
 
-    case IDS_APP_PASTE:
+    case kPaste:
       *accelerator = ui::Accelerator(ui::VKEY_V, ui::EF_PLATFORM_ACCELERATOR);
       return true;
 
-    case IDS_APP_SELECT_ALL:
+    case kSelectAll:
       *accelerator = ui::Accelerator(ui::VKEY_A, ui::EF_PLATFORM_ACCELERATOR);
       return true;
 
@@ -2339,15 +2339,14 @@
   context_menu_contents_.reset();
 
   context_menu_contents_ = std::make_unique<ui::SimpleMenuModel>(this);
-  context_menu_contents_->AddItemWithStringId(IDS_APP_UNDO, IDS_APP_UNDO);
+  context_menu_contents_->AddItemWithStringId(kUndo, IDS_APP_UNDO);
   context_menu_contents_->AddSeparator(ui::NORMAL_SEPARATOR);
-  context_menu_contents_->AddItemWithStringId(IDS_APP_CUT, IDS_APP_CUT);
-  context_menu_contents_->AddItemWithStringId(IDS_APP_COPY, IDS_APP_COPY);
-  context_menu_contents_->AddItemWithStringId(IDS_APP_PASTE, IDS_APP_PASTE);
-  context_menu_contents_->AddItemWithStringId(IDS_APP_DELETE, IDS_APP_DELETE);
+  context_menu_contents_->AddItemWithStringId(kCut, IDS_APP_CUT);
+  context_menu_contents_->AddItemWithStringId(kCopy, IDS_APP_COPY);
+  context_menu_contents_->AddItemWithStringId(kPaste, IDS_APP_PASTE);
+  context_menu_contents_->AddItemWithStringId(kDelete, IDS_APP_DELETE);
   context_menu_contents_->AddSeparator(ui::NORMAL_SEPARATOR);
-  context_menu_contents_->AddItemWithStringId(IDS_APP_SELECT_ALL,
-                                              IDS_APP_SELECT_ALL);
+  context_menu_contents_->AddItemWithStringId(kSelectAll, IDS_APP_SELECT_ALL);
 
   // If the controller adds menu commands, also override ExecuteCommand() and
   // IsCommandIdEnabled() as appropriate, for the commands added.
diff --git a/ui/views/controls/textfield/textfield.h b/ui/views/controls/textfield/textfield.h
index 21bafd3..5dce3b04 100644
--- a/ui/views/controls/textfield/textfield.h
+++ b/ui/views/controls/textfield/textfield.h
@@ -75,6 +75,13 @@
  public:
   METADATA_HEADER(Textfield);
 
+  enum MenuCommands {
+    kUndo = kLastTouchEditableCommandId + 1,
+    kDelete,
+    kSelectAll,
+    kLastCommandId = kSelectAll,
+  };
+
   // Returns the text cursor blink time, or 0 for no blinking.
   static base::TimeDelta GetCaretBlinkInterval();
 
diff --git a/ui/views/controls/textfield/textfield_unittest.cc b/ui/views/controls/textfield/textfield_unittest.cc
index 4a7a614..8075c29 100644
--- a/ui/views/controls/textfield/textfield_unittest.cc
+++ b/ui/views/controls/textfield/textfield_unittest.cc
@@ -1283,11 +1283,11 @@
   SetClipboardText(ui::ClipboardBuffer::kCopyPaste, "foo");
 
   // Cut and copy should be disabled.
-  EXPECT_FALSE(textfield_->IsCommandIdEnabled(IDS_APP_CUT));
-  textfield_->ExecuteCommand(IDS_APP_CUT, 0);
+  EXPECT_FALSE(textfield_->IsCommandIdEnabled(Textfield::kCut));
+  textfield_->ExecuteCommand(Textfield::kCut, 0);
   SendKeyEvent(ui::VKEY_X, false, true);
-  EXPECT_FALSE(textfield_->IsCommandIdEnabled(IDS_APP_COPY));
-  textfield_->ExecuteCommand(IDS_APP_COPY, 0);
+  EXPECT_FALSE(textfield_->IsCommandIdEnabled(Textfield::kCopy));
+  textfield_->ExecuteCommand(Textfield::kCopy, 0);
   SendKeyEvent(ui::VKEY_C, false, true);
   SendAlternateCopy();
   EXPECT_STR_EQ("foo", GetClipboardText(ui::ClipboardBuffer::kCopyPaste));
@@ -1297,8 +1297,8 @@
   SendKeyEvent(ui::VKEY_DELETE, true, false);
 
   // Paste should work normally.
-  EXPECT_TRUE(textfield_->IsCommandIdEnabled(IDS_APP_PASTE));
-  textfield_->ExecuteCommand(IDS_APP_PASTE, 0);
+  EXPECT_TRUE(textfield_->IsCommandIdEnabled(Textfield::kPaste));
+  textfield_->ExecuteCommand(Textfield::kPaste, 0);
   SendKeyEvent(ui::VKEY_V, false, true);
   SendAlternatePaste();
   EXPECT_STR_EQ("foo", GetClipboardText(ui::ClipboardBuffer::kCopyPaste));
@@ -1964,24 +1964,24 @@
 
   // Cut should be disabled.
   SetClipboardText(ui::ClipboardBuffer::kCopyPaste, "Test");
-  EXPECT_FALSE(textfield_->IsCommandIdEnabled(IDS_APP_CUT));
-  textfield_->ExecuteCommand(IDS_APP_CUT, 0);
+  EXPECT_FALSE(textfield_->IsCommandIdEnabled(Textfield::kCut));
+  textfield_->ExecuteCommand(Textfield::kCut, 0);
   SendKeyEvent(ui::VKEY_X, false, true);
   SendAlternateCut();
   EXPECT_STR_EQ("Test", GetClipboardText(ui::ClipboardBuffer::kCopyPaste));
   EXPECT_STR_EQ("read only", textfield_->GetText());
 
   // Paste should be disabled.
-  EXPECT_FALSE(textfield_->IsCommandIdEnabled(IDS_APP_PASTE));
-  textfield_->ExecuteCommand(IDS_APP_PASTE, 0);
+  EXPECT_FALSE(textfield_->IsCommandIdEnabled(Textfield::kPaste));
+  textfield_->ExecuteCommand(Textfield::kPaste, 0);
   SendKeyEvent(ui::VKEY_V, false, true);
   SendAlternatePaste();
   EXPECT_STR_EQ("read only", textfield_->GetText());
 
   // Copy should work normally.
   SetClipboardText(ui::ClipboardBuffer::kCopyPaste, "Test");
-  EXPECT_TRUE(textfield_->IsCommandIdEnabled(IDS_APP_COPY));
-  textfield_->ExecuteCommand(IDS_APP_COPY, 0);
+  EXPECT_TRUE(textfield_->IsCommandIdEnabled(Textfield::kCopy));
+  textfield_->ExecuteCommand(Textfield::kCopy, 0);
   EXPECT_STR_EQ("read only", GetClipboardText(ui::ClipboardBuffer::kCopyPaste));
   SetClipboardText(ui::ClipboardBuffer::kCopyPaste, "Test");
   SendKeyEvent(ui::VKEY_C, false, true);
@@ -2297,11 +2297,11 @@
 
 TEST_F(TextfieldTest, CutCopyPaste) {
   InitTextfield();
-  // Ensure IDS_APP_CUT cuts.
+  // Ensure kCut cuts.
   textfield_->SetText(ASCIIToUTF16("123"));
   textfield_->SelectAll(false);
-  EXPECT_TRUE(textfield_->IsCommandIdEnabled(IDS_APP_CUT));
-  textfield_->ExecuteCommand(IDS_APP_CUT, 0);
+  EXPECT_TRUE(textfield_->IsCommandIdEnabled(Textfield::kCut));
+  textfield_->ExecuteCommand(Textfield::kCut, 0);
   EXPECT_STR_EQ("123", GetClipboardText(ui::ClipboardBuffer::kCopyPaste));
   EXPECT_STR_EQ("", textfield_->GetText());
   EXPECT_EQ(ui::ClipboardBuffer::kCopyPaste, GetAndResetCopiedToClipboard());
@@ -2337,11 +2337,11 @@
   EXPECT_STR_EQ("123", textfield_->GetText());
   EXPECT_EQ(ui::ClipboardBuffer::kMaxValue, GetAndResetCopiedToClipboard());
 
-  // Ensure IDS_APP_COPY copies.
+  // Ensure kCopy copies.
   textfield_->SetText(ASCIIToUTF16("789"));
   textfield_->SelectAll(false);
-  EXPECT_TRUE(textfield_->IsCommandIdEnabled(IDS_APP_COPY));
-  textfield_->ExecuteCommand(IDS_APP_COPY, 0);
+  EXPECT_TRUE(textfield_->IsCommandIdEnabled(Textfield::kCopy));
+  textfield_->ExecuteCommand(Textfield::kCopy, 0);
   EXPECT_STR_EQ("789", GetClipboardText(ui::ClipboardBuffer::kCopyPaste));
   EXPECT_EQ(ui::ClipboardBuffer::kCopyPaste, GetAndResetCopiedToClipboard());
 
@@ -2363,12 +2363,12 @@
   EXPECT_STR_EQ("345", textfield_->GetText());
   EXPECT_EQ(ui::ClipboardBuffer::kCopyPaste, GetAndResetCopiedToClipboard());
 
-  // Ensure IDS_APP_PASTE, [Ctrl]+[V], and [Shift]+[Insert] pastes;
+  // Ensure kPaste, [Ctrl]+[V], and [Shift]+[Insert] pastes;
   // also ensure that [Ctrl]+[Alt]+[V] does nothing.
   SetClipboardText(ui::ClipboardBuffer::kCopyPaste, "abc");
   textfield_->SetText(base::string16());
-  EXPECT_TRUE(textfield_->IsCommandIdEnabled(IDS_APP_PASTE));
-  textfield_->ExecuteCommand(IDS_APP_PASTE, 0);
+  EXPECT_TRUE(textfield_->IsCommandIdEnabled(Textfield::kPaste));
+  textfield_->ExecuteCommand(Textfield::kPaste, 0);
   EXPECT_STR_EQ("abc", textfield_->GetText());
   SendKeyEvent(ui::VKEY_V, false, true);
   EXPECT_STR_EQ("abcabc", textfield_->GetText());
diff --git a/ui/views/touchui/touch_selection_menu_views.cc b/ui/views/touchui/touch_selection_menu_views.cc
index ca02bef4..8b014f1 100644
--- a/ui/views/touchui/touch_selection_menu_views.cc
+++ b/ui/views/touchui/touch_selection_menu_views.cc
@@ -10,6 +10,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "ui/aura/window.h"
 #include "ui/base/l10n/l10n_util.h"
+#include "ui/base/pointer/touch_editing_controller.h"
 #include "ui/display/display.h"
 #include "ui/display/screen.h"
 #include "ui/gfx/canvas.h"
@@ -25,7 +26,15 @@
 namespace views {
 namespace {
 
-constexpr int kMenuCommands[] = {IDS_APP_CUT, IDS_APP_COPY, IDS_APP_PASTE};
+struct MenuCommand {
+  int command_id;
+  int message_id;
+} kMenuCommands[] = {
+    {ui::TouchEditable::kCut, IDS_APP_CUT},
+    {ui::TouchEditable::kCopy, IDS_APP_COPY},
+    {ui::TouchEditable::kPaste, IDS_APP_PASTE},
+};
+
 constexpr int kSpacingBetweenButtons = 2;
 constexpr int kEllipsesButtonTag = -1;
 
@@ -95,8 +104,8 @@
     const ui::TouchSelectionMenuClient* client) {
   DCHECK(client);
 
-  const auto is_enabled = [client](int command) {
-    return client->IsCommandIdEnabled(command);
+  const auto is_enabled = [client](MenuCommand command) {
+    return client->IsCommandIdEnabled(command.command_id);
   };
   return std::any_of(std::cbegin(kMenuCommands), std::cend(kMenuCommands),
                      is_enabled);
@@ -113,12 +122,12 @@
 TouchSelectionMenuViews::~TouchSelectionMenuViews() = default;
 
 void TouchSelectionMenuViews::CreateButtons() {
-  for (int command_id : kMenuCommands) {
-    if (!client_->IsCommandIdEnabled(command_id))
+  for (const auto& command : kMenuCommands) {
+    if (!client_->IsCommandIdEnabled(command.command_id))
       continue;
 
-    Button* button =
-        CreateButton(l10n_util::GetStringUTF16(command_id), command_id);
+    Button* button = CreateButton(l10n_util::GetStringUTF16(command.message_id),
+                                  command.command_id);
     AddChildView(button);
   }
 
diff --git a/ui/views/view_unittest.cc b/ui/views/view_unittest.cc
index 16294076..429c47dc 100644
--- a/ui/views/view_unittest.cc
+++ b/ui/views/view_unittest.cc
@@ -2012,21 +2012,21 @@
   //
 
   normal->SelectAll(false);
-  normal->ExecuteCommand(IDS_APP_CUT, 0);
+  normal->ExecuteCommand(Textfield::kCut, 0);
   base::string16 result;
   clipboard->ReadText(ui::ClipboardBuffer::kCopyPaste, &result);
   EXPECT_EQ(kNormalText, result);
   normal->SetText(kNormalText);  // Let's revert to the original content.
 
   read_only->SelectAll(false);
-  read_only->ExecuteCommand(IDS_APP_CUT, 0);
+  read_only->ExecuteCommand(Textfield::kCut, 0);
   result.clear();
   clipboard->ReadText(ui::ClipboardBuffer::kCopyPaste, &result);
   // Cut should have failed, so the clipboard content should not have changed.
   EXPECT_EQ(kNormalText, result);
 
   password->SelectAll(false);
-  password->ExecuteCommand(IDS_APP_CUT, 0);
+  password->ExecuteCommand(Textfield::kCut, 0);
   result.clear();
   clipboard->ReadText(ui::ClipboardBuffer::kCopyPaste, &result);
   // Cut should have failed, so the clipboard content should not have changed.
@@ -2038,19 +2038,19 @@
 
   // Start with |read_only| to observe a change in clipboard text.
   read_only->SelectAll(false);
-  read_only->ExecuteCommand(IDS_APP_COPY, 0);
+  read_only->ExecuteCommand(Textfield::kCopy, 0);
   result.clear();
   clipboard->ReadText(ui::ClipboardBuffer::kCopyPaste, &result);
   EXPECT_EQ(kReadOnlyText, result);
 
   normal->SelectAll(false);
-  normal->ExecuteCommand(IDS_APP_COPY, 0);
+  normal->ExecuteCommand(Textfield::kCopy, 0);
   result.clear();
   clipboard->ReadText(ui::ClipboardBuffer::kCopyPaste, &result);
   EXPECT_EQ(kNormalText, result);
 
   password->SelectAll(false);
-  password->ExecuteCommand(IDS_APP_COPY, 0);
+  password->ExecuteCommand(Textfield::kCopy, 0);
   result.clear();
   clipboard->ReadText(ui::ClipboardBuffer::kCopyPaste, &result);
   // Text cannot be copied from an obscured field; the clipboard won't change.
@@ -2062,18 +2062,18 @@
 
   // Attempting to paste kNormalText in a read-only text-field should fail.
   read_only->SelectAll(false);
-  read_only->ExecuteCommand(IDS_APP_PASTE, 0);
+  read_only->ExecuteCommand(Textfield::kPaste, 0);
   EXPECT_EQ(kReadOnlyText, read_only->GetText());
 
   password->SelectAll(false);
-  password->ExecuteCommand(IDS_APP_PASTE, 0);
+  password->ExecuteCommand(Textfield::kPaste, 0);
   EXPECT_EQ(kNormalText, password->GetText());
 
   // Copy from |read_only| to observe a change in the normal textfield text.
   read_only->SelectAll(false);
-  read_only->ExecuteCommand(IDS_APP_COPY, 0);
+  read_only->ExecuteCommand(Textfield::kCopy, 0);
   normal->SelectAll(false);
-  normal->ExecuteCommand(IDS_APP_PASTE, 0);
+  normal->ExecuteCommand(Textfield::kPaste, 0);
   EXPECT_EQ(kReadOnlyText, normal->GetText());
   widget->CloseNow();
 }
diff --git a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/ExternalNavigationTest.java b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/ExternalNavigationTest.java
index 3551667..9777941 100644
--- a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/ExternalNavigationTest.java
+++ b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/ExternalNavigationTest.java
@@ -61,6 +61,8 @@
             "link_with_intent_to_chrome_in_new_tab.html";
     private static final String PAGE_THAT_INTENTS_TO_CHROME_ON_LOAD_FILE =
             "page_that_intents_to_chrome_on_load.html";
+    private static final String LINK_TO_PAGE_THAT_INTENTS_TO_CHROME_ON_LOAD_FILE =
+            "link_to_page_that_intents_to_chrome_on_load.html";
 
     // The test server handles "echo" with a response containing "Echo" :).
     private final String mTestServerSiteUrl = mActivityTestRule.getTestServer().getURL("/echo");
@@ -395,4 +397,50 @@
         Assert.assertEquals(INTENT_TO_CHROME_ACTION, intent.getAction());
         Assert.assertEquals(INTENT_TO_CHROME_DATA_STRING, intent.getDataString());
     }
+
+    /**
+     * Tests the following flow:
+     * - The user clicks on a link
+     * - This link goes to a page that loads a handleable intent in onload()
+     * This flow should result in the external intent being launched rather than blocked,
+     * because the initial navigation to the page did not occur via user typing.
+     */
+    @Test
+    @SmallTest
+    public void testUserClicksLinkToPageWithExternalIntentLaunchedViaOnLoad() throws Throwable {
+        InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(ABOUT_BLANK_URL);
+        IntentInterceptor intentInterceptor = new IntentInterceptor();
+        activity.setIntentInterceptor(intentInterceptor);
+
+        String url =
+                mActivityTestRule.getTestDataURL(LINK_TO_PAGE_THAT_INTENTS_TO_CHROME_ON_LOAD_FILE);
+
+        mActivityTestRule.navigateAndWait(url);
+
+        // Clicking on the link on this page should result in a navigation to the page that loads an
+        // intent in onLoad(), followed by a launching of that intent.
+        Tab tab = mActivityTestRule.getActivity().getTab();
+        String finalUrl =
+                mActivityTestRule.getTestDataURL(PAGE_THAT_INTENTS_TO_CHROME_ON_LOAD_FILE);
+        NavigationWaiter waiter =
+                new NavigationWaiter(finalUrl, tab, /*expectFailure=*/false, /*waitForPaint=*/true);
+
+        mActivityTestRule.executeScriptSync(
+                "document.onclick = function() {document.getElementById('link').click()}",
+                true /* useSeparateIsolate */);
+        EventUtils.simulateTouchCenterOfView(
+                mActivityTestRule.getActivity().getWindow().getDecorView());
+
+        waiter.waitForNavigation();
+
+        intentInterceptor.waitForIntent();
+
+        // The current URL should not have changed, and the intent should have been launched.
+        Assert.assertEquals(finalUrl, mActivityTestRule.getCurrentDisplayUrl());
+        Intent intent = intentInterceptor.mLastIntent;
+        Assert.assertNotNull(intent);
+        Assert.assertEquals(INTENT_TO_CHROME_PACKAGE, intent.getPackage());
+        Assert.assertEquals(INTENT_TO_CHROME_ACTION, intent.getAction());
+        Assert.assertEquals(INTENT_TO_CHROME_DATA_STRING, intent.getDataString());
+    }
 }
diff --git a/weblayer/test/data/link_to_page_that_intents_to_chrome_on_load.html b/weblayer/test/data/link_to_page_that_intents_to_chrome_on_load.html
new file mode 100644
index 0000000..d5e024b
--- /dev/null
+++ b/weblayer/test/data/link_to_page_that_intents_to_chrome_on_load.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <meta name="viewport"
+    content="width=device-width, initial-scale=1.0, maximum-scale=1.0" />
+</head>
+<body>
+  <a id='link' href='/weblayer/test/data/page_that_intents_to_chrome_on_load.html'>
+  Click to go to page that intents to Chrome on load
+</a>
+</body>
+</html>