diff --git a/DEPS b/DEPS
index ce290a9..15f06b9 100644
--- a/DEPS
+++ b/DEPS
@@ -275,19 +275,19 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': 'd78d52a86fa3c646a96cc21a149a4e42467f2871',
+  'skia_revision': 'b0831daddc8d12df16dd4d9b2a17686a4e92b15f',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
-  'v8_revision': 'd942c5c6ba97760ea343ec8043217ee99c59dfcd',
+  'v8_revision': 'd09bd16635919befeb7d86e768285e2ad2d9b5b5',
   # 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': '54e08a5af1f7ca50d5c154a09cc056166a9e49fa',
+  'angle_revision': 'cd80b511ee47200db2c12224a773bc4eb179ab55',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
-  'swiftshader_revision': '39495e438601c5fb4325014ae893b5e8ddc03722',
+  'swiftshader_revision': '5ca5b0ae5a7d0a2f05538d405426a5331ffd680b',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
@@ -390,7 +390,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': '09373989ecf3bb551565f2caf7016cb7ed0b7957',
+  'dawn_revision': 'e6c03a3799922151e821c57a69c78b5e422ac2a5',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -747,7 +747,7 @@
     Var('chromium_git') + '/external/github.com/toji/webvr.info.git' + '@' + 'c58ae99b9ff9e2aa4c524633519570bf33536248',
 
   'src/docs/website': {
-    'url': Var('chromium_git') + '/website.git' + '@' + 'c9e8aafa630691bee4a600eda757c133e7ecee5a',
+    'url': Var('chromium_git') + '/website.git' + '@' + '1da38cece144a8f2bcbb6f0f1d17486be448f887',
   },
 
   'src/ios/third_party/earl_grey2/src': {
@@ -756,7 +756,7 @@
   },
 
   'src/ios/third_party/edo/src': {
-      'url': Var('chromium_git') + '/external/github.com/google/eDistantObject.git' + '@' + 'c3ff324fbce0c09fe85efad31486c8d33df3cd64',
+      'url': Var('chromium_git') + '/external/github.com/google/eDistantObject.git' + '@' + 'a431b7fa37c84b5c5b9ca2851ae046da5b936c29',
       'condition': 'checkout_ios',
   },
 
@@ -920,7 +920,7 @@
     'packages': [
       {
           'package': 'chromium/third_party/androidx',
-          'version': 'g6lx-mVoDndE0ghMUoFVgf37Jij287NPfrraBvGw2WgC',
+          'version': 'D5bTTaUupCRV16Ip4D_XOLIU6YLCyvBCqFzYnvoKsogC',
       },
     ],
     'condition': 'checkout_android',
@@ -1131,12 +1131,12 @@
 
   # For Linux and Chromium OS.
   'src/third_party/cros_system_api': {
-      'url': Var('chromium_git') + '/chromiumos/platform2/system_api.git' + '@' + 'a3c1ad164e3eb5ca0330350c35f01f2965c91bbb',
+      'url': Var('chromium_git') + '/chromiumos/platform2/system_api.git' + '@' + 'fcfe475ad52efaeda2f78c4a6aad4bda140f57ee',
       'condition': 'checkout_linux',
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '31140af3cfe21203cd9b48135859654db4d39ca1',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '09c0c073ea80ea33335541e5179c809422fe6fb9',
 
   'src/third_party/devtools-frontend/src':
     Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'),
@@ -1290,7 +1290,7 @@
     Var('chromium_git') + '/chromium/deps/hunspell_dictionaries.git' + '@' + '41cdffd71c9948f63c7ad36e1fb0ff519aa7a37e',
 
   'src/third_party/icu':
-    Var('chromium_git') + '/chromium/deps/icu.git' + '@' + '4c1a868725d1226c13da64e3a5bfff2b8ae1a63b',
+    Var('chromium_git') + '/chromium/deps/icu.git' + '@' + '165825933050109d8331d0faa56cc9f52460fbbf',
 
   'src/third_party/icu4j': {
       'packages': [
@@ -1533,7 +1533,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' + 'd7010bdf9a07b6182f233797e6cfbf20589bfc62',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' + 'fecea55a5bc59c274e8200d06cd68260832d5591',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3',
@@ -1669,7 +1669,7 @@
       'condition': 'checkout_android',
   },
 
-  'src/third_party/vulkan-deps': '{chromium_git}/vulkan-deps@c1e419261c412e7c86a4b57a9a096ee0bbd0eb3b',
+  'src/third_party/vulkan-deps': '{chromium_git}/vulkan-deps@0899816630a9ae85789f2455fcbe9ac1b94b7438',
 
   'src/third_party/vulkan_memory_allocator':
     Var('chromium_git') + '/external/github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator.git' + '@' + 'ebe84bec02c041d28f902da0214bf442743fc907',
@@ -1708,7 +1708,7 @@
     Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + '5f05d6d5e625fe6f04903335473c5638ddf94514',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '2d4207e85a1478cf5269ca1df4c43522a089c45a',
+    Var('webrtc_git') + '/src.git' + '@' + '488dd9ad158f80cffe297cd48e91f9b646b770a1',
 
   'src/third_party/libgifcodec':
      Var('skia_git') + '/libgifcodec' + '@'+  Var('libgifcodec_revision'),
@@ -1781,7 +1781,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@b05e115e6fbfd8999958713238909faa8e41d879',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@5a341e08cea8e876756d576036550e1271cd4ea0',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/WATCHLISTS b/WATCHLISTS
index ffe79de9..3c6d47f0 100644
--- a/WATCHLISTS
+++ b/WATCHLISTS
@@ -2470,7 +2470,8 @@
     'content_worker': ['blink-worker-reviews@chromium.org',
                        'kinuko+watch@chromium.org'],
     'contextual_search': ['donnd+watch@chromium.org',
-                          'twellington+watch@chromium.org'],
+                          'twellington+watch@chromium.org',
+                          'gangwu+watch@chromium.org'],
     'core_web_vitals_plm': ['core-web-vitals-plm-reviews@chromium.org'],
     'core_web_vitals_wpt': ['chrome-speed-metrics-core+watchlist@google.com',
                             'lighthouse-eng-external+watch-speed-metrics@google.com'],
diff --git a/android_webview/BUILD.gn b/android_webview/BUILD.gn
index d71a086..85e858d 100644
--- a/android_webview/BUILD.gn
+++ b/android_webview/BUILD.gn
@@ -69,15 +69,18 @@
   }
 }
 
-# This version of the WebView APK doesn't include WebLayer java and resources.
+# This version of the WebView APK doesn't include WebLayer.
 # It's used to define the allowlist of resources to be pulled out of language
 # splits. See |shared_resources_allowlist_target|.
 standalone_system_webview_apk_tmpl("system_webview_no_weblayer_apk") {
   exclude_weblayer_java = true
   apk_name = "SystemWebViewNoWebLayer"
 
+  # Don't include any code to speed up compilation. This is used only for the
+  # resources allowlist.
   include_32_bit_webview = false
   include_64_bit_webview = false
+  omit_dex = true
 
   # Adding deps on recycler view in the base WebView APK will end up keeping the
   # Java in the base APK instead of the WebLayer DFM, even though it is not
diff --git a/android_webview/system_webview_apk_tmpl.gni b/android_webview/system_webview_apk_tmpl.gni
index 86f8504..8b3da5cb 100644
--- a/android_webview/system_webview_apk_tmpl.gni
+++ b/android_webview/system_webview_apk_tmpl.gni
@@ -45,6 +45,8 @@
                              "include_64_bit_webview",
                            ])
 
+    _omit_dex = defined(omit_dex) && omit_dex
+
     deps += [
       "//android_webview:locale_pak_assets",
       "//android_webview:pak_file_assets",
@@ -80,7 +82,9 @@
       }
     }
 
-    product_config_java_packages = [ webview_product_config_java_package ]
+    if (!_omit_dex) {
+      product_config_java_packages = [ webview_product_config_java_package ]
+    }
 
     if (webview_includes_weblayer) {
       if (_is_bundle_module) {
@@ -88,7 +92,9 @@
       } else {
         deps += [ "//weblayer:locale_pak_assets" ]
       }
-      product_config_java_packages += [ weblayer_product_config_java_package ]
+      if (!_omit_dex) {
+        product_config_java_packages += [ weblayer_product_config_java_package ]
+      }
     }
 
     if (!defined(alternative_android_sdk_dep)) {
@@ -106,14 +112,18 @@
         "If trichrome library is used, static_library_provider must be set " +
             "so that a dep can be added on the library APK.")
 
+    _include_32_bit_webview = !defined(invoker.include_32_bit_webview) ||
+                              invoker.include_32_bit_webview
+    if (android_64bit_target_cpu) {
+      _include_64_bit_webview = !defined(invoker.include_64_bit_webview) ||
+                                invoker.include_64_bit_webview
+    }
+
     # Pure 32-bit implies a 32-bit only Webview built on a 64-bit configuration.
-    _pure_32_bit =
-        android_64bit_target_cpu && defined(invoker.include_64_bit_webview) &&
-        !invoker.include_64_bit_webview
-    _pure_64_bit =
-        android_64bit_target_cpu && defined(invoker.include_32_bit_webview) &&
-        !invoker.include_32_bit_webview
+    _pure_32_bit = android_64bit_target_cpu && !_include_64_bit_webview
+    _pure_64_bit = android_64bit_target_cpu && !_include_32_bit_webview
     not_needed([
+                 "_include_32_bit_webview",
                  "_pure_32_bit",
                  "_pure_64_bit",
                ])
@@ -125,7 +135,8 @@
     if (!_use_trichrome_library) {
       shared_resources = true
 
-      if (!android_64bit_target_cpu || !_pure_32_bit) {
+      if ((!android_64bit_target_cpu && _include_32_bit_webview) ||
+          (android_64bit_target_cpu && !_pure_32_bit)) {
         shared_libraries = [ "//android_webview:libwebviewchromium" ]
         _include_primary_support = true
       }
@@ -143,16 +154,26 @@
       if (android_64bit_target_cpu) {
         if (invoker.is_64_bit_browser) {
           native_lib_placeholders = [ "libdummy.so" ]
-          if (invoker.include_32_bit_webview) {
+          if (_include_32_bit_webview) {
             secondary_abi_shared_libraries = [ "//android_webview:monochrome_64($android_secondary_abi_toolchain)" ]
             _include_secondary_support = true
           }
         } else {
-          if (invoker.include_64_bit_webview) {
+          if (_include_64_bit_webview) {
             shared_libraries = [ "//android_webview:monochrome" ]
             _include_primary_support = true
           }
           secondary_native_lib_placeholders = [ "libdummy.so" ]
+          static_library_provider_use_secondary_abi = true
+        }
+
+        # http://crbug.com/1042107.
+        if (is_component_build) {
+          if (invoker.is_64_bit_browser) {
+            main_component_library = "libmonochrome_64.cr.so"
+          } else {
+            main_component_library = "libmonochrome.cr.so"
+          }
         }
       } else {
         native_lib_placeholders = [ "libdummy.so" ]
@@ -246,7 +267,7 @@
       command_line_flags_file = "webview-command-line"
     }
 
-    if (!is_java_debug) {
+    if (!is_java_debug && !_omit_dex) {
       proguard_enabled = true
       if (!defined(proguard_configs)) {
         proguard_configs = []
@@ -263,13 +284,13 @@
       if (_use_trichrome_library) {
         if (android_64bit_target_cpu) {
           if (invoker.is_64_bit_browser) {
-            if (invoker.include_32_bit_webview) {
+            if (_include_32_bit_webview) {
               version_code = trichrome_64_32_version_code
             } else {
               version_code = trichrome_64_version_code
             }
           } else {
-            if (invoker.include_64_bit_webview) {
+            if (_include_64_bit_webview) {
               version_code = trichrome_32_64_version_code
             } else {
               version_code = trichrome_32_version_code
diff --git a/ash/DEPS b/ash/DEPS
index 93740f0..460ff9f 100644
--- a/ash/DEPS
+++ b/ash/DEPS
@@ -78,6 +78,8 @@
   # derived from the canonical state in the browser. Using both sources could
   # create subtle inconsistencies based on when observers are called.
   "-chromeos/login/login_state",
+  "+chromeos/ash/components/network",
+  # TODO(https://crbug.com/1164001): remove when chromeos/network have migrated.
   "+chromeos/network",
   # //ash can use the public interfaces of various services.
   "+chromeos/services/assistant/public" ,
diff --git a/ash/app_list/views/app_list_bubble_apps_page.cc b/ash/app_list/views/app_list_bubble_apps_page.cc
index 12c52abe..cd0c053 100644
--- a/ash/app_list/views/app_list_bubble_apps_page.cc
+++ b/ash/app_list/views/app_list_bubble_apps_page.cc
@@ -290,11 +290,12 @@
   // build a single animation with conditional parts. https://crbug.com/1266020
   const int section_offset = is_side_shelf ? -20 : 20;
   int vertical_offset = 0;
-  if (continue_section_->GetTasksSuggestionsCount() > 0) {
+  if (continue_section_->GetVisible() &&
+      continue_section_->GetTasksSuggestionsCount() > 0) {
     vertical_offset += section_offset;
     SlideViewIntoPosition(continue_section_, vertical_offset, slide_duration);
   }
-  if (recent_apps_->GetItemViewCount() > 0) {
+  if (recent_apps_->GetVisible() && recent_apps_->GetItemViewCount() > 0) {
     vertical_offset += section_offset;
     SlideViewIntoPosition(recent_apps_, vertical_offset, slide_duration);
   }
diff --git a/ash/app_list/views/app_list_bubble_apps_page_unittest.cc b/ash/app_list/views/app_list_bubble_apps_page_unittest.cc
index fa06601..132c2ce6 100644
--- a/ash/app_list/views/app_list_bubble_apps_page_unittest.cc
+++ b/ash/app_list/views/app_list_bubble_apps_page_unittest.cc
@@ -325,6 +325,37 @@
   EXPECT_TRUE(apps_page->separator_for_test()->GetVisible());
 }
 
+// Regression test for https://crbug.com/1329227
+TEST_F(AppListBubbleAppsPageTest, HiddenContinueSectionDoesNotAnimateOnShow) {
+  base::test::ScopedFeatureList feature_list(
+      features::kLauncherHideContinueSection);
+
+  // Simulate a user with the continue section hidden on startup.
+  Shell::Get()->app_list_controller()->SetHideContinueSection(true);
+
+  // Enable animations.
+  ui::ScopedAnimationDurationScaleMode duration(
+      ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
+
+  // Show the app list with enough items that the continue section and
+  // recent apps would normally be visible.
+  auto* helper = GetAppListTestHelper();
+  helper->AddContinueSuggestionResults(4);
+  helper->AddRecentApps(5);
+  helper->AddAppItems(5);
+  helper->ShowAppList();
+
+  // Continue section view is not visible and does not have a layer animation.
+  auto* continue_section = helper->GetBubbleContinueSectionView();
+  EXPECT_FALSE(continue_section->GetVisible());
+  EXPECT_FALSE(continue_section->layer());
+
+  // Recent apps view is not visible and does not have a layer animation.
+  auto* recent_apps = helper->GetBubbleRecentAppsView();
+  EXPECT_FALSE(recent_apps->GetVisible());
+  EXPECT_FALSE(recent_apps->layer());
+}
+
 TEST_F(AppListBubbleAppsPageTest, SortAppsMakesA11yAnnouncement) {
   auto* helper = GetAppListTestHelper();
   helper->AddAppItems(5);
diff --git a/ash/ash_strings.grd b/ash/ash_strings.grd
index 2b440b71..97e09b88 100644
--- a/ash/ash_strings.grd
+++ b/ash/ash_strings.grd
@@ -4691,9 +4691,6 @@
       <message name="IDS_APP_LIST_FOLDER_BUTTON_ACCESSIBILE_NAME" desc="The spoken feedback text for navigating to a folder button in top level app list">
         Folder <ph name="folder_name">$1<ex>OEM Apps</ex></ph>
       </message>
-      <message name="IDS_APP_LIST_FOLDER_OPEN_FOLDER_ACCESSIBILE_NAME" desc="The spoken feedback text for opening an app launcher folder">
-        Open folder
-      </message>
       <message name="IDS_APP_LIST_FOLDER_CLOSE_FOLDER_ACCESSIBILE_NAME" desc="The spoken feedback text for closing an app launcher folder">
         Close folder
       </message>
diff --git a/ash/components/arc/arc_features.cc b/ash/components/arc/arc_features.cc
index f0b7845..180f0ec3 100644
--- a/ash/components/arc/arc_features.cc
+++ b/ash/components/arc/arc_features.cc
@@ -97,11 +97,6 @@
 const base::Feature kKeyboardShortcutHelperIntegrationFeature{
     "ArcKeyboardShortcutHelperIntegration", base::FEATURE_DISABLED_BY_DEFAULT};
 
-// Controls experimental 64-bit native bridge support for ARC on boards that
-// have 64-bit native bridge support available but not yet enabled.
-const base::Feature kNativeBridge64BitSupportExperimentFeature{
-    "ArcNativeBridge64BitSupportExperiment", base::FEATURE_DISABLED_BY_DEFAULT};
-
 // Toggles between native bridge implementations for ARC.
 // Note, that we keep the original feature name to preserve
 // corresponding metrics.
diff --git a/ash/components/arc/arc_features.h b/ash/components/arc/arc_features.h
index 1729048..d576a75 100644
--- a/ash/components/arc/arc_features.h
+++ b/ash/components/arc/arc_features.h
@@ -32,7 +32,6 @@
 extern const base::Feature kLogdConfig;
 extern const base::FeatureParam<int> kLogdConfigSize;
 extern const base::Feature kKeyboardShortcutHelperIntegrationFeature;
-extern const base::Feature kNativeBridge64BitSupportExperimentFeature;
 extern const base::Feature kNativeBridgeToggleFeature;
 extern const base::Feature kOutOfProcessVideoDecoding;
 extern const base::Feature kPictureInPictureFeature;
diff --git a/ash/components/arc/arc_prefs.cc b/ash/components/arc/arc_prefs.cc
index 9f23d33..9b649df 100644
--- a/ash/components/arc/arc_prefs.cc
+++ b/ash/components/arc/arc_prefs.cc
@@ -126,13 +126,6 @@
 // Keeps the duration of the current ANR rate period.
 const char kAnrPendingDuration[] = "arc.anr_pending_duration";
 
-// A boolean preference that indicates whether this device has run with the
-// native bridge 64 bit support experiment enabled. Persisting value in local
-// state, rather than in a user profile, is required as it needs to be read
-// whenever ARC mini-container is started.
-const char kNativeBridge64BitSupportExperimentEnabled[] =
-    "arc.native_bridge_64bit_support_experiment";
-
 // A dictionary preference that keeps track of stability metric values, which is
 // maintained by StabilityMetricsManager. Persisting values in local state is
 // required to include these metrics in the initial stability log in case of a
@@ -154,8 +147,6 @@
   registry->RegisterStringPref(kArcSerialNumberSalt, std::string());
   registry->RegisterDictionaryPref(kArcSnapshotHours);
   registry->RegisterDictionaryPref(kArcSnapshotInfo);
-  registry->RegisterBooleanPref(kNativeBridge64BitSupportExperimentEnabled,
-                                false);
   registry->RegisterDictionaryPref(kStabilityMetrics);
 
   registry->RegisterIntegerPref(kAnrPendingCount, 0);
diff --git a/ash/components/arc/arc_prefs.h b/ash/components/arc/arc_prefs.h
index 289996e..d12acfc 100644
--- a/ash/components/arc/arc_prefs.h
+++ b/ash/components/arc/arc_prefs.h
@@ -52,7 +52,6 @@
 ARC_EXPORT extern const char kArcSerialNumberSalt[];
 ARC_EXPORT extern const char kArcSnapshotHours[];
 ARC_EXPORT extern const char kArcSnapshotInfo[];
-ARC_EXPORT extern const char kNativeBridge64BitSupportExperimentEnabled[];
 ARC_EXPORT extern const char kStabilityMetrics[];
 
 void RegisterLocalStatePrefs(PrefRegistrySimple* registry);
diff --git a/ash/components/arc/input_overlay/resources/com.dts.freefireth.json b/ash/components/arc/input_overlay/resources/com.dts.freefireth.json
deleted file mode 100644
index a18bf65..0000000
--- a/ash/components/arc/input_overlay/resources/com.dts.freefireth.json
+++ /dev/null
@@ -1,665 +0,0 @@
-// Copyright 2021 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.
-
-{
-  "mouse_lock": {
-    "key": "Escape"
-  },
-  "move": [
-    {
-      "id": 0,
-      "input_sources": [
-        "mouse"
-      ],
-      "name": "camera move",
-      "hide_cursor": 1,
-      "mouse_action": "hover_move",
-      "location": [
-        {
-          "type": "position",
-          "anchor_to_target": [
-            0.5,
-            0.5
-          ]
-        }
-      ],
-      "target_area": {
-        "top_left": {
-          "type": "position",
-          "anchor_to_target": [
-            0.5,
-            0
-          ]
-        },
-        "bottom_right": {
-          "type": "position",
-          "anchor_to_target": [
-            0.9999,
-            0.9999
-          ]
-        }
-      }
-    },
-    {
-      "id": 1,
-      "input_sources": [
-        "keyboard"
-      ],
-      "name": "Virtual Joystick",
-      "keys": [
-        "KeyW",
-        "KeyA",
-        "KeyS",
-        "KeyD"
-      ],
-      "location": [
-        {
-          "type": "dependent_position",
-          "aspect_ratio": 1.78,
-          "anchor": [
-            0,
-            1
-          ],
-          "anchor_to_target": [
-            0.16875,
-            -0.2472222222
-          ],
-          "x_on_y": 1.213483146,
-          "y_on_x": 0.8240740741
-        }
-      ],
-      "radius": 0.1171296296
-    }
-  ],
-  "tap": [
-    {
-      "id": 2,
-      "input_sources": [
-        "mouse"
-      ],
-      "name": "fight",
-      "mouse_action": "primary_click",
-      "location": [
-        {
-          "type": "dependent_position",
-          "aspect_ratio": 1.78,
-          "anchor": [
-            1,
-            1
-          ],
-          "anchor_to_target": [
-            -0.1796875,
-            -0.25
-          ],
-          "x_on_y": 1.277777778,
-          "y_on_x": 0.7826086957
-        }
-      ],
-      "radius": 0.0903
-    },
-    {
-      "id": 3,
-      "input_sources": [
-        "mouse"
-      ],
-      "name": "aim",
-      "mouse_action": "secondary_click",
-      "location": [
-        {
-          "type": "dependent_position",
-          "aspect_ratio": 1.78,
-          "anchor": [
-            1,
-            1
-          ],
-          "anchor_to_target": [
-            -0.0375,
-            -0.4574074074
-          ],
-          "x_on_y": 0.1457489879,
-          "y_on_x": 6.861111111
-        }
-      ],
-      "radius": 0.05555555556
-    },
-    {
-      "id": 4,
-      "input_sources": [
-        "keyboard"
-      ],
-      "name": "jump",
-      "key": "Space",
-      "location": [
-        {
-          "type": "dependent_position",
-          "aspect_ratio": 1.78,
-          "anchor": [
-            1,
-            1
-          ],
-          "anchor_to_target": [
-            -0.034375,
-            -0.3055555556
-          ],
-          "x_on_y": 0.2,
-          "y_on_x": 5
-        }
-      ],
-      "radius": 0.05555555556
-    },
-    {
-      "id": 5,
-      "input_sources": [
-        "keyboard"
-      ],
-      "name": "crouch",
-      "key": "KeyC",
-      "location": [
-        {
-          "type": "dependent_position",
-          "aspect_ratio": 1.78,
-          "anchor": [
-            1,
-            1
-          ],
-          "anchor_to_target": [
-            -0.0625,
-            -0.1083333333
-          ],
-          "x_on_y": 1.025641026,
-          "y_on_x": 0.975
-        }
-      ],
-      "radius": 0.05555555556
-    },
-    {
-      "id": 6,
-      "input_sources": [
-        "keyboard"
-      ],
-      "name": "lean",
-      "key": "KeyZ",
-      "location": [
-        {
-          "type": "dependent_position",
-          "aspect_ratio": 1.78,
-          "anchor": [
-            1,
-            1
-          ],
-          "anchor_to_target": [
-            -0.1786458333,
-            -0.075
-          ],
-          "x_on_y": 4.234567901,
-          "y_on_x": 0.2361516035
-        }
-      ],
-      "radius": 0.05555555556
-    },
-    {
-      "id": 7,
-      "input_sources": [
-        "keyboard"
-      ],
-      "name": "hand",
-      "key": "KeyQ",
-      "location": [
-        {
-          "type": "dependent_position",
-          "aspect_ratio": 1.78,
-          "anchor": [
-            1,
-            1
-          ],
-          "anchor_to_target": [
-            -0.2864583333,
-            -0.5064814815
-          ],
-          "x_on_y": 1.005484461,
-          "y_on_x": 0.9945454545
-        }
-      ],
-      "radius": 0.04675925926
-    },
-    {
-      "id": 8,
-      "input_sources": [
-        "keyboard"
-      ],
-      "name": "revive",
-      "key": "Digit5",
-      "location": [
-        {
-          "type": "dependent_position",
-          "aspect_ratio": 1.78,
-          "anchor": [
-            1,
-            1
-          ],
-          "anchor_to_target": [
-            -0.2864583333,
-            -0.3342592593
-          ],
-          "x_on_y": 1.523545706,
-          "y_on_x": 0.6563636364
-        }
-      ],
-      "radius": 0.04675925926
-    },
-    {
-      "id": 9,
-      "input_sources": [
-        "keyboard"
-      ],
-      "name": "reload",
-      "key": "KeyR",
-      "location": [
-        {
-          "type": "dependent_position",
-          "aspect_ratio": 1.78,
-          "anchor": [
-            1,
-            0
-          ],
-          "anchor_to_target": [
-            -0.09479166667,
-            0.085
-          ],
-          "x_on_y": 2,
-          "y_on_x": 0.5
-        }
-      ],
-      "radius": 0.05092592593
-    },
-    {
-      "id": 10,
-      "input_sources": [
-        "keyboard"
-      ],
-      "name": "gun1",
-      "key": "Digit1",
-      "location": [
-        {
-          "type": "dependent_position",
-          "aspect_ratio": 1.78,
-          "anchor": [
-            1,
-            0
-          ],
-          "anchor_to_target": [
-            -0.1557291667,
-            0.2009259259
-          ],
-          "x_on_y": 1.377880184,
-          "y_on_x": 0.7257525084
-        }
-      ],
-      "radius": 0.05555555556
-    },
-    {
-      "id": 11,
-      "input_sources": [
-        "keyboard"
-      ],
-      "name": "gun2",
-      "key": "Digit2",
-      "location": [
-        {
-          "type": "dependent_position",
-          "aspect_ratio": 1.78,
-          "anchor": [
-            1,
-            0
-          ],
-          "anchor_to_target": [
-            -0.09322916667,
-            0.2009259259
-          ],
-          "x_on_y": 0.8248847926,
-          "y_on_x": 1.212290503
-        }
-      ],
-      "radius": 0.05555555556
-    },
-    {
-      "id": 12,
-      "input_sources": [
-        "keyboard"
-      ],
-      "name": "gun3",
-      "key": "Digit3",
-      "location": [
-        {
-          "type": "dependent_position",
-          "aspect_ratio": 1.78,
-          "anchor": [
-            1,
-            0
-          ],
-          "anchor_to_target": [
-            -0.0296875,
-            0.2009259259
-          ],
-          "x_on_y": 0.2626728111,
-          "y_on_x": 3.807017544
-        }
-      ],
-      "radius": 0.05555555556
-    },
-    {
-      "id": 13,
-      "input_sources": [
-        "keyboard"
-      ],
-      "name": "item1",
-      "key": "KeyF",
-      "location": [
-        {
-          "type": "dependent_position",
-          "aspect_ratio": 1.78,
-          "anchor": [
-            1,
-            0
-          ],
-          "anchor_to_target": [
-            -0.2380208333,
-            0.3703703704
-          ],
-          "x_on_y": 1.1425,
-          "y_on_x": 0.875273523
-        }
-      ],
-      "radius": 0.05925925926
-    },
-    {
-      "id": 14,
-      "input_sources": [
-        "keyboard"
-      ],
-      "name": "item2",
-      "key": "KeyG",
-      "location": [
-        {
-          "type": "dependent_position",
-          "aspect_ratio": 1.78,
-          "anchor": [
-            1,
-            0
-          ],
-          "anchor_to_target": [
-            -0.1682291667,
-            0.3703703704
-          ],
-          "x_on_y": 0.8075,
-          "y_on_x": 1.238390093
-        }
-      ],
-      "radius": 0.05925925926
-    },
-    {
-      "id": 15,
-      "input_sources": [
-        "keyboard"
-      ],
-      "name": "item3",
-      "key": "KeyH",
-      "location": [
-        {
-          "type": "dependent_position",
-          "aspect_ratio": 1.78,
-          "anchor": [
-            1,
-            0
-          ],
-          "anchor_to_target": [
-            -0.1005208333,
-            0.3703703704
-          ],
-          "x_on_y": 0.4825,
-          "y_on_x": 2.07253886
-        }
-      ],
-      "radius": 0.05925925926
-    },
-    {
-      "id": 16,
-      "input_sources": [
-        "keyboard"
-      ],
-      "name": "item4",
-      "key": "KeyJ",
-      "location": [
-        {
-          "type": "dependent_position",
-          "aspect_ratio": 1.78,
-          "anchor": [
-            1,
-            0
-          ],
-          "anchor_to_target": [
-            -0.03125,
-            0.3703703704
-          ],
-          "x_on_y": 0.15,
-          "y_on_x": 6.666666667
-        }
-      ],
-      "radius": 0.05925925926
-    },
-    {
-      "id": 17,
-      "input_sources": [
-        "keyboard"
-      ],
-      "name": "map",
-      "key": "KeyM",
-      "location": [
-        {
-          "type": "dependent_position",
-          "aspect_ratio": 1.78,
-          "anchor": [
-            0,
-            0
-          ],
-          "anchor_to_target": [
-            0.078125,
-            0.1333333333
-          ],
-          "x_on_y": 1.041666667,
-          "y_on_x": 0.96
-        }
-      ],
-      "radius": 0.1
-    },
-    {
-      "id": 18,
-      "input_sources": [
-        "keyboard"
-      ],
-      "name": "mic",
-      "key": "KeyT",
-      "location": [
-        {
-          "type": "dependent_position",
-          "aspect_ratio": 1.78,
-          "anchor": [
-            0,
-            0
-          ],
-          "anchor_to_target": [
-            0.184375,
-            0.03055555556
-          ],
-          "x_on_y": 10.72727273,
-          "y_on_x": 0.09322033898
-        }
-      ],
-      "radius": 0.02824074074
-    },
-    {
-      "id": 19,
-      "input_sources": [
-        "keyboard"
-      ],
-      "name": "volumn",
-      "key": "KeyY",
-      "location": [
-        {
-          "type": "dependent_position",
-          "aspect_ratio": 1.78,
-          "anchor": [
-            0,
-            0
-          ],
-          "anchor_to_target": [
-            0.184375,
-            0.1055555556
-          ],
-          "x_on_y": 3.105263158,
-          "y_on_x": 0.3220338983
-        }
-      ],
-      "radius": 0.02824074074
-    },
-    {
-      "id": 20,
-      "input_sources": [
-        "keyboard"
-      ],
-      "name": "speed",
-      "key": "ShiftLeft",
-      "location": [
-        {
-          "type": "position",
-          "anchor": [
-            0,
-            0
-          ],
-          "anchor_to_target": [
-            0.1666666667,
-            0.4305555556
-          ],
-          "max_x": 320
-        }
-      ],
-      "radius": 0.05555555556
-    },
-    {
-      "id": 21,
-      "input_sources": [
-        "keyboard"
-      ],
-      "name": "surf",
-      "key": "ControlLeft",
-      "location": [
-        {
-          "type": "position",
-          "anchor": [
-            0,
-            0
-          ],
-          "anchor_to_target": [
-            0.3098958333,
-            0.3194444444
-          ],
-          "max_x": 595
-        }
-      ],
-      "radius": 0.05555555556
-    },
-    {
-      "id": 22,
-      "input_sources": [
-        "keyboard"
-      ],
-      "name": "fire",
-      "key": "KeyE",
-      "location": [
-        {
-          "type": "position",
-          "anchor": [
-            0,
-            0
-          ],
-          "anchor_to_target": [
-            0.2401041667,
-            0.4324074074
-          ],
-          "max_x": 461
-        }
-      ],
-      "radius": 0.05555555556
-    },
-    {
-      "id": 23,
-      "input_sources": [
-        "keyboard"
-      ],
-      "name": "bag",
-      "key": "Tab",
-      "location": [
-        {
-          "type": "position",
-          "anchor": [
-            0,
-            0
-          ],
-          "anchor_to_target": [
-            0.03072916667,
-            0.5
-          ],
-          "max_x": 60
-        }
-      ],
-      "radius": 0.0462962963
-    },
-    {
-      "id": 24,
-      "input_sources": [
-        "keyboard"
-      ],
-      "name": "heal",
-      "key": "Digit4",
-      "location": [
-        {
-          "type": "position",
-          "anchor": [
-            0,
-            0
-          ],
-          "anchor_to_target": [
-            0.03072916667,
-            0.6212962963
-          ],
-          "max_x": 60
-        }
-      ],
-      "radius": 0.0462962963
-    },
-    {
-      "id": 25,
-      "input_sources": [
-        "keyboard"
-      ],
-      "name": "bomb",
-      "key": "KeyB",
-      "location": [
-        {
-          "type": "position",
-          "anchor": [
-            0,
-            0
-          ],
-          "anchor_to_target": [
-            0.03072916667,
-            0.7222222222
-          ],
-          "max_x": 60
-        }
-      ],
-      "radius": 0.0462962963
-    }
-  ]
-}
diff --git a/ash/components/arc/input_overlay/resources/input_overlay_resources.grd b/ash/components/arc/input_overlay/resources/input_overlay_resources.grd
index 5ec08f8..bc56b14 100644
--- a/ash/components/arc/input_overlay/resources/input_overlay_resources.grd
+++ b/ash/components/arc/input_overlay/resources/input_overlay_resources.grd
@@ -16,7 +16,6 @@
        <include name="IDR_IO_ORG_CHROMIUM_ARC_TESTAPP_INPUTOVERLAY" file="org.chromium.arc.testapp.inputoverlay.json" type="BINDATA" />
        <include name="IDR_IO_COM_BLACKPANTHER_NINJAARASHI2" file="com.blackpanther.ninjaarashi2.json" type="BINDATA" />
        <include name="IDR_IO_COM_HABBY_ARCHERO" file="com.habby.archero.json" type="BINDATA" />
-       <include name="IDR_IO_COM_DTS_FREEFIRETH" file="com.dts.freefireth.json" type="BINDATA" />
        <include name="IDR_IO_COM_FINGERSOFT_HILLCLIMB" file="com.fingersoft.hillclimb.json" type="BINDATA" />
        <include name="IDR_IO_COM_ANDROBABY_GAME2048" file="com.androbaby.game2048.json" type="BINDATA" />
        <include name="IDR_IO_CO_IMBA_ARCHERO" file="co.imba.archero.json" type="BINDATA" />
diff --git a/ash/components/arc/mojom/tts.mojom b/ash/components/arc/mojom/tts.mojom
index 01667b7c..2e36f2a 100644
--- a/ash/components/arc/mojom/tts.mojom
+++ b/ash/components/arc/mojom/tts.mojom
@@ -1,7 +1,7 @@
 // Copyright 2016 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.
-// Next MinVersion: 4
+// Next MinVersion: 5
 
 module arc.mojom;
 
@@ -56,4 +56,7 @@
 
   // Sends a stop request to Android text to speech.
   Stop@2();
+
+  // Request to call OnVoicesChanged if necessary.
+  [MinVersion=4] RefreshVoices@4();
 };
diff --git a/ash/components/arc/net/arc_net_host_impl.cc b/ash/components/arc/net/arc_net_host_impl.cc
index 1e56aff..6eb6c6a8 100644
--- a/ash/components/arc/net/arc_net_host_impl.cc
+++ b/ash/components/arc/net/arc_net_host_impl.cc
@@ -22,6 +22,7 @@
 #include "base/strings/stringprintf.h"
 #include "chromeos/ash/components/dbus/patchpanel/patchpanel_client.h"
 #include "chromeos/ash/components/dbus/patchpanel/patchpanel_service.pb.h"
+#include "chromeos/ash/components/network/onc/network_onc_utils.h"
 #include "chromeos/dbus/shill/shill_manager_client.h"
 #include "chromeos/login/login_state/login_state.h"
 #include "chromeos/network/client_cert_util.h"
@@ -34,7 +35,6 @@
 #include "chromeos/network/network_state.h"
 #include "chromeos/network/network_state_handler.h"
 #include "chromeos/network/network_type_pattern.h"
-#include "chromeos/network/onc/network_onc_utils.h"
 #include "components/device_event_log/device_event_log.h"
 #include "components/prefs/pref_service.h"
 #include "components/user_manager/user_manager.h"
diff --git a/ash/constants/ash_features.cc b/ash/constants/ash_features.cc
index 5bb1fc1e..4085de3 100644
--- a/ash/constants/ash_features.cc
+++ b/ash/constants/ash_features.cc
@@ -29,6 +29,12 @@
 const base::Feature kAdaptiveCharging{"AdaptiveCharging",
                                       base::FEATURE_DISABLED_BY_DEFAULT};
 
+// Enable the logic to show the notifications for Adaptive Charging features.
+// This is intended to be used by developers to test the UI aspect of the
+// feature.
+const base::Feature kAdaptiveChargingForTesting{
+    "AdaptiveChargingForTesting", base::FEATURE_DISABLED_BY_DEFAULT};
+
 // Adjusts portrait mode split view to avoid the input field in the bottom
 // window being occluded by the virtual keyboard.
 const base::Feature kAdjustSplitViewForVK{"AdjustSplitViewForVK",
@@ -459,13 +465,6 @@
 const base::Feature kDisableIdleSocketsCloseOnMemoryPressure{
     "disable_idle_sockets_close_on_memory_pressure",
     base::FEATURE_DISABLED_BY_DEFAULT};
-
-// Disables "Office Editing for Docs, Sheets & Slides" component app so handlers
-// won't be registered, making it possible to install another version for
-// testing.
-const base::Feature kDisableOfficeEditingComponentApp{
-    "DisableOfficeEditingComponentApp", base::FEATURE_DISABLED_BY_DEFAULT};
-
 // Enables indicators to hint where displays are connected.
 const base::Feature kDisplayAlignAssist{"DisplayAlignAssist",
                                         base::FEATURE_DISABLED_BY_DEFAULT};
@@ -952,7 +951,7 @@
 // and recent apps context menu that allow the user to hide the continue
 // section.
 const base::Feature kLauncherHideContinueSection{
-    "LauncherHideContinueSection", base::FEATURE_DISABLED_BY_DEFAULT};
+    "LauncherHideContinueSection", base::FEATURE_ENABLED_BY_DEFAULT};
 
 // Uses short intervals for launcher nudge for testing if enabled.
 const base::Feature kLauncherNudgeShortInterval{
@@ -1452,7 +1451,7 @@
 // Uses new  AuthSession-based API in cryptohome to authenticate users during
 // sign-in.
 const base::Feature kUseAuthsessionAuthentication{
-    "UseAuthsessionAuthentication", base::FEATURE_ENABLED_BY_DEFAULT};
+    "UseAuthsessionAuthentication", base::FEATURE_DISABLED_BY_DEFAULT};
 
 // Enables using the BluetoothSystem Mojo interface for Bluetooth operations.
 const base::Feature kUseBluetoothSystemInAsh{"UseBluetoothSystemInAsh",
@@ -1611,6 +1610,10 @@
   return base::FeatureList::IsEnabled(kAdaptiveCharging);
 }
 
+bool IsAdaptiveChargingForTestingEnabled() {
+  return base::FeatureList::IsEnabled(kAdaptiveChargingForTesting);
+}
+
 bool IsAdjustSplitViewForVKEnabled() {
   return base::FeatureList::IsEnabled(kAdjustSplitViewForVK);
 }
diff --git a/ash/constants/ash_features.h b/ash/constants/ash_features.h
index 799bc47..9b241f86 100644
--- a/ash/constants/ash_features.h
+++ b/ash/constants/ash_features.h
@@ -20,6 +20,8 @@
 COMPONENT_EXPORT(ASH_CONSTANTS)
 extern const base::Feature kAdaptiveCharging;
 COMPONENT_EXPORT(ASH_CONSTANTS)
+extern const base::Feature kAdaptiveChargingForTesting;
+COMPONENT_EXPORT(ASH_CONSTANTS)
 extern const base::Feature kAdjustSplitViewForVK;
 COMPONENT_EXPORT(ASH_CONSTANTS) extern const base::Feature kAllowAmbientEQ;
 COMPONENT_EXPORT(ASH_CONSTANTS)
@@ -190,8 +192,6 @@
 extern const base::Feature kDisableCryptAuthV1DeviceSync;
 COMPONENT_EXPORT(ASH_CONSTANTS)
 extern const base::Feature kDisableIdleSocketsCloseOnMemoryPressure;
-COMPONENT_EXPORT(ASH_CONSTANTS)
-extern const base::Feature kDisableOfficeEditingComponentApp;
 COMPONENT_EXPORT(ASH_CONSTANTS) extern const base::Feature kDisplayAlignAssist;
 COMPONENT_EXPORT(ASH_CONSTANTS) extern const base::Feature kDockedMagnifier;
 COMPONENT_EXPORT(ASH_CONSTANTS)
@@ -618,6 +618,7 @@
 COMPONENT_EXPORT(ASH_CONSTANTS) bool AreImprovedScreenCaptureSettingsEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool DoWindowsFollowCursor();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsAdaptiveChargingEnabled();
+COMPONENT_EXPORT(ASH_CONSTANTS) bool IsAdaptiveChargingForTestingEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsAdjustSplitViewForVKEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsAllowAmbientEQEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsAmbientModeAnimationEnabled();
diff --git a/ash/constants/ash_switches.cc b/ash/constants/ash_switches.cc
index adaa940..9b777f31 100644
--- a/ash/constants/ash_switches.cc
+++ b/ash/constants/ash_switches.cc
@@ -105,12 +105,6 @@
 // See also |kArcVmUreadaheadMode|.
 const char kArcDisableUreadahead[] = "arc-disable-ureadahead";
 
-// Flag to enables an experiment to allow users to turn on 64-bit support in
-// native bridge on systems that have such support available but not yet enabled
-// by default.
-const char kArcEnableNativeBridge64BitSupportExperiment[] =
-    "arc-enable-native-bridge-64bit-support-experiment";
-
 // Flag that forces the OptIn ui to be shown. Used in tests.
 const char kArcForceShowOptInUi[] = "arc-force-show-optin-ui";
 
diff --git a/ash/constants/ash_switches.h b/ash/constants/ash_switches.h
index 9f2fd73..9bb10502 100644
--- a/ash/constants/ash_switches.h
+++ b/ash/constants/ash_switches.h
@@ -41,8 +41,6 @@
 extern const char kArcDisableMediaStoreMaintenance[];
 COMPONENT_EXPORT(ASH_CONSTANTS) extern const char kArcDisablePlayAutoInstall[];
 COMPONENT_EXPORT(ASH_CONSTANTS) extern const char kArcDisableUreadahead[];
-COMPONENT_EXPORT(ASH_CONSTANTS)
-extern const char kArcEnableNativeBridge64BitSupportExperiment[];
 COMPONENT_EXPORT(ASH_CONSTANTS) extern const char kArcForceShowOptInUi[];
 COMPONENT_EXPORT(ASH_CONSTANTS) extern const char kArcGeneratePlayAutoInstall[];
 COMPONENT_EXPORT(ASH_CONSTANTS)
diff --git a/ash/projector/projector_controller_impl.cc b/ash/projector/projector_controller_impl.cc
index 336dbb19..d2702049 100644
--- a/ash/projector/projector_controller_impl.cc
+++ b/ash/projector/projector_controller_impl.cc
@@ -282,11 +282,6 @@
   return result;
 }
 
-void ProjectorControllerImpl::OnToolSet(const AnnotatorTool& tool) {
-  // TODO(b/198184362): Reflect the annotator tool changes on the Projector
-  // toolbar.
-}
-
 void ProjectorControllerImpl::OnUndoRedoAvailabilityChanged(
     bool undo_available,
     bool redo_available) {
diff --git a/ash/projector/projector_controller_impl.h b/ash/projector/projector_controller_impl.h
index 4c772fea7..00865d7 100644
--- a/ash/projector/projector_controller_impl.h
+++ b/ash/projector/projector_controller_impl.h
@@ -75,7 +75,6 @@
   void OnSpeechRecognitionStopped() override;
   bool IsEligible() const override;
   NewScreencastPrecondition GetNewScreencastPrecondition() const override;
-  void OnToolSet(const AnnotatorTool& tool) override;
   void OnUndoRedoAvailabilityChanged(bool undo_available,
                                      bool redo_available) override;
   void OnCanvasInitialized(bool success) override;
diff --git a/ash/public/cpp/projector/projector_controller.h b/ash/public/cpp/projector/projector_controller.h
index a4bce550..e2972868d 100644
--- a/ash/public/cpp/projector/projector_controller.h
+++ b/ash/public/cpp/projector/projector_controller.h
@@ -11,7 +11,6 @@
 
 namespace ash {
 
-struct AnnotatorTool;
 struct NewScreencastPrecondition;
 
 // File extension of Projector metadata file. It is used to identify Projector
@@ -85,8 +84,6 @@
   // The following functions are callbacks from the annotator back to the
   // ProjectorController.
 
-  // Callback indicating that the annotator tool has changed.
-  virtual void OnToolSet(const AnnotatorTool& tool) = 0;
   // Callback indicating availability of undo and redo functionalities.
   virtual void OnUndoRedoAvailabilityChanged(bool undo_available,
                                              bool redo_available) = 0;
diff --git a/ash/public/cpp/test/mock_projector_controller.h b/ash/public/cpp/test/mock_projector_controller.h
index 3d9fd3a..266576b5 100644
--- a/ash/public/cpp/test/mock_projector_controller.h
+++ b/ash/public/cpp/test/mock_projector_controller.h
@@ -30,7 +30,6 @@
   MOCK_METHOD1(SetProjectorToolsVisible, void(bool is_visible));
   MOCK_CONST_METHOD0(IsEligible, bool());
   MOCK_CONST_METHOD0(GetNewScreencastPrecondition, NewScreencastPrecondition());
-  MOCK_METHOD1(OnToolSet, void(const AnnotatorTool& tool));
   MOCK_METHOD2(OnUndoRedoAvailabilityChanged,
                void(bool undo_available, bool redo_available));
   MOCK_METHOD1(OnCanvasInitialized, void(bool success));
diff --git a/ash/system/phonehub/multidevice_feature_opt_in_view.cc b/ash/system/phonehub/multidevice_feature_opt_in_view.cc
index 4557b4d..e33d8044 100644
--- a/ash/system/phonehub/multidevice_feature_opt_in_view.cc
+++ b/ash/system/phonehub/multidevice_feature_opt_in_view.cc
@@ -172,11 +172,18 @@
 
 void MultideviceFeatureOptInView::UpdateVisibility() {
   DCHECK(multidevice_feature_access_manager_);
-  // Refresh the permission status.
-  setup_mode_ = GetPermissionSetupMode(multidevice_feature_access_manager_);
+  // Refresh the permission status if changed
+  phonehub::util::PermissionsOnboardingSetUpMode current_mode =
+      GetPermissionSetupMode(multidevice_feature_access_manager_);
+  if (current_mode != setup_mode_) {
+    setup_mode_ = current_mode;
+    RefreshDescription(
+        GetDescriptionStringId(multidevice_feature_access_manager_));
+  }
   SetVisible(setup_mode_ != PermissionsOnboardingSetUpMode::kNone &&
              !multidevice_feature_access_manager_
                   ->HasMultideviceFeatureSetupUiBeenDismissed());
+  PreferredSizeChanged();
 }
 
 BEGIN_METADATA(MultideviceFeatureOptInView, views::View)
diff --git a/ash/system/phonehub/sub_feature_opt_in_view.cc b/ash/system/phonehub/sub_feature_opt_in_view.cc
index bd92576..f22d394 100644
--- a/ash/system/phonehub/sub_feature_opt_in_view.cc
+++ b/ash/system/phonehub/sub_feature_opt_in_view.cc
@@ -49,6 +49,12 @@
 
 SubFeatureOptInView::~SubFeatureOptInView() = default;
 
+void SubFeatureOptInView::RefreshDescription(int description_string_id) {
+  description_string_id_ = description_string_id;
+  text_label_->SetText(l10n_util::GetStringFUTF16(description_string_id_,
+                                                  ui::GetChromeOSDeviceName()));
+}
+
 void SubFeatureOptInView::InitLayout() {
   SetPaintToLayer();
   layer()->SetFillsBoundsOpaquely(false);
diff --git a/ash/system/phonehub/sub_feature_opt_in_view.h b/ash/system/phonehub/sub_feature_opt_in_view.h
index 5468a25..342cc9c4 100644
--- a/ash/system/phonehub/sub_feature_opt_in_view.h
+++ b/ash/system/phonehub/sub_feature_opt_in_view.h
@@ -31,6 +31,7 @@
   SubFeatureOptInView(PhoneHubViewID view_id,
                       int description_string_id,
                       int set_up_button_string_id);
+  void RefreshDescription(int description_string_id);
 
  private:
   void InitLayout();
diff --git a/ash/system/power/adaptive_charging_controller.cc b/ash/system/power/adaptive_charging_controller.cc
index 4aba183..ba58ec55 100644
--- a/ash/system/power/adaptive_charging_controller.cc
+++ b/ash/system/power/adaptive_charging_controller.cc
@@ -4,12 +4,22 @@
 
 #include "ash/system/power/adaptive_charging_controller.h"
 
+#include "ash/constants/ash_features.h"
 #include "base/scoped_observation.h"
 #include "chromeos/dbus/power/power_manager_client.h"
 #include "chromeos/dbus/power_manager/power_supply_properties.pb.h"
 
 namespace ash {
 
+namespace {
+
+#if DCHECK_IS_ON()
+// Fake input for notification testing.
+constexpr int kFakeNotificationInputForTesting = 8;
+#endif  // DCHECK_IS_ON()
+
+}  // namespace
+
 AdaptiveChargingController::AdaptiveChargingController()
     : nudge_controller_(std::make_unique<AdaptiveChargingNudgeController>()),
       notification_controller_(
@@ -29,6 +39,23 @@
 
 void AdaptiveChargingController::PowerChanged(
     const power_manager::PowerSupplyProperties& proto) {
+#if DCHECK_IS_ON()
+  if (features::IsAdaptiveChargingForTestingEnabled()) {
+    bool is_on_charger_now = false;
+    if (proto.has_external_power()) {
+      is_on_charger_now =
+          proto.external_power() == power_manager::PowerSupplyProperties::AC;
+    }
+    if (!is_on_charger_ && is_on_charger_now) {
+      nudge_controller_->ShowNudgeForTesting();  // IN-TEST
+      notification_controller_->ShowAdaptiveChargingNotification(
+          kFakeNotificationInputForTesting);
+    }
+    is_on_charger_ = is_on_charger_now;
+    return;
+  }
+#endif  // DCHECK_IS_ON()
+
   // Return if this change does not contain any adaptive_delaying_charge info.
   if (!proto.has_adaptive_delaying_charge())
     return;
diff --git a/ash/system/power/adaptive_charging_controller.h b/ash/system/power/adaptive_charging_controller.h
index 21b84cee..bc84b2f 100644
--- a/ash/system/power/adaptive_charging_controller.h
+++ b/ash/system/power/adaptive_charging_controller.h
@@ -39,6 +39,9 @@
   void PowerChanged(const power_manager::PowerSupplyProperties& proto) override;
 
   bool is_adaptive_delaying_charge_ = false;
+#if DCHECK_IS_ON()
+  bool is_on_charger_ = false;
+#endif  // DCHECK_IS_ON()
 
   base::ScopedObservation<chromeos::PowerManagerClient,
                           chromeos::PowerManagerClient::Observer>
diff --git a/ash/system/power/adaptive_charging_nudge_controller.cc b/ash/system/power/adaptive_charging_nudge_controller.cc
index ae84379..e2c32dd 100644
--- a/ash/system/power/adaptive_charging_nudge_controller.cc
+++ b/ash/system/power/adaptive_charging_nudge_controller.cc
@@ -52,6 +52,12 @@
                      weak_ptr_factory_.GetWeakPtr()));
 }
 
+#if DCHECK_IS_ON()
+void AdaptiveChargingNudgeController::ShowNudgeForTesting() {
+  SystemNudgeController::ShowNudge();
+}
+#endif  // DCHECK_IS_ON()
+
 std::unique_ptr<SystemNudge>
 AdaptiveChargingNudgeController::CreateSystemNudge() {
   return std::make_unique<AdaptiveChargingNudge>();
diff --git a/ash/system/power/adaptive_charging_nudge_controller.h b/ash/system/power/adaptive_charging_nudge_controller.h
index 01a306d..01bd107 100644
--- a/ash/system/power/adaptive_charging_nudge_controller.h
+++ b/ash/system/power/adaptive_charging_nudge_controller.h
@@ -34,6 +34,12 @@
     return nudge_delay_timer_.get();
   }
 
+#if DCHECK_IS_ON()
+  // This is intended to be used by developers to test the UI of the adaptive
+  // charging feature.
+  void ShowNudgeForTesting();
+#endif  // DCHECK_IS_ON()
+
  private:
   // SystemNudgeController:
   std::unique_ptr<SystemNudge> CreateSystemNudge() override;
diff --git a/ash/webui/diagnostics_ui/backend/network_health_provider_unittest.cc b/ash/webui/diagnostics_ui/backend/network_health_provider_unittest.cc
index 44f7f06..8c46164 100644
--- a/ash/webui/diagnostics_ui/backend/network_health_provider_unittest.cc
+++ b/ash/webui/diagnostics_ui/backend/network_health_provider_unittest.cc
@@ -12,6 +12,7 @@
 #include "base/test/scoped_feature_list.h"
 #include "base/test/task_environment.h"
 #include "base/values.h"
+#include "chromeos/ash/components/network/onc/network_onc_utils.h"
 #include "chromeos/dbus/shill/shill_ipconfig_client.h"
 #include "chromeos/login/login_state/login_state.h"
 #include "chromeos/network/managed_network_configuration_handler.h"
@@ -21,7 +22,6 @@
 #include "chromeos/network/network_handler_test_helper.h"
 #include "chromeos/network/network_state_handler.h"
 #include "chromeos/network/network_type_pattern.h"
-#include "chromeos/network/onc/network_onc_utils.h"
 #include "chromeos/network/system_token_cert_db_storage.h"
 #include "chromeos/services/network_config/cros_network_config.h"
 #include "chromeos/services/network_config/in_process_instance.h"
diff --git a/ash/webui/diagnostics_ui/resources/diagnostics_card.html b/ash/webui/diagnostics_ui/resources/diagnostics_card.html
index 3f31499..4a5f30a 100644
--- a/ash/webui/diagnostics_ui/resources/diagnostics_card.html
+++ b/ash/webui/diagnostics_ui/resources/diagnostics_card.html
@@ -5,13 +5,21 @@
   }
 
   ::slotted([slot=icon]) {
-    @apply --diagnostics-card-icon;
+    --iron-icon-fill-color: var(--cros-color-prominent);
+    --iron-icon-height: 20px;
+    --iron-icon-width: 20px;
+    background-color: var(--cros-highlight-color);
+    border-radius: 50%;
     margin: 0 20px;
+    padding: 8px;
     position: relative;
   }
 
   ::slotted([slot=title]) {
-    @apply --diagnostics-card-title-font;
+    color: var(--diagnostics-card-title-text-color);
+    font-family: var(--diagnostics-roboto-font-family);
+    font-size: var(--diagnostics-card-title-font-size);
+    font-weight: var(--diagnostics-medium-font-weight);
   }
 
   ::slotted([slot=left-panel]) {
diff --git a/ash/webui/diagnostics_ui/resources/diagnostics_card_frame.html b/ash/webui/diagnostics_ui/resources/diagnostics_card_frame.html
index 5fadc4f6..2a6a924f 100644
--- a/ash/webui/diagnostics_ui/resources/diagnostics_card_frame.html
+++ b/ash/webui/diagnostics_ui/resources/diagnostics_card_frame.html
@@ -5,7 +5,10 @@
   }
 
   ::slotted([slot=title]) {
-    @apply --diagnostics-card-title-font;
+    color: var(--diagnostics-card-title-text-color);
+    font-family: var(--diagnostics-roboto-font-family);
+    font-size: var(--diagnostics-card-title-font-size);
+    font-weight: var(--diagnostics-medium-font-weight);
   }
 
   ::slotted([slot=left-panel]) {
diff --git a/ash/webui/diagnostics_ui/resources/diagnostics_fonts_css.html b/ash/webui/diagnostics_ui/resources/diagnostics_fonts_css.html
index 26420e6..8431512 100644
--- a/ash/webui/diagnostics_ui/resources/diagnostics_fonts_css.html
+++ b/ash/webui/diagnostics_ui/resources/diagnostics_fonts_css.html
@@ -38,29 +38,6 @@
       --diagnostics-caution-banner-text-color: var(--cros-text-color-primary);
       --diagnostics-link-text-color: var(--cros-link-color);
       --diagnostics-settings-link-text-color: var(--cros-text-color-secondary);
-
-      --diagnostics-card-title-font: {
-          color: var(--diagnostics-card-title-text-color);
-          font-family: var(--diagnostics-roboto-font-family);
-          font-size: var(--diagnostics-card-title-font-size);
-          font-weight: var(--diagnostics-medium-font-weight);
-      };
-      --diagnostics-chart-label-font: {
-          font-family: var(--diagnostics-google-sans-font-family);
-          font-size: var(--diagnostics-chart-label-font-size);
-          font-weight: var(--diagnostics-regular-font-weight);
-      };
-      --diagnostics-button-font: {
-          font-family: var(--diagnostics-roboto-font-family);
-          font-size: var(--diagnostics-button-font-size);
-          font-weight: var(--diagnostics-medium-font-weight);
-      };
-      --diagnostics-caution-banner-font: {
-        color: var(--diagnostics-caution-banner-text-color);
-        font-family: var(--diagnostics-roboto-font-family);
-        font-size: var(--diagnostics-caution-banner-font-size);
-        font-weight: var(--diagnostics-regular-font-weight);
-      };
     }
   </style>
 </template>
diff --git a/ash/webui/diagnostics_ui/resources/diagnostics_shared_css.html b/ash/webui/diagnostics_ui/resources/diagnostics_shared_css.html
index 6bea12d..26b296f 100644
--- a/ash/webui/diagnostics_ui/resources/diagnostics_shared_css.html
+++ b/ash/webui/diagnostics_ui/resources/diagnostics_shared_css.html
@@ -4,14 +4,6 @@
       --diagnostics-box-shadow: var(--cros-elevation-1-shadow);
       --diagnostics-box-shadow-elevation-2: var(--cros-elevation-2-shadow);
 
-      --diagnostics-card-icon: {
-        --iron-icon-fill-color: var(--cros-color-prominent);
-        --iron-icon-height: 20px;
-        --iron-icon-width: 20px;
-        background-color: var(--cros-highlight-color);
-        border-radius: 50%;
-        padding: 8px;
-      };
       --diagnostics-card-bg-color: var(--cros-bg-color);
       --diagnostics-chip-bg-color: var(--cros-bg-color-dropped-elevation-2);
     }
@@ -150,6 +142,13 @@
       padding-inline: 20px;
     }
 
+    .diagnostics-caution-banner-font {
+      color: var(--diagnostics-caution-banner-text-color);
+      font-family: var(--diagnostics-roboto-font-family);
+      font-size: var(--diagnostics-caution-banner-font-size);
+      font-weight: var(--diagnostics-regular-font-weight);
+    }
+
     @media (min-width:600px) {
       :host {
         --container-padding: 24px;
diff --git a/ash/webui/diagnostics_ui/resources/diagnostics_sticky_banner.html b/ash/webui/diagnostics_ui/resources/diagnostics_sticky_banner.html
index 85bac51..f3f7db61 100644
--- a/ash/webui/diagnostics_ui/resources/diagnostics_sticky_banner.html
+++ b/ash/webui/diagnostics_ui/resources/diagnostics_sticky_banner.html
@@ -18,15 +18,11 @@
     padding-inline-start: 40px;
   }
 
-  #bannerMsg {
-    @apply --diagnostics-caution-banner-font;
-  }
-
   .elevation-2 {
     box-shadow: var(--diagnostics-box-shadow-elevation-2);
   }
 </style>
 <div id="banner" hidden$="[[!bannerMessage]]" class$="[[scrollingClass_]]">
   <iron-icon icon="diagnostics:info" id="bannerIcon"></iron-icon>
-  <span id="bannerMsg">[[bannerMessage]]</span>
+  <span id="bannerMsg" class="diagnostics-caution-banner-font">[[bannerMessage]]</span>
 </div>
diff --git a/ash/webui/diagnostics_ui/resources/input_card.html b/ash/webui/diagnostics_ui/resources/input_card.html
index d3629d91..6abe741 100644
--- a/ash/webui/diagnostics_ui/resources/input_card.html
+++ b/ash/webui/diagnostics_ui/resources/input_card.html
@@ -6,8 +6,13 @@
   }
 
   .device iron-icon {
-    @apply --diagnostics-card-icon;
+    --iron-icon-fill-color: var(--cros-color-prominent);
+    --iron-icon-height: 20px;
+    --iron-icon-width: 20px;
+    background-color: var(--cros-highlight-color);
+    border-radius: 50%;
     margin-inline-end: 28px;
+    padding: 8px;
   }
 
   .device-body {
diff --git a/ash/webui/diagnostics_ui/resources/percent_bar_chart.html b/ash/webui/diagnostics_ui/resources/percent_bar_chart.html
index b3c31f1..8785bfe 100644
--- a/ash/webui/diagnostics_ui/resources/percent_bar_chart.html
+++ b/ash/webui/diagnostics_ui/resources/percent_bar_chart.html
@@ -2,7 +2,7 @@
   #chartName {
     color: var(--diagnostics-chart-title-color);
     display: inline-block;
-    font-family: Roboto;
+    font-family: var(--diagnostics-roboto-font-family);
     font-size: 13px;
     font-weight: var(--diagnostics-regular-font-weight);
     height: 20px;
diff --git a/ash/webui/diagnostics_ui/resources/routine_section.html b/ash/webui/diagnostics_ui/resources/routine_section.html
index 2fcd662..807ca79 100644
--- a/ash/webui/diagnostics_ui/resources/routine_section.html
+++ b/ash/webui/diagnostics_ui/resources/routine_section.html
@@ -7,18 +7,23 @@
   }
 
   .button-container {
-    @apply --diagnostics-button-font;
     padding: 8px 0 4px;
   }
 
   .learn-more-button {
-    @apply --diagnostics-button-font;
     border-radius: 4px;
     height: 32px;
     margin-top: 12px;
     padding: 8px 16px;
   }
 
+  .button-container,
+  .learn-more-button {
+    font-family: var(--diagnostics-roboto-font-family);
+    font-size: var(--diagnostics-button-font-size);
+    font-weight: var(--diagnostics-medium-font-weight);
+  }
+
   #messageIcon {
     --iron-icon-height: 20px;
     --iron-icon-width: 20px;
diff --git a/ash/webui/diagnostics_ui/resources/system_page.html b/ash/webui/diagnostics_ui/resources/system_page.html
index 8fd7d5e..1d86371 100644
--- a/ash/webui/diagnostics_ui/resources/system_page.html
+++ b/ash/webui/diagnostics_ui/resources/system_page.html
@@ -18,10 +18,6 @@
    padding-inline-start: 40px;
  }
 
- #bannerMsg {
-   @apply --diagnostics-caution-banner-font;
- }
-
  #diagnosticsContainer {
    align-items: center;
    box-sizing: border-box;
@@ -73,7 +69,7 @@
   </div>
   <div id="banner" hidden="[[!bannerMessage]]" class$="[[scrollingClass_]]">
     <iron-icon icon="diagnostics:info" id="bannerIcon"></iron-icon>
-    <span id="bannerMsg">[[bannerMessage]]</span>
+    <span id="bannerMsg" class="diagnostics-caution-banner-font">[[bannerMessage]]</span>
   </div>
   <div class="overview-container">
     <overview-card id="overviewCard"></overview-card>
diff --git a/ash/webui/os_feedback_ui/resources/BUILD.gn b/ash/webui/os_feedback_ui/resources/BUILD.gn
index 01ab727..a6ac9685 100644
--- a/ash/webui/os_feedback_ui/resources/BUILD.gn
+++ b/ash/webui/os_feedback_ui/resources/BUILD.gn
@@ -69,6 +69,7 @@
 
 js_library("confirmation_page") {
   deps = [
+    ":feedback_types",
     "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
   ]
 }
diff --git a/ash/webui/os_feedback_ui/resources/confirmation_page.html b/ash/webui/os_feedback_ui/resources/confirmation_page.html
index a8406c0..c4985ac85 100644
--- a/ash/webui/os_feedback_ui/resources/confirmation_page.html
+++ b/ash/webui/os_feedback_ui/resources/confirmation_page.html
@@ -12,32 +12,29 @@
   }
 </style>
 <!--TODO(xiangdongkong): use localized strings -->
-<!--TODO(xiangdongkong): handle offline case -->
 <div id="container">
   <div id="header">
-    <h1 id="title">Thanks for your feedback</h1>
+    <h1 id="title">[[getTitle_(sendReportStatus)]]</h1>
   </div>
-  <div id="message">
-    Your feedback helps improve ChromeOS and will be reviewed by our team.
-    Because of the large number of reports, we won’t be able to send a reply.
-  </div>
-  <div id="helpResources">
-    <p id="helpResourcesLabel">Here are some other helpful resources:</p>
-    <cr-link-row id="explore"
-        start-icon="help-resources:explore"
-        label="Explore app" external
-        sub-label="Find help articles and answers to common Chromebook questions">
-    </cr-link-row>
-    <cr-link-row id="diagnostics"
-        start-icon="help-resources:diagnostics"
-        label="Diagnostics app" external
-        sub-label="Run tests and troubleshooting for hardware issues">
-    </cr-link-row>
-    <cr-link-row id="chromebookCommunity"
-        start-icon="help-resources2:chromebook-community"
-        label="Chromebook community"
-        sub-label="Ask the experts in the Chromebook help forum" external>
-    </cr-link-row>
+  <div id="content">
+    <div id="message">[[getMessage_(sendReportStatus)]]</div>
+    <div id="helpResources">
+      <p id="helpResourcesLabel">Here are some other helpful resources:</p>
+      <cr-link-row id="explore" start-icon="help-resources:explore"
+          label="Explore app" external
+          sub-label="Find help articles and answers to common Chromebook questions">
+      </cr-link-row>
+      <cr-link-row id="diagnostics" start-icon="help-resources:diagnostics"
+          label="Diagnostics app" external
+          sub-label="Run tests and troubleshooting for hardware issues">
+      </cr-link-row>
+      <cr-link-row id="chromebookCommunity"
+          start-icon="help-resources2:chromebook-community"
+          label="Chromebook community"
+          sub-label="Ask the experts in the Chromebook help forum"
+          hidden="[[isOffline_(sendReportStatus)]]" external>
+      </cr-link-row>
+    </div>
   </div>
   <div id="navButtons">
     <cr-button id="buttonNewReport" class="action-button">
diff --git a/ash/webui/os_feedback_ui/resources/confirmation_page.js b/ash/webui/os_feedback_ui/resources/confirmation_page.js
index abb05d1..638df94e 100644
--- a/ash/webui/os_feedback_ui/resources/confirmation_page.js
+++ b/ash/webui/os_feedback_ui/resources/confirmation_page.js
@@ -7,10 +7,11 @@
 import 'chrome://resources/polymer/v3_0/iron-icon/iron-icon.js';
 import 'chrome://resources/cr_elements/cr_link_row/cr_link_row.js';
 import 'chrome://resources/cr_elements/cr_button/cr_button.m.js';
-import './os_feedback_shared_css.js';
 
 import {html, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
+import {SendReportStatus} from './feedback_types.js';
+
 /**
  * @fileoverview
  * 'confirmation-page' is the last step of the feedback tool.
@@ -24,13 +25,57 @@
     return html`{__html_template__}`;
   }
 
-  /** @override */
-  ready() {
-    super.ready();
+  static get properties() {
+    return {
+      sendReportStatus: {type: SendReportStatus, readOnly: false, notify: true},
+    };
   }
 
-  close_() {
-    window.close();
+  constructor() {
+    super();
+
+    /**
+     * The status of sending the report.
+     * @type {?SendReportStatus}
+     */
+    this.sendReportStatus;
+  }
+
+  /**
+   * The page shows different information when the device is offline.
+   * @returns {boolean}
+   * @protected
+   */
+  isOffline_() {
+    return this.sendReportStatus === SendReportStatus.kDelayed;
+  }
+
+  /**
+   * @returns {string}
+   * @protected
+   */
+  getTitle_() {
+    // TODO(xiangdongkong): Localize the strings.
+    if (this.isOffline_()) {
+      return 'You are offline now. Feedback will be sent later.';
+    }
+    return 'Thanks for your feedback';
+  }
+
+  /**
+   * @returns {string}
+   * @protected
+   */
+  getMessage_() {
+    // TODO(xiangdongkong): Localize the strings.
+    if (this.isOffline_()) {
+      return 'Thanks for the feedback. Your feedback helps improve Chrome OS ' +
+          'and will be reviewed by the Chrome OS team. Because of the number ' +
+          ' of reports submitted, you won’t receive a direct reply. ';
+    }
+    return 'Your feedback helps improve ChromeOS and will be reviewed by ' +
+        'our team. Because of the large number of reports, we won\’t be able ' +
+        ' to send a reply.';
   }
 }
 
diff --git a/ash/webui/os_feedback_ui/resources/feedback_flow.html b/ash/webui/os_feedback_ui/resources/feedback_flow.html
index 73adb516..8c1095c 100644
--- a/ash/webui/os_feedback_ui/resources/feedback_flow.html
+++ b/ash/webui/os_feedback_ui/resources/feedback_flow.html
@@ -6,6 +6,8 @@
         on-continue-click="handleContinueClick_"
         on-go-back-click="handleGoBackClick_">
     </share-data-page>
-    <confirmation-page id="confirmationPage"></confirmation-page>
+    <confirmation-page id="confirmationPage"
+        send-report-status="[[sendReportStatus_]]">
+    </confirmation-page>
   </iron-pages>
 </div>
diff --git a/ash/webui/os_feedback_ui/resources/feedback_flow.js b/ash/webui/os_feedback_ui/resources/feedback_flow.js
index 705da101..8771ecc 100644
--- a/ash/webui/os_feedback_ui/resources/feedback_flow.js
+++ b/ash/webui/os_feedback_ui/resources/feedback_flow.js
@@ -11,7 +11,7 @@
 import {stringToMojoString16} from 'chrome://resources/ash/common/mojo_utils.js';
 import {html, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
-import {FeedbackContext, FeedbackServiceProviderInterface, Report} from './feedback_types.js';
+import {FeedbackContext, FeedbackServiceProviderInterface, Report, SendReportStatus} from './feedback_types.js';
 import {getFeedbackServiceProvider} from './mojo_interface_provider.js';
 
 /**
@@ -70,6 +70,13 @@
      * @private
      */
     this.description_;
+
+    /**
+     * The status of sending report.
+     * @type {?SendReportStatus}
+     * @private
+     */
+    this.sendReportStatus_;
   }
 
   ready() {
@@ -118,6 +125,7 @@
         // take a while.
         this.feedbackServiceProvider_.sendReport(report).then((response) => {
           this.currentState_ = FeedbackFlowState.CONFIRMATION;
+          this.sendReportStatus_ = response.status;
         });
         break;
       default:
@@ -147,6 +155,13 @@
   }
 
   /**
+   * @param {!SendReportStatus} status
+   */
+  setSendReportStatusForTesting(status) {
+    this.sendReportStatus_ = status;
+  }
+
+  /**
    * @param {string} text
    */
   setDescriptionForTesting(text) {
diff --git a/ash/webui/os_feedback_ui/resources/feedback_types.js b/ash/webui/os_feedback_ui/resources/feedback_types.js
index cd6138d..a7da5c2 100644
--- a/ash/webui/os_feedback_ui/resources/feedback_types.js
+++ b/ash/webui/os_feedback_ui/resources/feedback_types.js
@@ -7,6 +7,8 @@
  * Type aliases for the mojo API.
  */
 
+import '//resources/mojo/mojo/public/mojom/base/big_buffer.mojom-lite.js';
+import '//resources/mojo/mojo/public/mojom/base/string16.mojom-lite.js';
 import '//resources/mojo/mojo/public/js/mojo_bindings_lite.js';
 import '//resources/mojo/url/mojom/url.mojom-lite.js';
 import './mojom/os_feedback_ui.mojom-lite.js';
diff --git a/ash/webui/personalization_app/personalization_app_ui.cc b/ash/webui/personalization_app/personalization_app_ui.cc
index b41365d..3953851 100644
--- a/ash/webui/personalization_app/personalization_app_ui.cc
+++ b/ash/webui/personalization_app/personalization_app_ui.cc
@@ -203,6 +203,8 @@
        IDS_PERSONALIZATION_APP_KEYBOARD_BACKLIGHT_TITLE},
       {"wallpaperColor",
        IDS_PERSONALIZATION_APP_KEYBOARD_BACKLIGHT_WALLPAPER_COLOR_LABEL},
+      {"wallpaperColorTooltipText",
+       IDS_PERSONALIZATION_APP_KEYBOARD_BACKLIGHT_WALLPAPER_COLOR_TOOLTIP_TEXT},
       {"whiteColor",
        IDS_PERSONALIZATION_APP_KEYBOARD_BACKLIGHT_WHITE_COLOR_LABEL},
       {"redColor", IDS_PERSONALIZATION_APP_KEYBOARD_BACKLIGHT_RED_COLOR_LABEL},
diff --git a/ash/webui/personalization_app/resources/common/styles.html b/ash/webui/personalization_app/resources/common/styles.html
index 366a9dc..8871d7a6 100644
--- a/ash/webui/personalization_app/resources/common/styles.html
+++ b/ash/webui/personalization_app/resources/common/styles.html
@@ -303,5 +303,24 @@
     .clickable {
       cursor: pointer;
     }
+
+    paper-tooltip::part(tooltip) {
+      align-items: flex-end;
+      display: flex;
+      flex-direction: row;
+      height: 18px;
+      padding: 3px 8px;
+    }
+
+    paper-tooltip {
+      --paper-tooltip-background: var(--cros-tooltip-background-color);
+      --paper-tooltip-text-color: var(--cros-tooltip-label-color);
+    }
+
+    paper-tooltip span {
+      align-items: center;
+      display: flex;
+      font: var(--cros-body-2-font);
+    }
   </style>
 </template>
diff --git a/ash/webui/personalization_app/resources/trusted/ambient/ambient_preview_element.html b/ash/webui/personalization_app/resources/trusted/ambient/ambient_preview_element.html
index ab5e77b..2fb3f4a0 100644
--- a/ash/webui/personalization_app/resources/trusted/ambient/ambient_preview_element.html
+++ b/ash/webui/personalization_app/resources/trusted/ambient/ambient_preview_element.html
@@ -158,26 +158,6 @@
     object-fit: cover;
     width: 100%;
   }
-
-  #albumTitleTooltip::part(tooltip) {
-    align-items: flex-end;
-    display: flex;
-    flex-direction: row;
-    height: 18px;
-    padding: 3px 8px;
-  }
-
-  #albumTitleTooltip {
-    --paper-tooltip-background: var(--cros-tooltip-background-color);
-    --paper-tooltip-opacity: 0.8;
-    --paper-tooltip-text-color: var(--cros-tooltip-label-color);
-  }
-
-  #albumTitleTooltip span {
-    align-items: center;
-    display: flex;
-    font: 400 12px/18px var(--cros-font-family-roboto);
-  }
 </style>
 <div class$="[[getPreviewContainerClass_(ambientModeEnabled_, loading_)]]" id="container">
   <slot></slot>
diff --git a/ash/webui/personalization_app/resources/trusted/keyboard_backlight/keyboard_backlight_element.html b/ash/webui/personalization_app/resources/trusted/keyboard_backlight/keyboard_backlight_element.html
index 7a5ead34..e0bf677 100644
--- a/ash/webui/personalization_app/resources/trusted/keyboard_backlight/keyboard_backlight_element.html
+++ b/ash/webui/personalization_app/resources/trusted/keyboard_backlight/keyboard_backlight_element.html
@@ -103,6 +103,10 @@
         <iron-icon icon="personalization:auto"></iron-icon>
       </div>
     </div>
+    <paper-tooltip id="wallpaperColorTooltip" for$="[[wallpaperColorId_]]"
+        offset="0" aria-hidden="true">
+      <span>$i18n{wallpaperColorTooltipText}</span>
+    </paper-tooltip>
     <div class="divider"></div>
     <template is="dom-repeat" items="[[presetColorIds_]]" as="presetColorId">
       <div id$="[[presetColorId]]" class$="[[getPresetColorContainerClass_(presetColorId, presetColors_, backlightColor_)]]"
diff --git a/ash/webui/personalization_app/resources/trusted/keyboard_backlight/keyboard_backlight_element.ts b/ash/webui/personalization_app/resources/trusted/keyboard_backlight/keyboard_backlight_element.ts
index adc1b65e8..9683ca0b 100644
--- a/ash/webui/personalization_app/resources/trusted/keyboard_backlight/keyboard_backlight_element.ts
+++ b/ash/webui/personalization_app/resources/trusted/keyboard_backlight/keyboard_backlight_element.ts
@@ -5,6 +5,7 @@
 import 'chrome://resources/polymer/v3_0/iron-a11y-keys/iron-a11y-keys.js';
 import 'chrome://resources/polymer/v3_0/iron-selector/iron-selector.js';
 import 'chrome://resources/polymer/v3_0/paper-ripple/paper-ripple.js';
+import 'chrome://resources/polymer/v3_0/paper-tooltip/paper-tooltip.js';
 import '../../common/styles.js';
 import '../cros_button_style.js';
 
diff --git a/ash/webui/personalization_app/resources/untrusted/images_grid.html b/ash/webui/personalization_app/resources/untrusted/images_grid.html
index 83688e31..2a50117 100644
--- a/ash/webui/personalization_app/resources/untrusted/images_grid.html
+++ b/ash/webui/personalization_app/resources/untrusted/images_grid.html
@@ -3,25 +3,35 @@
     margin: 12px 0;
   }
 </style>
-<iron-list grid items="[[tiles_]]" role="listbox"
-    aria-setsize$="[[tiles_.length]]">
-  <template>
-    <div class="photo-container">
-      <div class="photo-inner-container" tabindex$="[[tabIndex]]" role="option"
-          data-asset-id$="[[item.assetId]]"
-          data-unit-id$="[[item.unitId]]" on-click="onImageSelected_"
-          on-keypress="onImageSelected_"
-          aria-posinset$="[[getAriaIndex_(index)]]"
-          aria-selected$="[[getAriaSelected_(item, selectedAssetId_, pendingSelectedAssetId_)]]"
-          aria-label$="[[getAriaLabel_(item)]]">
-        <div class="photo-images-container">
-          <template is="dom-repeat" items="[[item.preview]]" as="preview">
-            <img is="cr-auto-img" class$="[[getClassForImg_(index, item)]]"
-                auto-src="[[preview.url]]" aria-hidden="true" clear-src>
-          </template>
-          <iron-icon icon="personalization:checkmark"></iron-icon>
-        </div>
+<template is="dom-if" if="[[tiles_]]">
+  <iron-list grid items="[[tiles_]]" role="listbox"
+      aria-setsize$="[[tiles_.length]]">
+    <template>
+      <div class="photo-container">
+        <template is="dom-if" if="[[isLoadingTile_(item)]]">
+          <div tabindex$="[[tabIndex]]" role="button"
+              class="photo-inner-container placeholder"
+              style$="[[getLoadingPlaceholderAnimationDelay_(index)]]"
+              aria-label="$i18n{ariaLabelLoading}" aria-disabled="true"></div>
+        </template>
+        <template is="dom-if" if="[[isImageTile_(item)]]">
+          <div class="photo-inner-container" tabindex$="[[tabIndex]]"
+              role="option" data-asset-id$="[[item.assetId]]"
+              data-unit-id$="[[item.unitId]]" on-click="onImageSelected_"
+              on-keypress="onImageSelected_"
+              aria-posinset$="[[getAriaIndex_(index)]]"
+              aria-selected$="[[getAriaSelected_(item, selectedAssetId_, pendingSelectedAssetId_)]]"
+              aria-label$="[[getAriaLabel_(item)]]">
+            <div class="photo-images-container">
+              <template is="dom-repeat" items="[[item.preview]]" as="preview">
+                <img is="cr-auto-img" class$="[[getClassForImg_(index, item)]]"
+                    auto-src="[[preview.url]]" aria-hidden="true" clear-src>
+              </template>
+              <iron-icon icon="personalization:checkmark"></iron-icon>
+            </div>
+          </div>
+        </template>
       </div>
-    </div>
-  </template>
-</iron-list>
+    </template>
+  </iron-list>
+</template>
diff --git a/ash/webui/personalization_app/resources/untrusted/images_grid.ts b/ash/webui/personalization_app/resources/untrusted/images_grid.ts
index 8904abd..17ac993 100644
--- a/ash/webui/personalization_app/resources/untrusted/images_grid.ts
+++ b/ash/webui/personalization_app/resources/untrusted/images_grid.ts
@@ -11,7 +11,7 @@
 import {afterNextRender, PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 import {Events, EventType, ImageTile} from '../common/constants.js';
-import {isSelectionEvent} from '../common/utils.js';
+import {getLoadingPlaceholderAnimationDelay, getLoadingPlaceholders, isSelectionEvent} from '../common/utils.js';
 import {selectImage, validateReceivedData} from '../untrusted/iframe_api.js';
 
 import {getTemplate} from './images_grid.html.js';
@@ -34,7 +34,11 @@
     return {
       tiles_: {
         type: Array,
-        value: [],
+        value() {
+          // Fill the view with loading tiles. Will be adjusted to the correct
+          // number of tiles when collections are received.
+          return getLoadingPlaceholders(() => 0);
+        }
       },
 
       selectedAssetId_: {
@@ -49,7 +53,7 @@
     };
   }
 
-  private tiles_: ImageTile[];
+  private tiles_: ImageTile[]|number[];
   private selectedAssetId_: bigint|undefined;
   private pendingSelectedAssetId_: bigint|undefined;
 
@@ -91,6 +95,19 @@
     }
   }
 
+  private isLoadingTile_(tile: number|ImageTile): tile is number {
+    return typeof tile === 'number';
+  }
+
+  private isImageTile_(tile: number|ImageTile): tile is ImageTile {
+    return tile.hasOwnProperty('preview') &&
+        Array.isArray((tile as any).preview);
+  }
+
+  private getLoadingPlaceholderAnimationDelay_(index: number): string {
+    return getLoadingPlaceholderAnimationDelay(index);
+  }
+
   private getAriaSelected_(
       tile: ImageTile, selectedAssetId: bigint|undefined,
       pendingSelectedAssetId: bigint|undefined): string {
diff --git a/ash/webui/projector_app/annotator_message_handler.cc b/ash/webui/projector_app/annotator_message_handler.cc
index 5a80037a1..6ccc92a839 100644
--- a/ash/webui/projector_app/annotator_message_handler.cc
+++ b/ash/webui/projector_app/annotator_message_handler.cc
@@ -20,10 +20,6 @@
 
 void AnnotatorMessageHandler::RegisterMessages() {
   web_ui()->RegisterMessageCallback(
-      "onToolSet", base::BindRepeating(&AnnotatorMessageHandler::OnToolSet,
-                                       base::Unretained(this)));
-
-  web_ui()->RegisterMessageCallback(
       "onUndoRedoAvailabilityChanged",
       base::BindRepeating(
           &AnnotatorMessageHandler::OnUndoRedoAvailabilityChanged,
@@ -33,10 +29,6 @@
       "onCanvasInitialized",
       base::BindRepeating(&AnnotatorMessageHandler::OnCanvasInitialized,
                           base::Unretained(this)));
-
-  web_ui()->RegisterMessageCallback(
-      "onError", base::BindRepeating(&AnnotatorMessageHandler::OnError,
-                                     base::Unretained(this)));
 }
 
 void AnnotatorMessageHandler::SetTool(const AnnotatorTool& tool) {
@@ -59,11 +51,6 @@
   FireWebUIListener("clear");
 }
 
-void AnnotatorMessageHandler::OnToolSet(const base::Value::List& args) {
-  DCHECK_EQ(args.size(), 1u);
-  ProjectorController::Get()->OnToolSet(AnnotatorTool::FromValue(args[0]));
-}
-
 void AnnotatorMessageHandler::OnUndoRedoAvailabilityChanged(
     const base::Value::List& args) {
   DCHECK_EQ(args.size(), 2u);
@@ -80,10 +67,4 @@
   ProjectorController::Get()->OnCanvasInitialized(args[0].GetBool());
 }
 
-void AnnotatorMessageHandler::OnError(const base::Value::List& args) {
-  // TODO(b/200846160): The annotator is in an error state. Show creation flow
-  // error notification and trigger a reload of the WebContent hosting the
-  // annotator to clear the error state.
-}
-
 }  // namespace ash
diff --git a/ash/webui/projector_app/resources/annotator/annotator_embedder_impl.js b/ash/webui/projector_app/resources/annotator/annotator_embedder_impl.js
index fbe526d..94fd9e5 100644
--- a/ash/webui/projector_app/resources/annotator/annotator_embedder_impl.js
+++ b/ash/webui/projector_app/resources/annotator/annotator_embedder_impl.js
@@ -58,10 +58,7 @@
 
     this.addWebUIListener('setTool', async (tool) => {
       try {
-        const success = await client.setTool(tool);
-        if (success) {
-          ProjectorBrowserProxyImpl.getInstance().onToolSet(tool);
-        }
+        client.setTool(tool);
       } catch (error) {
         ProjectorBrowserProxyImpl.getInstance().onError(
             [AnnotatorToolErrorType.SET_TOOL_ERROR]);
diff --git a/ash/webui/projector_app/resources/communication/projector_browser_proxy.js b/ash/webui/projector_app/resources/communication/projector_browser_proxy.js
index 0ee2b80..7097bf0 100644
--- a/ash/webui/projector_app/resources/communication/projector_browser_proxy.js
+++ b/ash/webui/projector_app/resources/communication/projector_browser_proxy.js
@@ -12,12 +12,6 @@
  */
 export class ProjectorBrowserProxy {
   /**
-   * Notifies the embedder content that tool has been set for annotator.
-   * @param {!projectorApp.AnnotatorToolParams} tool
-   */
-  onToolSet(tool) {}
-
-  /**
    * Notifies the embedder content that undo/redo availability changed for
    * annotator.
    * @param {boolean} undoAvailable
@@ -135,11 +129,6 @@
  */
 export class ProjectorBrowserProxyImpl {
   /** @override */
-  onToolSet(tool) {
-    return chrome.send('onToolSet', [tool]);
-  }
-
-  /** @override */
   onUndoRedoAvailabilityChanged(undoAvailable, redoAvailable) {
     return chrome.send(
         'onUndoRedoAvailabilityChanged', [undoAvailable, redoAvailable]);
diff --git a/ash/webui/projector_app/test/annotator_message_handler_unittest.cc b/ash/webui/projector_app/test/annotator_message_handler_unittest.cc
index cc0a9a5..4b82a32 100644
--- a/ash/webui/projector_app/test/annotator_message_handler_unittest.cc
+++ b/ash/webui/projector_app/test/annotator_message_handler_unittest.cc
@@ -84,13 +84,6 @@
 
   AnnotatorTool requested_tool = AnnotatorTool::FromValue(*call_data.arg2());
   EXPECT_EQ(requested_tool, expected_tool);
-
-  // Now let's check that when the tool has been set, we notify the callback.
-  EXPECT_CALL(controller(), OnToolSet(expected_tool));
-
-  base::ListValue list_args;
-  list_args.Append(expected_tool.ToValue());
-  web_ui().HandleReceivedMessage("onToolSet", &list_args);
 }
 
 TEST_F(AnnotatorMessageHandlerTest, Undo) {
diff --git a/ash/webui/shimless_rma/backend/version_updater_unittest.cc b/ash/webui/shimless_rma/backend/version_updater_unittest.cc
index 977a172..faae4321 100644
--- a/ash/webui/shimless_rma/backend/version_updater_unittest.cc
+++ b/ash/webui/shimless_rma/backend/version_updater_unittest.cc
@@ -8,6 +8,7 @@
 
 #include "base/bind.h"
 #include "base/test/task_environment.h"
+#include "chromeos/ash/components/network/onc/network_onc_utils.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/dbus/update_engine/fake_update_engine_client.h"
 #include "chromeos/dbus/update_engine/update_engine.pb.h"
@@ -20,7 +21,6 @@
 #include "chromeos/network/network_handler.h"
 #include "chromeos/network/network_profile_handler.h"
 #include "chromeos/network/network_state_test_helper.h"
-#include "chromeos/network/onc/network_onc_utils.h"
 #include "chromeos/network/proxy/ui_proxy_config_service.h"
 #include "chromeos/services/network_config/public/cpp/cros_network_config_test_helper.h"
 #include "chromeos/services/network_config/public/mojom/cros_network_config.mojom.h"
diff --git a/ash/webui/shimless_rma/resources/shimless_rma.html b/ash/webui/shimless_rma/resources/shimless_rma.html
index 9d8a6d5..553ae9d2 100644
--- a/ash/webui/shimless_rma/resources/shimless_rma.html
+++ b/ash/webui/shimless_rma/resources/shimless_rma.html
@@ -37,8 +37,9 @@
 
   #back {
     border: 0;
-    height: 100%;
-    padding-inline-start: 0;
+    border-radius: 16px;
+    height: 32px;
+    padding-inline-start: 16px;
   }
 
   #next {
diff --git a/base/BUILD.gn b/base/BUILD.gn
index 01eda7c..d1abe6d5 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -1858,6 +1858,8 @@
     # by public //base headers, which requires they be on the include path.
     # TODO(https://crbug.com/841171): Move these back to |deps|.
     public_deps += [
+      "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.buildinfo",
+      "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.hwinfo",
       "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.intl",
       "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.io",
       "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.logger",
@@ -3742,6 +3744,7 @@
       "fuchsia/service_directory_test_base.h",
       "fuchsia/service_provider_impl_unittest.cc",
       "fuchsia/system_build_info_unittest.cc",
+      "fuchsia/system_product_info_unittest.cc",
       "fuchsia/test_component_context_for_process_unittest.cc",
       "fuchsia/time_zone_data_unittest.cc",
       "message_loop/fd_watch_controller_posix_unittest.cc",
@@ -3754,6 +3757,7 @@
       ":test_log_listener_safe",
       ":testfidl",
       "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.buildinfo",
+      "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.hwinfo",
       "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.intl",
       "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.logger",
       "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.mem",
diff --git a/base/fuchsia/system_info.cc b/base/fuchsia/system_info.cc
index 1fe1812..4cf82970 100644
--- a/base/fuchsia/system_info.cc
+++ b/base/fuchsia/system_info.cc
@@ -5,54 +5,69 @@
 #include "base/fuchsia/system_info.h"
 
 #include <fuchsia/buildinfo/cpp/fidl.h>
+#include <fuchsia/hwinfo/cpp/fidl.h>
 #include <lib/sys/cpp/component_context.h>
 
 #include "base/check.h"
 #include "base/fuchsia/fuchsia_logging.h"
 #include "base/fuchsia/process_context.h"
+#include "base/location.h"
 #include "base/no_destructor.h"
 #include "base/threading/scoped_blocking_call.h"
-#include "base/threading/thread_restrictions.h"
 
 namespace base {
 
 namespace {
 
-fuchsia::buildinfo::BuildInfo FetchSystemBuildInfo() {
-  ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::WILL_BLOCK);
-
-  fuchsia::buildinfo::ProviderSyncPtr build_info_provider_sync;
-  ComponentContextForProcess()->svc()->Connect(
-      build_info_provider_sync.NewRequest());
-
-  fuchsia::buildinfo::BuildInfo build_info;
-  zx_status_t status = build_info_provider_sync->GetBuildInfo(&build_info);
-  ZX_DCHECK(status == ZX_OK, status);
-  DCHECK(!build_info.IsEmpty()) << "FIDL service returned empty BuildInfo";
-  return build_info;
+// Returns this process's ProductInfo object.
+template <typename Data>
+Data& CachedData() {
+  static NoDestructor<Data> data;
+  return *data;
 }
 
-// Returns this process's BuildInfo object.
-fuchsia::buildinfo::BuildInfo& CachedBuildInfo() {
-  static NoDestructor<fuchsia::buildinfo::BuildInfo> build_info;
-  return *build_info;
+template <typename Data>
+const Data& GetCachedData() {
+  DCHECK(!CachedData<Data>().IsEmpty())
+      << "FetchAndCacheSystemInfo() has not been called in this process";
+  return CachedData<Data>();
+}
+
+template <typename Interface,
+          typename Data,
+          zx_status_t (Interface::Sync_::*Getter)(Data*)>
+void FetchAndCacheData() {
+  DCHECK(CachedData<Data>().IsEmpty()) << "Only call once per process";
+
+  fidl::SynchronousInterfacePtr<Interface> provider_sync;
+  ComponentContextForProcess()->svc()->Connect(provider_sync.NewRequest());
+
+  zx_status_t status = (provider_sync.get()->*Getter)(&CachedData<Data>());
+  ZX_CHECK(status == ZX_OK, status) << Interface::Name_;
+  DCHECK(!CachedData<Data>().IsEmpty()) << "FIDL service returned empty data";
 }
 
 }  // namespace
 
 void FetchAndCacheSystemInfo() {
-  DCHECK(CachedBuildInfo().IsEmpty()) << "Only call once per process";
-  CachedBuildInfo() = FetchSystemBuildInfo();
+  ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::WILL_BLOCK);
+  FetchAndCacheData<fuchsia::buildinfo::Provider, fuchsia::buildinfo::BuildInfo,
+                    &fuchsia::buildinfo::Provider_Sync::GetBuildInfo>();
+  FetchAndCacheData<fuchsia::hwinfo::Product, fuchsia::hwinfo::ProductInfo,
+                    &fuchsia::hwinfo::Product_Sync::GetInfo>();
 }
 
 const fuchsia::buildinfo::BuildInfo& GetCachedBuildInfo() {
-  DCHECK(!CachedBuildInfo().IsEmpty())
-      << "FetchAndCacheSystemInfo() has not been called in this process";
-  return CachedBuildInfo();
+  return GetCachedData<fuchsia::buildinfo::BuildInfo>();
+}
+
+const fuchsia::hwinfo::ProductInfo& GetCachedProductInfo() {
+  return GetCachedData<fuchsia::hwinfo::ProductInfo>();
 }
 
 void ClearCachedSystemInfoForTesting() {
-  CachedBuildInfo() = {};
+  CachedData<fuchsia::buildinfo::BuildInfo>() = {};
+  CachedData<fuchsia::hwinfo::ProductInfo>() = {};
 }
 
 }  // namespace base
diff --git a/base/fuchsia/system_info.h b/base/fuchsia/system_info.h
index 481e2ff..ad48a992 100644
--- a/base/fuchsia/system_info.h
+++ b/base/fuchsia/system_info.h
@@ -6,25 +6,29 @@
 #define BASE_FUCHSIA_SYSTEM_INFO_H_
 
 #include "base/base_export.h"
-#include "base/strings/string_piece_forward.h"
 
-namespace fuchsia {
-namespace buildinfo {
+namespace fuchsia::buildinfo {
 class BuildInfo;
 }
-}  // namespace fuchsia
+namespace fuchsia::hwinfo {
+class ProductInfo;
+}
 
 namespace base {
 
-// Fetches the build info from the system and caches it before returning.
-// Must be called in each process before calling other non-test functions.
+// Makes a blocking call to fetch the info from the system and caches it
+// before returning. Must be called in each process during the initialization
+// phase.
 BASE_EXPORT void FetchAndCacheSystemInfo();
 
 // Returns the cached build info.
 BASE_EXPORT const fuchsia::buildinfo::BuildInfo& GetCachedBuildInfo();
 
-// Reset the cached BuildInfo to empty so that FetchAndCacheSystemInfo()
-// can be called again in this process.
+// Returns the cached product info.
+BASE_EXPORT const fuchsia::hwinfo::ProductInfo& GetCachedProductInfo();
+
+// Reset the cached system info to empty so that
+// FetchAndCacheSystemInfo() can be called again in this process.
 BASE_EXPORT void ClearCachedSystemInfoForTesting();
 
 }  // namespace base
diff --git a/base/fuchsia/system_product_info_unittest.cc b/base/fuchsia/system_product_info_unittest.cc
new file mode 100644
index 0000000..63171d1
--- /dev/null
+++ b/base/fuchsia/system_product_info_unittest.cc
@@ -0,0 +1,130 @@
+// Copyright 2022 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 "base/fuchsia/system_info.h"
+
+#include <fuchsia/buildinfo/cpp/fidl.h>
+#include <fuchsia/hwinfo/cpp/fidl.h>
+#include <fuchsia/hwinfo/cpp/fidl_test_base.h>
+#include <memory>
+
+#include "base/bind.h"
+#include "base/fuchsia/scoped_service_binding.h"
+#include "base/fuchsia/scoped_service_publisher.h"
+#include "base/fuchsia/test_component_context_for_process.h"
+#include "base/location.h"
+#include "base/run_loop.h"
+#include "base/strings/string_piece_forward.h"
+#include "base/test/bind.h"
+#include "base/test/gtest_util.h"
+#include "base/test/task_environment.h"
+#include "base/test/test_future.h"
+#include "base/threading/sequence_bound.h"
+#include "base/threading/thread.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+namespace {
+
+class FakeHardwareInfoProduct
+    : public fuchsia::hwinfo::testing::Product_TestBase {
+ public:
+  FakeHardwareInfoProduct(const base::StringPiece model,
+                          const base::StringPiece manufacturer,
+                          sys::OutgoingDirectory* outgoing_services)
+      : model_(model),
+        manufacturer_(manufacturer),
+        binding_(outgoing_services, this) {}
+  FakeHardwareInfoProduct(const FakeHardwareInfoProduct&) = delete;
+  FakeHardwareInfoProduct& operator=(const FakeHardwareInfoProduct&) = delete;
+  ~FakeHardwareInfoProduct() override = default;
+
+  // fuchsia::hwinfo::testing::Provider_TestBase implementation
+  void GetInfo(GetInfoCallback callback) override {
+    fuchsia::hwinfo::ProductInfo product_info;
+    product_info.set_model(model_);
+    product_info.set_manufacturer(manufacturer_);
+    callback(std::move(product_info));
+  }
+  void NotImplemented_(const std::string& name) final {
+    ADD_FAILURE() << "Unexpected call: " << name;
+  }
+
+ private:
+  std::string model_;
+  std::string manufacturer_;
+  ScopedServiceBinding<fuchsia::hwinfo::Product> binding_;
+};
+
+}  // namespace
+
+// Uses a fake "fuchsia.hwinfo.Product" implementation.
+// clears the cached ProductInfo to ensure that each test starts with no cached
+// ProductInfo and that subsequent tests runs do not use fake values.
+class ProductInfoTest : public testing::Test {
+ protected:
+  ProductInfoTest()
+      : task_environment_(
+            base::test::SingleThreadTaskEnvironment::MainThreadType::IO),
+        thread_("ProductInfo Retrieval Thread") {
+    thread_.StartWithOptions(
+        base::Thread::Options(base::MessagePumpType::IO, 0));
+    ClearCachedSystemInfoForTesting();
+    component_context_.AddService(fuchsia::buildinfo::Provider::Name_);
+  }
+  ~ProductInfoTest() override { ClearCachedSystemInfoForTesting(); }
+
+  // Fetch the product info in a separate thread, while servicing the
+  // FIDL fake implementation on the main thread.
+  void FetchProductInfoAndWaitUntilCached() {
+    base::RunLoop run_loop;
+    thread_.task_runner()->PostTaskAndReply(
+        FROM_HERE, BindOnce(&FetchAndCacheSystemInfo), run_loop.QuitClosure());
+    run_loop.Run();
+  }
+
+  base::test::SingleThreadTaskEnvironment task_environment_;
+  TestComponentContextForProcess component_context_;
+  base::Thread thread_;
+};
+
+using ProductInfoDeathTest = ProductInfoTest;
+
+TEST_F(ProductInfoTest, GetCachedProductInfoReturnsFakedValues) {
+  FakeHardwareInfoProduct hwinfo_product_provider(
+      "test.model", "test.manufacturer",
+      component_context_.additional_services());
+  FetchProductInfoAndWaitUntilCached();
+
+  const auto& product_info = GetCachedProductInfo();
+  EXPECT_EQ(product_info.model(), "test.model");
+  EXPECT_EQ(product_info.manufacturer(), "test.manufacturer");
+}
+
+TEST_F(ProductInfoDeathTest, DcheckOnGetWithoutFetch) {
+  EXPECT_DCHECK_DEATH_WITH(
+      GetCachedProductInfo(),
+      "FetchAndCacheSystemInfo\\(\\) has not been called in this "
+      "process");
+}
+
+TEST_F(ProductInfoTest, SystemServiceReturnsValidValues) {
+  component_context_.AddService(fuchsia::hwinfo::Product::Name_);
+  FetchProductInfoAndWaitUntilCached();
+
+  const auto& product_info = GetCachedProductInfo();
+  EXPECT_TRUE(product_info.has_model());
+  EXPECT_FALSE(product_info.model().empty());
+
+  EXPECT_TRUE(product_info.has_manufacturer());
+  EXPECT_FALSE(product_info.manufacturer().empty());
+}
+
+TEST_F(ProductInfoDeathTest, DcheckOnServiceNotPresent) {
+  EXPECT_DCHECK_DEATH_WITH(FetchProductInfoAndWaitUntilCached(),
+                           "ZX_ERR_PEER_CLOSED");
+}
+
+}  // namespace base
diff --git a/base/process/process.h b/base/process/process.h
index 19d2c2e..619374c 100644
--- a/base/process/process.h
+++ b/base/process/process.h
@@ -20,10 +20,13 @@
 #include <lib/zx/process.h>
 #endif
 
-#if BUILDFLAG(IS_APPLE)
+#if BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_CHROMEOS)
 #include "base/feature_list.h"
+#endif  // BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_CHROMEOS)
+
+#if BUILDFLAG(IS_APPLE)
 #include "base/process/port_provider_mac.h"
-#endif
+#endif  // BUILDFLAG(IS_APPLE)
 
 namespace base {
 
@@ -31,6 +34,14 @@
 extern const Feature kMacAllowBackgroundingProcesses;
 #endif
 
+#if BUILDFLAG(IS_CHROMEOS)
+// OneGroupPerRenderer feature places each foreground renderer process into
+// its own cgroup. This will cause the scheduler to use the aggregate runtime
+// of all threads in the process when deciding on the next thread to schedule.
+// It will help guarantee fairness between renderers.
+BASE_EXPORT extern const Feature kOneGroupPerRenderer;
+#endif
+
 // Provides a move-only encapsulation of a process.
 //
 // This object is not tied to the lifetime of the underlying process: the
@@ -113,6 +124,12 @@
   // Returns true if this process is the current process.
   bool is_current() const;
 
+#if BUILDFLAG(IS_CHROMEOS)
+  // A unique token generated for each process, this is used to create a unique
+  // cgroup for each renderer.
+  const std::string& unique_token() const { return unique_token_; }
+#endif
+
   // Close the process handle. This will not terminate the process.
   void Close();
 
@@ -171,8 +188,8 @@
 #if BUILDFLAG(IS_MAC)
   // The Mac needs a Mach port in order to manipulate a process's priority,
   // and there's no good way to get that from base given the pid. These Mac
-  // variants of the IsProcessBackgrounded and SetProcessBackgrounded API take
-  // a port provider for this reason. See crbug.com/460102
+  // variants of the IsProcessBackgrounded() and SetProcessBackgrounded() API
+  // take a port provider for this reason. See crbug.com/460102
   //
   // A process is backgrounded when its task priority is
   // |TASK_BACKGROUND_APPLICATION|.
@@ -211,7 +228,39 @@
   ProcessId GetPidInNamespace() const;
 #endif
 
+#if BUILDFLAG(IS_CHROMEOS)
+  // Returns true if the 'OneGroupPerRenderer' feature is enabled. The feature
+  // is enabled if the kOneGroupPerRenderer feature flag is enabled and the
+  // system supports the chrome cgroups.
+  static bool OneGroupPerRendererEnabled();
+
+  // If OneGroupPerRenderer is enabled, runs at process startup to clean up
+  // any stale cgroups that were left behind from any unclean exits of the
+  // browser process.
+  static void CleanUpStaleProcessStates();
+
+  // Initializes the process's priority. If OneGroupPerRenderer is enabled, it
+  // creates a unique cgroup for the process. This should be called before
+  // SetProcessBackgrounded(). This is a no-op if the Process is not valid
+  // or if it has already been called.
+  void InitializePriority();
+#endif  // BUILDFLAG(IS_CHROMEOS)
+
  private:
+#if BUILDFLAG(IS_CHROMEOS)
+  // Cleans up process state. If OneGroupPerRenderer is enabled, it cleans up
+  // the cgroup created by InitializePriority(). If the process has not
+  // fully terminated yet, it will post a background task to try again.
+  void CleanUpProcess(int remaining_retries) const;
+
+  // Calls CleanUpProcess() on a background thread.
+  void CleanUpProcessAsync() const;
+
+  // Used to call CleanUpProcess() on a background thread because Process is not
+  // refcounted.
+  static void CleanUpProcessScheduled(Process process, int remaining_retries);
+#endif  // BUILDFLAG(IS_CHROMEOS)
+
 #if BUILDFLAG(IS_WIN)
   win::ScopedHandle process_;
 #elif BUILDFLAG(IS_FUCHSIA)
@@ -223,6 +272,14 @@
 #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_FUCHSIA)
   bool is_current_process_;
 #endif
+
+#if BUILDFLAG(IS_CHROMEOS)
+  // A unique token per process not per class instance (`base::Process`). This
+  // is similar to the PID of a process but should not be reused after the
+  // process's termination. The token will be copied during Duplicate()
+  // and move semantics as is the PID/ProcessHandle.
+  std::string unique_token_;
+#endif
 };
 
 #if BUILDFLAG(IS_CHROMEOS)
diff --git a/base/process/process_linux.cc b/base/process/process_linux.cc
index a5cf7ba..3d916bd 100644
--- a/base/process/process_linux.cc
+++ b/base/process/process_linux.cc
@@ -7,21 +7,49 @@
 #include <errno.h>
 #include <sys/resource.h>
 
+#include <cstring>
+
 #include "base/check.h"
 #include "base/files/file_util.h"
+#include "base/location.h"
+#include "base/logging.h"
 #include "base/notreached.h"
 #include "base/posix/can_lower_nice_to.h"
 #include "base/process/internal_linux.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_split.h"
 #include "base/strings/stringprintf.h"
-#include "base/synchronization/lock.h"
 #include "base/threading/thread_restrictions.h"
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
 
+#if BUILDFLAG(IS_CHROMEOS)
+#include "base/bind.h"
+#include "base/feature_list.h"
+#include "base/files/file_enumerator.h"
+#include "base/files/file_path.h"
+#include "base/process/process_handle.h"
+#include "base/strings/strcat.h"
+#include "base/strings/string_util.h"
+#include "base/task/thread_pool.h"
+#include "base/unguessable_token.h"
+#endif  // BUILDFLAG(IS_CHROMEOS)
+
 namespace base {
 
+#if BUILDFLAG(IS_CHROMEOS)
+const Feature kOneGroupPerRenderer {
+  "OneGroupPerRenderer",
+
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+      FEATURE_ENABLED_BY_DEFAULT
+};
+#else
+      FEATURE_DISABLED_BY_DEFAULT
+};
+#endif  // BUILDFLAG(IS_CHROMEOS_LACROS)
+#endif  // BUILDFLAG(IS_CHROMEOS)
+
 namespace {
 
 const int kForegroundPriority = 0;
@@ -36,9 +64,21 @@
 // chrome / chromeos specific logic here.
 const int kBackgroundPriority = 19;
 const char kControlPath[] = "/sys/fs/cgroup/cpu%s/cgroup.procs";
+const char kFullRendererCgroupRoot[] = "/sys/fs/cgroup/cpu/chrome_renderers";
 const char kForeground[] = "/chrome_renderers/foreground";
 const char kBackground[] = "/chrome_renderers/background";
 const char kProcPath[] = "/proc/%d/cgroup";
+const char kUclampMinFile[] = "cpu.uclamp.min";
+const char kUclampMaxFile[] = "cpu.uclamp.max";
+
+constexpr int kCgroupDeleteRetries = 3;
+constexpr TimeDelta kCgroupDeleteRetryTime(Seconds(1));
+
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+const char kCgroupPrefix[] = "l-";
+#elif BUILDFLAG(IS_CHROMEOS_ASH)
+const char kCgroupPrefix[] = "a-";
+#endif
 
 struct CGroups {
   // Check for cgroups files. ChromeOS supports these by default. It creates
@@ -47,21 +87,59 @@
   // all background renderers. This allows us to limit the impact of background
   // renderers on foreground ones to a greater level than simple renicing.
   bool enabled;
-  base::FilePath foreground_file;
-  base::FilePath background_file;
+  FilePath foreground_file;
+  FilePath background_file;
+
+  // A unique token for this instance of the browser.
+  std::string group_prefix_token;
+
+  // UCLAMP settings for the foreground cgroups.
+  std::string uclamp_min;
+  std::string uclamp_max;
 
   CGroups() {
-    foreground_file =
-        base::FilePath(base::StringPrintf(kControlPath, kForeground));
-    background_file =
-        base::FilePath(base::StringPrintf(kControlPath, kBackground));
-    base::FileSystemType foreground_type;
-    base::FileSystemType background_type;
-    enabled =
-        base::GetFileSystemType(foreground_file, &foreground_type) &&
-        base::GetFileSystemType(background_file, &background_type) &&
-        foreground_type == FILE_SYSTEM_CGROUP &&
-        background_type == FILE_SYSTEM_CGROUP;
+    foreground_file = FilePath(StringPrintf(kControlPath, kForeground));
+    background_file = FilePath(StringPrintf(kControlPath, kBackground));
+    FileSystemType foreground_type;
+    FileSystemType background_type;
+    enabled = GetFileSystemType(foreground_file, &foreground_type) &&
+              GetFileSystemType(background_file, &background_type) &&
+              foreground_type == FILE_SYSTEM_CGROUP &&
+              background_type == FILE_SYSTEM_CGROUP;
+
+    if (!enabled || !FeatureList::IsEnabled(kOneGroupPerRenderer)) {
+      return;
+    }
+
+    // Generate a unique token for the full browser process
+    group_prefix_token =
+        StrCat({kCgroupPrefix, UnguessableToken::Create().ToString(), "-"});
+
+    // Reads the ULCAMP settings from the foreground cgroup that will be used
+    // for each renderer's cgroup.
+    FilePath foreground_path = foreground_file.DirName();
+    ReadFileToString(foreground_path.Append(kUclampMinFile), &uclamp_min);
+    ReadFileToString(foreground_path.Append(kUclampMaxFile), &uclamp_max);
+  }
+
+  // Returns the full path to a the cgroup dir of a process using
+  // the supplied token.
+  static FilePath GetForegroundCgroupDir(const std::string& token) {
+    // Get individualized cgroup if the feature is enabled
+    std::string cgroup_path_str;
+    StrAppend(&cgroup_path_str, {kFullRendererCgroupRoot, "/", token});
+    return FilePath(cgroup_path_str);
+  }
+
+  // Returns the path to the cgroup.procs file of the foreground cgroup.
+  static FilePath GetForegroundCgroupFile(const std::string& token) {
+    // Processes with an empty token use the default foreground cgroup.
+    if (token.empty()) {
+      return CGroups::Get().foreground_file;
+    }
+
+    FilePath cgroup_path = GetForegroundCgroupDir(token);
+    return cgroup_path.Append("cgroup.procs");
   }
 
   static CGroups& Get() {
@@ -69,6 +147,7 @@
     return groups;
   }
 };
+
 #else
 const int kBackgroundPriority = 5;
 #endif  // BUILDFLAG(IS_CHROMEOS)
@@ -110,10 +189,9 @@
 #if BUILDFLAG(IS_CHROMEOS)
   if (CGroups::Get().enabled) {
     // Used to allow reading the process priority from proc on thread launch.
-    base::ThreadRestrictions::ScopedAllowIO allow_io;
+    ThreadRestrictions::ScopedAllowIO allow_io;
     std::string proc;
-    if (base::ReadFileToString(
-            base::FilePath(StringPrintf(kProcPath, process_)), &proc)) {
+    if (ReadFileToString(FilePath(StringPrintf(kProcPath, process_)), &proc)) {
       return IsProcessBackgroundedCGroup(proc);
     }
     return false;
@@ -129,9 +207,10 @@
 #if BUILDFLAG(IS_CHROMEOS)
   if (CGroups::Get().enabled) {
     std::string pid = NumberToString(process_);
-    const base::FilePath file = background ? CGroups::Get().background_file
-                                           : CGroups::Get().foreground_file;
-    return base::WriteFile(file, pid.c_str(), pid.size()) > 0;
+    const FilePath file =
+        background ? CGroups::Get().background_file
+                   : CGroups::Get().GetForegroundCgroupFile(unique_token_);
+    return WriteFile(file, pid);
   }
 #endif  // BUILDFLAG(IS_CHROMEOS)
 
@@ -207,4 +286,132 @@
 }
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
+#if BUILDFLAG(IS_CHROMEOS)
+// static
+bool Process::OneGroupPerRendererEnabled() {
+  return CGroups::Get().enabled && FeatureList::IsEnabled(kOneGroupPerRenderer);
+}
+
+// On Chrome OS, each renderer runs in its own cgroup when running in the
+// foreground. After process creation the cgroup is created using a
+// unique token.
+void Process::InitializePriority() {
+  if (!OneGroupPerRendererEnabled() || !IsValid() || !unique_token_.empty()) {
+    return;
+  }
+
+  // The token has the following format:
+  //   {cgroup_prefix}{UnguessableToken}
+  // The cgroup prefix is to distinguish ash from lacros tokens for stale
+  // cgroup cleanup.
+  unique_token_ = StrCat({CGroups::Get().group_prefix_token,
+                          UnguessableToken::Create().ToString()});
+
+  FilePath cgroup_path = CGroups::Get().GetForegroundCgroupDir(unique_token_);
+  // Note that CreateDirectoryAndGetError() does not fail if the directory
+  // already exits.
+  if (!CreateDirectoryAndGetError(cgroup_path, nullptr)) {
+    // If creating the directory fails, fall back to use the foreground group.
+    int saved_errno = errno;
+    LOG(ERROR) << "Failed to create cgroup, falling back to foreground"
+               << ", cgroup=" << cgroup_path
+               << ", errno=" << strerror(saved_errno);
+
+    unique_token_.clear();
+    return;
+  }
+
+  if (!CGroups::Get().uclamp_min.empty() &&
+      !WriteFile(cgroup_path.Append(kUclampMinFile),
+                 CGroups::Get().uclamp_min)) {
+    LOG(ERROR) << "Failed to write uclamp min file, cgroup_path="
+               << cgroup_path;
+  }
+  if (!CGroups::Get().uclamp_min.empty() &&
+      !WriteFile(cgroup_path.Append(kUclampMaxFile),
+                 CGroups::Get().uclamp_max)) {
+    LOG(ERROR) << "Failed to write uclamp max file, cgroup_path="
+               << cgroup_path;
+  }
+}
+
+// static
+void Process::CleanUpProcessScheduled(Process process, int remaining_retries) {
+  process.CleanUpProcess(remaining_retries);
+}
+
+void Process::CleanUpProcessAsync() const {
+  if (!OneGroupPerRendererEnabled() || unique_token_.empty()) {
+    return;
+  }
+
+  ThreadPool::PostTask(FROM_HERE, {MayBlock(), TaskPriority::BEST_EFFORT},
+                       BindOnce(&Process::CleanUpProcessScheduled, Duplicate(),
+                                kCgroupDeleteRetries));
+}
+
+void Process::CleanUpProcess(int remaining_retries) const {
+  if (!OneGroupPerRendererEnabled() || unique_token_.empty()) {
+    return;
+  }
+
+  // Try to delete the cgroup
+  // TODO(1322562): We can use notify_on_release to automoatically delete the
+  // cgroup when the process has left the cgroup.
+  FilePath cgroup = CGroups::Get().GetForegroundCgroupDir(unique_token_);
+  if (!DeleteFile(cgroup)) {
+    auto saved_errno = errno;
+    LOG(ERROR) << "Failed to delete cgroup " << cgroup
+               << ", errno=" << strerror(saved_errno);
+    // If the delete failed, then the process is still potentially in the
+    // cgroup. Move the process to background and schedule a callback to try
+    // again.
+    if (remaining_retries > 0) {
+      std::string pidstr = NumberToString(process_);
+      if (!WriteFile(CGroups::Get().background_file, pidstr)) {
+        // Failed to move the process, LOG a warning but try again.
+        saved_errno = errno;
+        LOG(WARNING) << "Failed to move the process to background"
+                     << ", pid=" << pidstr
+                     << ", errno=" << strerror(saved_errno);
+      }
+      ThreadPool::PostDelayedTask(FROM_HERE,
+                                  {MayBlock(), TaskPriority::BEST_EFFORT},
+                                  BindOnce(&Process::CleanUpProcessScheduled,
+                                           Duplicate(), remaining_retries - 1),
+                                  kCgroupDeleteRetryTime);
+    }
+  }
+}
+
+// static
+void Process::CleanUpStaleProcessStates() {
+  if (!OneGroupPerRendererEnabled()) {
+    return;
+  }
+
+  FileEnumerator traversal(FilePath(kFullRendererCgroupRoot), false,
+                           FileEnumerator::DIRECTORIES);
+  for (FilePath path = traversal.Next(); !path.empty();
+       path = traversal.Next()) {
+    std::string dirname = path.BaseName().value();
+    if (dirname == FilePath(kForeground).BaseName().value() ||
+        dirname == FilePath(kBackground).BaseName().value()) {
+      continue;
+    }
+
+    if (!StartsWith(dirname, kCgroupPrefix) ||
+        StartsWith(dirname, CGroups::Get().group_prefix_token)) {
+      continue;
+    }
+
+    if (!DeleteFile(path)) {
+      auto saved_errno = errno;
+      LOG(ERROR) << "Failed to delete " << path
+                 << ", errno=" << strerror(saved_errno);
+    }
+  }
+}
+#endif  // BUILDFLAG(IS_CHROMEOS)
+
 }  // namespace base
diff --git a/base/process/process_posix.cc b/base/process/process_posix.cc
index bc49d4d..f9f633a9 100644
--- a/base/process/process_posix.cc
+++ b/base/process/process_posix.cc
@@ -231,21 +231,27 @@
 
 namespace base {
 
-Process::Process(ProcessHandle handle) : process_(handle) {
-}
-
-Process::~Process() = default;
+Process::Process(ProcessHandle handle) : process_(handle) {}
 
 Process::Process(Process&& other) : process_(other.process_) {
+#if BUILDFLAG(IS_CHROMEOS)
+  unique_token_ = std::move(other.unique_token_);
+#endif
+
   other.Close();
 }
 
 Process& Process::operator=(Process&& other) {
   process_ = other.process_;
+#if BUILDFLAG(IS_CHROMEOS)
+  unique_token_ = std::move(other.unique_token_);
+#endif
   other.Close();
   return *this;
 }
 
+Process::~Process() = default;
+
 // static
 Process Process::Current() {
   return Process(GetCurrentProcessHandle());
@@ -286,7 +292,13 @@
   if (is_current())
     return Current();
 
+#if BUILDFLAG(IS_CHROMEOS)
+  Process duplicate = Process(process_);
+  duplicate.unique_token_ = unique_token_;
+  return duplicate;
+#else
   return Process(process_);
+#endif
 }
 
 ProcessHandle Process::Release() {
@@ -318,6 +330,11 @@
     DPLOG(ERROR) << "Unable to terminate process " << process_;
     return false;
   }
+
+#if BUILDFLAG(IS_CHROMEOS)
+  CleanUpProcessAsync();
+#endif
+
   if (!wait || WaitForExitWithTimeout(Seconds(60), nullptr)) {
     return true;
   }
@@ -354,7 +371,11 @@
   return exited;
 }
 
-void Process::Exited(int exit_code) const {}
+void Process::Exited(int exit_code) const {
+#if BUILDFLAG(IS_CHROMEOS)
+  CleanUpProcessAsync();
+#endif
+}
 
 int Process::GetPriority() const {
   DCHECK(IsValid());
diff --git a/base/process/process_unittest.cc b/base/process/process_unittest.cc
index 3e64014..261cbee 100644
--- a/base/process/process_unittest.cc
+++ b/base/process/process_unittest.cc
@@ -17,6 +17,22 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/multiprocess_func_list.h"
 
+#if BUILDFLAG(IS_CHROMEOS)
+#include <unistd.h>
+
+#include <vector>
+
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_split.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/test/scoped_feature_list.h"
+#include "base/test/task_environment.h"
+#include "base/time/time.h"
+#endif  // BUILDFLAG(IS_CHROMEOS)
+
 #if BUILDFLAG(IS_WIN)
 #include "base/win/base_win_buildflags.h"
 #include "base/win/windows_version.h"
@@ -44,6 +60,44 @@
 };
 #endif
 
+#if BUILDFLAG(IS_CHROMEOS)
+const char kForeground[] = "/chrome_renderers/foreground";
+const char kCgroupRoot[] = "/sys/fs/cgroup/cpu";
+const char kFullRendererCgroupRoot[] = "/sys/fs/cgroup/cpu/chrome_renderers";
+const char kProcPath[] = "/proc/%d/cgroup";
+
+std::string GetProcessCpuCgroup(const base::Process& process) {
+  std::string proc;
+  if (!base::ReadFileToString(
+          base::FilePath(base::StringPrintf(kProcPath, process.Pid())),
+          &proc)) {
+    return std::string();
+  }
+
+  std::vector<base::StringPiece> lines = SplitStringPiece(
+      proc, "\n", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
+  for (const auto& line : lines) {
+    std::vector<base::StringPiece> fields = SplitStringPiece(
+        line, ":", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
+    if (fields.size() != 3U) {
+      continue;
+    }
+
+    if (fields[1] == "cpu") {
+      return static_cast<std::string>(fields[2]);
+    }
+  }
+
+  return std::string();
+}
+
+bool AddProcessToCpuCgroup(const base::Process& process, std::string& cgroup) {
+  base::FilePath path(cgroup);
+  path = path.Append("cgroup.procs");
+  return base::WriteFile(path, base::NumberToString(process.Pid()));
+}
+#endif  // BUILDFLAG(IS_CHROMEOS)
+
 }  // namespace
 
 namespace base {
@@ -417,6 +471,188 @@
   EXPECT_TRUE(IsProcessBackgroundedCGroup(kBackgrounded));
 }
 
+TEST_F(ProcessTest, InitializePriorityEmptyProcess) {
+  // TODO(b/172213843): base::Process is used by base::TestSuite::Initialize
+  // before we can use ScopedFeatureList here. Update the test to allow the
+  // use of ScopedFeatureList before base::TestSuite::Initialize runs.
+  if (!Process::OneGroupPerRendererEnabled())
+    return;
+
+  Process process;
+  process.InitializePriority();
+  const std::string unique_token = process.unique_token();
+  ASSERT_TRUE(unique_token.empty());
+}
+
+TEST_F(ProcessTest, SetProcessBackgroundedOneCgroupPerRender) {
+  if (!Process::OneGroupPerRendererEnabled())
+    return;
+
+  base::test::TaskEnvironment task_env;
+
+  Process process(SpawnChild("SimpleChildProcess"));
+  process.InitializePriority();
+  const std::string unique_token = process.unique_token();
+  ASSERT_FALSE(unique_token.empty());
+
+  EXPECT_TRUE(process.SetProcessBackgrounded(false));
+  EXPECT_FALSE(process.IsProcessBackgrounded());
+  std::string cgroup = GetProcessCpuCgroup(process);
+  EXPECT_FALSE(cgroup.empty());
+  EXPECT_NE(cgroup.find(unique_token), std::string::npos);
+
+  EXPECT_TRUE(process.SetProcessBackgrounded(true));
+  EXPECT_TRUE(process.IsProcessBackgrounded());
+
+  EXPECT_TRUE(process.Terminate(0, false));
+  // Terminate should post a task, wait for it to run
+  task_env.RunUntilIdle();
+
+  cgroup = std::string(kCgroupRoot) + cgroup;
+  EXPECT_FALSE(base::DirectoryExists(FilePath(cgroup)));
+}
+
+TEST_F(ProcessTest, CleanUpBusyProcess) {
+  if (!Process::OneGroupPerRendererEnabled())
+    return;
+
+  base::test::TaskEnvironment task_env;
+
+  Process process(SpawnChild("SimpleChildProcess"));
+  process.InitializePriority();
+  const std::string unique_token = process.unique_token();
+  ASSERT_FALSE(unique_token.empty());
+
+  EXPECT_TRUE(process.SetProcessBackgrounded(false));
+  EXPECT_FALSE(process.IsProcessBackgrounded());
+  std::string cgroup = GetProcessCpuCgroup(process);
+  EXPECT_FALSE(cgroup.empty());
+  EXPECT_NE(cgroup.find(unique_token), std::string::npos);
+
+  // Add another process to the cgroup to ensure it stays busy.
+  cgroup = std::string(kCgroupRoot) + cgroup;
+  Process process2(SpawnChild("SimpleChildProcess"));
+  EXPECT_TRUE(AddProcessToCpuCgroup(process2, cgroup));
+
+  // Terminate the first process that should tirgger a cleanup of the cgroup
+  EXPECT_TRUE(process.Terminate(0, false));
+  // Wait until the background task runs once. This should fail and requeue
+  // another task to retry.
+  task_env.RunUntilIdle();
+  EXPECT_TRUE(base::DirectoryExists(FilePath(cgroup)));
+
+  // Move the second process to free the cgroup
+  std::string foreground_path =
+      std::string(kCgroupRoot) + std::string(kForeground);
+  EXPECT_TRUE(AddProcessToCpuCgroup(process2, foreground_path));
+
+  // Wait for the retry.
+  PlatformThread::Sleep(base::Milliseconds(1100));
+  task_env.RunUntilIdle();
+  // The cgroup should be deleted now.
+  EXPECT_FALSE(base::DirectoryExists(FilePath(cgroup)));
+
+  process2.Terminate(0, false);
+}
+
+TEST_F(ProcessTest, SetProcessBackgroundedEmptyToken) {
+  if (!Process::OneGroupPerRendererEnabled())
+    return;
+
+  Process process(SpawnChild("SimpleChildProcess"));
+  const std::string unique_token = process.unique_token();
+  ASSERT_TRUE(unique_token.empty());
+
+  // Moving to the foreground should use the default foregorund path
+  EXPECT_TRUE(process.SetProcessBackgrounded(false));
+  EXPECT_FALSE(process.IsProcessBackgrounded());
+  std::string cgroup = GetProcessCpuCgroup(process);
+  EXPECT_FALSE(cgroup.empty());
+  EXPECT_EQ(cgroup, kForeground);
+}
+
+TEST_F(ProcessTest, CleansUpStaleGroups) {
+  if (!Process::OneGroupPerRendererEnabled())
+    return;
+
+  base::test::TaskEnvironment task_env;
+
+  // Create a process that will not be cleaned up
+  Process process(SpawnChild("SimpleChildProcess"));
+  process.InitializePriority();
+  const std::string unique_token = process.unique_token();
+  ASSERT_FALSE(unique_token.empty());
+
+  EXPECT_TRUE(process.SetProcessBackgrounded(true));
+  EXPECT_TRUE(process.IsProcessBackgrounded());
+
+  // Create a stale cgroup
+  std::string root = kFullRendererCgroupRoot;
+  std::string cgroup = root + "/" + unique_token;
+  std::vector<std::string> tokens = base::SplitString(
+      cgroup, "-", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL);
+  tokens[1] = "fake";
+  std::string fake_cgroup = base::JoinString(tokens, "-");
+  EXPECT_TRUE(base::CreateDirectory(FilePath(fake_cgroup)));
+
+  // Clean up stale groups
+  Process::CleanUpStaleProcessStates();
+
+  // validate the fake group is deleted
+  EXPECT_FALSE(base::DirectoryExists(FilePath(fake_cgroup)));
+
+  // validate the active process cgroup is not deleted
+  EXPECT_TRUE(base::DirectoryExists(FilePath(cgroup)));
+
+  // validate foreground and background are not deleted
+  EXPECT_TRUE(base::DirectoryExists(FilePath(root + "/foreground")));
+  EXPECT_TRUE(base::DirectoryExists(FilePath(root + "/background")));
+
+  // clean up the process
+  EXPECT_TRUE(process.Terminate(0, false));
+  // Terminate should post a task, wait for it to run
+  task_env.RunUntilIdle();
+  EXPECT_FALSE(base::DirectoryExists(FilePath(cgroup)));
+}
+
+TEST_F(ProcessTest, OneCgroupDoesNotCleanUpGroupsWithWrongPrefix) {
+  if (!Process::OneGroupPerRendererEnabled())
+    return;
+
+  base::test::TaskEnvironment task_env;
+
+  // Create a process that will not be cleaned up
+  Process process(SpawnChild("SimpleChildProcess"));
+  process.InitializePriority();
+  const std::string unique_token = process.unique_token();
+  ASSERT_FALSE(unique_token.empty());
+
+  EXPECT_TRUE(process.SetProcessBackgrounded(false));
+  EXPECT_FALSE(process.IsProcessBackgrounded());
+  std::string cgroup = GetProcessCpuCgroup(process);
+  EXPECT_FALSE(cgroup.empty());
+  EXPECT_NE(cgroup.find(unique_token), std::string::npos);
+
+  // Create a stale cgroup
+  FilePath cgroup_path = FilePath(std::string(kCgroupRoot) + cgroup);
+  FilePath fake_cgroup = FilePath(kFullRendererCgroupRoot).AppendASCII("fake");
+  EXPECT_TRUE(base::CreateDirectory(fake_cgroup));
+
+  // Clean up stale groups
+  Process::CleanUpStaleProcessStates();
+
+  // validate the fake group is deleted
+  EXPECT_TRUE(base::DirectoryExists(fake_cgroup));
+  EXPECT_TRUE(base::DirectoryExists(cgroup_path));
+
+  // clean up the process
+  EXPECT_TRUE(process.SetProcessBackgrounded(true));
+  EXPECT_TRUE(process.IsProcessBackgrounded());
+  EXPECT_TRUE(process.Terminate(0, false));
+  task_env.RunUntilIdle();
+  EXPECT_FALSE(base::DirectoryExists(cgroup_path));
+  base::DeleteFile(fake_cgroup);
+}
 #endif  // BUILDFLAG(IS_CHROMEOS)
 
 }  // namespace base
diff --git a/base/system/sys_info.cc b/base/system/sys_info.cc
index daedeb7..adb238f 100644
--- a/base/system/sys_info.cc
+++ b/base/system/sys_info.cc
@@ -100,7 +100,8 @@
 #endif
 
 void SysInfo::GetHardwareInfo(base::OnceCallback<void(HardwareInfo)> callback) {
-#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_APPLE)
+#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_APPLE) || \
+    BUILDFLAG(IS_FUCHSIA)
   base::ThreadPool::PostTaskAndReplyWithResult(
       FROM_HERE, {}, base::BindOnce(&GetHardwareInfoSync), std::move(callback));
 #elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
diff --git a/base/system/sys_info_fuchsia.cc b/base/system/sys_info_fuchsia.cc
index fd77757..e857b41 100644
--- a/base/system/sys_info_fuchsia.cc
+++ b/base/system/sys_info_fuchsia.cc
@@ -5,6 +5,7 @@
 #include "base/system/sys_info.h"
 
 #include <fuchsia/buildinfo/cpp/fidl.h>
+#include <fuchsia/hwinfo/cpp/fidl.h>
 #include <sys/statvfs.h>
 #include <zircon/syscalls.h>
 
@@ -206,4 +207,14 @@
   return getpagesize();
 }
 
+SysInfo::HardwareInfo SysInfo::GetHardwareInfoSync() {
+  const auto& product_info = GetCachedProductInfo();
+
+  return {
+      .manufacturer =
+          product_info.has_manufacturer() ? product_info.manufacturer() : "",
+      .model = product_info.has_model() ? product_info.model() : "",
+  };
+}
+
 }  // namespace base
diff --git a/base/system/sys_info_unittest.cc b/base/system/sys_info_unittest.cc
index 1b0bcae..226ef8c2 100644
--- a/base/system/sys_info_unittest.cc
+++ b/base/system/sys_info_unittest.cc
@@ -233,7 +233,7 @@
   EXPECT_TRUE(IsStringUTF8(hardware_info->model));
   bool empty_result_expected =
 #if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_WIN) || \
-    BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
+    BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_FUCHSIA)
       false;
 #else
       true;
diff --git a/build/android/gyp/proguard.py b/build/android/gyp/proguard.py
index ad16a8a..6fd132f 100755
--- a/build/android/gyp/proguard.py
+++ b/build/android/gyp/proguard.py
@@ -464,10 +464,6 @@
         'com.google.common.flogger.backend.google.GooglePlatform',
         'com.google.common.flogger.backend.system.DefaultPlatform',
 
-        # trichrome_webview_google_bundle contains this missing reference.
-        # TODO(crbug.com/1142530): Fix this missing reference properly.
-        'org.chromium.build.NativeLibraries',
-
         # TODO(agrieve): Exclude these only when use_jacoco_coverage=true.
         'java.lang.instrument.ClassFileTransformer',
         'java.lang.instrument.IllegalClassFormatException',
diff --git a/build/config/android/rules.gni b/build/config/android/rules.gni
index 1a9b3736..6c119948 100644
--- a/build/config/android/rules.gni
+++ b/build/config/android/rules.gni
@@ -2133,6 +2133,7 @@
   #     uncompressed in the APK. Must be unset or true if load_library_from_apk
   #     is set to true.
   #   uncompress_dex: Store final .dex files uncompressed in the apk.
+  #   omit_dex: If true, do not build or include classes.dex.
   #   strip_resource_names: True if resource names should be stripped from the
   #     resources.arsc file in the apk or module.
   #   strip_unused_resources: True if unused resources should be stripped from
@@ -2215,6 +2216,7 @@
           defined(invoker.is_base_module) && invoker.is_base_module
     }
 
+    _omit_dex = defined(invoker.omit_dex) && invoker.omit_dex
     _enable_multidex =
         !defined(invoker.enable_multidex) || invoker.enable_multidex
 
@@ -2358,12 +2360,13 @@
     _rebased_build_config = rebase_path(_build_config, root_build_dir)
     assert(_rebased_build_config != "")  # Mark as used.
 
-    _generate_buildconfig_java = !defined(invoker.apk_under_test)
+    _generate_buildconfig_java = !defined(invoker.apk_under_test) && !_omit_dex
     if (defined(invoker.generate_buildconfig_java)) {
       _generate_buildconfig_java = invoker.generate_buildconfig_java
     }
 
-    _generate_productconfig_java = defined(invoker.product_config_java_packages)
+    _generate_productconfig_java =
+        defined(invoker.product_config_java_packages) && !_omit_dex
 
     # JNI generation usually goes hand-in-hand with buildconfig generation.
     _generate_final_jni = _generate_buildconfig_java
@@ -2427,7 +2430,7 @@
       _incremental_apk_path = "${_final_apk_path_no_ext}_incremental.apk"
     }
 
-    if (!_incremental_apk) {
+    if (!_incremental_apk && !_omit_dex) {
       # Bundle modules don't build the dex here, but need to write this path
       # to their .build_config.json file.
       if (_proguard_enabled) {
@@ -2684,8 +2687,9 @@
     } else {
       _generate_native_libraries_java =
           (!_is_bundle_module || _is_base_module) &&
-          (_native_libs_deps != [] || _secondary_abi_native_libs_deps != []) &&
-          !_uses_static_library_synchronized_proguard
+          (_native_libs_deps != [] || _secondary_abi_native_libs_deps != [] ||
+           defined(invoker.static_library_provider)) &&
+          !_uses_static_library_synchronized_proguard && !_omit_dex
     }
     if (_generate_native_libraries_java) {
       write_native_libraries_java("${_template_name}__native_libraries") {
@@ -2694,7 +2698,17 @@
         # Do not add a dep on the generated_file target in order to avoid having
         # to build the native libraries before this target. The dependency is
         # instead captured via a depfile.
-        if (_native_libs_deps != []) {
+        if (_uses_static_library) {
+          _prefix = get_label_info(invoker.static_library_provider,
+                                   "target_gen_dir") + "/" +
+                    get_label_info(invoker.static_library_provider, "name")
+          if (defined(invoker.static_library_provider_use_secondary_abi) &&
+              invoker.static_library_provider_use_secondary_abi) {
+            native_libraries_list_file = "${_prefix}.secondary_abi_native_libs"
+          } else {
+            native_libraries_list_file = "${_prefix}.native_libs"
+          }
+        } else if (_native_libs_deps != []) {
           native_libraries_list_file = _shared_library_list_file
         } else {
           native_libraries_list_file = _secondary_abi_shared_library_list_file
@@ -2790,14 +2804,15 @@
                  ])
     }
 
-    _java_target = "${_template_name}__java"
-
     if (_is_bundle_module) {
       _add_view_trace_events =
           defined(invoker.add_view_trace_events) &&
           invoker.add_view_trace_events && enable_trace_event_bytecode_rewriting
     }
 
+    # We cannot skip this target when omit_dex = true because it writes the
+    # build_config.json.
+    _java_target = "${_template_name}__java"
     java_library_impl(_java_target) {
       forward_variables_from(invoker,
                              [
@@ -2929,7 +2944,7 @@
 
     if (_uses_static_library_synchronized_proguard) {
       _final_dex_target_dep = "${invoker.static_library_provider}__dexsplitter"
-    } else if (_is_bundle_module && _proguard_enabled) {
+    } else if ((_is_bundle_module && _proguard_enabled) || _omit_dex) {
       _final_deps += [ ":$_java_target" ]
     } else if (_incremental_apk) {
       if (defined(invoker.enable_proguard_checks)) {
@@ -3205,7 +3220,7 @@
         deps = _deps + [ ":$_build_config_target" ]
 
         if ((!_proguard_enabled || _incremental_apk) &&
-            enable_jdk_library_desugaring) {
+            enable_jdk_library_desugaring && !_omit_dex) {
           _all_jdk_libs = "//build/android:all_jdk_libs"
           deps += [ _all_jdk_libs ]
           jdk_libs_dex = get_label_info(_all_jdk_libs, "target_out_dir") +
@@ -3485,6 +3500,7 @@
                                "expected_libs_and_assets_base",
                                "generate_buildconfig_java",
                                "generate_final_jni",
+                               "generate_native_libraries_java",
                                "include_size_info",
                                "input_jars_paths",
                                "use_modern_linker",
@@ -3509,6 +3525,7 @@
                                "native_lib_placeholders",
                                "never_incremental",
                                "no_xml_namespaces",
+                               "omit_dex",
                                "png_to_webp",
                                "post_process_package_resources_script",
                                "processor_args_javac",
@@ -3533,6 +3550,7 @@
                                "srcjar_deps",
                                "static_library_dependent_targets",
                                "static_library_provider",
+                               "static_library_provider_use_secondary_abi",
                                "static_library_synchronized_proguard",
                                "target_sdk_version",
                                "testonly",
@@ -3632,6 +3650,7 @@
                                "load_library_from_apk",
                                "loadable_modules",
                                "product_config_java_packages",
+                               "main_component_library",
                                "manifest_package",
                                "max_sdk_version",
                                "min_sdk_version",
@@ -3660,6 +3679,7 @@
                                "short_resource_paths",
                                "srcjar_deps",
                                "static_library_provider",
+                               "static_library_provider_use_secondary_abi",
                                "static_library_synchronized_proguard",
                                "strip_resource_names",
                                "strip_unused_resources",
diff --git a/build/config/fuchsia/test/minimum.shard.test-cml b/build/config/fuchsia/test/minimum.shard.test-cml
index 31ffcaf..432b77ac 100644
--- a/build/config/fuchsia/test/minimum.shard.test-cml
+++ b/build/config/fuchsia/test/minimum.shard.test-cml
@@ -64,6 +64,7 @@
     },
     {
       protocol: [
+        "fuchsia.hwinfo.Product",
         "fuchsia.media.ProfileProvider",
         "fuchsia.process.Launcher",
         "fuchsia.sys.Loader",
diff --git a/build/config/fuchsia/test/minimum_capabilities.test-cmx b/build/config/fuchsia/test/minimum_capabilities.test-cmx
index 09c26346..ed903be 100644
--- a/build/config/fuchsia/test/minimum_capabilities.test-cmx
+++ b/build/config/fuchsia/test/minimum_capabilities.test-cmx
@@ -3,6 +3,11 @@
     "fuchsia.test": {
       "injected-services": {
         "fuchsia.buildinfo.Provider": "fuchsia-pkg://fuchsia.com/build-info-service#meta/build-info.cmx",
+        "fuchsia.factory.MiscFactoryStoreProvider": [
+            "fuchsia-pkg://fuchsia.com/fake_factory_store_providers#meta/misc.cmx",
+            "--config=/config/data/fuchsia.factory.MiscFactoryStoreProvider.config"
+        ],
+        "fuchsia.hwinfo.Product": "fuchsia-pkg://fuchsia.com/hwinfo#meta/hwinfo.cmx",
         "fuchsia.intl.PropertyProvider": "fuchsia-pkg://fuchsia.com/intl_property_manager#meta/intl_property_manager_v1.cmx"
       },
       "system-services": [
@@ -19,6 +24,8 @@
     ],
     "services": [
       "fuchsia.buildinfo.Provider",
+      "fuchsia.factory.MiscFactoryStoreProvider",
+      "fuchsia.hwinfo.Product",
       "fuchsia.intl.PropertyProvider",
       "fuchsia.logger.LogSink",
       "fuchsia.media.ProfileProvider",
diff --git a/build/fuchsia/linux_internal.sdk.sha1 b/build/fuchsia/linux_internal.sdk.sha1
index 21ca36d..074f8b8 100644
--- a/build/fuchsia/linux_internal.sdk.sha1
+++ b/build/fuchsia/linux_internal.sdk.sha1
@@ -1 +1 @@
-8.20220525.4.1
+8.20220526.4.1
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index a19272e6..693b89f 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -2925,12 +2925,6 @@
   }
 }
 
-# TODO(agrieve): Remove this once we switch to using bundle targets to
-# generate APK stubs.
-android_resources("trichrome_dummy_resources") {
-  sources = [ "trichrome/res_dummy/values/strings.xml" ]
-}
-
 chrome_public_unit_test_apk_manifest =
     "$root_gen_dir/chrome_public_unit_test_apk_manifest/AndroidManifest.xml"
 chrome_public_test_apk_manifest =
diff --git a/chrome/android/chrome_public_apk_tmpl.gni b/chrome/android/chrome_public_apk_tmpl.gni
index 6fb71709..87220dc 100644
--- a/chrome/android/chrome_public_apk_tmpl.gni
+++ b/chrome/android/chrome_public_apk_tmpl.gni
@@ -235,6 +235,19 @@
       use_chromium_linker = chromium_linker_supported
     }
 
+    if (_is_trichrome) {
+      static_library_provider_use_secondary_abi = _is_secondary_abi_primary
+
+      # http://crbug.com/1042107.
+      if (is_component_build) {
+        if (android_64bit_target_cpu && _is_64_bit_browser) {
+          main_component_library = "libmonochrome_64.cr.so"
+        } else {
+          main_component_library = "libmonochrome.cr.so"
+        }
+      }
+    }
+
     if (!_is_monochrome && !_is_trichrome) {
       deps += [
         "//chrome/android:chrome_public_v8_assets",
diff --git a/chrome/android/expectations/trichrome_library_apk.AndroidManifest.expected b/chrome/android/expectations/trichrome_library_apk.AndroidManifest.expected
index fd2f714..cb6d641c 100644
--- a/chrome/android/expectations/trichrome_library_apk.AndroidManifest.expected
+++ b/chrome/android/expectations/trichrome_library_apk.AndroidManifest.expected
@@ -10,6 +10,7 @@
   <uses-sdk android:minSdkVersion="29" android:targetSdkVersion="31"/>
   <application
       android:extractNativeLibs="false"
+      android:hasCode="false"
       android:icon="@drawable/icon_webview"
       android:label="Trichrome Library"
       android:multiArch="true"
diff --git a/chrome/android/java/AndroidManifest_trichrome_library.xml b/chrome/android/java/AndroidManifest_trichrome_library.xml
index 97e9ddd..070efc3 100644
--- a/chrome/android/java/AndroidManifest_trichrome_library.xml
+++ b/chrome/android/java/AndroidManifest_trichrome_library.xml
@@ -18,6 +18,7 @@
 
     <!-- TODO(torne): we should specify an icon, roundIcon, and label from resources. -->
     <application
+        android:hasCode="false"
         android:label="{{ application_label|default('Trichrome Library') }}"
         android:icon="@drawable/icon_webview"
         android:multiArch="true"
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/app/appmenu/AppMenuPropertiesDelegateImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/app/appmenu/AppMenuPropertiesDelegateImpl.java
index 0cb36ee..ec145ccf 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/app/appmenu/AppMenuPropertiesDelegateImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/app/appmenu/AppMenuPropertiesDelegateImpl.java
@@ -299,6 +299,7 @@
     private boolean isInTabSwitcher() {
         return mLayoutStateProvider != null
                 && mLayoutStateProvider.isLayoutVisible(LayoutType.TAB_SWITCHER)
+                && !mLayoutStateProvider.isLayoutStartingToHide(LayoutType.TAB_SWITCHER)
                 && !isInStartSurfaceHomepage();
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerImpl.java
index 2ce5c4a8..c4902b05 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerImpl.java
@@ -1186,6 +1186,11 @@
     }
 
     @Override
+    public boolean isLayoutStartingToHide(int layoutType) {
+        return isLayoutVisible(layoutType) && getActiveLayout().isStartingToHide();
+    }
+
+    @Override
     public void addObserver(LayoutStateObserver listener) {
         mLayoutObservers.addObserver(listener);
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabIntentDataProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabIntentDataProvider.java
index 46fed5e..c6f7979 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabIntentDataProvider.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabIntentDataProvider.java
@@ -609,6 +609,11 @@
     }
 
     @Override
+    public boolean isPartialHeightCustomTab() {
+        return getInitialActivityHeight() > 0;
+    }
+
+    @Override
     public boolean shouldAnimateOnFinish() {
         return mAnimationBundle != null && getClientPackageName() != null;
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/PartialCustomTabHeightStrategy.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/PartialCustomTabHeightStrategy.java
index 9af423e..4e1a40e1 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/PartialCustomTabHeightStrategy.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/PartialCustomTabHeightStrategy.java
@@ -87,7 +87,7 @@
     private final Activity mActivity;
     private final @Px int mInitialHeight;
     private final @Px int mMaxHeight;
-    private final @Px int mNavbarHeight;
+
     private final @Px int mFullyExpandedAdjustmentHeight;
     private final Integer mNavigationBarColor;
     private final Integer mNavigationBarDividerColor;
@@ -100,6 +100,9 @@
     private @HeightStatus int mStatus = HeightStatus.INITIAL_HEIGHT;
     private @HeightStatus int mTargetStatus;
 
+    // Bottom navigation bar height. Set to zero when the bar is positioned on the right side
+    // in landcape mode.
+    private @Px int mNavbarHeight;
     private int mOrientation;
     private boolean mIsInMultiWindowMode;
 
@@ -250,13 +253,17 @@
         mActivity = activity;
         mParentViewSupplier = parentViewSupplier;
         mMaxHeight = getMaximumPossibleHeight();
-        mNavbarHeight = getNavbarHeight(); // Needs mMaxHeight.
         mInitialHeight = MathUtils.clamp(
                 initialHeight, mMaxHeight, (int) (mMaxHeight * MINIMAL_HEIGHT_RATIO));
+
+        // Invoked twice - when populated/destroyed(null)
         parentViewSupplier.addObserver(parentView -> {
-            // Invoked twice: populated(on init) -> null(on destruction)
+            // When the navigation bar on the right side (not at the bottom), no need to call
+            // the methods below since the contents height is fixed and the system navigation
+            // bar works as expected.
+            if (mNavbarHeight == 0) return;
             setContentsHeight();
-            showNavbar(parentView != null);
+            updateNavbarVisibility(parentView != null);
         });
 
         mOnResizedCallback = onResizedCallback;
@@ -302,7 +309,6 @@
             @Override
             public void onAnimationCancel(Animator animator) {}
         };
-        mActivity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);
         initializeHeight();
     }
 
@@ -314,7 +320,7 @@
                     .getInsets(WindowInsets.Type.navigationBars())
                     .bottom;
         }
-        return mMaxHeight - getAppUsableScreenHeight();
+        return getDisplayHeight() - getAppUsableScreenHeight();
     }
 
     private int getAppUsableScreenHeight() {
@@ -358,6 +364,8 @@
         if (newConfig.orientation != mOrientation) {
             mOrientation = newConfig.orientation;
             initializeHeight();
+            setContentsHeight();
+            updateNavbarVisibility(true);
         }
     }
 
@@ -396,21 +404,24 @@
         mActivity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL);
         mActivity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
 
+        mNavbarHeight = getNavbarHeight();
         int maxHeight = getDisplayHeight();
         int maxExpandedY = getFullyExpandedYCoordinate();
         final @Px int height;
+
         if (mOrientation == Configuration.ORIENTATION_LANDSCAPE) {
-            height = maxHeight - maxExpandedY;
             // Resizing by user dragging is not supported in landscape mode; no need to set
             // the status here.
+            height = maxHeight - maxExpandedY;
+            mActivity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);
         } else {
             height = mInitialHeight;
             mStatus = HeightStatus.INITIAL_HEIGHT;
+            mActivity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);
         }
 
         WindowManager.LayoutParams attributes = mActivity.getWindow().getAttributes();
-        // TODO(ctzsm): Consider to handle rotation and resizing when entering/exiting multi-window
-        // mode.
+        // TODO(jinsukkim): Handle multi-window mode.
         if (attributes.height == height) return;
 
         // We do not resize Window but just translate its vertical offset, and resize the parent
@@ -435,16 +446,19 @@
 
     private void onMoveStart() {
         showSpinnerView();
-        showNavbar(false);
+        updateNavbarVisibility(false);
     }
 
     private void onMoveEnd() {
         setContentsHeight();
+
+        // TODO(crbug.com/1328555): Look into observing a view resize event to ensure the fade
+        // animation can always cover the transition artifact.
         mSpinnerView.animate()
                 .alpha(0f)
                 .setDuration(SPINNER_FADE_DURATION_MS)
                 .setListener(mSpinnerFadeoutAnimatorListener);
-        showNavbar(true);
+        updateNavbarVisibility(true);
     }
 
     private void showSpinnerView() {
@@ -500,15 +514,17 @@
         int windowPos = mActivity.getWindow().getAttributes().y;
         lp.height = getDisplayHeight() - windowPos - mHandleHeight - mNavbarHeight;
         parentView.setLayoutParams(lp);
-        if (oldHeight >= 0 && lp.height != oldHeight) {
-            mOnResizedCallback.onResized(lp.height);
-        }
+        if (oldHeight >= 0 && lp.height != oldHeight) mOnResizedCallback.onResized(lp.height);
     }
 
     // Show or hide our own navigation bar.
-    // TODO: Handle landscape mode where the 3-button navigation bar is located on a side,
-    //       not at the bottom.
-    private void showNavbar(boolean show) {
+    private void updateNavbarVisibility(boolean show) {
+        // No need draw its own navigation bar when it is located on the right side since
+        // the system navigation bar is visible and can handle API #setNavigationBarColor.
+        if (mNavbarHeight == 0) {
+            if (mNavbar != null) mNavbar.setVisibility(View.GONE);
+            return;
+        }
         if (show) {
             if (mNavbar == null) {
                 mNavbar = (LinearLayout) mActivity.getLayoutInflater().inflate(
@@ -646,4 +662,9 @@
         mSpinner = spinner;
         mToolbarView = toolbar;
     }
+
+    @VisibleForTesting
+    int getNavbarHeightForTesting() {
+        return mNavbarHeight;
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/CustomTabNavigationBarController.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/CustomTabNavigationBarController.java
index e53b429..63cc68e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/CustomTabNavigationBarController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/CustomTabNavigationBarController.java
@@ -33,10 +33,14 @@
         Integer navigationBarDividerColor =
                 intentDataProvider.getColorProvider().getNavigationBarDividerColor();
 
+        // PCCT is deemed incapable of system dark button support due to the way it implements
+        // partial height (window coordinate translation). We do the darkening ourselves.
+        boolean supportsDarkButtons = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
+                && !intentDataProvider.isPartialHeightCustomTab();
         boolean needsDarkButtons = navigationBarColor != null
                 && !ColorUtils.shouldUseLightForegroundOnBackground(navigationBarColor);
 
-        updateBarColor(window, navigationBarColor, needsDarkButtons);
+        updateBarColor(window, navigationBarColor, supportsDarkButtons, needsDarkButtons);
 
         // navigationBarDividerColor can only be set in Android P+
         if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) return;
@@ -49,12 +53,10 @@
     /**
      * Sets the navigation bar color according to intent extras.
      */
-    private static void updateBarColor(
-            Window window, Integer navigationBarColor, boolean needsDarkButtons) {
+    private static void updateBarColor(Window window, Integer navigationBarColor,
+            boolean supportsDarkButtons, boolean needsDarkButtons) {
         if (navigationBarColor == null) return;
 
-        boolean supportsDarkButtons = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O;
-
         if (supportsDarkButtons) {
             UiUtils.setNavigationBarIconColor(window.getDecorView().getRootView(),
                     needsDarkButtons);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappActivity.java
index a96ea1e..7b2518e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappActivity.java
@@ -92,6 +92,8 @@
 
     @Override
     protected LaunchCauseMetrics createLaunchCauseMetrics() {
-        return new WebappLaunchCauseMetrics(this, mWebappActivityCoordinator.getWebappInfo());
+        return new WebappLaunchCauseMetrics(this,
+                mWebappActivityCoordinator == null ? null
+                                                   : mWebappActivityCoordinator.getWebappInfo());
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappLaunchCauseMetrics.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappLaunchCauseMetrics.java
index f5587ad..a29e5c4 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappLaunchCauseMetrics.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappLaunchCauseMetrics.java
@@ -6,6 +6,8 @@
 
 import android.app.Activity;
 
+import androidx.annotation.Nullable;
+
 import org.chromium.chrome.browser.app.metrics.LaunchCauseMetrics;
 import org.chromium.chrome.browser.browserservices.intents.WebappInfo;
 import org.chromium.components.webapps.ShortcutSource;
@@ -15,15 +17,17 @@
  * LaunchCauseMetrics for WebappActivity.
  */
 public class WebappLaunchCauseMetrics extends LaunchCauseMetrics {
+    @Nullable
     private WebappInfo mWebappInfo;
 
-    public WebappLaunchCauseMetrics(Activity activity, WebappInfo info) {
+    public WebappLaunchCauseMetrics(Activity activity, @Nullable WebappInfo info) {
         super(activity);
         mWebappInfo = info;
     }
 
     @Override
     public @LaunchCause int computeIntentLaunchCause() {
+        if (mWebappInfo == null) return LaunchCause.OTHER;
         if (mWebappInfo.isLaunchedFromHomescreen()) {
             if (mWebappInfo.isForWebApk()) {
                 if (mWebappInfo.distributor() == WebApkDistributor.BROWSER) {
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/RecentlyClosedBridgeTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/RecentlyClosedBridgeTest.java
index 2e22e24..38d0601 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/RecentlyClosedBridgeTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/RecentlyClosedBridgeTest.java
@@ -4,6 +4,7 @@
 
 package org.chromium.chrome.browser.ntp;
 
+import androidx.test.filters.LargeTest;
 import androidx.test.filters.MediumTest;
 
 import org.junit.After;
@@ -16,6 +17,7 @@
 
 import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.CommandLineFlags;
+import org.chromium.base.test.util.CriteriaHelper;
 import org.chromium.base.test.util.Restriction;
 import org.chromium.chrome.browser.ChromeTabbedActivity;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
@@ -587,6 +589,104 @@
     }
 
     /**
+     * Tests opening a specific closed group and that it persists across restarts.
+     */
+    @Test
+    @LargeTest
+    @EnableFeatures({ChromeFeatureList.BULK_TAB_RESTORE, ChromeFeatureList.TAB_GROUPS_ANDROID})
+    @Restriction({Restriction.RESTRICTION_TYPE_NON_LOW_END_DEVICE})
+    public void testOpenRecentlyClosedEntry_Group_FromGroupClosure_WithRestart() {
+        if (mTabGroupModelFilter == null) return;
+
+        // Tab order is inverted in RecentlyClosedEntry as most recent comes first so log data in
+        // reverse.
+        final String urls[] =
+                new String[] {getUrl(TEST_PAGE_C), getUrl(TEST_PAGE_B), getUrl(TEST_PAGE_A)};
+        final Tab tabA = sActivityTestRule.loadUrlInNewTab(urls[2], /*incognito=*/false);
+        final Tab tabB = sActivityTestRule.loadUrlInNewTab(urls[1], /*incognito=*/false);
+        final Tab tabC = sActivityTestRule.loadUrlInNewTab(urls[0], /*incognito=*/false);
+
+        final String[] titles = new String[3];
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            mTabGroupModelFilter.mergeTabsToGroup(tabB.getId(), tabA.getId());
+            mTabGroupModelFilter.mergeTabsToGroup(tabC.getId(), tabA.getId());
+            TabGroupTitleUtils.storeTabGroupTitle(tabA.getId(), "Bar");
+            titles[2] = tabA.getTitle();
+            titles[1] = tabB.getTitle();
+            titles[0] = tabC.getTitle();
+            mTabModel.closeMultipleTabs(Arrays.asList(new Tab[] {tabA, tabB, tabC}), false);
+        });
+
+        final List<RecentlyClosedEntry> recentEntries = new ArrayList<>();
+        final int tabCount = getRecentEntriesAndReturnActiveTabCount(recentEntries);
+        Assert.assertEquals(1, tabCount);
+        Assert.assertEquals(1, recentEntries.size());
+        assertEntryIs(recentEntries.get(0), RecentlyClosedGroup.class, new String[] {"Bar"}, titles,
+                urls);
+
+        final RecentlyClosedGroup group = (RecentlyClosedGroup) recentEntries.get(0);
+        TestThreadUtils.runOnUiThreadBlocking(
+                () -> { mRecentlyClosedBridge.openRecentlyClosedEntry(mTabModel, group); });
+
+        // 1. Blank tab
+        // 2. tabA restored in new tab.
+        // 3. tabB restored in new tab.
+        // 4. tabC restored in new tab.
+        final List<Tab> tabs = getAllTabs();
+        Assert.assertEquals(4, tabs.size());
+        Assert.assertEquals(titles[2], ChromeTabUtils.getTitleOnUiThread(tabs.get(1)));
+        Assert.assertEquals(urls[2], ChromeTabUtils.getUrlOnUiThread(tabs.get(1)).getSpec());
+        Assert.assertEquals(titles[1], ChromeTabUtils.getTitleOnUiThread(tabs.get(2)));
+        Assert.assertEquals(urls[1], ChromeTabUtils.getUrlOnUiThread(tabs.get(2)).getSpec());
+        Assert.assertEquals(titles[0], ChromeTabUtils.getTitleOnUiThread(tabs.get(3)));
+        Assert.assertEquals(urls[0], ChromeTabUtils.getUrlOnUiThread(tabs.get(3)).getSpec());
+        final int tabIds[] =
+                new int[] {tabs.get(1).getId(), tabs.get(2).getId(), tabs.get(3).getId()};
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            Assert.assertEquals("Bar", TabGroupTitleUtils.getTabGroupTitle(tabs.get(1).getId()));
+            Assert.assertTrue(mTabGroupModelFilter.hasOtherRelatedTabs(tabs.get(1)));
+            Assert.assertTrue(mTabGroupModelFilter.hasOtherRelatedTabs(tabs.get(2)));
+            Assert.assertTrue(mTabGroupModelFilter.hasOtherRelatedTabs(tabs.get(3)));
+            Assert.assertEquals(Arrays.asList(new Tab[] {tabs.get(1), tabs.get(2), tabs.get(3)}),
+                    mTabGroupModelFilter.getRelatedTabList(tabs.get(1).getId()));
+        });
+
+        // Restart activity.
+        TestThreadUtils.runOnUiThreadBlocking(() -> { mActivity.saveState(); });
+        sActivityTestRule.recreateActivity();
+        mActivity = sActivityTestRule.getActivity();
+        mTabModelSelector = mActivity.getTabModelSelectorSupplier().get();
+        CriteriaHelper.pollUiThread(mTabModelSelector::isTabStateInitialized);
+        mTabModel = mTabModelSelector.getModel(false);
+        TabModelFilter filter =
+                mTabModelSelector.getTabModelFilterProvider().getTabModelFilter(false);
+        assert filter instanceof TabGroupModelFilter;
+        mTabGroupModelFilter = (TabGroupModelFilter) filter;
+
+        // Confirm the same tabs are present with the same group structure.
+        tabs.clear();
+        tabs.addAll(getAllTabs());
+        Assert.assertEquals(4, tabs.size());
+        Assert.assertEquals(tabIds[0], tabs.get(1).getId());
+        Assert.assertEquals(titles[2], ChromeTabUtils.getTitleOnUiThread(tabs.get(1)));
+        Assert.assertEquals(urls[2], ChromeTabUtils.getUrlOnUiThread(tabs.get(1)).getSpec());
+        Assert.assertEquals(tabIds[1], tabs.get(2).getId());
+        Assert.assertEquals(titles[1], ChromeTabUtils.getTitleOnUiThread(tabs.get(2)));
+        Assert.assertEquals(urls[1], ChromeTabUtils.getUrlOnUiThread(tabs.get(2)).getSpec());
+        Assert.assertEquals(tabIds[2], tabs.get(3).getId());
+        Assert.assertEquals(titles[0], ChromeTabUtils.getTitleOnUiThread(tabs.get(3)));
+        Assert.assertEquals(urls[0], ChromeTabUtils.getUrlOnUiThread(tabs.get(3)).getSpec());
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            Assert.assertEquals("Bar", TabGroupTitleUtils.getTabGroupTitle(tabs.get(1).getId()));
+            Assert.assertTrue(mTabGroupModelFilter.hasOtherRelatedTabs(tabs.get(1)));
+            Assert.assertTrue(mTabGroupModelFilter.hasOtherRelatedTabs(tabs.get(2)));
+            Assert.assertTrue(mTabGroupModelFilter.hasOtherRelatedTabs(tabs.get(3)));
+            Assert.assertEquals(Arrays.asList(new Tab[] {tabs.get(1), tabs.get(2), tabs.get(3)}),
+                    mTabGroupModelFilter.getRelatedTabList(tabs.get(1).getId()));
+        });
+    }
+
+    /**
      * Tests opening a specific closed {@link Tab} that was closed as part of a bulk closure.
      */
     @Test
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebappLaunchCauseMetricsTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebappLaunchCauseMetricsTest.java
index fb29f2d..e76e466 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebappLaunchCauseMetricsTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebappLaunchCauseMetricsTest.java
@@ -121,4 +121,18 @@
         ++count;
         Assert.assertEquals(count, histogramCountForValue(LaunchCause.EXTERNAL_VIEW_INTENT));
     }
+
+    @Test
+    @SmallTest
+    @UiThreadTest
+    public void testNullWebAppInfo() throws Throwable {
+        int count = histogramCountForValue(LaunchCause.OTHER);
+
+        WebappLaunchCauseMetrics metrics = new WebappLaunchCauseMetrics(mActivity, null);
+
+        metrics.onReceivedIntent();
+        metrics.recordLaunchCause();
+        ++count;
+        Assert.assertEquals(count, histogramCountForValue(LaunchCause.OTHER));
+    }
 }
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/PartialCustomTabHeightStrategyTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/PartialCustomTabHeightStrategyTest.java
index 120cfe68..96a8c28 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/PartialCustomTabHeightStrategyTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/PartialCustomTabHeightStrategyTest.java
@@ -12,6 +12,7 @@
 import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.anyObject;
 import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 import static org.robolectric.Shadows.shadowOf;
@@ -119,6 +120,7 @@
 
     private List<WindowManager.LayoutParams> mAttributeResults;
     private DisplayMetrics mRealMetrics;
+    private Point mDisplaySize;
     private ObservableSupplierImpl<FrameLayout> mParentViewSupplier =
             new ObservableSupplierImpl<>();
     private Callback<Integer> mBottomInsetCallback = inset -> {};
@@ -175,10 +177,14 @@
         })
                 .when(mDisplay)
                 .getRealMetrics(any(DisplayMetrics.class));
+
+        mDisplaySize = new Point();
+        mDisplaySize.x = DEVICE_WIDTH;
+        mDisplaySize.y = DEVICE_HEIGHT - NAVBAR_HEIGHT;
         doAnswer(invocation -> {
             Point point = invocation.getArgument(0);
-            point.x = DEVICE_WIDTH;
-            point.y = DEVICE_HEIGHT - NAVBAR_HEIGHT;
+            point.x = mDisplaySize.x;
+            point.y = mDisplaySize.y;
             return null;
         })
                 .when(mDisplay)
@@ -226,6 +232,8 @@
         mConfiguration.orientation = Configuration.ORIENTATION_LANDSCAPE;
         mRealMetrics.widthPixels = DEVICE_HEIGHT;
         mRealMetrics.heightPixels = DEVICE_WIDTH;
+        mDisplaySize.x = DEVICE_HEIGHT - NAVBAR_HEIGHT;
+        mDisplaySize.y = DEVICE_WIDTH;
         new PartialCustomTabHeightStrategy(mActivity, mParentViewSupplier, 800,
                 mMultiWindowModeStateDispatcher, null, null, mOnResizedCallback,
                 mActivityLifecycleDispatcher);
@@ -319,6 +327,7 @@
         PartialCustomTabHeightStrategy strategy = new PartialCustomTabHeightStrategy(mActivity,
                 mParentViewSupplier, 800, mMultiWindowModeStateDispatcher, null, null,
                 mOnResizedCallback, mActivityLifecycleDispatcher);
+        strategy.setMockViewForTesting(mNavbar, mSpinnerView, mSpinner, mToolbarView);
 
         // Pass null because we have a mock Activity and we don't depend on the GestureDetector
         // inside as we test MotionEvents directly.
@@ -335,6 +344,25 @@
     }
 
     @Test
+    public void rotateToLandescapeHideCustomNavbar() {
+        PartialCustomTabHeightStrategy strategy = new PartialCustomTabHeightStrategy(mActivity,
+                mParentViewSupplier, 800, mMultiWindowModeStateDispatcher, null, null,
+                mOnResizedCallback, mActivityLifecycleDispatcher);
+        strategy.setMockViewForTesting(mNavbar, mSpinnerView, mSpinner, mToolbarView);
+
+        mConfiguration.orientation = Configuration.ORIENTATION_LANDSCAPE;
+        mRealMetrics.widthPixels = DEVICE_HEIGHT;
+        mRealMetrics.heightPixels = DEVICE_WIDTH;
+        mDisplaySize.x = DEVICE_HEIGHT - NAVBAR_HEIGHT;
+        mDisplaySize.y = DEVICE_WIDTH;
+
+        strategy.onConfigurationChanged(mConfiguration);
+
+        assertEquals(0, strategy.getNavbarHeightForTesting());
+        verify(mNavbar, times(1)).setVisibility(View.GONE);
+    }
+
+    @Test
     public void enterMultiwindowModeUnresizable() {
         PartialCustomTabHeightStrategy strategy = new PartialCustomTabHeightStrategy(mActivity,
                 mParentViewSupplier, 800, mMultiWindowModeStateDispatcher, null, null,
diff --git a/chrome/android/proguard/trichrome.flags b/chrome/android/proguard/trichrome.flags
deleted file mode 100644
index 68fb39a..0000000
--- a/chrome/android/proguard/trichrome.flags
+++ /dev/null
@@ -1,8 +0,0 @@
-# Copyright 2018 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.
-
-# TODO(agrieve): Once this -keep is removed, add a @CheckDiscard to LibraryLoaderConfig.java.
-# Currently the Trichrome library just contains NativeLibraries, which we keep.
-# https://crbug.com/901465
--keep class org.chromium.build.NativeLibraries { *; }
diff --git a/chrome/android/trichrome.gni b/chrome/android/trichrome.gni
index 01b7f0b..1e2eec7 100644
--- a/chrome/android/trichrome.gni
+++ b/chrome/android/trichrome.gni
@@ -114,7 +114,7 @@
       product_version_resources_dep =
           "//chrome/android:product_version_resources"
     } else {
-      generate_buildconfig_java = false
+      omit_dex = true
     }
 
     # TODO(torne): using icon_resources just to get a temporary icon
@@ -194,37 +194,6 @@
         }
       }
     }
-
-    # http://crbug.com/1042107.
-    if (is_component_build) {
-      if (android_64bit_target_cpu && invoker.is_64_bit_browser) {
-        main_component_library = "libmonochrome_64.cr.so"
-      } else {
-        main_component_library = "libmonochrome.cr.so"
-      }
-    }
-
-    if (!is_java_debug) {
-      proguard_enabled = true
-      proguard_configs = [
-        "//base/android/proguard/chromium_apk.flags",
-        "//base/android/proguard/chromium_code.flags",
-        "//chrome/android/proguard/trichrome.flags",
-      ]
-      if (trichrome_synchronized_proguard) {
-        proguard_configs += [
-          "//chrome/android/proguard/static_library_dex_reference_workarounds.flags",
-          "//base/android/proguard/enable_obfuscation.flags",
-        ]
-      } else {
-        # Disabling all obfuscation for the Trichrome library as a temporary
-        # workaround for crbug.com/1012842. There were naming conflicts between
-        # Library and Chrome, since each Proguard run doesn't know about the
-        # other, and thus handed out the first names (a, b, c) to both.
-        proguard_enable_obfuscation = false
-      }
-    }
-    deps += [ "//chrome/android:trichrome_dummy_resources" ]
   }
 }
 
diff --git a/chrome/android/trichrome/res_dummy/values/strings.xml b/chrome/android/trichrome/res_dummy/values/strings.xml
deleted file mode 100644
index a0d71c24..0000000
--- a/chrome/android/trichrome/res_dummy/values/strings.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright 2019 The Chromium Authors. All rights reserved.
-     Use of this source code is governed by a BSD-style license that can be
-     found in the LICENSE file. -->
-
-<!-- DO NOT ADD MORE RESOURCES HERE -->
-<resources>
-    <string name="dummy"></string>
-</resources>
\ No newline at end of file
diff --git a/chrome/android/trichrome/static_library_shared_java_code.md b/chrome/android/trichrome/static_library_shared_java_code.md
deleted file mode 100644
index a288902..0000000
--- a/chrome/android/trichrome/static_library_shared_java_code.md
+++ /dev/null
@@ -1,100 +0,0 @@
-# Static Library Java code
-
-[TOC]
-
-## Overview
-
-This document describes how static library targets can be used to share common
-Java code between multiple APKs. More detail can be found at
-[go/proguarding-trichrome](goto.google.com/proguarding-trichrome).
-
-## TrichromeLibrary
-
-Currently (Jan 2020) trichrome library is the only target to make use of static
-shared library APKs and is used to share common code used by both Chrome and
-Webview.
-
-## Status
-
-Java code sharing is mostly implemented at this point but there is one remaining
-blocker related to how
-[native method resolution works in Webview](crbug.com/1025009).
-
-## How it works
-
-### Build variables
-
-For `android_apk_or_module` base templates:
-
-`static_library_provider`: Specifies that this target depends on a static shared
-library APK. When synchronized proguard is turned on, the
-`static_library_provider` becomes the target that provides the final dex file.
-
-`static_library_dependent_targets`: If set, generates final dex files for
-itself and for all targets in the `static_library_dependent_targets` list.
-
-`static_library_synchronized_proguard`: Turns on synchronized proguard for
-targets that also set `static_library_provider`.
-
-### .build_config
-
-`write_build_config.py` is responsible for figuring out where code and related
-artifacts for the `static_library_provider` and
-`static_library_dependent_targets` belongs. The main difference from regular
-`.build_configs` is the mapping recording which input jars belong to each final
-dex file. Ex:
-
-```
-"deps_info": {
-  ...
-  "static_library_dependent_classpath_configs": {
-      "gen/android_webview/trichrome_webview_apk.build_config.json": [
-        "obj/android_webview/trichrome_webview_apk/trichrome_webview_apk.jar",
-        ...
-      ],
-      "gen/chrome/android/trichrome_chrome_bundle.build_config.json": [
-        "lib.java/chrome/android/app_hooks_java.jar",
-        ...
-      "gen/chrome/android/trichrome_library_apk.build_config.json": [
-        "lib.java/base/base_java.jar",
-        ...
-      ]
-      ...
-  }
-}
-```
-
-### Synchronized ProGuard
-
-TrichromeChromeBundle (base module) and TrichromeWebview do not have a final
-`dex` or `proguard` step. Instead the library APK creates a "fat" dex from the
-`.build_config.json:deps_info:java_runtime_classpath`.
-
-Then, the mapping of `.build_config.json` -> owned input jars stored in the
-`.build_config.json` is used by `dexsplitter` to generate final .dex files for
-TrichromeLibrary, TrichromeChrome, and TrichromeWebview.
-
-### Resources
-
-For Java code to be shared between Chrome and Webview in [Trichrome][trichrome],
-we ensure that Chrome and Webview use the same resource IDs. This requires a
-few adjustments to how resources are created.
-
-1. Webview's resources are compiled first without any changes.
-2. Chrome's resources are compiled second, but use the same resource IDs as
-   Webview when possible.
-3. When synchronized proguarding is turned on, the `R.java` files generated in
-   the previous step are discarded. The shared static library APK target
-   (trichrome library) takes the output `R.txt` files from the previous steps
-   and includes those resources in its own `R.java` generation.
-
-[trichrome]: /chrome/android/trichrome/static_library_shared_java_code.md
-
-### Usage
-
-* Building trichrome_chrome_bundle or trichrome_webview_apk (and various arch
-  variants) will ensure the correct library target is also built.
-* Using the generated wrapper script from the main APK is sufficient (no need
-  to explicitly install the library).
-  * `bin/trichrome_chrome_bundle run` will ensure TrichromeChromeBundle and
-    TrichromeLibrary are installed before launching Chrome.
diff --git a/chrome/app/chrome.cml b/chrome/app/chrome.cml
index 7d2b289..dcf1ced8 100644
--- a/chrome/app/chrome.cml
+++ b/chrome/app/chrome.cml
@@ -55,6 +55,7 @@
                 "fuchsia.device.NameProvider",
                 "fuchsia.element.GraphicalPresenter",
                 "fuchsia.fonts.Provider",
+                "fuchsia.hwinfo.Product",
                 "fuchsia.input.virtualkeyboard.ControllerCreator",
                 "fuchsia.intl.PropertyProvider",
                 "fuchsia.media.Audio",
diff --git a/chrome/app/chrome_v1.cmx b/chrome/app/chrome_v1.cmx
index a45fa78..cd376be 100644
--- a/chrome/app/chrome_v1.cmx
+++ b/chrome/app/chrome_v1.cmx
@@ -18,6 +18,7 @@
       "fuchsia.device.NameProvider",
       "fuchsia.element.GraphicalPresenter",
       "fuchsia.fonts.Provider",
+      "fuchsia.hwinfo.Product",
       "fuchsia.input.virtualkeyboard.ControllerCreator",
       "fuchsia.intl.PropertyProvider",
       "fuchsia.logger.LogSink",
diff --git a/chrome/app/chromeos_strings.grdp b/chrome/app/chromeos_strings.grdp
index a85481e..1bebb23d1 100644
--- a/chrome/app/chromeos_strings.grdp
+++ b/chrome/app/chromeos_strings.grdp
@@ -5071,7 +5071,7 @@
 
   <!-- Strings for EcheApp -->
   <message name="IDS_ECHE_APP_SCREEN_LOCK_SETTINGS_BUTTON" desc="Label for the button in the screen lock notification of EcheApp to open the settings app.">
-    Open Settings
+    Settings
   </message>
   <message name="IDS_ECHE_APP_SCREEN_LOCK_LEARN_MORE" desc="Learn more link text in screen lock notification of EcheApp.">
     Learn more
@@ -5083,10 +5083,10 @@
     Learn more
   </message>
   <message name="IDS_ECHE_APP_SCREEN_LOCK_NOTIFICATION_TITLE" desc="Title for notification shown when screen lock is not enabled while using app stream of EcheApp.">
-    Can't open app
+    Can't open <ph name="APP_NAME">$1<ex>the app</ex></ph>
   </message>
   <message name="IDS_ECHE_APP_SCREEN_LOCK_NOTIFICATION_MESSAGE" desc="Message for the notification shown when screen lock is not enabled while using app stream of EcheApp.">
-    To open <ph name="APP_NAME">$1<ex>the app</ex></ph>, enable "Show lock screen when waking from sleep"
+    Turn on "Show lock screen when waking from sleep" and try again
   </message>
   <message name="IDS_ECHE_APP_NOTIFICATION_OPEN_AGAIN_BUTTON" desc="Label for the inactivity notification button of EcheApp to open open.">
     Open again
diff --git a/chrome/app/chromeos_strings_grdp/IDS_ECHE_APP_SCREEN_LOCK_NOTIFICATION_MESSAGE.png.sha1 b/chrome/app/chromeos_strings_grdp/IDS_ECHE_APP_SCREEN_LOCK_NOTIFICATION_MESSAGE.png.sha1
index 33e553e..9f91339a 100644
--- a/chrome/app/chromeos_strings_grdp/IDS_ECHE_APP_SCREEN_LOCK_NOTIFICATION_MESSAGE.png.sha1
+++ b/chrome/app/chromeos_strings_grdp/IDS_ECHE_APP_SCREEN_LOCK_NOTIFICATION_MESSAGE.png.sha1
@@ -1 +1 @@
-680ae77ac0feb1ff37f4aa80877bea62a049a96b
\ No newline at end of file
+0b22106158ee9d776742fee4c189b3f24fa4a292
\ No newline at end of file
diff --git a/chrome/app/chromeos_strings_grdp/IDS_ECHE_APP_SCREEN_LOCK_NOTIFICATION_TITLE.png.sha1 b/chrome/app/chromeos_strings_grdp/IDS_ECHE_APP_SCREEN_LOCK_NOTIFICATION_TITLE.png.sha1
index 33e553e..9f91339a 100644
--- a/chrome/app/chromeos_strings_grdp/IDS_ECHE_APP_SCREEN_LOCK_NOTIFICATION_TITLE.png.sha1
+++ b/chrome/app/chromeos_strings_grdp/IDS_ECHE_APP_SCREEN_LOCK_NOTIFICATION_TITLE.png.sha1
@@ -1 +1 @@
-680ae77ac0feb1ff37f4aa80877bea62a049a96b
\ No newline at end of file
+0b22106158ee9d776742fee4c189b3f24fa4a292
\ No newline at end of file
diff --git a/chrome/app/chromeos_strings_grdp/IDS_ECHE_APP_SCREEN_LOCK_SETTINGS_BUTTON.png.sha1 b/chrome/app/chromeos_strings_grdp/IDS_ECHE_APP_SCREEN_LOCK_SETTINGS_BUTTON.png.sha1
index f7fc0077..9f91339a 100644
--- a/chrome/app/chromeos_strings_grdp/IDS_ECHE_APP_SCREEN_LOCK_SETTINGS_BUTTON.png.sha1
+++ b/chrome/app/chromeos_strings_grdp/IDS_ECHE_APP_SCREEN_LOCK_SETTINGS_BUTTON.png.sha1
@@ -1 +1 @@
-bf284f3dc923008482ba763eb9d056f1ab69f8e9
\ No newline at end of file
+0b22106158ee9d776742fee4c189b3f24fa4a292
\ No newline at end of file
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index d4e71c2..4152141a7b 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -207,6 +207,7 @@
 #endif
 
 #if BUILDFLAG(IS_CHROMEOS)
+#include "base/process/process.h"
 #include "chrome/browser/apps/intent_helper/intent_picker_features.h"
 #include "chromeos/constants/chromeos_features.h"
 #endif
@@ -3513,6 +3514,10 @@
     {"adaptive-charging", flag_descriptions::kAdaptiveChargingName,
      flag_descriptions::kAdaptiveChargingDescription, kOsCrOS,
      FEATURE_VALUE_TYPE(ash::features::kAdaptiveCharging)},
+    {"adaptive-charging-for-testing",
+     flag_descriptions::kAdaptiveChargingForTestingName,
+     flag_descriptions::kAdaptiveChargingForTestingDescription, kOsCrOS,
+     FEATURE_VALUE_TYPE(ash::features::kAdaptiveChargingForTesting)},
     {"allow-poly-device-pairing",
      flag_descriptions::kAllowPolyDevicePairingName,
      flag_descriptions::kAllowPolyDevicePairingDescription, kOsCrOS,
@@ -3590,10 +3595,6 @@
      kOsCrOS,
      FEATURE_VALUE_TYPE(
          chromeos::features::kDisableIdleSocketsCloseOnMemoryPressure)},
-    {"disable-office-editing-component-app",
-     flag_descriptions::kDisableOfficeEditingComponentAppName,
-     flag_descriptions::kDisableOfficeEditingComponentAppDescription, kOsCrOS,
-     FEATURE_VALUE_TYPE(chromeos::features::kDisableOfficeEditingComponentApp)},
     {"oobe-hid-detection-revamp",
      flag_descriptions::kOobeHidDetectionRevampName,
      flag_descriptions::kOobeHidDetectionRevampDescription, kOsCrOS,
@@ -3778,10 +3779,18 @@
      flag_descriptions::kDeprecateLowUsageCodecsName,
      flag_descriptions::kDeprecateLowUsageCodecsDescription,
      kOsCrOS | kOsLacros, FEATURE_VALUE_TYPE(media::kDeprecateLowUsageCodecs)},
+    {"disable-office-editing-component-app",
+     flag_descriptions::kDisableOfficeEditingComponentAppName,
+     flag_descriptions::kDisableOfficeEditingComponentAppDescription,
+     kOsCrOS | kOsLacros,
+     FEATURE_VALUE_TYPE(chromeos::features::kDisableOfficeEditingComponentApp)},
     {"enable-tts-lacros-support",
      flag_descriptions::kEnableTtsLacrosSupportName,
      flag_descriptions::kEnableTtsLacrosSupportDescription, kOsCrOS | kOsLacros,
      FEATURE_VALUE_TYPE(chromeos::kLacrosTtsSupport)},
+    {"one-group-per-renderer", flag_descriptions::kOneGroupPerRendererName,
+     flag_descriptions::kOneGroupPerRendererDescription, kOsCrOS | kOsLacros,
+     FEATURE_VALUE_TYPE(base::kOneGroupPerRenderer)},
 #endif  // BUILDFLAG(IS_CHROMEOS)
 
 #if BUILDFLAG(IS_LINUX)
@@ -4868,11 +4877,6 @@
     {"arc-native-bridge-toggle", flag_descriptions::kArcNativeBridgeToggleName,
      flag_descriptions::kArcNativeBridgeToggleDescription, kOsCrOS,
      FEATURE_VALUE_TYPE(arc::kNativeBridgeToggleFeature)},
-    {"arc-native-bridge-64bit-support-experiment",
-     flag_descriptions::kArcNativeBridge64BitSupportExperimentName,
-     flag_descriptions::kArcNativeBridge64BitSupportExperimentDescription,
-     kOsCrOS,
-     FEATURE_VALUE_TYPE(arc::kNativeBridge64BitSupportExperimentFeature)},
     {"arc-right-click-long-press",
      flag_descriptions::kArcRightClickLongPressName,
      flag_descriptions::kArcRightClickLongPressDescription, kOsCrOS,
diff --git a/chrome/browser/android/browserservices/intents/java/src/org/chromium/chrome/browser/browserservices/intents/BrowserServicesIntentDataProvider.java b/chrome/browser/android/browserservices/intents/java/src/org/chromium/chrome/browser/browserservices/intents/BrowserServicesIntentDataProvider.java
index 2fdcc6b..9e4f0b7c 100644
--- a/chrome/browser/android/browserservices/intents/java/src/org/chromium/chrome/browser/browserservices/intents/BrowserServicesIntentDataProvider.java
+++ b/chrome/browser/android/browserservices/intents/java/src/org/chromium/chrome/browser/browserservices/intents/BrowserServicesIntentDataProvider.java
@@ -460,6 +460,13 @@
     }
 
     /**
+     * @return Whether the intent is for partial-height custom tabs.
+     */
+    public boolean isPartialHeightCustomTab() {
+        return false;
+    }
+
+    /**
      * @return The value in pixels  of the initial height of the Activity. It will return 0 if there
      *         is no value set.
      */
diff --git a/chrome/browser/android/usb/web_usb_chooser_android.cc b/chrome/browser/android/usb/web_usb_chooser_android.cc
index d8889670..21a5ccb 100644
--- a/chrome/browser/android/usb/web_usb_chooser_android.cc
+++ b/chrome/browser/android/usb/web_usb_chooser_android.cc
@@ -10,16 +10,15 @@
 #include "chrome/browser/ui/android/device_dialog/usb_chooser_dialog_android.h"
 #include "chrome/browser/usb/usb_chooser_controller.h"
 
-WebUsbChooserAndroid::WebUsbChooserAndroid(
-    content::RenderFrameHost* render_frame_host)
-    : WebUsbChooser(render_frame_host) {}
+WebUsbChooserAndroid::WebUsbChooserAndroid() = default;
 
-WebUsbChooserAndroid::~WebUsbChooserAndroid() {}
+WebUsbChooserAndroid::~WebUsbChooserAndroid() = default;
 
 void WebUsbChooserAndroid::ShowChooser(
+    content::RenderFrameHost* render_frame_host,
     std::unique_ptr<UsbChooserController> controller) {
   dialog_ = UsbChooserDialogAndroid::Create(
-      render_frame_host(), std::move(controller),
+      render_frame_host, std::move(controller),
       base::BindOnce(&WebUsbChooserAndroid::OnDialogClosed,
                      base::Unretained(this)));
 }
@@ -27,7 +26,3 @@
 void WebUsbChooserAndroid::OnDialogClosed() {
   dialog_.reset();
 }
-
-base::WeakPtr<WebUsbChooser> WebUsbChooserAndroid::GetWeakPtr() {
-  return weak_factory_.GetWeakPtr();
-}
diff --git a/chrome/browser/android/usb/web_usb_chooser_android.h b/chrome/browser/android/usb/web_usb_chooser_android.h
index 55bd972..0e5b993 100644
--- a/chrome/browser/android/usb/web_usb_chooser_android.h
+++ b/chrome/browser/android/usb/web_usb_chooser_android.h
@@ -6,7 +6,6 @@
 #define CHROME_BROWSER_ANDROID_USB_WEB_USB_CHOOSER_ANDROID_H_
 
 #include <memory>
-#include <vector>
 
 #include "chrome/browser/usb/web_usb_chooser.h"
 
@@ -18,7 +17,7 @@
 // to access a certain device.
 class WebUsbChooserAndroid : public WebUsbChooser {
  public:
-  explicit WebUsbChooserAndroid(content::RenderFrameHost* render_frame_host);
+  WebUsbChooserAndroid();
 
   WebUsbChooserAndroid(const WebUsbChooserAndroid&) = delete;
   WebUsbChooserAndroid& operator=(const WebUsbChooserAndroid&) = delete;
@@ -26,16 +25,13 @@
   ~WebUsbChooserAndroid() override;
 
   // WebUsbChooser implementation
-  void ShowChooser(std::unique_ptr<UsbChooserController> controller) override;
-  base::WeakPtr<WebUsbChooser> GetWeakPtr() override;
+  void ShowChooser(content::RenderFrameHost* render_frame_host,
+                   std::unique_ptr<UsbChooserController> controller) override;
 
  private:
   void OnDialogClosed();
 
-  // Only a single dialog can be shown at a time.
   std::unique_ptr<UsbChooserDialogAndroid> dialog_;
-
-  base::WeakPtrFactory<WebUsbChooserAndroid> weak_factory_{this};
 };
 
 #endif  // CHROME_BROWSER_ANDROID_USB_WEB_USB_CHOOSER_ANDROID_H_
diff --git a/chrome/browser/ash/BUILD.gn b/chrome/browser/ash/BUILD.gn
index fc7b422..a98a0f5 100644
--- a/chrome/browser/ash/BUILD.gn
+++ b/chrome/browser/ash/BUILD.gn
@@ -348,8 +348,6 @@
     "arc/input_overlay/ui/action_edit_menu.h",
     "arc/input_overlay/ui/action_label.cc",
     "arc/input_overlay/ui/action_label.h",
-    "arc/input_overlay/ui/action_tag.cc",
-    "arc/input_overlay/ui/action_tag.h",
     "arc/input_overlay/ui/action_view.cc",
     "arc/input_overlay/ui/action_view.h",
     "arc/input_overlay/ui/edit_mode_exit_view.cc",
diff --git a/chrome/browser/ash/accessibility/dictation_browsertest.cc b/chrome/browser/ash/accessibility/dictation_browsertest.cc
index fd6635c..1662fb5 100644
--- a/chrome/browser/ash/accessibility/dictation_browsertest.cc
+++ b/chrome/browser/ash/accessibility/dictation_browsertest.cc
@@ -44,6 +44,7 @@
 #include "content/public/test/browser_test.h"
 #include "content/public/test/browser_test_utils.h"
 #include "content/public/test/fake_speech_recognition_manager.h"
+#include "extensions/browser/browsertest_util.h"
 #include "extensions/browser/extension_host_test_helper.h"
 #include "media/mojo/mojom/speech_recognition_service.mojom.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
@@ -318,7 +319,13 @@
   }
 
   // Routers to SpeechRecognitionTestHelper methods.
-  void WaitForRecognitionStarted() { test_helper_.WaitForRecognitionStarted(); }
+  void WaitForRecognitionStarted() {
+    test_helper_.WaitForRecognitionStarted();
+    // Dictation intializes FocusHandler when speech recognition starts.
+    // Several tests require FocusHandler logic, so wait for it to initialize
+    // before proceeding.
+    WaitForFocusHandler();
+  }
 
   void WaitForRecognitionStopped() { test_helper_.WaitForRecognitionStopped(); }
 
@@ -390,6 +397,29 @@
         .Wait();
   }
 
+  void WaitForFocusHandler() {
+    std::string error_message = "Still waiting for FocusHandler";
+    std::string script = R"(
+      if (accessibilityCommon.dictation_.focusHandler_.isReadyForTesting()) {
+        window.domAutomationController.send("ready");
+      } else {
+        window.domAutomationController.send("not ready");
+      }
+    )";
+    SuccessWaiter(
+        base::BindLambdaForTesting([&]() {
+          std::string result =
+              extensions::browsertest_util::ExecuteScriptInBackgroundPage(
+                  /*context=*/browser()->profile(),
+                  /*extension_id=*/
+                  extension_misc::kAccessibilityCommonExtensionId,
+                  /*script=*/script);
+          return result == "ready";
+        }),
+        error_message)
+        .Wait();
+  }
+
   void ToggleDictationWithKeystroke() {
     ASSERT_NO_FATAL_FAILURE(ASSERT_TRUE(ui_test_utils::SendKeyPressToWindowSync(
         nullptr, ui::KeyboardCode::VKEY_D, false, false, false, true)));
diff --git a/chrome/browser/ash/accessibility/speech_monitor.cc b/chrome/browser/ash/accessibility/speech_monitor.cc
index afb0130..8f679fd9 100644
--- a/chrome/browser/ash/accessibility/speech_monitor.cc
+++ b/chrome/browser/ash/accessibility/speech_monitor.cc
@@ -103,6 +103,8 @@
 void SpeechMonitor::FinalizeVoiceOrdering(
     std::vector<content::VoiceData>& voices) {}
 
+void SpeechMonitor::RefreshVoices() {}
+
 double SpeechMonitor::CalculateUtteranceDelayMS() {
   std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now();
   std::chrono::duration<double> time_span =
diff --git a/chrome/browser/ash/accessibility/speech_monitor.h b/chrome/browser/ash/accessibility/speech_monitor.h
index d3155a4..c7f916c 100644
--- a/chrome/browser/ash/accessibility/speech_monitor.h
+++ b/chrome/browser/ash/accessibility/speech_monitor.h
@@ -102,6 +102,7 @@
   void SetError(const std::string& error) override;
   void Shutdown() override;
   void FinalizeVoiceOrdering(std::vector<content::VoiceData>& voices) override;
+  void RefreshVoices() override;
 
   void MaybeContinueReplay();
   void MaybePrintExpectations();
diff --git a/chrome/browser/ash/arc/input_overlay/actions/action.cc b/chrome/browser/ash/arc/input_overlay/actions/action.cc
index 609b16b..4d5c609 100644
--- a/chrome/browser/ash/arc/input_overlay/actions/action.cc
+++ b/chrome/browser/ash/arc/input_overlay/actions/action.cc
@@ -214,8 +214,7 @@
   return (input_element.input_sources() & InputSource::IS_MOUSE) != 0;
 }
 
-void Action::PrepareToBind(std::unique_ptr<InputElement> input_element,
-                           DisplayMode mode) {
+void Action::PrepareToBind(std::unique_ptr<InputElement> input_element) {
   if (pending_binding_)
     pending_binding_.reset();
   pending_binding_ = std::move(input_element);
@@ -224,7 +223,6 @@
   if (!action_view_)
     return;
   action_view_->SetViewContent(BindingOption::kPending, bounds);
-  action_view_->SetDisplayMode(mode);
 }
 
 void Action::BindPending() {
@@ -248,12 +246,17 @@
 
 void Action::RestoreToDefault(const gfx::RectF& content_bounds) {
   DCHECK(action_view_);
-  if (!action_view_ || GetCurrentDisplayedBinding() == *original_binding_)
+  if (!action_view_)
     return;
-  pending_binding_.reset();
-  pending_binding_ = std::make_unique<InputElement>(*original_binding_);
-  action_view_->SetViewContent(BindingOption::kPending, content_bounds);
-  action_view_->SetDisplayMode(DisplayMode::kEdited);
+
+  if (GetCurrentDisplayedBinding() != *original_binding_) {
+    pending_binding_.reset();
+    pending_binding_ = std::make_unique<InputElement>(*original_binding_);
+    action_view_->SetViewContent(BindingOption::kPending, content_bounds);
+  }
+  // Set to |DisplayMode::kRestore| to clear the focus even the current binding
+  // is same as original binding.
+  action_view_->SetDisplayMode(DisplayMode::kRestore);
 }
 
 const InputElement& Action::GetCurrentDisplayedBinding() {
@@ -365,7 +368,12 @@
     return;
   auto bounds = CalculateWindowContentBounds(target_window_);
   action_view_->SetViewContent(BindingOption::kPending, bounds);
-  action_view_->SetDisplayMode(DisplayMode::kEditedUnbound);
+  const int label_index = action_view_->unbind_label_index();
+  action_view_->SetDisplayMode(DisplayMode::kEditedUnbound,
+                               (label_index == kDefaultLabelIndex
+                                    ? nullptr
+                                    : action_view_->labels()[label_index]));
+  action_view_->set_unbind_label_index(kDefaultLabelIndex);
 }
 
 std::unique_ptr<ActionProto> Action::ConvertToProtoIfCustomized() {
diff --git a/chrome/browser/ash/arc/input_overlay/actions/action.h b/chrome/browser/ash/arc/input_overlay/actions/action.h
index 2811face..efa6ed1e 100644
--- a/chrome/browser/ash/arc/input_overlay/actions/action.h
+++ b/chrome/browser/ash/arc/input_overlay/actions/action.h
@@ -88,8 +88,7 @@
 
   // This is called for editing the actions before change is saved. Or for
   // loading the customized data to override the default input mapping.
-  void PrepareToBind(std::unique_ptr<InputElement> input_element,
-                     DisplayMode mode = DisplayMode::kEdit);
+  void PrepareToBind(std::unique_ptr<InputElement> input_element);
   // Save |pending_binding_| as |current_binding_|.
   void BindPending();
   // Cancel |pending_binding_|.
diff --git a/chrome/browser/ash/arc/input_overlay/actions/action_move.cc b/chrome/browser/ash/arc/input_overlay/actions/action_move.cc
index b5486ba5..b0238e9 100644
--- a/chrome/browser/ash/arc/input_overlay/actions/action_move.cc
+++ b/chrome/browser/ash/arc/input_overlay/actions/action_move.cc
@@ -7,7 +7,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/ash/arc/input_overlay/actions/action.h"
 #include "chrome/browser/ash/arc/input_overlay/touch_id_manager.h"
-#include "chrome/browser/ash/arc/input_overlay/ui/action_tag.h"
+#include "chrome/browser/ash/arc/input_overlay/ui/action_label.h"
 #include "ui/events/keycodes/dom/dom_code.h"
 #include "ui/events/keycodes/dom/keycode_converter.h"
 #include "ui/gfx/geometry/point.h"
@@ -31,7 +31,7 @@
                                                             {1, 0}};
 // UI specs.
 // Offset by label center.
-constexpr int kTagOffset = 49;
+constexpr int kLabelOffset = 49;
 
 std::unique_ptr<Position> ParseApplyAreaPosition(const base::Value& value,
                                                  base::StringPiece key) {
@@ -66,22 +66,33 @@
   // TODO(cuicuiruan): rewrite for post MVP once design is ready.
   void SetViewContent(BindingOption binding_option,
                       const gfx::RectF& content_bounds) override {
-    auto tag = ActionTag::CreateTextActionTag(kMouseCursorLock);
-    auto tag_size = tag->GetPreferredSize();
-    tag->SetSize(tag_size);
-    SetSize(tag_size);
-    center_.set_x(tag_size.width() / 2);
-    center_.set_y(tag_size.height() / 2);
-    tags_.emplace_back(AddChildView(std::move(tag)));
+    auto label = ActionLabel::CreateTextActionLabel(kMouseCursorLock);
+    labels_.emplace_back(AddChildView(std::move(label)));
   }
 
   // TODO(cuicuiruan): rewrite for post MVP once design is ready.
-  void OnKeyBindingChange(ActionTag* action_tag, ui::DomCode code) override {
+  void OnKeyBindingChange(ActionLabel* action_label,
+                          ui::DomCode code) override {
     NOTIMPLEMENTED();
   }
   void OnBindingToKeyboard() override { NOTIMPLEMENTED(); }
   void OnBindingToMouse(std::string mouse_action) override { NOTIMPLEMENTED(); }
   void OnMenuEntryPressed() override { NOTIMPLEMENTED(); }
+
+  void ChildPreferredSizeChanged(View* child) override {
+    if (static_cast<ActionLabel*>(child) != labels_[0])
+      return;
+
+    auto label_size = labels_[0]->CalculatePreferredSize();
+    labels_[0]->SetSize(label_size);
+    labels_[0]->SetPosition(gfx::Point());
+    center_.set_x(label_size.width() / 2);
+    center_.set_y(label_size.height() / 2);
+    SetSize(label_size);
+    auto center_pos = action_->GetUICenterPosition(
+        CalculateWindowContentBounds(action_->target_window()));
+    SetPositionFromCenterPosition(center_pos);
+  }
 };
 
 class ActionMove::ActionMoveKeyView : public ActionView {
@@ -103,8 +114,7 @@
         std::max(kActionMoveMinRadius, action_->GetUIRadius(content_bounds));
     auto* action_move = static_cast<ActionMove*>(action_);
     action_move->set_move_distance(radius / 2);
-    SetSize(gfx::Size(radius * 2, radius * 2));
-    if (!circle_) {
+    if (show_circle() && !circle_) {
       auto circle = std::make_unique<ActionCircle>(radius);
       circle_ = AddChildView(std::move(circle));
     }
@@ -130,51 +140,45 @@
       return;
 
     auto keys = binding->keys();
-    if (tags_.empty()) {
+    if (labels_.empty()) {
       for (int i = 0; i < keys.size(); i++) {
-        auto tag = ActionTag::CreateTextActionTag(GetDisplayText(keys[i]));
-        auto tag_size = tag->GetPreferredSize();
-        tag->SetSize(tag_size);
-        int x = kDirection[i][0];
-        int y = kDirection[i][1];
-        auto pos = gfx::Point(
-            radius + x * (radius - kTagOffset) - tag_size.width() / 2,
-            radius + y * (radius - kTagOffset) - tag_size.height() / 2);
-        tag->SetPosition(pos);
-        tags_.emplace_back(AddChildView(std::move(tag)));
+        auto label =
+            ActionLabel::CreateTextActionLabel(GetDisplayText(keys[i]));
+        labels_.emplace_back(AddChildView(std::move(label)));
       }
     } else {
-      DCHECK(tags_.size() == keys.size());
+      DCHECK(labels_.size() == keys.size());
       for (int i = 0; i < keys.size(); i++)
-        tags_[i]->SetTextActionTag(std::move(GetDisplayText(keys[i])));
+        labels_[i]->SetTextActionLabel(std::move(GetDisplayText(keys[i])));
     }
   }
 
-  void OnKeyBindingChange(ActionTag* action_tag, ui::DomCode code) override {
-    DCHECK(tags_.size() == kActionMoveKeysSize);
-    if (tags_.size() != kActionMoveKeysSize)
+  void OnKeyBindingChange(ActionLabel* action_label,
+                          ui::DomCode code) override {
+    DCHECK(labels_.size() == kActionMoveKeysSize);
+    if (labels_.size() != kActionMoveKeysSize)
       return;
-    auto it = std::find(tags_.begin(), tags_.end(), action_tag);
-    DCHECK(it != tags_.end());
-    if (it == tags_.end())
+    auto it = std::find(labels_.begin(), labels_.end(), action_label);
+    DCHECK(it != labels_.end());
+    if (it == labels_.end())
       return;
 
-    if (ShouldShowErrorMsg(code))
+    if (ShouldShowErrorMsg(code, action_label))
       return;
 
     auto& binding = action_->GetCurrentDisplayedBinding();
     DCHECK(binding.keys().size() == kActionMoveKeysSize);
-    const int index = it - tags_.begin();
+    const int index = it - labels_.begin();
     std::vector<ui::DomCode> new_keys = binding.keys();
     new_keys[index] = code;
     auto input_element = InputElement::CreateActionMoveKeyElement(new_keys);
-    display_overlay_controller_->OnBindingChange(action_,
-                                                 std::move(input_element));
+    ChangeBinding(action_, action_label, std::move(input_element));
   }
 
   // TODO(cuicuiruan): Remove this for post MVP for editing |ActionMove|.
-  void SetDisplayMode(const DisplayMode mode) override {
-    ActionView::SetDisplayMode(mode);
+  void SetDisplayMode(const DisplayMode mode,
+                      ActionLabel* editing_label = nullptr) override {
+    ActionView::SetDisplayMode(mode, editing_label);
     if (menu_entry_)
       menu_entry_->SetVisible(false);
   }
@@ -183,6 +187,53 @@
   void OnBindingToKeyboard() override { NOTIMPLEMENTED(); }
   void OnBindingToMouse(std::string mouse_action) override { NOTIMPLEMENTED(); }
   void OnMenuEntryPressed() override { NOTIMPLEMENTED(); }
+
+  void ChildPreferredSizeChanged(View* child) override {
+    DCHECK(labels_.size() == kActionMoveKeysSize);
+    if (labels_.size() != kActionMoveKeysSize)
+      return;
+
+    int label_index = -1;
+    auto* label = static_cast<ActionLabel*>(child);
+    for (int i = 0; i < kActionMoveKeysSize; i++) {
+      if (label == labels_[i]) {
+        label_index = i;
+        break;
+      }
+    }
+    if (label_index == -1)
+      return;
+
+    auto content_bounds =
+        CalculateWindowContentBounds(action_->target_window());
+    int radius =
+        std::max(kActionMoveMinRadius, action_->GetUIRadius(content_bounds));
+    auto label_size = label->CalculatePreferredSize();
+    label->SetSize(label_size);
+    int x = kDirection[label_index][0];
+    int y = kDirection[label_index][1];
+    auto pos = gfx::Point(
+        radius + x * (radius - kLabelOffset) - label_size.width() / 2,
+        radius + y * (radius - kLabelOffset) - label_size.height() / 2);
+    label->SetPosition(pos);
+
+    // Calculate minimum size of the |ActionMoveKeyView|.
+    int left = INT_MAX, right = 0, top = INT_MAX, bottom = 0;
+    for (int i = 0; i < kActionMoveKeysSize; i++) {
+      left = std::min(left, labels_[i]->bounds().x());
+      right = std::max(right, labels_[i]->bounds().right());
+      top = std::min(top, labels_[i]->bounds().y());
+      bottom = std::max(bottom, labels_[i]->bounds().bottom());
+    }
+    DCHECK_LT(left, right);
+    DCHECK_LT(top, bottom);
+
+    auto size = gfx::Size(radius * 2, radius * 2);
+    size.SetToMax(gfx::Size(right - left, bottom - top));
+    SetSize(size);
+    auto center_pos = action_->GetUICenterPosition(content_bounds);
+    SetPositionFromCenterPosition(center_pos);
+  }
 };
 
 ActionMove::ActionMove(aura::Window* window) : Action(window) {}
@@ -342,8 +393,6 @@
   }
   action_view_ = view.get();
   view->set_editable(true);
-  auto center_pos = GetUICenterPosition(content_bounds);
-  view->SetPositionFromCenterPosition(center_pos);
   return view;
 }
 
@@ -354,16 +403,18 @@
     // It might be partially overlapped and only remove the keys overlapped.
     for (auto code : input_element.keys()) {
       for (int i = 0; i < pending_binding_->keys().size(); i++) {
-        if (code == pending_binding_->keys()[i])
+        if (code == pending_binding_->keys()[i]) {
           pending_binding_->SetKey(i, ui::DomCode::NONE);
+          if (action_view_)
+            action_view_->set_unbind_label_index(i);
+          PostUnbindProcess();
+        }
       }
     }
   } else {
     // TODO(cuicuiruan): Implement for unbinding mouse-bound action move.
     NOTIMPLEMENTED();
   }
-
-  PostUnbindProcess();
 }
 
 bool ActionMove::RewriteKeyEvent(const ui::KeyEvent* key_event,
diff --git a/chrome/browser/ash/arc/input_overlay/actions/action_tap.cc b/chrome/browser/ash/arc/input_overlay/actions/action_tap.cc
index 1ab0183..5e03e19 100644
--- a/chrome/browser/ash/arc/input_overlay/actions/action_tap.cc
+++ b/chrome/browser/ash/arc/input_overlay/actions/action_tap.cc
@@ -8,7 +8,7 @@
 #include "chrome/browser/ash/arc/input_overlay/actions/input_element.h"
 #include "chrome/browser/ash/arc/input_overlay/constants.h"
 #include "chrome/browser/ash/arc/input_overlay/touch_id_manager.h"
-#include "chrome/browser/ash/arc/input_overlay/ui/action_tag.h"
+#include "chrome/browser/ash/arc/input_overlay/ui/action_label.h"
 #include "ui/aura/window.h"
 #include "ui/events/base_event_utils.h"
 #include "ui/events/keycodes/dom/dom_code.h"
@@ -20,26 +20,22 @@
 namespace input_overlay {
 namespace {
 // UI specs.
-constexpr int kTagPositionToSide = 36;
-constexpr int kTagMargin = 2;
+constexpr int kLabelPositionToSide = 36;
+constexpr int kLabelMargin = 2;
 
-// Create |ActionTag| for |ActionTap|.
-std::unique_ptr<ActionTag> CreateActionTag(InputElement& input_element) {
-  std::unique_ptr<ActionTag> tag;
+// Create |ActionLabel| for |ActionTap|.
+std::unique_ptr<ActionLabel> CreateActionLabel(InputElement& input_element) {
+  std::unique_ptr<ActionLabel> label;
   if (IsKeyboardBound(input_element)) {
     DCHECK(input_element.keys().size() == 1);
-    tag =
-        ActionTag::CreateTextActionTag(GetDisplayText(input_element.keys()[0]));
-    tag->SetSize(tag->GetPreferredSize());
+    label = ActionLabel::CreateTextActionLabel(
+        GetDisplayText(input_element.keys()[0]));
+  } else if (IsMouseBound(input_element)) {
+    label = ActionLabel::CreateImageActionLabel(input_element.mouse_action());
   } else {
-    if (IsMouseBound(input_element)) {
-      tag = ActionTag::CreateImageActionTag(input_element.mouse_action());
-    } else {
-      tag = ActionTag::CreateTextActionTag("?");
-    }
-    tag->SetSize(tag->GetPreferredSize());
+    label = ActionLabel::CreateTextActionLabel("?");
   }
-  return tag;
+  return label;
 }
 
 }  // namespace
@@ -61,7 +57,7 @@
                       const gfx::RectF& content_bounds) override {
     // Add circle if it doesn't exist.
     int radius = action_->GetUIRadius(content_bounds);
-    if (!circle_) {
+    if (show_circle() && !circle_) {
       auto circle = std::make_unique<ActionCircle>(radius);
       circle_ = AddChildView(std::move(circle));
     }
@@ -83,53 +79,34 @@
     if (!binding)
       return;
 
-    if (tags_.empty()) {
-      // Create new action tag when initializing.
-      auto tag = CreateActionTag(*binding);
-      tags_.emplace_back(AddChildView(std::move(tag)));
+    if (labels_.empty()) {
+      // Create new action label when initializing.
+      auto label = CreateActionLabel(*binding);
+      labels_.emplace_back(AddChildView(std::move(label)));
     } else if (!IsBound(*binding)) {
-      // Action tag exists but without any bindings.
-      tags_[0]->SetTextActionTag(std::move(GetDisplayText(ui::DomCode::NONE)));
+      // Action label exists but without any bindings.
+      labels_[0]->SetTextActionLabel(
+          std::move(GetDisplayText(ui::DomCode::NONE)));
     } else if (IsKeyboardBound(*binding)) {
-      // Action tag is bound to keyboard key.
-      tags_[0]->SetTextActionTag(std::move(GetDisplayText(binding->keys()[0])));
+      // Action label is bound to keyboard key.
+      labels_[0]->SetTextActionLabel(
+          std::move(GetDisplayText(binding->keys()[0])));
     } else {
-      // Action tag is bound to mouse.
-      tags_[0]->SetImageActionTag(binding->mouse_action());
-    }
-    auto* tag = tags_[0];
-    auto tag_size = tag->GetPreferredSize();
-    int width = std::max(radius * 2,
-                         radius * 2 - kTagPositionToSide + tag_size.width());
-    SetSize(gfx::Size(width, radius * 2));
-
-    if (action_->on_left_or_middle_side()) {
-      circle_->SetPosition(gfx::Point());
-      tag->SetPosition(gfx::Point(tag_size.width() > kTagPositionToSide
-                                      ? width - tag_size.width()
-                                      : width - kTagPositionToSide,
-                                  radius * 2 - tag_size.height() - kTagMargin));
-      center_.set_x(radius);
-      center_.set_y(radius);
-    } else {
-      circle_->SetPosition(gfx::Point(width - radius * 2, 0));
-      tag->SetPosition(
-          gfx::Point(0, radius * 2 - tag_size.height() - kTagMargin));
-      center_.set_x(width - radius);
-      center_.set_y(radius);
+      // Action label is bound to mouse.
+      labels_[0]->SetImageActionLabel(binding->mouse_action());
     }
   }
 
-  void OnKeyBindingChange(ActionTag* action_tag, ui::DomCode code) override {
-    if (ShouldShowErrorMsg(code))
+  void OnKeyBindingChange(ActionLabel* action_label,
+                          ui::DomCode code) override {
+    if (ShouldShowErrorMsg(code, action_label))
       return;
-    DCHECK(tags_.size() == 1 && tags_[0] == action_tag);
-    if (tags_.size() != 1 || tags_[0] != action_tag)
+    DCHECK(labels_.size() == 1 && labels_[0] == action_label);
+    if (labels_.size() != 1 || labels_[0] != action_label)
       return;
 
     auto input_element = InputElement::CreateActionTapKeyElement(code);
-    display_overlay_controller_->OnBindingChange(action_,
-                                                 std::move(input_element));
+    ChangeBinding(action_, action_label, std::move(input_element));
   }
 
   void OnBindingToKeyboard() override {
@@ -141,7 +118,6 @@
     action_->set_pending_binding(std::move(input_element));
     auto bounds = CalculateWindowContentBounds(action_->target_window());
     SetViewContent(BindingOption::kPending, bounds);
-    SetDisplayMode(DisplayMode::kEdited);
   }
 
   void OnBindingToMouse(std::string mouse_action) override {
@@ -156,8 +132,7 @@
 
     auto input_element =
         InputElement::CreateActionTapMouseElement(mouse_action);
-    display_overlay_controller_->OnBindingChange(action_,
-                                                 std::move(input_element));
+    ChangeBinding(action_, /*ActionLabel=*/nullptr, std::move(input_element));
   }
 
   void OnMenuEntryPressed() override {
@@ -167,6 +142,42 @@
       return;
     menu_entry_->RequestFocus();
   }
+
+  void ChildPreferredSizeChanged(View* child) override {
+    DCHECK(labels_.size() == 1);
+    if (static_cast<ActionLabel*>(child) != labels_[0])
+      return;
+
+    auto content_bounds =
+        CalculateWindowContentBounds(action_->target_window());
+    int radius = action_->GetUIRadius(content_bounds);
+    auto* label = labels_[0];
+    auto label_size = label->CalculatePreferredSize();
+    int width = std::max(
+        radius * 2, radius * 2 - kLabelPositionToSide + label_size.width());
+    if (action_->on_left_or_middle_side()) {
+      if (show_circle())
+        circle_->SetPosition(gfx::Point());
+      label->SetPosition(
+          gfx::Point(label_size.width() > kLabelPositionToSide
+                         ? width - label_size.width()
+                         : width - kLabelPositionToSide,
+                     radius * 2 - label_size.height() - kLabelMargin));
+      center_.set_x(radius);
+      center_.set_y(radius);
+    } else {
+      if (show_circle())
+        circle_->SetPosition(gfx::Point(width - radius * 2, 0));
+      label->SetPosition(
+          gfx::Point(0, radius * 2 - label_size.height() - kLabelMargin));
+      center_.set_x(width - radius);
+      center_.set_y(radius);
+    }
+    label->SetSize(label_size);
+    SetSize(gfx::Size(width, radius * 2));
+    auto center_pos = action_->GetUICenterPosition(content_bounds);
+    SetPositionFromCenterPosition(center_pos);
+  }
 };
 
 ActionTap::ActionTap(aura::Window* window) : Action(window) {}
@@ -256,8 +267,6 @@
   auto view = std::make_unique<ActionTapView>(this, display_overlay_controller,
                                               content_bounds);
   view->set_editable(true);
-  auto center_pos = GetUICenterPosition(content_bounds);
-  view->SetPositionFromCenterPosition(center_pos);
   action_view_ = view.get();
   return view;
 }
@@ -266,7 +275,8 @@
   if (pending_binding_)
     pending_binding_.reset();
   pending_binding_ = std::make_unique<InputElement>();
-
+  if (action_view_)
+    action_view_->set_unbind_label_index(0);
   PostUnbindProcess();
 }
 
diff --git a/chrome/browser/ash/arc/input_overlay/actions/input_element.cc b/chrome/browser/ash/arc/input_overlay/actions/input_element.cc
index 2c1a3da..7e4c341 100644
--- a/chrome/browser/ash/arc/input_overlay/actions/input_element.cc
+++ b/chrome/browser/ash/arc/input_overlay/actions/input_element.cc
@@ -201,5 +201,9 @@
   return equal;
 }
 
+bool InputElement::operator!=(const InputElement& other) const {
+  return !(*this == other);
+}
+
 }  // namespace input_overlay
 }  // namespace arc
diff --git a/chrome/browser/ash/arc/input_overlay/actions/input_element.h b/chrome/browser/ash/arc/input_overlay/actions/input_element.h
index 404f12f..2fcb623 100644
--- a/chrome/browser/ash/arc/input_overlay/actions/input_element.h
+++ b/chrome/browser/ash/arc/input_overlay/actions/input_element.h
@@ -80,6 +80,7 @@
   int mouse_flags() const { return mouse_flags_; }
 
   bool operator==(const InputElement& other) const;
+  bool operator!=(const InputElement& other) const;
 
  private:
   // Input source for this input element, could be keyboard or mouse or both.
diff --git a/chrome/browser/ash/arc/input_overlay/constants.h b/chrome/browser/ash/arc/input_overlay/constants.h
index 8b3f52a6..87d10c3 100644
--- a/chrome/browser/ash/arc/input_overlay/constants.h
+++ b/chrome/browser/ash/arc/input_overlay/constants.h
@@ -20,14 +20,19 @@
   // Display overlay can receive events and action labels can be focused. It
   // shows input mapping in edit mode.
   kEdit,
-  // Display overlay can still receive events. It is a mode after an action
-  // label receives a valid key mapping.
-  kEdited,
   // Display overlay can receive events but action labels can't be focused.
   // It shows expanded menu and input mapping as in view mode.
   kMenu,
-  // It is a mode when an action is removed the input binding.
+
+  // Below are related to edit for |ActionView|.
+  // Edit mode when action is assigned a pending input binding.
+  kEditedSuccess,
+  // Edit mode when an action is removed the input binding.
   kEditedUnbound,
+  // Edit mode when a wrong/unsupported input is trying to bind.
+  kEditedError,
+  // Restore mode when restoring the default input bindings.
+  kRestore,
 };
 
 // Binding options for different ui display stages.
@@ -49,6 +54,21 @@
   kMove,
 };
 
+// Edit states related to the mouse input and focus for |ActionLabel|.
+enum class EditState {
+  kNone,
+  // Default edit state when no mouse hovers or no focus.
+  kEditDefault,
+  // Edit state when the mouse hovers. This can be overridden by |kEditFocus|.
+  kEditHover,
+  // Edit state when the |ActionLabel| is focused.
+  kEditFocus,
+  // Edit state when a wrong/unsupported input tries to bind.
+  kEditError,
+  // Edit state when a input binding is removed.
+  kEditUnbind,
+};
+
 }  // namespace input_overlay
 }  // namespace arc
 
diff --git a/chrome/browser/ash/arc/input_overlay/input_overlay_resources_util.cc b/chrome/browser/ash/arc/input_overlay/input_overlay_resources_util.cc
index 3afe9e2..154ff9c 100644
--- a/chrome/browser/ash/arc/input_overlay/input_overlay_resources_util.cc
+++ b/chrome/browser/ash/arc/input_overlay/input_overlay_resources_util.cc
@@ -16,7 +16,6 @@
        IDR_IO_ORG_CHROMIUM_ARC_TESTAPP_INPUTOVERLAY},
       {"com.blackpanther.ninjaarashi2", IDR_IO_COM_BLACKPANTHER_NINJAARASHI2},
       {"com.habby.archero", IDR_IO_COM_HABBY_ARCHERO},
-      {"com.dts.freefireth", IDR_IO_COM_DTS_FREEFIRETH},
       {"com.fingersoft.hillclimb", IDR_IO_COM_FINGERSOFT_HILLCLIMB},
       {"com.androbaby.game2048", IDR_IO_COM_ANDROBABY_GAME2048},
       {"co.imba.archero", IDR_IO_CO_IMBA_ARCHERO},
diff --git a/chrome/browser/ash/arc/input_overlay/touch_injector.cc b/chrome/browser/ash/arc/input_overlay/touch_injector.cc
index 6fa9dcc6..69213e0 100644
--- a/chrome/browser/ash/arc/input_overlay/touch_injector.cc
+++ b/chrome/browser/ash/arc/input_overlay/touch_injector.cc
@@ -175,9 +175,9 @@
   observation_.Reset();
 }
 
-void TouchInjector::OnBindingChange(Action* target_action,
-                                    std::unique_ptr<InputElement> input_element,
-                                    DisplayMode mode) {
+void TouchInjector::OnBindingChange(
+    Action* target_action,
+    std::unique_ptr<InputElement> input_element) {
   if (display_overlay_controller_)
     display_overlay_controller_->RemoveEditErrorMsg();
   Action* overlapped_action = nullptr;
@@ -195,7 +195,7 @@
   if (overlapped_action)
     overlapped_action->Unbind(*input_element);
 
-  target_action->PrepareToBind(std::move(input_element), mode);
+  target_action->PrepareToBind(std::move(input_element));
 }
 
 void TouchInjector::OnApplyPendingBinding() {
@@ -237,7 +237,7 @@
     auto input_element =
         InputElement::ConvertFromProto(action_proto.input_element());
     DCHECK(input_element);
-    OnBindingChange(action, std::move(input_element), DisplayMode::kView);
+    OnBindingChange(action, std::move(input_element));
   }
   OnApplyPendingBinding();
 }
diff --git a/chrome/browser/ash/arc/input_overlay/touch_injector.h b/chrome/browser/ash/arc/input_overlay/touch_injector.h
index f385e4e..a4be1a2 100644
--- a/chrome/browser/ash/arc/input_overlay/touch_injector.h
+++ b/chrome/browser/ash/arc/input_overlay/touch_injector.h
@@ -105,8 +105,7 @@
   // (|mode| = DisplayMode::kEdit) or from customized protobuf data (|mode| =
   // DisplayMode::kView).
   void OnBindingChange(Action* target_action,
-                       std::unique_ptr<InputElement> input_element,
-                       DisplayMode mode = DisplayMode::kEdit);
+                       std::unique_ptr<InputElement> input_element);
   // Apply pending binding as current binding, but don't save into the storage.
   void OnApplyPendingBinding();
   // Save customized input binding/pending binding as current binding and go
diff --git a/chrome/browser/ash/arc/input_overlay/ui/action_circle.cc b/chrome/browser/ash/arc/input_overlay/ui/action_circle.cc
index 761e9e9..8407920c 100644
--- a/chrome/browser/ash/arc/input_overlay/ui/action_circle.cc
+++ b/chrome/browser/ash/arc/input_overlay/ui/action_circle.cc
@@ -64,7 +64,7 @@
       SetBackground(std::make_unique<CircleBackground>(kEditDefaultColor,
                                                        kEditDefaultStroke));
       break;
-    case DisplayMode::kEdited:
+    case DisplayMode::kEditedSuccess:
     case DisplayMode::kEditedUnbound:
       SetBackground(
           std::make_unique<CircleBackground>(kEditedColor, kEditedStroke));
diff --git a/chrome/browser/ash/arc/input_overlay/ui/action_label.cc b/chrome/browser/ash/arc/input_overlay/ui/action_label.cc
index 552ec07..450c9cb 100644
--- a/chrome/browser/ash/arc/input_overlay/ui/action_label.cc
+++ b/chrome/browser/ash/arc/input_overlay/ui/action_label.cc
@@ -6,30 +6,45 @@
 
 #include <set>
 
-#include "chrome/browser/ash/arc/input_overlay/ui/action_tag.h"
+#include "ash/style/style_util.h"
+#include "chrome/app/vector_icons/vector_icons.h"
+#include "chrome/browser/ash/arc/input_overlay/ui/action_label.h"
+#include "chrome/browser/ash/arc/input_overlay/ui/action_view.h"
 #include "third_party/skia/include/core/SkColor.h"
 #include "ui/events/keycodes/dom/keycode_converter.h"
 #include "ui/gfx/color_palette.h"
+#include "ui/gfx/paint_vector_icon.h"
 #include "ui/views/background.h"
+#include "ui/views/controls/highlight_path_generator.h"
 
 namespace arc {
 namespace input_overlay {
 namespace {
 // UI specs.
-constexpr int kWidthPadding = 10;
-constexpr gfx::Size kMinimumViewLabelSize(32, 32);
+constexpr int kSideInset = 6;
+constexpr gfx::Size kMinimumSmallSize(32, 32);
+constexpr gfx::Size kMinimumLargeSize(48, 48);
 constexpr int kCornerRadiusView = 6;
+constexpr int kIconSize = 20;
+constexpr char kFontSytle[] = "Google Sans";
+constexpr int kSmallFontSize = 16;
+constexpr int kLargeFontSize = 20;
 
+// About colors.
 constexpr SkColor kViewModeBgColor = SkColorSetA(SK_ColorGRAY, 0x99);
 constexpr SkColor kEditModeBgColor = SK_ColorWHITE;
 constexpr SkColor kEditedUnboundBgColor = gfx::kGoogleRed300;
 constexpr SkColor kViewTextColor = SK_ColorWHITE;
 constexpr SkColor kEditTextColor = gfx::kGoogleGrey900;
+constexpr SkColor kFocusRingGreyColor = SkColorSetA(gfx::kGoogleGrey200, 0x60);
+constexpr SkColor kFocusRingBlueColor = gfx::kGoogleBlue300;
+constexpr SkColor kFocusRingRedColor = gfx::kGoogleRed300;
 
-constexpr char kFontSytle[] = "Google Sans";
-constexpr int kViewFontSize = 16;
-constexpr int kUnFocusFontSize = 16;
-constexpr int kFocusFontSize = 20;
+// About focus ring.
+// Gap between focus ring outer edge to label.
+constexpr float kHaloInset = -3;
+// Thickness of focus ring.
+constexpr float kHaloThickness = 2;
 
 // UI strings.
 // TODO(cuicuiruan): move the strings to chrome/app/generated_resources.grd
@@ -80,81 +95,219 @@
   }
 }
 
-ActionLabel::ActionLabel() : views::Label() {}
-ActionLabel::ActionLabel(const std::u16string& text) : views::Label(text) {
-  SetToViewMode();
+ActionLabel::ActionLabel() : views::LabelButton() {
+  SetRequestFocusOnPress(true);
+  SetHorizontalAlignment(gfx::ALIGN_CENTER);
+  SetBorder(views::CreateEmptyBorder(gfx::Insets::VH(0, kSideInset)));
 }
 
 ActionLabel::~ActionLabel() = default;
 
+// static
+std::unique_ptr<ActionLabel> ActionLabel::CreateTextActionLabel(
+    const std::string& text) {
+  auto label = std::make_unique<ActionLabel>();
+  label->SetTextActionLabel(text);
+  return label;
+}
+
+// static
+std::unique_ptr<ActionLabel> ActionLabel::CreateImageActionLabel(
+    MouseAction mouse_action) {
+  DCHECK(mouse_action == MouseAction::PRIMARY_CLICK ||
+         mouse_action == MouseAction::SECONDARY_CLICK);
+  if (mouse_action != MouseAction::PRIMARY_CLICK &&
+      mouse_action != MouseAction::SECONDARY_CLICK) {
+    return nullptr;
+  }
+  auto label = std::make_unique<ActionLabel>();
+  label->SetImageActionLabel(mouse_action);
+  return label;
+}
+
+void ActionLabel::SetTextActionLabel(const std::string& text) {
+  label()->SetText(base::UTF8ToUTF16(text));
+  SetAccessibleName(label()->GetText());
+}
+
+void ActionLabel::SetImageActionLabel(MouseAction mouse_action) {
+  SetAccessibleName(base::UTF8ToUTF16(GetClassName()));
+  set_mouse_action(mouse_action);
+}
+
+void ActionLabel::SetDisplayMode(DisplayMode mode) {
+  DCHECK(mode != DisplayMode::kMenu);
+  if (mode == DisplayMode::kMenu)
+    return;
+
+  switch (mode) {
+    case DisplayMode::kView:
+      SetToViewMode();
+      break;
+    case DisplayMode::kEdit:
+      SetToEditMode();
+      break;
+    case DisplayMode::kEditedSuccess:
+      SetToEditFocus();
+      break;
+    case DisplayMode::kEditedUnbound:
+      SetToEditUnBind();
+      break;
+    case DisplayMode::kEditedError:
+      SetToEditError();
+      break;
+    case DisplayMode::kRestore:
+      if (!ClearFocus())
+        SetToEditDefault();
+      break;
+    default:
+      NOTREACHED();
+      break;
+  }
+}
+
 gfx::Size ActionLabel::CalculatePreferredSize() const {
-  auto size = Label::CalculatePreferredSize();
-  size.set_width(size.width() + kWidthPadding);
-  size.SetToMax(kMinimumViewLabelSize);
+  auto size = LabelButton::CalculatePreferredSize();
+  switch (edit_state_) {
+    case EditState::kNone:
+    case EditState::kEditDefault:
+    case EditState::kEditUnbind:
+      size.SetToMax(kMinimumSmallSize);
+      break;
+    default:
+      size.SetToMax(kMinimumLargeSize);
+      break;
+  }
   return size;
 }
 
-void ActionLabel::OnKeyEvent(ui::KeyEvent* event) {
-  if (event->type() == ui::ET_KEY_PRESSED)
-    return;
-
+bool ActionLabel::OnKeyPressed(const ui::KeyEvent& event) {
   DCHECK(parent());
-  auto code = event->code();
-  auto* parent_view = static_cast<ActionTag*>(parent());
-  if (base::UTF8ToUTF16(GetDisplayText(code)) == GetText())
-    parent_view->ShowErrorMsg(kEditErrorSameKey);
+  auto code = event.code();
+  auto* parent_view = static_cast<ActionView*>(parent());
+  if (base::UTF8ToUTF16(GetDisplayText(code)) == GetText()) {
+    parent_view->ShowErrorMsg(kEditErrorSameKey, this);
+  } else {
+    parent_view->OnKeyBindingChange(this, code);
+  }
+  return true;
+}
 
-  parent_view->OnKeyBindingChange(code);
+void ActionLabel::OnMouseEntered(const ui::MouseEvent& event) {
+  if (!HasFocus())
+    SetToEditHover();
+}
+
+void ActionLabel::OnMouseExited(const ui::MouseEvent& event) {
+  if (!HasFocus())
+    SetToEditDefault();
 }
 
 void ActionLabel::OnFocus() {
   SetToEditFocus();
-  SelectAll();
-  Label::OnFocus();
+  LabelButton::OnFocus();
 }
 
 void ActionLabel::OnBlur() {
-  SetToEditMode();
-  ClearSelection();
-  Label::OnBlur();
+  SetToEditDefault();
+  LabelButton::OnBlur();
+}
+
+bool ActionLabel::ClearFocus() {
+  auto* focus_manager = GetFocusManager();
+  bool has_focus = false;
+  if (focus_manager) {
+    has_focus = HasFocus();
+    focus_manager->ClearFocus();
+  }
+  return has_focus;
 }
 
 void ActionLabel::SetToViewMode() {
-  SetFontList(gfx::FontList({kFontSytle}, gfx::Font::NORMAL, kViewFontSize,
-                            gfx::Font::Weight::BOLD));
-  SetFocusBehavior(FocusBehavior::NEVER);
+  ClearFocus();
+  SetInstallFocusRingOnFocus(false);
+  label()->SetFontList(gfx::FontList({kFontSytle}, gfx::Font::NORMAL,
+                                     kSmallFontSize, gfx::Font::Weight::BOLD));
+  SetEnabledTextColors(kViewTextColor);
+
+  if (mouse_action_ != MouseAction::NONE) {
+    if (mouse_action_ == MouseAction::PRIMARY_CLICK) {
+      auto left_click_icon = gfx::CreateVectorIcon(
+          gfx::IconDescription(kMouseLeftClickViewIcon, kIconSize));
+      SetImage(views::Button::STATE_NORMAL, left_click_icon);
+    } else {
+      auto right_click_icon = gfx::CreateVectorIcon(
+          gfx::IconDescription(kMouseRightClickViewIcon, kIconSize));
+      SetImage(views::Button::STATE_NORMAL, right_click_icon);
+    }
+  }
+
   SetBackground(
       views::CreateRoundedRectBackground(kViewModeBgColor, kCornerRadiusView));
-  SetAutoColorReadabilityEnabled(false);
-  SetEnabledColor(kViewTextColor);
-  SetSize(GetPreferredSize());
-  SetSelectable(false);
-  SetEnabled(false);
+  SetPreferredSize(CalculatePreferredSize());
 }
 
 void ActionLabel::SetToEditMode() {
-  SetFontList(gfx::FontList({kFontSytle}, gfx::Font::NORMAL, kUnFocusFontSize,
-                            gfx::Font::Weight::BOLD));
-  SetFocusBehavior(FocusBehavior::ALWAYS);
-  SetBackground(
-      views::CreateRoundedRectBackground(kEditModeBgColor, kCornerRadiusView));
-  SetEnabledColor(kEditTextColor);
-  SetSize(GetPreferredSize());
-  SetSelectable(true);
-  SetEnabled(true);
+  SetInstallFocusRingOnFocus(true);
+  auto* focus_ring = views::FocusRing::Get(this);
+  focus_ring->SetHaloInset(kHaloInset);
+  focus_ring->SetHaloThickness(kHaloThickness);
+  focus_ring->SetHasFocusPredicate([](views::View* view) {
+    return view->IsMouseHovered() || view->HasFocus();
+  });
+
+  SetEnabledTextColors(kEditTextColor);
+
+  if (mouse_action_ != MouseAction::NONE) {
+    if (mouse_action_ == MouseAction::PRIMARY_CLICK) {
+      auto left_click_icon = gfx::CreateVectorIcon(
+          gfx::IconDescription(kMouseLeftClickEditIcon, kIconSize));
+      SetImage(views::Button::STATE_NORMAL, left_click_icon);
+    } else {
+      auto right_click_icon = gfx::CreateVectorIcon(
+          gfx::IconDescription(kMouseRightClickEditIcon, kIconSize));
+      SetImage(views::Button::STATE_NORMAL, right_click_icon);
+    }
+  }
+  SetToEditDefault();
 }
 
-void ActionLabel::SetToEditedUnBind() {
-  SetBackground(views::CreateRoundedRectBackground(kEditedUnboundBgColor,
-                                                   kCornerRadiusView));
-  SetSize(GetPreferredSize());
-  SetSelectable(true);
-  SetEnabled(true);
+void ActionLabel::SetToEditDefault() {
+  edit_state_ = EditState::kEditDefault;
+  label()->SetFontList(gfx::FontList({kFontSytle}, gfx::Font::NORMAL,
+                                     kSmallFontSize, gfx::Font::Weight::BOLD));
+  SetPreferredSize(CalculatePreferredSize());
+  SetBackground(
+      views::CreateRoundedRectBackground(kEditModeBgColor, kCornerRadiusView));
+}
+
+void ActionLabel::SetToEditHover() {
+  edit_state_ = EditState::kEditHover;
+  auto* focus_ring = views::FocusRing::Get(this);
+  focus_ring->SetColor(kFocusRingGreyColor);
+  SetPreferredSize(CalculatePreferredSize());
 }
 
 void ActionLabel::SetToEditFocus() {
-  SetFontList(gfx::FontList({kFontSytle}, gfx::Font::NORMAL, kFocusFontSize,
-                            gfx::Font::Weight::BOLD));
+  edit_state_ = EditState::kEditFocus;
+  views::FocusRing::Get(this)->SetColor(kFocusRingBlueColor);
+  label()->SetFontList(gfx::FontList({kFontSytle}, gfx::Font::NORMAL,
+                                     kLargeFontSize, gfx::Font::Weight::BOLD));
+  SetPreferredSize(CalculatePreferredSize());
+  SetBackground(
+      views::CreateRoundedRectBackground(kEditModeBgColor, kCornerRadiusView));
+}
+
+void ActionLabel::SetToEditError() {
+  edit_state_ = EditState::kEditError;
+  views::FocusRing::Get(this)->SetColor(kFocusRingRedColor);
+}
+
+void ActionLabel::SetToEditUnBind() {
+  edit_state_ = EditState::kEditUnbind;
+  SetPreferredSize(CalculatePreferredSize());
+  SetBackground(views::CreateRoundedRectBackground(kEditedUnboundBgColor,
+                                                   kCornerRadiusView));
 }
 
 }  // namespace input_overlay
diff --git a/chrome/browser/ash/arc/input_overlay/ui/action_label.h b/chrome/browser/ash/arc/input_overlay/ui/action_label.h
index 14a57871..fb5da151 100644
--- a/chrome/browser/ash/arc/input_overlay/ui/action_label.h
+++ b/chrome/browser/ash/arc/input_overlay/ui/action_label.h
@@ -8,7 +8,8 @@
 #include <string>
 
 #include "chrome/browser/ash/arc/input_overlay/constants.h"
-#include "ui/views/controls/label.h"
+#include "chrome/browser/ash/arc/input_overlay/db/proto/app_data.pb.h"
+#include "ui/views/controls/button/label_button.h"
 
 namespace arc {
 namespace input_overlay {
@@ -18,27 +19,55 @@
 std::string GetDisplayText(const ui::DomCode code);
 
 // ActionLabel shows text mapping hint for each action.
-class ActionLabel : public views::Label {
+class ActionLabel : public views::LabelButton {
  public:
   ActionLabel();
-  explicit ActionLabel(const std::u16string& text);
-
   ActionLabel(const ActionLabel&) = delete;
   ActionLabel& operator=(const ActionLabel&) = delete;
   ~ActionLabel() override;
 
-  void SetToViewMode();
-  void SetToEditMode();
-  void SetToEditedUnBind();
+  static std::unique_ptr<ActionLabel> CreateTextActionLabel(
+      const std::string& text);
+  static std::unique_ptr<ActionLabel> CreateImageActionLabel(
+      MouseAction mouse_action);
+
+  void SetTextActionLabel(const std::string& text);
+  void SetImageActionLabel(MouseAction mouse_action);
+  void SetDisplayMode(DisplayMode mode);
+  void ShowErrorMsg(base::StringPiece error_msg);
+  void OnKeyBindingChange(ui::DomCode code);
 
   // views::View:
   gfx::Size CalculatePreferredSize() const override;
-  void OnKeyEvent(ui::KeyEvent* event) override;
+  bool OnKeyPressed(const ui::KeyEvent& event) override;
+  void OnMouseEntered(const ui::MouseEvent& event) override;
+  void OnMouseExited(const ui::MouseEvent& event) override;
   void OnFocus() override;
   void OnBlur() override;
 
+  void set_mouse_action(MouseAction mouse_action) {
+    mouse_action_ = mouse_action;
+  }
+
  private:
+  // Return true if it has focus before clear focus.
+  bool ClearFocus();
+
+  void SetToViewMode();
+  void SetToEditMode();
+  // In edit mode without mouse hover or focus.
+  void SetToEditDefault();
+  // In edit mode when mouse hovers.
+  void SetToEditHover();
+  // In edit mode when this view is focused.
   void SetToEditFocus();
+  // In edit mode when there is edit error.
+  void SetToEditError();
+  // In edit mode when the input is unbound.
+  void SetToEditUnBind();
+
+  MouseAction mouse_action_ = MouseAction::NONE;
+  EditState edit_state_ = EditState::kNone;
 };
 }  // namespace input_overlay
 }  // namespace arc
diff --git a/chrome/browser/ash/arc/input_overlay/ui/action_tag.cc b/chrome/browser/ash/arc/input_overlay/ui/action_tag.cc
deleted file mode 100644
index ff9f4339..0000000
--- a/chrome/browser/ash/arc/input_overlay/ui/action_tag.cc
+++ /dev/null
@@ -1,183 +0,0 @@
-// Copyright 2022 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/ash/arc/input_overlay/ui/action_tag.h"
-
-#include "chrome/app/vector_icons/vector_icons.h"
-#include "chrome/browser/ash/arc/input_overlay/actions/input_element.h"
-#include "chrome/browser/ash/arc/input_overlay/ui/action_view.h"
-#include "ui/gfx/paint_vector_icon.h"
-#include "ui/views/background.h"
-#include "ui/views/controls/button/image_button.h"
-
-namespace arc {
-namespace input_overlay {
-namespace {
-constexpr int kIconSize = 20;
-constexpr int kCornerRadius = 6;
-constexpr SkColor kEditModeBgColor = SK_ColorWHITE;
-constexpr SkColor kViewModeBgColor = SkColorSetA(SK_ColorGRAY, 0x99);
-constexpr gfx::Size kImageButtonSize(32, 32);
-}  // namespace
-
-class ActionTag::ActionImage : public views::ImageButton {
- public:
-  explicit ActionImage(MouseAction mouse_action)
-      : views::ImageButton(), mouse_action_(mouse_action) {
-    SetToViewMode();
-  }
-
-  ActionImage() : views::ImageButton() { SetToViewMode(); }
-
-  ~ActionImage() override = default;
-
-  void SetDisplayMode(DisplayMode mode) {
-    switch (mode) {
-      case DisplayMode::kMenu:
-      case DisplayMode::kView:
-        SetToViewMode();
-        break;
-      case DisplayMode::kEdit:
-        SetToEditMode();
-        break;
-      default:
-        NOTREACHED();
-        break;
-    }
-  }
-
-  void SetToViewMode() {
-    if (mouse_action_ == MouseAction::NONE)
-      return;
-    if (mouse_action_ == MouseAction::PRIMARY_CLICK) {
-      auto left_click_icon = gfx::CreateVectorIcon(
-          gfx::IconDescription(kMouseLeftClickViewIcon, kIconSize));
-      SetImage(views::Button::STATE_NORMAL, left_click_icon);
-    } else {
-      auto right_click_icon = gfx::CreateVectorIcon(
-          gfx::IconDescription(kMouseRightClickViewIcon, kIconSize));
-      SetImage(views::Button::STATE_NORMAL, right_click_icon);
-    }
-    SetBackground(
-        views::CreateRoundedRectBackground(kViewModeBgColor, kCornerRadius));
-  }
-
-  void SetToEditMode() {
-    if (mouse_action_ == MouseAction::PRIMARY_CLICK) {
-      auto left_click_icon = gfx::CreateVectorIcon(
-          gfx::IconDescription(kMouseLeftClickEditIcon, kIconSize));
-      SetImage(views::Button::STATE_NORMAL, left_click_icon);
-    } else {
-      auto right_click_icon = gfx::CreateVectorIcon(
-          gfx::IconDescription(kMouseRightClickEditIcon, kIconSize));
-      SetImage(views::Button::STATE_NORMAL, right_click_icon);
-    }
-    SetBackground(
-        views::CreateRoundedRectBackground(kEditModeBgColor, kCornerRadius));
-  }
-
- private:
-  MouseAction mouse_action_;
-};
-
-ActionTag::ActionTag() : views::View() {}
-ActionTag::~ActionTag() = default;
-
-void ActionTag::SetTextActionTag(const std::string& text) {
-  if (image_) {
-    RemoveChildViewT(image_);
-    image_ = nullptr;
-  }
-
-  if (!label_) {
-    auto label = std::make_unique<ActionLabel>(base::UTF8ToUTF16(text));
-    label->SetPosition(gfx::Point());
-    label_ = AddChildView(std::move(label));
-  } else {
-    label_->SetText(base::UTF8ToUTF16(text));
-  }
-  label_->SetSize(label_->GetPreferredSize());
-}
-
-void ActionTag::SetImageActionTag(MouseAction mouse_action) {
-  RemoveAllChildViews();
-  label_ = nullptr;
-  image_ = nullptr;
-
-  auto image = std::make_unique<ActionImage>(mouse_action);
-  image->SetAccessibleName(base::UTF8ToUTF16(image->GetClassName()));
-  image->SetImageHorizontalAlignment(views::ImageButton::ALIGN_CENTER);
-  image->SetImageVerticalAlignment(views::ImageButton::ALIGN_MIDDLE);
-  image->SetToViewMode();
-  image->SetPosition(gfx::Point());
-  image->SetSize(kImageButtonSize);
-  image_ = AddChildView(std::move(image));
-}
-
-// static
-std::unique_ptr<ActionTag> ActionTag::CreateTextActionTag(std::string text) {
-  auto tag = std::make_unique<ActionTag>();
-  tag->SetTextActionTag(text);
-  return tag;
-}
-
-// static
-std::unique_ptr<ActionTag> ActionTag::CreateImageActionTag(
-    MouseAction mouse_action) {
-  DCHECK(mouse_action == MouseAction::PRIMARY_CLICK ||
-         mouse_action == MouseAction::SECONDARY_CLICK);
-  if (mouse_action != MouseAction::PRIMARY_CLICK &&
-      mouse_action != MouseAction::SECONDARY_CLICK) {
-    return nullptr;
-  }
-  auto tag = std::make_unique<ActionTag>();
-  tag->SetImageActionTag(mouse_action);
-
-  return tag;
-}
-
-void ActionTag::SetDisplayMode(DisplayMode mode) {
-  switch (mode) {
-    case DisplayMode::kMenu:
-    case DisplayMode::kView:
-      if (label_)
-        label_->SetToViewMode();
-      if (image_)
-        image_->SetToViewMode();
-      break;
-    case DisplayMode::kEdit:
-    case DisplayMode::kEdited:
-      if (label_)
-        label_->SetToEditMode();
-      if (image_)
-        image_->SetToEditMode();
-      break;
-    case DisplayMode::kEditedUnbound:
-      if (label_)
-        label_->SetToEditedUnBind();
-      break;
-    default:
-      NOTREACHED();
-      break;
-  }
-}
-
-void ActionTag::ShowErrorMsg(base::StringPiece error_msg) {
-  static_cast<ActionView*>(parent())->ShowErrorMsg(error_msg);
-}
-
-void ActionTag::OnKeyBindingChange(ui::DomCode code) {
-  DCHECK(parent());
-  static_cast<ActionView*>(parent())->OnKeyBindingChange(this, code);
-}
-
-gfx::Size ActionTag::CalculatePreferredSize() const {
-  DCHECK((label_ && !image_) || (!label_ && image_));
-  if (image_)
-    return image_->size();
-  return label_ ? label_->GetPreferredSize() : gfx::Size();
-}
-
-}  // namespace input_overlay
-}  // namespace arc
diff --git a/chrome/browser/ash/arc/input_overlay/ui/action_tag.h b/chrome/browser/ash/arc/input_overlay/ui/action_tag.h
deleted file mode 100644
index 08718e2..0000000
--- a/chrome/browser/ash/arc/input_overlay/ui/action_tag.h
+++ /dev/null
@@ -1,53 +0,0 @@
-// Copyright 2022 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 CHROME_BROWSER_ASH_ARC_INPUT_OVERLAY_UI_ACTION_TAG_H_
-#define CHROME_BROWSER_ASH_ARC_INPUT_OVERLAY_UI_ACTION_TAG_H_
-
-#include "base/memory/raw_ptr.h"
-#include "base/strings/string_piece_forward.h"
-#include "chrome/browser/ash/arc/input_overlay/constants.h"
-#include "chrome/browser/ash/arc/input_overlay/db/proto/app_data.pb.h"
-#include "chrome/browser/ash/arc/input_overlay/ui/action_label.h"
-#include "ui/views/view.h"
-
-namespace arc {
-namespace input_overlay {
-
-// ActionTag is used to showing the mapping hint for each action. It can show
-// text hint or image hint.
-class ActionTag : public views::View {
- public:
-  ActionTag();
-  ActionTag(const ActionTag&) = delete;
-  ActionTag& operator=(const ActionTag&) = delete;
-  ~ActionTag() override;
-
-  static std::unique_ptr<ActionTag> CreateTextActionTag(std::string text);
-  static std::unique_ptr<ActionTag> CreateImageActionTag(
-      MouseAction mouse_action);
-
-  void SetTextActionTag(const std::string& text);
-  void SetImageActionTag(MouseAction mouse_action);
-  void SetDisplayMode(DisplayMode mode);
-  void ShowErrorMsg(base::StringPiece error_msg);
-  void OnKeyBindingChange(ui::DomCode code);
-
-  // views::View:
-  gfx::Size CalculatePreferredSize() const override;
-
- private:
-  class ActionImage;
-
-  void InitTextTag();
-  void InitImageTag();
-
-  raw_ptr<ActionImage> image_ = nullptr;
-  raw_ptr<ActionLabel> label_ = nullptr;
-};
-
-}  // namespace input_overlay
-}  // namespace arc
-
-#endif  // CHROME_BROWSER_ASH_ARC_INPUT_OVERLAY_UI_ACTION_TAG_H_
diff --git a/chrome/browser/ash/arc/input_overlay/ui/action_view.cc b/chrome/browser/ash/arc/input_overlay/ui/action_view.cc
index a41dbe2..c652991 100644
--- a/chrome/browser/ash/arc/input_overlay/ui/action_view.cc
+++ b/chrome/browser/ash/arc/input_overlay/ui/action_view.cc
@@ -41,7 +41,7 @@
       display_overlay_controller_(display_overlay_controller) {}
 ActionView::~ActionView() = default;
 
-void ActionView::SetDisplayMode(DisplayMode mode) {
+void ActionView::SetDisplayMode(DisplayMode mode, ActionLabel* editing_label) {
   DCHECK(mode != DisplayMode::kEducation);
   if ((!editable_ && mode == DisplayMode::kEdit) || mode == DisplayMode::kMenu)
     return;
@@ -56,10 +56,14 @@
       SetVisible(true);
   }
 
-  if (circle_)
+  if (show_circle() && circle_)
     circle_->SetDisplayMode(mode);
-  for (auto* tag : tags_)
-    tag->SetDisplayMode(mode);
+  if (!editing_label) {
+    for (auto* label : labels_)
+      label->SetDisplayMode(mode);
+  } else {
+    editing_label->SetDisplayMode(mode);
+  }
 }
 
 void ActionView::SetPositionFromCenterPosition(gfx::PointF& center_position) {
@@ -86,9 +90,18 @@
   display_overlay_controller_->RemoveActionEditMenu();
 }
 
-void ActionView::ShowErrorMsg(base::StringPiece error_msg) {
+void ActionView::ShowErrorMsg(base::StringPiece error_msg,
+                              ActionLabel* editing_label) {
   display_overlay_controller_->AddEditErrorMsg(this, error_msg);
-  SetDisplayMode(DisplayMode::kEdited);
+  SetDisplayMode(DisplayMode::kEditedError, editing_label);
+}
+
+void ActionView::ChangeBinding(Action* action,
+                               ActionLabel* action_label,
+                               std::unique_ptr<InputElement> input_element) {
+  display_overlay_controller_->OnBindingChange(action,
+                                               std::move(input_element));
+  SetDisplayMode(DisplayMode::kEditedSuccess, action_label);
 }
 
 void ActionView::OnResetBinding() {
@@ -124,7 +137,8 @@
   menu_entry_ = nullptr;
 }
 
-bool ActionView::ShouldShowErrorMsg(ui::DomCode code) {
+bool ActionView::ShouldShowErrorMsg(ui::DomCode code,
+                                    ActionLabel* editing_label) {
   // Check if |code| is duplicated with the keys in its action. For example,
   // there are four keys involved in the key-bound |ActionMove|.
   auto& binding = action_->GetCurrentDisplayedBinding();
@@ -132,8 +146,7 @@
     for (const auto& key : binding.keys()) {
       if (key != code)
         continue;
-      display_overlay_controller_->AddEditErrorMsg(this,
-                                                   kEditErrorDuplicatedKey);
+      ShowErrorMsg(kEditErrorDuplicatedKey, editing_label);
       return true;
     }
   }
@@ -141,8 +154,7 @@
   if ((!action_->support_modifier_key() &&
        ModifierDomCodeToEventFlag(code) != ui::EF_NONE) ||
       IsReservedDomCode(code)) {
-    display_overlay_controller_->AddEditErrorMsg(this,
-                                                 kEditErrorUnsupportedKey);
+    ShowErrorMsg(kEditErrorUnsupportedKey, editing_label);
     return true;
   }
 
diff --git a/chrome/browser/ash/arc/input_overlay/ui/action_view.h b/chrome/browser/ash/arc/input_overlay/ui/action_view.h
index 7d236c1..5f23958 100644
--- a/chrome/browser/ash/arc/input_overlay/ui/action_view.h
+++ b/chrome/browser/ash/arc/input_overlay/ui/action_view.h
@@ -12,7 +12,7 @@
 #include "chrome/browser/ash/arc/input_overlay/display_overlay_controller.h"
 #include "chrome/browser/ash/arc/input_overlay/ui/action_circle.h"
 #include "chrome/browser/ash/arc/input_overlay/ui/action_edit_button.h"
-#include "chrome/browser/ash/arc/input_overlay/ui/action_tag.h"
+#include "chrome/browser/ash/arc/input_overlay/ui/action_label.h"
 #include "ui/gfx/geometry/point.h"
 #include "ui/gfx/geometry/point_f.h"
 #include "ui/views/view.h"
@@ -24,6 +24,9 @@
 class DisplayOverlayController;
 class ActionEditButton;
 
+// Represents the default label index. Default -1 means all the index.
+constexpr int kDefaultLabelIndex = -1;
+
 // ActionView is the view for each action.
 class ActionView : public views::View {
  public:
@@ -37,7 +40,8 @@
   virtual void SetViewContent(BindingOption binding_option,
                               const gfx::RectF& content_bounds) = 0;
   // Each type of the actions acts differently on key binding change.
-  virtual void OnKeyBindingChange(ActionTag* action_tag, ui::DomCode code) = 0;
+  virtual void OnKeyBindingChange(ActionLabel* action_label,
+                                  ui::DomCode code) = 0;
   virtual void OnBindingToKeyboard() = 0;
   virtual void OnBindingToMouse(std::string mouse_action) = 0;
   // Each type of the actions shows different edit menu.
@@ -45,7 +49,10 @@
 
   // TODO(cuicuiruan): Remove virtual for post MVP once edit menu is ready for
   // |ActionMove|.
-  virtual void SetDisplayMode(const DisplayMode mode);
+  // If |editing_label| == nullptr, set display mode for all the |ActionLabel|
+  // child views, otherwise, only set the display mode for |editing_label|.
+  virtual void SetDisplayMode(const DisplayMode mode,
+                              ActionLabel* editing_label = nullptr);
 
   // Set position from its center position.
   void SetPositionFromCenterPosition(gfx::PointF& center_position);
@@ -53,18 +60,31 @@
   gfx::Point GetEditMenuPosition(gfx::Size menu_size);
   void RemoveEditMenu();
   // Show error message for action.
-  void ShowErrorMsg(base::StringPiece error_msg);
+  void ShowErrorMsg(base::StringPiece error_msg, ActionLabel* editing_label);
+  // Change binding for |action| binding to |input_element| and set
+  // |kEditedSuccess| on |action_label| if |action_label| is not nullptr.
+  // Otherwise, set |kEditedSuccess| to all |ActionLabel|.
+  void ChangeBinding(Action* action,
+                     ActionLabel* action_label,
+                     std::unique_ptr<InputElement> input_element);
   // Reset binding to its previous binding before entering to the edit mode.
   void OnResetBinding();
 
   Action* action() { return action_; }
+  const std::vector<ActionLabel*>& labels() const { return labels_; }
   void set_editable(bool editable) { editable_ = editable; }
   DisplayOverlayController* display_overlay_controller() {
     return display_overlay_controller_;
   }
+  void set_unbind_label_index(int label_index) {
+    unbind_label_index_ = label_index;
+  }
+  int unbind_label_index() { return unbind_label_index_; }
+  bool show_circle() const { return show_circle_; }
 
  protected:
-  bool ShouldShowErrorMsg(ui::DomCode code);
+  bool ShouldShowErrorMsg(ui::DomCode code,
+                          ActionLabel* editing_label = nullptr);
 
   // Reference to the action of this UI.
   raw_ptr<Action> action_ = nullptr;
@@ -77,7 +97,7 @@
   // The circle view shows up for editing the action.
   raw_ptr<ActionCircle> circle_ = nullptr;
   // Labels for mapping hints.
-  std::vector<ActionTag*> tags_;
+  std::vector<ActionLabel*> labels_;
   // Current display mode.
   DisplayMode current_display_mode_ = DisplayMode::kNone;
   // Center position of the circle view.
@@ -88,6 +108,14 @@
  private:
   void AddEditButton();
   void RemoveEditButton();
+
+  // By default, all the labels are unbound.
+  int unbind_label_index_ = kDefaultLabelIndex;
+
+  // TODO(cuicuiruan) As requested, we remove the action circle for edit mode
+  // for now. We will remove the circle permanently once the future design for
+  // MVP confirm that circle is not needed anymore.
+  bool show_circle_ = false;
 };
 
 }  // namespace input_overlay
diff --git a/chrome/browser/ash/arc/intent_helper/arc_settings_service.cc b/chrome/browser/ash/arc/intent_helper/arc_settings_service.cc
index 7aa42c9..ba0b609 100644
--- a/chrome/browser/ash/arc/intent_helper/arc_settings_service.cc
+++ b/chrome/browser/ash/arc/intent_helper/arc_settings_service.cc
@@ -35,11 +35,11 @@
 #include "chrome/browser/ui/app_list/arc/arc_app_utils.h"
 #include "chrome/browser/ui/zoom/chrome_zoom_level_prefs.h"
 #include "chrome/common/pref_names.h"
+#include "chromeos/ash/components/network/onc/network_onc_utils.h"
 #include "chromeos/network/network_handler.h"
 #include "chromeos/network/network_state.h"
 #include "chromeos/network/network_state_handler.h"
 #include "chromeos/network/network_state_handler_observer.h"
-#include "chromeos/network/onc/network_onc_utils.h"
 #include "chromeos/network/proxy/proxy_config_service_impl.h"
 #include "components/arc/common/intent_helper/arc_intent_helper_package.h"
 #include "components/arc/intent_helper/arc_intent_helper_bridge.h"
diff --git a/chrome/browser/ash/arc/session/arc_session_manager.cc b/chrome/browser/ash/arc/session/arc_session_manager.cc
index 6ef5aad..33731ec2 100644
--- a/chrome/browser/ash/arc/session/arc_session_manager.cc
+++ b/chrome/browser/ash/arc/session/arc_session_manager.cc
@@ -1808,30 +1808,14 @@
   // For ARCVM, generate <dest_path>/{combined.prop,fstab}. For ARC, generate
   // <dest_path>/{default,build,vendor_build}.prop.
   const bool is_arcvm = arc::IsArcVmEnabled();
-  bool add_native_bridge_64bit_support = false;
-  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
-          ash::switches::kArcEnableNativeBridge64BitSupportExperiment)) {
-    PrefService* local_pref_service = g_browser_process->local_state();
-    if (base::FeatureList::IsEnabled(
-            arc::kNativeBridge64BitSupportExperimentFeature)) {
-      // Note that we treat this experiment as a one-way off->on switch, across
-      // all users of the device, as the lifetime of ARC mini-container and user
-      // sessions are different in different scenarios, and removing the
-      // experiment after it has been in effect for a user's ARC instance can
-      // lead to unexpected, and unsupported, results.
-      local_pref_service->SetBoolean(
-          prefs::kNativeBridge64BitSupportExperimentEnabled, true);
-    }
-    add_native_bridge_64bit_support = local_pref_service->GetBoolean(
-        prefs::kNativeBridge64BitSupportExperimentEnabled);
-  }
 
   std::deque<JobDesc> jobs = {
       JobDesc{kArcPrepareHostGeneratedDirJobName,
               UpstartOperation::JOB_START,
               {std::string("IS_ARCVM=") + (is_arcvm ? "1" : "0"),
-               std::string("ADD_NATIVE_BRIDGE_64BIT_SUPPORT=") +
-                   (add_native_bridge_64bit_support ? "1" : "0")}},
+               // TODO(b/233107740) Remove this once crrev.com/c/3656121 has
+               // landed.
+               std::string("ADD_NATIVE_BRIDGE_64BIT_SUPPORT=0")}},
   };
   ConfigureUpstartJobs(std::move(jobs),
                        base::BindOnce(&ArcSessionManager::OnExpandPropertyFiles,
diff --git a/chrome/browser/ash/arc/tts/arc_tts_service_unittest.cc b/chrome/browser/ash/arc/tts/arc_tts_service_unittest.cc
index 396f490..1d94eed 100644
--- a/chrome/browser/ash/arc/tts/arc_tts_service_unittest.cc
+++ b/chrome/browser/ash/arc/tts/arc_tts_service_unittest.cc
@@ -64,6 +64,7 @@
   content::TtsEngineDelegate* GetTtsEngineDelegate() override {
     return nullptr;
   }
+  void RefreshVoices() override {}
   void SetRemoteTtsEngineDelegate(
       content::RemoteTtsEngineDelegate* delegate) override {}
   void SetTtsPlatform(content::TtsPlatform* tts_platform) override {}
diff --git a/chrome/browser/ash/crosapi/network_settings_service_ash.cc b/chrome/browser/ash/crosapi/network_settings_service_ash.cc
index 838283c..17a7df793 100644
--- a/chrome/browser/ash/crosapi/network_settings_service_ash.cc
+++ b/chrome/browser/ash/crosapi/network_settings_service_ash.cc
@@ -12,10 +12,10 @@
 #include "chrome/browser/ash/crosapi/network_settings_translation.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/profiles/profile_manager.h"
+#include "chromeos/ash/components/network/onc/network_onc_utils.h"
 #include "chromeos/network/network_handler.h"
 #include "chromeos/network/network_state.h"
 #include "chromeos/network/network_state_handler.h"
-#include "chromeos/network/onc/network_onc_utils.h"
 #include "chromeos/network/proxy/proxy_config_service_impl.h"
 #include "components/prefs/pref_change_registrar.h"
 #include "components/prefs/pref_registry_simple.h"
diff --git a/chrome/browser/ash/crostini/crostini_terminal.cc b/chrome/browser/ash/crostini/crostini_terminal.cc
index 4c67ae57..7967c006 100644
--- a/chrome/browser/ash/crostini/crostini_terminal.cc
+++ b/chrome/browser/ash/crostini/crostini_terminal.cc
@@ -523,11 +523,9 @@
   }
 
   for (const auto& container : containers) {
-    // Use label 'penguin' if we have only the default container, else use
-    // <vm_name>:<container_name>.
-    std::string label = kCrostiniDefaultContainerName;
-    if (containers.size() > 1 ||
-        container != crostini::ContainerId::GetDefault()) {
+    // Use <container_name> for termina, else <vm_name>:<container_name>.
+    std::string label = container.container_name;
+    if (container.vm_name != kCrostiniDefaultVmName) {
       label = base::StrCat({container.vm_name, ":", container.container_name});
     }
     apps::AddShortcutCommandItem(next_command_id++,
diff --git a/chrome/browser/ash/eche_app/eche_app_notification_controller.cc b/chrome/browser/ash/eche_app/eche_app_notification_controller.cc
index b99042f..8589f69 100644
--- a/chrome/browser/ash/eche_app/eche_app_notification_controller.cc
+++ b/chrome/browser/ash/eche_app/eche_app_notification_controller.cc
@@ -138,14 +138,14 @@
     const std::u16string& title) {
   message_center::RichNotificationData rich_notification_data;
   rich_notification_data.buttons.push_back(message_center::ButtonInfo(
-      l10n_util::GetStringUTF16(IDS_ECHE_APP_SCREEN_LOCK_SETTINGS_BUTTON)));
-  rich_notification_data.buttons.push_back(message_center::ButtonInfo(
       l10n_util::GetStringUTF16(IDS_ECHE_APP_SCREEN_LOCK_LEARN_MORE)));
+  rich_notification_data.buttons.push_back(message_center::ButtonInfo(
+      l10n_util::GetStringUTF16(IDS_ECHE_APP_SCREEN_LOCK_SETTINGS_BUTTON)));
   ShowNotification(CreateNotification(
       kEcheAppScreenLockNotifierId, NotificationCatalogName::kEcheAppScreenLock,
-      l10n_util::GetStringUTF16(IDS_ECHE_APP_SCREEN_LOCK_NOTIFICATION_TITLE),
-      l10n_util::GetStringFUTF16(IDS_ECHE_APP_SCREEN_LOCK_NOTIFICATION_MESSAGE,
+      l10n_util::GetStringFUTF16(IDS_ECHE_APP_SCREEN_LOCK_NOTIFICATION_TITLE,
                                  title),
+      l10n_util::GetStringUTF16(IDS_ECHE_APP_SCREEN_LOCK_NOTIFICATION_MESSAGE),
       ui::ImageModel(), rich_notification_data,
       new NotificationDelegate(kEcheAppScreenLockNotifierId,
                                weak_ptr_factory_.GetWeakPtr())));
@@ -191,10 +191,10 @@
 
   if (notification_id_ == kEcheAppScreenLockNotifierId) {
     if (*button_index == 0) {
-      notification_controller_->LaunchSettings();
+      notification_controller_->LaunchLearnMore();
     } else {
       DCHECK_EQ(1, *button_index);
-      notification_controller_->LaunchLearnMore();
+      notification_controller_->LaunchSettings();
     }
   } else if (notification_id_ == kEcheAppRetryConnectionNotifierId) {
     if (*button_index == 0) {
diff --git a/chrome/browser/ash/eche_app/eche_app_notification_controller_unittest.cc b/chrome/browser/ash/eche_app/eche_app_notification_controller_unittest.cc
index bce1e55..ee97449 100644
--- a/chrome/browser/ash/eche_app/eche_app_notification_controller_unittest.cc
+++ b/chrome/browser/ash/eche_app/eche_app_notification_controller_unittest.cc
@@ -172,14 +172,14 @@
   ASSERT_EQ(2u, notification->buttons().size());
   EXPECT_EQ(message_center::SYSTEM_PRIORITY, notification->priority());
 
-  // Clicking the first notification button should launch settings.
-  EXPECT_CALL(*notification_controller_, LaunchSettings());
-  notification->delegate()->Click(0, absl::nullopt);
-
-  // Clicking the second notification button should launch learn more.
+  // Clicking the first notification button should launch learn more.
   EXPECT_CALL(*new_window_delegate_,
               OpenUrl(GURL(kEcheAppLearnMoreUrl),
                       NewWindowDelegate::OpenUrlFrom::kUserInteraction));
+  notification->delegate()->Click(0, absl::nullopt);
+
+  // Clicking the second notification button should launch settings.
+  EXPECT_CALL(*notification_controller_, LaunchSettings());
   notification->delegate()->Click(1, absl::nullopt);
 }
 
@@ -196,14 +196,14 @@
   ASSERT_EQ(2u, notification->buttons().size());
   EXPECT_EQ(message_center::SYSTEM_PRIORITY, notification->priority());
 
-  // Clicking the first notification button should launch settings.
-  EXPECT_CALL(*notification_controller_, LaunchSettings());
-  notification->delegate()->Click(0, absl::nullopt);
-
   // Clicking the second notification button should launch learn more.
   EXPECT_CALL(*new_window_delegate_,
               OpenUrl(GURL(kEcheAppLearnMoreUrl),
                       NewWindowDelegate::OpenUrlFrom::kUserInteraction));
+  notification->delegate()->Click(0, absl::nullopt);
+
+  // Clicking the first notification button should launch settings.
+  EXPECT_CALL(*notification_controller_, LaunchSettings());
   notification->delegate()->Click(1, absl::nullopt);
 }
 
diff --git a/chrome/browser/ash/file_manager/trash_io_task.cc b/chrome/browser/ash/file_manager/trash_io_task.cc
index 9a10b15..61596f8d 100644
--- a/chrome/browser/ash/file_manager/trash_io_task.cc
+++ b/chrome/browser/ash/file_manager/trash_io_task.cc
@@ -11,6 +11,7 @@
 #include "base/task/bind_post_task.h"
 #include "base/task/thread_pool.h"
 #include "base/time/time_to_iso8601.h"
+#include "chrome/browser/ash/drive/drive_integration_service.h"
 #include "chrome/browser/ash/file_manager/fileapi_util.h"
 #include "chrome/browser/ash/file_manager/io_task_util.h"
 #include "chrome/browser/ash/file_manager/path_util.h"
@@ -22,6 +23,7 @@
 namespace io_task {
 
 constexpr char kTrashFolderName[] = ".Trash";
+constexpr char kTrashUIDFolderName[] = ".Trash-1000";
 constexpr char kInfoFolderName[] = "info";
 constexpr char kFilesFolderName[] = "files";
 
@@ -145,6 +147,24 @@
   progress_callback_ = std::move(progress_callback);
   complete_callback_ = std::move(complete_callback);
 
+  // Build the list of known paths that are enabled, for now Downloads is a bind
+  // mount at MyFiles/Downloads so treat them as separate volumes.
+  // TODO(b/233976434): Consider storing this data in the `DirectoryInfo` struct
+  // instead of separately.
+  enabled_trash_paths_.insert(
+      std::pair{util::GetMyFilesFolderForProfile(profile_), kTrashFolderName});
+  enabled_trash_paths_.insert(std::pair{
+      util::GetDownloadsFolderForProfile(profile_), kTrashFolderName});
+
+  auto* integration_service =
+      drive::DriveIntegrationServiceFactory::FindForProfile(profile_);
+  if (integration_service) {
+    enabled_trash_paths_.insert(std::pair{
+        integration_service->GetMountPointPath(), kTrashUIDFolderName});
+  }
+
+  // TODO(b/231830443): Add in support for crostini.
+
   progress_.state = State::kInProgress;
 
   UpdateTrashEntry(0);
@@ -165,17 +185,14 @@
   if (!base_path_.empty() && !source_path.IsAbsolute()) {
     source_path = base_path_.Append(source_path);
   }
-  const base::FilePath my_files_path =
-      util::GetMyFilesFolderForProfile(profile_);
-  const base::FilePath downloads_path =
-      util::GetDownloadsFolderForProfile(profile_);
 
-  base::FilePath trash_parent_path;
-  if (downloads_path.IsParent(source_path)) {
-    trash_parent_path = downloads_path;
-  } else if (my_files_path.IsParent(source_path)) {
-    trash_parent_path = my_files_path;
-  } else {
+  const auto& trash_parent_path_it =
+      std::find_if(enabled_trash_paths_.rbegin(), enabled_trash_paths_.rend(),
+                   [&source_path](const auto& it) -> bool {
+                     return it.first.IsParent(source_path);
+                   });
+
+  if (trash_parent_path_it == enabled_trash_paths_.rend()) {
     // The `source_path` is not parented at a supported Trash location, bail
     // out completely.
     // TODO(b/231830211): This may be better handled more gracefully by
@@ -186,8 +203,11 @@
     return;
   }
 
+  const base::FilePath trash_parent_path = trash_parent_path_it->first;
+  const std::string folder_name = trash_parent_path_it->second;
+
   TrashEntry& entry = trash_entries_[source_idx];
-  entry.trash_path = trash_parent_path.Append(kTrashFolderName);
+  entry.trash_path = trash_parent_path.Append(folder_name);
 
   if (!UpdateTrashInfoContents(source_path, trash_parent_path, entry)) {
     // If we can't update the trash entry, update the source error and finish
@@ -200,7 +220,7 @@
 
   auto it = free_space_map_.find(trash_parent_path);
   if (it == free_space_map_.end()) {
-    GetFreeDiskSpace(source_idx, trash_parent_path);
+    GetFreeDiskSpace(source_idx, trash_parent_path, folder_name);
     return;
   }
 
@@ -268,13 +288,14 @@
 }
 
 void TrashIOTask::GetFreeDiskSpace(size_t source_idx,
-                                   const base::FilePath& trash_parent_path) {
+                                   const base::FilePath& trash_parent_path,
+                                   const std::string& folder_name) {
   base::ThreadPool::PostTaskAndReplyWithResult(
       FROM_HERE, {base::MayBlock()},
       base::BindOnce(&base::SysInfo::AmountOfFreeDiskSpace, trash_parent_path),
       base::BindOnce(&TrashIOTask::GotFreeDiskSpace,
                      weak_ptr_factory_.GetWeakPtr(), source_idx,
-                     trash_parent_path));
+                     trash_parent_path, folder_name));
 }
 
 base::FilePath TrashIOTask::MakeRelativeFromBasePath(
@@ -293,9 +314,10 @@
 
 void TrashIOTask::GotFreeDiskSpace(size_t source_idx,
                                    const base::FilePath& trash_parent_path,
+                                   const std::string& folder_name,
                                    int64_t free_space) {
   base::FilePath trash_path =
-      MakeRelativeFromBasePath(trash_parent_path.Append(kTrashFolderName));
+      MakeRelativeFromBasePath(trash_parent_path.Append(folder_name));
   const storage::FileSystemURL files_url = CreateFileSystemURL(
       progress_.sources[source_idx].url, trash_path.Append(kFilesFolderName));
   const storage::FileSystemURL info_url = CreateFileSystemURL(
diff --git a/chrome/browser/ash/file_manager/trash_io_task.h b/chrome/browser/ash/file_manager/trash_io_task.h
index 1ec8a1b..cee66ef 100644
--- a/chrome/browser/ash/file_manager/trash_io_task.h
+++ b/chrome/browser/ash/file_manager/trash_io_task.h
@@ -74,6 +74,9 @@
 // Constant representing the Trash folder name.
 extern const char kTrashFolderName[];
 
+// Constant representing the Trash folder name with the chronos UID suffix.
+extern const char kTrashUIDFolderName[];
+
 // Constant representing the "info" folder name inside .Trash.
 extern const char kInfoFolderName[];
 
@@ -126,10 +129,16 @@
   using FreeSpaceMap = std::map<base::FilePath, DirectoryInfo>;
   void ValidateAndDecrementFreeSpace(size_t source_idx,
                                      FreeSpaceMap::iterator& it);
+  // Get the free disk space for `trash_parent_path` to know whether the
+  // metadata can be written. The `folder_name` is used to differentiate between
+  // .Trash and .Trash-1000 folder names on various file systems (both are valid
+  // in the XDG spec).
   void GetFreeDiskSpace(size_t source_idx,
-                        const base::FilePath& trash_parent_path);
+                        const base::FilePath& trash_parent_path,
+                        const std::string& folder_name);
   void GotFreeDiskSpace(size_t source_idx,
                         const base::FilePath& trash_parent_path,
+                        const std::string& folder_name,
                         int64_t free_space);
 
   // Sets up the .Trash/files and .Trash/info subdirectories specified by the
@@ -198,6 +207,9 @@
   // finish the operation.
   Speedometer speedometer_;
 
+  // Any paths which are descendants from this list are enabled for trash.
+  std::map<const base::FilePath, const std::string> enabled_trash_paths_;
+
   // Stores the id of the operations currently behind undertaken by Trash,
   // including directory creation. Enables cancelling an inflight operation.
   absl::optional<storage::FileSystemOperationRunner::OperationID> operation_id_;
diff --git a/chrome/browser/ash/file_manager/trash_io_task_unittest.cc b/chrome/browser/ash/file_manager/trash_io_task_unittest.cc
index 7b7febb..d988302 100644
--- a/chrome/browser/ash/file_manager/trash_io_task_unittest.cc
+++ b/chrome/browser/ash/file_manager/trash_io_task_unittest.cc
@@ -14,9 +14,16 @@
 #include "base/test/mock_callback.h"
 #include "base/time/time_override.h"
 #include "base/time/time_to_iso8601.h"
-#include "chrome/browser/ash/file_manager/fake_disk_mount_manager.h"
+#include "chrome/browser/ash/drive/drive_integration_service.h"
+#include "chrome/browser/ash/drive/drivefs_test_support.h"
 #include "chrome/browser/ash/file_manager/path_util.h"
+#include "chrome/browser/ash/login/users/fake_chrome_user_manager.h"
 #include "chrome/test/base/testing_profile.h"
+#include "chromeos/dbus/dbus_thread_manager.h"
+#include "components/account_id/account_id.h"
+#include "components/drive/drive_pref_names.h"
+#include "components/prefs/pref_service.h"
+#include "components/user_manager/scoped_user_manager.h"
 #include "content/public/test/browser_task_environment.h"
 #include "storage/browser/file_system/external_mount_points.h"
 #include "storage/browser/test/test_file_system_context.h"
@@ -65,29 +72,57 @@
  protected:
   void SetUp() override {
     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
-    file_system_context_ = storage::CreateFileSystemContextForTesting(
-        nullptr, temp_dir_.GetPath());
+
+    // Pass in a mock factory method that sets up a fake
+    // DriveIntegrationService. This ensures the enabled paths contain the drive
+    // path.
+    create_drive_integration_service_ =
+        base::BindRepeating(&TrashIOTaskTest::CreateDriveIntegrationService,
+                            base::Unretained(this));
+    service_factory_for_test_ = std::make_unique<
+        drive::DriveIntegrationServiceFactory::ScopedFactoryForTest>(
+        &create_drive_integration_service_);
+
+    // Create the profile and add it to the user manager for DriveFS.
     profile_ =
         std::make_unique<TestingProfile>(base::FilePath(temp_dir_.GetPath()));
+    auto user_manager = std::make_unique<ash::FakeChromeUserManager>();
+    AccountId account_id =
+        AccountId::FromUserEmailGaiaId(profile_->GetProfileUserName(), "12345");
+    user_manager->AddUser(account_id);
+    user_manager->LoginUser(account_id);
+    scoped_user_manager_ = std::make_unique<user_manager::ScopedUserManager>(
+        std::move(user_manager));
 
-    // Create Downloads and MyFiles inside the `temp_dir_`.
+    file_system_context_ = storage::CreateFileSystemContextForTesting(
+        nullptr, temp_dir_.GetPath());
     my_files_dir_ = temp_dir_.GetPath().Append("MyFiles");
-    downloads_dir_ = my_files_dir_.Append(
-        file_manager::util::GetDownloadsMountPointName(profile_.get()));
-    ASSERT_TRUE(base::CreateDirectory(downloads_dir_));
-    ASSERT_TRUE(base::CreateDirectory(my_files_dir_));
-
-    // Register `my_files_dir_` as the parent directory for Downloads.
     storage::ExternalMountPoints::GetSystemInstance()->RegisterFileSystem(
         file_manager::util::GetDownloadsMountPointName(profile_.get()),
         storage::kFileSystemTypeLocal, storage::FileSystemMountOption(),
         my_files_dir_);
+
+    // Create Downloads inside the `temp_dir_` which will implicitly create the
+    // `my_files_dir_.
+    downloads_dir_ = my_files_dir_.Append("Downloads");
+    ASSERT_TRUE(base::CreateDirectory(downloads_dir_));
   }
 
   void TearDown() override {
     // Ensure any previously registered mount points for Downloads are revoked.
-    storage::ExternalMountPoints::GetSystemInstance()->RevokeFileSystem(
-        file_manager::util::GetDownloadsMountPointName(profile_.get()));
+    storage::ExternalMountPoints::GetSystemInstance()->RevokeAllFileSystems();
+  }
+
+  drive::DriveIntegrationService* CreateDriveIntegrationService(
+      Profile* profile) {
+    base::ScopedAllowBlockingForTesting allow_blocking;
+    base::FilePath mount_point = temp_dir_.GetPath().Append("drivefs");
+    fake_drivefs_helper_ =
+        std::make_unique<drive::FakeDriveFsHelper>(profile, mount_point);
+    integration_service_ = new drive::DriveIntegrationService(
+        profile, "", mount_point,
+        fake_drivefs_helper_->CreateFakeDriveFsListenerFactory());
+    return integration_service_;
   }
 
   storage::FileSystemURL CreateFileSystemURL(
@@ -138,9 +173,20 @@
   const blink::StorageKey kTestStorageKey =
       blink::StorageKey::CreateFromStringForTesting("chrome-extension://abc");
 
+  // DriveFS setup methods to ensure the tests have access to a mock
+  // DriveIntegrationService tied to the TestingProfile.
+  std::unique_ptr<user_manager::ScopedUserManager> scoped_user_manager_;
+  std::unique_ptr<drive::FakeDriveFsHelper> fake_drivefs_helper_;
+  drive::DriveIntegrationService* integration_service_ = nullptr;
+  drive::DriveIntegrationServiceFactory::FactoryCallback
+      create_drive_integration_service_;
+  std::unique_ptr<drive::DriveIntegrationServiceFactory::ScopedFactoryForTest>
+      service_factory_for_test_;
+
   base::ScopedTempDir temp_dir_;
   base::FilePath downloads_dir_;
   base::FilePath my_files_dir_;
+  base::FilePath drive_dir_;
   scoped_refptr<storage::FileSystemContext> file_system_context_;
 };
 
@@ -221,6 +267,11 @@
 }
 
 TEST_F(TrashIOTaskTest, SupportedDirectoryShouldSucceed) {
+  // Force the drive integration service to be created, this ensures the code
+  // path that adds the drive mount point is exercised.
+  ash::DBusThreadManager::Initialize();
+  drive::DriveIntegrationServiceFactory::GetForProfile(profile_.get());
+
   std::string foo_contents = base::RandBytesAsString(kTestFileSize);
   const base::FilePath file_path = downloads_dir_.Append("foo.txt");
   ASSERT_TRUE(base::WriteFile(file_path, foo_contents));
@@ -233,8 +284,8 @@
   base::MockRepeatingCallback<void(const ProgressStatus&)> progress_callback;
   base::MockOnceCallback<void(ProgressStatus)> complete_callback;
 
-  // Progress callback should not be called as the verification of disk space
-  // should fail before any file operations occur.
+  // Progress callback is only invoked when there is multiple files being
+  // trashed.
   EXPECT_CALL(progress_callback, Run(_)).Times(0);
 
   EXPECT_CALL(complete_callback,
diff --git a/chrome/browser/ash/policy/core/browser_policy_connector_ash.cc b/chrome/browser/ash/policy/core/browser_policy_connector_ash.cc
index 9099b2a..699f0eb 100644
--- a/chrome/browser/ash/policy/core/browser_policy_connector_ash.cc
+++ b/chrome/browser/ash/policy/core/browser_policy_connector_ash.cc
@@ -73,11 +73,11 @@
 #include "chrome/common/chrome_features.h"
 #include "chrome/common/pref_names.h"
 #include "chromeos/ash/components/dbus/upstart/upstart_client.h"
+#include "chromeos/ash/components/network/onc/onc_certificate_importer_impl.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/dbus/session_manager/session_manager_client.h"
 #include "chromeos/network/network_cert_loader.h"
 #include "chromeos/network/network_handler.h"
-#include "chromeos/network/onc/onc_certificate_importer_impl.h"
 #include "chromeos/system/statistics_provider.h"
 #include "components/policy/core/common/cloud/cloud_policy_client.h"
 #include "components/policy/core/common/cloud/cloud_policy_refresh_scheduler.h"
diff --git a/chrome/browser/ash/policy/networking/policy_certs_browsertest.cc b/chrome/browser/ash/policy/networking/policy_certs_browsertest.cc
index 362d3cd..ef24187 100644
--- a/chrome/browser/ash/policy/networking/policy_certs_browsertest.cc
+++ b/chrome/browser/ash/policy/networking/policy_certs_browsertest.cc
@@ -48,10 +48,10 @@
 #include "chrome/browser/ui/webui/chromeos/login/signin_screen_handler.h"
 #include "chrome/common/chrome_paths.h"
 #include "chrome/test/base/in_process_browser_test.h"
+#include "chromeos/ash/components/network/onc/onc_certificate_importer.h"
+#include "chromeos/ash/components/network/onc/onc_certificate_importer_impl.h"
 #include "chromeos/components/onc/onc_test_utils.h"
 #include "chromeos/network/network_cert_loader.h"
-#include "chromeos/network/onc/onc_certificate_importer.h"
-#include "chromeos/network/onc/onc_certificate_importer_impl.h"
 #include "chromeos/network/policy_certificate_provider.h"
 #include "chromeos/test/chromeos_test_utils.h"
 #include "components/onc/onc_constants.h"
diff --git a/chrome/browser/browser_resources.grd b/chrome/browser/browser_resources.grd
index e79aacf..1bf2c65 100644
--- a/chrome/browser/browser_resources.grd
+++ b/chrome/browser/browser_resources.grd
@@ -232,10 +232,14 @@
       </if>
       <if expr="chromeos_ash and _google_chrome">
         <include name="IDR_HELP_MANIFEST" file="resources\help_app\manifest.json" type="BINDATA" />
-        <include name="IDR_QUICKOFFICE_MANIFEST" file="resources\chromeos\quickoffice\manifest.json" type="BINDATA" />
         <include name="IDR_PRODUCT_CHROMEOS_SYNC_CONSENT_SCREEN_ICONS" file="internal\resources\chromeos-sync-consent-icons.html" type="BINDATA" />
         <include name="IDR_PRODUCT_CHROMEOS_SYNC_CONSENT_SCREEN_ICONS_M_JS" file="internal\resources\chromeos-sync-consent-icons.m.js" type="BINDATA" />
       </if>
+      <if expr="chromeos_ash or chromeos_lacros">
+        <if expr="_google_chrome">
+          <include name="IDR_QUICKOFFICE_MANIFEST" file="resources\chromeos\quickoffice\manifest.json" type="BINDATA" />
+        </if>
+      </if>
       <if expr="_google_chrome">
         <include name="IDR_PREF_HASH_SEED_BIN" file="resources\settings_internal\pref_hash_seed.bin" type="BINDATA" />
         <include name="IDR_ADDITIONAL_MODULE_IDS" file="${additional_modules_list_file}" use_base_dir="false" type="BINDATA" />
diff --git a/chrome/browser/chrome_browser_main.cc b/chrome/browser/chrome_browser_main.cc
index 118f2bc..68042f64 100644
--- a/chrome/browser/chrome_browser_main.cc
+++ b/chrome/browser/chrome_browser_main.cc
@@ -215,6 +215,11 @@
 #include "chrome/browser/first_run/upgrade_util.h"
 #endif  // !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_CHROMEOS_ASH)
 
+#if BUILDFLAG(IS_CHROMEOS)
+#include "base/process/process.h"
+#include "base/task/task_traits.h"
+#endif  // BUILDFLAG(IS_CHROMEOS)
+
 #if !BUILDFLAG(IS_CHROMEOS_ASH)
 #include "components/enterprise/browser/controller/chrome_browser_cloud_management_controller.h"
 #endif  // !BUILDFLAG(IS_CHROMEOS_ASH)
@@ -1840,6 +1845,16 @@
   sharing::ShareHistory::CreateForProfile(
       ProfileManager::GetPrimaryUserProfile());
 #endif
+
+#if BUILDFLAG(IS_CHROMEOS)
+  // If OneGroupPerRenderer feature is enabled, post a task to clean any left
+  // over cgroups due to any unclean exits.
+  if (base::Process::OneGroupPerRendererEnabled()) {
+    base::ThreadPool::PostTask(
+        FROM_HERE, {base::MayBlock(), base::TaskPriority::BEST_EFFORT},
+        base::BindOnce(&base::Process::CleanUpStaleProcessStates));
+  }
+#endif  // BUILDFLAG(IS_CHROMEOS)
 }
 
 void ChromeBrowserMainParts::PostMainMessageLoopRun() {
diff --git a/chrome/browser/extensions/BUILD.gn b/chrome/browser/extensions/BUILD.gn
index 917a61f1..10b2f06 100644
--- a/chrome/browser/extensions/BUILD.gn
+++ b/chrome/browser/extensions/BUILD.gn
@@ -1003,6 +1003,7 @@
     deps += [
       "//chrome/browser/chromeos/extensions:constants",
       "//chrome/browser/chromeos/extensions/login_screen",
+      "//chromeos/constants",
       "//chromeos/components/disks:prefs",
       "//chromeos/components/quick_answers/public/cpp:prefs",
       "//chromeos/crosapi/cpp",
diff --git a/chrome/browser/extensions/api/networking_private/networking_private_chromeos_apitest.cc b/chrome/browser/extensions/api/networking_private/networking_private_chromeos_apitest.cc
index 6878f1d5..44907c96 100644
--- a/chrome/browser/extensions/api/networking_private/networking_private_chromeos_apitest.cc
+++ b/chrome/browser/extensions/api/networking_private/networking_private_chromeos_apitest.cc
@@ -20,6 +20,7 @@
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/extensions/api/networking_private/networking_private_ui_delegate_chromeos.h"
 #include "chrome/browser/extensions/extension_apitest.h"
+#include "chromeos/ash/components/network/onc/network_onc_utils.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/dbus/shill/shill_device_client.h"
 #include "chromeos/dbus/shill/shill_ipconfig_client.h"
@@ -36,7 +37,6 @@
 #include "chromeos/network/network_state.h"
 #include "chromeos/network/network_state_handler.h"
 #include "chromeos/network/network_type_pattern.h"
-#include "chromeos/network/onc/network_onc_utils.h"
 #include "components/onc/onc_constants.h"
 #include "components/onc/onc_pref_names.h"
 #include "components/policy/core/browser/browser_policy_connector.h"
diff --git a/chrome/browser/extensions/component_extensions_allowlist/allowlist.cc b/chrome/browser/extensions/component_extensions_allowlist/allowlist.cc
index 7cbc426..60ab74d 100644
--- a/chrome/browser/extensions/component_extensions_allowlist/allowlist.cc
+++ b/chrome/browser/extensions/component_extensions_allowlist/allowlist.cc
@@ -86,14 +86,16 @@
     case IDR_WALLPAPERMANAGER_MANIFEST:
 #if BUILDFLAG(GOOGLE_CHROME_BRANDING)
     case IDR_HELP_MANIFEST:
-    case IDR_QUICKOFFICE_MANIFEST:
 #endif  // BUILDFLAG(GOOGLE_CHROME_BRANDING)
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
 #if BUILDFLAG(IS_CHROMEOS)
     case IDR_CONTACT_CENTER_INSIGHTS_MANIFEST:
     case IDR_ECHO_MANIFEST:
-#endif
+#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
+    case IDR_QUICKOFFICE_MANIFEST:
+#endif  // BUILDFLAG(GOOGLE_CHROME_BRANDING)
+#endif  // BUILDFLAG(IS_CHROMEOS)
       return true;
   }
 
diff --git a/chrome/browser/extensions/component_loader.cc b/chrome/browser/extensions/component_loader.cc
index cdfc1d1e..a2b613f 100644
--- a/chrome/browser/extensions/component_loader.cc
+++ b/chrome/browser/extensions/component_loader.cc
@@ -70,6 +70,10 @@
 #include "ui/file_manager/grit/file_manager_resources.h"
 #endif
 
+#if BUILDFLAG(IS_CHROMEOS)
+#include "chromeos/constants/chromeos_features.h"
+#endif
+
 #if BUILDFLAG(ENABLE_PDF)
 #include "chrome/browser/pdf/pdf_extension_util.h"
 #endif
@@ -507,7 +511,15 @@
 #if BUILDFLAG(IS_CHROMEOS)
     Add(IDR_ECHO_MANIFEST,
         base::FilePath(FILE_PATH_LITERAL("/usr/share/chromeos-assets/echo")));
-#endif
+#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
+    if (!base::FeatureList::IsEnabled(
+            chromeos::features::kDisableOfficeEditingComponentApp)) {
+      Add(IDR_QUICKOFFICE_MANIFEST,
+          base::FilePath(
+              FILE_PATH_LITERAL("/usr/share/chromeos-assets/quickoffice")));
+    }
+#endif  // BUILDFLAG(GOOGLE_CHROME_BRANDING)
+#endif  // BUILDFLAG(IS_CHROMEOS)
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
     if (command_line->HasSwitch(switches::kLoadGuestModeTestExtension)) {
@@ -520,13 +532,6 @@
     AddImageLoaderExtension();
 
 #if BUILDFLAG(GOOGLE_CHROME_BRANDING)
-    if (!base::FeatureList::IsEnabled(
-            chromeos::features::kDisableOfficeEditingComponentApp)) {
-      Add(IDR_QUICKOFFICE_MANIFEST,
-          base::FilePath(
-              FILE_PATH_LITERAL("/usr/share/chromeos-assets/quickoffice")));
-    }
-
     // TODO(https://crbug.com/1005083): Force the off the record profile to be
     // created to allow the virtual keyboard to work in guest mode.
     if (!IsNormalSession())
diff --git a/chrome/browser/feature_guide/notifications/internal/feature_notification_guide_service_impl_unittest.cc b/chrome/browser/feature_guide/notifications/internal/feature_notification_guide_service_impl_unittest.cc
index 1eae77a7..23102f9 100644
--- a/chrome/browser/feature_guide/notifications/internal/feature_notification_guide_service_impl_unittest.cc
+++ b/chrome/browser/feature_guide/notifications/internal/feature_notification_guide_service_impl_unittest.cc
@@ -20,6 +20,7 @@
 #include "components/optimization_guide/proto/models.pb.h"
 #include "components/segmentation_platform/public/segment_selection_result.h"
 #include "components/segmentation_platform/public/segmentation_platform_service.h"
+#include "components/segmentation_platform/public/trigger_context.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -109,6 +110,17 @@
         OPTIMIZATION_TARGET_SEGMENTATION_CHROME_LOW_USER_ENGAGEMENT;
     return result;
   }
+  int RegisterOnDemandSegmentSelectionCallback(
+      const std::string& segmentation_key,
+      const OnDemandSegmentSelectionCallback& callback) override {
+    return 0;
+  }
+  void UnregisterOnDemandSegmentSelectionCallback(
+      int callback_id,
+      const std::string& segmentation_key) override {}
+  void OnTrigger(
+      segmentation_platform::TriggerType trigger,
+      const segmentation_platform::TriggerContext& trigger_context) override {}
   void EnableMetrics(bool signal_collection_allowed) override {}
   segmentation_platform::ServiceProxy* GetServiceProxy() override {
     return nullptr;
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index 079aecbc..bc25d4e 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -64,6 +64,11 @@
     "expiry_milestone": 115
   },
   {
+    "name": "adaptive-charging-for-testing",
+    "owners": [ "thanhdng" ],
+    "expiry_milestone": 115
+  },
+  {
     "name": "add-passwords-in-settings",
     "owners": [ "vidhanj", "mamir", "lizapopova" ],
     "expiry_milestone": 103
@@ -221,13 +226,6 @@
     "expiry_milestone": 108
   },
   {
-    "name": "arc-native-bridge-64bit-support-experiment",
-    "owners": [ "jhorwich" ],
-    // Used on ChromeOS to experimentally enable 64-bit ARC native-bridge
-    // support before promoting it to enabled on a specific device class.
-    "expiry_milestone": 93
-  },
-  {
     "name": "arc-native-bridge-toggle",
     "owners": [ "levarum@google.com" ],
     // Used on ChromeOS to compare and debug different ARC native-bridge
@@ -4650,6 +4648,11 @@
     "expiry_milestone": 105
   },
   {
+    "name": "one-group-per-renderer",
+    "owners": ["youssefesmat", "baseos-perf@google.com"],
+    "expiry_milestone": 120
+  },
+  {
     "name": "oobe-hid-detection-revamp",
     "owners": [ "gordonseto", "cros-connectivity@google.com"],
     "expiry_milestone": 105
@@ -5757,7 +5760,7 @@
   {
     "name": "tab-search-fuzzy-search",
     "owners": [ "yuhengh", "chrome-cros@google.com" ],
-    "expiry_milestone": 101
+    "expiry_milestone": 110
   },
   {
     "name": "tab-search-media-tabs",
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 3d174a9..e52c5bbd 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -4113,6 +4113,15 @@
     "Enable hardware-accelerated mjpeg decode for captured frame where "
     "available.";
 
+const char kAdaptiveChargingForTestingName[] =
+    "Show adaptive charging notifications for testing";
+const char kAdaptiveChargingForTestingDescription[] =
+    "Show adaptive charging notifications and nudges for testing. This is "
+    "meant to be used by developers to test the feature UI only. The "
+    "notifications will be shown after the device is plugged in to the "
+    "charger. Please DO NOT enable this if you're not a developer who wants to "
+    "test the UI of the adaptive charging feature.";
+
 const char kAdaptiveChargingName[] = "Enable adaptive charging feature";
 const char kAdaptiveChargingDescription[] =
     "Show settings to enable/disable adaptive charging feature.";
@@ -4204,11 +4213,6 @@
 const char kArcNativeBridgeToggleDescription[] =
     "Toggle between native bridge implementations for ARC.";
 
-const char kArcNativeBridge64BitSupportExperimentName[] =
-    "Enable experimental 64-bit native bridge support for ARC";
-const char kArcNativeBridge64BitSupportExperimentDescription[] =
-    "Enable experimental 64-bit native bridge support for ARC where available.";
-
 const char kArcRightClickLongPressName[] =
     "Enable ARC right click long press compatibility feature.";
 const char kArcRightClickLongPressDescription[] =
@@ -4585,14 +4589,6 @@
 const char kUseHDRTransferFunctionDescription[] =
     "Allows using the HDR transfer functions of any connected monitor that "
     "supports it";
-
-const char kDisableOfficeEditingComponentAppName[] =
-    "Disable Office Editing for Docs, Sheets & Slides";
-const char kDisableOfficeEditingComponentAppDescription[] =
-    "Disables Office Editing for Docs, Sheets & Slides component app so "
-    "handlers won't be registered, making it possible to install another "
-    "version for testing.";
-
 const char kDoubleTapToZoomInTabletModeName[] =
     "Double-tap to zoom in tablet mode";
 const char kDoubleTapToZoomInTabletModeDescription[] =
@@ -5683,6 +5679,13 @@
     "Deprecates low usage codecs. Disable this feature to allow playback of "
     "AMR and GSM.";
 
+const char kDisableOfficeEditingComponentAppName[] =
+    "Disable Office Editing for Docs, Sheets & Slides";
+const char kDisableOfficeEditingComponentAppDescription[] =
+    "Disables Office Editing for Docs, Sheets & Slides component app so "
+    "handlers won't be registered, making it possible to install another "
+    "version for testing.";
+
 const char kVaapiAV1DecoderName[] = "VA-API decode acceleration for AV1";
 const char kVaapiAV1DecoderDescription[] =
     "Enable or disable decode acceleration of AV1 videos using the VA-API.";
@@ -5720,6 +5723,11 @@
 const char kMessagesPreinstallDescription[] =
     "Enables preinstallation of the Messages for Web PWA for unmanaged users.";
 
+const char kOneGroupPerRendererName[] =
+    "Use one cgroup for each foreground renderer";
+const char kOneGroupPerRendererDescription[] =
+    "Places each Chrome foreground renderer into its own cgroup";
+
 const char kSyncChromeOSExplicitPassphraseSharingName[] =
     "Sync passphrase sharing";
 const char kSyncChromeOSExplicitPassphraseSharingDescription[] =
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 38238535..ba72d16 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -2356,6 +2356,9 @@
 extern const char kAdaptiveChargingName[];
 extern const char kAdaptiveChargingDescription[];
 
+extern const char kAdaptiveChargingForTestingName[];
+extern const char kAdaptiveChargingForTestingDescription[];
+
 extern const char kAllowDisableTouchpadHapticFeedbackName[];
 extern const char kAllowDisableTouchpadHapticFeedbackDescription[];
 
@@ -2407,9 +2410,6 @@
 extern const char kArcNativeBridgeToggleName[];
 extern const char kArcNativeBridgeToggleDescription[];
 
-extern const char kArcNativeBridge64BitSupportExperimentName[];
-extern const char kArcNativeBridge64BitSupportExperimentDescription[];
-
 extern const char kArcRightClickLongPressName[];
 extern const char kArcRightClickLongPressDescription[];
 
@@ -2616,10 +2616,6 @@
 
 extern const char kUseHDRTransferFunctionName[];
 extern const char kUseHDRTransferFunctionDescription[];
-
-extern const char kDisableOfficeEditingComponentAppName[];
-extern const char kDisableOfficeEditingComponentAppDescription[];
-
 extern const char kDoubleTapToZoomInTabletModeName[];
 extern const char kDoubleTapToZoomInTabletModeDescription[];
 
@@ -3253,12 +3249,13 @@
 
 extern const char kDefaultCalculatorWebAppName[];
 extern const char kDefaultCalculatorWebAppDescription[];
-#endif  // BUILDFLAG(IS_CHROMEOS)
 
-#if BUILDFLAG(IS_CHROMEOS)
 extern const char kDeprecateLowUsageCodecsName[];
 extern const char kDeprecateLowUsageCodecsDescription[];
 
+extern const char kDisableOfficeEditingComponentAppName[];
+extern const char kDisableOfficeEditingComponentAppDescription[];
+
 extern const char kVaapiAV1DecoderName[];
 extern const char kVaapiAV1DecoderDescription[];
 
@@ -3280,6 +3277,9 @@
 extern const char kMessagesPreinstallName[];
 extern const char kMessagesPreinstallDescription[];
 
+extern const char kOneGroupPerRendererName[];
+extern const char kOneGroupPerRendererDescription[];
+
 extern const char kSyncChromeOSExplicitPassphraseSharingName[];
 extern const char kSyncChromeOSExplicitPassphraseSharingDescription[];
 #endif  // BUILDFLAG(IS_CHROMEOS)
diff --git a/chrome/browser/history_clusters/history_clusters_metrics_logger.cc b/chrome/browser/history_clusters/history_clusters_metrics_logger.cc
index 253f5ca..4f298fb 100644
--- a/chrome/browser/history_clusters/history_clusters_metrics_logger.cc
+++ b/chrome/browser/history_clusters/history_clusters_metrics_logger.cc
@@ -36,19 +36,23 @@
     // window closing.
     final_state_ = HistoryClustersFinalState::kCloseTab;
   }
+  // TODO(crbug.com/1326954): Add UI-driven counts to UKM during final state
+  // clean up.
   builder.SetInitialState(static_cast<int>(*initial_state_));
   builder.SetFinalState(static_cast<int>(*final_state_));
   builder.SetNumQueries(num_queries_);
   builder.SetNumTogglesToBasicHistory(num_toggles_to_basic_history_);
   builder.Record(ukm::UkmRecorder::Get());
 
-  // Record UMA metrics.
-  base::UmaHistogramExactLinear("History.Clusters.Actions.LinksOpened",
-                                links_opened_count_, 100);
   base::UmaHistogramEnumeration("History.Clusters.Actions.InitialState",
                                 *initial_state_);
+
+  // TODO(crbug.com/1326954): Clean up once the metrics are refactored
+  // to be driven by UI events rather than navigation state. Also
+  // add collected counts to both UMA and UKM.
   base::UmaHistogramEnumeration("History.Clusters.Actions.FinalState",
                                 *final_state_);
+
   base::UmaHistogramBoolean("History.Clusters.Actions.DidMakeQuery",
                             num_queries_ > 0);
   if (num_queries_ > 0) {
@@ -59,6 +63,48 @@
   }
 }
 
+void HistoryClustersMetricsLogger::RecordVisitAction(VisitAction visit_action,
+                                                     uint32_t visit_index,
+                                                     VisitType visit_type) {
+  base::UmaHistogramCounts100(
+      "History.Clusters.UIActions.Visit." + VisitActionToString(visit_action),
+      visit_index);
+
+  base::UmaHistogramCounts100("History.Clusters.UIActions." +
+                                  VisitTypeToString(visit_type) + "Visit." +
+                                  VisitActionToString(visit_action),
+                              visit_index);
+  // TODO(crbug.com/1326954): Add final state of total counts for each visit
+  // action.
+}
+
+void HistoryClustersMetricsLogger::RecordClusterAction(
+    ClusterAction cluster_action,
+    uint32_t cluster_index) {
+  base::UmaHistogramCounts100("History.Clusters.UIActions.Cluster." +
+                                  ClusterActionToString(cluster_action),
+                              cluster_index);
+  // TODO(crbug.com/1326954): Add final state of total counts for each cluster
+  // action.
+}
+
+void HistoryClustersMetricsLogger::RecordRelatedSearchAction(
+    RelatedSearchAction action,
+    uint32_t related_search_index) {
+  base::UmaHistogramCounts100("History.Clusters.UIActions.RelatedSearch." +
+                                  RelatedSearchActionToString(action),
+                              related_search_index);
+  // TODO(crbug.com/1326954): Add final state of total counts for each related
+  // search action.
+}
+
+void HistoryClustersMetricsLogger::RecordToggledVisibility(bool visible) {
+  base::UmaHistogramBoolean("History.Clusters.UIActions.ToggledVisiblity",
+                            visible);
+  // TODO(crbug.com/1326954): Add final state of total counts for each
+  // visibility toggle.
+}
+
 PAGE_USER_DATA_KEY_IMPL(HistoryClustersMetricsLogger);
 
 }  // namespace history_clusters
diff --git a/chrome/browser/history_clusters/history_clusters_metrics_logger.h b/chrome/browser/history_clusters/history_clusters_metrics_logger.h
index 49e2f04..27d30bc 100644
--- a/chrome/browser/history_clusters/history_clusters_metrics_logger.h
+++ b/chrome/browser/history_clusters/history_clusters_metrics_logger.h
@@ -5,6 +5,7 @@
 #ifndef CHROME_BROWSER_HISTORY_CLUSTERS_HISTORY_CLUSTERS_METRICS_LOGGER_H_
 #define CHROME_BROWSER_HISTORY_CLUSTERS_HISTORY_CLUSTERS_METRICS_LOGGER_H_
 
+#include "components/history_clusters/core/cluster_metrics_utils.h"
 #include "content/public/browser/page.h"
 #include "content/public/browser/page_user_data.h"
 #include "content/public/browser/web_contents_observer.h"
@@ -32,6 +33,8 @@
 };
 
 // The final state, or outcome, of an interaction on the HistoryClusters UI.
+// TODO(crbug.com/1326954): Clean up once the metrics are refactored
+// to be driven by UI events rather than navigation state.
 //
 // Keep in sync with HistoryClustersFinalState in
 // tools/metrics/histograms/enums.xml.
@@ -92,7 +95,23 @@
     navigation_id_ = navigation_id;
   }
 
-  void IncrementLinksOpenedCount() { links_opened_count_++; }
+  // Records that an |visit_action| in the UI occurred at |visit_index| position
+  // on a specified |visit_type|.
+  void RecordVisitAction(VisitAction visit_action,
+                         uint32_t visit_index,
+                         VisitType visit_type);
+
+  // Records that a related search link was clicked at |related_search_index|.
+  void RecordRelatedSearchAction(RelatedSearchAction action,
+                                 uint32_t related_search_index);
+
+  // Records that the journeys UI visibility was toggled.
+  void RecordToggledVisibility(bool visible);
+
+  // Records that an |cluster_action| in the UI occurred at |cluster_index|
+  // position
+  void RecordClusterAction(ClusterAction cluster_action,
+                           uint32_t cluster_index);
 
  private:
   // The navigation ID of the navigation handle that this data is associated
@@ -113,12 +132,6 @@
   // The number of times in this interaction with HistoryClusters included the
   // user toggled to the basic History UI from the HistoryClusters UI.
   int num_toggles_to_basic_history_ = 0;
-
-  // The number of links opened from the HistoryClusters UI. Includes both
-  // same-tab and new-tab/window navigations. Includes both visit and related
-  // search links. Does not include sidebar navigations (e.g. 'Clear browsing
-  // data').
-  int links_opened_count_ = 0;
 };
 
 }  // namespace history_clusters
diff --git a/chrome/browser/history_clusters/history_clusters_tab_helper.cc b/chrome/browser/history_clusters/history_clusters_tab_helper.cc
index 84e9145..896f904 100644
--- a/chrome/browser/history_clusters/history_clusters_tab_helper.cc
+++ b/chrome/browser/history_clusters/history_clusters_tab_helper.cc
@@ -348,10 +348,10 @@
     auto* logger =
         history_clusters::HistoryClustersMetricsLogger::GetOrCreateForPage(
             navigation_handle->GetWebContents()->GetPrimaryPage());
+    // TODO(crbug.com/1326954): Clean up once the metrics are refactored
+    // to be driven by UI events rather than navigation state.
     logger->set_final_state(
         history_clusters::HistoryClustersFinalState::kLinkClick);
-    if (CanAddURLToHistory(navigation_handle->GetURL()))
-      logger->IncrementLinksOpenedCount();
   }
 }
 
@@ -408,28 +408,6 @@
   logger->set_initial_state(initial_state);
 }
 
-void HistoryClustersTabHelper::DidOpenRequestedURL(
-    content::WebContents* new_contents,
-    content::RenderFrameHost* source_render_frame_host,
-    const GURL& url,
-    const content::Referrer& referrer,
-    WindowOpenDisposition disposition,
-    ui::PageTransition transition,
-    bool started_from_context_menu,
-    bool renderer_initiated) {
-  // Will detect when a link was followed from the history clusters page in a
-  // new web contents (e.g. new tab or window).
-  // And will update this page's associated `HistoryClustersMetricsLogger`.
-  if (IsHistoryPage(web_contents()->GetLastCommittedURL(),
-                    GURL(chrome::kChromeUIHistoryClustersURL)) &&
-      CanAddURLToHistory(url)) {
-    auto* logger =
-        history_clusters::HistoryClustersMetricsLogger::GetOrCreateForPage(
-            web_contents()->GetPrimaryPage());
-    logger->IncrementLinksOpenedCount();
-  }
-}
-
 void HistoryClustersTabHelper::WebContentsDestroyed() {
   // Complete any incomplete visits associated with navigations made in this
   // tab.
diff --git a/chrome/browser/history_clusters/history_clusters_tab_helper.h b/chrome/browser/history_clusters/history_clusters_tab_helper.h
index 7adb9c9..b5cf172 100644
--- a/chrome/browser/history_clusters/history_clusters_tab_helper.h
+++ b/chrome/browser/history_clusters/history_clusters_tab_helper.h
@@ -63,14 +63,6 @@
       content::NavigationHandle* navigation_handle) override;
   void DidFinishNavigation(
       content::NavigationHandle* navigation_handle) override;
-  void DidOpenRequestedURL(content::WebContents* new_contents,
-                           content::RenderFrameHost* source_render_frame_host,
-                           const GURL& url,
-                           const content::Referrer& referrer,
-                           WindowOpenDisposition disposition,
-                           ui::PageTransition transition,
-                           bool started_from_context_menu,
-                           bool renderer_initiated) override;
   void WebContentsDestroyed() override;
 
  private:
diff --git a/chrome/browser/paint_preview/services/paint_preview_tab_service.cc b/chrome/browser/paint_preview/services/paint_preview_tab_service.cc
index 808bf5a..1e789ad 100644
--- a/chrome/browser/paint_preview/services/paint_preview_tab_service.cc
+++ b/chrome/browser/paint_preview/services/paint_preview_tab_service.cc
@@ -294,7 +294,7 @@
   auto* rfh = content::RenderFrameHost::FromID(task->frame_routing_id());
   if (!contents || !rfh || contents->IsBeingDestroyed() ||
       contents->GetMainFrame() != rfh || !rfh->IsActive() ||
-      !rfh->IsRenderFrameCreated() || !rfh->IsRenderFrameLive()) {
+      !rfh->IsRenderFrameLive()) {
     task->OnCaptured(Status::kWebContentsGone);
     return;
   }
diff --git a/chrome/browser/policy/networking/network_configuration_updater_ash_unittest.cc b/chrome/browser/policy/networking/network_configuration_updater_ash_unittest.cc
index f2fcbc0..30a297e 100644
--- a/chrome/browser/policy/networking/network_configuration_updater_ash_unittest.cc
+++ b/chrome/browser/policy/networking/network_configuration_updater_ash_unittest.cc
@@ -20,6 +20,7 @@
 #include "chrome/browser/policy/networking/device_network_configuration_updater_ash.h"
 #include "chrome/browser/policy/networking/user_network_configuration_updater_ash.h"
 #include "chrome/test/base/testing_profile.h"
+#include "chromeos/ash/components/network/onc/onc_certificate_importer.h"
 #include "chromeos/components/onc/certificate_scope.h"
 #include "chromeos/components/onc/onc_parsed_certificates.h"
 #include "chromeos/components/onc/onc_test_utils.h"
@@ -27,7 +28,6 @@
 #include "chromeos/dbus/session_manager/session_manager_client.h"
 #include "chromeos/network/fake_network_device_handler.h"
 #include "chromeos/network/mock_managed_network_configuration_handler.h"
-#include "chromeos/network/onc/onc_certificate_importer.h"
 #include "chromeos/network/policy_certificate_provider.h"
 #include "chromeos/system/fake_statistics_provider.h"
 #include "chromeos/system/statistics_provider.h"
diff --git a/chrome/browser/policy/networking/user_network_configuration_updater_ash.cc b/chrome/browser/policy/networking/user_network_configuration_updater_ash.cc
index 226aa8a..621979cd 100644
--- a/chrome/browser/policy/networking/user_network_configuration_updater_ash.cc
+++ b/chrome/browser/policy/networking/user_network_configuration_updater_ash.cc
@@ -17,12 +17,12 @@
 #include "chrome/browser/net/nss_service.h"
 #include "chrome/browser/net/nss_service_factory.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chromeos/ash/components/network/onc/network_onc_utils.h"
+#include "chromeos/ash/components/network/onc/onc_certificate_importer_impl.h"
 #include "chromeos/components/onc/onc_parsed_certificates.h"
 #include "chromeos/components/onc/onc_utils.h"
 #include "chromeos/network/managed_network_configuration_handler.h"
 #include "chromeos/network/network_cert_loader.h"
-#include "chromeos/network/onc/network_onc_utils.h"
-#include "chromeos/network/onc/onc_certificate_importer_impl.h"
 #include "components/policy/policy_constants.h"
 #include "components/user_manager/user.h"
 #include "content/public/browser/browser_task_traits.h"
diff --git a/chrome/browser/policy/test/policy_certs_browsertest.cc b/chrome/browser/policy/test/policy_certs_browsertest.cc
index 8a04699..f285894b2 100644
--- a/chrome/browser/policy/test/policy_certs_browsertest.cc
+++ b/chrome/browser/policy/test/policy_certs_browsertest.cc
@@ -27,10 +27,10 @@
 #include "chrome/browser/ui/browser_list.h"
 #include "chrome/common/chrome_paths.h"
 #include "chrome/test/base/in_process_browser_test.h"
+#include "chromeos/ash/components/network/onc/onc_certificate_importer.h"
+#include "chromeos/ash/components/network/onc/onc_certificate_importer_impl.h"
 #include "chromeos/components/onc/onc_test_utils.h"
 #include "chromeos/network/network_cert_loader.h"
-#include "chromeos/network/onc/onc_certificate_importer.h"
-#include "chromeos/network/onc/onc_certificate_importer_impl.h"
 #include "chromeos/test/chromeos_test_utils.h"
 #include "components/onc/onc_constants.h"
 #include "components/policy/core/browser/browser_policy_connector.h"
diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc
index 10ed54b..8f3e3bc 100644
--- a/chrome/browser/prefs/browser_prefs.cc
+++ b/chrome/browser/prefs/browser_prefs.cc
@@ -750,6 +750,12 @@
 const char kColorModeThemed[] = "ash.dark_mode.color_mode_themed";
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+// Deprecated 05/2022.
+const char kNativeBridge64BitSupportExperimentEnabled[] =
+    "arc.native_bridge_64bit_support_experiment";
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+
 // Register local state used only for migration (clearing or moving to a new
 // key).
 void RegisterLocalStatePrefsForMigration(PrefRegistrySimple* registry) {
@@ -798,6 +804,11 @@
 #if !BUILDFLAG(IS_ANDROID)
   registry->RegisterIntegerPref(kStabilityRendererLaunchCount, 0);
 #endif  // !BUILDFLAG(IS_ANDROID)
+
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+  registry->RegisterBooleanPref(kNativeBridge64BitSupportExperimentEnabled,
+                                false);
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 }
 
 // Register prefs used only for migration (clearing or moving to a new key).
@@ -1652,6 +1663,11 @@
   local_state->ClearPref(prefs::kTabFreezingEnabled);
 #endif
 
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+  // Added 05/2022.
+  local_state->ClearPref(kNativeBridge64BitSupportExperimentEnabled);
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+
   // Please don't delete the following line. It is used by PRESUBMIT.py.
   // END_MIGRATE_OBSOLETE_LOCAL_STATE_PREFS
 
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/BUILD.gn b/chrome/browser/resources/chromeos/accessibility/accessibility_common/BUILD.gn
index 1b2f0c5..439f9d35 100644
--- a/chrome/browser/resources/chromeos/accessibility/accessibility_common/BUILD.gn
+++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/BUILD.gn
@@ -42,6 +42,7 @@
     "dictation/earcons/audio_end.wav",
     "dictation/earcons/audio_initiate.wav",
     "dictation/earcons/null_selection.wav",
+    "dictation/focus_handler.js",
     "dictation/input_controller.js",
     "dictation/macros/input_text_view_macro.js",
     "dictation/macros/list_commands_macro.js",
@@ -124,6 +125,7 @@
     "dictation/dictation_test.js",
     "dictation/dictation_test_base.js",
     "dictation/dictation_ui_test.js",
+    "dictation/focus_handler_test.js",
     "dictation/macros/dictation_macros_test.js",
     "dictation/parse/dictation_parse_test.js",
     "magnifier/magnifier_test.js",
@@ -200,6 +202,7 @@
 js_library("dictation") {
   sources = [ "dictation/dictation.js" ]
   deps = [
+    ":dictation_focus_handler",
     ":dictation_input_controller",
     ":dictation_metrics",
     ":dictation_ui_controller",
@@ -214,10 +217,6 @@
 
 js_library("dictation_input_controller") {
   sources = [ "dictation/input_controller.js" ]
-  deps = [
-    "../common:automation_predicate",
-    "../common:constants",
-  ]
   externs_list = [
     "$externs_path/input_method_private.js",
     "$externs_path/language_settings_private.js",
@@ -270,3 +269,12 @@
     "$externs_path/speech_recognition_private.js",
   ]
 }
+
+js_library("dictation_focus_handler") {
+  sources = [ "dictation/focus_handler.js" ]
+  deps = [
+    "../common:automation_predicate",
+    "../common:constants",
+  ]
+  externs_list = [ "$externs_path/automation.js" ]
+}
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/dictation.js b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/dictation.js
index a7017c2cc..68a0981 100644
--- a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/dictation.js
+++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/dictation.js
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import {FocusHandler} from '/accessibility_common/dictation/focus_handler.js';
 import {InputController} from '/accessibility_common/dictation/input_controller.js';
 import {Macro} from '/accessibility_common/dictation/macros/macro.js';
 import {MacroName} from '/accessibility_common/dictation/macros/macro_names.js';
@@ -65,6 +66,9 @@
     /** @private {boolean} */
     this.isPumpkinEnabled_ = false;
 
+    /** @private {?FocusHandler} */
+    this.focusHandler_ = null;
+
     this.initialize_();
   }
 
@@ -73,8 +77,9 @@
    * @private
    */
   initialize_() {
-    this.inputController_ =
-        new InputController(() => this.stopDictation_(/*notify=*/ true));
+    this.focusHandler_ = new FocusHandler();
+    this.inputController_ = new InputController(
+        () => this.stopDictation_(/*notify=*/ true), this.focusHandler_);
     this.uiController_ = new UIController();
     this.speechParser_ = new SpeechParser(this.inputController_);
     if (this.localePref_) {
@@ -213,6 +218,7 @@
   /**
    * Called when the Speech Recognition engine receives a recognition event.
    * @param {ResultEvent} event
+   * @return {!Promise}
    * @private
    */
   async onSpeechRecognitionResult_(event) {
@@ -233,6 +239,7 @@
    * @param {string} transcript
    * @param {boolean} isFinal Whether this is a finalized transcript or an
    *     interim result.
+   * @return {!Promise}
    * @private
    */
   async processSpeechRecognitionResult_(transcript, isFinal) {
@@ -295,6 +302,7 @@
 
     this.uiController_.setState(
         UIState.STANDBY, {context: HintContext.STANDBY});
+    this.focusHandler_.refresh();
   }
 
   /**
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/dictation_test_base.js b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/dictation_test_base.js
index f660e19..a15e9314 100644
--- a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/dictation_test_base.js
+++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/dictation_test_base.js
@@ -92,6 +92,8 @@
 #include "base/command_line.h"
 #include "chrome/browser/ash/accessibility/accessibility_manager.h"
 #include "ui/accessibility/accessibility_features.h"
+#include "components/prefs/pref_service.h"
+#include "ash/constants/ash_pref_names.h"
     `);
   }
 
@@ -99,6 +101,9 @@
   testGenPreamble() {
     super.testGenPreamble();
     GEN(`
+  browser()->profile()->GetPrefs()->SetBoolean(
+        ash::prefs::kDictationAcceleratorDialogHasBeenAccepted, true);
+
   base::OnceClosure load_cb =
     base::BindOnce(&ash::AccessibilityManager::SetDictationEnabled,
         base::Unretained(ash::AccessibilityManager::Get()),
@@ -196,6 +201,7 @@
   /**
    * Checks that the latest IME commit text matches the expected value.
    * @param {string} expected
+   * @return {!Promise}
    */
   async assertCommittedText(expected) {
     if (!this.mockInputIme.getLastCommittedParameters()) {
@@ -217,6 +223,7 @@
   /**
    * Async function to get a preference value from Settings.
    * @param {string} name
+   * @return {!Promise<*>}
    */
   async getPref(name) {
     return new Promise(resolve => {
@@ -229,6 +236,7 @@
   /**
    * Async function to set a preference value in Settings.
    * @param {string} name
+   * @return {!Promise}
    */
   async setPref(name, value) {
     return new Promise(resolve => {
@@ -307,6 +315,7 @@
    * Waits for the updateDictationBubble() API to be called with the given
    * properties.
    * @param {DictationBubbleProperties} targetProps
+   * @return {!Promise}
    */
   async waitForUIProperties(targetProps) {
     // Poll until the updateDictationBubble() API gets called with
@@ -321,7 +330,7 @@
           clearInterval(intervalId);
           resolve();
         }
-      });
+      }, 100);
     });
   }
 
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/focus_handler.js b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/focus_handler.js
new file mode 100644
index 0000000..3669fa2ec
--- /dev/null
+++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/focus_handler.js
@@ -0,0 +1,109 @@
+// Copyright 2022 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.
+
+const AutomationNode = chrome.automation.AutomationNode;
+const AutomationEvent = chrome.automation.AutomationEvent;
+const EventType = chrome.automation.EventType;
+
+export class FocusHandler {
+  constructor() {
+    /** @private {boolean} */
+    this.active_ = false;
+
+    /**
+     * The currently focused editable node.
+     * @private {?AutomationNode}
+     */
+    this.editableNode_ = null;
+
+    /** @private {?number} */
+    this.deactivateTimeoutId_ = null;
+
+    /** @private {?EventHandler} */
+    this.eventHandler_ = null;
+  }
+
+  /**
+   * Starts listening to focus events and sets a timeout to deactivate after
+   * a certain amount of inactivity.
+   * @return {!Promise}
+   */
+  async refresh() {
+    if (this.deactivateTimeoutId_ !== null) {
+      clearTimeout(this.deactivateTimeoutId_);
+    }
+    this.deactivateTimeoutId_ = setTimeout(
+        () => this.deactivate_(), FocusHandler.DEACTIVATE_TIMEOUT_MS_);
+
+    await this.activate_();
+  }
+
+  /**
+   * Gets the current focus and starts listening for focus events.
+   * @private
+   * @return {!Promise}
+   */
+  async activate_() {
+    if (this.active_) {
+      return;
+    }
+
+    const desktop =
+        await new Promise(resolve => chrome.automation.getDesktop(resolve));
+
+    const focus =
+        await new Promise(resolve => chrome.automation.getFocus(resolve));
+    if (focus && AutomationPredicate.editText(focus)) {
+      this.editableNode_ = focus;
+    }
+
+    if (!this.eventHandler_) {
+      this.eventHandler_ = new EventHandler(
+          [], EventType.FOCUS, event => this.onFocusChanged_(event));
+    }
+    this.eventHandler_.setNodes(desktop);
+    this.eventHandler_.start();
+
+    this.active_ = true;
+  }
+
+  /** @private */
+  deactivate_() {
+    this.eventHandler_.stop();
+    this.eventHandler_ = null;
+    this.active_ = false;
+    this.editableNode_ = null;
+  }
+
+  /**
+   * Saves the focused node if it's an editable.
+   * @param {!AutomationEvent} event
+   * @private
+   */
+  onFocusChanged_(event) {
+    const node = event.target;
+    if (!node || !AutomationPredicate.editText(node)) {
+      this.editableNode_ = null;
+      return;
+    }
+
+    this.editableNode_ = node;
+  }
+
+  /** @return {?AutomationNode} */
+  getEditableNode() {
+    return this.editableNode_;
+  }
+
+  /** @return {boolean} */
+  isReadyForTesting() {
+    return this.active_ && this.editableNode_ !== null;
+  }
+}
+
+/**
+ * @const {number}
+ * @private
+ */
+FocusHandler.DEACTIVATE_TIMEOUT_MS_ = 45 * 1000;
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/focus_handler_test.js b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/focus_handler_test.js
new file mode 100644
index 0000000..378e11a
--- /dev/null
+++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/focus_handler_test.js
@@ -0,0 +1,121 @@
+// Copyright 2022 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.
+
+GEN_INCLUDE(['dictation_test_base.js']);
+
+DictationFocusHandlerTest = class extends DictationE2ETestBase {
+  /** @override */
+  async setUpDeferred() {
+    await super.setUpDeferred();
+    await importModule(
+        'FocusHandler', '/accessibility_common/dictation/focus_handler.js');
+  }
+
+  /** @return {!FocusHandler} */
+  getFocusHandler() {
+    return accessibilityCommon.dictation_.focusHandler_;
+  }
+
+  /** @return {!Promise} */
+  async activateFocusHandler() {
+    await this.getFocusHandler().refresh();
+    assertTrue(this.getFocusHandler().active_);
+    assertNotNullNorUndefined(this.getFocusHandler().deactivateTimeoutId_);
+  }
+
+  /**
+   * @param {boolean} active
+   * @return {!Promise}
+   */
+  async waitForFocusHandlerActive(active) {
+    return new Promise(resolve => {
+      const intervalId = setInterval(() => {
+        if (this.getFocusHandler().active_ === active) {
+          clearInterval(intervalId);
+          resolve();
+        }
+      }, 100);
+    });
+  }
+
+  /**
+   * @param {!AutomationNode} target
+   * @return {!Promise}
+   */
+  async waitForFocus(target) {
+    return new Promise(resolve => {
+      const intervalId = setInterval(() => {
+        if (this.getFocusHandler().editableNode_ === target) {
+          clearInterval(intervalId);
+          resolve();
+        }
+      }, 100);
+    });
+  }
+
+  /** @return {string} */
+  simpleSite() {
+    return `
+      <button autofocus>Start</button>
+      <input id="first" type="text"></input>
+    `;
+  }
+};
+
+// This test ensures that FocusHandler activates when Dictation is toggled on.
+SYNC_TEST_F('DictationFocusHandlerTest', 'Activate', async function() {
+  const root = await this.runWithLoadedTree(this.simpleSite());
+  const input = root.find({role: chrome.automation.RoleType.TEXT_FIELD});
+  assertTrue(Boolean(input));
+
+  // FocusHandler should not be activated until Dictation is toggled on.
+  assertFalse(this.getFocusHandler().active_);
+  assertEquals(null, this.getFocusHandler().editableNode_);
+  input.focus();
+  this.toggleDictationOn();
+  await this.waitForFocusHandlerActive(true);
+});
+
+// This test ensures that FocusHandler deactivates automatically after a
+// period of inactivity.
+SYNC_TEST_F('DictationFocusHandlerTest', 'Deactivate', async function() {
+  // Shorten timeout for testing.
+  FocusHandler.DEACTIVATE_TIMEOUT_MS_ = 1000;
+  await this.activateFocusHandler();
+  await this.waitForFocusHandlerActive(false);
+});
+
+// This test ensures that FocusHandler tracks focus once it's been activated.
+SYNC_TEST_F('DictationFocusHandlerTest', 'OnFocusChanged', async function() {
+  await this.activateFocusHandler();
+  const root = await this.runWithLoadedTree(this.simpleSite());
+  const input = root.find({role: chrome.automation.RoleType.TEXT_FIELD});
+  assertTrue(Boolean(input));
+
+  input.focus();
+  await this.waitForFocus(input);
+});
+
+// This test ensures that the timeout to deactivate FocusHandler is reset
+// whenever Dictation toggles on.
+SYNC_TEST_F(
+    'DictationFocusHandlerTest', 'ResetDeactivateTimeout', async function() {
+      this.mockSetTimeoutMethod();
+      this.toggleDictationOn();
+      await this.waitForFocusHandlerActive(true);
+      let callback =
+          this.getCallbackWithDelay(FocusHandler.DEACTIVATE_TIMEOUT_MS_);
+      assertNotNullNorUndefined(callback);
+
+      this.clearSetTimeoutData();
+      this.toggleDictationOff();
+      this.toggleDictationOn();
+      // Toggling Dictation on should set a new timeout to deactivate
+      // FocusHandler.
+      callback = this.getCallbackWithDelay(FocusHandler.DEACTIVATE_TIMEOUT_MS_);
+      assertNotNullNorUndefined(callback);
+      // Running `callback` should deactivate FocusHandler.
+      callback();
+      await this.waitForFocusHandlerActive(false);
+    });
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/input_controller.js b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/input_controller.js
index 7c3d268..15c10a175 100644
--- a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/input_controller.js
+++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/input_controller.js
@@ -2,19 +2,19 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-const AutomationNode = chrome.automation.AutomationNode;
-const AutomationEvent = chrome.automation.AutomationEvent;
-const EventType = chrome.automation.EventType;
 const IconType = chrome.accessibilityPrivate.DictationBubbleIconType;
 
 /**
  * InputController handles interaction with input fields for Dictation.
  */
 export class InputController {
-  constructor(stopDictationCallback) {
+  constructor(stopDictationCallback, focusHandler) {
     /** @private {number} */
     this.activeImeContextId_ = InputController.NO_ACTIVE_IME_CONTEXT_ID_;
 
+    /** @private {!FocusHandler} */
+    this.focusHandler_ = focusHandler;
+
     /**
      * The engine ID of the previously active IME input method. Used to
      * restore the previous IME after Dictation is deactivated.
@@ -28,15 +28,6 @@
     /** @private {?function():void} */
     this.onConnectCallback_ = null;
 
-    /**
-     * The currently focused editable node.
-     * @private {?AutomationNode}
-     */
-    this.editableNode_ = null;
-
-    /** @private {?EventHandler} */
-    this.focusHandler_ = null;
-
     this.initialize_();
   }
 
@@ -50,16 +41,6 @@
         (context) => this.onImeFocus_(context));
     chrome.input.ime.onBlur.addListener(
         (contextId) => this.onImeBlur_(contextId));
-
-    // IME focus and blur listeners do not tell us which AutomationNode is
-    // currently focused. Register a focus event handler that will give us this
-    // information.
-    this.focusHandler_ = new EventHandler(
-        [], EventType.FOCUS, event => this.onFocusChanged_(event));
-    chrome.automation.getDesktop((desktop) => {
-      this.focusHandler_.setNodes(desktop);
-      this.focusHandler_.start();
-    });
   }
 
   /**
@@ -153,20 +134,6 @@
   }
 
   /**
-   * @param {!AutomationEvent} event
-   * @private
-   */
-  onFocusChanged_(event) {
-    const node = event.target;
-    if (!node || !AutomationPredicate.editText(node)) {
-      this.editableNode_ = null;
-      return;
-    }
-
-    this.editableNode_ = node;
-  }
-
-  /**
    * @param {string} text
    * @return {string}
    */
@@ -177,14 +144,15 @@
     // space when committed to a text field. This is a temporary workaround
     // until the blocking SODA bug can be fixed. Note, a similar strategy
     // already exists in Dictation::OnSpeechResult().
-    if (!this.editableNode_ ||
+    const editableNode = this.focusHandler_.getEditableNode();
+    if (!editableNode ||
         InputController.BEGINS_WITH_WHITESPACE_REGEX_.test(text)) {
       return text;
     }
 
-    const value = this.editableNode_.value;
-    const selStart = this.editableNode_.textSelStart;
-    const selEnd = this.editableNode_.textSelEnd;
+    const value = editableNode.value;
+    const selStart = editableNode.textSelStart;
+    const selEnd = editableNode.textSelEnd;
     // Prepend a space to `text` if there is text directly left of the cursor.
     if (!selStart || selStart !== selEnd || !value ||
         InputController.BEGINS_WITH_WHITESPACE_REGEX_.test(
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/parse/speech_parser.js b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/parse/speech_parser.js
index aef2ae4..a82d9c0c 100644
--- a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/parse/speech_parser.js
+++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/parse/speech_parser.js
@@ -36,6 +36,7 @@
   /**
    * @param {string} locale The Dictation recognition locale. Only some locales
    *     are supported by Pumpkin.
+   * @return {!Promise}
    */
   async initialize(locale) {
     this.isRTLLocale_ = SpeechParser.RTLLocales.has(locale);
diff --git a/chrome/browser/resources/history/history.ts b/chrome/browser/resources/history/history.ts
index d14eb35..ddc12a6 100644
--- a/chrome/browser/resources/history/history.ts
+++ b/chrome/browser/resources/history/history.ts
@@ -5,8 +5,8 @@
 import './app.js';
 
 export {BrowserProxyImpl} from 'chrome://resources/cr_components/history_clusters/browser_proxy.js';
-export {PageCallbackRouter, PageHandlerRemote} from 'chrome://resources/cr_components/history_clusters/history_clusters.mojom-webui.js';
-export {ClusterAction, MetricsProxy, MetricsProxyImpl, RelatedSearchAction, VisitAction, VisitType} from 'chrome://resources/cr_components/history_clusters/metrics_proxy.js';
+export {ClusterAction, PageCallbackRouter, PageHandlerRemote, RelatedSearchAction, VisitAction, VisitType} from 'chrome://resources/cr_components/history_clusters/history_clusters.mojom-webui.js';
+export {MetricsProxy, MetricsProxyImpl} from 'chrome://resources/cr_components/history_clusters/metrics_proxy.js';
 export {CrActionMenuElement} from 'chrome://resources/cr_elements/cr_action_menu/cr_action_menu.js';
 export {CrDialogElement} from 'chrome://resources/cr_elements/cr_dialog/cr_dialog.m.js';
 export {ensureLazyLoaded, HistoryAppElement, listenForPrivilegedLinkClicks} from './app.js';
diff --git a/chrome/browser/resources/settings/chromeos/os_a11y_page/tts_subpage.js b/chrome/browser/resources/settings/chromeos/os_a11y_page/tts_subpage.js
index 999cfb6..a75d8ef3 100644
--- a/chrome/browser/resources/settings/chromeos/os_a11y_page/tts_subpage.js
+++ b/chrome/browser/resources/settings/chromeos/os_a11y_page/tts_subpage.js
@@ -145,6 +145,7 @@
         'tts-preview-state-changed',
         isSpeaking => this.onTtsPreviewStateChanged_(isSpeaking));
     this.ttsBrowserProxy_.getTtsExtensions();
+    this.ttsBrowserProxy_.refreshTtsVoices();
   },
 
   /**
diff --git a/chrome/browser/resources/settings/chromeos/os_a11y_page/tts_subpage_browser_proxy.js b/chrome/browser/resources/settings/chromeos/os_a11y_page/tts_subpage_browser_proxy.js
index e26f04a..f7eaa44c 100644
--- a/chrome/browser/resources/settings/chromeos/os_a11y_page/tts_subpage_browser_proxy.js
+++ b/chrome/browser/resources/settings/chromeos/os_a11y_page/tts_subpage_browser_proxy.js
@@ -32,6 +32,12 @@
    * Awakens the tts engine.
    */
   wakeTtsEngine() {}
+
+  /**
+   * Triggers the TtsPlatform to update its list of voices and relay that update
+   * through VoicesChanged.
+   */
+  refreshTtsVoices() {}
 }
 
 /**
@@ -57,8 +63,13 @@
   wakeTtsEngine() {
     chrome.send('wakeTtsEngine');
   }
+
+  /** @override */
+  refreshTtsVoices() {
+    chrome.send('refreshTtsVoices');
+  }
 }
 
 // The singleton instance_ is replaced with a test version of this wrapper
 // during testing.
-addSingletonGetter(TtsSubpageBrowserProxyImpl);
\ No newline at end of file
+addSingletonGetter(TtsSubpageBrowserProxyImpl);
diff --git a/chrome/browser/segmentation_platform/segmentation_platform_profile_observer_unittest.cc b/chrome/browser/segmentation_platform/segmentation_platform_profile_observer_unittest.cc
index 8ca0e89b..73fa5fe 100644
--- a/chrome/browser/segmentation_platform/segmentation_platform_profile_observer_unittest.cc
+++ b/chrome/browser/segmentation_platform/segmentation_platform_profile_observer_unittest.cc
@@ -33,6 +33,13 @@
   MOCK_METHOD(SegmentSelectionResult,
               GetCachedSegmentResult,
               (const std::string&));
+  MOCK_METHOD(int,
+              RegisterOnDemandSegmentSelectionCallback,
+              (const std::string&, const OnDemandSegmentSelectionCallback&));
+  MOCK_METHOD(void,
+              UnregisterOnDemandSegmentSelectionCallback,
+              (int, const std::string&));
+  MOCK_METHOD(void, OnTrigger, (TriggerType, const TriggerContext&));
   MOCK_METHOD(void, EnableMetrics, (bool));
   MOCK_METHOD(void, GetServiceStatus, ());
   MOCK_METHOD(bool, IsPlatformInitialized, ());
diff --git a/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/send_tab_to_self/SendTabToSelfBottomSheetRenderTest.java b/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/send_tab_to_self/SendTabToSelfBottomSheetRenderTest.java
new file mode 100644
index 0000000..f6f3c534
--- /dev/null
+++ b/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/send_tab_to_self/SendTabToSelfBottomSheetRenderTest.java
@@ -0,0 +1,122 @@
+// Copyright 2022 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.share.send_tab_to_self;
+
+import static org.mockito.Mockito.when;
+
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.drawable.Drawable;
+import android.view.View;
+
+import androidx.appcompat.content.res.AppCompatResources;
+import androidx.test.filters.MediumTest;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import org.chromium.base.test.util.Feature;
+import org.chromium.chrome.browser.profiles.Profile;
+import org.chromium.chrome.browser.share.send_tab_to_self.TargetDeviceInfo.DeviceType;
+import org.chromium.chrome.browser.signin.services.IdentityServicesProvider;
+import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
+import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
+import org.chromium.components.signin.base.AccountCapabilities;
+import org.chromium.components.signin.base.AccountInfo;
+import org.chromium.components.signin.base.CoreAccountId;
+import org.chromium.components.signin.identitymanager.ConsentLevel;
+import org.chromium.components.signin.identitymanager.IdentityManager;
+import org.chromium.components.signin.test.util.R;
+import org.chromium.content_public.browser.test.util.TestThreadUtils;
+import org.chromium.ui.test.util.BlankUiTestActivityTestCase;
+import org.chromium.ui.test.util.RenderTestRule;
+import org.chromium.url.JUnitTestGURLs;
+
+import java.util.Arrays;
+import java.util.Calendar;
+import java.util.HashMap;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+/** Render tests for the send-tab-to-self bottom sheets. */
+@RunWith(ChromeJUnit4ClassRunner.class)
+public class SendTabToSelfBottomSheetRenderTest extends BlankUiTestActivityTestCase {
+    @Rule
+    public final RenderTestRule mRenderTestRule =
+            RenderTestRule.Builder.withPublicCorpus()
+                    .setBugComponent(RenderTestRule.Component.UI_BROWSER_SHARING)
+                    .build();
+    @Rule
+    public final MockitoRule mMockitoRule = MockitoJUnit.rule();
+
+    @Mock
+    private Profile mProfile;
+    @Mock
+    private IdentityServicesProvider mIdentityServicesProvider;
+    @Mock
+    private IdentityManager mIdentityManager;
+    @Mock
+    private BottomSheetController mBottomSheetController;
+
+    @Test
+    @MediumTest
+    @Feature("RenderTest")
+    public void testDevicePickerBottomSheet() throws Throwable {
+        AccountInfo account = createFakeAccount();
+        when(mIdentityManager.getPrimaryAccountInfo(ConsentLevel.SIGNIN)).thenReturn(account);
+        when(mIdentityManager.findExtendedAccountInfoByEmailAddress(account.getEmail()))
+                .thenReturn(account);
+        when(mIdentityServicesProvider.getIdentityManager(mProfile)).thenReturn(mIdentityManager);
+        IdentityServicesProvider.setInstanceForTests(mIdentityServicesProvider);
+        Profile.setLastUsedProfileForTesting(mProfile);
+
+        long todayTimestamp = Calendar.getInstance().getTimeInMillis();
+        List<TargetDeviceInfo> devices = Arrays.asList(
+                new TargetDeviceInfo("My Phone", "guid1", DeviceType.PHONE, todayTimestamp),
+                new TargetDeviceInfo("My Computer", "guid2", DeviceType.WIN,
+                        todayTimestamp - TimeUnit.DAYS.toMillis(1)));
+        View view = TestThreadUtils.runOnUiThreadBlockingNoException(() -> {
+            DevicePickerBottomSheetContent sheetContent =
+                    new DevicePickerBottomSheetContent(getActivity(), JUnitTestGURLs.HTTP_URL,
+                            "Title", mBottomSheetController, devices);
+            getActivity().setContentView(sheetContent.getContentView());
+            return sheetContent.getContentView();
+        });
+        mRenderTestRule.render(view, "device_picker");
+    }
+
+    @Test
+    @MediumTest
+    @Feature("RenderTest")
+    public void testNoTargetDeviceBottomSheet() throws Throwable {
+        View view = TestThreadUtils.runOnUiThreadBlockingNoException(() -> {
+            NoTargetDeviceBottomSheetContent sheetContent =
+                    new NoTargetDeviceBottomSheetContent(getActivity());
+            getActivity().setContentView(sheetContent.getContentView());
+            return sheetContent.getContentView();
+        });
+        mRenderTestRule.render(view, "no_target_device");
+    }
+
+    // TODO(crbug.com/1219434): This duplicates the account in AccountManagerTestRule, so tests can
+    // later adopt the rule without failing the golden diffs. That's not done now because it
+    // requires changing the device picker to depend on ProfileDataCache instead of IdentityManager.
+    private AccountInfo createFakeAccount() {
+        Drawable drawable =
+                AppCompatResources.getDrawable(getActivity(), R.drawable.test_profile_picture);
+        Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(),
+                drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
+        Canvas canvas = new Canvas(bitmap);
+        drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
+        drawable.draw(canvas);
+
+        return new AccountInfo(new CoreAccountId("id"), "test@gmail.com", "gaiaId", "John Doe",
+                "John", bitmap, new AccountCapabilities(new HashMap<>()));
+    }
+}
\ No newline at end of file
diff --git a/chrome/browser/share/android/test_java_sources.gni b/chrome/browser/share/android/test_java_sources.gni
index d43abc7..b1a170d9 100644
--- a/chrome/browser/share/android/test_java_sources.gni
+++ b/chrome/browser/share/android/test_java_sources.gni
@@ -11,6 +11,7 @@
   "//chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/long_screenshots/bitmap_generation/LongScreenshotsTabServiceTest.java",
   "//chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/screenshot/ScreenshotShareSheetViewTest.java",
   "//chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/scroll_capture/ScrollCaptureCallbackRenderTest.java",
+  "//chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/send_tab_to_self/SendTabToSelfBottomSheetRenderTest.java",
   "//chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/send_tab_to_self/SendTabToSelfCoordinatorTest.java",
   "//chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/share_sheet/ChromeProvidedSharingOptionsProviderTest.java",
   "//chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/share_sheet/ShareSheetBottomSheetContentTest.java",
diff --git a/chrome/browser/speech/extension_api/tts_extension_apitest.cc b/chrome/browser/speech/extension_api/tts_extension_apitest.cc
index 184b04f6..bd245f79 100644
--- a/chrome/browser/speech/extension_api/tts_extension_apitest.cc
+++ b/chrome/browser/speech/extension_api/tts_extension_apitest.cc
@@ -139,6 +139,8 @@
         [](const content::VoiceData& voice) { return !voice.native; });
   }
 
+  void RefreshVoices() override {}
+
   void GetVoicesForBrowserContext(
       content::BrowserContext* browser_context,
       const GURL& source_url,
diff --git a/chrome/browser/speech/tts_chromeos.cc b/chrome/browser/speech/tts_chromeos.cc
index 028df481..18443aa 100644
--- a/chrome/browser/speech/tts_chromeos.cc
+++ b/chrome/browser/speech/tts_chromeos.cc
@@ -163,6 +163,22 @@
       [](const content::VoiceData& voice) { return !voice.native; });
 }
 
+void TtsPlatformImplChromeOs::RefreshVoices() {
+  // Android voices can be updated silently.
+  // If it happens, we can't return the latest voices here, but below
+  // eventually calls TtsController::VoicesChanged.
+  auto* const arc_service_manager = arc::ArcServiceManager::Get();
+  if (!arc_service_manager)
+    return;
+
+  arc::mojom::TtsInstance* tts = ARC_GET_INSTANCE_FOR_METHOD(
+      arc_service_manager->arc_bridge_service()->tts(), RefreshVoices);
+  if (!tts)
+    return;
+
+  tts->RefreshVoices();
+}
+
 // static
 TtsPlatformImplChromeOs*
 TtsPlatformImplChromeOs::GetInstance() {
diff --git a/chrome/browser/speech/tts_chromeos.h b/chrome/browser/speech/tts_chromeos.h
index 54d43b0..ce29cfcb 100644
--- a/chrome/browser/speech/tts_chromeos.h
+++ b/chrome/browser/speech/tts_chromeos.h
@@ -38,6 +38,7 @@
   void SetError(const std::string& error) override;
   bool IsSpeaking() override;
   void FinalizeVoiceOrdering(std::vector<content::VoiceData>& voices) override;
+  void RefreshVoices() override;
 
   // Unimplemented.
   void Pause() override {}
diff --git a/chrome/browser/speech/tts_lacros.h b/chrome/browser/speech/tts_lacros.h
index 5ed4644..4c72ef2c 100644
--- a/chrome/browser/speech/tts_lacros.h
+++ b/chrome/browser/speech/tts_lacros.h
@@ -58,6 +58,7 @@
       content::TtsUtterance* utterance,
       const content::VoiceData& voice_data) override {}
   void Shutdown() override {}
+  void RefreshVoices() override {}
 
  private:
   friend class base::NoDestructor<TtsPlatformImplLacros>;
diff --git a/chrome/browser/ui/android/layouts/java/src/org/chromium/chrome/browser/layouts/LayoutStateProvider.java b/chrome/browser/ui/android/layouts/java/src/org/chromium/chrome/browser/layouts/LayoutStateProvider.java
index 20735887..42d429b 100644
--- a/chrome/browser/ui/android/layouts/java/src/org/chromium/chrome/browser/layouts/LayoutStateProvider.java
+++ b/chrome/browser/ui/android/layouts/java/src/org/chromium/chrome/browser/layouts/LayoutStateProvider.java
@@ -60,6 +60,12 @@
     boolean isLayoutVisible(@LayoutType int layoutType);
 
     /**
+     * @return Whether or not the {@link Layout} is starting to hide.
+     * @param layoutType whether the {@link Layout} give {@link LayoutType} is starting to hide.
+     */
+    boolean isLayoutStartingToHide(@LayoutType int layoutType);
+
+    /**
      * Get the type of the layout that is currently active.
      * @return The {@link LayoutType} of the active layout.
      */
diff --git a/chrome/browser/ui/app_list/app_service/app_service_app_item.cc b/chrome/browser/ui/app_list/app_service/app_service_app_item.cc
index 400b775a..8fcd5ad 100644
--- a/chrome/browser/ui/app_list/app_service/app_service_app_item.cc
+++ b/chrome/browser/ui/app_list/app_service/app_service_app_item.cc
@@ -106,7 +106,7 @@
       SetPosition(CalculateDefaultPositionIfApplicable());
     }
 
-    // Crostini apps and the Terminal System App start in the crostini folder.
+    // Crostini apps start in the crostini folder.
     if (app_type_ == apps::AppType::kCrostini) {
       DCHECK(folder_id().empty());
       SetChromeFolderId(ash::kCrostiniFolderId);
diff --git a/chrome/browser/ui/app_list/search/files/file_result.cc b/chrome/browser/ui/app_list/search/files/file_result.cc
index d6d8958f..363e5d4 100644
--- a/chrome/browser/ui/app_list/search/files/file_result.cc
+++ b/chrome/browser/ui/app_list/search/files/file_result.cc
@@ -149,43 +149,16 @@
   if (details)
     SetDetails(details.value());
 
-  // Launcher search results UI is light by default, so use icons for light
-  // background if dark/light mode feature is not enabled. Productivity launcher
-  // has dark background by default, so use icons for dark background in that
-  // case.
-  const bool dark_background =
-      ash::features::IsDarkLightModeEnabled()
-          ? ash::ColorProvider::Get()->IsDarkModeEnabled()
-          : ash::features::IsProductivityLauncherEnabled();
-  if (display_type == DisplayType::kChip) {
-    SetChipIcon(chromeos::GetChipIconForPath(filepath, dark_background));
-  } else if (display_type == DisplayType::kContinue) {
-    // For Continue Section, if dark/light mode is disabled, we should use the
-    // icon and not the chip icon with a dark background as default.
-    const gfx::ImageSkia chip_icon =
-        ash::features::IsDarkLightModeEnabled()
-            ? chromeos::GetChipIconForPath(filepath, dark_background)
-            : chromeos::GetIconForPath(filepath, /*dark_background=*/true);
-    SetChipIcon(chip_icon);
-  } else {
-    switch (type) {
-      case Type::kFile:
-        SetIcon(IconInfo(chromeos::GetIconForPath(filepath, dark_background),
-                         kSystemIconDimension));
-        break;
-      case Type::kDirectory:
-        SetIcon(IconInfo(chromeos::GetIconFromType("folder", dark_background),
-                         kSystemIconDimension));
-        break;
-      case Type::kSharedDirectory:
-        SetIcon(IconInfo(chromeos::GetIconFromType("shared", dark_background),
-                         kSystemIconDimension));
-        break;
-    }
-  }
+  UpdateIcon();
+
+  if (ash::ColorProvider::Get())
+    ash::ColorProvider::Get()->AddObserver(this);
 }
 
-FileResult::~FileResult() = default;
+FileResult::~FileResult() {
+  if (ash::ColorProvider::Get())
+    ash::ColorProvider::Get()->RemoveObserver(this);
+}
 
 void FileResult::Open(int event_flags) {
   switch (type_) {
@@ -253,6 +226,10 @@
                                         weak_factory_.GetWeakPtr()));
 }
 
+void FileResult::OnColorModeChanged(bool dark_mode_enabled) {
+  UpdateIcon();
+}
+
 void FileResult::OnThumbnailLoaded(const SkBitmap* bitmap,
                                    base::File::Error error) {
   if (!bitmap) {
@@ -278,6 +255,46 @@
                                 weak_factory_.GetWeakPtr()));
 }
 
+void FileResult::UpdateIcon() {
+  // Launcher search results UI is light by default, so use icons for light
+  // background if dark/light mode feature is not enabled. Productivity launcher
+  // has dark background by default, so use icons for dark background in that
+  // case.
+  const bool is_dark_light_enabled = ash::features::IsDarkLightModeEnabled();
+  // ColorProvider might be nullptr in tests.
+  auto* color_provider = ash::ColorProvider::Get();
+  const bool dark_background =
+      is_dark_light_enabled
+          ? color_provider && color_provider->IsDarkModeEnabled()
+          : ash::features::IsProductivityLauncherEnabled();
+  if (display_type() == DisplayType::kChip) {
+    SetChipIcon(chromeos::GetChipIconForPath(filepath_, dark_background));
+  } else if (display_type() == DisplayType::kContinue) {
+    // For Continue Section, if dark/light mode is disabled, we should use the
+    // icon and not the chip icon with a dark background as default.
+    const gfx::ImageSkia chip_icon =
+        is_dark_light_enabled
+            ? chromeos::GetChipIconForPath(filepath_, dark_background)
+            : chromeos::GetIconForPath(filepath_, /*dark_background=*/true);
+    SetChipIcon(chip_icon);
+  } else {
+    switch (type_) {
+      case Type::kFile:
+        SetIcon(IconInfo(chromeos::GetIconForPath(filepath_, dark_background),
+                         kSystemIconDimension));
+        break;
+      case Type::kDirectory:
+        SetIcon(IconInfo(chromeos::GetIconFromType("folder", dark_background),
+                         kSystemIconDimension));
+        break;
+      case Type::kSharedDirectory:
+        SetIcon(IconInfo(chromeos::GetIconFromType("shared", dark_background),
+                         kSystemIconDimension));
+        break;
+    }
+  }
+}
+
 void FileResult::OnJustificationStringReturned(
     absl::optional<std::u16string> justification) {
   if (justification)
diff --git a/chrome/browser/ui/app_list/search/files/file_result.h b/chrome/browser/ui/app_list/search/files/file_result.h
index cdb0bfe32..ddf4a09 100644
--- a/chrome/browser/ui/app_list/search/files/file_result.h
+++ b/chrome/browser/ui/app_list/search/files/file_result.h
@@ -7,6 +7,7 @@
 
 #include <iosfwd>
 
+#include "ash/public/cpp/style/color_mode_observer.h"
 #include "base/files/file.h"
 #include "base/files/file_path.h"
 #include "chrome/browser/ui/app_list/search/chrome_search_result.h"
@@ -24,7 +25,7 @@
 
 // TODO(crbug.com/1258415): We should split this into four subclasses:
 // {drive,local} {zero-state,search}.
-class FileResult : public ChromeSearchResult {
+class FileResult : public ChromeSearchResult, public ash::ColorModeObserver {
  public:
   enum class Type { kFile, kDirectory, kSharedDirectory };
 
@@ -67,6 +68,9 @@
   }
 
  private:
+  // ash::ColorModeObserver:
+  void OnColorModeChanged(bool dark_mode_enabled) override;
+
   // Callback for the result of RequestThumbnail's call to the ThumbnailLoader.
   void OnThumbnailLoaded(const SkBitmap* bitmap, base::File::Error error);
 
@@ -75,6 +79,8 @@
   void OnJustificationStringReturned(
       absl::optional<std::u16string> justification);
 
+  void UpdateIcon();
+
   const base::FilePath filepath_;
   const Type type_;
   Profile* const profile_;
diff --git a/chrome/browser/ui/app_list/search/omnibox_answer_result.cc b/chrome/browser/ui/app_list/search/omnibox_answer_result.cc
index 77c1baa..2ba346a1 100644
--- a/chrome/browser/ui/app_list/search/omnibox_answer_result.cc
+++ b/chrome/browser/ui/app_list/search/omnibox_answer_result.cc
@@ -47,9 +47,12 @@
 ChromeSearchResult::IconInfo CreateAnswerIconInfo(
     const gfx::VectorIcon& vector_icon) {
   const int dimension = GetAnswerCardIconDimension();
-  const bool dark_mode = ash::features::IsProductivityLauncherEnabled() ||
-                         (ash::features::IsDarkLightModeEnabled() &&
-                          ash::ColorProvider::Get()->IsDarkModeEnabled());
+  // ColorProvider might be nullptr in tests.
+  const bool dark_mode =
+      ash::features::IsProductivityLauncherEnabled() ||
+      (ash::features::IsDarkLightModeEnabled() && ash::ColorProvider::Get() &&
+       ash::ColorProvider::Get()->IsDarkModeEnabled());
+
   const auto icon =
       dark_mode ? gfx::ImageSkiaOperations::CreateImageWithCircleBackground(
                       dimension / 2, gfx::kGoogleBlue300,
@@ -195,15 +198,25 @@
   } else {
     UpdateClassicTitleAndDetails();
   }
+  if (ash::ColorProvider::Get())
+    ash::ColorProvider::Get()->AddObserver(this);
 }
 
-OmniboxAnswerResult::~OmniboxAnswerResult() = default;
+OmniboxAnswerResult::~OmniboxAnswerResult() {
+  if (ash::ColorProvider::Get())
+    ash::ColorProvider::Get()->RemoveObserver(this);
+}
 
 void OmniboxAnswerResult::Open(int event_flags) {
   list_controller_->OpenURL(profile_, match_.destination_url, match_.transition,
                             ui::DispositionFromEventFlags(event_flags));
 }
 
+void OmniboxAnswerResult::OnColorModeChanged(bool dark_mode_enabled) {
+  if (!IsWeatherResult())
+    UpdateIcon();
+}
+
 void OmniboxAnswerResult::UpdateIcon() {
   if (IsCalculatorResult()) {
     SetIcon(CreateAnswerIconInfo(omnibox::kCalculatorIcon));
diff --git a/chrome/browser/ui/app_list/search/omnibox_answer_result.h b/chrome/browser/ui/app_list/search/omnibox_answer_result.h
index 623ce49b..73534a05 100644
--- a/chrome/browser/ui/app_list/search/omnibox_answer_result.h
+++ b/chrome/browser/ui/app_list/search/omnibox_answer_result.h
@@ -5,6 +5,7 @@
 #ifndef CHROME_BROWSER_UI_APP_LIST_SEARCH_OMNIBOX_ANSWER_RESULT_H_
 #define CHROME_BROWSER_UI_APP_LIST_SEARCH_OMNIBOX_ANSWER_RESULT_H_
 
+#include "ash/public/cpp/style/color_mode_observer.h"
 #include "base/memory/weak_ptr.h"
 #include "chrome/browser/bitmap_fetcher/bitmap_fetcher_delegate.h"
 #include "chrome/browser/ui/app_list/search/chrome_search_result.h"
@@ -21,7 +22,8 @@
 // cards at the top of the categorical search launcher, so are separated in the
 // UI from other Omnibox results.
 class OmniboxAnswerResult : public ChromeSearchResult,
-                            public BitmapFetcherDelegate {
+                            public BitmapFetcherDelegate,
+                            public ash::ColorModeObserver {
  public:
   OmniboxAnswerResult(Profile* profile,
                       AppListControllerDelegate* list_controller,
@@ -40,6 +42,9 @@
   void OnFetchComplete(const GURL& url, const SkBitmap* bitmap) override;
 
  private:
+  // ash::ColorModeObserver:
+  void OnColorModeChanged(bool dark_mode_enabled) override;
+
   void UpdateIcon();
   // Updates title and details for the productivity launcher.
   void UpdateTitleAndDetails();
diff --git a/chrome/browser/ui/ash/system_tray_client_impl.cc b/chrome/browser/ui/ash/system_tray_client_impl.cc
index 8bbc3c6..c7fb7a28 100644
--- a/chrome/browser/ui/ash/system_tray_client_impl.cc
+++ b/chrome/browser/ui/ash/system_tray_client_impl.cc
@@ -56,13 +56,13 @@
 #include "chrome/browser/upgrade_detector/upgrade_detector.h"
 #include "chrome/browser/web_applications/web_app_id_constants.h"
 #include "chrome/common/url_constants.h"
+#include "chromeos/ash/components/network/onc/network_onc_utils.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/dbus/session_manager/session_manager_client.h"
 #include "chromeos/network/network_handler.h"
 #include "chromeos/network/network_state.h"
 #include "chromeos/network/network_state_handler.h"
 #include "chromeos/network/network_util.h"
-#include "chromeos/network/onc/network_onc_utils.h"
 #include "chromeos/network/tether_constants.h"
 #include "components/session_manager/core/session_manager.h"
 #include "components/session_manager/core/session_manager_observer.h"
diff --git a/chrome/browser/ui/extensions/OWNERS b/chrome/browser/ui/extensions/OWNERS
index 9e241ac..e4f2c270 100644
--- a/chrome/browser/ui/extensions/OWNERS
+++ b/chrome/browser/ui/extensions/OWNERS
@@ -5,3 +5,4 @@
 # Extension-y stuff
 finnur@chromium.org
 rdevlin.cronin@chromium.org
+emiliapaz@chromium.org
diff --git a/chrome/browser/ui/singleton_tabs.cc b/chrome/browser/ui/singleton_tabs.cc
index 98263487..9396744 100644
--- a/chrome/browser/ui/singleton_tabs.cc
+++ b/chrome/browser/ui/singleton_tabs.cc
@@ -35,11 +35,6 @@
   Navigate(&params);
 }
 
-void ShowSingletonTabRespectRef(Browser* browser, const GURL& url) {
-  NavigateParams params(GetSingletonTabNavigateParams(browser, url));
-  Navigate(&params);
-}
-
 void ShowSingletonTabOverwritingNTP(Browser* browser, NavigateParams* params) {
   DCHECK(browser);
   DCHECK_EQ(params->disposition, WindowOpenDisposition::SINGLETON_TAB);
diff --git a/chrome/browser/ui/singleton_tabs.h b/chrome/browser/ui/singleton_tabs.h
index a69681d..3295a4f 100644
--- a/chrome/browser/ui/singleton_tabs.h
+++ b/chrome/browser/ui/singleton_tabs.h
@@ -20,9 +20,6 @@
 // is created.
 void ShowSingletonTab(Browser* browser, const GURL& url);
 
-// Same as ShowSingletonTab, but does not ignore ref.
-void ShowSingletonTabRespectRef(Browser* browser, const GURL& url);
-
 // As ShowSingletonTab, but if the current tab is the new tab page or
 // about:blank, then overwrite it with the passed contents.
 void ShowSingletonTabOverwritingNTP(Browser* browser, NavigateParams* params);
diff --git a/chrome/browser/ui/views/tabs/tab_drag_controller.cc b/chrome/browser/ui/views/tabs/tab_drag_controller.cc
index 3047ed88..fa43f05 100644
--- a/chrome/browser/ui/views/tabs/tab_drag_controller.cc
+++ b/chrome/browser/ui/views/tabs/tab_drag_controller.cc
@@ -2516,10 +2516,23 @@
   int left_most_selected_x_position =
       left_most_selected_tab->x() + tab_left_inset;
 
-  if ((left_most_selected_x_position <= left_edge - buffer) &&
-      left_group.has_value() &&
+  if (left_group.has_value() &&
       !attached_model->IsGroupCollapsed(left_group.value())) {
-    return left_group;
+    // Take the dragged tabs out of left_group if they are at the rightmost edge
+    // of the tabstrip. This happens when the tabstrip is full and the dragged
+    // tabs are as far right as they can go without being pulled out into a new
+    // window. In this case, since the dragged tabs can't move further right in
+    // the tabstrip, it will never go "beyond" the left_group and therefore
+    // never leave it unless we add this check. See crbug.com/1134376.
+    // TODO(crbug/1329344): Update this to work better with Tab Scrolling once
+    // dragging near the end of the tabstrip is cleaner.
+    if (attached_context_->GetTabAt(selected.back())->bounds().right() >=
+        attached_context_->TabDragAreaEndX()) {
+      return absl::nullopt;
+    }
+
+    if (left_most_selected_x_position <= left_edge - buffer)
+      return left_group;
   }
   if ((left_most_selected_x_position >= left_edge + buffer) &&
       right_group.has_value() &&
diff --git a/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.cc b/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.cc
index c1c015a..7dd3710b 100644
--- a/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.cc
+++ b/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.cc
@@ -913,7 +913,7 @@
   EXPECT_EQ(group_model->GetTabGroup(group1)->ListTabs(), gfx::Range(0, 4));
 }
 
-// Creates a browser with four tabs. The last tab is in Tab Group 1. The
+// Creates a browser with five tabs. The fourth tab is in Tab Group 1. The
 // second tab is in Tab Group 2. Dragging the third tab over one to the right
 // will result in the tab joining Tab Group 1. Then dragging the second tab
 // over one to the right will result in the tab joining Tab Group 1. Then
@@ -927,7 +927,7 @@
   TabStripModel* model = browser()->tab_strip_model();
   TabGroupModel* group_model = model->group_model();
 
-  AddTabsAndResetBrowser(browser(), 3);
+  AddTabsAndResetBrowser(browser(), 4);
   tab_groups::TabGroupId group1 = model->AddToNewGroup({3});
   model->AddToNewGroup({1});
   StopAnimating(tab_strip);
@@ -940,7 +940,7 @@
   ASSERT_TRUE(ReleaseInput());
   StopAnimating(tab_strip);
 
-  EXPECT_EQ("0 1 3 2", IDString(model));
+  EXPECT_EQ("0 1 3 2 4", IDString(model));
   EXPECT_EQ(group_model->GetTabGroup(group1)->ListTabs(), gfx::Range(2, 4));
 
   // Dragging the tab in the first index to the tab in the second index switches
@@ -950,7 +950,7 @@
   ASSERT_TRUE(ReleaseInput());
   StopAnimating(tab_strip);
 
-  EXPECT_EQ("0 3 1 2", IDString(model));
+  EXPECT_EQ("0 3 1 2 4", IDString(model));
   EXPECT_EQ(group_model->GetTabGroup(group1)->ListTabs(), gfx::Range(1, 4));
 
   // Dragging the tab in the zero-th index to the tab in the first index
@@ -960,10 +960,37 @@
   ASSERT_TRUE(ReleaseInput());
   StopAnimating(tab_strip);
 
-  EXPECT_EQ("3 0 1 2", IDString(model));
+  EXPECT_EQ("3 0 1 2 4", IDString(model));
   EXPECT_EQ(group_model->GetTabGroup(group1)->ListTabs(), gfx::Range(0, 4));
 }
 
+// Creates a browser with five tabs. The last two tabs are in a Tab Group.
+// Dragging the first tab past the last slot should allow it to exit the group.
+IN_PROC_BROWSER_TEST_P(DetachToBrowserTabDragControllerTest,
+                       DragSingleTabRightOfRightmostGroup) {
+  ASSERT_TRUE(browser()->tab_strip_model()->SupportsTabGroups());
+
+  TabStrip* tab_strip = GetTabStripForBrowser(browser());
+  TabStripModel* model = browser()->tab_strip_model();
+  TabGroupModel* group_model = model->group_model();
+
+  AddTabsAndResetBrowser(browser(), 4);
+  tab_groups::TabGroupId group = model->AddToNewGroup({3, 4});
+  StopAnimating(tab_strip);
+  EnsureFocusToTabStrip(tab_strip);
+
+  // Dragging the first tab past the last slot brings it to the end of the
+  // tabstrip, where it should not be in a group.
+  ASSERT_TRUE(PressInput(GetCenterInScreenCoordinates(tab_strip->tab_at(0))));
+  ASSERT_TRUE(DragInputTo(GetCenterInScreenCoordinates(tab_strip->tab_at(4)) +
+                          gfx::Vector2d(1, 0)));
+  ASSERT_TRUE(ReleaseInput());
+  StopAnimating(tab_strip);
+
+  EXPECT_EQ("1 2 3 4 0", IDString(model));
+  EXPECT_EQ(group_model->GetTabGroup(group)->ListTabs(), gfx::Range(2, 4));
+}
+
 // Creates a browser with four tabs each in its own group. Selecting and
 // dragging the first and third tabs right at the first tab will result in the
 // tabs joining the same group as the tab in the second position. Then dragging
diff --git a/chrome/browser/ui/webui/app_management/app_management_page_handler.cc b/chrome/browser/ui/webui/app_management/app_management_page_handler.cc
index 2f36872..33bc72f 100644
--- a/chrome/browser/ui/webui/app_management/app_management_page_handler.cc
+++ b/chrome/browser/ui/webui/app_management/app_management_page_handler.cc
@@ -68,10 +68,8 @@
     app_constants::kLacrosAppId,
 };
 
-#if !BUILDFLAG(IS_CHROMEOS)
 const char kFileHandlingLearnMore[] =
     "https://support.google.com/chrome/?p=pwa_default_associations";
-#endif
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
 constexpr char const* kAppIdsWithHiddenStoragePermission[] = {
@@ -443,15 +441,16 @@
         std::move(run_on_os_login.value()));
   }
 
-// Speculative fix for crbug.com/1315958
-#if !BUILDFLAG(IS_CHROMEOS)
   if (update.AppType() == apps::AppType::kWeb) {
-    auto* provider =
-        web_app::WebAppProvider::GetForLocalAppsUnchecked(profile_);
-    const bool fh_enabled =
-        !provider->registrar().IsAppFileHandlerPermissionBlocked(app->id);
     std::string file_handling_types;
     std::string file_handling_types_label;
+    bool fh_enabled = false;
+// Speculative fix for crbug.com/1315958
+#if !BUILDFLAG(IS_CHROMEOS)
+    auto* provider =
+        web_app::WebAppProvider::GetForLocalAppsUnchecked(profile_);
+    fh_enabled =
+        !provider->registrar().IsAppFileHandlerPermissionBlocked(app->id);
     if (provider->os_integration_manager().IsFileHandlingAPIAvailable(
             app->id) &&
         !provider->registrar().IsSystemApp(app->id) &&
@@ -478,6 +477,10 @@
                   static_cast<int>(truncated_extensions.size()),
               "LINK", "#"));
     }
+
+    app->hide_window_mode = provider->registrar().IsIsolated(app->id);
+#endif
+
     absl::optional<GURL> learn_more_url;
     if (!CanShowDefaultAppAssociationsUi())
       learn_more_url = GURL(kFileHandlingLearnMore);
@@ -485,10 +488,7 @@
     app->file_handling_state = app_management::mojom::FileHandlingState::New(
         fh_enabled, /*is_managed=*/false, file_handling_types,
         file_handling_types_label, learn_more_url);
-
-    app->hide_window_mode = provider->registrar().IsIsolated(app->id);
   }
-#endif
 
   app->publisher_id = update.PublisherId();
 
diff --git a/chrome/browser/ui/webui/chromeos/network_ui.cc b/chrome/browser/ui/webui/chromeos/network_ui.cc
index f6eb7834..914ad73 100644
--- a/chrome/browser/ui/webui/chromeos/network_ui.cc
+++ b/chrome/browser/ui/webui/chromeos/network_ui.cc
@@ -32,6 +32,7 @@
 #include "chrome/grit/generated_resources.h"
 #include "chrome/grit/network_ui_resources.h"
 #include "chrome/grit/network_ui_resources_map.h"
+#include "chromeos/ash/components/network/onc/network_onc_utils.h"
 #include "chromeos/network/cellular_esim_profile_handler.h"
 #include "chromeos/network/cellular_esim_profile_handler_impl.h"
 #include "chromeos/network/cellular_esim_uninstall_handler.h"
@@ -42,7 +43,6 @@
 #include "chromeos/network/network_device_handler.h"
 #include "chromeos/network/network_state.h"
 #include "chromeos/network/network_state_handler.h"
-#include "chromeos/network/onc/network_onc_utils.h"
 #include "chromeos/services/network_config/public/mojom/cros_network_config.mojom.h"
 #include "chromeos/services/network_health/public/mojom/network_diagnostics.mojom.h"
 #include "chromeos/services/network_health/public/mojom/network_health.mojom.h"
diff --git a/chrome/browser/ui/webui/chromeos/onc_import_message_handler.cc b/chrome/browser/ui/webui/chromeos/onc_import_message_handler.cc
index ddc6867..9c76401 100644
--- a/chrome/browser/ui/webui/chromeos/onc_import_message_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/onc_import_message_handler.cc
@@ -14,10 +14,10 @@
 #include "chrome/browser/net/nss_service.h"
 #include "chrome/browser/net/nss_service_factory.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chromeos/ash/components/network/onc/network_onc_utils.h"
+#include "chromeos/ash/components/network/onc/onc_certificate_importer_impl.h"
 #include "chromeos/components/onc/onc_parsed_certificates.h"
 #include "chromeos/components/onc/onc_utils.h"
-#include "chromeos/network/onc/network_onc_utils.h"
-#include "chromeos/network/onc/onc_certificate_importer_impl.h"
 #include "components/onc/onc_constants.h"
 #include "components/policy/core/browser/policy_conversions.h"
 #include "content/public/browser/browser_task_traits.h"
diff --git a/chrome/browser/ui/webui/history_clusters/history_clusters_handler.cc b/chrome/browser/ui/webui/history_clusters/history_clusters_handler.cc
index 7938188..d612ba5 100644
--- a/chrome/browser/ui/webui/history_clusters/history_clusters_handler.cc
+++ b/chrome/browser/ui/webui/history_clusters/history_clusters_handler.cc
@@ -23,6 +23,7 @@
 #include "chrome/browser/ui/browser_finder.h"
 #include "chrome/common/pref_names.h"
 #include "components/history/core/browser/history_types.h"
+#include "components/history_clusters/core/cluster_metrics_utils.h"
 #include "components/history_clusters/core/config.h"
 #include "components/history_clusters/core/features.h"
 #include "components/history_clusters/core/history_clusters_prefs.h"
@@ -345,4 +346,37 @@
   page_->OnVisitsRemoved(std::move(visits));
 }
 
+void HistoryClustersHandler::RecordVisitAction(mojom::VisitAction visit_action,
+                                               uint32_t visit_index,
+                                               mojom::VisitType visit_type) {
+  HistoryClustersMetricsLogger::GetOrCreateForPage(
+      web_contents_->GetPrimaryPage())
+      ->RecordVisitAction(static_cast<VisitAction>(visit_action), visit_index,
+                          static_cast<VisitType>(visit_type));
+}
+
+void HistoryClustersHandler::RecordClusterAction(
+    mojom::ClusterAction cluster_action,
+    uint32_t cluster_index) {
+  HistoryClustersMetricsLogger::GetOrCreateForPage(
+      web_contents_->GetPrimaryPage())
+      ->RecordClusterAction(static_cast<ClusterAction>(cluster_action),
+                            cluster_index);
+}
+
+void HistoryClustersHandler::RecordRelatedSearchAction(
+    mojom::RelatedSearchAction action,
+    uint32_t related_search_index) {
+  HistoryClustersMetricsLogger::GetOrCreateForPage(
+      web_contents_->GetPrimaryPage())
+      ->RecordRelatedSearchAction(static_cast<RelatedSearchAction>(action),
+                                  related_search_index);
+}
+
+void HistoryClustersHandler::RecordToggledVisibility(bool visible) {
+  HistoryClustersMetricsLogger::GetOrCreateForPage(
+      web_contents_->GetPrimaryPage())
+      ->RecordToggledVisibility(visible);
+}
+
 }  // namespace history_clusters
diff --git a/chrome/browser/ui/webui/history_clusters/history_clusters_handler.h b/chrome/browser/ui/webui/history_clusters/history_clusters_handler.h
index 23bacc5..e2e1559 100644
--- a/chrome/browser/ui/webui/history_clusters/history_clusters_handler.h
+++ b/chrome/browser/ui/webui/history_clusters/history_clusters_handler.h
@@ -64,6 +64,14 @@
   void RemoveVisits(std::vector<mojom::URLVisitPtr> visits,
                     RemoveVisitsCallback callback) override;
   void OpenVisitUrlsInTabGroup(std::vector<mojom::URLVisitPtr> visits) override;
+  void RecordVisitAction(mojom::VisitAction visit_action,
+                         uint32_t visit_index,
+                         mojom::VisitType visit_type) override;
+  void RecordRelatedSearchAction(mojom::RelatedSearchAction action,
+                                 uint32_t related_search_index) override;
+  void RecordClusterAction(mojom::ClusterAction cluster_action,
+                           uint32_t cluster_index) override;
+  void RecordToggledVisibility(bool visible) override;
 
   // HistoryClustersService::Observer:
   void OnDebugMessage(const std::string& message) override;
diff --git a/chrome/browser/ui/webui/history_clusters/history_clusters_handler_browsertest.cc b/chrome/browser/ui/webui/history_clusters/history_clusters_handler_browsertest.cc
index a3e6652b..6e158b1 100644
--- a/chrome/browser/ui/webui/history_clusters/history_clusters_handler_browsertest.cc
+++ b/chrome/browser/ui/webui/history_clusters/history_clusters_handler_browsertest.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/ui/webui/history_clusters/history_clusters_handler.h"
 
 #include "base/memory/raw_ptr.h"
+#include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
@@ -91,4 +92,68 @@
   ASSERT_EQ(33, tab_strip_model->GetTabCount());
 }
 
+IN_PROC_BROWSER_TEST_F(HistoryClustersHandlerBrowserTest,
+                       RecordUIVisitActions) {
+  auto* tab_strip_model = browser()->tab_strip_model();
+  ASSERT_EQ(1, tab_strip_model->GetTabCount());
+
+  base::HistogramTester histogram_tester;
+
+  handler_->RecordVisitAction(mojom::VisitAction::kClicked, 0,
+                              mojom::VisitType::kNonSRP);
+  histogram_tester.ExpectBucketCount("History.Clusters.UIActions.Visit.Clicked",
+                                     0, 1);
+  histogram_tester.ExpectBucketCount(
+      "History.Clusters.UIActions.nonSRPVisit.Clicked", 0, 1);
+
+  handler_->RecordVisitAction(mojom::VisitAction::kDeleted, 0,
+                              mojom::VisitType::kSRP);
+  histogram_tester.ExpectBucketCount("History.Clusters.UIActions.Visit.Deleted",
+                                     0, 1);
+  histogram_tester.ExpectBucketCount(
+      "History.Clusters.UIActions.SRPVisit.Deleted", 0, 1);
+}
+
+IN_PROC_BROWSER_TEST_F(HistoryClustersHandlerBrowserTest,
+                       RecordUIClusterActions) {
+  auto* tab_strip_model = browser()->tab_strip_model();
+  ASSERT_EQ(1, tab_strip_model->GetTabCount());
+
+  base::HistogramTester histogram_tester;
+
+  handler_->RecordClusterAction(mojom::ClusterAction::kVisitClicked, 0);
+  histogram_tester.ExpectUniqueSample(
+      "History.Clusters.UIActions.Cluster.VisitClicked", 0, 1);
+  handler_->RecordClusterAction(mojom::ClusterAction::kDeleted, 0);
+  histogram_tester.ExpectUniqueSample(
+      "History.Clusters.UIActions.Cluster.Deleted", 0, 1);
+}
+
+IN_PROC_BROWSER_TEST_F(HistoryClustersHandlerBrowserTest,
+                       RecordUIRelatedSearchActions) {
+  auto* tab_strip_model = browser()->tab_strip_model();
+  ASSERT_EQ(1, tab_strip_model->GetTabCount());
+
+  base::HistogramTester histogram_tester;
+
+  handler_->RecordRelatedSearchAction(mojom::RelatedSearchAction::kClicked, 0);
+  histogram_tester.ExpectUniqueSample(
+      "History.Clusters.UIActions.RelatedSearch.Clicked", 0, 1);
+}
+
+IN_PROC_BROWSER_TEST_F(HistoryClustersHandlerBrowserTest,
+                       RecordUIToggledVisibility) {
+  auto* tab_strip_model = browser()->tab_strip_model();
+  ASSERT_EQ(1, tab_strip_model->GetTabCount());
+
+  base::HistogramTester histogram_tester;
+
+  handler_->RecordToggledVisibility(true);
+  histogram_tester.ExpectBucketCount(
+      "History.Clusters.UIActions.ToggledVisiblity", true, 1);
+  handler_->RecordToggledVisibility(false);
+  histogram_tester.ExpectBucketCount(
+      "History.Clusters.UIActions.ToggledVisiblity", false, 1);
+}
+
 }  // namespace history_clusters
diff --git a/chrome/browser/ui/webui/settings/chromeos/tts_handler.cc b/chrome/browser/ui/webui/settings/chromeos/tts_handler.cc
index 0105a30..8e61955f 100644
--- a/chrome/browser/ui/webui/settings/chromeos/tts_handler.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/tts_handler.cc
@@ -169,6 +169,9 @@
   web_ui()->RegisterMessageCallback(
       "wakeTtsEngine",
       base::BindRepeating(&TtsHandler::WakeTtsEngine, base::Unretained(this)));
+  web_ui()->RegisterMessageCallback(
+      "refreshTtsVoices", base::BindRepeating(&TtsHandler::RefreshTtsVoices,
+                                              base::Unretained(this)));
 }
 
 void TtsHandler::OnJavascriptAllowed() {
@@ -204,6 +207,10 @@
   OnVoicesChanged();
 }
 
+void TtsHandler::RefreshTtsVoices(const base::Value::List& args) {
+  content::TtsController::GetInstance()->RefreshVoices();
+}
+
 void TtsHandler::RemoveTtsControllerDelegates() {
   content::TtsController::GetInstance()->RemoveVoicesChangedDelegate(this);
   content::TtsController::GetInstance()->RemoveUtteranceEventDelegate(this);
diff --git a/chrome/browser/ui/webui/settings/chromeos/tts_handler.h b/chrome/browser/ui/webui/settings/chromeos/tts_handler.h
index 411a3fa..2afab95 100644
--- a/chrome/browser/ui/webui/settings/chromeos/tts_handler.h
+++ b/chrome/browser/ui/webui/settings/chromeos/tts_handler.h
@@ -45,6 +45,7 @@
  private:
   void WakeTtsEngine(const base::Value::List& args);
   void OnTtsEngineAwake(bool success);
+  void RefreshTtsVoices(const base::Value::List& args);
   int GetVoiceLangMatchScore(const content::VoiceData* voice,
                              const std::string& app_locale);
   void RemoveTtsControllerDelegates();
diff --git a/chrome/browser/usb/frame_usb_services.cc b/chrome/browser/usb/frame_usb_services.cc
index 4dca5c38..57cfdac 100644
--- a/chrome/browser/usb/frame_usb_services.cc
+++ b/chrome/browser/usb/frame_usb_services.cc
@@ -8,18 +8,11 @@
 
 #include "build/build_config.h"
 #include "chrome/browser/usb/usb_tab_helper.h"
+#include "content/public/browser/web_contents.h"
 #include "content/public/common/content_features.h"
 #include "mojo/public/cpp/bindings/message.h"
 #include "third_party/blink/public/mojom/permissions_policy/permissions_policy.mojom.h"
 
-#if BUILDFLAG(IS_ANDROID)
-#include "chrome/browser/android/usb/web_usb_chooser_android.h"
-#else
-#include "chrome/browser/ui/browser_finder.h"
-#include "chrome/browser/ui/tabs/tab_strip_model.h"
-#include "chrome/browser/usb/web_usb_chooser_desktop.h"
-#endif  // BUILDFLAG(IS_ANDROID)
-
 using content::RenderFrameHost;
 using content::WebContents;
 
@@ -41,16 +34,6 @@
 
 FrameUsbServices::~FrameUsbServices() = default;
 
-void FrameUsbServices::InitializeWebUsbChooser() {
-  if (!usb_chooser_) {
-#if BUILDFLAG(IS_ANDROID)
-    usb_chooser_ = std::make_unique<WebUsbChooserAndroid>(&render_frame_host());
-#else
-    usb_chooser_ = std::make_unique<WebUsbChooserDesktop>(&render_frame_host());
-#endif  // BUILDFLAG(IS_ANDROID)
-  }
-}
-
 void FrameUsbServices::InitializeWebUsbService(
     mojo::PendingReceiver<blink::mojom::WebUsbService> receiver) {
   if (!AllowedByPermissionsPolicy()) {
@@ -58,10 +41,9 @@
     return;
   }
 
-  InitializeWebUsbChooser();
   if (!web_usb_service_) {
-    web_usb_service_ = std::make_unique<WebUsbServiceImpl>(
-        &render_frame_host(), usb_chooser_->GetWeakPtr());
+    web_usb_service_ =
+        std::make_unique<WebUsbServiceImpl>(&render_frame_host());
   }
   web_usb_service_->BindReceiver(std::move(receiver));
 }
diff --git a/chrome/browser/usb/frame_usb_services.h b/chrome/browser/usb/frame_usb_services.h
index f70d1da..4578a70d 100644
--- a/chrome/browser/usb/frame_usb_services.h
+++ b/chrome/browser/usb/frame_usb_services.h
@@ -8,7 +8,6 @@
 #include "chrome/browser/usb/web_usb_service_impl.h"
 #include "content/public/browser/document_user_data.h"
 #include "content/public/browser/render_frame_host.h"
-#include "content/public/browser/web_contents.h"
 
 namespace blink {
 namespace mojom {
@@ -16,8 +15,6 @@
 }
 }  // namespace blink
 
-class WebUsbChooser;
-
 // Collection of USB-related document-associated services (e.g.
 // WebUsbServiceImpl) with the lifetime bound to the lifetime of the document.
 class FrameUsbServices : public content::DocumentUserData<FrameUsbServices> {
@@ -33,14 +30,11 @@
 
   friend class content::DocumentUserData<FrameUsbServices>;
 
-  void InitializeWebUsbChooser();
-
   void InitializeWebUsbService(
       mojo::PendingReceiver<blink::mojom::WebUsbService> receiver);
 
   bool AllowedByPermissionsPolicy() const;
 
-  std::unique_ptr<WebUsbChooser> usb_chooser_;
   std::unique_ptr<WebUsbServiceImpl> web_usb_service_;
 
   DOCUMENT_USER_DATA_KEY_DECL();
diff --git a/chrome/browser/usb/usb_browsertest.cc b/chrome/browser/usb/usb_browsertest.cc
index 057feca..ad9bad5 100644
--- a/chrome/browser/usb/usb_browsertest.cc
+++ b/chrome/browser/usb/usb_browsertest.cc
@@ -6,6 +6,7 @@
 #include <string>
 #include <utility>
 
+#include "base/bind.h"
 #include "base/memory/raw_ptr.h"
 #include "base/memory/ref_counted.h"
 #include "base/task/thread_pool/thread_pool_instance.h"
@@ -97,15 +98,15 @@
 
 class FakeUsbChooser : public WebUsbChooser {
  public:
-  explicit FakeUsbChooser(RenderFrameHost* render_frame_host)
-      : WebUsbChooser(render_frame_host) {}
+  FakeUsbChooser() = default;
 
   FakeUsbChooser(const FakeUsbChooser&) = delete;
   FakeUsbChooser& operator=(const FakeUsbChooser&) = delete;
 
-  ~FakeUsbChooser() override {}
+  ~FakeUsbChooser() override = default;
 
-  void ShowChooser(std::unique_ptr<UsbChooserController> controller) override {
+  void ShowChooser(content::RenderFrameHost* render_frame_host,
+                   std::unique_ptr<UsbChooserController> controller) override {
     // Device list initialization in UsbChooserController may completed before
     // having a valid view in which case OnOptionsInitialized() has no chance to
     // be triggered, so select the first option directly if options are ready.
@@ -114,15 +115,19 @@
     else
       new FakeChooserView(std::move(controller));
   }
-
-  base::WeakPtr<WebUsbChooser> GetWeakPtr() override {
-    return weak_factory_.GetWeakPtr();
-  }
-
- private:
-  base::WeakPtrFactory<FakeUsbChooser> weak_factory_{this};
 };
 
+std::unique_ptr<WebUsbChooser> RunChooser(
+    RenderFrameHost& frame,
+    std::vector<device::mojom::UsbDeviceFilterPtr> filters,
+    blink::mojom::WebUsbService::GetPermissionCallback callback) {
+  auto controller = std::make_unique<UsbChooserController>(
+      &frame, std::move(filters), std::move(callback));
+  auto chooser = std::make_unique<FakeUsbChooser>();
+  chooser->ShowChooser(&frame, std::move(controller));
+  return chooser;
+}
+
 class TestContentBrowserClient : public ChromeContentBrowserClient {
  public:
   TestContentBrowserClient() {}
@@ -140,9 +145,9 @@
       ChromeContentBrowserClient::CreateWebUsbService(render_frame_host,
                                                       std::move(receiver));
     } else {
-      usb_chooser_ = std::make_unique<FakeUsbChooser>(render_frame_host);
-      web_usb_service_ = std::make_unique<WebUsbServiceImpl>(
-          render_frame_host, usb_chooser_->GetWeakPtr());
+      web_usb_service_ = std::make_unique<WebUsbServiceImpl>(render_frame_host);
+      web_usb_service_->SetChooserFactoryForTesting(
+          base::BindRepeating(&RunChooser));
       web_usb_service_->BindReceiver(std::move(receiver));
     }
   }
@@ -152,7 +157,6 @@
  private:
   bool use_real_chooser_ = false;
   std::unique_ptr<WebUsbServiceImpl> web_usb_service_;
-  std::unique_ptr<WebUsbChooser> usb_chooser_;
 };
 
 scoped_refptr<device::FakeUsbDeviceInfo> CreateSmartCardDevice() {
diff --git a/chrome/browser/usb/web_usb_chooser.cc b/chrome/browser/usb/web_usb_chooser.cc
index efd0f7f8..1388d8d 100644
--- a/chrome/browser/usb/web_usb_chooser.cc
+++ b/chrome/browser/usb/web_usb_chooser.cc
@@ -6,38 +6,29 @@
 
 #include <utility>
 
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/usb/usb_chooser_context.h"
-#include "chrome/browser/usb/usb_chooser_context_factory.h"
+#include "build/build_config.h"
 #include "chrome/browser/usb/usb_chooser_controller.h"
-#include "content/public/browser/browser_thread.h"
-#include "content/public/browser/render_frame_host.h"
-#include "content/public/browser/web_contents.h"
-#include "services/device/public/mojom/usb_enumeration_options.mojom.h"
 
-WebUsbChooser::WebUsbChooser(content::RenderFrameHost* render_frame_host)
-    : render_frame_host_(render_frame_host) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  DCHECK(render_frame_host);
+#if BUILDFLAG(IS_ANDROID)
+#include "chrome/browser/android/usb/web_usb_chooser_android.h"
+#else
+#include "chrome/browser/usb/web_usb_chooser_desktop.h"
+#endif  // BUILDFLAG(IS_ANDROID)
+
+// static
+std::unique_ptr<WebUsbChooser> WebUsbChooser::Create(
+    content::RenderFrameHost* render_frame_host,
+    std::unique_ptr<UsbChooserController> controller) {
+  std::unique_ptr<WebUsbChooser> chooser;
+#if BUILDFLAG(IS_ANDROID)
+  chooser = std::make_unique<WebUsbChooserAndroid>();
+#else
+  chooser = std::make_unique<WebUsbChooserDesktop>();
+#endif  // BUILDFLAG(IS_ANDROID)
+  chooser->ShowChooser(render_frame_host, std::move(controller));
+  return chooser;
 }
 
-WebUsbChooser::~WebUsbChooser() {}
+WebUsbChooser::~WebUsbChooser() = default;
 
-void WebUsbChooser::GetPermission(
-    std::vector<device::mojom::UsbDeviceFilterPtr> device_filters,
-    blink::mojom::WebUsbService::GetPermissionCallback callback) {
-  DCHECK(!render_frame_host_->IsNestedWithinFencedFrame());
-  url::Origin origin =
-      render_frame_host_->GetMainFrame()->GetLastCommittedOrigin();
-  auto* profile =
-      Profile::FromBrowserContext(render_frame_host_->GetBrowserContext());
-  auto* context = UsbChooserContextFactory::GetForProfile(profile);
-  if (!context->CanRequestObjectPermission(origin)) {
-    std::move(callback).Run(nullptr);
-    return;
-  }
-
-  auto controller = std::make_unique<UsbChooserController>(
-      render_frame_host_, std::move(device_filters), std::move(callback));
-  ShowChooser(std::move(controller));
-}
+WebUsbChooser::WebUsbChooser() = default;
diff --git a/chrome/browser/usb/web_usb_chooser.h b/chrome/browser/usb/web_usb_chooser.h
index 434d1d4..9dfa5b4 100644
--- a/chrome/browser/usb/web_usb_chooser.h
+++ b/chrome/browser/usb/web_usb_chooser.h
@@ -6,11 +6,6 @@
 #define CHROME_BROWSER_USB_WEB_USB_CHOOSER_H_
 
 #include <memory>
-#include <vector>
-
-#include "base/memory/raw_ptr.h"
-#include "base/memory/weak_ptr.h"
-#include "third_party/blink/public/mojom/usb/web_usb_service.mojom.h"
 
 namespace content {
 class RenderFrameHost;
@@ -22,26 +17,21 @@
 // to access a certain device.
 class WebUsbChooser {
  public:
-  explicit WebUsbChooser(content::RenderFrameHost* render_frame_host);
+  static std::unique_ptr<WebUsbChooser> Create(
+      content::RenderFrameHost* frame,
+      std::unique_ptr<UsbChooserController> controller);
 
   WebUsbChooser(const WebUsbChooser&) = delete;
   WebUsbChooser& operator=(const WebUsbChooser&) = delete;
 
   virtual ~WebUsbChooser();
 
-  void GetPermission(
-      std::vector<device::mojom::UsbDeviceFilterPtr> device_filters,
-      blink::mojom::WebUsbService::GetPermissionCallback callback);
+ protected:
+  WebUsbChooser();
 
   virtual void ShowChooser(
+      content::RenderFrameHost* render_frame_host,
       std::unique_ptr<UsbChooserController> controller) = 0;
-
-  virtual base::WeakPtr<WebUsbChooser> GetWeakPtr() = 0;
-
-  content::RenderFrameHost* render_frame_host() { return render_frame_host_; }
-
- private:
-  const raw_ptr<content::RenderFrameHost> render_frame_host_;
 };
 
 #endif  // CHROME_BROWSER_USB_WEB_USB_CHOOSER_H_
diff --git a/chrome/browser/usb/web_usb_chooser_desktop.cc b/chrome/browser/usb/web_usb_chooser_desktop.cc
index 9261616..b717c67 100644
--- a/chrome/browser/usb/web_usb_chooser_desktop.cc
+++ b/chrome/browser/usb/web_usb_chooser_desktop.cc
@@ -7,23 +7,15 @@
 #include <utility>
 
 #include "chrome/browser/ui/browser_dialogs.h"
-#include "chrome/browser/ui/browser_finder.h"
 #include "chrome/browser/usb/usb_chooser_controller.h"
-#include "content/public/browser/web_contents.h"
 
-WebUsbChooserDesktop::WebUsbChooserDesktop(
-    content::RenderFrameHost* render_frame_host)
-    : WebUsbChooser(render_frame_host) {}
+WebUsbChooserDesktop::WebUsbChooserDesktop() = default;
 
 WebUsbChooserDesktop::~WebUsbChooserDesktop() = default;
 
 void WebUsbChooserDesktop::ShowChooser(
+    content::RenderFrameHost* render_frame_host,
     std::unique_ptr<UsbChooserController> controller) {
-  closure_runner_.RunAndReset();
   closure_runner_.ReplaceClosure(chrome::ShowDeviceChooserDialog(
-      render_frame_host(), std::move(controller)));
-}
-
-base::WeakPtr<WebUsbChooser> WebUsbChooserDesktop::GetWeakPtr() {
-  return weak_factory_.GetWeakPtr();
+      render_frame_host, std::move(controller)));
 }
diff --git a/chrome/browser/usb/web_usb_chooser_desktop.h b/chrome/browser/usb/web_usb_chooser_desktop.h
index 2d6578e1..838fb991 100644
--- a/chrome/browser/usb/web_usb_chooser_desktop.h
+++ b/chrome/browser/usb/web_usb_chooser_desktop.h
@@ -14,7 +14,7 @@
 // display the permission prompt.
 class WebUsbChooserDesktop : public WebUsbChooser {
  public:
-  explicit WebUsbChooserDesktop(content::RenderFrameHost* render_frame_host);
+  WebUsbChooserDesktop();
 
   WebUsbChooserDesktop(const WebUsbChooserDesktop&) = delete;
   WebUsbChooserDesktop& operator=(const WebUsbChooserDesktop&) = delete;
@@ -22,14 +22,11 @@
   ~WebUsbChooserDesktop() override;
 
   // WebUsbChooser implementation
-  void ShowChooser(std::unique_ptr<UsbChooserController> controller) override;
-
-  base::WeakPtr<WebUsbChooser> GetWeakPtr() override;
+  void ShowChooser(content::RenderFrameHost* render_frame_host,
+                   std::unique_ptr<UsbChooserController> controller) override;
 
  private:
-  base::ScopedClosureRunner closure_runner_{base::DoNothing()};
-
-  base::WeakPtrFactory<WebUsbChooserDesktop> weak_factory_{this};
+  base::ScopedClosureRunner closure_runner_;
 };
 
 #endif  // CHROME_BROWSER_USB_WEB_USB_CHOOSER_DESKTOP_H_
diff --git a/chrome/browser/usb/web_usb_service_impl.cc b/chrome/browser/usb/web_usb_service_impl.cc
index fc252130..0e0a7e4 100644
--- a/chrome/browser/usb/web_usb_service_impl.cc
+++ b/chrome/browser/usb/web_usb_service_impl.cc
@@ -15,6 +15,7 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/usb/usb_blocklist.h"
 #include "chrome/browser/usb/usb_chooser_context_factory.h"
+#include "chrome/browser/usb/usb_chooser_controller.h"
 #include "chrome/browser/usb/usb_tab_helper.h"
 #include "content/public/browser/browser_thread.h"
 #include "extensions/buildflags/buildflags.h"
@@ -73,6 +74,15 @@
   return false;
 }
 
+std::unique_ptr<WebUsbChooser> RunChooser(
+    content::RenderFrameHost& frame,
+    std::vector<device::mojom::UsbDeviceFilterPtr> filters,
+    WebUsbServiceImpl::GetPermissionCallback callback) {
+  auto controller = std::make_unique<UsbChooserController>(
+      &frame, std::move(filters), std::move(callback));
+  return WebUsbChooser::Create(&frame, std::move(controller));
+}
+
 }  // namespace
 
 // A UsbDeviceClient represents a UsbDevice pipe that has been passed to the
@@ -126,10 +136,8 @@
 };
 
 WebUsbServiceImpl::WebUsbServiceImpl(
-    content::RenderFrameHost* render_frame_host,
-    base::WeakPtr<WebUsbChooser> usb_chooser)
-    : render_frame_host_(render_frame_host),
-      usb_chooser_(std::move(usb_chooser)) {
+    content::RenderFrameHost* render_frame_host)
+    : render_frame_host_(render_frame_host) {
   DCHECK(render_frame_host_);
   content::WebContents* web_contents =
       content::WebContents::FromRenderFrameHost(render_frame_host_);
@@ -143,6 +151,8 @@
 
   receivers_.set_disconnect_handler(base::BindRepeating(
       &WebUsbServiceImpl::OnConnectionError, base::Unretained(this)));
+
+  chooser_factory_ = base::BindRepeating(&RunChooser);
 }
 
 WebUsbServiceImpl::~WebUsbServiceImpl() = default;
@@ -162,6 +172,11 @@
     permission_observation_.Observe(chooser_context_.get());
 }
 
+void WebUsbServiceImpl::SetChooserFactoryForTesting(
+    ChooserFactoryCallback chooser_factory) {
+  chooser_factory_ = std::move(chooser_factory);
+}
+
 bool WebUsbServiceImpl::HasDevicePermission(
     const device::mojom::UsbDeviceInfo& device_info) const {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
@@ -316,12 +331,14 @@
 void WebUsbServiceImpl::GetPermission(
     std::vector<device::mojom::UsbDeviceFilterPtr> device_filters,
     GetPermissionCallback callback) {
-  if (!usb_chooser_) {
+  if (!chooser_context_ ||
+      !chooser_context_->CanRequestObjectPermission(origin_)) {
     std::move(callback).Run(nullptr);
     return;
   }
 
-  usb_chooser_->GetPermission(std::move(device_filters), std::move(callback));
+  usb_chooser_ = chooser_factory_.Run(
+      *render_frame_host_, std::move(device_filters), std::move(callback));
 }
 
 void WebUsbServiceImpl::ForgetDevice(const std::string& guid,
diff --git a/chrome/browser/usb/web_usb_service_impl.h b/chrome/browser/usb/web_usb_service_impl.h
index 5c1f6be..0a0d708 100644
--- a/chrome/browser/usb/web_usb_service_impl.h
+++ b/chrome/browser/usb/web_usb_service_impl.h
@@ -20,7 +20,8 @@
 #include "mojo/public/cpp/bindings/receiver_set.h"
 #include "mojo/public/cpp/bindings/remote_set.h"
 #include "services/device/public/mojom/usb_device.mojom-forward.h"
-#include "third_party/blink/public/mojom/usb/web_usb_service.mojom-forward.h"
+#include "services/device/public/mojom/usb_enumeration_options.mojom.h"
+#include "third_party/blink/public/mojom/usb/web_usb_service.mojom.h"
 #include "url/origin.h"
 
 namespace content {
@@ -38,8 +39,13 @@
       public permissions::ObjectPermissionContextBase::PermissionObserver,
       public UsbChooserContext::DeviceObserver {
  public:
-  WebUsbServiceImpl(content::RenderFrameHost* render_frame_host,
-                    base::WeakPtr<WebUsbChooser> usb_chooser);
+  using ChooserFactoryCallback =
+      base::RepeatingCallback<std::unique_ptr<WebUsbChooser>(
+          content::RenderFrameHost&,
+          std::vector<device::mojom::UsbDeviceFilterPtr>,
+          WebUsbServiceImpl::GetPermissionCallback)>;
+
+  explicit WebUsbServiceImpl(content::RenderFrameHost* render_frame_host);
 
   WebUsbServiceImpl(const WebUsbServiceImpl&) = delete;
   WebUsbServiceImpl& operator=(const WebUsbServiceImpl&) = delete;
@@ -49,6 +55,9 @@
   void BindReceiver(
       mojo::PendingReceiver<blink::mojom::WebUsbService> receiver);
 
+  // Allow tests to define and create the WebUsbChooser.
+  void SetChooserFactoryForTesting(ChooserFactoryCallback chooser_factory);
+
  private:
   class UsbDeviceClient;
 
@@ -90,7 +99,7 @@
   void OnConnectionError();
 
   const raw_ptr<content::RenderFrameHost> render_frame_host_;
-  base::WeakPtr<WebUsbChooser> usb_chooser_;
+  std::unique_ptr<WebUsbChooser> usb_chooser_;
   raw_ptr<UsbChooserContext> chooser_context_;
   url::Origin origin_;
 
@@ -101,6 +110,8 @@
   // A UsbDeviceClient tracks a UsbDevice pipe that has been passed to Blink.
   std::vector<std::unique_ptr<UsbDeviceClient>> device_clients_;
 
+  ChooserFactoryCallback chooser_factory_;
+
   base::ScopedObservation<UsbChooserContext, UsbChooserContext::DeviceObserver>
       device_observation_{this};
   base::ScopedObservation<
diff --git a/chrome/build/linux.pgo.txt b/chrome/build/linux.pgo.txt
index 84065f8..589a64d 100644
--- a/chrome/build/linux.pgo.txt
+++ b/chrome/build/linux.pgo.txt
@@ -1 +1 @@
-chrome-linux-main-1653566255-a5554d80a78a9325625283d634d620f34f55fc8e.profdata
+chrome-linux-main-1653587488-d60b14ae1fa4042118b0bf3b4726c920abeb5fd9.profdata
diff --git a/chrome/build/mac-arm.pgo.txt b/chrome/build/mac-arm.pgo.txt
index 15d830e1..03564b48 100644
--- a/chrome/build/mac-arm.pgo.txt
+++ b/chrome/build/mac-arm.pgo.txt
@@ -1 +1 @@
-chrome-mac-arm-main-1653566255-300a8e5231c0420e085d40a875d1d0916634d103.profdata
+chrome-mac-arm-main-1653609490-214c9b5cdb017b7b3bb5893c5de67034564867b6.profdata
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt
index f7e7dba..19a0218 100644
--- a/chrome/build/mac.pgo.txt
+++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@
-chrome-mac-main-1653566255-582816f27c2206b67a04fc3217422c2cab5d7580.profdata
+chrome-mac-main-1653587488-c5a2a5c4dde425a5066dbec30cf55034be208a96.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index 8979f533..6c75567 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-main-1653577197-6910ce4f64c8f301cb957182ebd855b324e45c25.profdata
+chrome-win32-main-1653598715-9f8e6c116a08624b1864ac15fa7e6e1f1b5f72d9.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index 9947d25..cff9fab 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-main-1653577197-f298df9bcb5350339d3d0ea2f94a9371fef97747.profdata
+chrome-win64-main-1653598715-be0c36b77bcd6752e69788f777254325007803ab.profdata
diff --git a/chrome/installer/linux/common/rpm.include b/chrome/installer/linux/common/rpm.include
index 3b959f99..ade56391 100644
--- a/chrome/installer/linux/common/rpm.include
+++ b/chrome/installer/linux/common/rpm.include
@@ -24,6 +24,14 @@
     NEED_KEYS=1
   fi
 
+  # 2021 signing subkey
+  rpm -q ${KEY_PACKAGE} --qf '%{Pubkeys:armor}\n' | \
+    gpg --with-colons - 2>/dev/null | \
+    grep -q 4EB27DB2A3B88B8B
+  if [ "$?" -ne "0" ]; then
+    NEED_KEYS=1
+  fi
+
   if [ $NEED_KEYS -ne 1 ]; then
     return
   fi
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index ec9221b..643d6844 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -1141,7 +1141,10 @@
     use_xvfb = use_xvfb_in_this_config
 
     configs += [ "//build/config:precompiled_headers" ]
-    configs += [ ":disable_thinlto_cache_flags" ]
+    configs += [
+      ":disable_thinlto_cache_flags",
+      ":pdb_larger_than_4gb",
+    ]
     defines = [
       "HAS_OUT_OF_PROC_TEST_RUNNER",
       "CHROME_VERSION_MAJOR=" + chrome_version_major,
@@ -3924,7 +3927,6 @@
       data_deps += [
         "//ash/keyboard/ui:resources",
         "//chrome",
-        "//chrome/test/data/webui/cr_components/chromeos/cellular_setup:modulize_runtime_data",
         "//testing/buildbot/filters:chromeos_filters",
         "//ui/file_manager:unit_test_data",
       ]
@@ -4957,6 +4959,15 @@
   }
 }
 
+config("pdb_larger_than_4gb") {
+  if (is_win && symbol_level == 2 && use_thin_lto) {
+    # These binaries create PDBs larger than 4 GiB. Increasing the PDB page
+    # size allows larger PDBs, but not all tools can handle such large PDBs
+    # yet.
+    ldflags = [ "/pdbpagesize:8192" ]
+  }
+}
+
 test("unit_tests") {
   use_xvfb = use_xvfb_in_this_config
 
@@ -5435,7 +5446,10 @@
   }
 
   configs += [ "//build/config:precompiled_headers" ]
-  configs += [ ":disable_thinlto_cache_flags" ]
+  configs += [
+    ":disable_thinlto_cache_flags",
+    ":pdb_larger_than_4gb",
+  ]
 
   data_deps = [
     "//chrome/test/data/media/engagement/preload:generate_preload_list",
diff --git a/chrome/test/data/webui/BUILD.gn b/chrome/test/data/webui/BUILD.gn
index 70cb8df..70a78da 100644
--- a/chrome/test/data/webui/BUILD.gn
+++ b/chrome/test/data/webui/BUILD.gn
@@ -221,23 +221,6 @@
 
     if (is_chromeos_ash) {
       data += [
-        "$root_gen_dir/chrome/test/data/webui/cr_components/chromeos/cellular_setup/activation_code_page_test.m.js",
-        "$root_gen_dir/chrome/test/data/webui/cr_components/chromeos/cellular_setup/base_page_test.m.js",
-        "$root_gen_dir/chrome/test/data/webui/cr_components/chromeos/cellular_setup/button_bar_test.m.js",
-        "$root_gen_dir/chrome/test/data/webui/cr_components/chromeos/cellular_setup/confirmation_code_page_test.m.js",
-        "$root_gen_dir/chrome/test/data/webui/cr_components/chromeos/cellular_setup/cellular_eid_dialog_test.m.js",
-        "$root_gen_dir/chrome/test/data/webui/cr_components/chromeos/cellular_setup/cellular_setup_test.m.js",
-        "$root_gen_dir/chrome/test/data/webui/cr_components/chromeos/cellular_setup/esim_flow_ui_test.m.js",
-        "$root_gen_dir/chrome/test/data/webui/cr_components/chromeos/cellular_setup/final_page_test.m.js",
-        "$root_gen_dir/chrome/test/data/webui/cr_components/chromeos/cellular_setup/provisioning_page_test.m.js",
-        "$root_gen_dir/chrome/test/data/webui/cr_components/chromeos/cellular_setup/psim_flow_ui_test.m.js",
-        "$root_gen_dir/chrome/test/data/webui/cr_components/chromeos/cellular_setup/fake_barcode_detector.m.js",
-        "$root_gen_dir/chrome/test/data/webui/cr_components/chromeos/cellular_setup/fake_canvas_context.m.js",
-        "$root_gen_dir/chrome/test/data/webui/cr_components/chromeos/cellular_setup/setup_loading_page_test.m.js",
-        "$root_gen_dir/chrome/test/data/webui/cr_components/chromeos/cellular_setup/fake_cellular_setup_remote.m.js",
-        "$root_gen_dir/chrome/test/data/webui/cr_components/chromeos/cellular_setup/fake_cellular_setup_delegate.m.js",
-        "$root_gen_dir/chrome/test/data/webui/cr_components/chromeos/cellular_setup/fake_esim_manager_remote.m.js",
-        "$root_gen_dir/chrome/test/data/webui/cr_components/chromeos/cellular_setup/fake_media_devices.m.js",
         "$root_gen_dir/chrome/test/data/webui/cr_components/chromeos/multidevice_setup/integration_test.m.js",
         "$root_gen_dir/chrome/test/data/webui/cr_components/chromeos/multidevice_setup/setup_succeeded_page_test.m.js",
         "$root_gen_dir/chrome/test/data/webui/cr_components/chromeos/multidevice_setup/start_setup_page_test.m.js",
diff --git a/chrome/test/data/webui/chromeos/os_feedback_ui/BUILD.gn b/chrome/test/data/webui/chromeos/os_feedback_ui/BUILD.gn
index 35cd8cec..0a0e603 100644
--- a/chrome/test/data/webui/chromeos/os_feedback_ui/BUILD.gn
+++ b/chrome/test/data/webui/chromeos/os_feedback_ui/BUILD.gn
@@ -28,6 +28,7 @@
   deps = [
     "../..:chai_assert",
     "//ash/webui/os_feedback_ui/resources:confirmation_page",
+    "//ash/webui/os_feedback_ui/resources:feedback_types",
   ]
   externs_list = [ "$externs_path/mocha-2.5.js" ]
 }
diff --git a/chrome/test/data/webui/chromeos/os_feedback_ui/confirmation_page_test.js b/chrome/test/data/webui/chromeos/os_feedback_ui/confirmation_page_test.js
index 96158c2..eefeef09 100644
--- a/chrome/test/data/webui/chromeos/os_feedback_ui/confirmation_page_test.js
+++ b/chrome/test/data/webui/chromeos/os_feedback_ui/confirmation_page_test.js
@@ -3,66 +3,162 @@
 // found in the LICENSE file.
 
 import {ConfirmationPageElement} from 'chrome://os-feedback/confirmation_page.js';
+import {SendReportStatus} from 'chrome://os-feedback/feedback_types.js';
 
-import {assertEquals, assertTrue} from '../../chai_assert.js';
+import {assertEquals, assertFalse, assertTrue} from '../../chai_assert.js';
+import {flushTasks, isVisible} from '../../test_util.js';
+
+/** @type {string} */
+const ONLINE_TITLE = 'Thanks for your feedback';
+/** @type {string} */
+const OFFLINE_TITLE = 'You are offline now. Feedback will be sent later.';
+
+/** @type {string} */
+const ONLINE_MESSAGE =
+    'Your feedback helps improve ChromeOS and will be reviewed by ' +
+    'our team. Because of the large number of reports, we won\’t be able ' +
+    ' to send a reply.';
+/** @type {string} */
+const OFFLINE_MESSAGE =
+    'Thanks for the feedback. Your feedback helps improve Chrome OS ' +
+    'and will be reviewed by the Chrome OS team. Because of the number ' +
+    ' of reports submitted, you won’t receive a direct reply.';
 
 export function confirmationPageTest() {
   /** @type {?ConfirmationPageElement} */
-  let component = null;
+  let page = null;
 
   setup(() => {
     document.body.innerHTML = '';
-    component = /** @type {!ConfirmationPageElement} */ (
-        document.createElement('confirmation-page'));
-    document.body.appendChild(component);
   });
 
   teardown(() => {
-    component.remove();
-    component = null;
+    page.remove();
+    page = null;
   });
 
-  /**TODO(xiangdongkong): test user actions */
-  test('confirmationPageLoaded', () => {
-    assertEquals(
-        'Thanks for your feedback',
-        component.shadowRoot.querySelector('#title').textContent);
-    assertTrue(!!component.shadowRoot.querySelector('#message'));
+  function initializePage() {
+    assertFalse(!!page);
+    page = /** @type {!ConfirmationPageElement} */ (
+        document.createElement('confirmation-page'));
+    assertTrue(!!page);
+    document.body.appendChild(page);
+    return flushTasks();
+  }
 
-    // verify navigation buttons exist
-    const doneButton = component.shadowRoot.querySelector('#buttonDone');
-    const startNewButton =
-        component.shadowRoot.querySelector('#buttonNewReport');
-    assertTrue(!!doneButton);
-    assertTrue(!!startNewButton);
+  /**
+   * @param {?Element} host
+   * @param {string} selector
+   * @returns {!Element}
+   */
+  function getElement(host, selector) {
+    const element = host.shadowRoot.querySelector(selector);
+    assertTrue(!!element);
+    return element;
+  }
+
+  /**
+   * @param {?Element} host
+   * @param {string} selector
+   * @returns {string}
+   */
+  function getElementContent(host, selector) {
+    const element = getElement(host, selector);
+    return element.textContent.trim();
+  }
+
+  /**
+   * @param {boolean} isOnline
+   * @private
+   */
+  function verifyElementsByStatus(isOnline) {
+    if (isOnline) {
+      assertEquals(ONLINE_TITLE, getElementContent(page, '#title'));
+      assertEquals(ONLINE_MESSAGE, getElementContent(page, '#message'));
+    } else {
+      assertEquals(OFFLINE_TITLE, getElementContent(page, '#title'));
+      assertEquals(OFFLINE_MESSAGE, getElementContent(page, '#message'));
+    }
 
     // verify help resources exist
-    const helpResourcesSection =
-        component.shadowRoot.querySelector('#helpResources');
-    assertTrue(!!helpResourcesSection);
+    const helpResourcesSection = getElement(page, '#helpResources');
     assertEquals(
         'Here are some other helpful resources:',
-        component.shadowRoot.querySelector('#helpResourcesLabel').textContent);
+        getElementContent(page, '#helpResourcesLabel'));
     const helpLinks = helpResourcesSection.querySelectorAll('cr-link-row');
     assertTrue(!!helpLinks);
-    assertEquals(helpLinks.length, 3);
+    assertEquals(3, helpLinks.length);
 
-    assertTrue(!!helpLinks[0].shadowRoot.querySelector('#startIcon'));
-    assertTrue(!!helpLinks[0].shadowRoot.querySelector('#subLabel'));
-    const exploreLabel = helpLinks[0].shadowRoot.querySelector('#label');
-    assertTrue(!!exploreLabel);
-    assertEquals(exploreLabel.textContent.trim(), 'Explore app');
+    // Verify the explore app link.
+    const exploreLink = helpLinks[0];
+    assertTrue(isVisible(exploreLink));
+    assertEquals(
+        'help-resources:explore', getElement(exploreLink, '#startIcon').icon);
+    assertEquals('Explore app', getElementContent(exploreLink, '#label'));
+    assertEquals(
+        'Find help articles and answers to common Chromebook questions',
+        getElementContent(exploreLink, '#subLabel'));
 
-    assertTrue(!!helpLinks[1].shadowRoot.querySelector('#startIcon'));
-    assertTrue(!!helpLinks[1].shadowRoot.querySelector('#subLabel'));
-    const diagnosticsLabel = helpLinks[1].shadowRoot.querySelector('#label');
-    assertTrue(!!diagnosticsLabel);
-    assertEquals(diagnosticsLabel.textContent.trim(), 'Diagnostics app');
+    // Verify the diagnostics app link.
+    const diagnosticsLink = helpLinks[1];
+    assertTrue(isVisible(diagnosticsLink));
+    assertEquals(
+        'help-resources:diagnostics',
+        getElement(diagnosticsLink, '#startIcon').icon);
+    assertEquals(
+        'Diagnostics app', getElementContent(diagnosticsLink, '#label'));
+    assertEquals(
+        'Run tests and troubleshooting for hardware issues',
+        getElementContent(diagnosticsLink, '#subLabel'));
 
-    assertTrue(!!helpLinks[2].shadowRoot.querySelector('#startIcon'));
-    assertTrue(!!helpLinks[2].shadowRoot.querySelector('#subLabel'));
-    const communityLabel = helpLinks[2].shadowRoot.querySelector('#label');
-    assertTrue(!!communityLabel);
-    assertEquals(communityLabel.textContent.trim(), 'Chromebook community');
+    // Verify the community link.
+    const communityLink = helpLinks[2];
+    if (isOnline) {
+      assertTrue(isVisible(communityLink));
+    } else {
+      assertFalse(isVisible(communityLink));
+    }
+    assertEquals(
+        'help-resources2:chromebook-community',
+        getElement(communityLink, '#startIcon').icon);
+    assertEquals(
+        'Chromebook community', getElementContent(communityLink, '#label'));
+    assertEquals(
+        'Ask the experts in the Chromebook help forum',
+        getElementContent(communityLink, '#subLabel'));
+
+    // Verify buttons.
+    assertEquals(
+        'Send new report', getElementContent(page, '#buttonNewReport'));
+    assertEquals('Done', getElementContent(page, '#buttonDone'));
+  }
+
+  /**TODO(xiangdongkong): test user actions */
+
+  // Test when send report status is success, the corresponding title and
+  // message are being used. The community link should be visible,
+  test('onlineModeStatusSuccess', async () => {
+    await initializePage();
+
+    page.sendReportStatus = SendReportStatus.kSuccess;
+    verifyElementsByStatus(/**isOnline=*/ true);
+  });
+
+  // Test when send report status is unknown, the corresponding title and
+  // message are being used. The community link should be visible,
+  test('offlineModeStatusUnknown', async () => {
+    await initializePage();
+
+    page.sendReportStatus = SendReportStatus.kUnknown;
+    verifyElementsByStatus(/**isOnline=*/ true);
+  });
+
+  // Test when send report status is offline, the corresponding title and
+  // message are being used. The community link should be invisible,
+  test('offlineModeStatusDelayed', async () => {
+    await initializePage();
+
+    page.sendReportStatus = SendReportStatus.kDelayed;
+    verifyElementsByStatus(/**isOnline=*/ false);
   });
 }
diff --git a/chrome/test/data/webui/chromeos/os_feedback_ui/feedback_flow_test.js b/chrome/test/data/webui/chromeos/os_feedback_ui/feedback_flow_test.js
index 2791147..d6ca156 100644
--- a/chrome/test/data/webui/chromeos/os_feedback_ui/feedback_flow_test.js
+++ b/chrome/test/data/webui/chromeos/os_feedback_ui/feedback_flow_test.js
@@ -6,6 +6,7 @@
 import {FakeFeedbackServiceProvider} from 'chrome://os-feedback/fake_feedback_service_provider.js';
 import {FakeHelpContentProvider} from 'chrome://os-feedback/fake_help_content_provider.js';
 import {FeedbackFlowElement, FeedbackFlowState} from 'chrome://os-feedback/feedback_flow.js';
+import {SendReportStatus} from 'chrome://os-feedback/feedback_types.js';
 import {setFeedbackServiceProviderForTesting, setHelpContentProviderForTesting} from 'chrome://os-feedback/mojo_interface_provider.js';
 
 import {assertEquals, assertFalse, assertTrue} from '../../chai_assert.js';
@@ -104,6 +105,7 @@
   test('ConfirmationPageIsShown', async () => {
     await initializePage();
     page.setCurrentStateForTesting(FeedbackFlowState.CONFIRMATION);
+    page.setSendReportStatusForTesting(SendReportStatus.kSuccess);
 
     const activePage = page.shadowRoot.querySelector('.iron-selected');
     assertTrue(!!activePage);
@@ -226,6 +228,7 @@
     activePage = page.shadowRoot.querySelector('.iron-selected');
     assertTrue(!!activePage);
     assertEquals('confirmationPage', activePage.id);
+    assertEquals(SendReportStatus.kSuccess, activePage.sendReportStatus);
   });
 
   // Test that the getUserEmail is called after initialization.
diff --git a/chrome/test/data/webui/chromeos/personalization_app/wallpaper_images_element_test.ts b/chrome/test/data/webui/chromeos/personalization_app/wallpaper_images_element_test.ts
index 00a1967..cd6258eb 100644
--- a/chrome/test/data/webui/chromeos/personalization_app/wallpaper_images_element_test.ts
+++ b/chrome/test/data/webui/chromeos/personalization_app/wallpaper_images_element_test.ts
@@ -96,6 +96,7 @@
   });
 
   test('displays images for current collection id', async () => {
+    loadTimeData.overrideValues({isDarkLightModeEnabled: false});
     personalizationStore.data.wallpaper.backdrop.images = {
       'id_0': wallpaperProvider.images,
       'id_1': [
diff --git a/chrome/test/data/webui/cr_components/chromeos/BUILD.gn b/chrome/test/data/webui/cr_components/chromeos/BUILD.gn
index 8354b800..819eb9e 100644
--- a/chrome/test/data/webui/cr_components/chromeos/BUILD.gn
+++ b/chrome/test/data/webui/cr_components/chromeos/BUILD.gn
@@ -7,7 +7,6 @@
 
 group("modulize") {
   deps = [
-    "cellular_setup:modulize",
     "multidevice_setup:modulize",
     "network:modulize",
   ]
diff --git a/chrome/test/data/webui/cr_components/chromeos/cellular_setup/BUILD.gn b/chrome/test/data/webui/cr_components/chromeos/cellular_setup/BUILD.gn
index 53e6543..ae1922e1 100644
--- a/chrome/test/data/webui/cr_components/chromeos/cellular_setup/BUILD.gn
+++ b/chrome/test/data/webui/cr_components/chromeos/cellular_setup/BUILD.gn
@@ -1,41 +1,3 @@
 # 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.
-
-import("//ui/webui/resources/cr_components/chromeos/os_cr_components.gni")
-import("//ui/webui/resources/tools/js_modulizer.gni")
-
-js_modulizer("modulize") {
-  input_files = [
-    "activation_code_page_test.js",
-    "base_page_test.js",
-    "button_bar_test.js",
-    "confirmation_code_page_test.js",
-    "cellular_eid_dialog_test.js",
-    "cellular_setup_test.js",
-    "esim_flow_ui_test.js",
-    "final_page_test.js",
-    "provisioning_page_test.js",
-    "psim_flow_ui_test.js",
-    "setup_loading_page_test.js",
-    "fake_cellular_setup_delegate.js",
-    "fake_cellular_setup_remote.js",
-    "fake_esim_manager_remote.js",
-    "fake_media_devices.js",
-    "fake_canvas_context.js",
-    "fake_barcode_detector.js",
-    "mock_metrics_private.js",
-  ]
-  namespace_rewrites = cr_components_chromeos_namespace_rewrites + [
-                         "cellular_setup.FakeCellularSetupDelegate|FakeCellularSetupDelegate",
-                         "cellular_setup.FakeCarrierPortalHandlerRemote|FakeCarrierPortalHandlerRemote",
-                         "cellular_setup.FakeCellularSetupRemote|FakeCellularSetupRemote",
-                         "cellular_setup.FakeESimManagerRemote|FakeESimManagerRemote",
-                         "cellular_setup.FakeMediaDevices|FakeMediaDevices",
-                         "cellular_setup.FakeCanvasContext|FakeCanvasContext",
-                         "cellular_setup.MockMetricsPrivate|MockMetricsPrivate",
-                         "test_util.eventToPromise|eventToPromise",
-                         "test_util.flushTasks|flushTasks",
-                         "test_util.waitAfterNextRender|waitAfterNextRender",
-                       ]
-}
diff --git a/chrome/test/data/webui/cr_components/chromeos/cellular_setup/activation_code_page_test.js b/chrome/test/data/webui/cr_components/chromeos/cellular_setup/activation_code_page_test.js
index ad00aaa..95137a1a 100644
--- a/chrome/test/data/webui/cr_components/chromeos/cellular_setup/activation_code_page_test.js
+++ b/chrome/test/data/webui/cr_components/chromeos/cellular_setup/activation_code_page_test.js
@@ -2,16 +2,16 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// clang-format off
-// #import 'chrome://os-settings/strings.m.js';
-// #import 'chrome://resources/cr_components/chromeos/cellular_setup/activation_code_page.m.js';
+import 'chrome://os-settings/strings.m.js';
+import 'chrome://resources/cr_components/chromeos/cellular_setup/activation_code_page.m.js';
 
-// #import {flush, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-// #import {assertTrue} from '../../../chai_assert.js';
-// #import {FakeMediaDevices} from './fake_media_devices.m.js';
-// #import {FakeBarcodeDetector, FakeImageCapture} from './fake_barcode_detector.m.js';
-// #import {eventToPromise, flushTasks, waitAfterNextRender} from 'chrome://test/test_util.js';
-// clang-format on
+import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {eventToPromise, flushTasks, waitAfterNextRender} from 'chrome://test/test_util.js';
+
+import {assertTrue} from '../../../chai_assert.js';
+
+import {FakeBarcodeDetector, FakeImageCapture} from './fake_barcode_detector.js';
+import {FakeMediaDevices} from './fake_media_devices.js';
 
 suite('CrComponentsActivationCodePageTest', function() {
   /** @type {string} */
@@ -29,7 +29,7 @@
   let intervalFunction = null;
 
   function flushAsync() {
-    Polymer.dom.flush();
+    flush();
     // Use setTimeout to wait for the next macrotask.
     return new Promise(resolve => setTimeout(resolve));
   }
@@ -53,14 +53,14 @@
         FakeBarcodeDetector, FakeImageCapture, setIntervalFunction,
         playVideoFunction, stopStreamFunction);
     document.body.appendChild(activationCodePage);
-    Polymer.dom.flush();
+    flush();
 
-    mediaDevices = new cellular_setup.FakeMediaDevices();
+    mediaDevices = new FakeMediaDevices();
     mediaDevices.addDevice();
     activationCodePage.setMediaDevices(mediaDevices);
     mediaDevices.resolveEnumerateDevices();
     await waitAfterNextRender(activationCodePage);
-    Polymer.dom.flush();
+    flush();
   });
 
   teardown(function() {
@@ -117,7 +117,7 @@
     assertTrue(switchCameraButton.hidden);
 
     const focusNextButtonPromise =
-        test_util.eventToPromise('focus-default-button', activationCodePage);
+        eventToPromise('focus-default-button', activationCodePage);
 
     // Mock camera scanning a code.
     await intervalFunction();
@@ -126,7 +126,7 @@
     // The scanFinishContainer and scanSuccessContainer should now be visible,
     // video, start scanning UI, scanFailureContainer hidden and nextbutton
     // is focused.
-    await Promise.all([focusNextButtonPromise, test_util.flushTasks()]);
+    await Promise.all([focusNextButtonPromise, flushTasks()]);
     assertFalse(scanFinishContainer.hidden);
     assertTrue(startScanningContainer.hidden);
     assertTrue(video.hidden);
@@ -447,8 +447,8 @@
 
     const setInputAndAssert =
         async (activationCode, isInputInvalid, shouldEventContainCode) => {
-      const activationCodeUpdatedPromise = test_util.eventToPromise(
-          'activation-code-updated', activationCodePage);
+      const activationCodeUpdatedPromise =
+          eventToPromise('activation-code-updated', activationCodePage);
       input.value = activationCode;
       const activationCodeUpdatedEvent = await activationCodeUpdatedPromise;
       assertEquals(
@@ -522,7 +522,7 @@
 
     // Mock camera scanning an invalid code.
     let activationCodeUpdatedPromise =
-        test_util.eventToPromise('activation-code-updated', activationCodePage);
+        eventToPromise('activation-code-updated', activationCodePage);
     FakeBarcodeDetector.setDetectedBarcode(ACTIVATION_CODE_INVALID);
     await intervalFunction();
     await flushAsync();
@@ -544,7 +544,7 @@
 
     // Mock camera scanning a valid, incomplete code.
     activationCodeUpdatedPromise =
-        test_util.eventToPromise('activation-code-updated', activationCodePage);
+        eventToPromise('activation-code-updated', activationCodePage);
     FakeBarcodeDetector.setDetectedBarcode(/*barcode=*/ 'LPA:');
     await intervalFunction();
     await flushAsync();
@@ -566,7 +566,7 @@
 
     // Mock camera scanning a valid code.
     activationCodeUpdatedPromise =
-        test_util.eventToPromise('activation-code-updated', activationCodePage);
+        eventToPromise('activation-code-updated', activationCodePage);
     FakeBarcodeDetector.setDetectedBarcode(ACTIVATION_CODE_VALID);
     await intervalFunction();
     await flushAsync();
diff --git a/chrome/test/data/webui/cr_components/chromeos/cellular_setup/base_page_test.js b/chrome/test/data/webui/cr_components/chromeos/cellular_setup/base_page_test.js
index dc9d171c..252a0e0 100644
--- a/chrome/test/data/webui/cr_components/chromeos/cellular_setup/base_page_test.js
+++ b/chrome/test/data/webui/cr_components/chromeos/cellular_setup/base_page_test.js
@@ -2,23 +2,21 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// clang-format off
-// #import 'chrome://resources/cr_components/chromeos/cellular_setup/base_page.m.js';
+import 'chrome://resources/cr_components/chromeos/cellular_setup/base_page.m.js';
 
-// #import {flush, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-// clang-format on
+import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 suite('CrComponentsBasePageTest', function() {
   let basePage;
   setup(function() {
     basePage = document.createElement('base-page');
     document.body.appendChild(basePage);
-    Polymer.dom.flush();
+    flush();
   });
 
   test('Title is shown', function() {
     basePage.title = 'Base page titile';
-    Polymer.dom.flush();
+    flush();
     const title = basePage.$$('#title');
     assertTrue(!!title);
   });
@@ -30,7 +28,7 @@
 
   test('Message icon is shown', function() {
     basePage.messageIcon = 'warning';
-    Polymer.dom.flush();
+    flush();
     const messageIcon = basePage.$$('iron-icon');
     assertTrue(!!messageIcon);
     assertFalse(messageIcon.hidden);
diff --git a/chrome/test/data/webui/cr_components/chromeos/cellular_setup/button_bar_test.js b/chrome/test/data/webui/cr_components/chromeos/cellular_setup/button_bar_test.js
index cc022a5..5cecdcec 100644
--- a/chrome/test/data/webui/cr_components/chromeos/cellular_setup/button_bar_test.js
+++ b/chrome/test/data/webui/cr_components/chromeos/cellular_setup/button_bar_test.js
@@ -2,14 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// clang-format off
-// #import 'chrome://os-settings/strings.m.js';
-// #import 'chrome://resources/cr_components/chromeos/cellular_setup/button_bar.m.js';
+import 'chrome://os-settings/strings.m.js';
+import 'chrome://resources/cr_components/chromeos/cellular_setup/button_bar.m.js';
 
-// #import {flush, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-// #import {ButtonState, Button, ButtonBarState, CellularSetupPageName} from 'chrome://resources/cr_components/chromeos/cellular_setup/cellular_types.m.js';
-// #import {assertEquals, assertFalse, assertTrue} from '../../../chai_assert.js';
-// clang-format on
+import {ButtonBarState, ButtonState} from 'chrome://resources/cr_components/chromeos/cellular_setup/cellular_types.m.js';
+import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
+import {assertEquals, assertTrue} from '../../../chai_assert.js';
 
 suite('CellularSetupButtonBarTest', function() {
   /** @type {!ButtonBarElement} */
@@ -17,7 +16,7 @@
   setup(function() {
     buttonBar = document.createElement('button-bar');
     document.body.appendChild(buttonBar);
-    Polymer.dom.flush();
+    flush();
   });
 
   teardown(function() {
@@ -25,7 +24,7 @@
   });
 
   /**
-   * @param {!cellularSetup.ButtonBarState} state
+   * @param {!ButtonBarState} state
    */
   function setStateForAllButtons(state) {
     buttonBar.buttonState = {
@@ -33,7 +32,7 @@
       cancel: state,
       forward: state,
     };
-    Polymer.dom.flush();
+    flush();
   }
 
   /**
@@ -61,44 +60,44 @@
   }
 
   test('individual buttons appear if enabled', function() {
-    setStateForAllButtons(cellularSetup.ButtonState.ENABLED);
+    setStateForAllButtons(ButtonState.ENABLED);
     assertTrue(isButtonShownAndEnabled(buttonBar.$$('#backward')));
     assertTrue(isButtonShownAndEnabled(buttonBar.$$('#cancel')));
     assertTrue(isButtonShownAndEnabled(buttonBar.$$('#forward')));
   });
 
   test('individual buttons appear but are diabled', function() {
-    setStateForAllButtons(cellularSetup.ButtonState.DISABLED);
+    setStateForAllButtons(ButtonState.DISABLED);
     assertTrue(isButtonShownAndDisabled(buttonBar.$$('#backward')));
     assertTrue(isButtonShownAndDisabled(buttonBar.$$('#cancel')));
     assertTrue(isButtonShownAndDisabled(buttonBar.$$('#forward')));
   });
 
   test('individual buttons are hidden', function() {
-    setStateForAllButtons(cellularSetup.ButtonState.HIDDEN);
+    setStateForAllButtons(ButtonState.HIDDEN);
     assertTrue(isButtonHidden(buttonBar.$$('#backward')));
     assertTrue(isButtonHidden(buttonBar.$$('#cancel')));
     assertTrue(isButtonHidden(buttonBar.$$('#forward')));
   });
 
   test('default focus is on last button if all are enabled', function() {
-    setStateForAllButtons(cellularSetup.ButtonState.ENABLED);
+    setStateForAllButtons(ButtonState.ENABLED);
     buttonBar.focusDefaultButton();
 
-    Polymer.dom.flush();
+    flush();
 
     assertEquals(buttonBar.shadowRoot.activeElement, buttonBar.$$('#forward'));
   });
 
   test('default focus is on first button if rest are hidden', function() {
     buttonBar.buttonState = {
-      backward: cellularSetup.ButtonState.ENABLED,
-      cancel: cellularSetup.ButtonState.HIDDEN,
-      forward: cellularSetup.ButtonState.HIDDEN,
+      backward: ButtonState.ENABLED,
+      cancel: ButtonState.HIDDEN,
+      forward: ButtonState.HIDDEN,
     };
     buttonBar.focusDefaultButton();
 
-    Polymer.dom.flush();
+    flush();
 
     assertEquals(buttonBar.shadowRoot.activeElement, buttonBar.$$('#backward'));
   });
@@ -107,13 +106,13 @@
       'default focus is on first button if rest are visible but disabled',
       function() {
         buttonBar.buttonState = {
-          backward: cellularSetup.ButtonState.ENABLED,
-          cancel: cellularSetup.ButtonState.DISABLED,
-          forward: cellularSetup.ButtonState.DISABLED,
+          backward: ButtonState.ENABLED,
+          cancel: ButtonState.DISABLED,
+          forward: ButtonState.DISABLED,
         };
         buttonBar.focusDefaultButton();
 
-        Polymer.dom.flush();
+        flush();
 
         assertEquals(
             buttonBar.shadowRoot.activeElement, buttonBar.$$('#backward'));
diff --git a/chrome/test/data/webui/cr_components/chromeos/cellular_setup/cellular_eid_dialog_test.js b/chrome/test/data/webui/cr_components/chromeos/cellular_setup/cellular_eid_dialog_test.js
index 8e833bc5..b09b933 100644
--- a/chrome/test/data/webui/cr_components/chromeos/cellular_setup/cellular_eid_dialog_test.js
+++ b/chrome/test/data/webui/cr_components/chromeos/cellular_setup/cellular_eid_dialog_test.js
@@ -2,18 +2,16 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// clang-format off
-// #import 'chrome://os-settings/strings.m.js';
-// #import 'chrome://resources/cr_components/chromeos/cellular_setup/cellular_eid_dialog.m.js';
+import 'chrome://os-settings/strings.m.js';
+import 'chrome://resources/cr_components/chromeos/cellular_setup/cellular_eid_dialog.m.js';
 
-// #import {afterNextRender, flush, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-// #import {setESimManagerRemoteForTesting} from 'chrome://resources/cr_components/chromeos/cellular_setup/mojo_interface_provider.m.js';
-// #import {assertTrue, assertEquals, assertDeepEquals} from '../../../chai_assert.js';
-// #import {FakeESimManagerRemote} from './fake_esim_manager_remote.m.js';
-// #import {FakeCanvasContext} from "./fake_canvas_context.m.js";
-// #import {eventToPromise, flushTasks} from 'chrome://test/test_util.js';
-// #import {waitAfterNextRender} from 'chrome://test/test_util.js';
-// clang-format on
+import {setESimManagerRemoteForTesting} from 'chrome://resources/cr_components/chromeos/cellular_setup/mojo_interface_provider.m.js';
+import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
+import {assertDeepEquals, assertEquals, assertTrue} from '../../../chai_assert.js';
+
+import {FakeCanvasContext} from './fake_canvas_context.js';
+import {FakeESimManagerRemote} from './fake_esim_manager_remote.js';
 
 suite('CrComponentsCellularEidDialogTest', function() {
   let eSimManagerRemote;
@@ -22,8 +20,8 @@
   let canvasContext;
 
   function init(eidQRCode) {
-    eSimManagerRemote = new cellular_setup.FakeESimManagerRemote();
-    cellular_setup.setESimManagerRemoteForTesting(eSimManagerRemote);
+    eSimManagerRemote = new FakeESimManagerRemote();
+    setESimManagerRemoteForTesting(eSimManagerRemote);
     testEuicc = eSimManagerRemote.addEuiccForTest(1);
     if (eidQRCode) {
       testEuicc.setEidQRCodeForTest(eidQRCode);
@@ -31,12 +29,12 @@
 
     eidDialog = document.createElement('cellular-eid-dialog');
     eidDialog.euicc = testEuicc;
-    canvasContext = new cellular_setup.FakeCanvasContext();
+    canvasContext = new FakeCanvasContext();
     eidDialog.setCanvasContextForTest(canvasContext);
     document.body.appendChild(eidDialog);
 
     // Flush and wait for next macrotask.
-    Polymer.dom.flush();
+    flush();
     return new Promise(resolve => setTimeout(resolve));
   }
 
@@ -61,7 +59,7 @@
     assertTrue(eidDialog.$.eidDialog.open);
 
     eidDialog.$.done.click();
-    Polymer.dom.flush();
+    flush();
 
     assertFalse(eidDialog.$.eidDialog.open);
   });
diff --git a/chrome/test/data/webui/cr_components/chromeos/cellular_setup/cellular_setup_test.js b/chrome/test/data/webui/cr_components/chromeos/cellular_setup/cellular_setup_test.js
index 9e22cc9..fe3b06f 100644
--- a/chrome/test/data/webui/cr_components/chromeos/cellular_setup/cellular_setup_test.js
+++ b/chrome/test/data/webui/cr_components/chromeos/cellular_setup/cellular_setup_test.js
@@ -2,20 +2,20 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// clang-format off
-// #import 'chrome://os-settings/strings.m.js';
-// #import 'chrome://resources/cr_components/chromeos/cellular_setup/cellular_setup.m.js';
-// #import 'chrome://resources/cr_components/chromeos/cellular_setup/psim_flow_ui.m.js';
+import 'chrome://os-settings/strings.m.js';
+import 'chrome://resources/cr_components/chromeos/cellular_setup/cellular_setup.m.js';
+import 'chrome://resources/cr_components/chromeos/cellular_setup/psim_flow_ui.m.js';
 
-// #import {CellularSetupPageName} from 'chrome://resources/cr_components/chromeos/cellular_setup/cellular_types.m.js';
-// #import {flush, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-// #import {assertTrue, assertFalse} from '../../../chai_assert.js';
-// #import {FakeCellularSetupDelegate} from './fake_cellular_setup_delegate.m.js';
-// #import {FakeNetworkConfig} from 'chrome://test/chromeos/fake_network_config_mojom.js';
-// #import {FakeESimManagerRemote} from 'chrome://test/cr_components/chromeos/cellular_setup/fake_esim_manager_remote.m.js';
-// #import {setESimManagerRemoteForTesting} from 'chrome://resources/cr_components/chromeos/cellular_setup/mojo_interface_provider.m.js';
-// #import {MojoInterfaceProviderImpl} from 'chrome://resources/cr_components/chromeos/network/mojo_interface_provider.m.js';
-// clang-format on
+import {CellularSetupPageName} from 'chrome://resources/cr_components/chromeos/cellular_setup/cellular_types.m.js';
+import {setESimManagerRemoteForTesting} from 'chrome://resources/cr_components/chromeos/cellular_setup/mojo_interface_provider.m.js';
+import {MojoInterfaceProviderImpl} from 'chrome://resources/cr_components/chromeos/network/mojo_interface_provider.m.js';
+import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {FakeNetworkConfig} from 'chrome://test/chromeos/fake_network_config_mojom.js';
+import {FakeESimManagerRemote} from 'chrome://test/cr_components/chromeos/cellular_setup/fake_esim_manager_remote.js';
+
+import {assertFalse, assertTrue} from '../../../chai_assert.js';
+
+import {FakeCellularSetupDelegate} from './fake_cellular_setup_delegate.js';
 
 suite('CrComponentsCellularSetupTest', function() {
   let cellularSetupPage;
@@ -23,15 +23,15 @@
   let networkConfigRemote;
 
   setup(function() {
-    eSimManagerRemote = new cellular_setup.FakeESimManagerRemote();
-    cellular_setup.setESimManagerRemoteForTesting(eSimManagerRemote);
+    eSimManagerRemote = new FakeESimManagerRemote();
+    setESimManagerRemoteForTesting(eSimManagerRemote);
 
     networkConfigRemote = new FakeNetworkConfig();
-    network_config.MojoInterfaceProviderImpl.getInstance().remote_ = networkConfigRemote;
+    MojoInterfaceProviderImpl.getInstance().remote_ = networkConfigRemote;
   });
 
   async function flushAsync() {
-    Polymer.dom.flush();
+    flush();
     // Use setTimeout to wait for the next macrotask.
     return new Promise(resolve => setTimeout(resolve));
   }
@@ -44,12 +44,12 @@
       simInfos: [{slot_id: 0, iccid: '1111111111111111'}],
     });
     eSimManagerRemote.addEuiccForTest(2);
-    Polymer.dom.flush();
+    flush();
 
     cellularSetupPage = document.createElement('cellular-setup');
-    cellularSetupPage.delegate = new cellular_setup.FakeCellularSetupDelegate();
+    cellularSetupPage.delegate = new FakeCellularSetupDelegate();
     document.body.appendChild(cellularSetupPage);
-    Polymer.dom.flush();
+    flush();
   }
 
   test('Show pSim flow ui', async function() {
@@ -61,8 +61,7 @@
     assertTrue(!!eSimFlow);
     assertFalse(!!pSimFlow);
 
-    cellularSetupPage.currentPageName =
-        cellularSetup.CellularSetupPageName.PSIM_FLOW_UI;
+    cellularSetupPage.currentPageName = CellularSetupPageName.PSIM_FLOW_UI;
     await flushAsync();
     eSimFlow = cellularSetupPage.$$('esim-flow-ui');
     pSimFlow = cellularSetupPage.$$('psim-flow-ui');
diff --git a/chrome/test/data/webui/cr_components/chromeos/cellular_setup/confirmation_code_page_test.js b/chrome/test/data/webui/cr_components/chromeos/cellular_setup/confirmation_code_page_test.js
index 24866ab2..760cb029 100644
--- a/chrome/test/data/webui/cr_components/chromeos/cellular_setup/confirmation_code_page_test.js
+++ b/chrome/test/data/webui/cr_components/chromeos/cellular_setup/confirmation_code_page_test.js
@@ -2,19 +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 'chrome://os-settings/strings.m.js';
-// #import 'chrome://resources/cr_components/chromeos/cellular_setup/confirmation_code_page.m.js';
+import 'chrome://os-settings/strings.m.js';
+import 'chrome://resources/cr_components/chromeos/cellular_setup/confirmation_code_page.m.js';
 
-// #import {flush, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-// #import {assertTrue} from '../../../chai_assert.js';
-// clang-format on
+import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
+import {assertTrue} from '../../../chai_assert.js';
 
 suite('CrComponentsConfirmationCodePageTest', function() {
   let confirmationCodePage;
 
   function flushAsync() {
-    Polymer.dom.flush();
+    flush();
     // Use setTimeout to wait for the next macrotask.
     return new Promise(resolve => setTimeout(resolve));
   }
@@ -22,7 +21,7 @@
   setup(function() {
     confirmationCodePage = document.createElement('confirmation-code-page');
     document.body.appendChild(confirmationCodePage);
-    Polymer.dom.flush();
+    flush();
   });
 
   test('Event is fired when enter is pressed on input', async function() {
diff --git a/chrome/test/data/webui/cr_components/chromeos/cellular_setup/esim_flow_ui_test.js b/chrome/test/data/webui/cr_components/chromeos/cellular_setup/esim_flow_ui_test.js
index 32414a9..fe18823 100644
--- a/chrome/test/data/webui/cr_components/chromeos/cellular_setup/esim_flow_ui_test.js
+++ b/chrome/test/data/webui/cr_components/chromeos/cellular_setup/esim_flow_ui_test.js
@@ -2,23 +2,23 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// clang-format off
-// #import 'chrome://os-settings/strings.m.js';
-// #import 'chrome://resources/cr_components/chromeos/cellular_setup/esim_flow_ui.m.js';
+import 'chrome://os-settings/strings.m.js';
+import 'chrome://resources/cr_components/chromeos/cellular_setup/esim_flow_ui.m.js';
 
-// #import {flush, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-// #import {setESimManagerRemoteForTesting} from 'chrome://resources/cr_components/chromeos/cellular_setup/mojo_interface_provider.m.js';
-// #import {ButtonState} from 'chrome://resources/cr_components/chromeos/cellular_setup/cellular_types.m.js';
-// #import {ESimPageName, ESimSetupFlowResult, ESIM_SETUP_RESULT_METRIC_NAME, SUCCESSFUL_ESIM_SETUP_DURATION_METRIC_NAME, FAILED_ESIM_SETUP_DURATION_METRIC_NAME} from 'chrome://resources/cr_components/chromeos/cellular_setup/esim_flow_ui.m.js';
-// #import {assertEquals, assertTrue} from '../../../chai_assert.js';
-// #import {FakeESimManagerRemote} from './fake_esim_manager_remote.m.js';
-// #import {FakeCellularSetupDelegate} from './fake_cellular_setup_delegate.m.js';
-// #import {FakeBarcodeDetector, FakeImageCapture} from './fake_barcode_detector.m.js';
-// #import {FakeNetworkConfig} from 'chrome://test/chromeos/fake_network_config_mojom.js';
-// #import {OncMojo} from 'chrome://resources/cr_components/chromeos/network/onc_mojo.m.js';
-// #import {MojoInterfaceProviderImpl} from 'chrome://resources/cr_components/chromeos/network/mojo_interface_provider.m.js';
-// #import {MockMetricsPrivate} from './mock_metrics_private.m.js';
-// clang-format on
+import {ButtonState} from 'chrome://resources/cr_components/chromeos/cellular_setup/cellular_types.m.js';
+import {ESimPageName, ESimSetupFlowResult, FAILED_ESIM_SETUP_DURATION_METRIC_NAME, SUCCESSFUL_ESIM_SETUP_DURATION_METRIC_NAME} from 'chrome://resources/cr_components/chromeos/cellular_setup/esim_flow_ui.m.js';
+import {setESimManagerRemoteForTesting} from 'chrome://resources/cr_components/chromeos/cellular_setup/mojo_interface_provider.m.js';
+import {MojoInterfaceProviderImpl} from 'chrome://resources/cr_components/chromeos/network/mojo_interface_provider.m.js';
+import {OncMojo} from 'chrome://resources/cr_components/chromeos/network/onc_mojo.m.js';
+import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {FakeNetworkConfig} from 'chrome://test/chromeos/fake_network_config_mojom.js';
+
+import {assertEquals, assertTrue} from '../../../chai_assert.js';
+
+import {FakeBarcodeDetector, FakeImageCapture} from './fake_barcode_detector.js';
+import {FakeCellularSetupDelegate} from './fake_cellular_setup_delegate.js';
+import {FakeESimManagerRemote} from './fake_esim_manager_remote.js';
+import {MockMetricsPrivate} from './mock_metrics_private.js';
 
 suite('CrComponentsEsimFlowUiTest', function() {
   /** @type {string} */
@@ -38,7 +38,7 @@
   const wifiGuidPrefix = 'wifi';
 
   async function flushAsync() {
-    Polymer.dom.flush();
+    flush();
     // Use setTimeout to wait for the next macrotask.
     return new Promise(resolve => setTimeout(resolve));
   }
@@ -46,7 +46,7 @@
   /** @param {ESimSetupFlowResult} eSimSetupFlowResult */
   function endFlowAndVerifyResult(eSimSetupFlowResult) {
     eSimPage.remove();
-    Polymer.dom.flush();
+    flush();
     assertEquals(
         chrome.metricsPrivate.getHistogramEnumValueCount(eSimSetupFlowResult),
         1);
@@ -80,8 +80,7 @@
     onlineNetwork.connectionState =
         chromeos.networkConfig.mojom.ConnectionStateType.kOnline;
     networkConfigRemote.addNetworksForTest([onlineNetwork]);
-    network_config.MojoInterfaceProviderImpl.getInstance().remote_ =
-        networkConfigRemote;
+    MojoInterfaceProviderImpl.getInstance().remote_ = networkConfigRemote;
   }
 
   /** Takes actively online network offline. */
@@ -110,17 +109,17 @@
     addOnlineWifiNetwork();
 
     chrome.metricsPrivate = new MockMetricsPrivate();
-    eSimManagerRemote = new cellular_setup.FakeESimManagerRemote();
-    cellular_setup.setESimManagerRemoteForTesting(eSimManagerRemote);
+    eSimManagerRemote = new FakeESimManagerRemote();
+    setESimManagerRemoteForTesting(eSimManagerRemote);
 
     document.addEventListener('focus-default-button', () => {
       focusDefaultButtonEventFired = true;
     });
 
     eSimPage = document.createElement('esim-flow-ui');
-    eSimPage.delegate = new cellular_setup.FakeCellularSetupDelegate();
+    eSimPage.delegate = new FakeCellularSetupDelegate();
     document.body.appendChild(eSimPage);
-    Polymer.dom.flush();
+    flush();
 
     ironPages = eSimPage.$$('iron-pages');
     profileLoadingPage = eSimPage.$$('#profileLoadingPage');
@@ -163,13 +162,12 @@
    * navigating forward. Asserts that the button_bar and page state is disabled
    * and busy during the install.
    * @param {HTMLElement} page
-   * @param {cellularSetup.ButtonState} previousBackButtonState
+   * @param {ButtonState} previousBackButtonState
    */
   async function navigateForwardForInstall(page, previousBackButtonState) {
     const checkShowBusyState =
         (page !== profileDiscoveryPage && page !== finalPage);
-    assertEquals(
-        eSimPage.buttonState.forward, cellularSetup.ButtonState.ENABLED);
+    assertEquals(eSimPage.buttonState.forward, ButtonState.ENABLED);
     assertEquals(eSimPage.buttonState.backward, previousBackButtonState);
     if (checkShowBusyState) {
       assertFalse(page.showBusy);
@@ -178,16 +176,14 @@
     // If back button is hidden before installation began, the new back button
     // state should also be hidden, if it was enabled new back button state
     // should be disabled while installation is taking place.
-    let newBackButtonState = cellularSetup.ButtonState.HIDDEN;
-    if (previousBackButtonState === cellularSetup.ButtonState.ENABLED) {
-      newBackButtonState = cellularSetup.ButtonState.DISABLED;
+    let newBackButtonState = ButtonState.HIDDEN;
+    if (previousBackButtonState === ButtonState.ENABLED) {
+      newBackButtonState = ButtonState.DISABLED;
     }
     eSimPage.navigateForward();
 
-    assertEquals(
-        eSimPage.buttonState.forward, cellularSetup.ButtonState.DISABLED);
-    assertEquals(
-        eSimPage.buttonState.cancel, cellularSetup.ButtonState.DISABLED);
+    assertEquals(eSimPage.buttonState.forward, ButtonState.DISABLED);
+    assertEquals(eSimPage.buttonState.cancel, ButtonState.DISABLED);
     assertEquals(eSimPage.buttonState.backward, newBackButtonState);
     if (checkShowBusyState) {
       assertTrue(page.showBusy);
@@ -212,13 +208,11 @@
   }
 
   async function assertFinalPageAndPressDoneButton(shouldBeShowingError) {
-    assertSelectedPage(cellular_setup.ESimPageName.FINAL, finalPage);
+    assertSelectedPage(ESimPageName.FINAL, finalPage);
     assertEquals(!!finalPage.$$('.error'), shouldBeShowingError);
-    assertEquals(
-        cellularSetup.ButtonState.ENABLED, eSimPage.buttonState.forward);
-    assertEquals(
-        cellularSetup.ButtonState.HIDDEN, eSimPage.buttonState.backward);
-    assertEquals(cellularSetup.ButtonState.HIDDEN, eSimPage.buttonState.cancel);
+    assertEquals(ButtonState.ENABLED, eSimPage.buttonState.forward);
+    assertEquals(ButtonState.HIDDEN, eSimPage.buttonState.backward);
+    assertEquals(ButtonState.HIDDEN, eSimPage.buttonState.cancel);
     assertEquals(eSimPage.forwardButtonLabel, 'Done');
     let exitCellularSetupEventFired = false;
     eSimPage.addEventListener('exit-cellular-setup', () => {
@@ -232,16 +226,16 @@
 
   /**
    * @param {boolean} forwardButtonShouldBeEnabled
-   * @param {cellularSetup.ButtonState} backButtonState
+   * @param {ButtonState} backButtonState
    */
   function assertButtonState(forwardButtonShouldBeEnabled, backButtonState) {
     const buttonState = eSimPage.buttonState;
     assertEquals(buttonState.backward, backButtonState);
-    assertEquals(buttonState.cancel, cellularSetup.ButtonState.ENABLED);
+    assertEquals(buttonState.cancel, ButtonState.ENABLED);
     assertEquals(
         buttonState.forward,
-        forwardButtonShouldBeEnabled ? cellularSetup.ButtonState.ENABLED :
-                                       cellularSetup.ButtonState.DISABLED);
+        forwardButtonShouldBeEnabled ? ButtonState.ENABLED :
+                                       ButtonState.DISABLED);
   }
 
   function assertFocusDefaultButtonEventFired() {
@@ -250,20 +244,18 @@
   }
 
   async function assertProfileLoadingPageAndContinue() {
-    assertSelectedPage(
-        cellular_setup.ESimPageName.PROFILE_LOADING, profileLoadingPage);
+    assertSelectedPage(ESimPageName.PROFILE_LOADING, profileLoadingPage);
     assertButtonState(
         /*forwardButtonShouldBeEnabled=*/ false,
-        /*backButtonState=*/ cellularSetup.ButtonState.HIDDEN);
+        /*backButtonState=*/ ButtonState.HIDDEN);
     await flushAsync();
   }
 
   function assertProfileDiscoveryPage() {
-    assertSelectedPage(
-        cellular_setup.ESimPageName.PROFILE_DISCOVERY, profileDiscoveryPage);
+    assertSelectedPage(ESimPageName.PROFILE_DISCOVERY, profileDiscoveryPage);
     assertButtonState(
         /*forwardButtonShouldBeEnabled*/ true,
-        /*backButtonState*/ cellularSetup.ButtonState.HIDDEN);
+        /*backButtonState*/ ButtonState.HIDDEN);
   }
 
   function assertActivationCodePage(
@@ -272,8 +264,7 @@
       // In the initial state, input should be cleared.
       assertEquals(activationCodePage.$$('#activationCode').value, '');
     }
-    assertSelectedPage(
-        cellular_setup.ESimPageName.ACTIVATION_CODE, activationCodePage);
+    assertSelectedPage(ESimPageName.ACTIVATION_CODE, activationCodePage);
     assertButtonState(forwardButtonShouldBeEnabled, backButtonState);
   }
 
@@ -283,8 +274,7 @@
       // In the initial state, input should be cleared.
       assertEquals(confirmationCodePage.$$('#confirmationCode').value, '');
     }
-    assertSelectedPage(
-        cellular_setup.ESimPageName.CONFIRMATION_CODE, confirmationCodePage);
+    assertSelectedPage(ESimPageName.CONFIRMATION_CODE, confirmationCodePage);
     assertButtonState(forwardButtonShouldBeEnabled, backButtonState);
   }
 
@@ -304,14 +294,14 @@
       // Should now be at the activation code page.
       assertActivationCodePage(
           /*forwardButtonShouldBeEnabled*/ false,
-          /*backButtonState*/ cellularSetup.ButtonState.HIDDEN);
+          /*backButtonState*/ ButtonState.HIDDEN);
       // Insert an activation code.
       activationCodePage.$$('#activationCode').value = ACTIVATION_CODE_VALID;
 
       // Forward button should now be enabled.
       assertActivationCodePage(
           /*forwardButtonShouldBeEnabled*/ true,
-          /*backButtonState*/ cellularSetup.ButtonState.HIDDEN);
+          /*backButtonState*/ ButtonState.HIDDEN);
     });
 
     test('Invalid activation code', async function() {
@@ -321,12 +311,12 @@
 
       await navigateForwardForInstall(
           activationCodePage,
-          /*backButtonState*/ cellularSetup.ButtonState.HIDDEN);
+          /*backButtonState*/ ButtonState.HIDDEN);
 
       // Install should fail and still be at activation code page.
       assertActivationCodePage(
           /*forwardButtonShouldBeEnabled*/ true,
-          /*backButtonState*/ cellularSetup.ButtonState.HIDDEN);
+          /*backButtonState*/ ButtonState.HIDDEN);
       assertTrue(activationCodePage.showError);
 
       endFlowAndVerifyResult(
@@ -336,7 +326,7 @@
     test('Valid activation code', async function() {
       await navigateForwardForInstall(
           activationCodePage,
-          /*backButtonState*/ cellularSetup.ButtonState.HIDDEN);
+          /*backButtonState*/ ButtonState.HIDDEN);
 
       // Should go to final page.
       await assertFinalPageAndPressDoneButton(false);
@@ -351,17 +341,17 @@
 
       await navigateForwardForInstall(
           activationCodePage,
-          /*backButtonState*/ cellularSetup.ButtonState.HIDDEN);
+          /*backButtonState*/ ButtonState.HIDDEN);
 
       // Confirmation code page should be showing.
       assertConfirmationCodePage(
           /*forwardButtonShouldBeEnabled*/ false,
-          /*backButtonState*/ cellularSetup.ButtonState.ENABLED);
+          /*backButtonState*/ ButtonState.ENABLED);
 
       euicc.setProfileInstallResultForTest(
           ash.cellularSetup.mojom.ProfileInstallResult.kSuccess);
       await enterConfirmationCode(
-          /*backButtonState*/ cellularSetup.ButtonState.ENABLED);
+          /*backButtonState*/ ButtonState.ENABLED);
 
       // Should go to final page.
       await assertFinalPageAndPressDoneButton(false);
@@ -376,22 +366,22 @@
 
       await navigateForwardForInstall(
           activationCodePage,
-          /*backButtonState*/ cellularSetup.ButtonState.HIDDEN);
+          /*backButtonState*/ ButtonState.HIDDEN);
 
       // Confirmation code page should be showing.
       assertConfirmationCodePage(
           /*forwardButtonShouldBeEnabled*/ false,
-          /*backButtonState*/ cellularSetup.ButtonState.ENABLED);
+          /*backButtonState*/ ButtonState.ENABLED);
 
       euicc.setProfileInstallResultForTest(
           ash.cellularSetup.mojom.ProfileInstallResult.kFailure);
       const confirmationCodeInput = await enterConfirmationCode(
-          /*backButtonState*/ cellularSetup.ButtonState.ENABLED);
+          /*backButtonState*/ ButtonState.ENABLED);
 
       // Should still be at confirmation code page with input showing error.
       assertConfirmationCodePage(
           /*forwardButtonShouldBeEnabled*/ true,
-          /*backButtonState*/ cellularSetup.ButtonState.ENABLED);
+          /*backButtonState*/ ButtonState.ENABLED);
       assertTrue(confirmationCodeInput.invalid);
 
       endFlowAndVerifyResult(ESimSetupFlowResult.INSTALL_FAIL);
@@ -404,12 +394,12 @@
 
       await navigateForwardForInstall(
           activationCodePage,
-          /*backButtonState*/ cellularSetup.ButtonState.HIDDEN);
+          /*backButtonState*/ ButtonState.HIDDEN);
 
       // Confirmation code page should be showing.
       assertConfirmationCodePage(
           /*forwardButtonShouldBeEnabled*/ false,
-          /*backButtonState*/ cellularSetup.ButtonState.ENABLED);
+          /*backButtonState*/ ButtonState.ENABLED);
       confirmationCodePage.$$('#confirmationCode').value = 'CONFIRMATION_CODE';
 
       eSimPage.navigateBackward();
@@ -418,7 +408,7 @@
       // Should now be at the activation code page.
       assertActivationCodePage(
           /*forwardButtonShouldBeEnabled*/ true,
-          /*backButtonState*/ cellularSetup.ButtonState.HIDDEN);
+          /*backButtonState*/ ButtonState.HIDDEN);
       assertEquals(
           activationCodePage.$$('#activationCode').value,
           ACTIVATION_CODE_VALID);
@@ -482,12 +472,12 @@
       // Confirmation code page should be showing.
       assertConfirmationCodePage(
           /*forwardButtonShouldBeEnabled*/ false,
-          /*backButtonState*/ cellularSetup.ButtonState.HIDDEN);
+          /*backButtonState*/ ButtonState.HIDDEN);
 
       profile.setProfileInstallResultForTest(
           ash.cellularSetup.mojom.ProfileInstallResult.kSuccess);
       await enterConfirmationCode(
-          /*backButtonState*/ cellularSetup.ButtonState.HIDDEN);
+          /*backButtonState*/ ButtonState.HIDDEN);
 
       // Should go to final page.
       await assertFinalPageAndPressDoneButton(false);
@@ -505,17 +495,17 @@
       // Confirmation code page should be showing.
       assertConfirmationCodePage(
           /*forwardButtonShouldBeEnabled*/ false,
-          /*backButtonState*/ cellularSetup.ButtonState.HIDDEN);
+          /*backButtonState*/ ButtonState.HIDDEN);
 
       profile.setProfileInstallResultForTest(
           ash.cellularSetup.mojom.ProfileInstallResult.kFailure);
       const confirmationCodeInput =
-          await enterConfirmationCode(cellularSetup.ButtonState.HIDDEN);
+          await enterConfirmationCode(ButtonState.HIDDEN);
 
       // Should still be at confirmation code page with input showing error.
       assertConfirmationCodePage(
           /*forwardButtonShouldBeEnabled*/ true,
-          /*backButtonState*/ cellularSetup.ButtonState.HIDDEN);
+          /*backButtonState*/ ButtonState.HIDDEN);
       assertTrue(confirmationCodeInput.invalid);
 
       endFlowAndVerifyResult(ESimSetupFlowResult.INSTALL_FAIL);
@@ -531,7 +521,7 @@
       // Confirmation code page should be showing.
       assertConfirmationCodePage(
           /*forwardButtonShouldBeEnabled*/ false,
-          /*backButtonState*/ cellularSetup.ButtonState.HIDDEN);
+          /*backButtonState*/ ButtonState.HIDDEN);
       confirmationCodePage.$$('#confirmationCode').value = 'CONFIRMATION_CODE';
 
       endFlowAndVerifyResult(
@@ -577,15 +567,14 @@
 
     function skipDiscovery() {
       // Simulate pressing 'Skip'.
-      assertTrue(
-          eSimPage.buttonState.forward === cellularSetup.ButtonState.ENABLED);
+      assertTrue(eSimPage.buttonState.forward === ButtonState.ENABLED);
       eSimPage.navigateForward();
-      Polymer.dom.flush();
+      flush();
 
       // Should now be at the activation code page.
       assertActivationCodePage(
           /*forwardButtonShouldBeEnabled*/ false,
-          /*backButtonState*/ cellularSetup.ButtonState.ENABLED);
+          /*backButtonState*/ ButtonState.ENABLED);
       assertFocusDefaultButtonEventFired();
 
       // Insert an activation code.
@@ -594,7 +583,7 @@
 
       assertActivationCodePage(
           /*forwardButtonShouldBeEnabled*/ true,
-          /*backButtonState*/ cellularSetup.ButtonState.ENABLED);
+          /*backButtonState*/ ButtonState.ENABLED);
     }
 
     test('Skip discovery flow', async function() {
@@ -602,7 +591,7 @@
 
       await navigateForwardForInstall(
           activationCodePage,
-          /*backButtonState*/ cellularSetup.ButtonState.ENABLED);
+          /*backButtonState*/ ButtonState.ENABLED);
 
       // Should now be at the final page.
       await assertFinalPageAndPressDoneButton(false);
@@ -621,12 +610,12 @@
 
           await navigateForwardForInstall(
               activationCodePage,
-              /*backButtonState*/ cellularSetup.ButtonState.ENABLED);
+              /*backButtonState*/ ButtonState.ENABLED);
 
           // Confirmation code page should be showing.
           assertConfirmationCodePage(
               /*forwardButtonShouldBeEnabled*/ false,
-              /*backButtonState*/ cellularSetup.ButtonState.ENABLED);
+              /*backButtonState*/ ButtonState.ENABLED);
           assertFocusDefaultButtonEventFired();
           confirmationCodePage.$$('#confirmationCode').value =
               'CONFIRMATION_CODE';
@@ -638,7 +627,7 @@
 
           assertActivationCodePage(
               /*forwardButtonShouldBeEnabled*/ true,
-              /*backButtonState*/ cellularSetup.ButtonState.ENABLED);
+              /*backButtonState*/ ButtonState.ENABLED);
           assertFocusDefaultButtonEventFired();
           assertEquals(
               activationCodePage.$$('#activationCode').value,
@@ -660,16 +649,15 @@
       // Select the first profile on the list.
       const profileList = profileDiscoveryPage.$$('#profileList');
       profileList.selectItem(profileList.items[0]);
-      Polymer.dom.flush();
+      flush();
 
       // The 'Forward' button should now be enabled.
-      assertTrue(
-          eSimPage.buttonState.forward === cellularSetup.ButtonState.ENABLED);
+      assertTrue(eSimPage.buttonState.forward === ButtonState.ENABLED);
 
       // Simulate pressing 'Forward'.
       await navigateForwardForInstall(
           profileDiscoveryPage,
-          /*backButtonState*/ cellularSetup.ButtonState.HIDDEN);
+          /*backButtonState*/ ButtonState.HIDDEN);
     }
 
     test('Select profile flow', async function() {
@@ -694,13 +682,13 @@
       // Confirmation code page should be showing.
       assertConfirmationCodePage(
           /*forwardButtonShouldBeEnabled*/ false,
-          /*backButtonState*/ cellularSetup.ButtonState.ENABLED);
+          /*backButtonState*/ ButtonState.ENABLED);
       assertFocusDefaultButtonEventFired();
 
       profileList.profiles[0].setProfileInstallResultForTest(
           ash.cellularSetup.mojom.ProfileInstallResult.kSuccess);
       await enterConfirmationCode(
-          /*backButtonState*/ cellularSetup.ButtonState.ENABLED);
+          /*backButtonState*/ ButtonState.ENABLED);
 
       // Should go to final page.
       await assertFinalPageAndPressDoneButton(false);
@@ -724,7 +712,7 @@
           // Confirmation code page should be showing.
           assertConfirmationCodePage(
               /*forwardButtonShouldBeEnabled*/ false,
-              /*backButtonState*/ cellularSetup.ButtonState.ENABLED);
+              /*backButtonState*/ ButtonState.ENABLED);
           confirmationCodePage.$$('#confirmationCode').value =
               'CONFIRMATION_CODE';
 
@@ -764,8 +752,7 @@
         pSimNetwork.connectionState =
             chromeos.networkConfig.mojom.ConnectionStateType.kConnected;
         networkConfigRemote.addNetworksForTest([pSimNetwork]);
-        network_config.MojoInterfaceProviderImpl.getInstance().remote_ =
-            networkConfigRemote;
+        MojoInterfaceProviderImpl.getInstance().remote_ = networkConfigRemote;
         await flushAsync();
 
         assertEquals(
diff --git a/chrome/test/data/webui/cr_components/chromeos/cellular_setup/fake_barcode_detector.js b/chrome/test/data/webui/cr_components/chromeos/cellular_setup/fake_barcode_detector.js
index df23a8b..f89a3b23 100644
--- a/chrome/test/data/webui/cr_components/chromeos/cellular_setup/fake_barcode_detector.js
+++ b/chrome/test/data/webui/cr_components/chromeos/cellular_setup/fake_barcode_detector.js
@@ -18,7 +18,7 @@
 /**
  * @implements {BarcodeDetector}
  */
-/* #export */ class FakeBarcodeDetector {
+export class FakeBarcodeDetector {
   constructor() {}
 
   /** @override */
@@ -53,7 +53,7 @@
 /**
  * @implements {ImageCapture}
  */
-/* #export */ class FakeImageCapture {
+export class FakeImageCapture {
   constructor(mediaStream) {
     this.track = {
       readyState: 'live',
@@ -66,4 +66,4 @@
   grabFrame() {
     return null;
   }
-}
\ No newline at end of file
+}
diff --git a/chrome/test/data/webui/cr_components/chromeos/cellular_setup/fake_canvas_context.js b/chrome/test/data/webui/cr_components/chromeos/cellular_setup/fake_canvas_context.js
index 4ddb3b1..2314b85b 100644
--- a/chrome/test/data/webui/cr_components/chromeos/cellular_setup/fake_canvas_context.js
+++ b/chrome/test/data/webui/cr_components/chromeos/cellular_setup/fake_canvas_context.js
@@ -2,41 +2,36 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-cr.define('cellular_setup', function() {
-  /**
-   * @implements {CanvasRenderingContext2D}
-   */
-  /* #export */ class FakeCanvasContext {
-    constructor() {
-      this.clearRectCalls_ = [];
-      this.fillRectCalls_ = [];
-    }
-
-    /** @override */
-    clearRect(x, y, width, height) {
-      this.clearRectCalls_.push([x, y, width, height]);
-    }
-
-    /** @override */
-    fillRect(x, y, width, height) {
-      this.fillRectCalls_.push([x, y, width, height]);
-    }
-
-    /**
-     * @return {Array<Array<int>}
-     */
-    getClearRectCalls() {
-      return this.clearRectCalls_;
-    }
-
-    /**
-     * @return {Array<Array<int>}
-     */
-    getFillRectCalls() {
-      return this.fillRectCalls_;
-    }
+/**
+ * @implements {CanvasRenderingContext2D}
+ */
+export class FakeCanvasContext {
+  constructor() {
+    this.clearRectCalls_ = [];
+    this.fillRectCalls_ = [];
   }
 
-  // #cr_define_end
-  return {FakeCanvasContext};
-});
\ No newline at end of file
+  /** @override */
+  clearRect(x, y, width, height) {
+    this.clearRectCalls_.push([x, y, width, height]);
+  }
+
+  /** @override */
+  fillRect(x, y, width, height) {
+    this.fillRectCalls_.push([x, y, width, height]);
+  }
+
+  /**
+   * @return {Array<Array<int>}
+   */
+  getClearRectCalls() {
+    return this.clearRectCalls_;
+  }
+
+  /**
+   * @return {Array<Array<int>}
+   */
+  getFillRectCalls() {
+    return this.fillRectCalls_;
+  }
+}
diff --git a/chrome/test/data/webui/cr_components/chromeos/cellular_setup/fake_cellular_setup_delegate.js b/chrome/test/data/webui/cr_components/chromeos/cellular_setup/fake_cellular_setup_delegate.js
index 69ad6f9a..20e0d17 100644
--- a/chrome/test/data/webui/cr_components/chromeos/cellular_setup/fake_cellular_setup_delegate.js
+++ b/chrome/test/data/webui/cr_components/chromeos/cellular_setup/fake_cellular_setup_delegate.js
@@ -2,26 +2,17 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// clang-format off
-// #import {CellularSetupDelegate} from 'chrome://resources/cr_components/chromeos/cellular_setup/cellular_setup_delegate.m.js';
-// clang-format on
+import {CellularSetupDelegate} from 'chrome://resources/cr_components/chromeos/cellular_setup/cellular_setup_delegate.m.js';
 
-cr.define('cellular_setup', function() {
-  /** @implements {cellular_setup.CellularSetupDelegate} */
-  /* #export */ class FakeCellularSetupDelegate {
-    /** @override */
-    shouldShowPageTitle() {
-      return false;
-    }
-
-    /** @override */
-    shouldShowCancelButton() {
-      return true;
-    }
+/** @implements {CellularSetupDelegate} */
+export class FakeCellularSetupDelegate {
+  /** @override */
+  shouldShowPageTitle() {
+    return false;
   }
 
-  // #cr_define_end
-  return {
-    FakeCellularSetupDelegate: FakeCellularSetupDelegate,
-  };
-});
\ No newline at end of file
+  /** @override */
+  shouldShowCancelButton() {
+    return true;
+  }
+}
diff --git a/chrome/test/data/webui/cr_components/chromeos/cellular_setup/fake_cellular_setup_remote.js b/chrome/test/data/webui/cr_components/chromeos/cellular_setup/fake_cellular_setup_remote.js
index b2e47b1..28ebb80 100644
--- a/chrome/test/data/webui/cr_components/chromeos/cellular_setup/fake_cellular_setup_remote.js
+++ b/chrome/test/data/webui/cr_components/chromeos/cellular_setup/fake_cellular_setup_remote.js
@@ -2,50 +2,41 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+/**
+ * @implements {ash.cellularSetup.mojom.CarrierPortalHandlerInterface}
+ */
+export class FakeCarrierPortalHandlerRemote {
+  constructor() {}
 
-cr.define('cellular_setup', function() {
+  /** @override */
+  onCarrierPortalStatusChange(status) {
+    this.status_ = status;
+  }
+}
+
+/** @implements {ash.cellularSetup.mojom.CellularSetupInterface} */
+export class FakeCellularSetupRemote {
   /**
-   * @implements {ash.cellularSetup.mojom.CarrierPortalHandlerInterface}
+   * @param {!FakeCarrierPortalHandlerRemote} handler
    */
-  /* #export */ class FakeCarrierPortalHandlerRemote {
-    constructor() {}
-
-    /** @override */
-    onCarrierPortalStatusChange(status) {
-      this.status_ = status;
-    }
+  constructor(handler) {
+    this.carrierHandler_ = handler;
   }
 
-  /** @implements {ash.cellularSetup.mojom.CellularSetupInterface} */
-  /* #export */ class FakeCellularSetupRemote {
-    /**
-     * @param {!FakeCarrierPortalHandlerRemote} handler
-     */
-    constructor(handler) {
-      this.carrierHandler_ = handler;
-    }
-
-    /** @override */
-    startActivation(delegate) {
-      this.delegate_ = delegate;
-      return new Promise((resolve, reject) => {
-        setTimeout(function() {
-          resolve({observer: this.carrierHandler_});
-        });
+  /** @override */
+  startActivation(delegate) {
+    this.delegate_ = delegate;
+    return new Promise((resolve, reject) => {
+      setTimeout(function() {
+        resolve({observer: this.carrierHandler_});
       });
-    }
-
-    /**
-     * @returns {!ash.cellularSetup.mojom.ActivationDelegateRemote}
-     */
-    getLastActivationDelegate() {
-      return this.delegate_;
-    }
+    });
   }
 
-  // #cr_define_end
-  return {
-    FakeCellularSetupRemote: FakeCellularSetupRemote,
-    FakeCarrierPortalHandlerRemote: FakeCarrierPortalHandlerRemote,
-  };
-});
+  /**
+   * @returns {!ash.cellularSetup.mojom.ActivationDelegateRemote}
+   */
+  getLastActivationDelegate() {
+    return this.delegate_;
+  }
+}
diff --git a/chrome/test/data/webui/cr_components/chromeos/cellular_setup/fake_esim_manager_remote.js b/chrome/test/data/webui/cr_components/chromeos/cellular_setup/fake_esim_manager_remote.js
index a4883190..b4d8754 100644
--- a/chrome/test/data/webui/cr_components/chromeos/cellular_setup/fake_esim_manager_remote.js
+++ b/chrome/test/data/webui/cr_components/chromeos/cellular_setup/fake_esim_manager_remote.js
@@ -2,378 +2,370 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+/** @implements {ash.cellularSetup.mojom.ESimProfile} */
+class FakeProfile {
+  constructor(eid, iccid, fakeEuicc) {
+    this.properties = {
+      eid,
+      iccid,
+      activationCode: 'activation-code-' + iccid,
+      name: {
+        data: this.stringToCharCodeArray_('profile' + iccid),
+      },
+      nickname: {
+        data: this.stringToCharCodeArray_('profile' + iccid),
+      },
+      serviceProvider: {
+        data: this.stringToCharCodeArray_('provider' + iccid),
+      },
+      state: ash.cellularSetup.mojom.ProfileState.kPending,
+    };
 
-cr.define('cellular_setup', function() {
-  /** @implements {ash.cellularSetup.mojom.ESimProfile} */
-  class FakeProfile {
-    constructor(eid, iccid, fakeEuicc) {
-      this.properties = {
-        eid,
-        iccid,
-        activationCode: 'activation-code-' + iccid,
-        name: {
-          data: this.stringToCharCodeArray_('profile' + iccid),
-        },
-        nickname: {
-          data: this.stringToCharCodeArray_('profile' + iccid),
-        },
-        serviceProvider: {
-          data: this.stringToCharCodeArray_('provider' + iccid),
-        },
-        state: ash.cellularSetup.mojom.ProfileState.kPending,
-      };
+    this.deferGetProperties_ = false;
+    this.deferredGetPropertiesPromises_ = [];
+    this.fakeEuicc_ = fakeEuicc;
+  }
 
-      this.deferGetProperties_ = false;
-      this.deferredGetPropertiesPromises_ = [];
-      this.fakeEuicc_ = fakeEuicc;
-    }
-
-    /**
-     * @override
-     * @return {!Promise<{properties:
-     *     ash.cellularSetup.mojom.ESimProfileProperties},}>}
-     */
-    getProperties() {
-      if (this.deferGetProperties_) {
-        const deferred = this.deferredPromise_();
-        this.deferredGetPropertiesPromises_.push(deferred);
-        return deferred.promise;
-      } else {
-        return Promise.resolve({
-          properties: this.properties,
-        });
-      }
-    }
-
-    /**
-     * @param {boolean} defer
-     */
-    setDeferGetProperties(defer) {
-      this.deferGetProperties_ = defer;
-    }
-
-    resolveLastGetPropertiesPromise() {
-      if (!this.deferredGetPropertiesPromises_.length) {
-        return;
-      }
-      const deferred = this.deferredGetPropertiesPromises_.pop();
-      deferred.resolve({properties: this.properties});
-    }
-
-    /**
-     * @override
-     * @param {string} confirmationCode
-     * @return {!Promise<{result:
-     *     ash.cellularSetup.mojom.ProfileInstallResult},}>}
-     */
-    installProfile(confirmationCode) {
-      if (!this.profileInstallResult_ ||
-          this.profileInstallResult_ ===
-              ash.cellularSetup.mojom.ProfileInstallResult.kSuccess) {
-        this.properties.state = ash.cellularSetup.mojom.ProfileState.kActive;
-      }
-      this.fakeEuicc_.notifyProfileChangedForTest(this);
-      this.fakeEuicc_.notifyProfileListChangedForTest();
-      // Simulate a delay in response. This is neccessary because a few tests
-      // require UI to be in installing state.
-      return new Promise(
-          resolve => setTimeout(
-              () => resolve({
-                result: this.profileInstallResult_ ?
-                    this.profileInstallResult_ :
-                    ash.cellularSetup.mojom.ProfileInstallResult.kSuccess
-              }),
-              0));
-    }
-
-    /**
-     * @param {ash.cellularSetup.mojom.ProfileInstallResult} result
-     */
-    setProfileInstallResultForTest(result) {
-      this.profileInstallResult_ = result;
-    }
-
-    /**
-     * @param {ash.cellularSetup.mojom.ESimOperationResult} result
-     */
-    setEsimOperationResultForTest(result) {
-      this.esimOperationResult_ = result;
-    }
-
-    /**
-     * @param {string} string
-     * @private
-     */
-    stringToCharCodeArray_(string) {
-      const res = [];
-      for (let i = 0; i < string.length; i++) {
-        res.push(string.charCodeAt(i));
-      }
-      return res;
-    }
-
-    /**
-     * @return {Object}
-     * @private
-     */
-    deferredPromise_() {
-      const deferred = {};
-      const promise = new Promise(function(resolve, reject) {
-        deferred.resolve = resolve;
-        deferred.reject = reject;
-      });
-      deferred.promise = promise;
-      return deferred;
-    }
-
-    /**
-     * @override
-     * @param {?mojoBase.mojom.String16} nickname
-     * @return {!Promise<{result:
-     *     ash.cellularSetup.mojom.ESimOperationResult},}>}
-     */
-    setProfileNickname(nickname) {
-      if (!this.esimOperationResult_ ||
-          this.esimOperationResult_ ===
-              ash.cellularSetup.mojom.ESimOperationResult.kSuccess) {
-        this.properties.nickname = nickname;
-      }
-
-      this.deferredSetProfileNicknamePromise_ = this.deferredPromise_();
-      return this.deferredSetProfileNicknamePromise_.promise;
-    }
-
-    /** @private */
-    resolveSetProfileNicknamePromise_() {
-      this.deferredSetProfileNicknamePromise_.resolve({
-        result: this.esimOperationResult_ ?
-            this.esimOperationResult_ :
-            ash.cellularSetup.mojom.ESimOperationResult.kSuccess
-      });
-    }
-
-    /** @override */
-    uninstallProfile() {
-      this.fakeEuicc_.notifyProfileChangedForTest(this);
-      this.defferedUninstallProfilePromise_ = this.deferredPromise_();
-      return this.defferedUninstallProfilePromise_.promise;
-    }
-
-    /** @return {Promise<void>} */
-    async resolveUninstallProfilePromise() {
-      if (!this.esimOperationResult_ ||
-          this.esimOperationResult_ ===
-              ash.cellularSetup.mojom.ESimOperationResult.kSuccess) {
-        const removeProfileResult =
-            await this.fakeEuicc_.removeProfileForTest(this.properties.iccid);
-        this.defferedUninstallProfilePromise_.resolve(removeProfileResult);
-        return;
-      }
-
-      this.defferedUninstallProfilePromise_.resolve({
-        result: this.esimOperationResult_ ?
-            this.esimOperationResult_ :
-            ash.cellularSetup.mojom.ESimOperationResult.kSuccess
+  /**
+   * @override
+   * @return {!Promise<{properties:
+   *     ash.cellularSetup.mojom.ESimProfileProperties},}>}
+   */
+  getProperties() {
+    if (this.deferGetProperties_) {
+      const deferred = this.deferredPromise_();
+      this.deferredGetPropertiesPromises_.push(deferred);
+      return deferred.promise;
+    } else {
+      return Promise.resolve({
+        properties: this.properties,
       });
     }
   }
 
-  /** @implements {ash.cellularSetup.mojom.Euicc} */
-  class FakeEuicc {
-    constructor(eid, numProfiles, fakeESimManager) {
-      this.fakeESimManager_ = fakeESimManager;
-      this.properties = {eid};
-      this.profiles_ = [];
-      for (let i = 0; i < numProfiles; i++) {
-        this.addProfile();
+  /**
+   * @param {boolean} defer
+   */
+  setDeferGetProperties(defer) {
+    this.deferGetProperties_ = defer;
+  }
+
+  resolveLastGetPropertiesPromise() {
+    if (!this.deferredGetPropertiesPromises_.length) {
+      return;
+    }
+    const deferred = this.deferredGetPropertiesPromises_.pop();
+    deferred.resolve({properties: this.properties});
+  }
+
+  /**
+   * @override
+   * @param {string} confirmationCode
+   * @return {!Promise<{result:
+   *     ash.cellularSetup.mojom.ProfileInstallResult},}>}
+   */
+  installProfile(confirmationCode) {
+    if (!this.profileInstallResult_ ||
+        this.profileInstallResult_ ===
+            ash.cellularSetup.mojom.ProfileInstallResult.kSuccess) {
+      this.properties.state = ash.cellularSetup.mojom.ProfileState.kActive;
+    }
+    this.fakeEuicc_.notifyProfileChangedForTest(this);
+    this.fakeEuicc_.notifyProfileListChangedForTest();
+    // Simulate a delay in response. This is neccessary because a few tests
+    // require UI to be in installing state.
+    return new Promise(
+        resolve => setTimeout(
+            () => resolve({
+              result: this.profileInstallResult_ ?
+                  this.profileInstallResult_ :
+                  ash.cellularSetup.mojom.ProfileInstallResult.kSuccess
+            }),
+            0));
+  }
+
+  /**
+   * @param {ash.cellularSetup.mojom.ProfileInstallResult} result
+   */
+  setProfileInstallResultForTest(result) {
+    this.profileInstallResult_ = result;
+  }
+
+  /**
+   * @param {ash.cellularSetup.mojom.ESimOperationResult} result
+   */
+  setEsimOperationResultForTest(result) {
+    this.esimOperationResult_ = result;
+  }
+
+  /**
+   * @param {string} string
+   * @private
+   */
+  stringToCharCodeArray_(string) {
+    const res = [];
+    for (let i = 0; i < string.length; i++) {
+      res.push(string.charCodeAt(i));
+    }
+    return res;
+  }
+
+  /**
+   * @return {Object}
+   * @private
+   */
+  deferredPromise_() {
+    const deferred = {};
+    const promise = new Promise(function(resolve, reject) {
+      deferred.resolve = resolve;
+      deferred.reject = reject;
+    });
+    deferred.promise = promise;
+    return deferred;
+  }
+
+  /**
+   * @override
+   * @param {?mojoBase.mojom.String16} nickname
+   * @return {!Promise<{result:
+   *     ash.cellularSetup.mojom.ESimOperationResult},}>}
+   */
+  setProfileNickname(nickname) {
+    if (!this.esimOperationResult_ ||
+        this.esimOperationResult_ ===
+            ash.cellularSetup.mojom.ESimOperationResult.kSuccess) {
+      this.properties.nickname = nickname;
+    }
+
+    this.deferredSetProfileNicknamePromise_ = this.deferredPromise_();
+    return this.deferredSetProfileNicknamePromise_.promise;
+  }
+
+  /** @private */
+  resolveSetProfileNicknamePromise_() {
+    this.deferredSetProfileNicknamePromise_.resolve({
+      result: this.esimOperationResult_ ?
+          this.esimOperationResult_ :
+          ash.cellularSetup.mojom.ESimOperationResult.kSuccess
+    });
+  }
+
+  /** @override */
+  uninstallProfile() {
+    this.fakeEuicc_.notifyProfileChangedForTest(this);
+    this.defferedUninstallProfilePromise_ = this.deferredPromise_();
+    return this.defferedUninstallProfilePromise_.promise;
+  }
+
+  /** @return {Promise<void>} */
+  async resolveUninstallProfilePromise() {
+    if (!this.esimOperationResult_ ||
+        this.esimOperationResult_ ===
+            ash.cellularSetup.mojom.ESimOperationResult.kSuccess) {
+      const removeProfileResult =
+          await this.fakeEuicc_.removeProfileForTest(this.properties.iccid);
+      this.defferedUninstallProfilePromise_.resolve(removeProfileResult);
+      return;
+    }
+
+    this.defferedUninstallProfilePromise_.resolve({
+      result: this.esimOperationResult_ ?
+          this.esimOperationResult_ :
+          ash.cellularSetup.mojom.ESimOperationResult.kSuccess
+    });
+  }
+}
+
+/** @implements {ash.cellularSetup.mojom.Euicc} */
+class FakeEuicc {
+  constructor(eid, numProfiles, fakeESimManager) {
+    this.fakeESimManager_ = fakeESimManager;
+    this.properties = {eid};
+    this.profiles_ = [];
+    for (let i = 0; i < numProfiles; i++) {
+      this.addProfile();
+    }
+    this.requestPendingProfilesResult_ =
+        ash.cellularSetup.mojom.ESimOperationResult.kSuccess;
+  }
+
+  /**
+   * @override
+   * @return {!Promise<{properties:
+   *     ash.cellularSetup.mojom.EuiccProperties},}>}
+   */
+  getProperties() {
+    return Promise.resolve({properties: this.properties});
+  }
+
+  /**
+   * @override
+   * @return {!Promise<{result:
+   *     ash.cellularSetup.mojom.ESimOperationResult},}>}
+   */
+  requestPendingProfiles() {
+    return Promise.resolve({
+      result: this.requestPendingProfilesResult_,
+    });
+  }
+
+  /**
+   * @override
+   * @return {!Promise<{profiles: Array<!ESimProfile>,}>}
+   */
+  getProfileList() {
+    return Promise.resolve({
+      profiles: this.profiles_,
+    });
+  }
+
+  /**
+   * @override
+   * @return {!Promise<{qrCode: ash.cellularSetup.mojom.QRCode} | null>}
+   */
+  getEidQRCode() {
+    if (this.eidQRCode_) {
+      return Promise.resolve({qrCode: this.eidQRCode_});
+    } else {
+      return Promise.resolve(null);
+    }
+  }
+
+  /**
+   * @override
+   * @param {string} activationCode
+   * @param {string} confirmationCode
+   * @return {!Promise<{result:
+   *     ash.cellularSetup.mojom.ProfileInstallResult},}>}
+   */
+  installProfileFromActivationCode(activationCode, confirmationCode) {
+    this.notifyProfileListChangedForTest();
+    return Promise.resolve({
+      result: this.profileInstallResult_ ?
+          this.profileInstallResult_ :
+          ash.cellularSetup.mojom.ProfileInstallResult.kSuccess,
+    });
+  }
+
+  /**
+   * @param {ash.cellularSetup.mojom.ESimOperationResult} result
+   */
+  setRequestPendingProfilesResult(result) {
+    this.requestPendingProfilesResult_ = result;
+  }
+
+  /**
+   * @param {ash.cellularSetup.mojom.ProfileInstallResult} result
+   */
+  setProfileInstallResultForTest(result) {
+    this.profileInstallResult_ = result;
+  }
+
+  /**
+   * @param {ash.cellularSetup.mojom.QRCode} qrcode
+   */
+  setEidQRCodeForTest(qrcode) {
+    this.eidQRCode_ = qrcode;
+  }
+
+  /**
+   * @param {string} iccid
+   */
+  async removeProfileForTest(iccid) {
+    const result = [];
+    let profileRemoved = false;
+    for (const profile of this.profiles_) {
+      const property = await profile.getProperties();
+      if (property.properties.iccid === iccid) {
+        profileRemoved = true;
+        continue;
       }
-      this.requestPendingProfilesResult_ =
-          ash.cellularSetup.mojom.ESimOperationResult.kSuccess;
+      result.push(profile);
     }
+    this.profiles_ = result;
 
-    /**
-     * @override
-     * @return {!Promise<{properties:
-     *     ash.cellularSetup.mojom.EuiccProperties},}>}
-     */
-    getProperties() {
-      return Promise.resolve({properties: this.properties});
-    }
-
-    /**
-     * @override
-     * @return {!Promise<{result:
-     *     ash.cellularSetup.mojom.ESimOperationResult},}>}
-     */
-    requestPendingProfiles() {
-      return Promise.resolve({
-        result: this.requestPendingProfilesResult_,
-      });
-    }
-
-    /**
-     * @override
-     * @return {!Promise<{profiles: Array<!ESimProfile>,}>}
-     */
-    getProfileList() {
-      return Promise.resolve({
-        profiles: this.profiles_,
-      });
-    }
-
-    /**
-     * @override
-     * @return {!Promise<{qrCode: ash.cellularSetup.mojom.QRCode} | null>}
-     */
-    getEidQRCode() {
-      if (this.eidQRCode_) {
-        return Promise.resolve({qrCode: this.eidQRCode_});
-      } else {
-        return Promise.resolve(null);
-      }
-    }
-
-    /**
-     * @override
-     * @param {string} activationCode
-     * @param {string} confirmationCode
-     * @return {!Promise<{result:
-     *     ash.cellularSetup.mojom.ProfileInstallResult},}>}
-     */
-    installProfileFromActivationCode(activationCode, confirmationCode) {
+    if (profileRemoved) {
       this.notifyProfileListChangedForTest();
-      return Promise.resolve({
-        result: this.profileInstallResult_ ?
-            this.profileInstallResult_ :
-            ash.cellularSetup.mojom.ProfileInstallResult.kSuccess,
-      });
+      return {result: ash.cellularSetup.mojom.ESimOperationResult.kSuccess};
     }
+    return {result: ash.cellularSetup.mojom.ESimOperationResult.kFailure};
+  }
 
-    /**
-     * @param {ash.cellularSetup.mojom.ESimOperationResult} result
-     */
-    setRequestPendingProfilesResult(result) {
-      this.requestPendingProfilesResult_ = result;
-    }
+  /**
+   * @param {FakeProfile} profile
+   */
+  notifyProfileChangedForTest(profile) {
+    this.fakeESimManager_.notifyProfileChangedForTest(profile);
+  }
 
-    /**
-     * @param {ash.cellularSetup.mojom.ProfileInstallResult} result
-     */
-    setProfileInstallResultForTest(result) {
-      this.profileInstallResult_ = result;
-    }
+  notifyProfileListChangedForTest() {
+    this.fakeESimManager_.notifyProfileListChangedForTest(this);
+  }
 
-    /**
-     * @param {ash.cellularSetup.mojom.QRCode} qrcode
-     */
-    setEidQRCodeForTest(qrcode) {
-      this.eidQRCode_ = qrcode;
-    }
+  /** @private */
+  addProfile() {
+    const iccid = this.profiles_.length + 1 + '';
+    this.profiles_.push(new FakeProfile(this.properties.eid, iccid, this));
+  }
+}
 
-    /**
-     * @param {string} iccid
-     */
-    async removeProfileForTest(iccid) {
-      const result = [];
-      let profileRemoved = false;
-      for (const profile of this.profiles_) {
-        const property = await profile.getProperties();
-        if (property.properties.iccid === iccid) {
-          profileRemoved = true;
-          continue;
-        }
-        result.push(profile);
-      }
-      this.profiles_ = result;
+/** @implements {ash.cellularSetup.mojom.ESimManagerInterface} */
+export class FakeESimManagerRemote {
+  constructor() {
+    this.euiccs_ = [];
+    this.observers_ = [];
+  }
 
-      if (profileRemoved) {
-        this.notifyProfileListChangedForTest();
-        return {result: ash.cellularSetup.mojom.ESimOperationResult.kSuccess};
-      }
-      return {result: ash.cellularSetup.mojom.ESimOperationResult.kFailure};
-    }
+  /**
+   * @override
+   * @return {!Promise<{euiccs: !Array<!Euicc>,}>}
+   */
+  getAvailableEuiccs() {
+    return Promise.resolve({
+      euiccs: this.euiccs_,
+    });
+  }
 
-    /**
-     * @param {FakeProfile} profile
-     */
-    notifyProfileChangedForTest(profile) {
-      this.fakeESimManager_.notifyProfileChangedForTest(profile);
-    }
+  /**
+   * @param {number} numProfiles The number of profiles the EUICC has.
+   * @return {FakeEuicc} The euicc that was added.
+   */
+  addEuiccForTest(numProfiles) {
+    const eid = this.euiccs_.length + 1 + '';
+    const euicc = new FakeEuicc(eid, numProfiles, this);
+    this.euiccs_.push(euicc);
+    this.notifyAvailableEuiccListChanged();
+    return euicc;
+  }
 
-    notifyProfileListChangedForTest() {
-      this.fakeESimManager_.notifyProfileListChangedForTest(this);
-    }
+  /**
+   * @param {!ash.cellularSetup.mojom.ESimManagerObserverInterface} observer
+   */
+  addObserver(observer) {
+    this.observers_.push(observer);
+  }
 
-    /** @private */
-    addProfile() {
-      const iccid = this.profiles_.length + 1 + '';
-      this.profiles_.push(new FakeProfile(this.properties.eid, iccid, this));
+  notifyAvailableEuiccListChanged() {
+    for (const observer of this.observers_) {
+      observer.onAvailableEuiccListChanged();
     }
   }
 
-  /** @implements {ash.cellularSetup.mojom.ESimManagerInterface} */
-  /* #export */ class FakeESimManagerRemote {
-    constructor() {
-      this.euiccs_ = [];
-      this.observers_ = [];
-    }
-
-    /**
-     * @override
-     * @return {!Promise<{euiccs: !Array<!Euicc>,}>}
-     */
-    getAvailableEuiccs() {
-      return Promise.resolve({
-        euiccs: this.euiccs_,
-      });
-    }
-
-    /**
-     * @param {number} numProfiles The number of profiles the EUICC has.
-     * @return {FakeEuicc} The euicc that was added.
-     */
-    addEuiccForTest(numProfiles) {
-      const eid = this.euiccs_.length + 1 + '';
-      const euicc = new FakeEuicc(eid, numProfiles, this);
-      this.euiccs_.push(euicc);
-      this.notifyAvailableEuiccListChanged();
-      return euicc;
-    }
-
-    /**
-     * @param {!ash.cellularSetup.mojom.ESimManagerObserverInterface} observer
-     */
-    addObserver(observer) {
-      this.observers_.push(observer);
-    }
-
-    notifyAvailableEuiccListChanged() {
-      for (const observer of this.observers_) {
-        observer.onAvailableEuiccListChanged();
-      }
-    }
-
-    /**
-     * @param {FakeEuicc} euicc
-     */
-    notifyProfileListChangedForTest(euicc) {
-      for (const observer of this.observers_) {
-        observer.onProfileListChanged(euicc);
-      }
-    }
-
-    /**
-     * @param {FakeProfile} profile
-     */
-    notifyProfileChangedForTest(profile) {
-      for (const observer of this.observers_) {
-        observer.onProfileChanged(profile);
-      }
+  /**
+   * @param {FakeEuicc} euicc
+   */
+  notifyProfileListChangedForTest(euicc) {
+    for (const observer of this.observers_) {
+      observer.onProfileListChanged(euicc);
     }
   }
 
-  // #cr_define_end
-  return {
-    FakeESimManagerRemote: FakeESimManagerRemote,
-  };
-});
+  /**
+   * @param {FakeProfile} profile
+   */
+  notifyProfileChangedForTest(profile) {
+    for (const observer of this.observers_) {
+      observer.onProfileChanged(profile);
+    }
+  }
+}
diff --git a/chrome/test/data/webui/cr_components/chromeos/cellular_setup/fake_media_devices.js b/chrome/test/data/webui/cr_components/chromeos/cellular_setup/fake_media_devices.js
index 623651b..e1333ab7 100644
--- a/chrome/test/data/webui/cr_components/chromeos/cellular_setup/fake_media_devices.js
+++ b/chrome/test/data/webui/cr_components/chromeos/cellular_setup/fake_media_devices.js
@@ -2,110 +2,101 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-cr.define('cellular_setup', function() {
+/**
+ * @implements {MediaDevices}
+ */
+export class FakeMediaDevices {
+  constructor() {
+    this.isStreamingUserFacingCamera = true;
+    this.devices_ = [];
+
+    /** @private {?function()} */
+    this.enumerateDevicesResolver_ = null;
+
+    /** @private {?function()} */
+    this.getMediaDevicesResolver_ = null;
+  }
+
+  /** @override */
+  addEventListener(type, listener) {
+    this.deviceChangeListener_ = listener;
+  }
+
+  /** @override */
+  enumerateDevices() {
+    return new Promise((res, rej) => {
+      this.enumerateDevicesResolver_ = res;
+    });
+  }
+
   /**
-   * @implements {MediaDevices}
+   * Resolves promise returned from enumerateDevices().
    */
-  /* #export */ class FakeMediaDevices {
-    constructor() {
-      this.isStreamingUserFacingCamera = true;
-      this.devices_ = [];
+  resolveEnumerateDevices() {
+    assertTrue(
+        !!this.enumerateDevicesResolver_, 'enumerateDevices was not called');
+    this.enumerateDevicesResolver_(this.devices_);
+  }
 
-      /** @private {?function()} */
-      this.enumerateDevicesResolver_ = null;
+  /** @override */
+  getSupportedConstraints() {
+    return null;
+  }
 
-      /** @private {?function()} */
-      this.getMediaDevicesResolver_ = null;
-    }
+  /** @override */
+  getDisplayMedia() {
+    return new Promise((res, rej) => {
+      res(null);
+    });
+  }
 
-    /** @override */
-    addEventListener(type, listener) {
-      this.deviceChangeListener_ = listener;
-    }
+  /** @override */
+  getUserMedia(constraints) {
+    this.isStreamingUserFacingCamera = constraints.video.facingMode === 'user';
+    return new Promise((res, rej) => {
+      this.getMediaDevicesResolver_ = res;
+    });
+  }
 
-    /** @override */
-    enumerateDevices() {
-      return new Promise((res, rej) => {
-        this.enumerateDevicesResolver_ = res;
-      });
-    }
+  /**
+   * Resolves promise returned from getUserMedia().
+   */
+  resolveGetUserMedia() {
+    assertTrue(!!this.getMediaDevicesResolver_, 'getUserMedia was not called');
+    this.getMediaDevicesResolver_(new MediaStream());
+  }
 
-    /**
-     * Resolves promise returned from enumerateDevices().
-     */
-    resolveEnumerateDevices() {
-      assertTrue(
-          !!this.enumerateDevicesResolver_, 'enumerateDevices was not called');
-      this.enumerateDevicesResolver_(this.devices_);
-    }
+  /** @override */
+  removeEventListener(event, fn) {}
 
-    /** @override */
-    getSupportedConstraints() {
-      return null;
-    }
-
-    /** @override */
-    getDisplayMedia() {
-      return new Promise((res, rej) => {
-        res(null);
-      });
-    }
-
-    /** @override */
-    getUserMedia(constraints) {
-      this.isStreamingUserFacingCamera =
-          constraints.video.facingMode === 'user';
-      return new Promise((res, rej) => {
-        this.getMediaDevicesResolver_ = res;
-      });
-    }
-
-    /**
-     * Resolves promise returned from getUserMedia().
-     */
-    resolveGetUserMedia() {
-      assertTrue(
-          !!this.getMediaDevicesResolver_, 'getUserMedia was not called');
-      this.getMediaDevicesResolver_(new MediaStream());
-    }
-
-    /** @override */
-    removeEventListener(event, fn) {}
-
-    /**
-     * Adds a video input device to the list of media devices.
-     */
-    addDevice() {
-      const device = {
-        deviceId: '',
-        kind: 'videoinput',
-        label: '',
-        groupId: '',
-      };
-      device.__proto__ = MediaDeviceInfo.prototype;
-      this.devices_.push(device);
-      if (this.deviceChangeListener_) {
-        this.deviceChangeListener_();
-      }
-    }
-
-    /**
-     * Removes the most recently added media device from the list of media
-     * devices.
-     */
-    removeDevice() {
-      this.devices_.pop();
-      if (this.devices_.length <= 1) {
-        this.isStreamingUserFacingCamera = true;
-      }
-      if (this.deviceChangeListener_) {
-        this.deviceChangeListener_();
-      }
+  /**
+   * Adds a video input device to the list of media devices.
+   */
+  addDevice() {
+    const device = {
+      deviceId: '',
+      kind: 'videoinput',
+      label: '',
+      groupId: '',
+    };
+    device.__proto__ = MediaDeviceInfo.prototype;
+    this.devices_.push(device);
+    if (this.deviceChangeListener_) {
+      this.deviceChangeListener_();
     }
   }
 
-  // #cr_define_end
-  return {
-    FakeMediaDevices: FakeMediaDevices,
-  };
-});
\ No newline at end of file
+  /**
+   * Removes the most recently added media device from the list of media
+   * devices.
+   */
+  removeDevice() {
+    this.devices_.pop();
+    if (this.devices_.length <= 1) {
+      this.isStreamingUserFacingCamera = true;
+    }
+    if (this.deviceChangeListener_) {
+      this.deviceChangeListener_();
+    }
+  }
+}
diff --git a/chrome/test/data/webui/cr_components/chromeos/cellular_setup/final_page_test.js b/chrome/test/data/webui/cr_components/chromeos/cellular_setup/final_page_test.js
index 8ee98a4..3689218 100644
--- a/chrome/test/data/webui/cr_components/chromeos/cellular_setup/final_page_test.js
+++ b/chrome/test/data/webui/cr_components/chromeos/cellular_setup/final_page_test.js
@@ -2,21 +2,21 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// clang-format off
-// #import 'chrome://resources/cr_components/chromeos/cellular_setup/final_page.m.js';
+import 'chrome://resources/cr_components/chromeos/cellular_setup/final_page.m.js';
 
-// #import {flush, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-// #import {assertFalse, assertTrue} from '../../../chai_assert.js';
-// #import {FakeCellularSetupDelegate} from './fake_cellular_setup_delegate.m.js';
-// clang-format on
+import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
+import {assertTrue} from '../../../chai_assert.js';
+
+import {FakeCellularSetupDelegate} from './fake_cellular_setup_delegate.js';
 
 suite('CrComponentsFinalPageTest', function() {
   let finalPage;
   setup(function() {
     finalPage = document.createElement('final-page');
-    finalPage.delegate = new cellular_setup.FakeCellularSetupDelegate();
+    finalPage.delegate = new FakeCellularSetupDelegate();
     document.body.appendChild(finalPage);
-    Polymer.dom.flush();
+    flush();
   });
 
   test('Base test', function() {
diff --git a/chrome/test/data/webui/cr_components/chromeos/cellular_setup/mock_metrics_private.js b/chrome/test/data/webui/cr_components/chromeos/cellular_setup/mock_metrics_private.js
index 1236598..45e99c30 100644
--- a/chrome/test/data/webui/cr_components/chromeos/cellular_setup/mock_metrics_private.js
+++ b/chrome/test/data/webui/cr_components/chromeos/cellular_setup/mock_metrics_private.js
@@ -2,54 +2,47 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-cr.define('cellular_setup', function() {
-  /**
-   * A mock to intercept metric logging calls.
-   */
-  /* #export */ class MockMetricsPrivate {
-    constructor() {
-      this.cellularSetupResultDict_ = {};
-      this.histogramCounts_ = {};
-    }
-
-    recordEnumerationValue(histogramName, setupFlowResult, enumSize) {
-      if (setupFlowResult in this.cellularSetupResultDict_) {
-        this.cellularSetupResultDict_[setupFlowResult]++;
-      } else {
-        this.cellularSetupResultDict_[setupFlowResult] = 1;
-      }
-      this.incrementHistogramCount_(histogramName);
-    }
-
-    recordLongTime(histogramName, value) {
-      this.incrementHistogramCount_(histogramName);
-    }
-
-    getHistogramEnumValueCount(enumValue) {
-      if (enumValue in this.cellularSetupResultDict_) {
-        return this.cellularSetupResultDict_[enumValue];
-      }
-      return 0;
-    }
-
-    getHistogramCount(histogramName) {
-      if (histogramName in this.histogramCounts_) {
-        return this.histogramCounts_[histogramName];
-      }
-      return 0;
-    }
-
-    incrementHistogramCount_(histogramName) {
-      if (histogramName in this.histogramCounts_) {
-        this.histogramCounts_[histogramName]++;
-      } else {
-        this.histogramCounts_[histogramName] = 1;
-      }
-    }
+/**
+ * A mock to intercept metric logging calls.
+ */
+export class MockMetricsPrivate {
+  constructor() {
+    this.cellularSetupResultDict_ = {};
+    this.histogramCounts_ = {};
   }
 
-  // #cr_define_end
-  return {
-    MockMetricsPrivate: MockMetricsPrivate,
-  };
-});
\ No newline at end of file
+  recordEnumerationValue(histogramName, setupFlowResult, enumSize) {
+    if (setupFlowResult in this.cellularSetupResultDict_) {
+      this.cellularSetupResultDict_[setupFlowResult]++;
+    } else {
+      this.cellularSetupResultDict_[setupFlowResult] = 1;
+    }
+    this.incrementHistogramCount_(histogramName);
+  }
+
+  recordLongTime(histogramName, value) {
+    this.incrementHistogramCount_(histogramName);
+  }
+
+  getHistogramEnumValueCount(enumValue) {
+    if (enumValue in this.cellularSetupResultDict_) {
+      return this.cellularSetupResultDict_[enumValue];
+    }
+    return 0;
+  }
+
+  getHistogramCount(histogramName) {
+    if (histogramName in this.histogramCounts_) {
+      return this.histogramCounts_[histogramName];
+    }
+    return 0;
+  }
+
+  incrementHistogramCount_(histogramName) {
+    if (histogramName in this.histogramCounts_) {
+      this.histogramCounts_[histogramName]++;
+    } else {
+      this.histogramCounts_[histogramName] = 1;
+    }
+  }
+}
diff --git a/chrome/test/data/webui/cr_components/chromeos/cellular_setup/provisioning_page_test.js b/chrome/test/data/webui/cr_components/chromeos/cellular_setup/provisioning_page_test.js
index 7b692f8..cb952e91 100644
--- a/chrome/test/data/webui/cr_components/chromeos/cellular_setup/provisioning_page_test.js
+++ b/chrome/test/data/webui/cr_components/chromeos/cellular_setup/provisioning_page_test.js
@@ -2,22 +2,22 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// clang-format off
-// #import 'chrome://os-settings/strings.m.js';
-// #import 'chrome://resources/cr_components/chromeos/cellular_setup/provisioning_page.m.js';
+import 'chrome://os-settings/strings.m.js';
+import 'chrome://resources/cr_components/chromeos/cellular_setup/provisioning_page.m.js';
 
-// #import {flush, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-// #import {assertFalse, assertTrue} from '../../../chai_assert.js';
-// #import {FakeCellularSetupDelegate} from './fake_cellular_setup_delegate.m.js';
-// clang-format on
+import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
+import {assertTrue} from '../../../chai_assert.js';
+
+import {FakeCellularSetupDelegate} from './fake_cellular_setup_delegate.js';
 
 suite('CrComponentsProvisioningPageTest', function() {
   let provisioningPage;
   setup(function() {
     provisioningPage = document.createElement('provisioning-page');
-    provisioningPage.delegate = new cellular_setup.FakeCellularSetupDelegate();
+    provisioningPage.delegate = new FakeCellularSetupDelegate();
     document.body.appendChild(provisioningPage);
-    Polymer.dom.flush();
+    flush();
   });
 
   test('Base test', function() {
diff --git a/chrome/test/data/webui/cr_components/chromeos/cellular_setup/psim_flow_ui_test.js b/chrome/test/data/webui/cr_components/chromeos/cellular_setup/psim_flow_ui_test.js
index fd946bf..c243e4c1 100644
--- a/chrome/test/data/webui/cr_components/chromeos/cellular_setup/psim_flow_ui_test.js
+++ b/chrome/test/data/webui/cr_components/chromeos/cellular_setup/psim_flow_ui_test.js
@@ -2,20 +2,20 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// clang-format off
-// #import 'chrome://os-settings/strings.m.js';
-// #import 'chrome://resources/cr_components/chromeos/cellular_setup/psim_flow_ui.m.js';
+import 'chrome://os-settings/strings.m.js';
+import 'chrome://resources/cr_components/chromeos/cellular_setup/psim_flow_ui.m.js';
 
-// #import {PSimUIState, PSimPageName, PSimSetupFlowResult, PSIM_SETUP_RESULT_METRIC_NAME, SUCCESSFUL_PSIM_SETUP_DURATION_METRIC_NAME, FAILED_PSIM_SETUP_DURATION_METRIC_NAME} from 'chrome://resources/cr_components/chromeos/cellular_setup/psim_flow_ui.m.js';
-// #import {setCellularSetupRemoteForTesting} from 'chrome://resources/cr_components/chromeos/cellular_setup/mojo_interface_provider.m.js';
-// #import {flush, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-// #import {assertTrue} from '../../../chai_assert.js';
-// #import {ButtonState} from 'chrome://resources/cr_components/chromeos/cellular_setup/cellular_types.m.js';
-// #import {FakeCellularSetupDelegate} from './fake_cellular_setup_delegate.m.js';
-// #import {FakeCarrierPortalHandlerRemote, FakeCellularSetupRemote} from './fake_cellular_setup_remote.m.js';
-// #import {MockMetricsPrivate} from './mock_metrics_private.m.js';
-// #import {eventToPromise, flushTasks} from 'chrome://test/test_util.js';
-// clang-format on
+import {ButtonState} from 'chrome://resources/cr_components/chromeos/cellular_setup/cellular_types.m.js';
+import {setCellularSetupRemoteForTesting} from 'chrome://resources/cr_components/chromeos/cellular_setup/mojo_interface_provider.m.js';
+import {FAILED_PSIM_SETUP_DURATION_METRIC_NAME, PSimPageName, PSimSetupFlowResult, PSimUIState, SUCCESSFUL_PSIM_SETUP_DURATION_METRIC_NAME} from 'chrome://resources/cr_components/chromeos/cellular_setup/psim_flow_ui.m.js';
+import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {eventToPromise} from 'chrome://test/test_util.js';
+
+import {assertTrue} from '../../../chai_assert.js';
+
+import {FakeCellularSetupDelegate} from './fake_cellular_setup_delegate.js';
+import {FakeCarrierPortalHandlerRemote, FakeCellularSetupRemote} from './fake_cellular_setup_remote.js';
+import {MockMetricsPrivate} from './mock_metrics_private.js';
 
 suite('CrComponentsPsimFlowUiTest', function() {
   let pSimPage;
@@ -33,7 +33,7 @@
   let timeoutFunction = null;
 
   async function flushAsync() {
-    Polymer.dom.flush();
+    flush();
     // Use setTimeout to wait for the next macrotask.
     return new Promise(resolve => setTimeout(resolve));
   }
@@ -41,7 +41,7 @@
   /** @param {PSimSetupFlowResult} pSimSetupFlowResult */
   function endFlowAndVerifyResult(pSimSetupFlowResult) {
     pSimPage.remove();
-    Polymer.dom.flush();
+    flush();
     assertEquals(
         chrome.metricsPrivate.getHistogramEnumValueCount(pSimSetupFlowResult),
         1);
@@ -69,26 +69,24 @@
   }
 
   setup(async function() {
-    cellularCarrierHandler =
-        new cellular_setup.FakeCarrierPortalHandlerRemote();
-    cellularSetupRemote =
-        new cellular_setup.FakeCellularSetupRemote(cellularCarrierHandler);
-    cellular_setup.setCellularSetupRemoteForTesting(cellularSetupRemote);
+    cellularCarrierHandler = new FakeCarrierPortalHandlerRemote();
+    cellularSetupRemote = new FakeCellularSetupRemote(cellularCarrierHandler);
+    setCellularSetupRemoteForTesting(cellularSetupRemote);
     chrome.metricsPrivate = new MockMetricsPrivate();
 
     pSimPage = document.createElement('psim-flow-ui');
-    pSimPage.delegate = new cellular_setup.FakeCellularSetupDelegate();
+    pSimPage.delegate = new FakeCellularSetupDelegate();
     pSimPage.setTimerFunctionForTest(function(fn, milliseconds) {
       timeoutFunction = fn;
       return 1;
     });
 
     const focusNextButtonPromise =
-        test_util.eventToPromise('focus-default-button', pSimPage);
+        eventToPromise('focus-default-button', pSimPage);
     pSimPage.initSubflow();
     await focusNextButtonPromise;
     document.body.appendChild(pSimPage);
-    Polymer.dom.flush();
+    flush();
   });
 
   teardown(function() {
@@ -102,8 +100,7 @@
     const provisioningPage = pSimPage.$$('#provisioningPage');
     assertTrue(!!provisioningPage);
     assertFalse(
-        pSimPage.selectedPSimPageName_ ===
-        cellularSetup.PSimPageName.provisioningPage);
+        pSimPage.selectedPSimPageName_ === PSimPageName.provisioningPage);
 
     cellularActivationDelegate.onActivationFinished(
         ash.cellularSetup.mojom.ActivationResult
@@ -111,16 +108,13 @@
 
     await flushAsync();
 
-    assertTrue(
-        pSimPage.selectedPSimPageName_ ===
-        cellularSetup.PSimPageName.PROVISIONING);
+    assertTrue(pSimPage.selectedPSimPageName_ === PSimPageName.PROVISIONING);
 
     endFlowAndVerifyResult(PSimSetupFlowResult.SUCCESS);
   });
 
   test('Sim detection failure with retries', async function() {
-    assertTrue(
-        pSimPage.state_ === cellularSetup.PSimUIState.STARTING_ACTIVATION);
+    assertTrue(pSimPage.state_ === PSimUIState.STARTING_ACTIVATION);
     assertTrue(!!pSimPage.currentTimeoutId_);
 
     await flushAsync();
@@ -128,15 +122,13 @@
     // Simulate timeout.
     timeoutFunction();
 
-    assertTrue(
-        pSimPage.state_ === cellularSetup.PSimUIState.TIMEOUT_START_ACTIVATION);
+    assertTrue(pSimPage.state_ === PSimUIState.TIMEOUT_START_ACTIVATION);
     assertTrue(pSimPage.forwardButtonLabel === 'Try again');
 
     // Simulate clicking 'Try Again'.
     pSimPage.navigateForward();
 
-    assertTrue(
-        pSimPage.state_ === cellularSetup.PSimUIState.STARTING_ACTIVATION);
+    assertTrue(pSimPage.state_ === PSimUIState.STARTING_ACTIVATION);
     assertTrue(!!pSimPage.currentTimeoutId_);
 
     await flushAsync();
@@ -144,15 +136,13 @@
     // Timeout again.
     timeoutFunction();
 
-    assertTrue(
-        pSimPage.state_ === cellularSetup.PSimUIState.TIMEOUT_START_ACTIVATION);
+    assertTrue(pSimPage.state_ === PSimUIState.TIMEOUT_START_ACTIVATION);
     assertTrue(pSimPage.forwardButtonLabel === 'Try again');
 
     // Click 'Try Again' again.
     pSimPage.navigateForward();
 
-    assertTrue(
-        pSimPage.state_ === cellularSetup.PSimUIState.STARTING_ACTIVATION);
+    assertTrue(pSimPage.state_ === PSimUIState.STARTING_ACTIVATION);
     assertTrue(!!pSimPage.currentTimeoutId_);
 
     await flushAsync();
@@ -161,9 +151,7 @@
     timeoutFunction();
 
     // Should now be at the failure state.
-    assertTrue(
-        pSimPage.state_ ===
-        cellularSetup.PSimUIState.FINAL_TIMEOUT_START_ACTIVATION);
+    assertTrue(pSimPage.state_ === PSimUIState.FINAL_TIMEOUT_START_ACTIVATION);
   });
 
   test('Carrier title on provisioning page', async () => {
@@ -190,15 +178,13 @@
   });
 
   test('forward navigation and finish cellular setup test', async function() {
-    pSimPage.state_ = cellularSetup.PSimUIState.WAITING_FOR_PORTAL_TO_LOAD;
-    Polymer.dom.flush();
+    pSimPage.state_ = PSimUIState.WAITING_FOR_PORTAL_TO_LOAD;
+    flush();
     pSimPage.navigateForward();
     assertTrue(
-        pSimPage.state_ ===
-        cellularSetup.PSimUIState.WAITING_FOR_ACTIVATION_TO_FINISH);
+        pSimPage.state_ === PSimUIState.WAITING_FOR_ACTIVATION_TO_FINISH);
 
-    assertEquals(
-        cellularSetup.ButtonState.ENABLED, pSimPage.buttonState.forward);
+    assertEquals(ButtonState.ENABLED, pSimPage.buttonState.forward);
     assertEquals(pSimPage.forwardButtonLabel, 'Done');
     let exitCellularSetupEventFired = false;
     pSimPage.addEventListener('exit-cellular-setup', () => {
@@ -240,8 +226,7 @@
     const provisioningPage = pSimPage.$$('#provisioningPage');
     assertTrue(!!provisioningPage);
     assertFalse(
-        pSimPage.selectedPSimPageName_ ===
-        cellularSetup.PSimPageName.provisioningPage);
+        pSimPage.selectedPSimPageName_ === PSimPageName.provisioningPage);
 
     cellularActivationDelegate.onActivationFinished(
         ash.cellularSetup.mojom.ActivationResult.kFailedToActivate);
diff --git a/chrome/test/data/webui/cr_components/chromeos/cellular_setup/setup_loading_page_test.js b/chrome/test/data/webui/cr_components/chromeos/cellular_setup/setup_loading_page_test.js
index 06036b4..f7646f9 100644
--- a/chrome/test/data/webui/cr_components/chromeos/cellular_setup/setup_loading_page_test.js
+++ b/chrome/test/data/webui/cr_components/chromeos/cellular_setup/setup_loading_page_test.js
@@ -2,13 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// clang-format off
-// #import 'chrome://os-settings/strings.m.js';
-// #import 'chrome://resources/cr_components/chromeos/cellular_setup/setup_loading_page.m.js';
+import 'chrome://os-settings/strings.m.js';
+import 'chrome://resources/cr_components/chromeos/cellular_setup/setup_loading_page.m.js';
 
-// #import {flush, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-// #import {assertFalse, assertTrue} from '../../../chai_assert.js';
-// clang-format on
+import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
+import {assertFalse, assertTrue} from '../../../chai_assert.js';
 
 suite('CrComponentsSetupLoadingPageTest', function() {
   let setupLoadingPage;
@@ -17,7 +16,7 @@
   setup(function() {
     setupLoadingPage = document.createElement('setup-loading-page');
     document.body.appendChild(setupLoadingPage);
-    Polymer.dom.flush();
+    flush();
 
     basePage = setupLoadingPage.$$('base-page');
     assertTrue(!!basePage);
@@ -25,12 +24,12 @@
 
   test('Loading animation and error graphic shown correctly', function() {
     setupLoadingPage.isSimDetectError = false;
-    Polymer.dom.flush();
+    flush();
     assertTrue(!!setupLoadingPage.$$('#animationContainer'));
     assertTrue(setupLoadingPage.$$('#simDetectError').hidden);
 
     setupLoadingPage.isSimDetectError = true;
-    Polymer.dom.flush();
+    flush();
     assertFalse(!!setupLoadingPage.$$('#animationContainer'));
     assertFalse(setupLoadingPage.$$('#simDetectError').hidden);
   });
diff --git a/chrome/test/data/webui/cr_components/chromeos/cr_components_chromeos_v3_browsertest.js b/chrome/test/data/webui/cr_components/chromeos/cr_components_chromeos_v3_browsertest.js
index 5631c074..405aacc 100644
--- a/chrome/test/data/webui/cr_components/chromeos/cr_components_chromeos_v3_browsertest.js
+++ b/chrome/test/data/webui/cr_components/chromeos/cr_components_chromeos_v3_browsertest.js
@@ -78,17 +78,17 @@
 ].forEach(test => registerTest('MultiDeviceSetup', 'multidevice-setup', ...test));
 
 [
- ['ActivationCodePage', 'cellular_setup/activation_code_page_test.m.js'],
- ['BasePage', 'cellular_setup/base_page_test.m.js'],
- ['ButtonBar', 'cellular_setup/button_bar_test.m.js'],
- ['ConfirmationCodePage', 'cellular_setup/confirmation_code_page_test.m.js'],
- ['CellularEidDialog', 'cellular_setup/cellular_eid_dialog_test.m.js'],
- ['CellularSetup', 'cellular_setup/cellular_setup_test.m.js'],
- ['EsimFlowUi', 'cellular_setup/esim_flow_ui_test.m.js'],
- ['FinalPage', 'cellular_setup/final_page_test.m.js'],
- ['ProvisioningPage', 'cellular_setup/provisioning_page_test.m.js'],
- ['PsimFlowUi', 'cellular_setup/psim_flow_ui_test.m.js'],
- ['SetupLoadingPage', 'cellular_setup/setup_loading_page_test.m.js'],
+ ['ActivationCodePage', 'cellular_setup/activation_code_page_test.js'],
+ ['BasePage', 'cellular_setup/base_page_test.js'],
+ ['ButtonBar', 'cellular_setup/button_bar_test.js'],
+ ['CellularEidDialog', 'cellular_setup/cellular_eid_dialog_test.js'],
+ ['CellularSetup', 'cellular_setup/cellular_setup_test.js'],
+ ['ConfirmationCodePage', 'cellular_setup/confirmation_code_page_test.js'],
+ ['EsimFlowUi', 'cellular_setup/esim_flow_ui_test.js'],
+ ['FinalPage', 'cellular_setup/final_page_test.js'],
+ ['ProvisioningPage', 'cellular_setup/provisioning_page_test.js'],
+ ['PsimFlowUi', 'cellular_setup/psim_flow_ui_test.js'],
+ ['SetupLoadingPage', 'cellular_setup/setup_loading_page_test.js'],
 ].forEach(test => registerTest('CellularSetup', 'os-settings', ...test));
 // clang-format on
 
diff --git a/chrome/test/data/webui/cr_components/chromeos/network/network_list_item_test.js b/chrome/test/data/webui/cr_components/chromeos/network/network_list_item_test.js
index 98160c33..2e0cff9 100644
--- a/chrome/test/data/webui/cr_components/chromeos/network/network_list_item_test.js
+++ b/chrome/test/data/webui/cr_components/chromeos/network/network_list_item_test.js
@@ -16,7 +16,7 @@
 // #import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 // #import {FakeNetworkConfig} from 'chrome://test/chromeos/fake_network_config_mojom.js';
 // #import {setESimManagerRemoteForTesting} from 'chrome://resources/cr_components/chromeos/cellular_setup/mojo_interface_provider.m.js';
-// #import {FakeESimManagerRemote} from 'chrome://test/cr_components/chromeos/cellular_setup/fake_esim_manager_remote.m.js';
+// #import {FakeESimManagerRemote} from 'chrome://test/cr_components/chromeos/cellular_setup/fake_esim_manager_remote.js';
 // #import {MojoInterfaceProviderImpl} from 'chrome://resources/cr_components/chromeos/network/mojo_interface_provider.m.js';
 // #import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
 // #import {NetworkList} from 'chrome://resources/cr_components/chromeos/network/network_list_types.m.js';
diff --git a/chrome/test/data/webui/settings/chromeos/cellular_networks_list_test.js b/chrome/test/data/webui/settings/chromeos/cellular_networks_list_test.js
index 77bce66..1c3415e 100644
--- a/chrome/test/data/webui/settings/chromeos/cellular_networks_list_test.js
+++ b/chrome/test/data/webui/settings/chromeos/cellular_networks_list_test.js
@@ -9,7 +9,7 @@
 import {OncMojo} from 'chrome://resources/cr_components/chromeos/network/onc_mojo.m.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {FakeNetworkConfig} from 'chrome://test/chromeos/fake_network_config_mojom.js';
-import {FakeESimManagerRemote} from 'chrome://test/cr_components/chromeos/cellular_setup/fake_esim_manager_remote.m.js';
+import {FakeESimManagerRemote} from 'chrome://test/cr_components/chromeos/cellular_setup/fake_esim_manager_remote.js';
 import {eventToPromise, flushTasks} from 'chrome://test/test_util.js';
 
 import {assertEquals, assertFalse, assertTrue} from '../../chai_assert.js';
diff --git a/chrome/test/data/webui/settings/chromeos/esim_install_error_dialog_test.js b/chrome/test/data/webui/settings/chromeos/esim_install_error_dialog_test.js
index f7b6130..9b2b43c 100644
--- a/chrome/test/data/webui/settings/chromeos/esim_install_error_dialog_test.js
+++ b/chrome/test/data/webui/settings/chromeos/esim_install_error_dialog_test.js
@@ -6,7 +6,7 @@
 
 import {setESimManagerRemoteForTesting} from 'chrome://resources/cr_components/chromeos/cellular_setup/mojo_interface_provider.m.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-import {FakeESimManagerRemote} from 'chrome://test/cr_components/chromeos/cellular_setup/fake_esim_manager_remote.m.js';
+import {FakeESimManagerRemote} from 'chrome://test/cr_components/chromeos/cellular_setup/fake_esim_manager_remote.js';
 
 import {assertEquals, assertTrue} from '../../chai_assert.js';
 
diff --git a/chrome/test/data/webui/settings/chromeos/esim_remove_profile_dialog_test.js b/chrome/test/data/webui/settings/chromeos/esim_remove_profile_dialog_test.js
index 5824fa19..6fd9c0c 100644
--- a/chrome/test/data/webui/settings/chromeos/esim_remove_profile_dialog_test.js
+++ b/chrome/test/data/webui/settings/chromeos/esim_remove_profile_dialog_test.js
@@ -8,7 +8,7 @@
 import {OncMojo} from 'chrome://resources/cr_components/chromeos/network/onc_mojo.m.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {FakeNetworkConfig} from 'chrome://test/chromeos/fake_network_config_mojom.js';
-import {FakeESimManagerRemote} from 'chrome://test/cr_components/chromeos/cellular_setup/fake_esim_manager_remote.m.js';
+import {FakeESimManagerRemote} from 'chrome://test/cr_components/chromeos/cellular_setup/fake_esim_manager_remote.js';
 import {eventToPromise} from 'chrome://test/test_util.js';
 
 import {assertEquals, assertTrue} from '../../chai_assert.js';
diff --git a/chrome/test/data/webui/settings/chromeos/esim_rename_dialog_test.js b/chrome/test/data/webui/settings/chromeos/esim_rename_dialog_test.js
index 530d00f..a25726a 100644
--- a/chrome/test/data/webui/settings/chromeos/esim_rename_dialog_test.js
+++ b/chrome/test/data/webui/settings/chromeos/esim_rename_dialog_test.js
@@ -3,13 +3,14 @@
 // found in the LICENSE file.
 
 import 'chrome://os-settings/chromeos/os_settings.js';
+
 import {setESimManagerRemoteForTesting} from 'chrome://resources/cr_components/chromeos/cellular_setup/mojo_interface_provider.m.js';
 import {MojoInterfaceProviderImpl} from 'chrome://resources/cr_components/chromeos/network/mojo_interface_provider.m.js';
 import {OncMojo} from 'chrome://resources/cr_components/chromeos/network/onc_mojo.m.js';
 import {getDeepActiveElement} from 'chrome://resources/js/util.m.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {FakeNetworkConfig} from 'chrome://test/chromeos/fake_network_config_mojom.js';
-import {FakeESimManagerRemote} from 'chrome://test/cr_components/chromeos/cellular_setup/fake_esim_manager_remote.m.js';
+import {FakeESimManagerRemote} from 'chrome://test/cr_components/chromeos/cellular_setup/fake_esim_manager_remote.js';
 import {eventToPromise} from 'chrome://test/test_util.js';
 
 import {assertEquals, assertTrue} from '../../chai_assert.js';
diff --git a/chrome/test/data/webui/settings/chromeos/internet_detail_menu_test.js b/chrome/test/data/webui/settings/chromeos/internet_detail_menu_test.js
index 6c2f7d9..06b2c6361 100644
--- a/chrome/test/data/webui/settings/chromeos/internet_detail_menu_test.js
+++ b/chrome/test/data/webui/settings/chromeos/internet_detail_menu_test.js
@@ -9,7 +9,7 @@
 import {getDeepActiveElement} from 'chrome://resources/js/util.m.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {FakeNetworkConfig} from 'chrome://test/chromeos/fake_network_config_mojom.js';
-import {FakeESimManagerRemote} from 'chrome://test/cr_components/chromeos/cellular_setup/fake_esim_manager_remote.m.js';
+import {FakeESimManagerRemote} from 'chrome://test/cr_components/chromeos/cellular_setup/fake_esim_manager_remote.js';
 import {eventToPromise, flushTasks, waitAfterNextRender} from 'chrome://test/test_util.js';
 
 import {assertEquals, assertTrue} from '../../chai_assert.js';
diff --git a/chrome/test/data/webui/settings/chromeos/internet_page_tests.js b/chrome/test/data/webui/settings/chromeos/internet_page_tests.js
index 8da1843..91ef7ec 100644
--- a/chrome/test/data/webui/settings/chromeos/internet_page_tests.js
+++ b/chrome/test/data/webui/settings/chromeos/internet_page_tests.js
@@ -10,7 +10,7 @@
 import {getDeepActiveElement} from 'chrome://resources/js/util.m.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {FakeNetworkConfig} from 'chrome://test/chromeos/fake_network_config_mojom.js';
-import {FakeESimManagerRemote} from 'chrome://test/cr_components/chromeos/cellular_setup/fake_esim_manager_remote.m.js';
+import {FakeESimManagerRemote} from 'chrome://test/cr_components/chromeos/cellular_setup/fake_esim_manager_remote.js';
 import {isVisible, waitAfterNextRender} from 'chrome://test/test_util.js';
 
 suite('InternetPage', function() {
diff --git a/chrome/test/data/webui/settings/chromeos/internet_subpage_tests.js b/chrome/test/data/webui/settings/chromeos/internet_subpage_tests.js
index 746dfe2..91e9ee8b 100644
--- a/chrome/test/data/webui/settings/chromeos/internet_subpage_tests.js
+++ b/chrome/test/data/webui/settings/chromeos/internet_subpage_tests.js
@@ -10,7 +10,7 @@
 import {getDeepActiveElement} from 'chrome://resources/js/util.m.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {FakeNetworkConfig} from 'chrome://test/chromeos/fake_network_config_mojom.js';
-import {FakeESimManagerRemote} from 'chrome://test/cr_components/chromeos/cellular_setup/fake_esim_manager_remote.m.js';
+import {FakeESimManagerRemote} from 'chrome://test/cr_components/chromeos/cellular_setup/fake_esim_manager_remote.js';
 import {waitAfterNextRender} from 'chrome://test/test_util.js';
 
 suite('InternetSubpage', function() {
diff --git a/chrome/test/data/webui/settings/chromeos/os_reset_page_test.js b/chrome/test/data/webui/settings/chromeos/os_reset_page_test.js
index bed6f87..97eb76a 100644
--- a/chrome/test/data/webui/settings/chromeos/os_reset_page_test.js
+++ b/chrome/test/data/webui/settings/chromeos/os_reset_page_test.js
@@ -7,7 +7,7 @@
 import {setESimManagerRemoteForTesting} from 'chrome://resources/cr_components/chromeos/cellular_setup/mojo_interface_provider.m.js';
 import {getDeepActiveElement} from 'chrome://resources/js/util.m.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-import {FakeESimManagerRemote} from 'chrome://test/cr_components/chromeos/cellular_setup/fake_esim_manager_remote.m.js';
+import {FakeESimManagerRemote} from 'chrome://test/cr_components/chromeos/cellular_setup/fake_esim_manager_remote.js';
 import {waitAfterNextRender} from 'chrome://test/test_util.js';
 
 import {assertEquals, assertFalse, assertTrue} from '../../chai_assert.js';
diff --git a/chrome/test/data/webui/settings/chromeos/text_to_speech_subpage_tests.js b/chrome/test/data/webui/settings/chromeos/text_to_speech_subpage_tests.js
index 46bdfca9..ccec2684 100644
--- a/chrome/test/data/webui/settings/chromeos/text_to_speech_subpage_tests.js
+++ b/chrome/test/data/webui/settings/chromeos/text_to_speech_subpage_tests.js
@@ -21,6 +21,7 @@
       'getTtsExtensions',
       'previewTtsVoice',
       'wakeTtsEngine',
+      'refreshTtsVoices',
     ]);
   }
 
@@ -43,6 +44,11 @@
   wakeTtsEngine() {
     this.methodCalled('wakeTtsEngine');
   }
+
+  /** @override */
+  refreshTtsVoices() {
+    this.methodCalled('refreshTtsVoices');
+  }
 }
 
 suite('TextToSpeechSubpageTests', function() {
diff --git a/chromecast/browser/accessibility/accessibility_service_impl.cc b/chromecast/browser/accessibility/accessibility_service_impl.cc
index afb3e4b..767d263 100644
--- a/chromecast/browser/accessibility/accessibility_service_impl.cc
+++ b/chromecast/browser/accessibility/accessibility_service_impl.cc
@@ -108,7 +108,7 @@
     if (!render_frame_host)
       continue;
 
-    if (!render_frame_host->IsRenderFrameCreated())
+    if (!render_frame_host->IsRenderFrameLive())
       continue;
 
     service_manager::InterfaceProvider* interface_provider =
diff --git a/chromecast/browser/accessibility/flutter/ax_tree_source_flutter_unittest.cc b/chromecast/browser/accessibility/flutter/ax_tree_source_flutter_unittest.cc
index 3d57773c..e2bdabd 100644
--- a/chromecast/browser/accessibility/flutter/ax_tree_source_flutter_unittest.cc
+++ b/chromecast/browser/accessibility/flutter/ax_tree_source_flutter_unittest.cc
@@ -66,6 +66,7 @@
                                   const GURL& source_url,
                                   std::vector<VoiceData>* out_voices) override {
   }
+  void RefreshVoices() override {}
   void LoadBuiltInTtsEngine(BrowserContext* browser_context) override {}
   void WillSpeakUtteranceWithVoice(TtsUtterance* utterance,
                                    const VoiceData& voice_data) override {}
diff --git a/chromecast/media/audio/mixer_service/output_stream_connection.cc b/chromecast/media/audio/mixer_service/output_stream_connection.cc
index d21cbb1..79d0eb3 100644
--- a/chromecast/media/audio/mixer_service/output_stream_connection.cc
+++ b/chromecast/media/audio/mixer_service/output_stream_connection.cc
@@ -241,7 +241,9 @@
         (message.mixer_underrun().type() == MixerUnderrun::INPUT_UNDERRUN
              ? "Platform.Audio.Mixer.StreamUnderrun"
              : "Platform.Audio.Mixer.OutputUnderrun");
-    RecordCastEvent(metric_name, CreateCastEvent(metric_name),
+    std::unique_ptr<CastEventBuilder> event = CreateCastEvent(metric_name);
+    delegate_->ProcessCastEvent(event.get());
+    RecordCastEvent(metric_name, std::move(event),
                     /* verbose_log_level = */ 0);
     delegate_->OnMixerUnderrun(static_cast<Delegate::MixerUnderrunType>(
         message.mixer_underrun().type()));
diff --git a/chromecast/media/audio/mixer_service/output_stream_connection.h b/chromecast/media/audio/mixer_service/output_stream_connection.h
index 086de58..17bf489 100644
--- a/chromecast/media/audio/mixer_service/output_stream_connection.h
+++ b/chromecast/media/audio/mixer_service/output_stream_connection.h
@@ -14,6 +14,7 @@
 #include "net/base/io_buffer.h"
 
 namespace chromecast {
+class CastEventBuilder;
 class IOBufferPool;
 
 namespace media {
@@ -63,6 +64,10 @@
     // Called when an underrun happens on mixer input/output.
     virtual void OnMixerUnderrun(MixerUnderrunType type) {}
 
+    // Called when OutputStreamConnection records a cast event. It allows
+    // the Delegate to provide some extra data to the event.
+    virtual void ProcessCastEvent(CastEventBuilder* event) {}
+
    protected:
     virtual ~Delegate() = default;
   };
diff --git a/chromeos/components/cros_elements/BUILD.gn b/chromeos/ash/components/cros_elements/BUILD.gn
similarity index 80%
rename from chromeos/components/cros_elements/BUILD.gn
rename to chromeos/ash/components/cros_elements/BUILD.gn
index 2ea8474..54fccc2 100644
--- a/chromeos/components/cros_elements/BUILD.gn
+++ b/chromeos/ash/components/cros_elements/BUILD.gn
@@ -2,8 +2,11 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import("//build/config/chromeos/ui_mode.gni")
 import("//ui/webui/resources/tools/generate_grd.gni")
 
+assert(is_chromeos_ash, "Non Chrome OS builds cannot depend on //chromeos/ash")
+
 generate_grd("build_grdp") {
   grd_prefix = "cros_elements"
   out_grd = "$target_gen_dir/${grd_prefix}_resources.grdp"
diff --git a/chromeos/components/cros_elements/OWNERS b/chromeos/ash/components/cros_elements/OWNERS
similarity index 100%
rename from chromeos/components/cros_elements/OWNERS
rename to chromeos/ash/components/cros_elements/OWNERS
diff --git a/chromeos/components/cros_elements/README.md b/chromeos/ash/components/cros_elements/README.md
similarity index 100%
rename from chromeos/components/cros_elements/README.md
rename to chromeos/ash/components/cros_elements/README.md
diff --git a/chromeos/components/cros_elements/button/BUILD.gn b/chromeos/ash/components/cros_elements/button/BUILD.gn
similarity index 87%
rename from chromeos/components/cros_elements/button/BUILD.gn
rename to chromeos/ash/components/cros_elements/button/BUILD.gn
index 5b6702ac..1aa395ec 100644
--- a/chromeos/components/cros_elements/button/BUILD.gn
+++ b/chromeos/ash/components/cros_elements/button/BUILD.gn
@@ -2,9 +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/chromeos/ui_mode.gni")
 import("//third_party/material_web_components/rewrite_imports.gni")
 import("//tools/typescript/ts_library.gni")
 
+assert(is_chromeos_ash, "Non Chrome OS builds cannot depend on //chromeos/ash")
+
 ts_library("button_ts") {
   composite = true
   root_dir = "."
diff --git a/chromeos/components/cros_elements/button/button.ts b/chromeos/ash/components/cros_elements/button/button.ts
similarity index 100%
rename from chromeos/components/cros_elements/button/button.ts
rename to chromeos/ash/components/cros_elements/button/button.ts
diff --git a/chromeos/components/cros_elements/tsconfig_base.json b/chromeos/ash/components/cros_elements/tsconfig_base.json
similarity index 100%
rename from chromeos/components/cros_elements/tsconfig_base.json
rename to chromeos/ash/components/cros_elements/tsconfig_base.json
diff --git a/chromeos/ash/components/network/onc/BUILD.gn b/chromeos/ash/components/network/onc/BUILD.gn
new file mode 100644
index 0000000..ac8ef45
--- /dev/null
+++ b/chromeos/ash/components/network/onc/BUILD.gn
@@ -0,0 +1,79 @@
+# Copyright 2022 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/config/chromeos/ui_mode.gni")
+import("//testing/libfuzzer/fuzzer_test.gni")
+
+assert(is_chromeos_ash, "Non-ChromeOS builds cannot depend on //chromeos/ash")
+
+source_set("onc") {
+  # Due to circular dependency, others should depend on via chromeos/network.
+  visibility = [ "//chromeos/network" ]
+
+  configs += [ "//chromeos/network:network_config" ]
+  deps = [
+    "//base",
+    "//chromeos/components/onc",
+    "//components/account_id",
+    "//components/certificate_matching",
+    "//components/device_event_log",
+    "//components/onc",
+    "//components/prefs",
+    "//components/proxy_config",
+    "//components/url_formatter",
+    "//components/user_manager",
+    "//net",
+  ]
+  sources = [
+    "network_onc_utils.cc",
+    "network_onc_utils.h",
+    "onc_certificate_importer.h",
+    "onc_certificate_importer_impl.cc",
+    "onc_certificate_importer_impl.h",
+    "onc_certificate_pattern.cc",
+    "onc_certificate_pattern.h",
+    "onc_merger.cc",
+    "onc_merger.h",
+    "onc_normalizer.cc",
+    "onc_normalizer.h",
+    "onc_translation_tables.cc",
+    "onc_translation_tables.h",
+    "onc_translator.h",
+    "onc_translator_onc_to_shill.cc",
+    "onc_translator_shill_to_onc.cc",
+  ]
+}
+
+source_set("unit_tests") {
+  testonly = true
+  deps = [
+    "//base/test:test_support",
+    "//chromeos:test_utils",
+    "//chromeos/components/onc",
+    "//chromeos/components/onc:test_support",
+    "//chromeos/network",
+    "//components/onc",
+    "//crypto:test_support",
+    "//net:test_support",
+    "//testing/gmock",
+    "//testing/gtest",
+  ]
+  sources = [
+    "network_onc_utils_unittest.cc",
+    "onc_certificate_importer_impl_unittest.cc",
+    "onc_certificate_pattern_unittest.cc",
+    "onc_merger_unittest.cc",
+    "onc_normalizer_unittest.cc",
+    "onc_translator_unittest.cc",
+  ]
+}
+
+fuzzer_test("onc_normalizer_fuzzer") {
+  sources = [ "onc_normalizer_fuzzer.cc" ]
+  deps = [
+    "//base",
+    "//chromeos/components/onc",
+    "//chromeos/network",
+  ]
+}
diff --git a/chromeos/network/onc/DIR_METADATA b/chromeos/ash/components/network/onc/DIR_METADATA
similarity index 100%
rename from chromeos/network/onc/DIR_METADATA
rename to chromeos/ash/components/network/onc/DIR_METADATA
diff --git a/chromeos/network/onc/network_onc_utils.cc b/chromeos/ash/components/network/onc/network_onc_utils.cc
similarity index 98%
rename from chromeos/network/onc/network_onc_utils.cc
rename to chromeos/ash/components/network/onc/network_onc_utils.cc
index 32f291c..800ca67 100644
--- a/chromeos/network/onc/network_onc_utils.cc
+++ b/chromeos/ash/components/network/onc/network_onc_utils.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chromeos/network/onc/network_onc_utils.h"
+#include "chromeos/ash/components/network/onc/network_onc_utils.h"
 
 #include <stddef.h>
 #include <stdint.h>
@@ -20,6 +20,9 @@
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_util.h"
 #include "base/values.h"
+#include "chromeos/ash/components/network/onc/network_onc_utils.h"
+#include "chromeos/ash/components/network/onc/onc_normalizer.h"
+#include "chromeos/ash/components/network/onc/onc_translator.h"
 #include "chromeos/components/onc/onc_mapper.h"
 #include "chromeos/components/onc/onc_signature.h"
 #include "chromeos/components/onc/onc_utils.h"
@@ -32,9 +35,6 @@
 #include "chromeos/network/network_state.h"
 #include "chromeos/network/network_state_handler.h"
 #include "chromeos/network/network_ui_data.h"
-#include "chromeos/network/onc/network_onc_utils.h"
-#include "chromeos/network/onc/onc_normalizer.h"
-#include "chromeos/network/onc/onc_translator.h"
 #include "chromeos/network/tether_constants.h"
 #include "components/account_id/account_id.h"
 #include "components/device_event_log/device_event_log.h"
diff --git a/chromeos/network/onc/network_onc_utils.h b/chromeos/ash/components/network/onc/network_onc_utils.h
similarity index 94%
rename from chromeos/network/onc/network_onc_utils.h
rename to chromeos/ash/components/network/onc/network_onc_utils.h
index e32af83..17c325f 100644
--- a/chromeos/network/onc/network_onc_utils.h
+++ b/chromeos/ash/components/network/onc/network_onc_utils.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROMEOS_NETWORK_ONC_NETWORK_ONC_UTILS_H_
-#define CHROMEOS_NETWORK_ONC_NETWORK_ONC_UTILS_H_
+#ifndef CHROMEOS_ASH_COMPONENTS_NETWORK_ONC_NETWORK_ONC_UTILS_H_
+#define CHROMEOS_ASH_COMPONENTS_NETWORK_ONC_NETWORK_ONC_UTILS_H_
 
 #include <map>
 #include <memory>
@@ -99,4 +99,4 @@
 }  // namespace onc
 }  // namespace chromeos
 
-#endif  // CHROMEOS_NETWORK_ONC_NETWORK_ONC_UTILS_H_
+#endif  // CHROMEOS_ASH_COMPONENTS_NETWORK_ONC_NETWORK_ONC_UTILS_H_
diff --git a/chromeos/network/onc/network_onc_utils_unittest.cc b/chromeos/ash/components/network/onc/network_onc_utils_unittest.cc
similarity index 98%
rename from chromeos/network/onc/network_onc_utils_unittest.cc
rename to chromeos/ash/components/network/onc/network_onc_utils_unittest.cc
index 411e897..3187e244 100644
--- a/chromeos/network/onc/network_onc_utils_unittest.cc
+++ b/chromeos/ash/components/network/onc/network_onc_utils_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chromeos/network/onc/network_onc_utils.h"
+#include "chromeos/ash/components/network/onc/network_onc_utils.h"
 
 #include <string>
 
diff --git a/chromeos/network/onc/onc_certificate_importer.h b/chromeos/ash/components/network/onc/onc_certificate_importer.h
similarity index 91%
rename from chromeos/network/onc/onc_certificate_importer.h
rename to chromeos/ash/components/network/onc/onc_certificate_importer.h
index 97aa3dd..8cc1e195 100644
--- a/chromeos/network/onc/onc_certificate_importer.h
+++ b/chromeos/ash/components/network/onc/onc_certificate_importer.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROMEOS_NETWORK_ONC_ONC_CERTIFICATE_IMPORTER_H_
-#define CHROMEOS_NETWORK_ONC_ONC_CERTIFICATE_IMPORTER_H_
+#ifndef CHROMEOS_ASH_COMPONENTS_NETWORK_ONC_ONC_CERTIFICATE_IMPORTER_H_
+#define CHROMEOS_ASH_COMPONENTS_NETWORK_ONC_ONC_CERTIFICATE_IMPORTER_H_
 
 #include "base/callback_forward.h"
 #include "base/component_export.h"
@@ -57,4 +57,4 @@
 }  // namespace onc
 }  // namespace chromeos
 
-#endif  // CHROMEOS_NETWORK_ONC_ONC_CERTIFICATE_IMPORTER_H_
+#endif  // CHROMEOS_ASH_COMPONENTS_NETWORK_ONC_ONC_CERTIFICATE_IMPORTER_H_
diff --git a/chromeos/network/onc/onc_certificate_importer_impl.cc b/chromeos/ash/components/network/onc/onc_certificate_importer_impl.cc
similarity index 98%
rename from chromeos/network/onc/onc_certificate_importer_impl.cc
rename to chromeos/ash/components/network/onc/onc_certificate_importer_impl.cc
index a5c2b90..f3c41aa 100644
--- a/chromeos/network/onc/onc_certificate_importer_impl.cc
+++ b/chromeos/ash/components/network/onc/onc_certificate_importer_impl.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chromeos/network/onc/onc_certificate_importer_impl.h"
+#include "chromeos/ash/components/network/onc/onc_certificate_importer_impl.h"
 
 #include <cert.h>
 #include <keyhi.h>
@@ -21,9 +21,9 @@
 #include "base/threading/sequenced_task_runner_handle.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/values.h"
+#include "chromeos/ash/components/network/onc/network_onc_utils.h"
 #include "chromeos/components/onc/onc_parsed_certificates.h"
 #include "chromeos/network/network_event_log.h"
-#include "chromeos/network/onc/network_onc_utils.h"
 #include "crypto/scoped_nss_types.h"
 #include "net/base/net_errors.h"
 #include "net/cert/nss_cert_database.h"
diff --git a/chromeos/network/onc/onc_certificate_importer_impl.h b/chromeos/ash/components/network/onc/onc_certificate_importer_impl.h
similarity index 92%
rename from chromeos/network/onc/onc_certificate_importer_impl.h
rename to chromeos/ash/components/network/onc/onc_certificate_importer_impl.h
index 2f433bc..9405809 100644
--- a/chromeos/network/onc/onc_certificate_importer_impl.h
+++ b/chromeos/ash/components/network/onc/onc_certificate_importer_impl.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROMEOS_NETWORK_ONC_ONC_CERTIFICATE_IMPORTER_IMPL_H_
-#define CHROMEOS_NETWORK_ONC_ONC_CERTIFICATE_IMPORTER_IMPL_H_
+#ifndef CHROMEOS_ASH_COMPONENTS_NETWORK_ONC_ONC_CERTIFICATE_IMPORTER_IMPL_H_
+#define CHROMEOS_ASH_COMPONENTS_NETWORK_ONC_ONC_CERTIFICATE_IMPORTER_IMPL_H_
 
 #include <map>
 #include <memory>
@@ -13,8 +13,8 @@
 #include "base/component_export.h"
 #include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
+#include "chromeos/ash/components/network/onc/onc_certificate_importer.h"
 #include "chromeos/components/onc/onc_parsed_certificates.h"
-#include "chromeos/network/onc/onc_certificate_importer.h"
 #include "components/onc/onc_constants.h"
 
 namespace base {
@@ -110,4 +110,4 @@
 }  // namespace onc
 }  // namespace chromeos
 
-#endif  // CHROMEOS_NETWORK_ONC_ONC_CERTIFICATE_IMPORTER_IMPL_H_
+#endif  // CHROMEOS_ASH_COMPONENTS_NETWORK_ONC_ONC_CERTIFICATE_IMPORTER_IMPL_H_
diff --git a/chromeos/network/onc/onc_certificate_importer_impl_unittest.cc b/chromeos/ash/components/network/onc/onc_certificate_importer_impl_unittest.cc
similarity index 99%
rename from chromeos/network/onc/onc_certificate_importer_impl_unittest.cc
rename to chromeos/ash/components/network/onc/onc_certificate_importer_impl_unittest.cc
index e883329..0ea3a9c 100644
--- a/chromeos/network/onc/onc_certificate_importer_impl_unittest.cc
+++ b/chromeos/ash/components/network/onc/onc_certificate_importer_impl_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chromeos/network/onc/onc_certificate_importer_impl.h"
+#include "chromeos/ash/components/network/onc/onc_certificate_importer_impl.h"
 
 #include <cert.h>
 
diff --git a/chromeos/network/onc/onc_certificate_pattern.cc b/chromeos/ash/components/network/onc/onc_certificate_pattern.cc
similarity index 98%
rename from chromeos/network/onc/onc_certificate_pattern.cc
rename to chromeos/ash/components/network/onc/onc_certificate_pattern.cc
index 856285f..11d58339 100644
--- a/chromeos/network/onc/onc_certificate_pattern.cc
+++ b/chromeos/ash/components/network/onc/onc_certificate_pattern.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chromeos/network/onc/onc_certificate_pattern.h"
+#include "chromeos/ash/components/network/onc/onc_certificate_pattern.h"
 
 #include <stddef.h>
 
diff --git a/chromeos/network/onc/onc_certificate_pattern.h b/chromeos/ash/components/network/onc/onc_certificate_pattern.h
similarity index 91%
rename from chromeos/network/onc/onc_certificate_pattern.h
rename to chromeos/ash/components/network/onc/onc_certificate_pattern.h
index e3a656cd..728195ea 100644
--- a/chromeos/network/onc/onc_certificate_pattern.h
+++ b/chromeos/ash/components/network/onc/onc_certificate_pattern.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROMEOS_NETWORK_ONC_ONC_CERTIFICATE_PATTERN_H_
-#define CHROMEOS_NETWORK_ONC_ONC_CERTIFICATE_PATTERN_H_
+#ifndef CHROMEOS_ASH_COMPONENTS_NETWORK_ONC_ONC_CERTIFICATE_PATTERN_H_
+#define CHROMEOS_ASH_COMPONENTS_NETWORK_ONC_ONC_CERTIFICATE_PATTERN_H_
 
 #include <memory>
 #include <string>
@@ -75,4 +75,4 @@
 
 }  // namespace chromeos
 
-#endif  // CHROMEOS_NETWORK_ONC_ONC_CERTIFICATE_PATTERN_H_
+#endif  // CHROMEOS_ASH_COMPONENTS_NETWORK_ONC_ONC_CERTIFICATE_PATTERN_H_
diff --git a/chromeos/network/onc/onc_certificate_pattern_unittest.cc b/chromeos/ash/components/network/onc/onc_certificate_pattern_unittest.cc
similarity index 98%
rename from chromeos/network/onc/onc_certificate_pattern_unittest.cc
rename to chromeos/ash/components/network/onc/onc_certificate_pattern_unittest.cc
index b635477..eee39b7 100644
--- a/chromeos/network/onc/onc_certificate_pattern_unittest.cc
+++ b/chromeos/ash/components/network/onc/onc_certificate_pattern_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chromeos/network/onc/onc_certificate_pattern.h"
+#include "chromeos/ash/components/network/onc/onc_certificate_pattern.h"
 
 #include <string>
 
diff --git a/chromeos/network/onc/onc_merger.cc b/chromeos/ash/components/network/onc/onc_merger.cc
similarity index 99%
rename from chromeos/network/onc/onc_merger.cc
rename to chromeos/ash/components/network/onc/onc_merger.cc
index 7ff4568..e9c93e8 100644
--- a/chromeos/network/onc/onc_merger.cc
+++ b/chromeos/ash/components/network/onc/onc_merger.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chromeos/network/onc/onc_merger.h"
+#include "chromeos/ash/components/network/onc/onc_merger.h"
 
 #include <set>
 #include <string>
diff --git a/chromeos/network/onc/onc_merger.h b/chromeos/ash/components/network/onc/onc_merger.h
similarity index 91%
rename from chromeos/network/onc/onc_merger.h
rename to chromeos/ash/components/network/onc/onc_merger.h
index 7dfedadb..3f7887a 100644
--- a/chromeos/network/onc/onc_merger.h
+++ b/chromeos/ash/components/network/onc/onc_merger.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROMEOS_NETWORK_ONC_ONC_MERGER_H_
-#define CHROMEOS_NETWORK_ONC_ONC_MERGER_H_
+#ifndef CHROMEOS_ASH_COMPONENTS_NETWORK_ONC_ONC_MERGER_H_
+#define CHROMEOS_ASH_COMPONENTS_NETWORK_ONC_ONC_MERGER_H_
 
 #include "base/component_export.h"
 
@@ -51,4 +51,4 @@
 }  // namespace onc
 }  // namespace chromeos
 
-#endif  // CHROMEOS_NETWORK_ONC_ONC_MERGER_H_
+#endif  // CHROMEOS_ASH_COMPONENTS_NETWORK_ONC_ONC_MERGER_H_
diff --git a/chromeos/network/onc/onc_merger_unittest.cc b/chromeos/ash/components/network/onc/onc_merger_unittest.cc
similarity index 98%
rename from chromeos/network/onc/onc_merger_unittest.cc
rename to chromeos/ash/components/network/onc/onc_merger_unittest.cc
index 6c97d15..d8eaa2ff 100644
--- a/chromeos/network/onc/onc_merger_unittest.cc
+++ b/chromeos/ash/components/network/onc/onc_merger_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chromeos/network/onc/onc_merger.h"
+#include "chromeos/ash/components/network/onc/onc_merger.h"
 
 #include <string>
 
diff --git a/chromeos/network/onc/onc_normalizer.cc b/chromeos/ash/components/network/onc/onc_normalizer.cc
similarity index 98%
rename from chromeos/network/onc/onc_normalizer.cc
rename to chromeos/ash/components/network/onc/onc_normalizer.cc
index 0057a52..f0d411f 100644
--- a/chromeos/network/onc/onc_normalizer.cc
+++ b/chromeos/ash/components/network/onc/onc_normalizer.cc
@@ -2,16 +2,16 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chromeos/network/onc/onc_normalizer.h"
+#include "chromeos/ash/components/network/onc/onc_normalizer.h"
 
 #include <string>
 
 #include "base/logging.h"
 #include "base/values.h"
+#include "chromeos/ash/components/network/onc/network_onc_utils.h"
 #include "chromeos/components/onc/onc_signature.h"
 #include "chromeos/components/onc/onc_utils.h"
 #include "chromeos/network/network_event_log.h"
-#include "chromeos/network/onc/network_onc_utils.h"
 #include "components/onc/onc_constants.h"
 
 namespace chromeos {
diff --git a/chromeos/network/onc/onc_normalizer.h b/chromeos/ash/components/network/onc/onc_normalizer.h
similarity index 91%
rename from chromeos/network/onc/onc_normalizer.h
rename to chromeos/ash/components/network/onc/onc_normalizer.h
index ac6ef76..6ef887f5 100644
--- a/chromeos/network/onc/onc_normalizer.h
+++ b/chromeos/ash/components/network/onc/onc_normalizer.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROMEOS_NETWORK_ONC_ONC_NORMALIZER_H_
-#define CHROMEOS_NETWORK_ONC_ONC_NORMALIZER_H_
+#ifndef CHROMEOS_ASH_COMPONENTS_NETWORK_ONC_ONC_NORMALIZER_H_
+#define CHROMEOS_ASH_COMPONENTS_NETWORK_ONC_ONC_NORMALIZER_H_
 
 #include "base/component_export.h"
 #include "base/values.h"
@@ -58,4 +58,4 @@
 }  // namespace onc
 }  // namespace chromeos
 
-#endif  // CHROMEOS_NETWORK_ONC_ONC_NORMALIZER_H_
+#endif  // CHROMEOS_ASH_COMPONENTS_NETWORK_ONC_ONC_NORMALIZER_H_
diff --git a/chromeos/network/onc/onc_normalizer_fuzzer.cc b/chromeos/ash/components/network/onc/onc_normalizer_fuzzer.cc
similarity index 93%
rename from chromeos/network/onc/onc_normalizer_fuzzer.cc
rename to chromeos/ash/components/network/onc/onc_normalizer_fuzzer.cc
index 2d04d4e7..6961c24 100644
--- a/chromeos/network/onc/onc_normalizer_fuzzer.cc
+++ b/chromeos/ash/components/network/onc/onc_normalizer_fuzzer.cc
@@ -8,8 +8,8 @@
 #include "base/json/json_reader.h"
 #include "base/strings/string_piece.h"
 #include "base/values.h"
+#include "chromeos/ash/components/network/onc/onc_normalizer.h"
 #include "chromeos/components/onc/onc_signature.h"
-#include "chromeos/network/onc/onc_normalizer.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace chromeos {
diff --git a/chromeos/network/onc/onc_normalizer_unittest.cc b/chromeos/ash/components/network/onc/onc_normalizer_unittest.cc
similarity index 98%
rename from chromeos/network/onc/onc_normalizer_unittest.cc
rename to chromeos/ash/components/network/onc/onc_normalizer_unittest.cc
index a6da6ba..5571636 100644
--- a/chromeos/network/onc/onc_normalizer_unittest.cc
+++ b/chromeos/ash/components/network/onc/onc_normalizer_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chromeos/network/onc/onc_normalizer.h"
+#include "chromeos/ash/components/network/onc/onc_normalizer.h"
 
 #include "base/values.h"
 #include "chromeos/components/onc/onc_signature.h"
diff --git a/chromeos/network/onc/onc_translation_tables.cc b/chromeos/ash/components/network/onc/onc_translation_tables.cc
similarity index 99%
rename from chromeos/network/onc/onc_translation_tables.cc
rename to chromeos/ash/components/network/onc/onc_translation_tables.cc
index e5067fd..5d7f846 100644
--- a/chromeos/network/onc/onc_translation_tables.cc
+++ b/chromeos/ash/components/network/onc/onc_translation_tables.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chromeos/network/onc/onc_translation_tables.h"
+#include "chromeos/ash/components/network/onc/onc_translation_tables.h"
 
 #include <cstddef>
 
diff --git a/chromeos/network/onc/onc_translation_tables.h b/chromeos/ash/components/network/onc/onc_translation_tables.h
similarity index 94%
rename from chromeos/network/onc/onc_translation_tables.h
rename to chromeos/ash/components/network/onc/onc_translation_tables.h
index 5cbcd27..36c75ec 100644
--- a/chromeos/network/onc/onc_translation_tables.h
+++ b/chromeos/ash/components/network/onc/onc_translation_tables.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROMEOS_NETWORK_ONC_ONC_TRANSLATION_TABLES_H_
-#define CHROMEOS_NETWORK_ONC_ONC_TRANSLATION_TABLES_H_
+#ifndef CHROMEOS_ASH_COMPONENTS_NETWORK_ONC_ONC_TRANSLATION_TABLES_H_
+#define CHROMEOS_ASH_COMPONENTS_NETWORK_ONC_ONC_TRANSLATION_TABLES_H_
 
 #include <string>
 #include <vector>
@@ -98,4 +98,4 @@
 }  // namespace onc
 }  // namespace chromeos
 
-#endif  // CHROMEOS_NETWORK_ONC_ONC_TRANSLATION_TABLES_H_
+#endif  // CHROMEOS_ASH_COMPONENTS_NETWORK_ONC_ONC_TRANSLATION_TABLES_H_
diff --git a/chromeos/network/onc/onc_translator.h b/chromeos/ash/components/network/onc/onc_translator.h
similarity index 91%
rename from chromeos/network/onc/onc_translator.h
rename to chromeos/ash/components/network/onc/onc_translator.h
index 21ed517..62fb6d8 100644
--- a/chromeos/network/onc/onc_translator.h
+++ b/chromeos/ash/components/network/onc/onc_translator.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROMEOS_NETWORK_ONC_ONC_TRANSLATOR_H_
-#define CHROMEOS_NETWORK_ONC_ONC_TRANSLATOR_H_
+#ifndef CHROMEOS_ASH_COMPONENTS_NETWORK_ONC_ONC_TRANSLATOR_H_
+#define CHROMEOS_ASH_COMPONENTS_NETWORK_ONC_ONC_TRANSLATOR_H_
 
 #include "base/component_export.h"
 #include "components/onc/onc_constants.h"
@@ -53,4 +53,4 @@
 }  // namespace onc
 }  // namespace chromeos
 
-#endif  // CHROMEOS_NETWORK_ONC_ONC_TRANSLATOR_H_
+#endif  // CHROMEOS_ASH_COMPONENTS_NETWORK_ONC_ONC_TRANSLATOR_H_
diff --git a/chromeos/network/onc/onc_translator_onc_to_shill.cc b/chromeos/ash/components/network/onc/onc_translator_onc_to_shill.cc
similarity index 98%
rename from chromeos/network/onc/onc_translator_onc_to_shill.cc
rename to chromeos/ash/components/network/onc/onc_translator_onc_to_shill.cc
index d50075d..408b52b76 100644
--- a/chromeos/network/onc/onc_translator_onc_to_shill.cc
+++ b/chromeos/ash/components/network/onc/onc_translator_onc_to_shill.cc
@@ -20,12 +20,12 @@
 #include "base/notreached.h"
 #include "base/strings/string_util.h"
 #include "base/values.h"
+#include "chromeos/ash/components/network/onc/network_onc_utils.h"
+#include "chromeos/ash/components/network/onc/onc_translation_tables.h"
+#include "chromeos/ash/components/network/onc/onc_translator.h"
 #include "chromeos/components/onc/onc_signature.h"
 #include "chromeos/network/client_cert_util.h"
 #include "chromeos/network/network_event_log.h"
-#include "chromeos/network/onc/network_onc_utils.h"
-#include "chromeos/network/onc/onc_translation_tables.h"
-#include "chromeos/network/onc/onc_translator.h"
 #include "chromeos/network/shill_property_util.h"
 #include "components/onc/onc_constants.h"
 #include "net/base/ip_address.h"
diff --git a/chromeos/network/onc/onc_translator_shill_to_onc.cc b/chromeos/ash/components/network/onc/onc_translator_shill_to_onc.cc
similarity index 99%
rename from chromeos/network/onc/onc_translator_shill_to_onc.cc
rename to chromeos/ash/components/network/onc/onc_translator_shill_to_onc.cc
index 7d78339..6d943ed 100644
--- a/chromeos/network/onc/onc_translator_shill_to_onc.cc
+++ b/chromeos/ash/components/network/onc/onc_translator_shill_to_onc.cc
@@ -12,14 +12,14 @@
 #include "base/logging.h"
 #include "base/strings/string_util.h"
 #include "base/values.h"
+#include "chromeos/ash/components/network/onc/network_onc_utils.h"
+#include "chromeos/ash/components/network/onc/onc_translation_tables.h"
+#include "chromeos/ash/components/network/onc/onc_translator.h"
 #include "chromeos/components/onc/onc_signature.h"
 #include "chromeos/components/onc/onc_utils.h"
 #include "chromeos/network/network_profile_handler.h"
 #include "chromeos/network/network_state.h"
 #include "chromeos/network/network_util.h"
-#include "chromeos/network/onc/network_onc_utils.h"
-#include "chromeos/network/onc/onc_translation_tables.h"
-#include "chromeos/network/onc/onc_translator.h"
 #include "chromeos/network/shill_property_util.h"
 #include "components/device_event_log/device_event_log.h"
 #include "components/onc/onc_constants.h"
diff --git a/chromeos/network/onc/onc_translator_unittest.cc b/chromeos/ash/components/network/onc/onc_translator_unittest.cc
similarity index 98%
rename from chromeos/network/onc/onc_translator_unittest.cc
rename to chromeos/ash/components/network/onc/onc_translator_unittest.cc
index ad3c7ac..ae61b154 100644
--- a/chromeos/network/onc/onc_translator_unittest.cc
+++ b/chromeos/ash/components/network/onc/onc_translator_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chromeos/network/onc/onc_translator.h"
+#include "chromeos/ash/components/network/onc/onc_translator.h"
 
 #include <string>
 #include <utility>
diff --git a/chromeos/chromeos_strings.grd b/chromeos/chromeos_strings.grd
index 375c48a..fbb6656 100644
--- a/chromeos/chromeos_strings.grd
+++ b/chromeos/chromeos_strings.grd
@@ -2356,6 +2356,9 @@
       <message name="IDS_PERSONALIZATION_APP_KEYBOARD_BACKLIGHT_WALLPAPER_COLOR_LABEL" desc="Label for the wallpaper extracted color in the keyboard backlight section of personalization hub.">
         Wallpaper color
       </message>
+      <message name="IDS_PERSONALIZATION_APP_KEYBOARD_BACKLIGHT_WALLPAPER_COLOR_TOOLTIP_TEXT" desc="The text label of the tooltip for the wallpaper extracted color in the keyboard backlight section of personalization hub.">
+        Keyboard color is based on wallpaper
+      </message>
       <message name="IDS_PERSONALIZATION_APP_KEYBOARD_BACKLIGHT_WHITE_COLOR_LABEL" desc="Label for the white color in the keyboard backlight section of personalization hub.">
         White
       </message>
diff --git a/chromeos/chromeos_strings_grd/IDS_PERSONALIZATION_APP_KEYBOARD_BACKLIGHT_WALLPAPER_COLOR_TOOLTIP_TEXT.png.sha1 b/chromeos/chromeos_strings_grd/IDS_PERSONALIZATION_APP_KEYBOARD_BACKLIGHT_WALLPAPER_COLOR_TOOLTIP_TEXT.png.sha1
new file mode 100644
index 0000000..83a72ea3
--- /dev/null
+++ b/chromeos/chromeos_strings_grd/IDS_PERSONALIZATION_APP_KEYBOARD_BACKLIGHT_WALLPAPER_COLOR_TOOLTIP_TEXT.png.sha1
@@ -0,0 +1 @@
+ef73aa1f0cd42d275901326ca9f37584c0192d7e
\ No newline at end of file
diff --git a/chromeos/constants/chromeos_features.cc b/chromeos/constants/chromeos_features.cc
index 2f478cc..390a6b9 100644
--- a/chromeos/constants/chromeos_features.cc
+++ b/chromeos/constants/chromeos_features.cc
@@ -21,6 +21,12 @@
 const base::Feature kDarkLightMode{"DarkLightMode",
                                    base::FEATURE_DISABLED_BY_DEFAULT};
 
+// Disables "Office Editing for Docs, Sheets & Slides" component app so handlers
+// won't be registered, making it possible to install another version for
+// testing.
+const base::Feature kDisableOfficeEditingComponentApp{
+    "DisableOfficeEditingComponentApp", base::FEATURE_DISABLED_BY_DEFAULT};
+
 // Disables translation services of the Quick Answers V2.
 const base::Feature kDisableQuickAnswersV2Translation{
     "DisableQuickAnswersV2Translation", base::FEATURE_DISABLED_BY_DEFAULT};
diff --git a/chromeos/constants/chromeos_features.h b/chromeos/constants/chromeos_features.h
index f09b8a494..c20552ab 100644
--- a/chromeos/constants/chromeos_features.h
+++ b/chromeos/constants/chromeos_features.h
@@ -26,6 +26,8 @@
 extern const base::Feature kBluetoothPhoneFilter;
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const base::Feature kDarkLightMode;
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
+extern const base::Feature kDisableOfficeEditingComponentApp;
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
 extern const base::Feature kDisableQuickAnswersV2Translation;
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
 extern const base::Feature kQuickAnswersV2SettingsSubToggle;
diff --git a/chromeos/language/language_packs/language_packs_impl.cc b/chromeos/language/language_packs/language_packs_impl.cc
index ce829da2..41adffa3 100644
--- a/chromeos/language/language_packs/language_packs_impl.cc
+++ b/chromeos/language/language_packs/language_packs_impl.cc
@@ -33,27 +33,28 @@
   }
 }
 
+PackState GetPackStateFromStatusCode(const PackResult::StatusCode status_code) {
+  switch (status_code) {
+    case PackResult::NOT_INSTALLED:
+      return PackState::NOT_INSTALLED;
+    case PackResult::IN_PROGRESS:
+      return PackState::INSTALLING;
+    case PackResult::INSTALLED:
+      return PackState::INSTALLED;
+    // Catch all remaining cases as error.
+    default:
+      return PackState::ERROR;
+  }
+}
+
 // Called when GetPackState() or InstallPack() functions from Language Packs
 // are complete.
 void OnOperationComplete(LanguagePacksImpl::GetPackInfoCallback mojo_callback,
                          const PackResult& pack_result) {
   auto info = LanguagePackInfo::New();
-  switch (pack_result.pack_state) {
-    case PackResult::NOT_INSTALLED:
-      info->pack_state = PackState::NOT_INSTALLED;
-      break;
-    case PackResult::IN_PROGRESS:
-      info->pack_state = PackState::INSTALLING;
-      break;
-    case PackResult::INSTALLED:
-      info->pack_state = PackState::INSTALLED;
-      info->path = pack_result.path;
-      break;
-
-    // Catch all remaining cases as error.
-    default:
-      info->pack_state = PackState::ERROR;
-      break;
+  info->pack_state = GetPackStateFromStatusCode(pack_result.pack_state);
+  if (pack_result.pack_state == PackResult::INSTALLED) {
+    info->path = pack_result.path;
   }
 
   base::UmaHistogramEnumeration("ChromeOS.LanguagePacks.Mojo.PackStateResponse",
diff --git a/chromeos/network/BUILD.gn b/chromeos/network/BUILD.gn
index ca35ad8..8c7bdcc 100644
--- a/chromeos/network/BUILD.gn
+++ b/chromeos/network/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("//testing/libfuzzer/fuzzer_test.gni")
 import("//testing/test.gni")
 import("//third_party/protobuf/proto_library.gni")
 
@@ -14,7 +13,10 @@
 
 component("network") {
   configs += [ ":network_config" ]
-  public_deps = [ "//chromeos/ash/components/network/metrics" ]
+  public_deps = [
+    "//chromeos/ash/components/network/metrics",
+    "//chromeos/ash/components/network/onc",
+  ]
   deps = [
     "//ash/constants",
     "//base",
@@ -28,7 +30,6 @@
     "//chromeos/login/login_state",
     "//chromeos/services/network_config/public/mojom",
     "//components/account_id",
-    "//components/certificate_matching",
     "//components/crx_file",
     "//components/device_event_log",
     "//components/onc",
@@ -47,7 +48,10 @@
 
   # Allow circular dependency from sub-directories of
   # chromeos/ash/components/network.
-  allow_circular_includes_from = [ "//chromeos/ash/components/network/metrics" ]
+  allow_circular_includes_from = [
+    "//chromeos/ash/components/network/metrics",
+    "//chromeos/ash/components/network/onc",
+  ]
   sources = [
     "auto_connect_handler.cc",
     "auto_connect_handler.h",
@@ -153,22 +157,6 @@
     "network_ui_data.h",
     "network_util.cc",
     "network_util.h",
-    "onc/network_onc_utils.cc",
-    "onc/network_onc_utils.h",
-    "onc/onc_certificate_importer.h",
-    "onc/onc_certificate_importer_impl.cc",
-    "onc/onc_certificate_importer_impl.h",
-    "onc/onc_certificate_pattern.cc",
-    "onc/onc_certificate_pattern.h",
-    "onc/onc_merger.cc",
-    "onc/onc_merger.h",
-    "onc/onc_normalizer.cc",
-    "onc/onc_normalizer.h",
-    "onc/onc_translation_tables.cc",
-    "onc/onc_translation_tables.h",
-    "onc/onc_translator.h",
-    "onc/onc_translator_onc_to_shill.cc",
-    "onc/onc_translator_shill_to_onc.cc",
     "policy_applicator.cc",
     "policy_applicator.h",
     "policy_certificate_provider.h",
@@ -267,6 +255,7 @@
     "//base/test:test_support",
     "//chromeos:test_utils",
     "//chromeos/ash/components/network/metrics:unit_tests",
+    "//chromeos/ash/components/network/onc:unit_tests",
     "//chromeos/components/feature_usage",
     "//chromeos/components/onc",
     "//chromeos/components/onc:test_support",
@@ -324,12 +313,6 @@
     "network_type_pattern_unittest.cc",
     "network_ui_data_unittest.cc",
     "network_util_unittest.cc",
-    "onc/network_onc_utils_unittest.cc",
-    "onc/onc_certificate_importer_impl_unittest.cc",
-    "onc/onc_certificate_pattern_unittest.cc",
-    "onc/onc_merger_unittest.cc",
-    "onc/onc_normalizer_unittest.cc",
-    "onc/onc_translator_unittest.cc",
     "profile_policies_unittest.cc",
     "prohibited_technologies_handler_unittest.cc",
     "proxy/proxy_config_service_impl_unittest.cc",
@@ -340,12 +323,3 @@
     "system_token_cert_db_storage_unittest.cc",
   ]
 }
-
-fuzzer_test("onc_normalizer_fuzzer") {
-  sources = [ "onc/onc_normalizer_fuzzer.cc" ]
-  deps = [
-    ":network",
-    "//base",
-    "//chromeos/components/onc:onc",
-  ]
-}
diff --git a/chromeos/network/client_cert_resolver.cc b/chromeos/network/client_cert_resolver.cc
index 60e3280..2e38af03 100644
--- a/chromeos/network/client_cert_resolver.cc
+++ b/chromeos/network/client_cert_resolver.cc
@@ -21,13 +21,13 @@
 #include "base/task/thread_pool.h"
 #include "base/time/clock.h"
 #include "base/values.h"
+#include "chromeos/ash/components/network/onc/onc_certificate_pattern.h"
 #include "chromeos/components/onc/variable_expander.h"
 #include "chromeos/dbus/shill/shill_service_client.h"
 #include "chromeos/network/certificate_helper.h"
 #include "chromeos/network/managed_network_configuration_handler.h"
 #include "chromeos/network/network_event_log.h"
 #include "chromeos/network/network_state.h"
-#include "chromeos/network/onc/onc_certificate_pattern.h"
 #include "components/onc/onc_constants.h"
 #include "crypto/scoped_nss_types.h"
 #include "dbus/object_path.h"
diff --git a/chromeos/network/client_cert_resolver_unittest.cc b/chromeos/network/client_cert_resolver_unittest.cc
index 23ea2b54..ca0963b 100644
--- a/chromeos/network/client_cert_resolver_unittest.cc
+++ b/chromeos/network/client_cert_resolver_unittest.cc
@@ -19,6 +19,7 @@
 #include "base/test/simple_test_clock.h"
 #include "base/test/task_environment.h"
 #include "base/values.h"
+#include "chromeos/ash/components/network/onc/onc_certificate_importer_impl.h"
 #include "chromeos/components/onc/onc_test_utils.h"
 #include "chromeos/dbus/shill/shill_clients.h"
 #include "chromeos/dbus/shill/shill_manager_client.h"
@@ -29,7 +30,6 @@
 #include "chromeos/network/network_configuration_handler.h"
 #include "chromeos/network/network_profile_handler.h"
 #include "chromeos/network/network_state_handler.h"
-#include "chromeos/network/onc/onc_certificate_importer_impl.h"
 #include "chromeos/network/system_token_cert_db_storage.h"
 #include "components/onc/onc_constants.h"
 #include "crypto/scoped_nss_types.h"
diff --git a/chromeos/network/client_cert_util.h b/chromeos/network/client_cert_util.h
index 8fc8226..b4f241e 100644
--- a/chromeos/network/client_cert_util.h
+++ b/chromeos/network/client_cert_util.h
@@ -10,7 +10,7 @@
 
 #include "base/component_export.h"
 #include "base/memory/ref_counted.h"
-#include "chromeos/network/onc/onc_certificate_pattern.h"
+#include "chromeos/ash/components/network/onc/onc_certificate_pattern.h"
 #include "components/onc/onc_constants.h"
 
 namespace base {
diff --git a/chromeos/network/managed_network_configuration_handler.cc b/chromeos/network/managed_network_configuration_handler.cc
index 2f4faf1c..a1cb9b85 100644
--- a/chromeos/network/managed_network_configuration_handler.cc
+++ b/chromeos/network/managed_network_configuration_handler.cc
@@ -6,9 +6,9 @@
 
 #include "base/memory/ptr_util.h"
 #include "base/values.h"
+#include "chromeos/ash/components/network/onc/network_onc_utils.h"
 #include "chromeos/network/managed_network_configuration_handler_impl.h"
 #include "chromeos/network/network_ui_data.h"
-#include "chromeos/network/onc/network_onc_utils.h"
 #include "third_party/cros_system_api/dbus/service_constants.h"
 
 namespace chromeos {
diff --git a/chromeos/network/managed_network_configuration_handler_impl.cc b/chromeos/network/managed_network_configuration_handler_impl.cc
index 4aec9e04..70f1cd9 100644
--- a/chromeos/network/managed_network_configuration_handler_impl.cc
+++ b/chromeos/network/managed_network_configuration_handler_impl.cc
@@ -20,6 +20,8 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/values.h"
 #include "chromeos/ash/components/network/metrics/esim_policy_login_metrics_logger.h"
+#include "chromeos/ash/components/network/onc/onc_merger.h"
+#include "chromeos/ash/components/network/onc/onc_translator.h"
 #include "chromeos/components/onc/onc_signature.h"
 #include "chromeos/components/onc/onc_utils.h"
 #include "chromeos/components/onc/onc_validator.h"
@@ -37,8 +39,6 @@
 #include "chromeos/network/network_state_handler.h"
 #include "chromeos/network/network_ui_data.h"
 #include "chromeos/network/network_util.h"
-#include "chromeos/network/onc/onc_merger.h"
-#include "chromeos/network/onc/onc_translator.h"
 #include "chromeos/network/policy_util.h"
 #include "chromeos/network/prohibited_technologies_handler.h"
 #include "chromeos/network/proxy/ui_proxy_config_service.h"
diff --git a/chromeos/network/network_connection_handler_impl_unittest.cc b/chromeos/network/network_connection_handler_impl_unittest.cc
index d85ccb1a..9cade526 100644
--- a/chromeos/network/network_connection_handler_impl_unittest.cc
+++ b/chromeos/network/network_connection_handler_impl_unittest.cc
@@ -14,6 +14,7 @@
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/test/task_environment.h"
+#include "chromeos/ash/components/network/onc/network_onc_utils.h"
 #include "chromeos/network/cellular_connection_handler.h"
 #include "chromeos/network/cellular_inhibitor.h"
 #include "chromeos/network/cellular_utils.h"
@@ -26,7 +27,6 @@
 #include "chromeos/network/network_profile_handler.h"
 #include "chromeos/network/network_state_handler.h"
 #include "chromeos/network/network_state_test_helper.h"
-#include "chromeos/network/onc/network_onc_utils.h"
 #include "chromeos/network/prohibited_technologies_handler.h"
 #include "chromeos/network/stub_cellular_networks_provider.h"
 #include "chromeos/network/system_token_cert_db_storage.h"
diff --git a/chromeos/network/network_util.cc b/chromeos/network/network_util.cc
index ef6b2d54..760701ea 100644
--- a/chromeos/network/network_util.cc
+++ b/chromeos/network/network_util.cc
@@ -13,6 +13,8 @@
 #include "base/strings/string_tokenizer.h"
 #include "base/strings/string_util.h"
 #include "base/values.h"
+#include "chromeos/ash/components/network/onc/onc_translation_tables.h"
+#include "chromeos/ash/components/network/onc/onc_translator.h"
 #include "chromeos/components/onc/onc_signature.h"
 #include "chromeos/login/login_state/login_state.h"
 #include "chromeos/network/device_state.h"
@@ -20,8 +22,6 @@
 #include "chromeos/network/network_state.h"
 #include "chromeos/network/network_state_handler.h"
 #include "chromeos/network/network_ui_data.h"
-#include "chromeos/network/onc/onc_translation_tables.h"
-#include "chromeos/network/onc/onc_translator.h"
 #include "components/device_event_log/device_event_log.h"
 #include "third_party/cros_system_api/dbus/service_constants.h"
 
diff --git a/chromeos/network/policy_applicator.cc b/chromeos/network/policy_applicator.cc
index c6bf902..9673bdc6a 100644
--- a/chromeos/network/policy_applicator.cc
+++ b/chromeos/network/policy_applicator.cc
@@ -15,6 +15,7 @@
 #include "base/location.h"
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
+#include "chromeos/ash/components/network/onc/onc_translator.h"
 #include "chromeos/components/onc/onc_signature.h"
 #include "chromeos/dbus/shill/shill_profile_client.h"
 #include "chromeos/network/cellular_policy_handler.h"
@@ -22,7 +23,6 @@
 #include "chromeos/network/network_event_log.h"
 #include "chromeos/network/network_type_pattern.h"
 #include "chromeos/network/network_ui_data.h"
-#include "chromeos/network/onc/onc_translator.h"
 #include "chromeos/network/policy_util.h"
 #include "chromeos/network/shill_property_util.h"
 #include "components/onc/onc_constants.h"
diff --git a/chromeos/network/policy_util.cc b/chromeos/network/policy_util.cc
index 1b464c06..7a6cd05 100644
--- a/chromeos/network/policy_util.cc
+++ b/chromeos/network/policy_util.cc
@@ -10,15 +10,15 @@
 #include "base/logging.h"
 #include "base/notreached.h"
 #include "base/values.h"
+#include "chromeos/ash/components/network/onc/network_onc_utils.h"
+#include "chromeos/ash/components/network/onc/onc_merger.h"
+#include "chromeos/ash/components/network/onc/onc_normalizer.h"
+#include "chromeos/ash/components/network/onc/onc_translator.h"
 #include "chromeos/components/onc/onc_signature.h"
 #include "chromeos/components/onc/onc_utils.h"
 #include "chromeos/network/network_profile.h"
 #include "chromeos/network/network_type_pattern.h"
 #include "chromeos/network/network_ui_data.h"
-#include "chromeos/network/onc/network_onc_utils.h"
-#include "chromeos/network/onc/onc_merger.h"
-#include "chromeos/network/onc/onc_normalizer.h"
-#include "chromeos/network/onc/onc_translator.h"
 #include "chromeos/network/shill_property_util.h"
 #include "components/onc/onc_constants.h"
 #include "third_party/cros_system_api/dbus/service_constants.h"
diff --git a/chromeos/network/prohibited_technologies_handler_unittest.cc b/chromeos/network/prohibited_technologies_handler_unittest.cc
index ca9724e..42287c7 100644
--- a/chromeos/network/prohibited_technologies_handler_unittest.cc
+++ b/chromeos/network/prohibited_technologies_handler_unittest.cc
@@ -14,12 +14,12 @@
 #include "base/memory/ptr_util.h"
 #include "base/run_loop.h"
 #include "base/test/task_environment.h"
+#include "chromeos/ash/components/network/onc/network_onc_utils.h"
 #include "chromeos/network/managed_network_configuration_handler_impl.h"
 #include "chromeos/network/network_configuration_handler.h"
 #include "chromeos/network/network_profile_handler.h"
 #include "chromeos/network/network_state_handler.h"
 #include "chromeos/network/network_state_test_helper.h"
-#include "chromeos/network/onc/network_onc_utils.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/cros_system_api/dbus/service_constants.h"
 
diff --git a/chromeos/network/proxy/proxy_config_handler.cc b/chromeos/network/proxy/proxy_config_handler.cc
index ea32bf2..afb5e9d 100644
--- a/chromeos/network/proxy/proxy_config_handler.cc
+++ b/chromeos/network/proxy/proxy_config_handler.cc
@@ -11,13 +11,13 @@
 #include "base/json/json_writer.h"
 #include "base/logging.h"
 #include "base/values.h"
+#include "chromeos/ash/components/network/onc/network_onc_utils.h"
 #include "chromeos/dbus/shill/shill_service_client.h"
 #include "chromeos/network/network_handler_callbacks.h"
 #include "chromeos/network/network_profile.h"
 #include "chromeos/network/network_profile_handler.h"
 #include "chromeos/network/network_state.h"
 #include "chromeos/network/network_state_handler.h"
-#include "chromeos/network/onc/network_onc_utils.h"
 #include "components/onc/onc_pref_names.h"
 #include "components/pref_registry/pref_registry_syncable.h"
 #include "components/prefs/pref_registry_simple.h"
diff --git a/chromeos/network/proxy/proxy_config_service_impl.cc b/chromeos/network/proxy/proxy_config_service_impl.cc
index b9a708390..c487edf 100644
--- a/chromeos/network/proxy/proxy_config_service_impl.cc
+++ b/chromeos/network/proxy/proxy_config_service_impl.cc
@@ -12,11 +12,11 @@
 #include "base/location.h"
 #include "base/logging.h"
 #include "base/values.h"
+#include "chromeos/ash/components/network/onc/network_onc_utils.h"
 #include "chromeos/network/network_profile.h"
 #include "chromeos/network/network_profile_handler.h"
 #include "chromeos/network/network_state.h"
 #include "chromeos/network/network_state_handler.h"
-#include "chromeos/network/onc/network_onc_utils.h"
 #include "chromeos/network/proxy/proxy_config_handler.h"
 #include "components/onc/onc_pref_names.h"
 #include "components/prefs/pref_service.h"
diff --git a/chromeos/services/network_config/cros_network_config.cc b/chromeos/services/network_config/cros_network_config.cc
index d8f472f9..7ef4d84 100644
--- a/chromeos/services/network_config/cros_network_config.cc
+++ b/chromeos/services/network_config/cros_network_config.cc
@@ -16,6 +16,7 @@
 #include "base/strings/string_split.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
+#include "chromeos/ash/components/network/onc/onc_translation_tables.h"
 #include "chromeos/components/sync_wifi/network_eligibility_checker.h"
 #include "chromeos/dbus/hermes/hermes_euicc_client.h"
 #include "chromeos/dbus/hermes/hermes_manager_client.h"
@@ -35,7 +36,6 @@
 #include "chromeos/network/network_state_handler.h"
 #include "chromeos/network/network_type_pattern.h"
 #include "chromeos/network/network_util.h"
-#include "chromeos/network/onc/onc_translation_tables.h"
 #include "chromeos/network/prohibited_technologies_handler.h"
 #include "chromeos/network/proxy/ui_proxy_config_service.h"
 #include "chromeos/services/network_config/public/cpp/cros_network_config_util.h"
diff --git a/chromeos/services/network_config/public/mojom/cros_network_config.mojom b/chromeos/services/network_config/public/mojom/cros_network_config.mojom
index 5ab2e54..d3b1f2d 100644
--- a/chromeos/services/network_config/public/mojom/cros_network_config.mojom
+++ b/chromeos/services/network_config/public/mojom/cros_network_config.mojom
@@ -740,9 +740,9 @@
 // Properties for SetProperties and ConfigureNetwork. All of the top level
 // properties and many nested properties are optional so that sparse
 // configurations can be provided, e.g. just the |auto_connect| property can
-// be changed. The ONC validation code (chromeos/network/onc/onc_validator.cc)
-// ensures that configurations are valid before translating and passing them to
-// Shill.
+// be changed. The ONC validation code
+// (chromeos/ash/components/network/onc/onc_validator.cc) ensures that
+// configurations are valid before translating and passing them to Shill.
 
 // Wrapper to allow optional auto_connect configuration.
 struct AutoConnectConfig {
diff --git a/chromeos/tast_control.gni b/chromeos/tast_control.gni
index f9f3e6c..17b33ee 100644
--- a/chromeos/tast_control.gni
+++ b/chromeos/tast_control.gni
@@ -226,10 +226,6 @@
   # https://crbug.com/1327361
   "policy.FullscreenAllowed",
 
-  # https://crbug.com/1328925
-  "inputs.InputMethodManagement",
-  "inputs.InputMethodManagement.guest",
-
   # https://crbug.com/1328128
   "policy.PopupsForURLCheck.blocklist",
 ]
diff --git a/components/autofill/content/browser/content_autofill_driver_factory.cc b/components/autofill/content/browser/content_autofill_driver_factory.cc
index d46c26e..7992fb1 100644
--- a/components/autofill/content/browser/content_autofill_driver_factory.cc
+++ b/components/autofill/content/browser/content_autofill_driver_factory.cc
@@ -137,7 +137,7 @@
     // 3. `SomeOtherWebContentsObserver::RenderFrameDeleted(render_frame_host)`
     //    calls `DriverForFrame(render_frame_host)`.
     // 5. `render_frame_host->~RenderFrameHostImpl()` finishes.
-    if (render_frame_host->IsRenderFrameCreated()) {
+    if (render_frame_host->IsRenderFrameLive()) {
       driver = CreateDriver(render_frame_host);
       DCHECK_EQ(driver_map_.find(render_frame_host)->second.get(),
                 driver.get());
diff --git a/components/autofill/ios/browser/autofill_agent_unittests.mm b/components/autofill/ios/browser/autofill_agent_unittests.mm
index 29aba95b..049f7f0 100644
--- a/components/autofill/ios/browser/autofill_agent_unittests.mm
+++ b/components/autofill/ios/browser/autofill_agent_unittests.mm
@@ -179,7 +179,7 @@
       fillFormData:form
            inFrame:fake_web_state_.GetWebFramesManager()->GetMainWebFrame()];
   fake_web_state_.WasShown();
-  EXPECT_EQ("__gCrWeb.autofill.fillForm({\"fields\":{\"2\":{\"section\":\"\","
+  EXPECT_EQ(u"__gCrWeb.autofill.fillForm({\"fields\":{\"2\":{\"section\":\"\","
             "\"value\":\"number_value\"},"
             "\"3\":{\"section\":\"\",\"value\":\"name_value\"}},"
             "\"formName\":\"CC form\",\"formRendererID\":1}, 0);",
diff --git a/components/autofill_assistant/browser/autofill_assistant_tts_controller_unittest.cc b/components/autofill_assistant/browser/autofill_assistant_tts_controller_unittest.cc
index fe18483..0e4f322 100644
--- a/components/autofill_assistant/browser/autofill_assistant_tts_controller_unittest.cc
+++ b/components/autofill_assistant/browser/autofill_assistant_tts_controller_unittest.cc
@@ -44,6 +44,7 @@
   content::TtsEngineDelegate* GetTtsEngineDelegate() override {
     return nullptr;
   }
+  void RefreshVoices() override {}
   void SetTtsPlatform(content::TtsPlatform* tts_platform) override {}
   int QueueSize() override { return 0; }
   void StripSSML(
diff --git a/components/cast_streaming/browser/BUILD.gn b/components/cast_streaming/browser/BUILD.gn
index 07f344c61..a69837fa 100644
--- a/components/cast_streaming/browser/BUILD.gn
+++ b/components/cast_streaming/browser/BUILD.gn
@@ -72,6 +72,7 @@
   source_set("streaming_session") {
     deps = [
       ":core",
+      ":demuxer_stream_data_provider",
       ":receiver_session_public",
       ":renderer_controls",
       ":streaming_initialization_info",
@@ -176,6 +177,7 @@
 
 source_set("demuxer_stream_data_provider") {
   deps = [
+    ":demuxer_stream_client",
     "//base",
     "//components/cast_streaming/public/mojom",
     "//media",
@@ -199,7 +201,7 @@
 
 source_set("streaming_initialization_info") {
   public_deps = [
-    ":demuxer_stream_data_provider",
+    ":demuxer_stream_client",
     "//media",
     "//third_party/openscreen/src/cast/streaming:receiver",
   ]
@@ -210,8 +212,15 @@
   ]
 }
 
+source_set("demuxer_stream_client") {
+  visibility = [ ":*" ]
+  sources = [ "demuxer_stream_client.h" ]
+  public_deps = [ "//base" ]
+}
+
 source_set("renderer_controls") {
   public_deps = [
+    ":demuxer_stream_client",
     ":streaming_initialization_info",
     "//base",
     "//components/cast_streaming/public:remoting_utils",
@@ -254,6 +263,7 @@
   testonly = true
   deps = [
     ":browser",
+    ":demuxer_stream_client",
     ":demuxer_stream_data_provider",
     ":renderer_controls",
     "//base",
diff --git a/components/cast_streaming/browser/demuxer_stream_client.h b/components/cast_streaming/browser/demuxer_stream_client.h
new file mode 100644
index 0000000..c320bff
--- /dev/null
+++ b/components/cast_streaming/browser/demuxer_stream_client.h
@@ -0,0 +1,32 @@
+// Copyright 2022 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_CAST_STREAMING_BROWSER_DEMUXER_STREAM_CLIENT_H_
+#define COMPONENTS_CAST_STREAMING_BROWSER_DEMUXER_STREAM_CLIENT_H_
+
+#include "base/callback.h"
+
+namespace cast_streaming {
+
+// This class acts as a client to act upon state changes of the DemuxerStream
+// and DemuxerStreamDataProvider with which it is associated.
+class DemuxerStreamClient {
+ public:
+  virtual ~DemuxerStreamClient() = default;
+
+  // Enables the bitstream converter for the data provider associated with this
+  // demuxer stream.
+  using BitstreamConverterEnabledCB = base::OnceCallback<void(bool)>;
+  virtual void EnableBitstreamConverter(BitstreamConverterEnabledCB cb) = 0;
+
+  // Called when no buffers are available for reading.
+  virtual void OnNoBuffersAvailable() = 0;
+
+  // Called when a fatal error occurs. Only called once.
+  virtual void OnError() = 0;
+};
+
+}  // namespace cast_streaming
+
+#endif  // COMPONENTS_CAST_STREAMING_BROWSER_DEMUXER_STREAM_CLIENT_H_
diff --git a/components/cast_streaming/browser/demuxer_stream_data_provider.h b/components/cast_streaming/browser/demuxer_stream_data_provider.h
index 7d024cb..6cc294cd 100644
--- a/components/cast_streaming/browser/demuxer_stream_data_provider.h
+++ b/components/cast_streaming/browser/demuxer_stream_data_provider.h
@@ -6,7 +6,9 @@
 #define COMPONENTS_CAST_STREAMING_BROWSER_DEMUXER_STREAM_DATA_PROVIDER_H_
 
 #include "base/callback_forward.h"
+#include "base/memory/weak_ptr.h"
 #include "base/sequence_checker.h"
+#include "components/cast_streaming/browser/demuxer_stream_client.h"
 #include "components/cast_streaming/public/mojom/demuxer_connector.mojom.h"
 #include "mojo/public/cpp/bindings/message.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
@@ -90,23 +92,21 @@
     return config_;
   }
 
-  void SetOnNoBuffersAvailableCallback(
-      base::RepeatingClosure on_no_buffers_available) {
+  void SetClient(base::WeakPtr<DemuxerStreamClient> client) {
     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-    on_no_buffers_available_ = std::move(on_no_buffers_available);
-  }
-
-  void SetOnErrorCallback(base::OnceClosure on_error) {
-    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-    on_error_ = std::move(on_error);
+    client_ = std::move(client);
   }
 
  private:
   using GetBufferCallback = typename TMojoReceiverType::GetBufferCallback;
+  using EnableBitstreamConverterCallback =
+      typename TMojoReceiverType::EnableBitstreamConverterCallback;
 
   void OnFatalError() {
     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-    std::move(on_error_).Run();
+    if (client_) {
+      client_->OnError();
+    }
     std::move(on_mojo_disconnect_).Run();
   }
 
@@ -123,7 +123,19 @@
     }
 
     current_callback_ = std::move(callback);
-    request_buffer_.Run(on_no_buffers_available_);
+    request_buffer_.Run(
+        base::BindOnce(&DemuxerStreamClient::OnNoBuffersAvailable, client_));
+  }
+
+  void EnableBitstreamConverter(
+      EnableBitstreamConverterCallback callback) override {
+    if (client_) {
+      client_->EnableBitstreamConverter(std::move(callback));
+    } else {
+      std::move(callback).Run(false);
+      LOG(WARNING)
+          << "EnableBitstreamConverter() called when no client was available";
+    }
   }
 
   // The most recently set config.
@@ -139,12 +151,9 @@
   // Callback to request a new buffer be read from the receiver.
   RequestBufferCB request_buffer_;
 
-  // Callback called when no buffers are available for reading.
-  base::RepeatingClosure on_no_buffers_available_;
-
-  // Callback called on an unrecoverable error, prior to shutdown of the
-  // component.
-  base::OnceClosure on_error_;
+  // Client to use when the associated DemuxerStream requires an action be
+  // performed.
+  base::WeakPtr<DemuxerStreamClient> client_;
 
   // Callback called upon a mojo disconnection.
   base::OnceClosure on_mojo_disconnect_;
diff --git a/components/cast_streaming/browser/demuxer_stream_data_provider_unittest.cc b/components/cast_streaming/browser/demuxer_stream_data_provider_unittest.cc
index 15fc978..992a1c7 100644
--- a/components/cast_streaming/browser/demuxer_stream_data_provider_unittest.cc
+++ b/components/cast_streaming/browser/demuxer_stream_data_provider_unittest.cc
@@ -6,8 +6,10 @@
 
 #include <utility>
 
+#include "base/memory/weak_ptr.h"
 #include "base/test/bind.h"
 #include "base/test/task_environment.h"
+#include "components/cast_streaming/browser/demuxer_stream_client.h"
 #include "media/base/audio_codecs.h"
 #include "media/base/audio_decoder_config.h"
 #include "media/base/channel_layout.h"
@@ -47,12 +49,7 @@
             base::Unretained(&callbacks_)),
         second_config_);
 
-    data_provider_->SetOnNoBuffersAvailableCallback(base::BindRepeating(
-        &DemuxerStreamDataProviderTest::Callbacks::OnNoBuffers,
-        base::Unretained(&callbacks_)));
-    data_provider_->SetOnErrorCallback(
-        base::BindRepeating(&DemuxerStreamDataProviderTest::Callbacks::OnError,
-                            base::Unretained(&callbacks_)));
+    data_provider_->SetClient(client_.weak_factory_.GetWeakPtr());
 
     std::vector<uint8_t> data = {1, 2, 3};
     first_buffer_ = media::DecoderBuffer::CopyFrom(data.data(), 3);
@@ -77,8 +74,6 @@
  protected:
   class Callbacks {
    public:
-    MOCK_METHOD0(OnNoBuffers, void());
-    MOCK_METHOD0(OnError, void());
     MOCK_METHOD1(RequestBuffer, void(base::OnceClosure));
     MOCK_METHOD0(OnMojoDisconnect, void());
 
@@ -101,6 +96,17 @@
     }
   };
 
+  class MockDemuxerStreamClient : public DemuxerStreamClient {
+   public:
+    ~MockDemuxerStreamClient() override = default;
+
+    MOCK_METHOD1(EnableBitstreamConverter, void(BitstreamConverterEnabledCB));
+    MOCK_METHOD0(OnNoBuffersAvailable, void());
+    MOCK_METHOD0(OnError, void());
+
+    base::WeakPtrFactory<MockDemuxerStreamClient> weak_factory_{this};
+  };
+
   using MojoPipePair = std::pair<mojo::ScopedDataPipeProducerHandle,
                                  mojo::ScopedDataPipeConsumerHandle>;
   MojoPipePair GetMojoPipePair() {
@@ -112,6 +118,7 @@
   }
 
   testing::StrictMock<Callbacks> callbacks_;
+  testing::StrictMock<MockDemuxerStreamClient> client_;
 
   base::test::SingleThreadTaskEnvironment task_environment_{
       base::test::TaskEnvironment::TimeSource::MOCK_TIME};
@@ -178,26 +185,24 @@
   task_environment_.RunUntilIdle();
 }
 
-TEST_F(DemuxerStreamDataProviderTest, NoBuffersCallsWithCallback) {
+TEST_F(DemuxerStreamDataProviderTest, NoBuffersCallback) {
   EXPECT_CALL(callbacks_, RequestBuffer(testing::_))
       .WillOnce([](base::OnceClosure no_buffers_cb) {
         std::move(no_buffers_cb).Run();
       });
-  EXPECT_CALL(callbacks_, OnNoBuffers());
+  EXPECT_CALL(client_, OnNoBuffersAvailable());
   remote_->GetBuffer(base::BindOnce(
       &DemuxerStreamDataProviderTest::Callbacks::OnGetBufferDone,
       base::Unretained(&callbacks_), first_config_, first_buffer_));
   task_environment_.RunUntilIdle();
 }
 
-TEST_F(DemuxerStreamDataProviderTest, NoBuffersCallsNoCallback) {
-  data_provider_->SetOnNoBuffersAvailableCallback(base::RepeatingClosure());
-  EXPECT_CALL(callbacks_, RequestBuffer(testing::_))
+TEST_F(DemuxerStreamDataProviderTest, EnableBitstreamConverter) {
+  EXPECT_CALL(client_, EnableBitstreamConverter(testing::_))
       .WillOnce(
-          [](base::OnceClosure no_buffers_cb) { ASSERT_FALSE(no_buffers_cb); });
-  remote_->GetBuffer(base::BindOnce(
-      &DemuxerStreamDataProviderTest::Callbacks::OnGetBufferDone,
-      base::Unretained(&callbacks_), first_config_, first_buffer_));
+          [](base::OnceCallback<void(bool)> cb) { std::move(cb).Run(true); });
+  ;
+  remote_->EnableBitstreamConverter(base::OnceCallback<void(bool)>());
   task_environment_.RunUntilIdle();
 }
 
diff --git a/components/cast_streaming/browser/playback_command_dispatcher.cc b/components/cast_streaming/browser/playback_command_dispatcher.cc
index 0e11e163..1a53541 100644
--- a/components/cast_streaming/browser/playback_command_dispatcher.cc
+++ b/components/cast_streaming/browser/playback_command_dispatcher.cc
@@ -90,39 +90,15 @@
   absl::optional<StreamingInitializationInfo::AudioStreamInfo>
       audio_stream_info;
   if (receivers.audio_receiver) {
-    auto no_buffers_cb = base::BindPostTask(
-        task_runner_,
-        base::BindRepeating(
-            &remoting::RpcDemuxerStreamHandler::RequestMoreAudioBuffers,
-            demuxer_stream_handler_->GetWeakPtr()),
-        FROM_HERE);
-    auto error_cb = base::BindPostTask(
-        task_runner_,
-        base::BindRepeating(&remoting::RpcDemuxerStreamHandler::OnAudioError,
-                            demuxer_stream_handler_->GetWeakPtr()),
-        FROM_HERE);
     audio_stream_info.emplace(media::AudioDecoderConfig(),
-                              receivers.audio_receiver,
-                              std::move(no_buffers_cb), std::move(error_cb));
+                              receivers.audio_receiver);
   }
 
   absl::optional<StreamingInitializationInfo::VideoStreamInfo>
       video_stream_info;
   if (receivers.video_receiver) {
-    auto no_buffers_cb = base::BindPostTask(
-        task_runner_,
-        base::BindRepeating(
-            &remoting::RpcDemuxerStreamHandler::RequestMoreVideoBuffers,
-            demuxer_stream_handler_->GetWeakPtr()),
-        FROM_HERE);
-    auto error_cb = base::BindPostTask(
-        task_runner_,
-        base::BindRepeating(&remoting::RpcDemuxerStreamHandler::OnVideoError,
-                            demuxer_stream_handler_->GetWeakPtr()),
-        FROM_HERE);
     video_stream_info.emplace(media::VideoDecoderConfig(),
-                              receivers.video_receiver,
-                              std::move(no_buffers_cb), std::move(error_cb));
+                              receivers.video_receiver);
   }
 
   streaming_init_info_.emplace(receiver_session_, std::move(audio_stream_info),
@@ -276,10 +252,18 @@
 
   DCHECK(demuxer_stream_handler_);
   if (streaming_init_info_->audio_stream_info) {
-    demuxer_stream_handler_->RequestMoreAudioBuffers();
+    auto client = demuxer_stream_handler_->GetAudioClient();
+    DCHECK(client);
+    client->OnNoBuffersAvailable();
+    streaming_init_info_->audio_stream_info->demuxer_stream_client =
+        std::move(client);
   }
   if (streaming_init_info_->video_stream_info) {
-    demuxer_stream_handler_->RequestMoreVideoBuffers();
+    auto client = demuxer_stream_handler_->GetVideoClient();
+    DCHECK(client);
+    client->OnNoBuffersAvailable();
+    streaming_init_info_->video_stream_info->demuxer_stream_client =
+        std::move(client);
   }
 
   // |streaming_init_info_| is intentionally copied here.
diff --git a/components/cast_streaming/browser/receiver_session_impl.cc b/components/cast_streaming/browser/receiver_session_impl.cc
index 05a40204..a09ddfc 100644
--- a/components/cast_streaming/browser/receiver_session_impl.cc
+++ b/components/cast_streaming/browser/receiver_session_impl.cc
@@ -95,11 +95,8 @@
             base::BindOnce(&ReceiverSessionImpl::OnMojoDisconnect,
                            weak_factory_.GetWeakPtr()),
             std::move(initialization_info.audio_stream_info->config));
-    audio_demuxer_stream_data_provider_->SetOnNoBuffersAvailableCallback(
-        std::move(
-            initialization_info.audio_stream_info->on_no_buffers_callback));
-    audio_demuxer_stream_data_provider_->SetOnErrorCallback(
-        std::move(initialization_info.audio_stream_info->on_error_callback));
+    audio_demuxer_stream_data_provider_->SetClient(std::move(
+        initialization_info.audio_stream_info->demuxer_stream_client));
     audio_info = mojom::AudioStreamInitializationInfo::New(
         std::move(audio_receiver),
         mojom::AudioStreamInfo::New(
@@ -117,11 +114,8 @@
             base::BindOnce(&ReceiverSessionImpl::OnMojoDisconnect,
                            weak_factory_.GetWeakPtr()),
             std::move(initialization_info.video_stream_info->config));
-    video_demuxer_stream_data_provider_->SetOnNoBuffersAvailableCallback(
-        std::move(
-            initialization_info.video_stream_info->on_no_buffers_callback));
-    video_demuxer_stream_data_provider_->SetOnErrorCallback(
-        std::move(initialization_info.video_stream_info->on_error_callback));
+    video_demuxer_stream_data_provider_->SetClient(std::move(
+        initialization_info.video_stream_info->demuxer_stream_client));
     video_info = mojom::VideoStreamInitializationInfo::New(
         std::move(video_receiver),
         mojom::VideoStreamInfo::New(
@@ -163,11 +157,8 @@
   if (audio_pipe_consumer) {
     if (!audio_demuxer_stream_data_provider_->config().Matches(
             initialization_info.audio_stream_info->config)) {
-      audio_demuxer_stream_data_provider_->SetOnNoBuffersAvailableCallback(
-          std::move(
-              initialization_info.audio_stream_info->on_no_buffers_callback));
-      audio_demuxer_stream_data_provider_->SetOnErrorCallback(
-          std::move(initialization_info.audio_stream_info->on_error_callback));
+      audio_demuxer_stream_data_provider_->SetClient(std::move(
+          initialization_info.audio_stream_info->demuxer_stream_client));
       audio_demuxer_stream_data_provider_->OnNewStreamInfo(
           std::move(initialization_info.audio_stream_info->config),
           std::move(*audio_pipe_consumer));
@@ -180,11 +171,8 @@
   if (video_pipe_consumer) {
     if (!video_demuxer_stream_data_provider_->config().Matches(
             initialization_info.video_stream_info->config)) {
-      video_demuxer_stream_data_provider_->SetOnNoBuffersAvailableCallback(
-          std::move(
-              initialization_info.video_stream_info->on_no_buffers_callback));
-      video_demuxer_stream_data_provider_->SetOnErrorCallback(
-          std::move(initialization_info.video_stream_info->on_error_callback));
+      video_demuxer_stream_data_provider_->SetClient(std::move(
+          initialization_info.video_stream_info->demuxer_stream_client));
       video_demuxer_stream_data_provider_->OnNewStreamInfo(
           std::move(initialization_info.video_stream_info->config),
           std::move(*video_pipe_consumer));
diff --git a/components/cast_streaming/browser/rpc_demuxer_stream_handler.cc b/components/cast_streaming/browser/rpc_demuxer_stream_handler.cc
index 70eb6ca..4aba944 100644
--- a/components/cast_streaming/browser/rpc_demuxer_stream_handler.cc
+++ b/components/cast_streaming/browser/rpc_demuxer_stream_handler.cc
@@ -20,13 +20,12 @@
 RpcDemuxerStreamHandler::RpcDemuxerStreamHandler(
     Client* client,
     HandleFactory handle_factory,
-    RpcProcessMessageCB message_processor)
+    RpcProcessMessageCB process_message_cb)
     : client_(client),
       handle_factory_(std::move(handle_factory)),
-      message_processor_(std::move(message_processor)),
-      weak_factory_(this) {
+      process_message_cb_(std::move(process_message_cb)) {
   DCHECK(handle_factory_);
-  DCHECK(message_processor_);
+  DCHECK(process_message_cb_);
 }
 
 RpcDemuxerStreamHandler::~RpcDemuxerStreamHandler() = default;
@@ -38,83 +37,27 @@
   // initialize the DemuxerStreams.
   if (audio_stream_handle != openscreen::cast::RpcMessenger::kInvalidHandle) {
     audio_message_processor_ = std::make_unique<MessageProcessor>(
-        client_, handle_factory_.Run(), audio_stream_handle,
-        MessageProcessor::Type::kAudio);
+        client_, process_message_cb_, handle_factory_.Run(),
+        audio_stream_handle, MessageProcessor::Type::kAudio);
     std::unique_ptr<openscreen::cast::RpcMessage> message =
         remoting::CreateMessageForDemuxerStreamInitialize(
             audio_message_processor_->local_handle());
-    message_processor_.Run(audio_message_processor_->remote_handle(),
-                           std::move(message));
+    process_message_cb_.Run(audio_message_processor_->remote_handle(),
+                            std::move(message));
   }
 
   if (video_stream_handle != openscreen::cast::RpcMessenger::kInvalidHandle) {
     video_message_processor_ = std::make_unique<MessageProcessor>(
-        client_, handle_factory_.Run(), video_stream_handle,
-        MessageProcessor::Type::kVideo);
+        client_, process_message_cb_, handle_factory_.Run(),
+        video_stream_handle, MessageProcessor::Type::kVideo);
     std::unique_ptr<openscreen::cast::RpcMessage> message =
         remoting::CreateMessageForDemuxerStreamInitialize(
             video_message_processor_->local_handle());
-    message_processor_.Run(video_message_processor_->remote_handle(),
-                           std::move(message));
+    process_message_cb_.Run(video_message_processor_->remote_handle(),
+                            std::move(message));
   }
 }
 
-void RpcDemuxerStreamHandler::RequestMoreAudioBuffers() {
-  if (!audio_message_processor_) {
-    return;
-  }
-
-  RequestMoreBuffers(audio_message_processor_.get());
-}
-
-void RpcDemuxerStreamHandler::RequestMoreVideoBuffers() {
-  if (!video_message_processor_) {
-    return;
-  }
-
-  RequestMoreBuffers(video_message_processor_.get());
-}
-
-void RpcDemuxerStreamHandler::RequestMoreBuffers(
-    MessageProcessor* message_processor) {
-  if (message_processor->is_read_until_call_pending()) {
-    return;
-  }
-
-  message_processor->set_read_until_call_pending();
-  auto message = CreateMessageForDemuxerStreamReadUntil(
-      message_processor->local_handle(),
-      message_processor->total_frames_received() + kNumFramesInEachReadUntil);
-  message_processor_.Run(message_processor->remote_handle(),
-                         std::move(message));
-}
-
-void RpcDemuxerStreamHandler::OnAudioError() {
-  if (!audio_message_processor_) {
-    return;
-  }
-
-  OnError(audio_message_processor_.get());
-}
-
-void RpcDemuxerStreamHandler::OnVideoError() {
-  if (!video_message_processor_) {
-    return;
-  }
-
-  OnError(video_message_processor_.get());
-}
-
-void RpcDemuxerStreamHandler::OnError(MessageProcessor* message_processor) {
-  auto message = CreateMessageForDemuxerStreamError();
-  message_processor_.Run(message_processor->remote_handle(),
-                         std::move(message));
-}
-
-base::WeakPtr<RpcDemuxerStreamHandler> RpcDemuxerStreamHandler::GetWeakPtr() {
-  return weak_factory_.GetWeakPtr();
-}
-
 void RpcDemuxerStreamHandler::OnRpcInitializeCallback(
     openscreen::cast::RpcMessenger::Handle handle,
     absl::optional<media::AudioDecoderConfig> audio_config,
@@ -152,22 +95,58 @@
   }
 }
 
+void RpcDemuxerStreamHandler::OnRpcBitstreamConverterEnabled(
+    openscreen::cast::RpcMessenger::Handle handle,
+    bool success) {
+  if (audio_message_processor_ &&
+      handle == audio_message_processor_->local_handle()) {
+    audio_message_processor_->OnBitstreamConverterEnabled(success);
+  } else if (video_message_processor_ &&
+             handle == video_message_processor_->local_handle()) {
+    video_message_processor_->OnBitstreamConverterEnabled(success);
+  } else {
+    LOG(WARNING)
+        << "OnRpcBitstreamConverterEnabled received for invalid handle";
+  }
+}
+
+base::WeakPtr<DemuxerStreamClient> RpcDemuxerStreamHandler::GetAudioClient() {
+  if (!audio_message_processor_) {
+    return nullptr;
+  }
+
+  return audio_message_processor_->GetWeakPtr();
+}
+
+base::WeakPtr<DemuxerStreamClient> RpcDemuxerStreamHandler::GetVideoClient() {
+  if (!video_message_processor_) {
+    return nullptr;
+  }
+
+  return video_message_processor_->GetWeakPtr();
+}
+
 RpcDemuxerStreamHandler::Client::~Client() = default;
 
 RpcDemuxerStreamHandler::MessageProcessor::MessageProcessor(
     Client* client,
+    RpcProcessMessageCB process_message_cb,
     openscreen::cast::RpcMessenger::Handle local_handle,
     openscreen::cast::RpcMessenger::Handle remote_handle,
     Type type)
     : client_(client),
+      process_message_cb_(std::move(process_message_cb)),
       local_handle_(local_handle),
       remote_handle_(remote_handle),
-      type_(type) {
+      type_(type),
+      weak_factory_(this) {
   DCHECK(client_);
   DCHECK_NE(local_handle_, openscreen::cast::RpcMessenger::kInvalidHandle);
   DCHECK_NE(remote_handle_, openscreen::cast::RpcMessenger::kInvalidHandle);
 }
 
+RpcDemuxerStreamHandler::MessageProcessor::~MessageProcessor() = default;
+
 bool RpcDemuxerStreamHandler::MessageProcessor::OnRpcInitializeCallback(
     absl::optional<media::AudioDecoderConfig> audio_config,
     absl::optional<media::VideoDecoderConfig> video_config) {
@@ -194,6 +173,7 @@
     uint32_t total_frames_received) {
   if (!OnRpcInitializeCallback(std::move(audio_config),
                                std::move(video_config))) {
+    LOG(WARNING) << "Failed to process OnRpcReadUntilCallback.";
     return false;
   }
 
@@ -202,4 +182,43 @@
   return true;
 }
 
+void RpcDemuxerStreamHandler::MessageProcessor::OnBitstreamConverterEnabled(
+    bool success) {
+  if (!bitstream_converter_enabled_cb_) {
+    return;
+  }
+
+  std::move(bitstream_converter_enabled_cb_).Run(success);
+}
+
+base::WeakPtr<RpcDemuxerStreamHandler::MessageProcessor>
+RpcDemuxerStreamHandler::MessageProcessor::GetWeakPtr() {
+  return weak_factory_.GetWeakPtr();
+}
+
+void RpcDemuxerStreamHandler::MessageProcessor::EnableBitstreamConverter(
+    BitstreamConverterEnabledCB cb) {
+  DCHECK(!bitstream_converter_enabled_cb_);
+  bitstream_converter_enabled_cb_ = std::move(cb);
+
+  auto message = CreateMessageForDemuxerStreamEnableBitstreamConverter();
+  process_message_cb_.Run(remote_handle(), std::move(message));
+}
+
+void RpcDemuxerStreamHandler::MessageProcessor::OnNoBuffersAvailable() {
+  if (is_read_until_call_pending()) {
+    return;
+  }
+
+  set_read_until_call_pending();
+  auto message = CreateMessageForDemuxerStreamReadUntil(
+      local_handle(), total_frames_received() + kNumFramesInEachReadUntil);
+  process_message_cb_.Run(remote_handle(), std::move(message));
+}
+
+void RpcDemuxerStreamHandler::MessageProcessor::OnError() {
+  auto message = CreateMessageForDemuxerStreamError();
+  process_message_cb_.Run(remote_handle(), std::move(message));
+}
+
 }  // namespace cast_streaming::remoting
diff --git a/components/cast_streaming/browser/rpc_demuxer_stream_handler.h b/components/cast_streaming/browser/rpc_demuxer_stream_handler.h
index 1a5b8a5..c1b2f1d 100644
--- a/components/cast_streaming/browser/rpc_demuxer_stream_handler.h
+++ b/components/cast_streaming/browser/rpc_demuxer_stream_handler.h
@@ -9,6 +9,7 @@
 
 #include "base/callback.h"
 #include "base/memory/weak_ptr.h"
+#include "components/cast_streaming/browser/demuxer_stream_client.h"
 #include "components/cast_streaming/public/rpc_call_message_handler.h"
 #include "media/base/audio_decoder_config.h"
 #include "media/base/video_decoder_config.h"
@@ -44,7 +45,7 @@
       std::unique_ptr<openscreen::cast::RpcMessage>)>;
   RpcDemuxerStreamHandler(Client* client,
                           HandleFactory handle_factory,
-                          RpcProcessMessageCB message_processor);
+                          RpcProcessMessageCB process_message_cb);
 
   ~RpcDemuxerStreamHandler() override;
 
@@ -55,19 +56,17 @@
       openscreen::cast::RpcMessenger::Handle audio_stream_handle,
       openscreen::cast::RpcMessenger::Handle video_stream_handle);
 
-  // To be called when no further buffers are available for reading to request
-  // more be sent.
-  void RequestMoreAudioBuffers();
-  void RequestMoreVideoBuffers();
+  // To be called when the RPC_DS_ENABLEBITSTREAMCONVERTER_CALLBACK message is
+  // received.
+  void OnRpcBitstreamConverterEnabled(
+      openscreen::cast::RpcMessenger::Handle handle,
+      bool success);
 
-  // Called when a DemuxerStream error occurs to inform the sender device.
-  void OnAudioError();
-  void OnVideoError();
-
-  base::WeakPtr<RpcDemuxerStreamHandler> GetWeakPtr();
+  base::WeakPtr<DemuxerStreamClient> GetAudioClient();
+  base::WeakPtr<DemuxerStreamClient> GetVideoClient();
 
  private:
-  class MessageProcessor {
+  class MessageProcessor : public DemuxerStreamClient {
    public:
     enum class Type { kUnknown = 0, kAudio, kVideo };
 
@@ -79,9 +78,11 @@
     // OnRpcAcquiredDemuxer() call and used as the handle for sending messages
     // back to the sender.
     MessageProcessor(Client* client,
+                     RpcProcessMessageCB process_message_cb,
                      openscreen::cast::RpcMessenger::Handle local_handle,
                      openscreen::cast::RpcMessenger::Handle remote_handle,
                      Type type);
+    ~MessageProcessor() override;
 
     bool OnRpcInitializeCallback(
         absl::optional<media::AudioDecoderConfig> audio_config,
@@ -90,6 +91,9 @@
         absl::optional<media::AudioDecoderConfig> audio_config,
         absl::optional<media::VideoDecoderConfig> video_config,
         uint32_t total_frames_received);
+    void OnBitstreamConverterEnabled(bool success);
+
+    base::WeakPtr<MessageProcessor> GetWeakPtr();
 
     uint32_t total_frames_received() const { return total_frames_received_; }
 
@@ -106,7 +110,12 @@
     void set_read_until_call_pending() { is_read_until_call_pending_ = true; }
 
    private:
+    void EnableBitstreamConverter(BitstreamConverterEnabledCB cb) override;
+    void OnNoBuffersAvailable() override;
+    void OnError() override;
+
     Client* client_;
+    RpcProcessMessageCB process_message_cb_;
     openscreen::cast::RpcMessenger::Handle local_handle_;
     openscreen::cast::RpcMessenger::Handle remote_handle_;
     Type type_ = Type::kUnknown;
@@ -114,6 +123,11 @@
     uint32_t total_frames_received_ = 0;
 
     bool is_read_until_call_pending_ = false;
+
+    // Most recent callback for EnableBitstreamConverter().
+    BitstreamConverterEnabledCB bitstream_converter_enabled_cb_;
+
+    base::WeakPtrFactory<MessageProcessor> weak_factory_;
   };
 
   // Helpers for the above methods of the same name.
@@ -133,12 +147,10 @@
 
   Client* const client_;
   HandleFactory handle_factory_;
-  RpcProcessMessageCB message_processor_;
+  RpcProcessMessageCB process_message_cb_;
 
   std::unique_ptr<MessageProcessor> audio_message_processor_;
   std::unique_ptr<MessageProcessor> video_message_processor_;
-
-  base::WeakPtrFactory<RpcDemuxerStreamHandler> weak_factory_;
 };
 
 }  // namespace cast_streaming::remoting
diff --git a/components/cast_streaming/browser/rpc_demuxer_stream_handler_unittests.cc b/components/cast_streaming/browser/rpc_demuxer_stream_handler_unittests.cc
index 8305553..3e0e57c 100644
--- a/components/cast_streaming/browser/rpc_demuxer_stream_handler_unittests.cc
+++ b/components/cast_streaming/browser/rpc_demuxer_stream_handler_unittests.cc
@@ -62,6 +62,16 @@
   EXPECT_EQ(handle, remote_handle);
 }
 
+ACTION_P(CheckEnableBistreamConverterCall, remote_handle) {
+  const openscreen::cast::RpcMessenger::Handle handle = arg0;
+  const std::unique_ptr<openscreen::cast::RpcMessage>& rpc = arg1;
+
+  ASSERT_TRUE(rpc);
+  EXPECT_EQ(rpc->proc(),
+            openscreen::cast::RpcMessage::RPC_DS_ENABLEBITSTREAMCONVERTER);
+  EXPECT_EQ(handle, remote_handle);
+}
+
 }  // namespace
 
 class RpcDemuxerStreamHandlerTest : public testing::Test {
@@ -119,6 +129,8 @@
 
   MOCK_METHOD0(GetHandle, openscreen::cast::RpcMessenger::Handle());
 
+  MOCK_METHOD1(OnBitstreamConverterEnabled, void(bool));
+
   void OnRpcInitializeCallback(
       openscreen::cast::RpcMessenger::Handle handle,
       absl::optional<media::AudioDecoderConfig> audio_config,
@@ -139,6 +151,46 @@
                                  total_frames_received);
   }
 
+  void RequestMoreAudioBuffers() {
+    auto client = stream_handler_.GetAudioClient();
+    ASSERT_TRUE(!!client);
+    client->OnNoBuffersAvailable();
+  }
+
+  void RequestMoreVideoBuffers() {
+    auto client = stream_handler_.GetVideoClient();
+    ASSERT_TRUE(!!client);
+    client->OnNoBuffersAvailable();
+  }
+
+  void OnAudioError() {
+    auto client = stream_handler_.GetAudioClient();
+    ASSERT_TRUE(!!client);
+    client->OnError();
+  }
+
+  void OnVideoError() {
+    auto client = stream_handler_.GetVideoClient();
+    ASSERT_TRUE(!!client);
+    client->OnError();
+  }
+
+  void EnableAudioBitstreamConverter() {
+    auto client = stream_handler_.GetAudioClient();
+    ASSERT_TRUE(!!client);
+    client->EnableBitstreamConverter(base::BindOnce(
+        &RpcDemuxerStreamHandlerTest::OnBitstreamConverterEnabled,
+        base::Unretained(this)));
+  }
+
+  void EnableVideoBitstreamConverter() {
+    auto client = stream_handler_.GetVideoClient();
+    ASSERT_TRUE(!!client);
+    client->EnableBitstreamConverter(base::BindOnce(
+        &RpcDemuxerStreamHandlerTest::OnBitstreamConverterEnabled,
+        base::Unretained(this)));
+  }
+
   openscreen::cast::RpcMessenger::Handle audio_remote_handle_ = 123;
   openscreen::cast::RpcMessenger::Handle video_remote_handle_ = 456;
 
@@ -219,7 +271,7 @@
   EXPECT_CALL(*this, SendMessage(_, _))
       .WillOnce(
           CheckReadUntilCall(audio_remote_handle_, audio_local_handle_, 1));
-  stream_handler_.RequestMoreAudioBuffers();
+  RequestMoreAudioBuffers();
 
   EXPECT_CALL(client_, OnNewAudioConfig(_))
       .WillOnce([this](media::AudioDecoderConfig config) {
@@ -230,7 +282,7 @@
   EXPECT_CALL(*this, SendMessage(_, _))
       .WillOnce(
           CheckReadUntilCall(audio_remote_handle_, audio_local_handle_, 17));
-  stream_handler_.RequestMoreAudioBuffers();
+  RequestMoreAudioBuffers();
 }
 
 TEST_F(RpcDemuxerStreamHandlerTest, RequestMoreVideoBuffers) {
@@ -243,7 +295,7 @@
   EXPECT_CALL(*this, SendMessage(_, _))
       .WillOnce(
           CheckReadUntilCall(video_remote_handle_, video_local_handle_, 12));
-  stream_handler_.RequestMoreVideoBuffers();
+  RequestMoreVideoBuffers();
 
   EXPECT_CALL(client_, OnNewVideoConfig(_))
       .WillOnce([this](media::VideoDecoderConfig config) {
@@ -254,19 +306,37 @@
   EXPECT_CALL(*this, SendMessage(_, _))
       .WillOnce(
           CheckReadUntilCall(video_remote_handle_, video_local_handle_, 42));
-  stream_handler_.RequestMoreVideoBuffers();
+  RequestMoreVideoBuffers();
 }
 
 TEST_F(RpcDemuxerStreamHandlerTest, OnAudioError) {
   EXPECT_CALL(*this, SendMessage(_, _))
       .WillOnce(CheckOnErrorCall(audio_remote_handle_));
-  stream_handler_.OnAudioError();
+  OnAudioError();
 }
 
 TEST_F(RpcDemuxerStreamHandlerTest, OnVideoError) {
   EXPECT_CALL(*this, SendMessage(_, _))
       .WillOnce(CheckOnErrorCall(video_remote_handle_));
-  stream_handler_.OnVideoError();
+  OnVideoError();
+}
+
+TEST_F(RpcDemuxerStreamHandlerTest, OnEnableAudioBitstreamConverter) {
+  EXPECT_CALL(*this, SendMessage(_, _))
+      .WillOnce(CheckEnableBistreamConverterCall(audio_remote_handle_));
+  EnableAudioBitstreamConverter();
+
+  EXPECT_CALL(*this, OnBitstreamConverterEnabled(true));
+  stream_handler_.OnRpcBitstreamConverterEnabled(audio_local_handle_, true);
+}
+
+TEST_F(RpcDemuxerStreamHandlerTest, OnEnableVideoBitstreamConverter) {
+  EXPECT_CALL(*this, SendMessage(_, _))
+      .WillOnce(CheckEnableBistreamConverterCall(video_remote_handle_));
+  EnableVideoBitstreamConverter();
+
+  EXPECT_CALL(*this, OnBitstreamConverterEnabled(false));
+  stream_handler_.OnRpcBitstreamConverterEnabled(video_local_handle_, false);
 }
 
 }  // namespace cast_streaming::remoting
diff --git a/components/cast_streaming/browser/streaming_initialization_info.cc b/components/cast_streaming/browser/streaming_initialization_info.cc
index f6e0ac0a..711473b 100644
--- a/components/cast_streaming/browser/streaming_initialization_info.cc
+++ b/components/cast_streaming/browser/streaming_initialization_info.cc
@@ -28,32 +28,18 @@
 StreamingInitializationInfo::AudioStreamInfo::AudioStreamInfo(
     media::AudioDecoderConfig audio_config,
     openscreen::cast::Receiver* cast_receiver)
-    : AudioStreamInfo(std::move(audio_config),
-                      cast_receiver,
-                      base::RepeatingClosure(),
-                      base::OnceClosure()) {}
+    : AudioStreamInfo(std::move(audio_config), cast_receiver, nullptr) {}
 
 StreamingInitializationInfo::AudioStreamInfo::AudioStreamInfo(
     media::AudioDecoderConfig audio_config,
     openscreen::cast::Receiver* cast_receiver,
-    base::RepeatingClosure on_no_buffers_cb,
-    base::OnceClosure on_error_cb)
+    base::WeakPtr<DemuxerStreamClient> ds_client)
     : config(std::move(audio_config)),
       receiver(cast_receiver),
-      on_no_buffers_callback(std::move(on_no_buffers_cb)),
-      on_error_callback(std::move(on_error_cb)) {}
+      demuxer_stream_client(std::move(ds_client)) {}
 
 StreamingInitializationInfo::AudioStreamInfo::AudioStreamInfo(
-    const StreamingInitializationInfo::AudioStreamInfo& other) {
-  auto& old = const_cast<AudioStreamInfo&>(other);
-  auto cb_pair = base::SplitOnceCallback(std::move(old.on_error_callback));
-  old.on_error_callback = std::move(cb_pair.first);
-
-  config = other.config;
-  receiver = other.receiver;
-  on_no_buffers_callback = other.on_no_buffers_callback;
-  on_error_callback = std::move(cb_pair.second);
-}
+    const StreamingInitializationInfo::AudioStreamInfo& other) = default;
 
 StreamingInitializationInfo::AudioStreamInfo::~AudioStreamInfo() = default;
 
@@ -62,32 +48,18 @@
 StreamingInitializationInfo::VideoStreamInfo::VideoStreamInfo(
     media::VideoDecoderConfig video_config,
     openscreen::cast::Receiver* cast_receiver)
-    : VideoStreamInfo(std::move(video_config),
-                      cast_receiver,
-                      base::RepeatingClosure(),
-                      base::OnceClosure()) {}
+    : VideoStreamInfo(std::move(video_config), cast_receiver, nullptr) {}
 
 StreamingInitializationInfo::VideoStreamInfo::VideoStreamInfo(
     media::VideoDecoderConfig video_config,
     openscreen::cast::Receiver* cast_receiver,
-    base::RepeatingClosure on_no_buffers_cb,
-    base::OnceClosure on_error_cb)
+    base::WeakPtr<DemuxerStreamClient> ds_client)
     : config(std::move(video_config)),
       receiver(cast_receiver),
-      on_no_buffers_callback(std::move(on_no_buffers_cb)),
-      on_error_callback(std::move(on_error_cb)) {}
+      demuxer_stream_client(std::move(ds_client)) {}
 
 StreamingInitializationInfo::VideoStreamInfo::VideoStreamInfo(
-    const StreamingInitializationInfo::VideoStreamInfo& other) {
-  auto& old = const_cast<VideoStreamInfo&>(other);
-  auto cb_pair = base::SplitOnceCallback(std::move(old.on_error_callback));
-  old.on_error_callback = std::move(cb_pair.first);
-
-  config = other.config;
-  receiver = other.receiver;
-  on_no_buffers_callback = other.on_no_buffers_callback;
-  on_error_callback = std::move(cb_pair.second);
-}
+    const StreamingInitializationInfo::VideoStreamInfo& other) = default;
 
 StreamingInitializationInfo::VideoStreamInfo::~VideoStreamInfo() = default;
 
diff --git a/components/cast_streaming/browser/streaming_initialization_info.h b/components/cast_streaming/browser/streaming_initialization_info.h
index 188f2faf..2fd89a7e 100644
--- a/components/cast_streaming/browser/streaming_initialization_info.h
+++ b/components/cast_streaming/browser/streaming_initialization_info.h
@@ -5,8 +5,8 @@
 #ifndef COMPONENTS_CAST_STREAMING_BROWSER_STREAMING_INITIALIZATION_INFO_H_
 #define COMPONENTS_CAST_STREAMING_BROWSER_STREAMING_INITIALIZATION_INFO_H_
 
-#include "base/callback.h"
-#include "base/callback_forward.h"
+#include "base/memory/weak_ptr.h"
+#include "components/cast_streaming/browser/demuxer_stream_client.h"
 #include "media/base/audio_decoder_config.h"
 #include "media/base/video_decoder_config.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
@@ -27,8 +27,7 @@
                     openscreen::cast::Receiver* cast_receiver);
     AudioStreamInfo(media::AudioDecoderConfig audio_config,
                     openscreen::cast::Receiver* cast_receiver,
-                    base::RepeatingClosure on_no_buffers_cb,
-                    base::OnceClosure on_error_cb);
+                    base::WeakPtr<DemuxerStreamClient> ds_client);
     AudioStreamInfo();
     AudioStreamInfo(const AudioStreamInfo& other);
     ~AudioStreamInfo();
@@ -40,11 +39,9 @@
     // duration of the streaming session.
     openscreen::cast::Receiver* receiver;
 
-    // Callback to be called when no buffers are available for reading.
-    base::RepeatingClosure on_no_buffers_callback;
-
-    // Callback to be called when a non-recoverable error occurs.
-    base::OnceClosure on_error_callback;
+    // Client with methods to be called when the DemuxerStream requires an
+    // action be executed.
+    base::WeakPtr<DemuxerStreamClient> demuxer_stream_client;
   };
 
   struct VideoStreamInfo {
@@ -52,8 +49,7 @@
                     openscreen::cast::Receiver* cast_receiver);
     VideoStreamInfo(media::VideoDecoderConfig video_config,
                     openscreen::cast::Receiver* cast_receiver,
-                    base::RepeatingClosure on_no_buffers_cb,
-                    base::OnceClosure on_error_cb);
+                    base::WeakPtr<DemuxerStreamClient> ds_client);
     VideoStreamInfo();
     VideoStreamInfo(const VideoStreamInfo& other);
     ~VideoStreamInfo();
@@ -65,11 +61,9 @@
     // duration of the streaming session.
     openscreen::cast::Receiver* receiver;
 
-    // Callback to be called when no buffers are available for reading.
-    base::RepeatingClosure on_no_buffers_callback;
-
-    // Callback to be called when a non-recoverable error occurs.
-    base::OnceClosure on_error_callback;
+    // Client with methods to be called when the DemuxerStream requires an
+    // action be executed.
+    base::WeakPtr<DemuxerStreamClient> demuxer_stream_client;
   };
 
   StreamingInitializationInfo(
diff --git a/components/cast_streaming/public/mojom/demuxer_connector.mojom b/components/cast_streaming/public/mojom/demuxer_connector.mojom
index d7c8ae0a..00ef810 100644
--- a/components/cast_streaming/public/mojom/demuxer_connector.mojom
+++ b/components/cast_streaming/public/mojom/demuxer_connector.mojom
@@ -47,6 +47,10 @@
   // returns.
   GetBuffer() => (AudioStreamInfo? stream_info,
                   media.mojom.DecoderBuffer buffer);
+
+  // Requests that the data source providing audio buffers enable its bitstream
+  // converter. Returns whether the operation was successful.
+  EnableBitstreamConverter() => (bool success);
 };
 
 // Provides a "pull" mechanism to request DecoderBuffer frames of video data.
@@ -56,6 +60,9 @@
   // As AudioBufferRequester::GetBuffer() above.
   GetBuffer() => (VideoStreamInfo? stream_info,
                   media.mojom.DecoderBuffer buffer);
+
+  // As AudioBufferRequester::EnableBitstreamConverter() above.
+  EnableBitstreamConverter() => (bool success);
 };
 
 // Initialization information for an audio DemuxerStream.
diff --git a/components/cast_streaming/public/remoting_message_factories.cc b/components/cast_streaming/public/remoting_message_factories.cc
index da597e17..2dcb3a0 100644
--- a/components/cast_streaming/public/remoting_message_factories.cc
+++ b/components/cast_streaming/public/remoting_message_factories.cc
@@ -150,6 +150,13 @@
 }
 
 std::unique_ptr<openscreen::cast::RpcMessage>
+CreateMessageForDemuxerStreamEnableBitstreamConverter() {
+  auto rpc = std::make_unique<openscreen::cast::RpcMessage>();
+  rpc->set_proc(openscreen::cast::RpcMessage::RPC_DS_ENABLEBITSTREAMCONVERTER);
+  return rpc;
+}
+
+std::unique_ptr<openscreen::cast::RpcMessage>
 CreateMessageForDemuxerStreamError() {
   auto rpc = std::make_unique<openscreen::cast::RpcMessage>();
   rpc->set_proc(openscreen::cast::RpcMessage::RPC_DS_ONERROR);
diff --git a/components/cast_streaming/public/remoting_message_factories.h b/components/cast_streaming/public/remoting_message_factories.h
index 980de9fa..9d00b066 100644
--- a/components/cast_streaming/public/remoting_message_factories.h
+++ b/components/cast_streaming/public/remoting_message_factories.h
@@ -80,6 +80,9 @@
     uint32_t buffers_requested);
 
 std::unique_ptr<openscreen::cast::RpcMessage>
+CreateMessageForDemuxerStreamEnableBitstreamConverter();
+
+std::unique_ptr<openscreen::cast::RpcMessage>
 CreateMessageForDemuxerStreamError();
 
 }  // namespace remoting
diff --git a/components/cast_streaming/public/remoting_message_factories_unittest.cc b/components/cast_streaming/public/remoting_message_factories_unittest.cc
index 59b18a1..17c825a9 100644
--- a/components/cast_streaming/public/remoting_message_factories_unittest.cc
+++ b/components/cast_streaming/public/remoting_message_factories_unittest.cc
@@ -208,6 +208,13 @@
   EXPECT_EQ(message.callback_handle(), kTestHandle);
 }
 
+TEST_F(RemotingMessageFactoriesTest,
+       CreateMessageForDemuxerStreamEnableBitstreamConverter) {
+  const auto rpc = CreateMessageForDemuxerStreamEnableBitstreamConverter();
+  EXPECT_EQ(rpc->proc(),
+            openscreen::cast::RpcMessage::RPC_DS_ENABLEBITSTREAMCONVERTER);
+}
+
 TEST_F(RemotingMessageFactoriesTest, CreateMessageForDemuxerStreamError) {
   const auto rpc = CreateMessageForDemuxerStreamError();
   EXPECT_EQ(rpc->proc(), openscreen::cast::RpcMessage::RPC_DS_ONERROR);
diff --git a/components/cast_streaming/renderer/cast_streaming_demuxer.cc b/components/cast_streaming/renderer/cast_streaming_demuxer.cc
index 50e2610d..2016431 100644
--- a/components/cast_streaming/renderer/cast_streaming_demuxer.cc
+++ b/components/cast_streaming/renderer/cast_streaming_demuxer.cc
@@ -182,6 +182,8 @@
     }
   }
 
+  void OnBitstreamConverterEnabled(bool success) { NOTIMPLEMENTED_LOG_ONCE(); }
+
   // DemuxerStream partial implementation.
   void Read(ReadCB read_cb) final {
     DVLOG(3) << __func__;
@@ -212,6 +214,12 @@
     buffer_reader_->ReadBufferAsync();
   }
 
+  void EnableBitstreamConverter() final {
+    remote_->EnableBitstreamConverter(
+        base::BindOnce(&CastStreamingDemuxerStream::OnBitstreamConverterEnabled,
+                       weak_factory_.GetWeakPtr()));
+  }
+
   media::StreamLiveness liveness() const final {
     return media::StreamLiveness::kLive;
   }
diff --git a/components/history_clusters/core/BUILD.gn b/components/history_clusters/core/BUILD.gn
index 45d448f..45d7b6a 100644
--- a/components/history_clusters/core/BUILD.gn
+++ b/components/history_clusters/core/BUILD.gn
@@ -9,6 +9,7 @@
 static_library("core") {
   sources = [
     "cluster_finalizer.h",
+    "cluster_metrics_utils.cc",
     "cluster_metrics_utils.h",
     "cluster_processor.h",
     "clusterer.cc",
diff --git a/components/history_clusters/core/cluster_metrics_utils.cc b/components/history_clusters/core/cluster_metrics_utils.cc
new file mode 100644
index 0000000..3ce089bf
--- /dev/null
+++ b/components/history_clusters/core/cluster_metrics_utils.cc
@@ -0,0 +1,59 @@
+// Copyright 2022 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/history_clusters/core/cluster_metrics_utils.h"
+
+#include "base/notreached.h"
+
+namespace history_clusters {
+
+std::string ClusterActionToString(ClusterAction action) {
+  switch (action) {
+    case ClusterAction::kDeleted:
+      return "Deleted";
+    case ClusterAction::kOpenedInTabGroup:
+      return "OpenedInTabGroup";
+    case ClusterAction::kRelatedSearchClicked:
+      return "RelatedSearchClicked";
+    case ClusterAction::kRelatedVisitsVisibilityToggled:
+      return "RelatedVisitsVisibilityToggled";
+    case ClusterAction::kVisitClicked:
+      return "VisitClicked";
+  }
+  NOTREACHED();
+  return std::string();
+}
+
+std::string VisitActionToString(VisitAction action) {
+  switch (action) {
+    case VisitAction::kDeleted:
+      return "Deleted";
+    case VisitAction::kClicked:
+      return "Clicked";
+  }
+  NOTREACHED();
+  return std::string();
+}
+
+std::string VisitTypeToString(VisitType action) {
+  switch (action) {
+    case VisitType::kSRP:
+      return "SRP";
+    case VisitType::kNonSRP:
+      return "nonSRP";
+  }
+  NOTREACHED();
+  return std::string();
+}
+
+std::string RelatedSearchActionToString(RelatedSearchAction action) {
+  switch (action) {
+    case RelatedSearchAction::kClicked:
+      return "Clicked";
+  }
+  NOTREACHED();
+  return std::string();
+}
+
+}  // namespace history_clusters
diff --git a/components/history_clusters/core/cluster_metrics_utils.h b/components/history_clusters/core/cluster_metrics_utils.h
index 9cb2e94..e9a5f1a 100644
--- a/components/history_clusters/core/cluster_metrics_utils.h
+++ b/components/history_clusters/core/cluster_metrics_utils.h
@@ -35,6 +35,45 @@
   const std::string filtered_reason_;
 };
 
+/**
+ * The following enums must be kept in sync with their respective variants in
+ * //tools/metrics/histograms/metadata/history/histograms.xml and
+ * //ui/webui/resources/cr_components/history_clusters/history_clusters.mojom
+ */
+
+// Actions that can be performed on clusters.
+enum class ClusterAction {
+  kDeleted = 0,
+  kOpenedInTabGroup = 1,
+  kRelatedSearchClicked = 2,
+  kRelatedVisitsVisibilityToggled = 3,
+  kVisitClicked = 4,
+};
+
+// Actions that can be performed on related search items.
+enum class RelatedSearchAction {
+  kClicked = 0,
+};
+
+// Actions that can be performed on visits.
+enum class VisitAction {
+  kClicked = 0,
+  kDeleted = 1,
+};
+
+// Types of visits that can be shown and acted on.
+enum class VisitType {
+  kSRP = 0,
+  kNonSRP = 1,
+};
+
+// Returns the string representation of each enum class used for
+// logging/histograms.
+std::string ClusterActionToString(ClusterAction action);
+std::string VisitActionToString(VisitAction action);
+std::string VisitTypeToString(VisitType action);
+std::string RelatedSearchActionToString(RelatedSearchAction action);
+
 }  // namespace history_clusters
 
 #endif  // COMPONENTS_HISTORY_CLUSTERS_CORE_CLUSTER_METRICS_UTILS_H_
diff --git a/components/minidump_uploader/android/java/src/org/chromium/components/minidump_uploader/MinidumpUploadJobService.java b/components/minidump_uploader/android/java/src/org/chromium/components/minidump_uploader/MinidumpUploadJobService.java
index de4bc33..34eb37d 100644
--- a/components/minidump_uploader/android/java/src/org/chromium/components/minidump_uploader/MinidumpUploadJobService.java
+++ b/components/minidump_uploader/android/java/src/org/chromium/components/minidump_uploader/MinidumpUploadJobService.java
@@ -9,9 +9,12 @@
 import android.app.job.JobService;
 import android.content.Context;
 import android.os.PersistableBundle;
+import android.os.SystemClock;
+import android.text.format.DateUtils;
 
 import org.chromium.base.ContextUtils;
 import org.chromium.base.Log;
+import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.build.BuildConfig;
 
 /**
@@ -85,6 +88,8 @@
     private MinidumpUploadJob.UploadsFinishedCallback createJobFinishedCallback(
             final JobParameters params) {
         return new MinidumpUploadJob.UploadsFinishedCallback() {
+            private final long mTaskStartTimeMs = SystemClock.uptimeMillis();
+
             @Override
             public void uploadsFinished(boolean reschedule) {
                 if (reschedule) {
@@ -96,6 +101,10 @@
                     }
                 }
                 MinidumpUploadJobService.this.jobFinished(params, reschedule);
+                long taskDurationMs = SystemClock.uptimeMillis() - mTaskStartTimeMs;
+                RecordHistogram.recordCustomTimesHistogram(
+                        "Stability.Android.MinidumpUploadingTime", taskDurationMs, 1,
+                        DateUtils.DAY_IN_MILLIS, 50);
             }
         };
     }
diff --git a/components/omnibox/browser/builtin_provider.cc b/components/omnibox/browser/builtin_provider.cc
index 9ffe1d29..4510287 100644
--- a/components/omnibox/browser/builtin_provider.cc
+++ b/components/omnibox/browser/builtin_provider.cc
@@ -27,7 +27,6 @@
 #include "url/url_constants.h"
 
 const int BuiltinProvider::kRelevance = 860;
-const int BuiltinProvider::kStarterPackRelevance = 1200;
 
 BuiltinProvider::BuiltinProvider(AutocompleteProviderClient* client)
     : AutocompleteProvider(AutocompleteProvider::TYPE_BUILTIN),
@@ -201,8 +200,9 @@
 }
 
 void BuiltinProvider::AddStarterPackMatch(const TemplateURL& template_url) {
-  AutocompleteMatch match(this, kStarterPackRelevance, false,
-                          AutocompleteMatchType::SEARCH_OTHER_ENGINE);
+  AutocompleteMatch match(
+      this, OmniboxFieldTrial::kSiteSearchStarterPackRelevanceScore.Get(),
+      false, AutocompleteMatchType::SEARCH_OTHER_ENGINE);
 
   match.fill_into_edit = template_url.keyword();
   match.destination_url = GURL(template_url.url());
diff --git a/components/omnibox/browser/builtin_provider.h b/components/omnibox/browser/builtin_provider.h
index ae4540b8..8d06e0f 100644
--- a/components/omnibox/browser/builtin_provider.h
+++ b/components/omnibox/browser/builtin_provider.h
@@ -32,7 +32,6 @@
   typedef std::vector<std::u16string> Builtins;
 
   static const int kRelevance;
-  static const int kStarterPackRelevance;
 
   // Populates `matches_` with matching starter pack keywords such as @history,
   // and @bookmarks
diff --git a/components/omnibox/browser/omnibox_field_trial.cc b/components/omnibox/browser/omnibox_field_trial.cc
index 5eabc98..918d58f 100644
--- a/components/omnibox/browser/omnibox_field_trial.cc
+++ b/components/omnibox/browser/omnibox_field_trial.cc
@@ -937,6 +937,11 @@
     "PrefixSuggestIgnoreDuplicateVisits",
     false);
 
+const base::FeatureParam<int> kSiteSearchStarterPackRelevanceScore(
+    &omnibox::kSiteSearchStarterPack,
+    "SiteSearchStarterPackRelevanceScore",
+    1200);
+
 }  // namespace OmniboxFieldTrial
 
 std::string OmniboxFieldTrial::internal::GetValueForRuleInContext(
diff --git a/components/omnibox/browser/omnibox_field_trial.h b/components/omnibox/browser/omnibox_field_trial.h
index a4633fd3..6f6d5c3 100644
--- a/components/omnibox/browser/omnibox_field_trial.h
+++ b/components/omnibox/browser/omnibox_field_trial.h
@@ -561,6 +561,10 @@
 // interval smaller than kAutocompleteDuplicateVisitIntervalThreshold.
 extern const base::FeatureParam<bool> kPrefixSuggestIgnoreDuplicateVisits;
 
+// Specifies the relevance scores for the Site Search Starter Pack ACMatches
+// (e.g. @bookmarks, @history) provided by the Builtin Provider.
+extern const base::FeatureParam<int> kSiteSearchStarterPackRelevanceScore;
+
 // New params should be inserted above this comment and formatted as:
 // - Short comment categorizing the relevant features & params.
 // - Optional: `bool Is[FeatureName]Enabled();` helpers that check if the
diff --git a/components/performance_manager/performance_manager.cc b/components/performance_manager/performance_manager.cc
index a8f30d7..802410e 100644
--- a/components/performance_manager/performance_manager.cc
+++ b/components/performance_manager/performance_manager.cc
@@ -95,8 +95,8 @@
   auto* frame_node = helper->GetFrameNode(rfh);
   if (!frame_node) {
     // This should only happen if GetFrameNodeForRenderFrameHost is called
-    // before the RenderFrameCreate notification is dispatched.
-    DCHECK(!rfh->IsRenderFrameCreated());
+    // before the RenderFrameCreated notification is dispatched.
+    DCHECK(!rfh->IsRenderFrameLive());
     return nullptr;
   }
   return frame_node->GetWeakPtrOnUIThread();
diff --git a/components/performance_manager/performance_manager_browsertest.cc b/components/performance_manager/performance_manager_browsertest.cc
index 39e742a..31feecd 100644
--- a/components/performance_manager/performance_manager_browsertest.cc
+++ b/components/performance_manager/performance_manager_browsertest.cc
@@ -44,7 +44,7 @@
   ASSERT_EQ(contents, old_contents);
   ASSERT_EQ(contents->GetLastCommittedURL().possibly_invalid_spec(), kUrl);
   content::RenderFrameHost* rfh = contents->GetMainFrame();
-  ASSERT_TRUE(rfh->IsRenderFrameCreated());
+  ASSERT_TRUE(rfh->IsRenderFrameLive());
 
   base::WeakPtr<FrameNode> frame_node =
       PerformanceManager::GetFrameNodeForRenderFrameHost(rfh);
diff --git a/components/performance_manager/performance_manager_tab_helper.cc b/components/performance_manager/performance_manager_tab_helper.cc
index bf5d7db..a8ab8dc 100644
--- a/components/performance_manager/performance_manager_tab_helper.cc
+++ b/components/performance_manager/performance_manager_tab_helper.cc
@@ -86,7 +86,7 @@
   // We have an early WebContents creation hook so should see it when there is
   // only a single frame, and it is not yet created. We sanity check that here.
 #if DCHECK_IS_ON()
-  DCHECK(!web_contents->GetMainFrame()->IsRenderFrameCreated());
+  DCHECK(!web_contents->GetMainFrame()->IsRenderFrameLive());
   size_t frame_count = 0;
   web_contents->ForEachRenderFrameHost(base::BindRepeating(
       [](size_t* frame_count, content::RenderFrameHost* render_frame_host) {
@@ -268,7 +268,7 @@
   if (it != frames_.end()) {
     new_frame = it->second.get();
   } else {
-    DCHECK(!new_host->IsRenderFrameCreated())
+    DCHECK(!new_host->IsRenderFrameLive())
         << "There shouldn't be a case where RenderFrameHostChanged is "
            "dispatched before RenderFrameCreated with a live RenderFrame\n";
   }
diff --git a/components/remote_cocoa/app_shim/native_widget_ns_window_bridge.h b/components/remote_cocoa/app_shim/native_widget_ns_window_bridge.h
index d6bc556..b627ac5 100644
--- a/components/remote_cocoa/app_shim/native_widget_ns_window_bridge.h
+++ b/components/remote_cocoa/app_shim/native_widget_ns_window_bridge.h
@@ -122,7 +122,6 @@
 
   // Sets the cursor associated with the NSWindow. Retains |cursor|.
   void SetCursor(NSCursor* cursor);
-  void SetCursor(const ui::Cursor& cursor);
 
   // Called internally by the NSWindowDelegate when the window is closing.
   void OnWindowWillClose();
@@ -275,6 +274,7 @@
       const mojom::WindowControlsOverlayNSViewType overlay_type) override;
   void RemoveWindowControlsOverlayNSView(
       const mojom::WindowControlsOverlayNSViewType overlay_type) override;
+  void SetCursor(const ui::Cursor& cursor) override;
 
   // Return true if [NSApp updateWindows] needs to be called after updating the
   // TextInputClient.
diff --git a/components/remote_cocoa/common/BUILD.gn b/components/remote_cocoa/common/BUILD.gn
index 5005f8e..6a35864a 100644
--- a/components/remote_cocoa/common/BUILD.gn
+++ b/components/remote_cocoa/common/BUILD.gn
@@ -21,6 +21,7 @@
     "//mojo/public/mojom/base",
     "//services/network/public/mojom",
     "//ui/base/accelerators/mojom",
+    "//ui/base/cursor/mojom",
     "//ui/base/mojom",
     "//ui/display/mojom",
     "//ui/events/mojom",
diff --git a/components/remote_cocoa/common/native_widget_ns_window.mojom b/components/remote_cocoa/common/native_widget_ns_window.mojom
index 9c103653..98292d6 100644
--- a/components/remote_cocoa/common/native_widget_ns_window.mojom
+++ b/components/remote_cocoa/common/native_widget_ns_window.mojom
@@ -7,6 +7,7 @@
 import "components/remote_cocoa/common/select_file_dialog.mojom";
 import "mojo/public/mojom/base/string16.mojom";
 import "services/network/public/mojom/network_param.mojom";
+import "ui/base/cursor/mojom/cursor.mojom";
 import "ui/base/mojom/ui_base_types.mojom";
 import "ui/events/mojom/event_constants.mojom";
 import "ui/gfx/geometry/mojom/geometry.mojom";
@@ -250,4 +251,7 @@
   // overlay display override for a |overlay_type|.
   RemoveWindowControlsOverlayNSView(
       WindowControlsOverlayNSViewType overlay_type);
+
+  // Set the cursor type to display.
+  SetCursor(ui.mojom.Cursor cursor);
 };
diff --git a/components/safe_browsing/content/browser/threat_details.cc b/components/safe_browsing/content/browser/threat_details.cc
index 7da4823..7b1bfd0e 100644
--- a/components/safe_browsing/content/browser/threat_details.cc
+++ b/components/safe_browsing/content/browser/threat_details.cc
@@ -645,7 +645,7 @@
       frame,
       back_forward_cache::DisabledReason(
           back_forward_cache::DisabledReasonId::kSafeBrowsingThreatDetails));
-  if (!frame->IsRenderFrameCreated()) {
+  if (!frame->IsRenderFrameLive()) {
     // A child frame may have been created browser-side but has not completed
     // setting up the renderer for it yet. In particular, this occurs if the
     // child frame was blocked and that's why we're showing a safe browsing page
diff --git a/components/search_engines/default_search_policy_handler.cc b/components/search_engines/default_search_policy_handler.cc
index 8c3dd4b..77afa2b4 100644
--- a/components/search_engines/default_search_policy_handler.cc
+++ b/components/search_engines/default_search_policy_handler.cc
@@ -197,6 +197,7 @@
   dict->SetString(DefaultSearchManager::kID,
                   base::NumberToString(kInvalidTemplateURLID));
   dict->SetInteger(DefaultSearchManager::kPrepopulateID, 0);
+  dict->SetInteger(DefaultSearchManager::kStarterPackId, 0);
   dict->SetString(DefaultSearchManager::kSyncGUID, std::string());
   dict->SetString(DefaultSearchManager::kOriginatingURL, std::string());
   dict->SetBoolean(DefaultSearchManager::kSafeForAutoReplace, true);
diff --git a/components/search_engines/template_url_data_util.cc b/components/search_engines/template_url_data_util.cc
index 39cded9..f1c98f6 100644
--- a/components/search_engines/template_url_data_util.cc
+++ b/components/search_engines/template_url_data_util.cc
@@ -54,6 +54,9 @@
   result->SetShortName(base::UTF8ToUTF16(*short_name));
   result->prepopulate_id = dict.FindIntKey(DefaultSearchManager::kPrepopulateID)
                                .value_or(result->prepopulate_id);
+  result->starter_pack_id =
+      dict.FindIntKey(DefaultSearchManager::kStarterPackId)
+          .value_or(result->starter_pack_id);
   string_value = dict.FindStringKey(DefaultSearchManager::kSyncGUID);
   if (string_value) {
     result->sync_guid = *string_value;
@@ -197,6 +200,8 @@
   url_dict->SetStringKey(DefaultSearchManager::kKeyword, data.keyword());
   url_dict->SetIntKey(DefaultSearchManager::kPrepopulateID,
                       data.prepopulate_id);
+  url_dict->SetIntKey(DefaultSearchManager::kStarterPackId,
+                      data.starter_pack_id);
   url_dict->SetStringKey(DefaultSearchManager::kSyncGUID, data.sync_guid);
 
   url_dict->SetStringKey(DefaultSearchManager::kURL, data.url());
diff --git a/components/segmentation_platform/content/BUILD.gn b/components/segmentation_platform/content/BUILD.gn
new file mode 100644
index 0000000..40bbe13c
--- /dev/null
+++ b/components/segmentation_platform/content/BUILD.gn
@@ -0,0 +1,61 @@
+# Copyright 2022 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.
+
+if (is_android) {
+  import("//build/config/android/config.gni")
+  import("//build/config/android/rules.gni")
+}
+
+source_set("content") {
+  sources = [
+    "page_load_trigger_context.cc",
+    "page_load_trigger_context.h",
+  ]
+
+  deps = [
+    "//base",
+    "//components/segmentation_platform/public",
+    "//content/public/browser",
+  ]
+
+  if (is_android) {
+    deps += [ ":jni_headers" ]
+  }
+}
+
+source_set("unit_tests") {
+  testonly = true
+
+  # IMPORTANT NOTE: When adding new tests, also remember to update the list of
+  # tests in //components/segmentation_platform/components_unittests.filter
+  sources = []
+
+  deps = [
+    ":content",
+    "//testing/gtest",
+  ]
+}
+
+if (is_android) {
+  android_library("content_java") {
+    visibility = [ "*" ]
+    sources = [ "android/java/src/org/chromium/components/segmentation_platform/PageLoadTriggerContext.java" ]
+
+    deps = [
+      "//base:base_java",
+      "//base:jni_java",
+      "//build/android:build_java",
+      "//components/segmentation_platform/public:public_java",
+      "//content/public/android:content_java",
+      "//ui/android:ui_no_recycler_view_java",
+    ]
+    annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ]
+  }
+
+  generate_jni("jni_headers") {
+    visibility = [ ":*" ]
+
+    sources = [ "android/java/src/org/chromium/components/segmentation_platform/PageLoadTriggerContext.java" ]
+  }
+}
diff --git a/components/segmentation_platform/content/DEPS b/components/segmentation_platform/content/DEPS
new file mode 100644
index 0000000..4bea8d18
--- /dev/null
+++ b/components/segmentation_platform/content/DEPS
@@ -0,0 +1,4 @@
+include_rules = [
+  "+content/public/android/java/src/org/chromium/content_public/browser/WebContents.java",
+  "+content/public/browser",
+]
diff --git a/components/segmentation_platform/content/android/java/src/org/chromium/components/segmentation_platform/PageLoadTriggerContext.java b/components/segmentation_platform/content/android/java/src/org/chromium/components/segmentation_platform/PageLoadTriggerContext.java
new file mode 100644
index 0000000..a59e1d8
--- /dev/null
+++ b/components/segmentation_platform/content/android/java/src/org/chromium/components/segmentation_platform/PageLoadTriggerContext.java
@@ -0,0 +1,24 @@
+// Copyright 2022 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.components.segmentation_platform;
+
+import org.chromium.base.annotations.CalledByNative;
+import org.chromium.content_public.browser.WebContents;
+
+/**
+ * Java representation of the native PageLoadTriggerContext.
+ */
+public class PageLoadTriggerContext extends TriggerContext {
+    public final WebContents webContents;
+
+    @CalledByNative
+    private static PageLoadTriggerContext createPageLoadTriggerContext(WebContents webContents) {
+        return new PageLoadTriggerContext(webContents);
+    }
+
+    public PageLoadTriggerContext(WebContents webContents) {
+        this.webContents = webContents;
+    }
+}
diff --git a/components/segmentation_platform/content/page_load_trigger_context.cc b/components/segmentation_platform/content/page_load_trigger_context.cc
new file mode 100644
index 0000000..bdfc874e
--- /dev/null
+++ b/components/segmentation_platform/content/page_load_trigger_context.cc
@@ -0,0 +1,31 @@
+// Copyright 2022 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/segmentation_platform/content/page_load_trigger_context.h"
+
+#include "content/public/browser/web_contents.h"
+
+#if BUILDFLAG(IS_ANDROID)
+#include "base/android/jni_android.h"
+#include "components/segmentation_platform/content/jni_headers/PageLoadTriggerContext_jni.h"
+#endif  // BUILDFLAG(IS_ANDROID)
+
+namespace segmentation_platform {
+
+PageLoadTriggerContext::PageLoadTriggerContext(
+    content::WebContents* web_contents)
+    : web_contents(web_contents) {}
+
+PageLoadTriggerContext::~PageLoadTriggerContext() = default;
+
+#if BUILDFLAG(IS_ANDROID)
+base::android::ScopedJavaLocalRef<jobject>
+PageLoadTriggerContext::CreateJavaObject() const {
+  JNIEnv* env = base::android::AttachCurrentThread();
+  return Java_PageLoadTriggerContext_createPageLoadTriggerContext(
+      env, web_contents->GetJavaWebContents());
+}
+#endif  // BUILDFLAG(IS_ANDROID)
+
+}  // namespace segmentation_platform
diff --git a/components/segmentation_platform/content/page_load_trigger_context.h b/components/segmentation_platform/content/page_load_trigger_context.h
new file mode 100644
index 0000000..22d06193
--- /dev/null
+++ b/components/segmentation_platform/content/page_load_trigger_context.h
@@ -0,0 +1,36 @@
+// Copyright 2022 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_SEGMENTATION_PLATFORM_CONTENT_PAGE_LOAD_TRIGGER_CONTEXT_H_
+#define COMPONENTS_SEGMENTATION_PLATFORM_CONTENT_PAGE_LOAD_TRIGGER_CONTEXT_H_
+
+#include "base/memory/raw_ptr.h"
+#include "components/segmentation_platform/public/trigger_context.h"
+
+#if BUILDFLAG(IS_ANDROID)
+#include "base/android/jni_android.h"
+#endif  // BUILDFLAG(IS_ANDROID)
+
+namespace content {
+class WebContents;
+}  // namespace content
+
+namespace segmentation_platform {
+
+// Contains contextual information for a page load trigger event.
+struct PageLoadTriggerContext : public TriggerContext {
+ public:
+  explicit PageLoadTriggerContext(content::WebContents* web_contents);
+  ~PageLoadTriggerContext() override;
+
+#if BUILDFLAG(IS_ANDROID)
+  base::android::ScopedJavaLocalRef<jobject> CreateJavaObject() const override;
+#endif  // BUILDFLAG(IS_ANDROID)
+
+  raw_ptr<content::WebContents> web_contents;
+};
+
+}  // namespace segmentation_platform
+
+#endif  // COMPONENTS_SEGMENTATION_PLATFORM_CONTENT_PAGE_LOAD_TRIGGER_CONTEXT_H_
diff --git a/components/segmentation_platform/internal/BUILD.gn b/components/segmentation_platform/internal/BUILD.gn
index 83d0f94..ad776c33 100644
--- a/components/segmentation_platform/internal/BUILD.gn
+++ b/components/segmentation_platform/internal/BUILD.gn
@@ -99,6 +99,8 @@
     "scheduler/model_execution_scheduler.h",
     "scheduler/model_execution_scheduler_impl.cc",
     "scheduler/model_execution_scheduler_impl.h",
+    "segment_id_convertor.cc",
+    "segment_id_convertor.h",
     "segmentation_platform_service_impl.cc",
     "segmentation_platform_service_impl.h",
     "segmentation_ukm_helper.cc",
@@ -158,8 +160,13 @@
     "//url:url",
   ]
 
+  public_deps =
+      [ "//components/optimization_guide/proto:optimization_guide_proto" ]
+
   if (is_android) {
     sources += [
+      "android/segmentation_platform_conversion_bridge.cc",
+      "android/segmentation_platform_conversion_bridge.h",
       "android/segmentation_platform_service_android.cc",
       "android/segmentation_platform_service_android.h",
     ]
@@ -177,14 +184,11 @@
       "execution/optimization_guide/optimization_guide_segmentation_model_provider.h",
       "execution/optimization_guide/segmentation_model_executor.cc",
       "execution/optimization_guide/segmentation_model_executor.h",
-      "segment_id_convertor.cc",
-      "segment_id_convertor.h",
     ]
     deps = [
       ":internal",
       "//base",
       "//components/optimization_guide/core",
-      "//components/optimization_guide/proto:optimization_guide_proto",
       "//components/segmentation_platform/internal/proto",
       "//components/segmentation_platform/public",
     ]
@@ -268,7 +272,6 @@
     "//components/leveldb_proto:test_support",
     "//components/optimization_guide/core",
     "//components/optimization_guide/core:test_support",
-    "//components/optimization_guide/proto:optimization_guide_proto",
     "//components/prefs",
     "//components/prefs:test_support",
     "//components/segmentation_platform/internal/proto",
@@ -322,7 +325,10 @@
 if (is_android) {
   android_library("internal_java") {
     visibility = [ "//chrome/android:chrome_all_java" ]
-    sources = [ "android/java/src/org/chromium/components/segmentation_platform/SegmentationPlatformServiceImpl.java" ]
+    sources = [
+      "android/java/src/org/chromium/components/segmentation_platform/SegmentationPlatformConversionBridge.java",
+      "android/java/src/org/chromium/components/segmentation_platform/SegmentationPlatformServiceImpl.java",
+    ]
 
     deps = [
       "//base:base_java",
@@ -340,6 +346,9 @@
       "//chrome/browser",
     ]
 
-    sources = [ "android/java/src/org/chromium/components/segmentation_platform/SegmentationPlatformServiceImpl.java" ]
+    sources = [
+      "android/java/src/org/chromium/components/segmentation_platform/SegmentationPlatformConversionBridge.java",
+      "android/java/src/org/chromium/components/segmentation_platform/SegmentationPlatformServiceImpl.java",
+    ]
   }
 }
diff --git a/components/segmentation_platform/internal/android/java/src/org/chromium/components/segmentation_platform/SegmentationPlatformConversionBridge.java b/components/segmentation_platform/internal/android/java/src/org/chromium/components/segmentation_platform/SegmentationPlatformConversionBridge.java
new file mode 100644
index 0000000..32e304c
--- /dev/null
+++ b/components/segmentation_platform/internal/android/java/src/org/chromium/components/segmentation_platform/SegmentationPlatformConversionBridge.java
@@ -0,0 +1,29 @@
+// Copyright 2022 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.components.segmentation_platform;
+
+import org.chromium.base.annotations.CalledByNative;
+import org.chromium.base.annotations.JNINamespace;
+import org.chromium.components.segmentation_platform.proto.SegmentationProto.SegmentId;
+
+/**
+ * Java side of the JNI bridge for various JNI conversions required by the segmentation platform.
+ */
+@JNINamespace("segmentation_platform")
+public class SegmentationPlatformConversionBridge {
+    @CalledByNative
+    private static SegmentSelectionResult createSegmentSelectionResult(
+            boolean isReady, int selectedSegment) {
+        SegmentId segment = SegmentId.forNumber(selectedSegment);
+        if (segment == null) segment = SegmentId.OPTIMIZATION_TARGET_UNKNOWN;
+        return new SegmentSelectionResult(isReady, segment);
+    }
+
+    @CalledByNative
+    private static OnDemandSegmentSelectionResult createOnDemandSegmentSelectionResult(
+            SegmentSelectionResult selectedSegment, TriggerContext triggerContext) {
+        return new OnDemandSegmentSelectionResult(selectedSegment, triggerContext);
+    }
+}
diff --git a/components/segmentation_platform/internal/android/java/src/org/chromium/components/segmentation_platform/SegmentationPlatformServiceImpl.java b/components/segmentation_platform/internal/android/java/src/org/chromium/components/segmentation_platform/SegmentationPlatformServiceImpl.java
index 2737e52a..2b74cfef 100644
--- a/components/segmentation_platform/internal/android/java/src/org/chromium/components/segmentation_platform/SegmentationPlatformServiceImpl.java
+++ b/components/segmentation_platform/internal/android/java/src/org/chromium/components/segmentation_platform/SegmentationPlatformServiceImpl.java
@@ -8,7 +8,6 @@
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.annotations.JNINamespace;
 import org.chromium.base.annotations.NativeMethods;
-import org.chromium.components.segmentation_platform.proto.SegmentationProto.SegmentId;
 
 /**
  * Java side of the JNI bridge between SegmentationPlatformServiceImpl in Java
@@ -40,17 +39,22 @@
                 mNativePtr, this, segmentationKey);
     }
 
-    @CalledByNative
-    private void clearNativePtr() {
-        mNativePtr = 0;
+    @Override
+    public int registerOnDemandSegmentSelectionCallback(
+            String segmentationKey, Callback<OnDemandSegmentSelectionResult> callback) {
+        return SegmentationPlatformServiceImplJni.get().registerOnDemandSegmentSelectionCallback(
+                mNativePtr, this, segmentationKey, callback);
+    }
+
+    @Override
+    public void unregisterOnDemandSegmentSelectionCallback(String segmentationKey, int callbackId) {
+        SegmentationPlatformServiceImplJni.get().unregisterOnDemandSegmentSelectionCallback(
+                mNativePtr, this, segmentationKey, callbackId);
     }
 
     @CalledByNative
-    private static SegmentSelectionResult createSegmentSelectionResult(
-            boolean isReady, int selectedSegment) {
-        SegmentId segment = SegmentId.forNumber(selectedSegment);
-        if (segment == null) segment = SegmentId.OPTIMIZATION_TARGET_UNKNOWN;
-        return new SegmentSelectionResult(isReady, segment);
+    private void clearNativePtr() {
+        mNativePtr = 0;
     }
 
     @NativeMethods
@@ -60,5 +64,11 @@
                 Callback<SegmentSelectionResult> callback);
         SegmentSelectionResult getCachedSegmentResult(long nativeSegmentationPlatformServiceAndroid,
                 SegmentationPlatformServiceImpl caller, String segmentationKey);
+        int registerOnDemandSegmentSelectionCallback(long nativeSegmentationPlatformServiceAndroid,
+                SegmentationPlatformServiceImpl caller, String segmentationKey,
+                Callback<OnDemandSegmentSelectionResult> callback);
+        void unregisterOnDemandSegmentSelectionCallback(
+                long nativeSegmentationPlatformServiceAndroid,
+                SegmentationPlatformServiceImpl caller, String segmentationKey, int callbackId);
     }
 }
diff --git a/components/segmentation_platform/internal/android/segmentation_platform_conversion_bridge.cc b/components/segmentation_platform/internal/android/segmentation_platform_conversion_bridge.cc
new file mode 100644
index 0000000..029aaa5
--- /dev/null
+++ b/components/segmentation_platform/internal/android/segmentation_platform_conversion_bridge.cc
@@ -0,0 +1,36 @@
+// Copyright 2022 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/segmentation_platform/internal/android/segmentation_platform_conversion_bridge.h"
+
+#include "components/segmentation_platform/internal/jni_headers/SegmentationPlatformConversionBridge_jni.h"
+#include "components/segmentation_platform/public/segment_selection_result.h"
+#include "components/segmentation_platform/public/trigger_context.h"
+
+namespace segmentation_platform {
+
+// static
+ScopedJavaLocalRef<jobject>
+SegmentationPlatformConversionBridge::CreateJavaSegmentSelectionResult(
+    JNIEnv* env,
+    const SegmentSelectionResult& result) {
+  int selected_segment = result.segment.has_value()
+                             ? result.segment.value()
+                             : proto::SegmentId::OPTIMIZATION_TARGET_UNKNOWN;
+  return Java_SegmentationPlatformConversionBridge_createSegmentSelectionResult(
+      env, result.is_ready, selected_segment);
+}
+
+// static
+ScopedJavaLocalRef<jobject>
+SegmentationPlatformConversionBridge::CreateJavaOnDemandSegmentSelectionResult(
+    JNIEnv* env,
+    const SegmentSelectionResult& result,
+    const TriggerContext& trigger_context) {
+  return Java_SegmentationPlatformConversionBridge_createOnDemandSegmentSelectionResult(
+      env, CreateJavaSegmentSelectionResult(env, result),
+      trigger_context.CreateJavaObject());
+}
+
+}  // namespace segmentation_platform
diff --git a/components/segmentation_platform/internal/android/segmentation_platform_conversion_bridge.h b/components/segmentation_platform/internal/android/segmentation_platform_conversion_bridge.h
new file mode 100644
index 0000000..fcc653f
--- /dev/null
+++ b/components/segmentation_platform/internal/android/segmentation_platform_conversion_bridge.h
@@ -0,0 +1,32 @@
+// Copyright 2022 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_SEGMENTATION_PLATFORM_INTERNAL_ANDROID_SEGMENTATION_PLATFORM_CONVERSION_BRIDGE_H_
+#define COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_ANDROID_SEGMENTATION_PLATFORM_CONVERSION_BRIDGE_H_
+
+#include "base/android/jni_android.h"
+#include "base/memory/raw_ptr.h"
+#include "components/segmentation_platform/public/segment_selection_result.h"
+#include "components/segmentation_platform/public/trigger_context.h"
+
+using base::android::ScopedJavaLocalRef;
+
+namespace segmentation_platform {
+
+// A helper class for creating Java objects required by the segmentation
+// platform from their C++ counterparts.
+class SegmentationPlatformConversionBridge {
+ public:
+  static ScopedJavaLocalRef<jobject> CreateJavaSegmentSelectionResult(
+      JNIEnv* env,
+      const SegmentSelectionResult& result);
+  static ScopedJavaLocalRef<jobject> CreateJavaOnDemandSegmentSelectionResult(
+      JNIEnv* env,
+      const SegmentSelectionResult& result,
+      const TriggerContext& trigger_context);
+};
+
+}  // namespace segmentation_platform
+
+#endif  // COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_ANDROID_SEGMENTATION_PLATFORM_CONVERSION_BRIDGE_H_
diff --git a/components/segmentation_platform/internal/android/segmentation_platform_service_android.cc b/components/segmentation_platform/internal/android/segmentation_platform_service_android.cc
index bad5ce9..24d1cf4 100644
--- a/components/segmentation_platform/internal/android/segmentation_platform_service_android.cc
+++ b/components/segmentation_platform/internal/android/segmentation_platform_service_android.cc
@@ -9,9 +9,11 @@
 #include "base/android/callback_android.h"
 #include "base/android/jni_string.h"
 #include "base/bind.h"
+#include "components/segmentation_platform/internal/android/segmentation_platform_conversion_bridge.h"
 #include "components/segmentation_platform/internal/jni_headers/SegmentationPlatformServiceImpl_jni.h"
 #include "components/segmentation_platform/public/segment_selection_result.h"
 #include "components/segmentation_platform/public/segmentation_platform_service.h"
+#include "components/segmentation_platform/public/trigger_context.h"
 
 using base::android::AttachCurrentThread;
 
@@ -21,21 +23,25 @@
 const char kSegmentationPlatformServiceBridgeKey[] =
     "segmentation_platform_service_bridge";
 
-ScopedJavaLocalRef<jobject> CreateJavaSegmentSelectionResult(
-    JNIEnv* env,
-    const SegmentSelectionResult& result) {
-  int selected_segment = result.segment.has_value()
-                             ? result.segment.value()
-                             : proto::SegmentId::OPTIMIZATION_TARGET_UNKNOWN;
-  return Java_SegmentationPlatformServiceImpl_createSegmentSelectionResult(
-      env, result.is_ready, selected_segment);
-}
-
 void RunGetSelectedSegmentCallback(const JavaRef<jobject>& j_callback,
                                    const SegmentSelectionResult& result) {
   JNIEnv* env = AttachCurrentThread();
-  RunObjectCallbackAndroid(j_callback,
-                           CreateJavaSegmentSelectionResult(env, result));
+  RunObjectCallbackAndroid(
+      j_callback,
+      SegmentationPlatformConversionBridge::CreateJavaSegmentSelectionResult(
+          env, result));
+}
+
+void RunOnDemandSegmentSelectionCallback(
+    const JavaRef<jobject>& j_callback,
+    const SegmentSelectionResult& result,
+    const TriggerContext& trigger_context) {
+  JNIEnv* env = AttachCurrentThread();
+  ScopedJavaLocalRef<jobject> j_on_demand_result =
+      SegmentationPlatformConversionBridge::
+          CreateJavaOnDemandSegmentSelectionResult(env, result,
+                                                   trigger_context);
+  RunObjectCallbackAndroid(j_callback, j_on_demand_result);
 }
 
 }  // namespace
@@ -89,11 +95,34 @@
     JNIEnv* env,
     const JavaParamRef<jobject>& jcaller,
     const JavaParamRef<jstring>& j_segmentation_key) {
-  return CreateJavaSegmentSelectionResult(
+  return SegmentationPlatformConversionBridge::CreateJavaSegmentSelectionResult(
       env, segmentation_platform_service_->GetCachedSegmentResult(
                ConvertJavaStringToUTF8(env, j_segmentation_key)));
 }
 
+int SegmentationPlatformServiceAndroid::
+    RegisterOnDemandSegmentSelectionCallback(
+        JNIEnv* env,
+        const JavaParamRef<jobject>& jcaller,
+        const JavaParamRef<jstring>& j_segmentation_key,
+        const JavaParamRef<jobject>& jcallback) {
+  return segmentation_platform_service_
+      ->RegisterOnDemandSegmentSelectionCallback(
+          ConvertJavaStringToUTF8(env, j_segmentation_key),
+          base::BindRepeating(&RunOnDemandSegmentSelectionCallback,
+                              ScopedJavaGlobalRef<jobject>(jcallback)));
+}
+
+void SegmentationPlatformServiceAndroid::
+    UnregisterOnDemandSegmentSelectionCallback(
+        JNIEnv* env,
+        const JavaParamRef<jobject>& jcaller,
+        const JavaParamRef<jstring>& j_segmentation_key,
+        jint j_callback_id) {
+  segmentation_platform_service_->UnregisterOnDemandSegmentSelectionCallback(
+      (int)j_callback_id, ConvertJavaStringToUTF8(env, j_segmentation_key));
+}
+
 ScopedJavaLocalRef<jobject>
 SegmentationPlatformServiceAndroid::GetJavaObject() {
   return ScopedJavaLocalRef<jobject>(java_obj_);
diff --git a/components/segmentation_platform/internal/android/segmentation_platform_service_android.h b/components/segmentation_platform/internal/android/segmentation_platform_service_android.h
index 86ad117a..7653f54 100644
--- a/components/segmentation_platform/internal/android/segmentation_platform_service_android.h
+++ b/components/segmentation_platform/internal/android/segmentation_platform_service_android.h
@@ -35,6 +35,18 @@
       const JavaParamRef<jobject>& jcaller,
       const JavaParamRef<jstring>& j_segmentation_key);
 
+  int RegisterOnDemandSegmentSelectionCallback(
+      JNIEnv* env,
+      const JavaParamRef<jobject>& jcaller,
+      const JavaParamRef<jstring>& j_segmentation_key,
+      const JavaParamRef<jobject>& jcallback);
+
+  void UnregisterOnDemandSegmentSelectionCallback(
+      JNIEnv* env,
+      const JavaParamRef<jobject>& jcaller,
+      const JavaParamRef<jstring>& j_segmentation_key,
+      jint j_callback_id);
+
   ScopedJavaLocalRef<jobject> GetJavaObject();
 
  private:
diff --git a/components/segmentation_platform/internal/dummy_segmentation_platform_service.cc b/components/segmentation_platform/internal/dummy_segmentation_platform_service.cc
index 51579d9..743b52c3 100644
--- a/components/segmentation_platform/internal/dummy_segmentation_platform_service.cc
+++ b/components/segmentation_platform/internal/dummy_segmentation_platform_service.cc
@@ -9,6 +9,7 @@
 #include "base/threading/sequenced_task_runner_handle.h"
 #include "components/segmentation_platform/internal/stats.h"
 #include "components/segmentation_platform/public/segment_selection_result.h"
+#include "components/segmentation_platform/public/trigger_context.h"
 
 namespace segmentation_platform {
 
@@ -31,6 +32,21 @@
   return SegmentSelectionResult();
 }
 
+int DummySegmentationPlatformService::RegisterOnDemandSegmentSelectionCallback(
+    const std::string& segmentation_key,
+    const OnDemandSegmentSelectionCallback& callback) {
+  return 0;
+}
+
+void DummySegmentationPlatformService::
+    UnregisterOnDemandSegmentSelectionCallback(
+        int callback_id,
+        const std::string& segmentation_key) {}
+
+void DummySegmentationPlatformService::OnTrigger(
+    TriggerType trigger,
+    const TriggerContext& trigger_context) {}
+
 void DummySegmentationPlatformService::EnableMetrics(
     bool signal_collection_allowed) {}
 
diff --git a/components/segmentation_platform/internal/dummy_segmentation_platform_service.h b/components/segmentation_platform/internal/dummy_segmentation_platform_service.h
index b8c2ee65..ac81913 100644
--- a/components/segmentation_platform/internal/dummy_segmentation_platform_service.h
+++ b/components/segmentation_platform/internal/dummy_segmentation_platform_service.h
@@ -29,6 +29,14 @@
                           SegmentSelectionCallback callback) override;
   SegmentSelectionResult GetCachedSegmentResult(
       const std::string& segmentation_key) override;
+  int RegisterOnDemandSegmentSelectionCallback(
+      const std::string& segmentation_key,
+      const OnDemandSegmentSelectionCallback& callback) override;
+  void UnregisterOnDemandSegmentSelectionCallback(
+      int callback_id,
+      const std::string& segmentation_key) override;
+  void OnTrigger(TriggerType trigger,
+                 const TriggerContext& trigger_context) override;
   void EnableMetrics(bool signal_collection_allowed) override;
   bool IsPlatformInitialized() override;
 };
diff --git a/components/segmentation_platform/internal/segmentation_platform_service_impl.cc b/components/segmentation_platform/internal/segmentation_platform_service_impl.cc
index 09022c0..bc503964 100644
--- a/components/segmentation_platform/internal/segmentation_platform_service_impl.cc
+++ b/components/segmentation_platform/internal/segmentation_platform_service_impl.cc
@@ -29,6 +29,7 @@
 #include "components/segmentation_platform/public/config.h"
 #include "components/segmentation_platform/public/field_trial_register.h"
 #include "components/segmentation_platform/public/model_provider.h"
+#include "components/segmentation_platform/public/trigger_context.h"
 
 namespace segmentation_platform {
 namespace {
@@ -135,6 +136,21 @@
   return selector->GetCachedSegmentResult();
 }
 
+int SegmentationPlatformServiceImpl::RegisterOnDemandSegmentSelectionCallback(
+    const std::string& segmentation_key,
+    const OnDemandSegmentSelectionCallback& callback) {
+  return 0;
+}
+
+void SegmentationPlatformServiceImpl::
+    UnregisterOnDemandSegmentSelectionCallback(
+        int callback_id,
+        const std::string& segmentation_key) {}
+
+void SegmentationPlatformServiceImpl::OnTrigger(
+    TriggerType trigger,
+    const TriggerContext& trigger_context) {}
+
 void SegmentationPlatformServiceImpl::EnableMetrics(
     bool signal_collection_allowed) {
   signal_handler_.EnableMetrics(signal_collection_allowed);
diff --git a/components/segmentation_platform/internal/segmentation_platform_service_impl.h b/components/segmentation_platform/internal/segmentation_platform_service_impl.h
index b7aa877..626fce5 100644
--- a/components/segmentation_platform/internal/segmentation_platform_service_impl.h
+++ b/components/segmentation_platform/internal/segmentation_platform_service_impl.h
@@ -92,6 +92,14 @@
                           SegmentSelectionCallback callback) override;
   SegmentSelectionResult GetCachedSegmentResult(
       const std::string& segmentation_key) override;
+  int RegisterOnDemandSegmentSelectionCallback(
+      const std::string& segmentation_key,
+      const OnDemandSegmentSelectionCallback& callback) override;
+  void UnregisterOnDemandSegmentSelectionCallback(
+      int callback_id,
+      const std::string& segmentation_key) override;
+  void OnTrigger(TriggerType trigger,
+                 const TriggerContext& trigger_context) override;
   void EnableMetrics(bool signal_collection_allowed) override;
   ServiceProxy* GetServiceProxy() override;
   bool IsPlatformInitialized() override;
diff --git a/components/segmentation_platform/public/BUILD.gn b/components/segmentation_platform/public/BUILD.gn
index d5aa36e..859a5d68 100644
--- a/components/segmentation_platform/public/BUILD.gn
+++ b/components/segmentation_platform/public/BUILD.gn
@@ -23,6 +23,9 @@
     "segmentation_platform_service.h",
     "service_proxy.cc",
     "service_proxy.h",
+    "trigger.h",
+    "trigger_context.cc",
+    "trigger_context.h",
   ]
 
   public_deps = [ "//components/segmentation_platform/public/proto" ]
@@ -31,6 +34,10 @@
     "//base",
     "//components/keyed_service/core",
   ]
+
+  if (is_android) {
+    deps += [ ":jni_headers" ]
+  }
 }
 
 source_set("unit_tests") {
@@ -49,12 +56,26 @@
 if (is_android) {
   android_library("public_java") {
     sources = [
+      "android/java/src/org/chromium/components/segmentation_platform/OnDemandSegmentSelectionResult.java",
       "android/java/src/org/chromium/components/segmentation_platform/SegmentSelectionResult.java",
       "android/java/src/org/chromium/components/segmentation_platform/SegmentationPlatformService.java",
+      "android/java/src/org/chromium/components/segmentation_platform/TriggerContext.java",
     ]
 
-    deps = [ "//base:base_java" ]
+    deps = [
+      "//base:base_java",
+      "//base:jni_java",
+      "//build/android:build_java",
+    ]
+
+    annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ]
 
     public_deps = [ "//components/segmentation_platform/public/proto:segmentation_platform_proto_java" ]
   }
+
+  generate_jni("jni_headers") {
+    visibility = [ ":*" ]
+
+    sources = [ "android/java/src/org/chromium/components/segmentation_platform/TriggerContext.java" ]
+  }
 }
diff --git a/components/segmentation_platform/public/android/java/src/org/chromium/components/segmentation_platform/OnDemandSegmentSelectionResult.java b/components/segmentation_platform/public/android/java/src/org/chromium/components/segmentation_platform/OnDemandSegmentSelectionResult.java
new file mode 100644
index 0000000..3537bb0
--- /dev/null
+++ b/components/segmentation_platform/public/android/java/src/org/chromium/components/segmentation_platform/OnDemandSegmentSelectionResult.java
@@ -0,0 +1,21 @@
+// Copyright 2022 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.components.segmentation_platform;
+
+/**
+ * Convenient wrapper containing results for on-demand segment selection along with the trigger
+ * context required by the UI layer.
+ */
+public class OnDemandSegmentSelectionResult {
+    public final SegmentSelectionResult segmentSelectionResult;
+    public final TriggerContext triggerContext;
+
+    /** Constructor */
+    public OnDemandSegmentSelectionResult(
+            SegmentSelectionResult segmentSelectionResult, TriggerContext triggerContext) {
+        this.segmentSelectionResult = segmentSelectionResult;
+        this.triggerContext = triggerContext;
+    }
+}
diff --git a/components/segmentation_platform/public/android/java/src/org/chromium/components/segmentation_platform/SegmentationPlatformService.java b/components/segmentation_platform/public/android/java/src/org/chromium/components/segmentation_platform/SegmentationPlatformService.java
index c300ec6..46f0dd5 100644
--- a/components/segmentation_platform/public/android/java/src/org/chromium/components/segmentation_platform/SegmentationPlatformService.java
+++ b/components/segmentation_platform/public/android/java/src/org/chromium/components/segmentation_platform/SegmentationPlatformService.java
@@ -26,4 +26,21 @@
      * @return The result of segment selection
      */
     SegmentSelectionResult getCachedSegmentResult(String segmentationKey);
-}
\ No newline at end of file
+
+    /**
+     * Called to register a callback to be invoked after a segment selection. Only used for
+     * on-demand segment selection.
+     * @param segmentationKey The key to be used to distinguish between different clients.
+     * @param callback The callback to be invoked after a segment selection is computed.
+     * @return A callback ID to be used when unregistering.
+     */
+    int registerOnDemandSegmentSelectionCallback(
+            String segmentationKey, Callback<OnDemandSegmentSelectionResult> callback);
+
+    /**
+     * Called to unregister a previously registered callback for segment selection result.
+     * @param segmentationKey The key to be used to distinguish between different clients.
+     * @param callbackId The associated callback ID obtained when registering.
+     */
+    void unregisterOnDemandSegmentSelectionCallback(String segmentationKey, int callbackId);
+}
diff --git a/components/segmentation_platform/public/android/java/src/org/chromium/components/segmentation_platform/TriggerContext.java b/components/segmentation_platform/public/android/java/src/org/chromium/components/segmentation_platform/TriggerContext.java
new file mode 100644
index 0000000..1ad07e2
--- /dev/null
+++ b/components/segmentation_platform/public/android/java/src/org/chromium/components/segmentation_platform/TriggerContext.java
@@ -0,0 +1,20 @@
+// Copyright 2022 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.components.segmentation_platform;
+
+import org.chromium.base.annotations.CalledByNative;
+
+/**
+ * Java representation for the native TriggerContext. Contains contextual information for a trigger
+ * event.
+ */
+public class TriggerContext {
+    @CalledByNative
+    private static TriggerContext createTriggerContext() {
+        return new TriggerContext();
+    }
+
+    public TriggerContext() {}
+}
diff --git a/components/segmentation_platform/public/segmentation_platform_service.h b/components/segmentation_platform/public/segmentation_platform_service.h
index 36669f159..38ee2aa 100644
--- a/components/segmentation_platform/public/segmentation_platform_service.h
+++ b/components/segmentation_platform/public/segmentation_platform_service.h
@@ -12,6 +12,7 @@
 #include "base/supports_user_data.h"
 #include "build/build_config.h"
 #include "components/keyed_service/core/keyed_service.h"
+#include "components/segmentation_platform/public/trigger.h"
 
 #if BUILDFLAG(IS_ANDROID)
 #include "base/android/jni_android.h"
@@ -22,6 +23,7 @@
 namespace segmentation_platform {
 class ServiceProxy;
 struct SegmentSelectionResult;
+struct TriggerContext;
 
 // The core class of segmentation platform that integrates all the required
 // pieces on the client side.
@@ -64,6 +66,24 @@
   virtual SegmentSelectionResult GetCachedSegmentResult(
       const std::string& segmentation_key) = 0;
 
+  // Called to register a callback that will be invoked on segment selection
+  // on-demand. Returns a callback ID that can be used for unregister.
+  using OnDemandSegmentSelectionCallback =
+      base::RepeatingCallback<void(const SegmentSelectionResult&,
+                                   const TriggerContext&)>;
+  virtual int RegisterOnDemandSegmentSelectionCallback(
+      const std::string& segmentation_key,
+      const OnDemandSegmentSelectionCallback& callback) = 0;
+
+  // Called to unregister the callback with the given callback_id.
+  virtual void UnregisterOnDemandSegmentSelectionCallback(
+      int callback_id,
+      const std::string& segmentation_key) = 0;
+
+  // Called when a trigger event happens.
+  virtual void OnTrigger(TriggerType trigger,
+                         const TriggerContext& trigger_context) = 0;
+
   // Called to enable or disable metrics collection. Must be explicitly called
   // on startup.
   virtual void EnableMetrics(bool signal_collection_allowed) = 0;
diff --git a/components/segmentation_platform/public/trigger.h b/components/segmentation_platform/public/trigger.h
new file mode 100644
index 0000000..2fdfebb
--- /dev/null
+++ b/components/segmentation_platform/public/trigger.h
@@ -0,0 +1,19 @@
+// Copyright 2022 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_SEGMENTATION_PLATFORM_PUBLIC_TRIGGER_H_
+#define COMPONENTS_SEGMENTATION_PLATFORM_PUBLIC_TRIGGER_H_
+
+namespace segmentation_platform {
+
+// Various trigger events that drive on-demand model execution.
+enum class TriggerType {
+  kNone = 0,
+  kPageLoad = 1,
+  kMaxValue = kPageLoad,
+};
+
+}  // namespace segmentation_platform
+
+#endif  // COMPONENTS_SEGMENTATION_PLATFORM_PUBLIC_TRIGGER_H_
diff --git a/components/segmentation_platform/public/trigger_context.cc b/components/segmentation_platform/public/trigger_context.cc
new file mode 100644
index 0000000..59c9735
--- /dev/null
+++ b/components/segmentation_platform/public/trigger_context.cc
@@ -0,0 +1,28 @@
+// Copyright 2022 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/segmentation_platform/public/trigger_context.h"
+
+#include "build/build_config.h"
+
+#if BUILDFLAG(IS_ANDROID)
+#include "base/android/jni_android.h"
+#include "components/segmentation_platform/public/jni_headers/TriggerContext_jni.h"
+#endif  // BUILDFLAG(IS_ANDROID)
+
+namespace segmentation_platform {
+
+TriggerContext::TriggerContext() = default;
+
+TriggerContext::~TriggerContext() = default;
+
+#if BUILDFLAG(IS_ANDROID)
+base::android::ScopedJavaLocalRef<jobject> TriggerContext::CreateJavaObject()
+    const {
+  JNIEnv* env = base::android::AttachCurrentThread();
+  return Java_TriggerContext_createTriggerContext(env);
+}
+#endif  // BUILDFLAG(IS_ANDROID)
+
+}  // namespace segmentation_platform
diff --git a/components/segmentation_platform/public/trigger_context.h b/components/segmentation_platform/public/trigger_context.h
new file mode 100644
index 0000000..c45343b
--- /dev/null
+++ b/components/segmentation_platform/public/trigger_context.h
@@ -0,0 +1,31 @@
+// Copyright 2022 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_SEGMENTATION_PLATFORM_PUBLIC_TRIGGER_CONTEXT_H_
+#define COMPONENTS_SEGMENTATION_PLATFORM_PUBLIC_TRIGGER_CONTEXT_H_
+
+#include "base/memory/raw_ptr.h"
+#include "build/build_config.h"
+
+#if BUILDFLAG(IS_ANDROID)
+#include "base/android/jni_android.h"
+#endif  // BUILDFLAG(IS_ANDROID)
+
+namespace segmentation_platform {
+
+// Contains contextual information for a trigger event.
+struct TriggerContext {
+ public:
+  TriggerContext();
+  virtual ~TriggerContext();
+
+#if BUILDFLAG(IS_ANDROID)
+  // Returns a Java object representing the TriggerContext.
+  virtual base::android::ScopedJavaLocalRef<jobject> CreateJavaObject() const;
+#endif  // BUILDFLAG(IS_ANDROID)
+};
+
+}  // namespace segmentation_platform
+
+#endif  // COMPONENTS_SEGMENTATION_PLATFORM_PUBLIC_TRIGGER_CONTEXT_H_
diff --git a/components/url_rewrite/browser/url_request_rewrite_rules_manager.cc b/components/url_rewrite/browser/url_request_rewrite_rules_manager.cc
index 2bd5b1d..fd24292a 100644
--- a/components/url_rewrite/browser/url_request_rewrite_rules_manager.cc
+++ b/components/url_rewrite/browser/url_request_rewrite_rules_manager.cc
@@ -101,7 +101,7 @@
 
 void UrlRequestRewriteRulesManager::Updater::MaybeRegisterExistingRenderFrame(
     content::RenderFrameHost* render_frame_host) {
-  if (render_frame_host->IsRenderFrameCreated()) {
+  if (render_frame_host->IsRenderFrameLive()) {
     // Call RenderFrameCreated() for frames that were created before this
     // observer started observing this WebContents.
     RenderFrameCreated(render_frame_host);
diff --git a/content/browser/accessibility/browser_accessibility_android.cc b/content/browser/accessibility/browser_accessibility_android.cc
index 29a058e7..5fa2ddb 100644
--- a/content/browser/accessibility/browser_accessibility_android.cc
+++ b/content/browser/accessibility/browser_accessibility_android.cc
@@ -481,7 +481,7 @@
     // On Android, contenteditable needs to be handled the same as any
     // other text field.
     role = ax::mojom::Role::kTextField;
-  } else if (ui::IsAndroidTextViewCandidate(role) && HasOnlyTextChildren()) {
+  } else if (IsAndroidTextView()) {
     // On Android, we want to report some extra nodes as TextViews. For example,
     // a <div> that only contains text, or a <p> that only contains text.
     role = ax::mojom::Role::kStaticText;
@@ -490,6 +490,10 @@
   return ui::AXRoleToAndroidClassName(role, PlatformGetParent() != nullptr);
 }
 
+bool BrowserAccessibilityAndroid::IsAndroidTextView() const {
+  return ui::IsAndroidTextViewCandidate(GetRole()) && HasOnlyTextChildren();
+}
+
 bool BrowserAccessibilityAndroid::IsChildOfLeaf() const {
   BrowserAccessibility* ancestor = InternalGetParent();
 
@@ -1924,19 +1928,6 @@
   auto* manager =
       static_cast<BrowserAccessibilityManagerAndroid*>(this->manager());
   manager->ClearNodeInfoCacheForGivenId(unique_id());
-
-  // For any nodes that are the children of a leaf, we also want to invalidate
-  // the cache for the ancestry chain up until the first non-leaf node.
-  if (IsChildOfLeaf()) {
-    BrowserAccessibilityAndroid* parent =
-        static_cast<BrowserAccessibilityAndroid*>(PlatformGetParent());
-
-    while (parent != nullptr && (parent->IsChildOfLeaf() || parent->IsLeaf())) {
-      manager->ClearNodeInfoCacheForGivenId(parent->unique_id());
-      parent = static_cast<BrowserAccessibilityAndroid*>(
-          parent->PlatformGetParent());
-    }
-  }
 }
 
 int BrowserAccessibilityAndroid::CountChildrenWithRole(
diff --git a/content/browser/accessibility/browser_accessibility_android.h b/content/browser/accessibility/browser_accessibility_android.h
index 3441934..372fc00 100644
--- a/content/browser/accessibility/browser_accessibility_android.h
+++ b/content/browser/accessibility/browser_accessibility_android.h
@@ -37,6 +37,7 @@
   std::u16string GetLocalizedStringForImageAnnotationStatus(
       ax::mojom::ImageAnnotationStatus status) const override;
 
+  bool IsAndroidTextView() const;
   bool IsCheckable() const;
   bool IsChecked() const;
   bool IsClickable() const override;
diff --git a/content/browser/accessibility/browser_accessibility_manager_android.cc b/content/browser/accessibility/browser_accessibility_manager_android.cc
index 7db2cbd..357ee15 100644
--- a/content/browser/accessibility/browser_accessibility_manager_android.cc
+++ b/content/browser/accessibility/browser_accessibility_manager_android.cc
@@ -263,6 +263,17 @@
       wcax->AnnounceLiveRegionText(text);
       break;
     }
+    case ui::AXEventGenerator::Event::NAME_CHANGED: {
+      // Clear node from cache whenever the name changes to ensure fresh data.
+      wcax->ClearNodeInfoCacheForGivenId(android_node->unique_id());
+
+      // If this is a simple text element, also send an event to the framework.
+      if (ui::IsText(android_node->GetRole()) ||
+          android_node->IsAndroidTextView()) {
+        wcax->HandleTextContentChanged(android_node->unique_id());
+      }
+      break;
+    }
     case ui::AXEventGenerator::Event::RANGE_VALUE_CHANGED:
       DCHECK(android_node->GetData().IsRangeValueSupported());
       if (android_node->IsSlider())
@@ -336,7 +347,6 @@
     case ui::AXEventGenerator::Event::MENU_ITEM_SELECTED:
     case ui::AXEventGenerator::Event::MULTILINE_STATE_CHANGED:
     case ui::AXEventGenerator::Event::MULTISELECTABLE_STATE_CHANGED:
-    case ui::AXEventGenerator::Event::NAME_CHANGED:
     case ui::AXEventGenerator::Event::OBJECT_ATTRIBUTE_CHANGED:
     case ui::AXEventGenerator::Event::OTHER_ATTRIBUTE_CHANGED:
     case ui::AXEventGenerator::Event::PARENT_CHANGED:
diff --git a/content/browser/accessibility/web_contents_accessibility_android.cc b/content/browser/accessibility/web_contents_accessibility_android.cc
index 23bc1fa..3cca1fc 100644
--- a/content/browser/accessibility/web_contents_accessibility_android.cc
+++ b/content/browser/accessibility/web_contents_accessibility_android.cc
@@ -445,6 +445,16 @@
       env, obj, base::android::ConvertUTF16ToJavaString(env, text));
 }
 
+void WebContentsAccessibilityAndroid::HandleTextContentChanged(
+    int32_t unique_id) {
+  JNIEnv* env = AttachCurrentThread();
+  ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
+  if (obj.is_null())
+    return;
+  Java_WebContentsAccessibilityImpl_handleTextContentChanged(env, obj,
+                                                             unique_id);
+}
+
 void WebContentsAccessibilityAndroid::HandleTextSelectionChanged(
     int32_t unique_id) {
   JNIEnv* env = AttachCurrentThread();
diff --git a/content/browser/accessibility/web_contents_accessibility_android.h b/content/browser/accessibility/web_contents_accessibility_android.h
index 905910cf7..47915ce8 100644
--- a/content/browser/accessibility/web_contents_accessibility_android.h
+++ b/content/browser/accessibility/web_contents_accessibility_android.h
@@ -385,6 +385,7 @@
   void HandleScrolledToAnchor(int32_t unique_id);
   void HandleDialogModalOpened(int32_t unique_id);
   void AnnounceLiveRegionText(const std::u16string& text);
+  void HandleTextContentChanged(int32_t unique_id);
   void HandleTextSelectionChanged(int32_t unique_id);
   void HandleEditableTextChanged(int32_t unique_id);
   void HandleSliderChanged(int32_t unique_id);
diff --git a/content/browser/child_process_launcher_helper_linux.cc b/content/browser/child_process_launcher_helper_linux.cc
index b2b29e7..16d838b7 100644
--- a/content/browser/child_process_launcher_helper_linux.cc
+++ b/content/browser/child_process_launcher_helper_linux.cc
@@ -73,7 +73,7 @@
     bool* is_synchronous_launch,
     int* launch_result) {
   *is_synchronous_launch = true;
-
+  Process process;
   ZygoteHandle zygote_handle =
       base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kNoZygote)
           ? nullptr
@@ -98,16 +98,20 @@
     }
 #endif
 
-    Process process;
     process.process = base::Process(handle);
     process.zygote = zygote_handle;
-    return process;
+  } else {
+    process.process = base::LaunchProcess(*command_line(), options);
+    *launch_result = process.process.IsValid() ? LAUNCH_RESULT_SUCCESS
+                                               : LAUNCH_RESULT_FAILURE;
   }
 
-  Process process;
-  process.process = base::LaunchProcess(*command_line(), options);
-  *launch_result = process.process.IsValid() ? LAUNCH_RESULT_SUCCESS
-                                             : LAUNCH_RESULT_FAILURE;
+#if BUILDFLAG(IS_CHROMEOS)
+  if (GetProcessType() == switches::kRendererProcess) {
+    process.process.InitializePriority();
+  }
+#endif
+
   return process;
 }
 
diff --git a/content/browser/data_decoder_browsertest.cc b/content/browser/data_decoder_browsertest.cc
index c4767ecb..5ba0d868 100644
--- a/content/browser/data_decoder_browsertest.cc
+++ b/content/browser/data_decoder_browsertest.cc
@@ -5,6 +5,7 @@
 #include <limits>
 
 #include "base/base_paths.h"
+#include "base/callback_helpers.h"
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
 #include "base/path_service.h"
@@ -253,4 +254,80 @@
   EXPECT_LE(overhead_estimate, end_to_end_duration_estimate);
 }
 
+IN_PROC_BROWSER_TEST_F(DataDecoderBrowserTest,
+                       NoCallbackAfterDestruction_Json) {
+  base::RunLoop run_loop;
+
+  auto decoder = std::make_unique<data_decoder::DataDecoder>();
+  auto* raw_decoder = decoder.get();
+
+  // Android's in-process parser can complete synchronously, so queue the
+  // delete task first unlike in the other tests.
+  base::SequencedTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE,
+                                                     std::move(decoder));
+
+  bool got_callback = false;
+  raw_decoder->ParseJson(
+      "[1, 2, 3]",
+      base::BindOnce(
+          [](bool* got_callback, base::ScopedClosureRunner quit_closure_runner,
+             data_decoder::DataDecoder::ValueOrError result) {
+            *got_callback = true;
+          },
+          // Pass the quit closure as a ScopedClosureRunner, so that the loop
+          // is quit if the callback is destroyed un-run or after it runs.
+          &got_callback, base::ScopedClosureRunner(run_loop.QuitClosure())));
+
+  run_loop.Run();
+
+  EXPECT_FALSE(got_callback);
+}
+
+IN_PROC_BROWSER_TEST_F(DataDecoderBrowserTest, NoCallbackAfterDestruction_Xml) {
+  base::RunLoop run_loop;
+
+  auto decoder = std::make_unique<data_decoder::DataDecoder>();
+  bool got_callback = false;
+  decoder->ParseXml(
+      "<marquee>hello world</marquee>",
+      data_decoder::mojom::XmlParser::WhitespaceBehavior::kIgnore,
+      base::BindOnce(
+          [](bool* got_callback, base::ScopedClosureRunner quit_closure_runner,
+             data_decoder::DataDecoder::ValueOrError result) {
+            *got_callback = true;
+          },
+          // Pass the quit closure as a ScopedClosureRunner, so that the loop
+          // is quit if the callback is destroyed un-run or after it runs.
+          &got_callback, base::ScopedClosureRunner(run_loop.QuitClosure())));
+
+  base::SequencedTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE,
+                                                     std::move(decoder));
+  run_loop.Run();
+
+  EXPECT_FALSE(got_callback);
+}
+
+IN_PROC_BROWSER_TEST_F(DataDecoderBrowserTest,
+                       NoCallbackAfterDestruction_Gzip) {
+  base::RunLoop run_loop;
+
+  auto decoder = std::make_unique<data_decoder::DataDecoder>();
+  bool got_callback = false;
+  decoder->GzipCompress(
+      {{0x1, 0x1, 0x1, 0x1, 0x1, 0x1}},
+      base::BindOnce(
+          [](bool* got_callback, base::ScopedClosureRunner quit_closure_runner,
+             data_decoder::DataDecoder::ResultOrError<mojo_base::BigBuffer>
+                 result) { *got_callback = true; },
+          // Pass the quit closure as a ScopedClosureRunner, so that the loop
+          // is quit if the callback is destroyed un-run or after it runs.
+          &got_callback, base::ScopedClosureRunner(run_loop.QuitClosure())));
+
+  base::SequencedTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE,
+                                                     std::move(decoder));
+  run_loop.Run();
+
+  EXPECT_FALSE(got_callback);
+}
+
 }  // namespace content
diff --git a/content/browser/find_request_manager.cc b/content/browser/find_request_manager.cc
index 1c788d9..e9d965a 100644
--- a/content/browser/find_request_manager.cc
+++ b/content/browser/find_request_manager.cc
@@ -233,7 +233,7 @@
 
     manager_->RemoveFrame(rfh);
     // Make sure RenderFrameDeleted will be called to clean up
-    DCHECK(rfh->IsRenderFrameCreated());
+    DCHECK(rfh->IsRenderFrameLive());
 
     if (IsFindInPageDisabled(rfh))
       return;
diff --git a/content/browser/interest_group/ad_auction_service_impl.cc b/content/browser/interest_group/ad_auction_service_impl.cc
index cc0e6dc8..a3ff007 100644
--- a/content/browser/interest_group/ad_auction_service_impl.cc
+++ b/content/browser/interest_group/ad_auction_service_impl.cc
@@ -39,6 +39,7 @@
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/blink/public/common/features.h"
 #include "third_party/blink/public/common/fenced_frame/fenced_frame_utils.h"
+#include "third_party/blink/public/common/interest_group/auction_config.h"
 #include "third_party/blink/public/common/interest_group/interest_group.h"
 #include "url/gurl.h"
 #include "url/origin.h"
@@ -71,50 +72,6 @@
   return true;
 }
 
-bool IsAuctionValid(const blink::mojom::AuctionAdConfig& config,
-                    bool is_top_level_auction) {
-  // The seller origin has to be HTTPS.
-  if (config.seller.scheme() != url::kHttpsScheme)
-    return false;
-
-  // Opaque Origins have empty schemes.
-  DCHECK(!config.seller.opaque());
-
-  // `decision_logic_url` and, if present, `trusted_scoring_signals_url` must
-  // share the seller's origin.
-  if (url::SchemeHostPort(config.decision_logic_url) !=
-          config.seller.GetTupleOrPrecursorTupleIfOpaque() ||
-      (config.trusted_scoring_signals_url &&
-       url::SchemeHostPort(*config.trusted_scoring_signals_url) !=
-           config.seller.GetTupleOrPrecursorTupleIfOpaque())) {
-    return false;
-  }
-
-  const auto& non_shared_params = config.auction_ad_config_non_shared_params;
-  // This isn't marked as optional in the Mojo struct, so Mojo should make sure
-  // it is non-null.
-  DCHECK(non_shared_params);
-
-  // All interest group owners must be HTTPS.
-  if (non_shared_params->interest_group_buyers) {
-    for (const url::Origin& buyer : *non_shared_params->interest_group_buyers) {
-      if (buyer.scheme() != url::kHttpsScheme)
-        return false;
-    }
-  }
-
-  for (const auto& component_auction :
-       config.auction_ad_config_non_shared_params->component_auctions) {
-    // Component auctions may not have their own nested component auctions.
-    if (!is_top_level_auction)
-      return false;
-    if (!IsAuctionValid(*component_auction, /*is_top_level_auction=*/false))
-      return false;
-  }
-
-  return true;
-}
-
 }  // namespace
 
 // static
@@ -240,7 +197,7 @@
       origin(), GetClientSecurityState());
 }
 
-void AdAuctionServiceImpl::RunAdAuction(blink::mojom::AuctionAdConfigPtr config,
+void AdAuctionServiceImpl::RunAdAuction(const blink::AuctionConfig& config,
                                         RunAdAuctionCallback callback) {
   // If the run ad auction API is not allowed for this context by Permissions
   // Policy, do nothing
@@ -249,10 +206,6 @@
     ReportBadMessageAndDeleteThis("Unexpected request");
     return;
   }
-  if (!IsAuctionValid(*config, /*is_top_level_auction=*/true)) {
-    std::move(callback).Run(absl::nullopt);
-    return;
-  }
 
   auto* auction_result_metrics = AdAuctionResultMetrics::GetOrCreateForPage(
       render_frame_host()->GetPage());
@@ -356,13 +309,8 @@
 }
 
 void AdAuctionServiceImpl::FinalizeAd(const std::string& ads_guid,
-                                      ::blink::mojom::AuctionAdConfigPtr config,
+                                      const blink::AuctionConfig& config,
                                       FinalizeAdCallback callback) {
-  if (!IsAuctionValid(*config, /*is_top_level_auction=*/true)) {
-    ReportBadMessageAndDeleteThis("Invalid auction");
-    return;
-  }
-
   if (ads_guid.empty()) {
     ReportBadMessageAndDeleteThis("GUID empty");
     return;
diff --git a/content/browser/interest_group/ad_auction_service_impl.h b/content/browser/interest_group/ad_auction_service_impl.h
index adfc465..4358a6a 100644
--- a/content/browser/interest_group/ad_auction_service_impl.h
+++ b/content/browser/interest_group/ad_auction_service_impl.h
@@ -20,6 +20,7 @@
 #include "services/network/public/cpp/wrapper_shared_url_loader_factory.h"
 #include "services/network/public/mojom/client_security_state.mojom.h"
 #include "services/network/public/mojom/url_loader_factory.mojom.h"
+#include "third_party/blink/public/common/interest_group/auction_config.h"
 #include "third_party/blink/public/mojom/interest_group/ad_auction_service.mojom.h"
 #include "third_party/blink/public/mojom/interest_group/interest_group_types.mojom-forward.h"
 #include "third_party/blink/public/mojom/parakeet/ad_request.mojom.h"
@@ -51,7 +52,7 @@
                           LeaveInterestGroupCallback callback) override;
   void LeaveInterestGroupForDocument() override;
   void UpdateAdInterestGroups() override;
-  void RunAdAuction(blink::mojom::AuctionAdConfigPtr config,
+  void RunAdAuction(const blink::AuctionConfig& config,
                     RunAdAuctionCallback callback) override;
   void DeprecatedGetURLFromURN(
       const GURL& urn_url,
@@ -63,7 +64,7 @@
   void CreateAdRequest(blink::mojom::AdRequestConfigPtr config,
                        CreateAdRequestCallback callback) override;
   void FinalizeAd(const std::string& ads_guid,
-                  blink::mojom::AuctionAdConfigPtr config,
+                  const blink::AuctionConfig& config,
                   FinalizeAdCallback callback) override;
 
   scoped_refptr<network::WrapperSharedURLLoaderFactory>
diff --git a/content/browser/interest_group/ad_auction_service_impl_unittest.cc b/content/browser/interest_group/ad_auction_service_impl_unittest.cc
index 3079fb3..f87601d 100644
--- a/content/browser/interest_group/ad_auction_service_impl_unittest.cc
+++ b/content/browser/interest_group/ad_auction_service_impl_unittest.cc
@@ -644,7 +644,7 @@
   // frame `rfh`. Returns the result of the auction, which is either a URL to
   // the winning ad, or absl::nullopt if no ad won the auction.
   absl::optional<GURL> RunAdAuctionAndFlushForFrame(
-      blink::mojom::AuctionAdConfigPtr auction_config,
+      const blink::AuctionConfig& auction_config,
       RenderFrameHost* rfh) {
     mojo::Remote<blink::mojom::AdAuctionService> interest_service;
     AdAuctionServiceImpl::CreateMojoService(
@@ -653,7 +653,7 @@
     base::RunLoop run_loop;
     absl::optional<GURL> maybe_url;
     interest_service->RunAdAuction(
-        std::move(auction_config),
+        auction_config,
         base::BindLambdaForTesting(
             [&run_loop, &maybe_url](const absl::optional<GURL>& result) {
               maybe_url = result;
@@ -667,8 +667,8 @@
   // Like RunAdAuctionAndFlushForFrame(), but uses the render frame host of the
   // main frame.
   absl::optional<GURL> RunAdAuctionAndFlush(
-      blink::mojom::AuctionAdConfigPtr auction_config) {
-    return RunAdAuctionAndFlushForFrame(std::move(auction_config), main_rfh());
+      const blink::AuctionConfig& auction_config) {
+    return RunAdAuctionAndFlushForFrame(auction_config, main_rfh());
   }
 
   // Like UpdateInterestGroupNoFlushForFrame, but uses the render frame host of
@@ -700,7 +700,7 @@
   // Finalizes an ad and expects the Mojo pipe to be closed without invoking the
   // callback, as should be done in the case of a bad Mojo message.
   void FinalizeAdAndExpectPipeClosed(const std::string& guid,
-                                     blink::mojom::AuctionAdConfigPtr config) {
+                                     const blink::AuctionConfig& config) {
     mojo::Remote<blink::mojom::AdAuctionService> interest_service;
     AdAuctionServiceImpl::CreateMojoService(
         main_rfh(), interest_service.BindNewPipeAndPassReceiver());
@@ -708,7 +708,7 @@
     base::RunLoop run_loop;
     interest_service.set_disconnect_handler(run_loop.QuitClosure());
     interest_service->FinalizeAd(
-        guid, std::move(config),
+        guid, config,
         base::BindLambdaForTesting(
             [&](const absl::optional<GURL>& creative_url) {
               ADD_FAILURE() << "Callback unexpectedly invoked.";
@@ -3430,13 +3430,10 @@
   JoinInterestGroupAndFlush(interest_group);
   EXPECT_EQ(1, GetJoinCount(kOriginA, kInterestGroupName));
 
-  auto auction_config = blink::mojom::AuctionAdConfig::New();
-  auction_config->seller = kOriginA;
-  auction_config->decision_logic_url = kUrlA.Resolve(kDecisionUrlPath);
-  auction_config->auction_ad_config_non_shared_params =
-      blink::mojom::AuctionAdConfigNonSharedParams::New();
-  auction_config->auction_ad_config_non_shared_params->interest_group_buyers = {
-      kOriginA};
+  blink::AuctionConfig auction_config;
+  auction_config.seller = kOriginA;
+  auction_config.decision_logic_url = kUrlA.Resolve(kDecisionUrlPath);
+  auction_config.non_shared_params.interest_group_buyers = {kOriginA};
   absl::optional<GURL> auction_result =
       RunAdAuctionAndFlush(std::move(auction_config));
   ASSERT_NE(auction_result, absl::nullopt);
@@ -3485,13 +3482,10 @@
   JoinInterestGroupAndFlush(interest_group_a);
   EXPECT_EQ(1, GetJoinCount(kOriginA, kInterestGroupName));
 
-  auto auction_config = blink::mojom::AuctionAdConfig::New();
-  auction_config->seller = kOriginA;
-  auction_config->decision_logic_url = kUrlA.Resolve(kDecisionUrlPath);
-  auction_config->auction_ad_config_non_shared_params =
-      blink::mojom::AuctionAdConfigNonSharedParams::New();
-  auction_config->auction_ad_config_non_shared_params->interest_group_buyers = {
-      kOriginA};
+  blink::AuctionConfig auction_config;
+  auction_config.seller = kOriginA;
+  auction_config.decision_logic_url = kUrlA.Resolve(kDecisionUrlPath);
+  auction_config.non_shared_params.interest_group_buyers = {kOriginA};
   absl::optional<GURL> auction_result =
       RunAdAuctionAndFlush(std::move(auction_config));
   ASSERT_NE(auction_result, absl::nullopt);
@@ -3548,13 +3542,10 @@
   JoinInterestGroupAndFlush(interest_group_a);
   EXPECT_EQ(1, GetJoinCount(kOriginA, kInterestGroupName));
 
-  auto auction_config = blink::mojom::AuctionAdConfig::New();
-  auction_config->seller = kOriginA;
-  auction_config->decision_logic_url = kUrlA.Resolve(kDecisionUrlPath);
-  auction_config->auction_ad_config_non_shared_params =
-      blink::mojom::AuctionAdConfigNonSharedParams::New();
-  auction_config->auction_ad_config_non_shared_params->interest_group_buyers = {
-      kOriginA};
+  blink::AuctionConfig auction_config;
+  auction_config.seller = kOriginA;
+  auction_config.decision_logic_url = kUrlA.Resolve(kDecisionUrlPath);
+  auction_config.non_shared_params.interest_group_buyers = {kOriginA};
   absl::optional<GURL> auction_result =
       RunAdAuctionAndFlush(std::move(auction_config));
   EXPECT_EQ(auction_result, absl::nullopt);
@@ -3604,13 +3595,10 @@
   JoinInterestGroupAndFlush(interest_group_a);
   EXPECT_EQ(1, GetJoinCount(kOriginA, kInterestGroupName));
 
-  auto auction_config = blink::mojom::AuctionAdConfig::New();
-  auction_config->seller = kOriginA;
-  auction_config->decision_logic_url = kUrlA.Resolve(kMissingScriptPath);
-  auction_config->auction_ad_config_non_shared_params =
-      blink::mojom::AuctionAdConfigNonSharedParams::New();
-  auction_config->auction_ad_config_non_shared_params->interest_group_buyers = {
-      kOriginA};
+  blink::AuctionConfig auction_config;
+  auction_config.seller = kOriginA;
+  auction_config.decision_logic_url = kUrlA.Resolve(kMissingScriptPath);
+  auction_config.non_shared_params.interest_group_buyers = {kOriginA};
   absl::optional<GURL> auction_result =
       RunAdAuctionAndFlush(std::move(auction_config));
   EXPECT_EQ(auction_result, absl::nullopt);
@@ -3667,13 +3655,10 @@
   JoinInterestGroupAndFlush(interest_group_no_update);
   EXPECT_EQ(1, GetJoinCount(kOriginNoUpdate, kInterestGroupName));
 
-  auto auction_config = blink::mojom::AuctionAdConfig::New();
-  auction_config->seller = kOriginNoUpdate;
-  auction_config->decision_logic_url = kUrlNoUpdate.Resolve(kDecisionUrlPath);
-  auction_config->auction_ad_config_non_shared_params =
-      blink::mojom::AuctionAdConfigNonSharedParams::New();
-  auction_config->auction_ad_config_non_shared_params->interest_group_buyers = {
-      kOriginNoUpdate};
+  blink::AuctionConfig auction_config;
+  auction_config.seller = kOriginNoUpdate;
+  auction_config.decision_logic_url = kUrlNoUpdate.Resolve(kDecisionUrlPath);
+  auction_config.non_shared_params.interest_group_buyers = {kOriginNoUpdate};
   absl::optional<GURL> auction_result =
       RunAdAuctionAndFlush(std::move(auction_config));
   ASSERT_NE(auction_result, absl::nullopt);
@@ -3771,22 +3756,16 @@
   EXPECT_EQ(1, GetJoinCount(kOriginC, kInterestGroupName));
 
   NavigateAndCommit(kUrlA);
-  auto auction_config = blink::mojom::AuctionAdConfig::New();
-  auction_config->seller = kOriginA;
-  auction_config->decision_logic_url = kUrlA.Resolve(kDecisionUrlPath);
-  auction_config->auction_ad_config_non_shared_params =
-      blink::mojom::AuctionAdConfigNonSharedParams::New();
-  auction_config->auction_ad_config_non_shared_params->interest_group_buyers = {
-      kOriginA};
-  auto component_auction = blink::mojom::AuctionAdConfig::New();
-  component_auction->seller = kOriginA;
-  component_auction->decision_logic_url = kUrlA.Resolve(kDecisionUrlPath);
-  component_auction->auction_ad_config_non_shared_params =
-      blink::mojom::AuctionAdConfigNonSharedParams::New();
-  component_auction->auction_ad_config_non_shared_params
-      ->interest_group_buyers = {kOriginC};
-  auction_config->auction_ad_config_non_shared_params->component_auctions
-      .emplace_back(std::move(component_auction));
+  blink::AuctionConfig auction_config;
+  auction_config.seller = kOriginA;
+  auction_config.decision_logic_url = kUrlA.Resolve(kDecisionUrlPath);
+  auction_config.non_shared_params.interest_group_buyers = {kOriginA};
+  blink::AuctionConfig component_auction;
+  component_auction.seller = kOriginA;
+  component_auction.decision_logic_url = kUrlA.Resolve(kDecisionUrlPath);
+  component_auction.non_shared_params.interest_group_buyers = {kOriginC};
+  auction_config.non_shared_params.component_auctions.emplace_back(
+      std::move(component_auction));
   absl::optional<GURL> auction_result =
       RunAdAuctionAndFlush(std::move(auction_config));
   ASSERT_NE(auction_result, absl::nullopt);
@@ -3881,22 +3860,16 @@
   EXPECT_EQ(1, GetJoinCount(kOriginC, kInterestGroupName));
 
   NavigateAndCommit(kUrlA);
-  auto auction_config = blink::mojom::AuctionAdConfig::New();
-  auction_config->seller = kOriginA;
-  auction_config->decision_logic_url = kUrlA.Resolve(kDecisionUrlPath);
-  auction_config->auction_ad_config_non_shared_params =
-      blink::mojom::AuctionAdConfigNonSharedParams::New();
-  auction_config->auction_ad_config_non_shared_params->interest_group_buyers = {
-      kOriginA};
-  auto component_auction = blink::mojom::AuctionAdConfig::New();
-  component_auction->seller = kOriginA;
-  component_auction->decision_logic_url = kUrlA.Resolve(kDecisionUrlPath);
-  component_auction->auction_ad_config_non_shared_params =
-      blink::mojom::AuctionAdConfigNonSharedParams::New();
-  component_auction->auction_ad_config_non_shared_params
-      ->interest_group_buyers = {kOriginC};
-  auction_config->auction_ad_config_non_shared_params->component_auctions
-      .emplace_back(std::move(component_auction));
+  blink::AuctionConfig auction_config;
+  auction_config.seller = kOriginA;
+  auction_config.decision_logic_url = kUrlA.Resolve(kDecisionUrlPath);
+  auction_config.non_shared_params.interest_group_buyers = {kOriginA};
+  blink::AuctionConfig component_auction;
+  component_auction.seller = kOriginA;
+  component_auction.decision_logic_url = kUrlA.Resolve(kDecisionUrlPath);
+  component_auction.non_shared_params.interest_group_buyers = {kOriginC};
+  auction_config.non_shared_params.component_auctions.emplace_back(
+      std::move(component_auction));
   absl::optional<GURL> auction_result =
       RunAdAuctionAndFlush(std::move(auction_config));
   ASSERT_NE(auction_result, absl::nullopt);
@@ -3993,22 +3966,16 @@
   EXPECT_EQ(1, GetJoinCount(kOriginC, kInterestGroupName));
 
   NavigateAndCommit(kUrlA);
-  auto auction_config = blink::mojom::AuctionAdConfig::New();
-  auction_config->seller = kOriginA;
-  auction_config->decision_logic_url = kUrlA.Resolve(kDecisionUrlPath);
-  auction_config->auction_ad_config_non_shared_params =
-      blink::mojom::AuctionAdConfigNonSharedParams::New();
-  auction_config->auction_ad_config_non_shared_params->interest_group_buyers = {
-      kOriginA};
-  auto component_auction = blink::mojom::AuctionAdConfig::New();
-  component_auction->seller = kOriginA;
-  component_auction->decision_logic_url = kUrlA.Resolve(kDecisionUrlPath);
-  component_auction->auction_ad_config_non_shared_params =
-      blink::mojom::AuctionAdConfigNonSharedParams::New();
-  component_auction->auction_ad_config_non_shared_params
-      ->interest_group_buyers = {kOriginC};
-  auction_config->auction_ad_config_non_shared_params->component_auctions
-      .emplace_back(std::move(component_auction));
+  blink::AuctionConfig auction_config;
+  auction_config.seller = kOriginA;
+  auction_config.decision_logic_url = kUrlA.Resolve(kDecisionUrlPath);
+  auction_config.non_shared_params.interest_group_buyers = {kOriginA};
+  blink::AuctionConfig component_auction;
+  component_auction.seller = kOriginA;
+  component_auction.decision_logic_url = kUrlA.Resolve(kDecisionUrlPath);
+  component_auction.non_shared_params.interest_group_buyers = {kOriginC};
+  auction_config.non_shared_params.component_auctions.emplace_back(
+      std::move(component_auction));
   absl::optional<GURL> auction_result =
       RunAdAuctionAndFlush(std::move(auction_config));
   EXPECT_EQ(auction_result, absl::nullopt);
@@ -4085,13 +4052,10 @@
   JoinInterestGroupAndFlush(interest_group);
   EXPECT_EQ(1, GetJoinCount(kOriginA, kInterestGroupName));
 
-  auto auction_config = blink::mojom::AuctionAdConfig::New();
-  auction_config->seller = kOriginA;
-  auction_config->decision_logic_url = kUrlA.Resolve(kDecisionUrlPath);
-  auction_config->auction_ad_config_non_shared_params =
-      blink::mojom::AuctionAdConfigNonSharedParams::New();
-  auction_config->auction_ad_config_non_shared_params->interest_group_buyers = {
-      kOriginA};
+  blink::AuctionConfig auction_config;
+  auction_config.seller = kOriginA;
+  auction_config.decision_logic_url = kUrlA.Resolve(kDecisionUrlPath);
+  auction_config.non_shared_params.interest_group_buyers = {kOriginA};
   absl::optional<GURL> auction_result =
       RunAdAuctionAndFlush(std::move(auction_config));
   EXPECT_NE(auction_result, absl::nullopt);
@@ -4171,13 +4135,10 @@
   JoinInterestGroupAndFlush(interest_group);
   EXPECT_EQ(1, GetJoinCount(kOriginA, kInterestGroupName));
 
-  auto auction_config = blink::mojom::AuctionAdConfig::New();
-  auction_config->seller = kOriginA;
-  auction_config->decision_logic_url = kUrlA.Resolve(kDecisionUrlPath);
-  auction_config->auction_ad_config_non_shared_params =
-      blink::mojom::AuctionAdConfigNonSharedParams::New();
-  auction_config->auction_ad_config_non_shared_params->interest_group_buyers = {
-      kOriginA};
+  blink::AuctionConfig auction_config;
+  auction_config.seller = kOriginA;
+  auction_config.decision_logic_url = kUrlA.Resolve(kDecisionUrlPath);
+  auction_config.non_shared_params.interest_group_buyers = {kOriginA};
   absl::optional<GURL> auction_result =
       RunAdAuctionAndFlush(std::move(auction_config));
   EXPECT_NE(auction_result, absl::nullopt);
@@ -4259,14 +4220,11 @@
     JoinInterestGroupAndFlush(interest_group);
     EXPECT_EQ(1, GetJoinCount(kOriginA, name));
 
-    auto auction_config = blink::mojom::AuctionAdConfig::New();
-    auction_config->seller = kOriginA;
+    blink::AuctionConfig auction_config;
+    auction_config.seller = kOriginA;
 
-    auction_config->decision_logic_url = kUrlA.Resolve(kDecisionUrlPath);
-    auction_config->auction_ad_config_non_shared_params =
-        blink::mojom::AuctionAdConfigNonSharedParams::New();
-    auction_config->auction_ad_config_non_shared_params
-        ->interest_group_buyers = {kOriginA};
+    auction_config.decision_logic_url = kUrlA.Resolve(kDecisionUrlPath);
+    auction_config.non_shared_params.interest_group_buyers = {kOriginA};
     absl::optional<GURL> auction_result =
         RunAdAuctionAndFlush(std::move(auction_config));
     EXPECT_NE(auction_result, absl::nullopt);
@@ -4343,15 +4301,11 @@
   JoinInterestGroupAndFlush(interest_group);
   EXPECT_EQ(1, GetJoinCount(kOriginA, kInterestGroupName));
 
-  auto auction_config = blink::mojom::AuctionAdConfig::New();
-  auction_config->seller = kOriginA;
-  auction_config->decision_logic_url = kUrlA.Resolve(kDecisionUrlPath);
-  auction_config->auction_ad_config_non_shared_params =
-      blink::mojom::AuctionAdConfigNonSharedParams::New();
-  auction_config->auction_ad_config_non_shared_params->interest_group_buyers = {
-      kOriginA};
-  absl::optional<GURL> auction_result =
-      RunAdAuctionAndFlush(std::move(auction_config));
+  blink::AuctionConfig auction_config;
+  auction_config.seller = kOriginA;
+  auction_config.decision_logic_url = kUrlA.Resolve(kDecisionUrlPath);
+  auction_config.non_shared_params.interest_group_buyers = {kOriginA};
+  absl::optional<GURL> auction_result = RunAdAuctionAndFlush(auction_config);
   EXPECT_NE(auction_result, absl::nullopt);
 
   task_environment()->FastForwardBy(base::Seconds(2));
@@ -4361,14 +4315,11 @@
   EXPECT_EQ(manager_->report_queue_length_for_testing(), 1u);
 
   // Run a second auction while the first auction's reporting is in progress.
-  auto auction_config2 = blink::mojom::AuctionAdConfig::New();
-  auction_config2->seller = kOriginA;
-  auction_config2->decision_logic_url = kUrlA.Resolve(kDecisionUrlPath);
-  auction_config2->auction_ad_config_non_shared_params =
-      blink::mojom::AuctionAdConfigNonSharedParams::New();
-  auction_config2->auction_ad_config_non_shared_params
-      ->interest_group_buyers = {kOriginA};
-  auction_result = RunAdAuctionAndFlush(std::move(auction_config2));
+  blink::AuctionConfig auction_config2;
+  auction_config2.seller = kOriginA;
+  auction_config2.decision_logic_url = kUrlA.Resolve(kDecisionUrlPath);
+  auction_config2.non_shared_params.interest_group_buyers = {kOriginA};
+  auction_result = RunAdAuctionAndFlush(auction_config2);
   EXPECT_NE(auction_result, absl::nullopt);
   // Two more reports are enqueued.
   EXPECT_EQ(manager_->report_queue_length_for_testing(), 3u);
@@ -4382,14 +4333,11 @@
 
   // Run a third auction after report queue is cleared, to make sure further
   // auction's reports can be normally enqueued and sent again.
-  auto auction_config3 = blink::mojom::AuctionAdConfig::New();
-  auction_config3->seller = kOriginA;
-  auction_config3->decision_logic_url = kUrlA.Resolve(kDecisionUrlPath);
-  auction_config3->auction_ad_config_non_shared_params =
-      blink::mojom::AuctionAdConfigNonSharedParams::New();
-  auction_config3->auction_ad_config_non_shared_params
-      ->interest_group_buyers = {kOriginA};
-  auction_result = RunAdAuctionAndFlush(std::move(auction_config3));
+  blink::AuctionConfig auction_config3;
+  auction_config3.seller = kOriginA;
+  auction_config3.decision_logic_url = kUrlA.Resolve(kDecisionUrlPath);
+  auction_config3.non_shared_params.interest_group_buyers = {kOriginA};
+  auction_result = RunAdAuctionAndFlush(auction_config3);
   EXPECT_NE(auction_result, absl::nullopt);
 
   // Set `max_reporting_round_duration_` high enough so that the auction's two
@@ -4460,71 +4408,61 @@
   // bucket size is 1 hour, so specifying kMaxTime will select the max bucket.
   constexpr base::TimeDelta kMaxTime{base::Days(1)};
 
-  auto succeed_auction_config = blink::mojom::AuctionAdConfig::New();
-  succeed_auction_config->seller = kOriginA;
-  succeed_auction_config->decision_logic_url = kUrlA.Resolve(kDecisionUrlPath);
-  succeed_auction_config->auction_ad_config_non_shared_params =
-      blink::mojom::AuctionAdConfigNonSharedParams::New();
-  succeed_auction_config->auction_ad_config_non_shared_params
-      ->interest_group_buyers = {kOriginA};
+  blink::AuctionConfig succeed_auction_config;
+  succeed_auction_config.seller = kOriginA;
+  succeed_auction_config.decision_logic_url = kUrlA.Resolve(kDecisionUrlPath);
+  succeed_auction_config.non_shared_params.interest_group_buyers = {kOriginA};
 
-  auto fail_auction_config = blink::mojom::AuctionAdConfig::New();
-  fail_auction_config->seller = kOriginA;
-  fail_auction_config->decision_logic_url =
+  blink::AuctionConfig fail_auction_config;
+  fail_auction_config.seller = kOriginA;
+  fail_auction_config.decision_logic_url =
       kUrlA.Resolve(kDecisionFailAllUrlPath);
-  fail_auction_config->auction_ad_config_non_shared_params =
-      blink::mojom::AuctionAdConfigNonSharedParams::New();
-  fail_auction_config->auction_ad_config_non_shared_params
-      ->interest_group_buyers = {kOriginA};
+  fail_auction_config.non_shared_params.interest_group_buyers = {kOriginA};
 
   // 1st auction
-  EXPECT_NE(RunAdAuctionAndFlush(succeed_auction_config->Clone()),
-            absl::nullopt);
+  EXPECT_NE(RunAdAuctionAndFlush(succeed_auction_config), absl::nullopt);
   // Time metrics are published every auction.
   histogram_tester.ExpectUniqueTimeSample(
       "Ads.InterestGroup.Auction.TimeSinceLastAuctionPerPage", kMaxTime, 1);
 
   // 2nd auction
   task_environment()->FastForwardBy(base::Seconds(1));
-  EXPECT_EQ(RunAdAuctionAndFlush(fail_auction_config->Clone()), absl::nullopt);
+  EXPECT_EQ(RunAdAuctionAndFlush(fail_auction_config), absl::nullopt);
   histogram_tester.ExpectTimeBucketCount(
       "Ads.InterestGroup.Auction.TimeSinceLastAuctionPerPage", base::Seconds(1),
       1);
 
   // 3rd auction
   task_environment()->FastForwardBy(base::Seconds(3));
-  EXPECT_NE(RunAdAuctionAndFlush(succeed_auction_config->Clone()),
-            absl::nullopt);
+  EXPECT_NE(RunAdAuctionAndFlush(succeed_auction_config), absl::nullopt);
   histogram_tester.ExpectTimeBucketCount(
       "Ads.InterestGroup.Auction.TimeSinceLastAuctionPerPage", base::Seconds(3),
       1);
 
   // 4th auction
   task_environment()->FastForwardBy(base::Minutes(1));
-  EXPECT_NE(RunAdAuctionAndFlush(succeed_auction_config->Clone()),
-            absl::nullopt);
+  EXPECT_NE(RunAdAuctionAndFlush(succeed_auction_config), absl::nullopt);
   histogram_tester.ExpectTimeBucketCount(
       "Ads.InterestGroup.Auction.TimeSinceLastAuctionPerPage", base::Minutes(1),
       1);
 
   // 5th auction
   task_environment()->FastForwardBy(base::Minutes(10));
-  EXPECT_NE(RunAdAuctionAndFlush(succeed_auction_config->Clone()),
-            absl::nullopt);
+  EXPECT_NE(RunAdAuctionAndFlush(succeed_auction_config), absl::nullopt);
   histogram_tester.ExpectTimeBucketCount(
       "Ads.InterestGroup.Auction.TimeSinceLastAuctionPerPage",
       base::Minutes(10), 1);
 
   // 6th auction
   task_environment()->FastForwardBy(base::Minutes(30));
-  EXPECT_EQ(RunAdAuctionAndFlush(fail_auction_config->Clone()), absl::nullopt);
+  EXPECT_EQ(RunAdAuctionAndFlush(fail_auction_config), absl::nullopt);
   histogram_tester.ExpectTimeBucketCount(
       "Ads.InterestGroup.Auction.TimeSinceLastAuctionPerPage",
       base::Minutes(30), 1);
 
   // 7th auction
   task_environment()->FastForwardBy(base::Hours(1));
-  EXPECT_EQ(RunAdAuctionAndFlush(fail_auction_config->Clone()), absl::nullopt);
+  EXPECT_EQ(RunAdAuctionAndFlush(fail_auction_config), absl::nullopt);
   // Since the 1st auction has no prior auction -- it gets put in the same
   // bucket with the 7th auction -- there are 2 auctions now in this bucket.
   histogram_tester.ExpectTimeBucketCount(
@@ -4625,33 +4563,26 @@
   // bucket size is 1 hour, so specifying kMaxTime will select the max bucket.
   constexpr base::TimeDelta kMaxTime{base::Days(1)};
 
-  auto succeed_auction_config = blink::mojom::AuctionAdConfig::New();
-  succeed_auction_config->seller = kOriginA;
-  succeed_auction_config->decision_logic_url = kUrlA.Resolve(kDecisionUrlPath);
-  succeed_auction_config->auction_ad_config_non_shared_params =
-      blink::mojom::AuctionAdConfigNonSharedParams::New();
-  succeed_auction_config->auction_ad_config_non_shared_params
-      ->interest_group_buyers = {kOriginA};
+  blink::AuctionConfig succeed_auction_config;
+  succeed_auction_config.seller = kOriginA;
+  succeed_auction_config.decision_logic_url = kUrlA.Resolve(kDecisionUrlPath);
+  succeed_auction_config.non_shared_params.interest_group_buyers = {kOriginA};
 
-  auto fail_auction_config = blink::mojom::AuctionAdConfig::New();
-  fail_auction_config->seller = kOriginA;
-  fail_auction_config->decision_logic_url =
+  blink::AuctionConfig fail_auction_config;
+  fail_auction_config.seller = kOriginA;
+  fail_auction_config.decision_logic_url =
       kUrlA.Resolve(kDecisionFailAllUrlPath);
-  fail_auction_config->auction_ad_config_non_shared_params =
-      blink::mojom::AuctionAdConfigNonSharedParams::New();
-  fail_auction_config->auction_ad_config_non_shared_params
-      ->interest_group_buyers = {kOriginA};
+  fail_auction_config.non_shared_params.interest_group_buyers = {kOriginA};
 
   // 1st auction
-  EXPECT_NE(RunAdAuctionAndFlush(succeed_auction_config->Clone()),
-            absl::nullopt);
+  EXPECT_NE(RunAdAuctionAndFlush(succeed_auction_config), absl::nullopt);
   // Time metrics are published every auction.
   histogram_tester.ExpectUniqueTimeSample(
       "Ads.InterestGroup.Auction.TimeSinceLastAuctionPerPage", kMaxTime, 1);
 
   // 2nd auction
   task_environment()->FastForwardBy(base::Seconds(1));
-  EXPECT_EQ(RunAdAuctionAndFlush(fail_auction_config->Clone()), absl::nullopt);
+  EXPECT_EQ(RunAdAuctionAndFlush(fail_auction_config), absl::nullopt);
   histogram_tester.ExpectTimeBucketCount(
       "Ads.InterestGroup.Auction.TimeSinceLastAuctionPerPage", base::Seconds(1),
       1);
@@ -4785,17 +4716,13 @@
 
   constexpr int kNumAuctions = 10;
   // Run kNumAuctions auctions, all should succeed since there's no limit:
-  auto succeed_auction_config = blink::mojom::AuctionAdConfig::New();
-  succeed_auction_config->seller = kOriginA;
-  succeed_auction_config->decision_logic_url = kUrlA.Resolve(kDecisionUrlPath);
-  succeed_auction_config->auction_ad_config_non_shared_params =
-      blink::mojom::AuctionAdConfigNonSharedParams::New();
-  succeed_auction_config->auction_ad_config_non_shared_params
-      ->interest_group_buyers = {kOriginA};
+  blink::AuctionConfig succeed_auction_config;
+  succeed_auction_config.seller = kOriginA;
+  succeed_auction_config.decision_logic_url = kUrlA.Resolve(kDecisionUrlPath);
+  succeed_auction_config.non_shared_params.interest_group_buyers = {kOriginA};
 
   for (int i = 0; i < kNumAuctions; i++) {
-    EXPECT_NE(RunAdAuctionAndFlush(succeed_auction_config->Clone()),
-              absl::nullopt);
+    EXPECT_NE(RunAdAuctionAndFlush(succeed_auction_config), absl::nullopt);
   }
 
   // Some metrics only get reported until after navigation.
@@ -4917,36 +4844,29 @@
 
 // An empty config should be treated as a bad message.
 TEST_F(AdAuctionServiceImplTest, FinalizeAdRejectsEmptyConfig) {
-  auto mojo_config = blink::mojom::AuctionAdConfig::New();
-  mojo_config->auction_ad_config_non_shared_params =
-      blink::mojom::AuctionAdConfigNonSharedParams::New();
-
+  blink::AuctionConfig config;
   FinalizeAdAndExpectPipeClosed(
-      /*guid=*/std::string("1234"), std::move(mojo_config));
+      /*guid=*/std::string("1234"), config);
 }
 
 // An HTTP decision logic URL should be treated as a bad message.
 TEST_F(AdAuctionServiceImplTest, FinalizeAdRejectsHTTPDecisionUrl) {
-  auto mojo_config = blink::mojom::AuctionAdConfig::New();
-  mojo_config->auction_ad_config_non_shared_params =
-      blink::mojom::AuctionAdConfigNonSharedParams::New();
-  mojo_config->seller = url::Origin::Create(GURL("https://site.test"));
-  mojo_config->decision_logic_url = GURL("http://site.test/");
+  blink::AuctionConfig config;
+  config.seller = url::Origin::Create(GURL("https://site.test"));
+  config.decision_logic_url = GURL("http://site.test/");
 
   FinalizeAdAndExpectPipeClosed(
-      /*guid=*/"1234", std::move(mojo_config));
+      /*guid=*/"1234", config);
 }
 
 // An empty GUID should be treated as a bad message.
 TEST_F(AdAuctionServiceImplTest, FinalizeAdRejectsMissingGuid) {
-  auto mojo_config = blink::mojom::AuctionAdConfig::New();
-  mojo_config->auction_ad_config_non_shared_params =
-      blink::mojom::AuctionAdConfigNonSharedParams::New();
-  mojo_config->seller = url::Origin::Create(GURL("https://site.test"));
-  mojo_config->decision_logic_url = GURL("https://site.test/");
+  blink::AuctionConfig config;
+  config.seller = url::Origin::Create(GURL("https://site.test"));
+  config.decision_logic_url = GURL("https://site.test/");
 
   FinalizeAdAndExpectPipeClosed(
-      /*guid=*/std::string(), std::move(mojo_config));
+      /*guid=*/std::string(), config);
 }
 
 class AdAuctionServiceImplNumAuctionLimitTest
@@ -5021,33 +4941,26 @@
   // bucket size is 1 hour, so specifying kMaxTime will select the max bucket.
   constexpr base::TimeDelta kMaxTime{base::Days(1)};
 
-  auto succeed_auction_config = blink::mojom::AuctionAdConfig::New();
-  succeed_auction_config->seller = kOriginA;
-  succeed_auction_config->decision_logic_url = kUrlA.Resolve(kDecisionUrlPath);
-  succeed_auction_config->auction_ad_config_non_shared_params =
-      blink::mojom::AuctionAdConfigNonSharedParams::New();
-  succeed_auction_config->auction_ad_config_non_shared_params
-      ->interest_group_buyers = {kOriginA};
+  blink::AuctionConfig succeed_auction_config;
+  succeed_auction_config.seller = kOriginA;
+  succeed_auction_config.decision_logic_url = kUrlA.Resolve(kDecisionUrlPath);
+  succeed_auction_config.non_shared_params.interest_group_buyers = {kOriginA};
 
-  auto fail_auction_config = blink::mojom::AuctionAdConfig::New();
-  fail_auction_config->seller = kOriginA;
-  fail_auction_config->decision_logic_url =
+  blink::AuctionConfig fail_auction_config;
+  fail_auction_config.seller = kOriginA;
+  fail_auction_config.decision_logic_url =
       kUrlA.Resolve(kDecisionFailAllUrlPath);
-  fail_auction_config->auction_ad_config_non_shared_params =
-      blink::mojom::AuctionAdConfigNonSharedParams::New();
-  fail_auction_config->auction_ad_config_non_shared_params
-      ->interest_group_buyers = {kOriginA};
+  fail_auction_config.non_shared_params.interest_group_buyers = {kOriginA};
 
   // 1st auction
-  EXPECT_NE(RunAdAuctionAndFlush(succeed_auction_config->Clone()),
-            absl::nullopt);
+  EXPECT_NE(RunAdAuctionAndFlush(succeed_auction_config), absl::nullopt);
   // Time metrics are published every auction.
   histogram_tester.ExpectUniqueTimeSample(
       "Ads.InterestGroup.Auction.TimeSinceLastAuctionPerPage", kMaxTime, 1);
 
   // 2nd auction
   task_environment()->FastForwardBy(base::Seconds(1));
-  EXPECT_EQ(RunAdAuctionAndFlush(fail_auction_config->Clone()), absl::nullopt);
+  EXPECT_EQ(RunAdAuctionAndFlush(fail_auction_config), absl::nullopt);
   histogram_tester.ExpectTimeBucketCount(
       "Ads.InterestGroup.Auction.TimeSinceLastAuctionPerPage", base::Seconds(1),
       1);
@@ -5055,8 +4968,7 @@
   // 3rd auction -- fails even though decision_logic.js is used because the
   // auction limit is encountered.
   task_environment()->FastForwardBy(base::Seconds(3));
-  EXPECT_EQ(RunAdAuctionAndFlush(succeed_auction_config->Clone()),
-            absl::nullopt);
+  EXPECT_EQ(RunAdAuctionAndFlush(succeed_auction_config), absl::nullopt);
   // The time metrics shouldn't get updated.
   histogram_tester.ExpectTimeBucketCount(
       "Ads.InterestGroup.Auction.TimeSinceLastAuctionPerPage", base::Seconds(3),
@@ -5138,13 +5050,10 @@
   JoinInterestGroupAndFlush(interest_group);
   EXPECT_EQ(1, GetJoinCount(kOriginA, kInterestGroupName));
 
-  auto succeed_auction_config = blink::mojom::AuctionAdConfig::New();
-  succeed_auction_config->seller = kOriginA;
-  succeed_auction_config->decision_logic_url = kUrlA.Resolve(kDecisionUrlPath);
-  succeed_auction_config->auction_ad_config_non_shared_params =
-      blink::mojom::AuctionAdConfigNonSharedParams::New();
-  succeed_auction_config->auction_ad_config_non_shared_params
-      ->interest_group_buyers = {kOriginA};
+  blink::AuctionConfig succeed_auction_config;
+  succeed_auction_config.seller = kOriginA;
+  succeed_auction_config.decision_logic_url = kUrlA.Resolve(kDecisionUrlPath);
+  succeed_auction_config.non_shared_params.interest_group_buyers = {kOriginA};
 
   // Pick some large number, larger than the auction limit.
   constexpr int kNumAuctions = 10;
@@ -5157,7 +5066,7 @@
 
   for (int i = 0; i < kNumAuctions; i++) {
     interest_service->RunAdAuction(
-        succeed_auction_config->Clone(),
+        succeed_auction_config,
         base::BindLambdaForTesting(
             [&one_auction_complete](
                 const absl::optional<GURL>& ignored_result) {
@@ -5400,16 +5309,12 @@
     JoinInterestGroupAndFlush(interest_group);
     EXPECT_EQ(1, GetJoinCount(kOriginA, name));
 
-    auto auction_config = blink::mojom::AuctionAdConfig::New();
-    auction_config->seller = kOriginA;
+    blink::AuctionConfig auction_config;
+    auction_config.seller = kOriginA;
 
-    auction_config->decision_logic_url = kUrlA.Resolve(kDecisionUrlPath);
-    auction_config->auction_ad_config_non_shared_params =
-        blink::mojom::AuctionAdConfigNonSharedParams::New();
-    auction_config->auction_ad_config_non_shared_params
-        ->interest_group_buyers = {kOriginA};
-    absl::optional<GURL> auction_result =
-        RunAdAuctionAndFlush(std::move(auction_config));
+    auction_config.decision_logic_url = kUrlA.Resolve(kDecisionUrlPath);
+    auction_config.non_shared_params.interest_group_buyers = {kOriginA};
+    absl::optional<GURL> auction_result = RunAdAuctionAndFlush(auction_config);
     EXPECT_NE(auction_result, absl::nullopt);
   }
 
diff --git a/content/browser/interest_group/auction_runner.cc b/content/browser/interest_group/auction_runner.cc
index 7aba832..d6c7304 100644
--- a/content/browser/interest_group/auction_runner.cc
+++ b/content/browser/interest_group/auction_runner.cc
@@ -38,6 +38,7 @@
 #include "services/network/public/mojom/url_loader_factory.mojom-forward.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/blink/public/common/interest_group/ad_auction_constants.h"
+#include "third_party/blink/public/common/interest_group/auction_config.h"
 #include "third_party/blink/public/common/interest_group/interest_group.h"
 #include "third_party/blink/public/mojom/interest_group/interest_group_types.mojom.h"
 #include "url/gurl.h"
@@ -171,7 +172,7 @@
 AuctionRunner::ScoredBid::~ScoredBid() = default;
 
 AuctionRunner::Auction::Auction(
-    blink::mojom::AuctionAdConfig* config,
+    const blink::AuctionConfig* config,
     const Auction* parent,
     AuctionWorkletManager* auction_worklet_manager,
     InterestGroupManagerImpl* interest_group_manager,
@@ -187,12 +188,12 @@
                                     config_->decision_logic_url);
 
   for (const auto& component_auction_config :
-       config->auction_ad_config_non_shared_params->component_auctions) {
+       config->non_shared_params.component_auctions) {
     // Nested component auctions are not supported.
     DCHECK(!parent_);
     component_auctions_.emplace_back(std::make_unique<Auction>(
-        component_auction_config.get(), /*parent=*/this,
-        auction_worklet_manager, interest_group_manager, auction_start_time));
+        &component_auction_config, /*parent=*/this, auction_worklet_manager,
+        interest_group_manager, auction_start_time));
   }
 }
 
@@ -269,9 +270,9 @@
     ++num_pending_loads_;
   }
 
-  if (config_->auction_ad_config_non_shared_params->interest_group_buyers) {
+  if (config_->non_shared_params.interest_group_buyers) {
     for (const auto& buyer :
-         *config_->auction_ad_config_non_shared_params->interest_group_buyers) {
+         *config_->non_shared_params.interest_group_buyers) {
       if (!is_interest_group_api_allowed_callback.Run(
               ContentBrowserClient::InterestGroupApiOperation::kBuy, buyer)) {
         continue;
@@ -362,9 +363,7 @@
     DCHECK(top_seller_signals);
     if (!auction_worklet_manager_->RequestSellerWorklet(
             config_->decision_logic_url, config_->trusted_scoring_signals_url,
-            config_->has_seller_experiment_group_id
-                ? absl::make_optional(config_->seller_experiment_group_id)
-                : absl::nullopt,
+            config_->seller_experiment_group_id,
             base::BindOnce(&Auction::ReportSellerResult, base::Unretained(this),
                            top_seller_signals),
             base::BindOnce(&Auction::OnWinningComponentSellerWorkletFatalError,
@@ -604,14 +603,13 @@
     std::vector<StorageInterestGroup> interest_groups) {
   ++num_owners_loaded_;
   if (!interest_groups.empty()) {
-    size_t size_limit =
-        config_->auction_ad_config_non_shared_params->all_buyers_group_limit;
+    size_t size_limit = config_->non_shared_params.all_buyers_group_limit;
     const url::Origin& owner = interest_groups[0].interest_group.owner;
     post_auction_update_owners_.push_back(owner);
-    const auto limit_iter = config_->auction_ad_config_non_shared_params
-                                ->per_buyer_group_limits.find(owner);
-    if (limit_iter != config_->auction_ad_config_non_shared_params
-                          ->per_buyer_group_limits.cend()) {
+    const auto limit_iter =
+        config_->non_shared_params.per_buyer_group_limits.find(owner);
+    if (limit_iter !=
+        config_->non_shared_params.per_buyer_group_limits.cend()) {
       size_limit = static_cast<size_t>(limit_iter->second);
     }
     StorageInterestGroupDescByPriority cmp;
@@ -751,9 +749,7 @@
                                     trace_id_);
   if (auction_worklet_manager_->RequestSellerWorklet(
           config_->decision_logic_url, config_->trusted_scoring_signals_url,
-          config_->has_seller_experiment_group_id
-              ? absl::make_optional(config_->seller_experiment_group_id)
-              : absl::nullopt,
+          config_->seller_experiment_group_id,
           base::BindOnce(&Auction::OnSellerWorkletReceived,
                          base::Unretained(this)),
           base::BindOnce(&Auction::OnSellerWorkletFatalError,
@@ -834,8 +830,8 @@
           interest_group.trusted_bidding_signals_keys,
           interest_group.user_bidding_signals, interest_group.ads,
           interest_group.ad_components),
-      config_->auction_ad_config_non_shared_params->auction_signals,
-      PerBuyerSignals(bid_state), PerBuyerTimeout(bid_state), config_->seller,
+      config_->non_shared_params.auction_signals, PerBuyerSignals(bid_state),
+      PerBuyerTimeout(bid_state), config_->seller,
       parent_ ? parent_->config_->seller : absl::optional<url::Origin>(),
       bid_state->bidder.bidding_browser_signals.Clone(), auction_start_time_,
       *bid_state->trace_id,
@@ -1003,8 +999,7 @@
 
   Bid* bid_raw = bid.get();
   seller_worklet_handle_->GetSellerWorklet()->ScoreAd(
-      bid_raw->ad_metadata, bid_raw->bid,
-      config_->auction_ad_config_non_shared_params.Clone(),
+      bid_raw->ad_metadata, bid_raw->bid, config_->non_shared_params,
       GetOtherSellerParam(*bid_raw), bid_raw->interest_group->owner,
       bid_raw->render_url, bid_raw->ad_components,
       bid_raw->bid_duration.InMilliseconds(), SellerTimeout(),
@@ -1182,8 +1177,7 @@
 
 absl::optional<std::string> AuctionRunner::Auction::PerBuyerSignals(
     const BidState* state) {
-  const auto& per_buyer_signals =
-      config_->auction_ad_config_non_shared_params->per_buyer_signals;
+  const auto& per_buyer_signals = config_->non_shared_params.per_buyer_signals;
   if (per_buyer_signals.has_value()) {
     auto it =
         per_buyer_signals.value().find(state->bidder.interest_group.owner);
@@ -1196,7 +1190,7 @@
 absl::optional<base::TimeDelta> AuctionRunner::Auction::PerBuyerTimeout(
     const BidState* state) {
   const auto& per_buyer_timeouts =
-      config_->auction_ad_config_non_shared_params->per_buyer_timeouts;
+      config_->non_shared_params.per_buyer_timeouts;
   if (per_buyer_timeouts.has_value()) {
     auto it =
         per_buyer_timeouts.value().find(state->bidder.interest_group.owner);
@@ -1204,18 +1198,16 @@
       return std::min(it->second, kMaxTimeout);
   }
   const auto& all_buyers_timeout =
-      config_->auction_ad_config_non_shared_params->all_buyers_timeout;
+      config_->non_shared_params.all_buyers_timeout;
   if (all_buyers_timeout.has_value())
     return std::min(all_buyers_timeout.value(), kMaxTimeout);
   return absl::nullopt;
 }
 
 absl::optional<base::TimeDelta> AuctionRunner::Auction::SellerTimeout() {
-  if (config_->auction_ad_config_non_shared_params->seller_timeout
-          .has_value()) {
-    return std::min(
-        config_->auction_ad_config_non_shared_params->seller_timeout.value(),
-        kMaxTimeout);
+  if (config_->non_shared_params.seller_timeout.has_value()) {
+    return std::min(config_->non_shared_params.seller_timeout.value(),
+                    kMaxTimeout);
   }
   return absl::nullopt;
 }
@@ -1326,10 +1318,9 @@
   }
 
   seller_worklet_handle_->GetSellerWorklet()->ReportResult(
-      config_->auction_ad_config_non_shared_params.Clone(),
-      GetOtherSellerParam(*top_bid_->bid), top_bid_->bid->interest_group->owner,
-      top_bid_->bid->render_url, top_bid_->bid->bid, top_bid_->score,
-      highest_scoring_other_bid_,
+      config_->non_shared_params, GetOtherSellerParam(*top_bid_->bid),
+      top_bid_->bid->interest_group->owner, top_bid_->bid->render_url,
+      top_bid_->bid->bid, top_bid_->score, highest_scoring_other_bid_,
       std::move(browser_signals_component_auction_report_result_params),
       top_bid_->scoring_signals_data_version.value_or(0),
       top_bid_->scoring_signals_data_version.has_value(), trace_id_,
@@ -1423,7 +1414,7 @@
 
   top_bid_->bid->bid_state->worklet_handle->GetBidderWorklet()->ReportWin(
       top_bid_->bid->interest_group->name,
-      config_->auction_ad_config_non_shared_params->auction_signals,
+      config_->non_shared_params.auction_signals,
       PerBuyerSignals(top_bid_->bid->bid_state), signals_for_winner,
       top_bid_->bid->render_url, top_bid_->bid->bid,
       /*browser_signal_highest_scoring_other_bid=*/highest_scoring_other_bid_,
@@ -1591,10 +1582,11 @@
 
   absl::optional<uint16_t> experiment_group_id = absl::nullopt;
   auto it = config_->per_buyer_experiment_group_ids.find(interest_group.owner);
-  if (it != config_->per_buyer_experiment_group_ids.end())
+  if (it != config_->per_buyer_experiment_group_ids.end()) {
     experiment_group_id = it->second;
-  else if (config_->has_all_buyer_experiment_group_id)
+  } else {
     experiment_group_id = config_->all_buyer_experiment_group_id;
+  }
 
   return auction_worklet_manager_->RequestBidderWorklet(
       interest_group.bidding_url.value_or(GURL()),
@@ -1607,7 +1599,7 @@
 std::unique_ptr<AuctionRunner> AuctionRunner::CreateAndStart(
     AuctionWorkletManager* auction_worklet_manager,
     InterestGroupManagerImpl* interest_group_manager,
-    blink::mojom::AuctionAdConfigPtr auction_config,
+    const blink::AuctionConfig& auction_config,
     network::mojom::ClientSecurityStatePtr client_security_state,
     IsInterestGroupApiAllowedCallback is_interest_group_api_allowed_callback,
     RunAuctionCallback callback) {
@@ -1646,7 +1638,7 @@
 AuctionRunner::AuctionRunner(
     AuctionWorkletManager* auction_worklet_manager,
     InterestGroupManagerImpl* interest_group_manager,
-    blink::mojom::AuctionAdConfigPtr auction_config,
+    const blink::AuctionConfig& auction_config,
     network::mojom::ClientSecurityStatePtr client_security_state,
     IsInterestGroupApiAllowedCallback is_interest_group_api_allowed_callback,
     RunAuctionCallback callback)
@@ -1654,9 +1646,9 @@
       client_security_state_(std::move(client_security_state)),
       is_interest_group_api_allowed_callback_(
           is_interest_group_api_allowed_callback),
-      owned_auction_config_(std::move(auction_config)),
+      owned_auction_config_(auction_config),
       callback_(std::move(callback)),
-      auction_(owned_auction_config_.get(),
+      auction_(&owned_auction_config_,
                /*parent=*/nullptr,
                auction_worklet_manager,
                interest_group_manager,
diff --git a/content/browser/interest_group/auction_runner.h b/content/browser/interest_group/auction_runner.h
index a15cb11..df369a62 100644
--- a/content/browser/interest_group/auction_runner.h
+++ b/content/browser/interest_group/auction_runner.h
@@ -31,6 +31,10 @@
 #include "url/gurl.h"
 #include "url/origin.h"
 
+namespace blink {
+struct AuctionConfig;
+}
+
 namespace content {
 
 class InterestGroupManagerImpl;
@@ -198,7 +202,7 @@
   static std::unique_ptr<AuctionRunner> CreateAndStart(
       AuctionWorkletManager* auction_worklet_manager,
       InterestGroupManagerImpl* interest_group_manager,
-      blink::mojom::AuctionAdConfigPtr auction_config,
+      const blink::AuctionConfig& auction_config,
       network::mojom::ClientSecurityStatePtr client_security_state,
       IsInterestGroupApiAllowedCallback is_interest_group_api_allowed_callback,
       RunAuctionCallback callback);
@@ -393,7 +397,7 @@
     // destroyed. `config` is typically owned by the AuctionRunner's
     // `owned_auction_config_` field. `parent` should be the parent Auction if
     // this is a component auction, and null, otherwise.
-    Auction(blink::mojom::AuctionAdConfig* config,
+    Auction(const blink::AuctionConfig* config,
             const Auction* parent,
             AuctionWorkletManager* auction_worklet_manager,
             InterestGroupManagerImpl* interest_group_manager,
@@ -732,7 +736,7 @@
     const raw_ptr<InterestGroupManagerImpl> interest_group_manager_;
 
     // Configuration of this auction.
-    raw_ptr<const blink::mojom::AuctionAdConfig> config_;
+    raw_ptr<const blink::AuctionConfig> config_;
     // If this is a component auction, the parent Auction. Null, otherwise.
     const raw_ptr<const Auction> parent_;
 
@@ -871,7 +875,7 @@
   AuctionRunner(
       AuctionWorkletManager* auction_worklet_manager,
       InterestGroupManagerImpl* interest_group_manager,
-      blink::mojom::AuctionAdConfigPtr auction_config,
+      const blink::AuctionConfig& auction_config,
       network::mojom::ClientSecurityStatePtr client_security_state,
       IsInterestGroupApiAllowedCallback is_interest_group_api_allowed_callback,
       RunAuctionCallback callback);
@@ -912,7 +916,7 @@
   IsInterestGroupApiAllowedCallback is_interest_group_api_allowed_callback_;
 
   // Configuration.
-  blink::mojom::AuctionAdConfigPtr owned_auction_config_;
+  blink::AuctionConfig owned_auction_config_;
   RunAuctionCallback callback_;
 
   Auction auction_;
diff --git a/content/browser/interest_group/auction_runner_unittest.cc b/content/browser/interest_group/auction_runner_unittest.cc
index a83340a..986600a 100644
--- a/content/browser/interest_group/auction_runner_unittest.cc
+++ b/content/browser/interest_group/auction_runner_unittest.cc
@@ -993,7 +993,7 @@
 
   void ScoreAd(const std::string& ad_metadata_json,
                double bid,
-               blink::mojom::AuctionAdConfigNonSharedParamsPtr
+               const blink::AuctionConfig::NonSharedParams&
                    auction_ad_config_non_shared_params,
                auction_worklet::mojom::ComponentAuctionOtherSellerPtr
                    browser_signals_other_seller,
@@ -1032,7 +1032,7 @@
   }
 
   void ReportResult(
-      blink::mojom::AuctionAdConfigNonSharedParamsPtr
+      const blink::AuctionConfig::NonSharedParams&
           auction_ad_config_non_shared_params,
       auction_worklet::mojom::ComponentAuctionOtherSellerPtr
           browser_signals_other_seller,
@@ -1453,24 +1453,18 @@
   std::string TakeBadMessage() { return std::move(bad_message_); }
 
   // Helper to create an auction config with the specified values.
-  blink::mojom::AuctionAdConfigPtr CreateAuctionConfig(
+  blink::AuctionConfig CreateAuctionConfig(
       const GURL& seller_decision_logic_url,
       absl::optional<std::vector<url::Origin>> buyers) {
-    blink::mojom::AuctionAdConfigPtr auction_config =
-        blink::mojom::AuctionAdConfig::New();
-    auction_config->seller = url::Origin::Create(seller_decision_logic_url);
-    auction_config->decision_logic_url = seller_decision_logic_url;
+    blink::AuctionConfig auction_config;
+    auction_config.seller = url::Origin::Create(seller_decision_logic_url);
+    auction_config.decision_logic_url = seller_decision_logic_url;
 
-    auction_config->auction_ad_config_non_shared_params =
-        blink::mojom::AuctionAdConfigNonSharedParams::New();
-    auction_config->auction_ad_config_non_shared_params->interest_group_buyers =
-        std::move(buyers);
+    auction_config.non_shared_params.interest_group_buyers = std::move(buyers);
 
-    auction_config->auction_ad_config_non_shared_params->seller_signals =
-        base::StringPrintf(R"({"url": "%s"})",
-                           seller_decision_logic_url.spec().c_str());
-    auction_config->auction_ad_config_non_shared_params->seller_timeout =
-        base::Milliseconds(1000);
+    auction_config.non_shared_params.seller_signals = base::StringPrintf(
+        R"({"url": "%s"})", seller_decision_logic_url.spec().c_str());
+    auction_config.non_shared_params.seller_timeout = base::Milliseconds(1000);
 
     base::flat_map<url::Origin, std::string> per_buyer_signals;
     // Use a combination of bidder and seller values, so can make sure bidders
@@ -1478,40 +1472,31 @@
     // as a defense against pulling the right values from the wrong places.
     per_buyer_signals[kBidder1] = base::StringPrintf(
         R"({"%sSignals": "%sSignals"})",
-        auction_config->seller.Serialize().c_str(), kBidder1Name.c_str());
+        auction_config.seller.Serialize().c_str(), kBidder1Name.c_str());
     per_buyer_signals[kBidder2] = base::StringPrintf(
         R"({"%sSignals": "%sSignals"})",
-        auction_config->seller.Serialize().c_str(), kBidder2Name.c_str());
-    auction_config->auction_ad_config_non_shared_params->per_buyer_signals =
+        auction_config.seller.Serialize().c_str(), kBidder2Name.c_str());
+    auction_config.non_shared_params.per_buyer_signals =
         std::move(per_buyer_signals);
 
     base::flat_map<url::Origin, base::TimeDelta> per_buyer_timeouts;
     // Any per buyer timeout higher than 500 ms will be clamped to 500 ms by the
     // AuctionRunner.
     per_buyer_timeouts[kBidder1] = base::Milliseconds(1000);
-    auction_config->auction_ad_config_non_shared_params->per_buyer_timeouts =
+    auction_config.non_shared_params.per_buyer_timeouts =
         std::move(per_buyer_timeouts);
-    auction_config->auction_ad_config_non_shared_params->all_buyers_timeout =
+    auction_config.non_shared_params.all_buyers_timeout =
         base::Milliseconds(150);
 
-    auction_config->auction_ad_config_non_shared_params->auction_signals =
-        base::StringPrintf(R"("auctionSignalsFor %s")",
-                           auction_config->seller.Serialize().c_str());
+    auction_config.non_shared_params.auction_signals = base::StringPrintf(
+        R"("auctionSignalsFor %s")", auction_config.seller.Serialize().c_str());
 
-    if (seller_experiment_group_id_.has_value()) {
-      auction_config->has_seller_experiment_group_id = true;
-      auction_config->seller_experiment_group_id =
-          seller_experiment_group_id_.value();
-    }
-
-    if (all_buyer_experiment_group_id_.has_value()) {
-      auction_config->has_all_buyer_experiment_group_id = true;
-      auction_config->all_buyer_experiment_group_id =
-          all_buyer_experiment_group_id_.value();
-    }
+    auction_config.seller_experiment_group_id = seller_experiment_group_id_;
+    auction_config.all_buyer_experiment_group_id =
+        all_buyer_experiment_group_id_;
 
     for (const auto& kv : per_buyer_experiment_group_id_) {
-      auction_config->per_buyer_experiment_group_ids[kv.first] = kv.second;
+      auction_config.per_buyer_experiment_group_ids[kv.first] = kv.second;
     }
 
     return auction_config;
@@ -1529,14 +1514,14 @@
                     std::vector<StorageInterestGroup> bidders) {
     auction_complete_ = false;
 
-    blink::mojom::AuctionAdConfigPtr auction_config =
+    auto auction_config =
         CreateAuctionConfig(seller_decision_logic_url, interest_group_buyers_);
 
-    auction_config->trusted_scoring_signals_url = trusted_scoring_signals_url_;
+    auction_config.trusted_scoring_signals_url = trusted_scoring_signals_url_;
 
     for (const auto& component_auction : component_auctions_) {
-      auction_config->auction_ad_config_non_shared_params->component_auctions
-          .emplace_back(component_auction.Clone());
+      auction_config.non_shared_params.component_auctions.push_back(
+          component_auction);
     }
 
     interest_group_manager_ = std::make_unique<InterestGroupManagerImpl>(
@@ -1982,7 +1967,7 @@
   absl::optional<std::vector<url::Origin>> interest_group_buyers_ = {
       {kBidder1, kBidder2}};
 
-  std::vector<blink::mojom::AuctionAdConfigPtr> component_auctions_;
+  std::vector<blink::AuctionConfig> component_auctions_;
 
   // Origins which are not allowed to take part in auctions, as the
   // corresponding participant types.
diff --git a/content/browser/interest_group/auction_worklet_manager_unittest.cc b/content/browser/interest_group/auction_worklet_manager_unittest.cc
index 0213c51..b43766f 100644
--- a/content/browser/interest_group/auction_worklet_manager_unittest.cc
+++ b/content/browser/interest_group/auction_worklet_manager_unittest.cc
@@ -266,7 +266,7 @@
 
   void ScoreAd(const std::string& ad_metadata_json,
                double bid,
-               blink::mojom::AuctionAdConfigNonSharedParamsPtr
+               const blink::AuctionConfig::NonSharedParams&
                    auction_ad_config_non_shared_params,
                auction_worklet::mojom::ComponentAuctionOtherSellerPtr
                    browser_signals_other_seller,
@@ -289,7 +289,7 @@
   }
 
   void ReportResult(
-      blink::mojom::AuctionAdConfigNonSharedParamsPtr
+      const blink::AuctionConfig::NonSharedParams&
           auction_ad_config_non_shared_params,
       auction_worklet::mojom::ComponentAuctionOtherSellerPtr
           browser_signals_other_seller,
diff --git a/content/browser/interest_group/interest_group_browsertest.cc b/content/browser/interest_group/interest_group_browsertest.cc
index 45f9e79..61a88fbb1 100644
--- a/content/browser/interest_group/interest_group_browsertest.cc
+++ b/content/browser/interest_group/interest_group_browsertest.cc
@@ -6408,308 +6408,6 @@
       "&experimentGroupId=1203"));
 }
 
-// This test exercises the interest group and ad auction services directly,
-// rather than via Blink, to ensure that those services running in the browser
-// implement important security checks (Blink may also perform its own
-// checking, but the render process is untrusted).
-IN_PROC_BROWSER_TEST_F(InterestGroupBrowserTest, RunAdAuctionBasicBypassBlink) {
-  ASSERT_TRUE(NavigateToURL(shell(), https_server_->GetURL("a.test", "/echo")));
-
-  mojo::Remote<blink::mojom::AdAuctionService> auction_service;
-  AdAuctionServiceImpl::CreateMojoService(
-      web_contents()->GetMainFrame(),
-      auction_service.BindNewPipeAndPassReceiver());
-
-  base::RunLoop run_loop;
-
-  auto auction_config = blink::mojom::AuctionAdConfig::New();
-  auction_config->auction_ad_config_non_shared_params =
-      blink::mojom::AuctionAdConfigNonSharedParams::New();
-
-  auction_service->RunAdAuction(
-      std::move(auction_config),
-      base::BindLambdaForTesting([&run_loop](const absl::optional<GURL>& url) {
-        EXPECT_THAT(url, Eq(absl::nullopt));
-        run_loop.Quit();
-      }));
-  run_loop.Run();
-}
-
-// Fixture for Blink-bypassing auction tests that share the same interest group
-// -- useful for checking auction service security validations.
-class InterestGroupBrowserTestRunAdAuctionBypassBlink
-    : public InterestGroupBrowserTest {
- protected:
-  void SetUpOnMainThread() override {
-    InterestGroupBrowserTest::SetUpOnMainThread();
-    ad_url_ = https_server_->GetURL("c.test", "/echo?render_ad");
-
-    GURL test_url_a = https_server_->GetURL("a.test", "/echo");
-    test_origin_a_ = url::Origin::Create(test_url_a);
-    ASSERT_TRUE(test_url_a.SchemeIs(url::kHttpsScheme));
-    ASSERT_TRUE(NavigateToURL(shell(), test_url_a));
-
-    mojo::Remote<blink::mojom::AdAuctionService> interest_service;
-    AdAuctionServiceImpl::CreateMojoService(
-        web_contents()->GetMainFrame(),
-        interest_service.BindNewPipeAndPassReceiver());
-
-    // Set up ad_url_ as the only interest group ad in the auction.
-    blink::InterestGroup interest_group;
-    interest_group.expiry = base::Time::Now() + base::Seconds(300);
-    constexpr char kGroupName[] = "cars";
-    interest_group.name = kGroupName;
-    interest_group.owner = test_origin_a_;
-    interest_group.bidding_url =
-        https_server_->GetURL("a.test", "/interest_group/bidding_logic.js");
-    interest_group.trusted_bidding_signals_url = https_server_->GetURL(
-        "a.test", "/interest_group/trusted_bidding_signals.json");
-    interest_group.trusted_bidding_signals_keys.emplace();
-    interest_group.trusted_bidding_signals_keys->push_back("key1");
-    interest_group.user_bidding_signals =
-        "{\"some\": \"json\", \"data\": {\"here\": [1, 2, 3]}}";
-    interest_group.ads.emplace();
-    interest_group.ads->push_back(blink::InterestGroup::Ad(
-        /* render_url = */ ad_url_,
-        /* metadata = */ "{\"ad\": \"metadata\", \"here\": [1, 2, 3]}"));
-
-    base::RunLoop run_loop;
-    interest_service->JoinInterestGroup(
-        interest_group,
-        base::BindLambdaForTesting([&](bool failed_well_known_check) {
-          EXPECT_FALSE(failed_well_known_check);
-          run_loop.Quit();
-        }));
-    run_loop.Run();
-
-    EXPECT_EQ(1, GetJoinCount(test_origin_a_, kGroupName));
-  }
-
-  absl::optional<GURL> RunAuctionBypassBlink(
-      blink::mojom::AuctionAdConfigPtr config) {
-    absl::optional<GURL> maybe_url;
-    base::RunLoop run_loop;
-    mojo::Remote<blink::mojom::AdAuctionService> auction_service;
-    AdAuctionServiceImpl::CreateMojoService(
-        web_contents()->GetMainFrame(),
-        auction_service.BindNewPipeAndPassReceiver());
-
-    auction_service->RunAdAuction(
-        std::move(config),
-        base::BindLambdaForTesting(
-            [&run_loop, &maybe_url](const absl::optional<GURL>& url) {
-              maybe_url = url;
-              run_loop.Quit();
-            }));
-    run_loop.Run();
-    if (maybe_url) {
-      TestFencedFrameURLMappingResultObserver observer;
-      ConvertFencedFrameURNToURL(*maybe_url, &observer);
-      EXPECT_TRUE(observer.mapped_url());
-      absl::optional<GURL> decoded_URL = observer.mapped_url();
-      EXPECT_EQ(decoded_URL, ConvertFencedFrameURNToURLInJS(*maybe_url));
-      NavigateIframeAndCheckURL(web_contents(), *maybe_url,
-                                decoded_URL.value_or(GURL()));
-      return *observer.mapped_url();
-    }
-    return absl::nullopt;
-  }
-
-  // Creates a valid AuctionAdConfigPtr which will run an auction with the
-  // InterestGroup added in SetUpOnMainThread() participating and winning.
-  blink::mojom::AuctionAdConfigPtr CreateValidAuctionConfig() {
-    auto config = blink::mojom::AuctionAdConfig::New();
-    config->seller = test_origin_a_;
-    config->decision_logic_url =
-        https_server_->GetURL("a.test", "/interest_group/decision_logic.js");
-    config->auction_ad_config_non_shared_params =
-        blink::mojom::AuctionAdConfigNonSharedParams::New();
-    config->auction_ad_config_non_shared_params->interest_group_buyers = {
-        test_origin_a_};
-    return config;
-  }
-
-  url::Origin test_origin_a_;
-  GURL ad_url_;
-};
-
-IN_PROC_BROWSER_TEST_F(InterestGroupBrowserTestRunAdAuctionBypassBlink,
-                       BasicSuccess) {
-  GURL test_url_b = https_server_->GetURL("b.test", "/page_with_iframe.html");
-  ASSERT_TRUE(test_url_b.SchemeIs(url::kHttpsScheme));
-  url::Origin test_origin_b = url::Origin::Create(test_url_b);
-  ASSERT_TRUE(NavigateToURL(shell(), test_url_b));
-
-  auto config = blink::mojom::AuctionAdConfig::New();
-  config->seller = test_origin_b;
-  config->decision_logic_url =
-      https_server_->GetURL("b.test", "/interest_group/decision_logic.js");
-  config->auction_ad_config_non_shared_params =
-      blink::mojom::AuctionAdConfigNonSharedParams::New();
-  config->auction_ad_config_non_shared_params->interest_group_buyers = {
-      test_origin_a_};
-
-  EXPECT_THAT(RunAuctionBypassBlink(std::move(config)), Optional(Eq(ad_url_)));
-}
-
-IN_PROC_BROWSER_TEST_F(InterestGroupBrowserTestRunAdAuctionBypassBlink,
-                       SellerNotHttps) {
-  GURL test_url_b = https_server_->GetURL("a.test", "/echo");
-  url::Origin test_origin_b = url::Origin::Create(test_url_b);
-  ASSERT_TRUE(NavigateToURL(shell(), test_url_b));
-
-  auto config = blink::mojom::AuctionAdConfig::New();
-  config->seller = test_origin_b;
-  config->decision_logic_url = embedded_test_server()->GetURL(
-      "b.test", "/interest_group/decision_logic.js");
-  ASSERT_TRUE(config->decision_logic_url.SchemeIs(url::kHttpScheme));
-  config->auction_ad_config_non_shared_params =
-      blink::mojom::AuctionAdConfigNonSharedParams::New();
-  config->auction_ad_config_non_shared_params->interest_group_buyers = {
-      test_origin_a_};
-
-  EXPECT_THAT(RunAuctionBypassBlink(std::move(config)), Eq(absl::nullopt));
-}
-
-IN_PROC_BROWSER_TEST_F(InterestGroupBrowserTestRunAdAuctionBypassBlink,
-                       WrongDecisionUrlOrigin) {
-  // The `decision_logic_url` origin doesn't match `seller`s, which is invalid.
-  auto config = blink::mojom::AuctionAdConfig::New();
-  config->seller = test_origin_a_;
-  config->decision_logic_url =
-      https_server_->GetURL("b.test", "/interest_group/decision_logic.js");
-  config->auction_ad_config_non_shared_params =
-      blink::mojom::AuctionAdConfigNonSharedParams::New();
-  config->auction_ad_config_non_shared_params->interest_group_buyers = {
-      test_origin_a_};
-
-  EXPECT_THAT(RunAuctionBypassBlink(std::move(config)), Eq(absl::nullopt));
-}
-
-IN_PROC_BROWSER_TEST_F(InterestGroupBrowserTestRunAdAuctionBypassBlink,
-                       InterestGroupBuyerOriginNotHttps) {
-  GURL test_url_b = https_server_->GetURL("b.test", "/page_with_iframe.html");
-  ASSERT_TRUE(test_url_b.SchemeIs(url::kHttpsScheme));
-  url::Origin test_origin_b = url::Origin::Create(test_url_b);
-  ASSERT_TRUE(NavigateToURL(shell(), test_url_b));
-
-  // Same hostname as `test_url_a_`, different scheme. This buyer is not valid
-  // because it is not https, so the auction fails.
-  GURL test_url_a_http = embedded_test_server()->GetURL("a.test", "/echo");
-  ASSERT_TRUE(test_url_a_http.SchemeIs(url::kHttpScheme));
-  url::Origin test_origin_a_http = url::Origin::Create(test_url_a_http);
-
-  auto config = blink::mojom::AuctionAdConfig::New();
-  config->seller = test_origin_b;
-  config->decision_logic_url =
-      https_server_->GetURL("b.test", "/interest_group/decision_logic.js");
-  config->auction_ad_config_non_shared_params =
-      blink::mojom::AuctionAdConfigNonSharedParams::New();
-  config->auction_ad_config_non_shared_params->interest_group_buyers = {
-      test_origin_a_http};
-
-  EXPECT_THAT(RunAuctionBypassBlink(std::move(config)), Eq(absl::nullopt));
-}
-
-IN_PROC_BROWSER_TEST_F(InterestGroupBrowserTestRunAdAuctionBypassBlink,
-                       InterestGroupBuyerOriginNotHttpsMultipleBuyers) {
-  GURL test_url_b = https_server_->GetURL("b.test", "/page_with_iframe.html");
-  ASSERT_TRUE(test_url_b.SchemeIs(url::kHttpsScheme));
-  url::Origin test_origin_b = url::Origin::Create(test_url_b);
-  ASSERT_TRUE(NavigateToURL(shell(), test_url_b));
-
-  // Same hostname as `test_url_a_`, different scheme. This buyer is not valid
-  // because it is not https, so the auction fails, even though the other buyer
-  // is valid.
-  GURL test_url_a_http = embedded_test_server()->GetURL("a.test", "/echo");
-  ASSERT_TRUE(test_url_a_http.SchemeIs(url::kHttpScheme));
-  url::Origin test_origin_a_http = url::Origin::Create(test_url_a_http);
-
-  auto config = blink::mojom::AuctionAdConfig::New();
-  config->seller = test_origin_b;
-  config->decision_logic_url =
-      https_server_->GetURL("b.test", "/interest_group/decision_logic.js");
-  config->auction_ad_config_non_shared_params =
-      blink::mojom::AuctionAdConfigNonSharedParams::New();
-  config->auction_ad_config_non_shared_params->interest_group_buyers = {
-      test_origin_a_, test_origin_a_http};
-
-  EXPECT_THAT(RunAuctionBypassBlink(std::move(config)), Eq(absl::nullopt));
-}
-
-IN_PROC_BROWSER_TEST_F(InterestGroupBrowserTestRunAdAuctionBypassBlink,
-                       BuyerWithNoRegisteredInterestGroupsIgnored) {
-  GURL test_url_b = https_server_->GetURL("b.test", "/page_with_iframe.html");
-  ASSERT_TRUE(test_url_b.SchemeIs(url::kHttpsScheme));
-  url::Origin test_origin_b = url::Origin::Create(test_url_b);
-  ASSERT_TRUE(NavigateToURL(shell(), test_url_b));
-
-  // New valid origin, not associated with any registered interest group. Its
-  // presence in the auctions `interest_group_buyers` shouldn't affect the
-  // auction outcome.
-  GURL test_url_c = https_server_->GetURL("c.test", "/echo");
-  ASSERT_TRUE(test_url_c.SchemeIs(url::kHttpsScheme));
-  url::Origin test_origin_c = url::Origin::Create(test_url_c);
-
-  auto config = blink::mojom::AuctionAdConfig::New();
-  config->seller = test_origin_b;
-  config->decision_logic_url =
-      https_server_->GetURL("b.test", "/interest_group/decision_logic.js");
-  config->auction_ad_config_non_shared_params =
-      blink::mojom::AuctionAdConfigNonSharedParams::New();
-  config->auction_ad_config_non_shared_params->interest_group_buyers = {
-      test_origin_a_, test_origin_c};
-
-  EXPECT_THAT(RunAuctionBypassBlink(std::move(config)), Optional(Eq(ad_url_)));
-}
-
-IN_PROC_BROWSER_TEST_F(InterestGroupBrowserTestRunAdAuctionBypassBlink,
-                       TrustedScoringSignalsUrlWrongOrigin) {
-  GURL test_url_b = https_server_->GetURL("b.test", "/page_with_iframe.html");
-  ASSERT_TRUE(test_url_b.SchemeIs(url::kHttpsScheme));
-  url::Origin test_origin_b = url::Origin::Create(test_url_b);
-  ASSERT_TRUE(NavigateToURL(shell(), test_url_b));
-
-  auto config = blink::mojom::AuctionAdConfig::New();
-  config->seller = test_origin_b;
-  config->decision_logic_url =
-      https_server_->GetURL("b.test", "/interest_group/decision_logic.js");
-  config->trusted_scoring_signals_url = https_server_->GetURL(
-      "not-b.test", "/interest_group/trusted_scoring_signals.json");
-  config->auction_ad_config_non_shared_params =
-      blink::mojom::AuctionAdConfigNonSharedParams::New();
-  config->auction_ad_config_non_shared_params->interest_group_buyers = {
-      test_origin_a_};
-
-  EXPECT_THAT(RunAuctionBypassBlink(std::move(config)), Eq(absl::nullopt));
-}
-
-IN_PROC_BROWSER_TEST_F(InterestGroupBrowserTestRunAdAuctionBypassBlink,
-                       InvalidComponentAuctionUrl) {
-  auto config = CreateValidAuctionConfig();
-  auto component_auction_config = CreateValidAuctionConfig();
-  // This is invalid because it's cross-origin to the seller.
-  component_auction_config->decision_logic_url =
-      https_server_->GetURL("d.test", "/interest_group/decision_logic.js");
-  config->auction_ad_config_non_shared_params->component_auctions.emplace_back(
-      std::move(component_auction_config));
-
-  EXPECT_THAT(RunAuctionBypassBlink(std::move(config)), Eq(absl::nullopt));
-}
-
-// Test that component auctions with their own component auctions are rejected.
-IN_PROC_BROWSER_TEST_F(InterestGroupBrowserTestRunAdAuctionBypassBlink,
-                       InvalidComponentAuctionDepth) {
-  auto config = CreateValidAuctionConfig();
-  auto component_auction_config = CreateValidAuctionConfig();
-  component_auction_config->auction_ad_config_non_shared_params
-      ->component_auctions.emplace_back(CreateValidAuctionConfig());
-  config->auction_ad_config_non_shared_params->component_auctions.emplace_back(
-      std::move(component_auction_config));
-
-  EXPECT_THAT(RunAuctionBypassBlink(std::move(config)), Eq(absl::nullopt));
-}
-
 // Validate that createAdRequest is available and be successfully called as part
 // of PARAKEET.
 IN_PROC_BROWSER_TEST_F(InterestGroupBrowserTest, CreateAdRequestWorks) {
diff --git a/content/browser/media/capture/content_capture_device_browsertest_base.cc b/content/browser/media/capture/content_capture_device_browsertest_base.cc
index 1cb1439..a7ebae1 100644
--- a/content/browser/media/capture/content_capture_device_browsertest_base.cc
+++ b/content/browser/media/capture/content_capture_device_browsertest_base.cc
@@ -21,6 +21,7 @@
 #include "content/public/test/test_utils.h"
 #include "content/shell/browser/shell.h"
 #include "content/shell/common/shell_switches.h"
+#include "media/base/video_types.h"
 #include "net/dns/mock_host_resolver.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
 #include "net/test/embedded_test_server/http_request.h"
@@ -94,7 +95,7 @@
 
   media::VideoCaptureParams params;
   params.requested_format = media::VideoCaptureFormat(
-      capture_size, kMaxFramesPerSecond, media::PIXEL_FORMAT_I420);
+      capture_size, kMaxFramesPerSecond, GetVideoPixelFormat());
   params.resolution_change_policy =
       IsFixedAspectRatioTest()
           ? media::ResolutionChangePolicy::FIXED_ASPECT_RATIO
@@ -102,6 +103,11 @@
   return params;
 }
 
+media::VideoPixelFormat
+ContentCaptureDeviceBrowserTestBase::GetVideoPixelFormat() const {
+  return media::VideoPixelFormat::PIXEL_FORMAT_I420;
+}
+
 base::TimeDelta ContentCaptureDeviceBrowserTestBase::GetMinCapturePeriod() {
   return base::Microseconds(
       base::Time::kMicrosecondsPerSecond /
diff --git a/content/browser/media/capture/content_capture_device_browsertest_base.h b/content/browser/media/capture/content_capture_device_browsertest_base.h
index 28a0fab..cc9947d 100644
--- a/content/browser/media/capture/content_capture_device_browsertest_base.h
+++ b/content/browser/media/capture/content_capture_device_browsertest_base.h
@@ -11,6 +11,7 @@
 #include "build/build_config.h"
 #include "content/browser/media/capture/fake_video_capture_stack.h"
 #include "content/public/test/content_browser_test.h"
+#include "media/base/video_types.h"
 #include "media/capture/video_capture_types.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/skia/include/core/SkColor.h"
@@ -54,6 +55,8 @@
   gfx::Size GetExpectedSourceSize();
 
   // Returns capture parameters based on the captured source size.
+  // Capture format can be customized by the subclasses, see
+  // |GetVideoPixelFormat()|.
   media::VideoCaptureParams SnapshotCaptureParams();
 
   // Returns the actual minimum capture period the device is using. This should
@@ -99,6 +102,9 @@
   virtual bool IsFixedAspectRatioTest() const;
   virtual bool IsCrossSiteCaptureTest() const;
 
+  // Used to customize the video pixel format that will be used for capture.
+  virtual media::VideoPixelFormat GetVideoPixelFormat() const;
+
   // Returns the size of the original content (i.e., not including any
   // stretching/scaling being done to fit it within a video frame).
   virtual gfx::Size GetCapturedSourceSize() const = 0;
diff --git a/content/browser/media/capture/fake_video_capture_stack.cc b/content/browser/media/capture/fake_video_capture_stack.cc
index 2af1cad..5515600f 100644
--- a/content/browser/media/capture/fake_video_capture_stack.cc
+++ b/content/browser/media/capture/fake_video_capture_stack.cc
@@ -6,17 +6,25 @@
 
 #include <stdint.h>
 
+#include <memory>
 #include <utility>
 
 #include "base/bind.h"
+#include "base/callback_helpers.h"
 #include "base/memory/raw_ptr.h"
+#include "base/memory/scoped_refptr.h"
 #include "content/browser/media/capture/frame_test_util.h"
+#include "gpu/command_buffer/common/mailbox_holder.h"
+#include "gpu/ipc/common/gpu_memory_buffer_support.h"
 #include "media/base/video_frame.h"
+#include "media/base/video_types.h"
+#include "media/base/video_util.h"
 #include "media/capture/video/video_frame_receiver.h"
 #include "media/capture/video_capture_types.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/skia/include/core/SkColorSpace.h"
 #include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/gpu_memory_buffer.h"
 
 namespace content {
 
@@ -47,15 +55,9 @@
     buffers_[buffer_id] = std::move(buffer_handle);
   }
 
-  void OnFrameReadyInBuffer(
+  scoped_refptr<media::VideoFrame> GetVideoFrameFromSharedMemory(
       media::ReadyFrameInBuffer frame,
-      std::vector<media::ReadyFrameInBuffer> scaled_frames) override {
-    const auto it = buffers_.find(frame.buffer_id);
-    CHECK(it != buffers_.end());
-
-    CHECK(it->second->is_read_only_shmem_region());
-    base::ReadOnlySharedMemoryMapping mapping =
-        it->second->get_read_only_shmem_region().Map();
+      base::ReadOnlySharedMemoryMapping mapping) {
     CHECK(mapping.IsValid());
 
     const auto& frame_format = media::VideoCaptureFormat(
@@ -70,9 +72,12 @@
         const_cast<uint8_t*>(static_cast<const uint8_t*>(mapping.memory())),
         mapping.size(), frame.frame_info->timestamp);
     CHECK(video_frame);
+
     video_frame->set_metadata(frame.frame_info->metadata);
-    if (frame.frame_info->color_space.has_value())
+    if (frame.frame_info->color_space.has_value()) {
       video_frame->set_color_space(frame.frame_info->color_space.value());
+    }
+
     // This destruction observer will unmap the shared memory when the
     // VideoFrame goes out-of-scope.
     video_frame->AddDestructionObserver(base::BindOnce(
@@ -83,6 +88,68 @@
         [](std::unique_ptr<Buffer::ScopedAccessPermission> access) {},
         std::move(frame.buffer_read_permission)));
 
+    return video_frame;
+  }
+
+  scoped_refptr<media::VideoFrame> GetVideoFrameFromGpuMemoryBuffer(
+      media::ReadyFrameInBuffer frame,
+      const gfx::GpuMemoryBufferHandle& gmb_handle) {
+    CHECK(!gmb_handle.is_null());
+    CHECK_EQ(frame.frame_info->pixel_format,
+             media::VideoPixelFormat::PIXEL_FORMAT_NV12);
+
+    gpu::GpuMemoryBufferSupport gmb_support;
+    std::unique_ptr<gfx::GpuMemoryBuffer> gmb =
+        gmb_support.CreateGpuMemoryBufferImplFromHandle(
+            gmb_handle.Clone(), frame.frame_info->coded_size,
+            gfx::BufferFormat::YUV_420_BIPLANAR,
+            gfx::BufferUsage::SCANOUT_VEA_CPU_READ, base::DoNothing());
+    CHECK(gmb);
+
+    gfx::Size size = gmb->GetSize();
+    gpu::MailboxHolder mailbox_holders[media::VideoFrame::kMaxPlanes];
+    auto video_frame = media::VideoFrame::WrapExternalGpuMemoryBuffer(
+        frame.frame_info->visible_rect, size, std::move(gmb), mailbox_holders,
+        base::BindOnce([](const gpu::SyncToken& token,
+                          std::unique_ptr<gfx::GpuMemoryBuffer> gmb) {}),
+        frame.frame_info->timestamp);
+    CHECK(video_frame);
+
+    video_frame->set_metadata(frame.frame_info->metadata);
+    if (frame.frame_info->color_space.has_value()) {
+      video_frame->set_color_space(frame.frame_info->color_space.value());
+    }
+
+    auto mapped_frame = media::ConvertToMemoryMappedFrame(video_frame);
+    CHECK(mapped_frame);
+
+    // This destruction observer will notify the video capture device once all
+    // downstream code is done using the VideoFrame.
+    mapped_frame->AddDestructionObserver(base::BindOnce(
+        [](std::unique_ptr<Buffer::ScopedAccessPermission> access) {},
+        std::move(frame.buffer_read_permission)));
+
+    return mapped_frame;
+  }
+
+  void OnFrameReadyInBuffer(
+      media::ReadyFrameInBuffer frame,
+      std::vector<media::ReadyFrameInBuffer> scaled_frames) override {
+    const auto it = buffers_.find(frame.buffer_id);
+    CHECK(it != buffers_.end());
+
+    CHECK(it->second->is_read_only_shmem_region() ||
+          it->second->is_gpu_memory_buffer_handle());
+
+    scoped_refptr<media::VideoFrame> video_frame = nullptr;
+    if (it->second->is_read_only_shmem_region()) {
+      video_frame = GetVideoFrameFromSharedMemory(
+          std::move(frame), it->second->get_read_only_shmem_region().Map());
+    } else {
+      video_frame = GetVideoFrameFromGpuMemoryBuffer(
+          std::move(frame), it->second->get_gpu_memory_buffer_handle());
+    }
+
     // This implementation does not forward scaled frames.
     capture_stack_->OnReceivedFrame(std::move(video_frame));
   }
@@ -155,6 +222,10 @@
 
   EXPECT_TRUE(frame->ColorSpace().IsValid());
 
+  if (on_frame_received_) {
+    on_frame_received_.Run(frame.get());
+  }
+
   frames_.emplace_back(std::move(frame));
 }
 
diff --git a/content/browser/media/capture/fake_video_capture_stack.h b/content/browser/media/capture/fake_video_capture_stack.h
index 975e0c77..f1be3ea9 100644
--- a/content/browser/media/capture/fake_video_capture_stack.h
+++ b/content/browser/media/capture/fake_video_capture_stack.h
@@ -8,6 +8,7 @@
 #include <memory>
 #include <string>
 
+#include "base/callback_forward.h"
 #include "base/containers/circular_deque.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/time/time.h"
@@ -58,6 +59,15 @@
   // capture stack.
   void ExpectNoLogMessages();
 
+  using FrameReceivedCallback =
+      base::RepeatingCallback<void(media::VideoFrame*)>;
+
+  // Sets a callback that will be invoked with a pointer to VideoFrame every
+  // time new frame gets added to the queue.
+  void SetFrameReceivedCallback(FrameReceivedCallback callback) {
+    on_frame_received_ = std::move(callback);
+  }
+
  private:
   // A minimal implementation of VideoFrameReceiver that wraps buffers into
   // VideoFrame instances and forwards all relevant callbacks and data to the
@@ -73,6 +83,7 @@
   base::circular_deque<std::string> log_messages_;
   base::circular_deque<scoped_refptr<media::VideoFrame>> frames_;
   base::TimeDelta last_frame_timestamp_ = base::TimeDelta::Min();
+  FrameReceivedCallback on_frame_received_;
 };
 
 }  // namespace content
diff --git a/content/browser/media/capture/frame_test_util.cc b/content/browser/media/capture/frame_test_util.cc
index 1ebfdf5..593cf06 100644
--- a/content/browser/media/capture/frame_test_util.cc
+++ b/content/browser/media/capture/frame_test_util.cc
@@ -10,7 +10,9 @@
 
 #include "base/numerics/safe_conversions.h"
 #include "media/base/video_frame.h"
+#include "media/base/video_types.h"
 #include "third_party/skia/include/core/SkColorSpace.h"
+#include "third_party/skia/include/core/SkTypes.h"
 #include "ui/gfx/color_space.h"
 #include "ui/gfx/color_transform.h"
 #include "ui/gfx/geometry/rect.h"
@@ -38,6 +40,34 @@
   }
 }
 
+void LoadStimsFromYUV(const uint8_t y_src[],
+                      const uint16_t uv_src[],
+                      int width,
+                      TriStim stims[]) {
+// https://docs.microsoft.com/en-us/windows/win32/medfound/recommended-8-bit-yuv-formats-for-video-rendering#nv12
+// "All of the Y samples appear first in memory as an array of unsigned char
+// values with an even number of lines. The Y plane is followed immediately by
+// an array of unsigned char values that contains packed U (Cb) and V (Cr)
+// samples. When the combined U-V array is addressed as an array of
+// little-endian WORD values, the LSBs contain the U values, and the MSBs
+// contain the V values."
+#if defined(SK_CPU_BENDIAN)
+  for (int i = 0; i < width; ++i) {
+    stims[i].SetPoint(
+        y_src[i] / 255.0f,
+        (uv_src[i / 2] >> 8) / 255.0f,  // MSB contains U values on LE
+        (uv_src[i / 2] & 0xFF) / 255.0f);
+  }
+#else
+  for (int i = 0; i < width; ++i) {
+    stims[i].SetPoint(
+        y_src[i] / 255.0f,
+        (uv_src[i / 2] & 0xFF) / 255.0f,  // LSB contains U values on LE
+        (uv_src[i / 2] >> 8) / 255.0f);
+  }
+#endif
+}
+
 // Maps [0.0,1.0]⇒[0,255], rounding to the nearest integer.
 uint8_t QuantizeAndClamp(float value) {
   return base::saturated_cast<uint8_t>(
@@ -82,13 +112,24 @@
   // Convert one row at a time.
   std::vector<gfx::ColorTransform::TriStim> stims(bitmap.width());
   for (int row = 0; row < bitmap.height(); ++row) {
-    LoadStimsFromYUV(frame.visible_data(media::VideoFrame::kYPlane) +
-                         row * frame.stride(media::VideoFrame::kYPlane),
-                     frame.visible_data(media::VideoFrame::kUPlane) +
-                         (row / 2) * frame.stride(media::VideoFrame::kUPlane),
-                     frame.visible_data(media::VideoFrame::kVPlane) +
-                         (row / 2) * frame.stride(media::VideoFrame::kVPlane),
-                     bitmap.width(), stims.data());
+    if (frame.format() == media::VideoPixelFormat::PIXEL_FORMAT_I420) {
+      LoadStimsFromYUV(frame.visible_data(media::VideoFrame::kYPlane) +
+                           row * frame.stride(media::VideoFrame::kYPlane),
+                       frame.visible_data(media::VideoFrame::kUPlane) +
+                           (row / 2) * frame.stride(media::VideoFrame::kUPlane),
+                       frame.visible_data(media::VideoFrame::kVPlane) +
+                           (row / 2) * frame.stride(media::VideoFrame::kVPlane),
+                       bitmap.width(), stims.data());
+    } else {
+      CHECK_EQ(frame.format(), media::VideoPixelFormat::PIXEL_FORMAT_NV12);
+      LoadStimsFromYUV(
+          frame.visible_data(media::VideoFrame::kYPlane) +
+              row * frame.stride(media::VideoFrame::kYPlane),
+          reinterpret_cast<const uint16_t*>(
+              frame.visible_data(media::VideoFrame::kUVPlane) +
+              (row / 2) * frame.stride(media::VideoFrame::kUVPlane)),
+          bitmap.width(), stims.data());
+    }
     transform->Transform(stims.data(), stims.size());
     StimsToN32Row(stims.data(), bitmap.width(),
                   reinterpret_cast<uint8_t*>(bitmap.getAddr32(0, row)));
diff --git a/content/browser/media/capture/web_contents_video_capture_device_browsertest.cc b/content/browser/media/capture/web_contents_video_capture_device_browsertest.cc
index e6028a5..dce8233 100644
--- a/content/browser/media/capture/web_contents_video_capture_device_browsertest.cc
+++ b/content/browser/media/capture/web_contents_video_capture_device_browsertest.cc
@@ -6,7 +6,9 @@
 
 #include <tuple>
 
+#include "base/bind.h"
 #include "base/run_loop.h"
+#include "base/test/scoped_feature_list.h"
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
 #include "cc/test/pixel_test_utils.h"
@@ -23,15 +25,17 @@
 #include "content/public/browser/web_contents.h"
 #include "content/public/test/browser_test.h"
 #include "content/shell/browser/shell.h"
+#include "media/base/video_frame.h"
+#include "media/base/video_types.h"
 #include "media/base/video_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/skia/include/core/SkBitmap.h"
+#include "third_party/skia/include/core/SkColor.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/geometry/rect_conversions.h"
 #include "ui/gfx/geometry/rect_f.h"
 
 #if BUILDFLAG(IS_WIN)
-#include "base/test/scoped_feature_list.h"
 #include "ui/aura/test/aura_test_utils.h"
 #include "ui/aura/window.h"
 #include "ui/aura/window_tree_host.h"
@@ -126,7 +130,7 @@
         }
 
         if (testing::Test::HasFailure()) {
-          ADD_FAILURE() << "Test failure occurred at this frame; PNG dump: "
+          ADD_FAILURE() << "Test failure occurred at this frame; PNG dump:\n"
                         << cc::GetPNGDataUrl(rgb_frame);
           return;
         }
@@ -147,7 +151,7 @@
           VLOG(1) << "Observed desired frame.";
           return;
         } else {
-          VLOG(3) << "PNG dump of undesired frame: "
+          VLOG(3) << "PNG dump of undesired frame:\n"
                   << cc::GetPNGDataUrl(rgb_frame);
         }
       }
@@ -411,7 +415,8 @@
 
 class WebContentsVideoCaptureDeviceBrowserTestP
     : public WebContentsVideoCaptureDeviceBrowserTest,
-      public testing::WithParamInterface<std::tuple<bool, bool, bool>> {
+      public testing::WithParamInterface<
+          std::tuple<bool, bool, bool, media::VideoPixelFormat>> {
  public:
   bool IsSoftwareCompositingTest() const override {
     return std::get<0>(GetParam());
@@ -422,6 +427,9 @@
   bool IsCrossSiteCaptureTest() const override {
     return std::get<2>(GetParam());
   }
+  media::VideoPixelFormat GetVideoPixelFormat() const override {
+    return std::get<3>(GetParam());
+  }
 };
 
 #if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_ANDROID)
@@ -434,7 +442,24 @@
         testing::Values(false /* variable aspect ratio */,
                         true /* fixed aspect ratio */),
         testing::Values(false /* page has only a main frame */,
-                        true /* page contains a cross-site iframe */)));
+                        true /* page contains a cross-site iframe */),
+        testing::Values(media::VideoPixelFormat::PIXEL_FORMAT_I420)));
+#elif BUILDFLAG(IS_MAC)
+// On MacOS, there is a newly added support for NV12-in-GMB. It relies on GPU
+// acceleration, but has a feature detection built-in if the format is
+// specified as media::VideoPixelFormat::PIXEL_FORMAT_UNKNOWN.
+INSTANTIATE_TEST_SUITE_P(
+    All,
+    WebContentsVideoCaptureDeviceBrowserTestP,
+    testing::Combine(
+        testing::Values(false /* GPU-accelerated compositing */,
+                        true /* software compositing */),
+        testing::Values(false /* variable aspect ratio */,
+                        true /* fixed aspect ratio */),
+        testing::Values(false /* page has only a main frame */,
+                        true /* page contains a cross-site iframe */),
+        testing::Values(media::VideoPixelFormat::PIXEL_FORMAT_I420,
+                        media::VideoPixelFormat::PIXEL_FORMAT_UNKNOWN)));
 #else
 INSTANTIATE_TEST_SUITE_P(
     All,
@@ -445,8 +470,9 @@
         testing::Values(false /* variable aspect ratio */,
                         true /* fixed aspect ratio */),
         testing::Values(false /* page has only a main frame */,
-                        true /* page contains a cross-site iframe */)));
-#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+                        true /* page contains a cross-site iframe */),
+        testing::Values(media::VideoPixelFormat::PIXEL_FORMAT_I420)));
+#endif
 
 // Tests that the device successfully captures a series of content changes,
 // whether the browser is running with software compositing or GPU-accelerated
@@ -462,6 +488,22 @@
                << (IsFixedAspectRatioTest() ? "Fixed Video Aspect Ratio"
                                             : "Variable Video Aspect Ratio"));
 
+  media::VideoPixelFormat specified_format = GetVideoPixelFormat();
+  media::VideoPixelFormat expected_format = specified_format;
+  if (specified_format == media::VideoPixelFormat::PIXEL_FORMAT_UNKNOWN) {
+    if (IsSoftwareCompositingTest()) {
+      expected_format = media::VideoPixelFormat::PIXEL_FORMAT_I420;
+    } else {
+      expected_format = media::VideoPixelFormat::PIXEL_FORMAT_NV12;
+    }
+  }
+
+  capture_stack()->SetFrameReceivedCallback(base::BindRepeating(
+      [](media::VideoPixelFormat expected_format, media::VideoFrame* frame) {
+        EXPECT_EQ(frame->format(), expected_format);
+      },
+      expected_format));
+
   NavigateToInitialDocument();
   AllocateAndStartAndWaitForFirstFrame();
   EXPECT_TRUE(shell()->web_contents()->IsBeingCaptured());
diff --git a/content/browser/renderer_host/media/media_stream_dispatcher_host.cc b/content/browser/renderer_host/media/media_stream_dispatcher_host.cc
index 2d5acf78..21277cd 100644
--- a/content/browser/renderer_host/media/media_stream_dispatcher_host.cc
+++ b/content/browser/renderer_host/media/media_stream_dispatcher_host.cc
@@ -45,7 +45,7 @@
 
   RenderFrameHost* render_frame_host =
       RenderFrameHost::FromID(render_process_id, render_frame_id);
-  if (render_frame_host && render_frame_host->IsRenderFrameCreated())
+  if (render_frame_host && render_frame_host->IsRenderFrameLive())
     render_frame_host->GetRemoteInterfaces()->GetInterface(std::move(receiver));
 }
 
diff --git a/content/browser/renderer_host/navigation_controller_impl.cc b/content/browser/renderer_host/navigation_controller_impl.cc
index 679cd1f9..1114de5 100644
--- a/content/browser/renderer_host/navigation_controller_impl.cc
+++ b/content/browser/renderer_host/navigation_controller_impl.cc
@@ -4400,7 +4400,7 @@
               frame_state.navigation_api_id.value_or(std::u16string()), url,
               frame_state.item_sequence_number,
               frame_state.document_sequence_number,
-              frame_state.navigation_api_state.value_or(std::u16string()));
+              frame_state.navigation_api_state);
 
       DCHECK(entry->url.empty() ||
              pending_origin.CanBeDerivedFrom(GURL(entry->url)));
diff --git a/content/browser/renderer_host/page_impl.cc b/content/browser/renderer_host/page_impl.cc
index dc292f9..87ec940 100644
--- a/content/browser/renderer_host/page_impl.cc
+++ b/content/browser/renderer_host/page_impl.cc
@@ -253,7 +253,7 @@
                                           bool animate) {
   // TODO(https://crbug.com/1154852): Asking for the LocalMainFrame interface
   // before the RenderFrame is created is racy.
-  if (!GetMainDocument().IsRenderFrameCreated())
+  if (!GetMainDocument().IsRenderFrameLive())
     return;
 
   GetMainDocument().GetAssociatedLocalMainFrame()->UpdateBrowserControlsState(
diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc
index 792d8e29..d0300c6 100644
--- a/content/browser/renderer_host/render_frame_host_impl.cc
+++ b/content/browser/renderer_host/render_frame_host_impl.cc
@@ -2230,7 +2230,7 @@
 
 void RenderFrameHostImpl::GetCanonicalUrl(
     base::OnceCallback<void(const absl::optional<GURL>&)> callback) {
-  if (IsRenderFrameCreated()) {
+  if (IsRenderFrameLive()) {
     // Validate that the URL returned by the renderer is HTTP(S) only. It is
     // allowed to be cross-origin.
     auto validate_and_forward =
@@ -2251,7 +2251,7 @@
 
 void RenderFrameHostImpl::GetOpenGraphMetadata(
     base::OnceCallback<void(blink::mojom::OpenGraphMetadataPtr)> callback) {
-  if (IsRenderFrameCreated()) {
+  if (IsRenderFrameLive()) {
     GetAssociatedLocalFrame()->GetOpenGraphMetadata(
         base::BindOnce(&ForwardOpenGraphMetadataIfValid, std::move(callback)));
   } else {
@@ -2282,7 +2282,7 @@
     const base::flat_map<blink::FrameToken, base::FilePath>& frame_token_map,
     bool save_with_empty_url,
     mojo::PendingRemote<mojom::FrameHTMLSerializerHandler> serializer_handler) {
-  if (!IsRenderFrameCreated())
+  if (!IsRenderFrameLive())
     return;
   GetMojomFrameInRenderer()->GetSerializedHtmlWithLocalLinks(
       url_map, frame_token_map, save_with_empty_url,
@@ -2565,7 +2565,7 @@
 }
 
 service_manager::InterfaceProvider* RenderFrameHostImpl::GetRemoteInterfaces() {
-  DCHECK(IsRenderFrameCreated());
+  DCHECK(IsRenderFrameLive());
   return remote_interfaces_.get();
 }
 
@@ -3172,7 +3172,7 @@
   if (IsPendingDeletion())
     return;
 
-  if (IsRenderFrameCreated()) {
+  if (IsRenderFrameLive()) {
     GetMojomFrameInRenderer()->Delete(intent);
 
     // We change the lifecycle state to kRunningUnloadHandlers at the end of
@@ -3339,7 +3339,7 @@
         // Inner frame trees shouldn't be possible here.
         DCHECK_EQ(render_frame_host->frame_tree(), main_rfh->frame_tree());
 
-        if (render_frame_host->IsRenderFrameCreated())
+        if (render_frame_host->IsRenderFrameLive())
           render_frame_host->frame_->ResumeBlockedRequests();
       },
       base::Unretained(this)));
@@ -4524,7 +4524,7 @@
 
   if (proxy) {
     SetLifecycleState(LifecycleStateImpl::kRunningUnloadHandlers);
-    if (IsRenderFrameCreated()) {
+    if (IsRenderFrameLive()) {
       GetMojomFrameInRenderer()->Unload(
           proxy->GetRoutingID(), is_loading,
           proxy->frame_tree_node()->current_replication_state().Clone(),
@@ -5478,7 +5478,7 @@
   DCHECK(network_service_disconnect_handler_holder_);
   network_service_disconnect_handler_holder_.FlushForTesting();  // IN-TEST
 
-  DCHECK(IsRenderFrameCreated());
+  DCHECK(IsRenderFrameLive());
   DCHECK(frame_);
   frame_.FlushForTesting();  // IN-TEST
 }
@@ -7891,7 +7891,7 @@
   TRACE_EVENT1("navigation", "RenderFrameHostImpl::Stop", "render_frame_host",
                this);
   // Don't call GetAssociatedLocalFrame before the RenderFrame is created.
-  if (!IsRenderFrameCreated())
+  if (!IsRenderFrameLive())
     return;
   GetAssociatedLocalFrame()->StopLoading();
 }
@@ -9207,7 +9207,7 @@
 
 void RenderFrameHostImpl::UpdateAccessibilityMode() {
   // Don't update accessibility mode for a frame that hasn't been created yet.
-  if (!IsRenderFrameCreated())
+  if (!IsRenderFrameLive())
     return;
 
   ui::AXMode ax_mode = delegate_->GetAccessibilityMode();
@@ -9256,7 +9256,7 @@
     AXTreeSnapshotCallback callback,
     mojom::SnapshotAccessibilityTreeParamsPtr params) {
   // TODO(https://crbug.com/859110): Remove once frame_ can no longer be null.
-  if (!IsRenderFrameCreated())
+  if (!IsRenderFrameLive())
     return;
 
   GetMojomFrameInRenderer()->SnapshotAccessibilityTree(
@@ -9268,7 +9268,7 @@
 void RenderFrameHostImpl::RequestDistilledAXTree(
     AXTreeDistillerCallback callback) {
   // TODO(https://crbug.com/859110): Remove once frame_ can no longer be null.
-  if (!IsRenderFrameCreated())
+  if (!IsRenderFrameLive())
     return;
 
   GetMojomFrameInRenderer()->SnapshotAndDistillAXTree(
@@ -9349,10 +9349,6 @@
   return ip_end_point.address().IsPubliclyRoutable();
 }
 
-bool RenderFrameHostImpl::IsRenderFrameCreated() {
-  return is_render_frame_created();
-}
-
 bool RenderFrameHostImpl::IsRenderFrameLive() {
   bool is_live =
       GetProcess()->IsInitializedAndNotDead() && is_render_frame_created();
diff --git a/content/browser/renderer_host/render_frame_host_impl.h b/content/browser/renderer_host/render_frame_host_impl.h
index 960fc98..edd55b94d 100644
--- a/content/browser/renderer_host/render_frame_host_impl.h
+++ b/content/browser/renderer_host/render_frame_host_impl.h
@@ -401,7 +401,6 @@
   blink::AssociatedInterfaceProvider* GetRemoteAssociatedInterfaces() override;
   content::PageVisibilityState GetVisibilityState() override;
   bool IsLastCommitIPAddressPubliclyRoutable() const override;
-  bool IsRenderFrameCreated() override;
   bool IsRenderFrameLive() override;
   LifecycleState GetLifecycleState() override;
   bool IsInLifecycleState(LifecycleState lifecycle_state) override;
diff --git a/content/browser/renderer_host/render_frame_host_impl_browsertest.cc b/content/browser/renderer_host/render_frame_host_impl_browsertest.cc
index d4d89d9..dda7275 100644
--- a/content/browser/renderer_host/render_frame_host_impl_browsertest.cc
+++ b/content/browser/renderer_host/render_frame_host_impl_browsertest.cc
@@ -333,7 +333,7 @@
                        MessagesBeforeAndAfterRenderFrameCreated) {
   // Start with a WebContents that hasn't created its main RenderFrame.
   WebContents* web_contents = shell()->web_contents();
-  ASSERT_FALSE(web_contents->GetMainFrame()->IsRenderFrameCreated());
+  ASSERT_FALSE(web_contents->GetMainFrame()->IsRenderFrameLive());
 
   // An attempt to run script via GetAssociatedLocalFrame will do nothing before
   // the RenderFrame is created, since the message sent to the renderer will get
@@ -345,7 +345,7 @@
   // Navigating will create the RenderFrame.
   GURL url(embedded_test_server()->GetURL("/title1.html"));
   EXPECT_TRUE(NavigateToURL(shell(), url));
-  ASSERT_TRUE(web_contents->GetMainFrame()->IsRenderFrameCreated());
+  ASSERT_TRUE(web_contents->GetMainFrame()->IsRenderFrameLive());
 
   // Future attempts to run script via GetAssociatedLocalFrame should succeed.
   // This timed out before the fix, since the message was dropped and no value
diff --git a/content/browser/renderer_host/render_frame_host_manager.cc b/content/browser/renderer_host/render_frame_host_manager.cc
index c8f2abaf..dd1e91e 100644
--- a/content/browser/renderer_host/render_frame_host_manager.cc
+++ b/content/browser/renderer_host/render_frame_host_manager.cc
@@ -3406,7 +3406,7 @@
   // 2) a current RenderFrameHost which has just received a commit IPC from the
   //    renderer, so it must have a live connection to its renderer frame in
   //    order to receive the IPC.
-  DCHECK(pending_rfh->IsRenderFrameCreated());
+  DCHECK(pending_rfh->IsRenderFrameLive());
 
   // We should not have a pending bfcache entry unless bfcache or prerendering
   // is enabled. Note that in prerendering, the prerendering page information is
diff --git a/content/browser/site_per_process_browsertest.cc b/content/browser/site_per_process_browsertest.cc
index 3262ca4..bcc761a 100644
--- a/content/browser/site_per_process_browsertest.cc
+++ b/content/browser/site_per_process_browsertest.cc
@@ -1866,7 +1866,6 @@
   EXPECT_FALSE(
       child->current_frame_host()->render_view_host()->IsRenderViewLive());
   EXPECT_FALSE(child->current_frame_host()->IsRenderFrameLive());
-  EXPECT_FALSE(child->current_frame_host()->IsRenderFrameCreated());
 
   // Now crash the top-level page to clear the child frame.
   {
diff --git a/content/browser/speech/tts_controller_impl.cc b/content/browser/speech/tts_controller_impl.cc
index 70c3bee..644dcef 100644
--- a/content/browser/speech/tts_controller_impl.cc
+++ b/content/browser/speech/tts_controller_impl.cc
@@ -452,6 +452,10 @@
   return engine_delegate_;
 }
 
+void TtsControllerImpl::RefreshVoices() {
+  GetTtsPlatform()->RefreshVoices();
+}
+
 void TtsControllerImpl::Shutdown() {
   if (tts_platform_)
     tts_platform_->Shutdown();
diff --git a/content/browser/speech/tts_controller_impl.h b/content/browser/speech/tts_controller_impl.h
index f5a718b..2a4bb7db 100644
--- a/content/browser/speech/tts_controller_impl.h
+++ b/content/browser/speech/tts_controller_impl.h
@@ -74,6 +74,7 @@
   void RemoveUtteranceEventDelegate(UtteranceEventDelegate* delegate) override;
   void SetTtsEngineDelegate(TtsEngineDelegate* delegate) override;
   TtsEngineDelegate* GetTtsEngineDelegate() override;
+  void RefreshVoices() override;
 
   void Shutdown();
 
diff --git a/content/browser/speech/tts_controller_unittest.cc b/content/browser/speech/tts_controller_unittest.cc
index 23c75cf3..dc280208 100644
--- a/content/browser/speech/tts_controller_unittest.cc
+++ b/content/browser/speech/tts_controller_unittest.cc
@@ -77,6 +77,7 @@
       content::BrowserContext* browser_context,
       const GURL& source_url,
       std::vector<content::VoiceData>* out_voices) override {}
+  void RefreshVoices() override {}
 
   void SetPlatformImplSupported(bool state) { platform_supported_ = state; }
   void SetPlatformImplInitialized(bool state) { platform_initialized_ = state; }
diff --git a/content/browser/speech/tts_platform_impl.h b/content/browser/speech/tts_platform_impl.h
index adb1b7d..9137346 100644
--- a/content/browser/speech/tts_platform_impl.h
+++ b/content/browser/speech/tts_platform_impl.h
@@ -33,6 +33,7 @@
       content::BrowserContext* browser_context,
       const GURL& source_url,
       std::vector<content::VoiceData>* out_voices) override {}
+  void RefreshVoices() override {}
 
  protected:
   TtsPlatformImpl() {}
diff --git a/content/public/android/java/src/org/chromium/content/browser/accessibility/WebContentsAccessibilityImpl.java b/content/public/android/java/src/org/chromium/content/browser/accessibility/WebContentsAccessibilityImpl.java
index fb07ff2..247205d 100644
--- a/content/public/android/java/src/org/chromium/content/browser/accessibility/WebContentsAccessibilityImpl.java
+++ b/content/public/android/java/src/org/chromium/content/browser/accessibility/WebContentsAccessibilityImpl.java
@@ -1660,6 +1660,16 @@
     }
 
     @CalledByNative
+    private void handleTextContentChanged(int id) {
+        AccessibilityEvent event =
+                buildAccessibilityEvent(id, AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
+        if (event != null) {
+            event.setContentChangeTypes(AccessibilityEvent.CONTENT_CHANGE_TYPE_TEXT);
+            requestSendAccessibilityEvent(event);
+        }
+    }
+
+    @CalledByNative
     private void handleEditableTextChanged(int id) {
         sendAccessibilityEvent(id, AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED);
     }
diff --git a/content/public/browser/render_frame_host.h b/content/public/browser/render_frame_host.h
index a7a7565..0ce03629 100644
--- a/content/public/browser/render_frame_host.h
+++ b/content/public/browser/render_frame_host.h
@@ -242,7 +242,7 @@
   // can be checked with IsRenderFrameLive().
   // NOTE: Due to historical relationships between RenderViewHost and
   // RenderWidgetHost, the main frame RenderWidgetHostView may initially exist
-  // before IsRenderFrameCreated() is true, but they would afterward change
+  // before IsRenderFrameLive() is true, but they would afterward change
   // values together. It is better to not rely on this behaviour as it is
   // intended to change. See https://crbug.com/419087.
   virtual RenderWidgetHostView* GetView() = 0;
@@ -638,13 +638,9 @@
   // Returns whether the IP address of the last commit was publicly routable.
   virtual bool IsLastCommitIPAddressPubliclyRoutable() const = 0;
 
-  // Returns true if WebContentsObserver::RenderFrameCreated notification has
-  // been dispatched for this frame, and so a RenderFrameDeleted notification
-  // will later be dispatched for this frame.
-  virtual bool IsRenderFrameCreated() = 0;
-
   // Returns whether the RenderFrame in the renderer process has been created
   // and still has a connection.  This is valid for all frames.
+  // RenderFrameDeleted notification will later be dispatched for this frame.
   virtual bool IsRenderFrameLive() = 0;
 
   // Defines different states the RenderFrameHost can be in during its lifetime,
diff --git a/content/public/browser/tts_controller.h b/content/public/browser/tts_controller.h
index f56d337..11a6c87 100644
--- a/content/public/browser/tts_controller.h
+++ b/content/public/browser/tts_controller.h
@@ -181,6 +181,10 @@
   // embedder.
   virtual TtsEngineDelegate* GetTtsEngineDelegate() = 0;
 
+  // Triggers the TtsPlatform to update its list of voices and relay that update
+  // through VoicesChanged.
+  virtual void RefreshVoices() = 0;
+
   // Visible for testing.
   virtual void SetTtsPlatform(TtsPlatform* tts_platform) = 0;
   virtual int QueueSize() = 0;
diff --git a/content/public/browser/tts_platform.h b/content/public/browser/tts_platform.h
index 1c56d83..85d4f6c 100644
--- a/content/public/browser/tts_platform.h
+++ b/content/public/browser/tts_platform.h
@@ -97,6 +97,10 @@
   // Given engine delegate and platform voices, returns the finalized voice
   // ordering used by the controller when exposing voices to clients.
   virtual void FinalizeVoiceOrdering(std::vector<VoiceData>& voices) = 0;
+
+  // Triggers the TtsPlatform to update its list of voices and relay that update
+  // through VoicesChanged.
+  virtual void RefreshVoices() = 0;
 };
 
 }  // namespace content
diff --git a/content/public/test/browser_test_base.cc b/content/public/test/browser_test_base.cc
index b58ef821..451fb8c1 100644
--- a/content/public/test/browser_test_base.cc
+++ b/content/public/test/browser_test_base.cc
@@ -407,7 +407,7 @@
 
   ui::fuchsia::IgnorePresentCallsForTest();
 
-  // Clear the per-process cached BuildInfo, which was initialized by
+  // Clear the per-process cached system info, which was initialized by
   // TestSuite::Initialize(), to prevent a DCHECK for multiple calls during
   // in-process browser tests. There is not a single TestSuite for all browser
   // tests and some use the cached values, so skipping the earlier
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
index 1138a407..6c0e195ba 100644
--- a/content/renderer/render_frame_impl.cc
+++ b/content/renderer/render_frame_impl.cc
@@ -978,8 +978,10 @@
   item.SetURLString(WebString::FromUTF16(entry.url));
   item.SetItemSequenceNumber(entry.item_sequence_number);
   item.SetDocumentSequenceNumber(entry.document_sequence_number);
-  item.SetNavigationApiState(
-      WebSerializedScriptValue::FromString(WebString::FromUTF16(entry.state)));
+  if (entry.state) {
+    item.SetNavigationApiState(WebSerializedScriptValue::FromString(
+        WebString::FromUTF16(entry.state)));
+  }
   return item;
 }
 
@@ -4140,6 +4142,10 @@
     observer.DidSetPageLifecycleState();
 }
 
+void RenderFrameImpl::NotifyCurrentHistoryItemChanged() {
+  SendUpdateState();
+}
+
 void RenderFrameImpl::DidUpdateCurrentHistoryItem() {
   StartDelayedSyncTimer();
 }
diff --git a/content/renderer/render_frame_impl.h b/content/renderer/render_frame_impl.h
index 3557a63..8e3d2fe 100644
--- a/content/renderer/render_frame_impl.h
+++ b/content/renderer/render_frame_impl.h
@@ -574,6 +574,7 @@
   void WillFreezePage() override;
   void DidOpenDocumentInputStream(const blink::WebURL& url) override;
   void DidSetPageLifecycleState() override;
+  void NotifyCurrentHistoryItemChanged() override;
   void DidUpdateCurrentHistoryItem() override;
   base::UnguessableToken GetDevToolsFrameToken() override;
   void AbortClientNavigation() override;
diff --git a/content/services/auction_worklet/seller_worklet.cc b/content/services/auction_worklet/seller_worklet.cc
index 003b48e..5af35d31d 100644
--- a/content/services/auction_worklet/seller_worklet.cc
+++ b/content/services/auction_worklet/seller_worklet.cc
@@ -33,6 +33,7 @@
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/bindings/remote.h"
 #include "services/network/public/mojom/url_loader_factory.mojom.h"
+#include "third_party/blink/public/common/interest_group/auction_config.h"
 #include "third_party/blink/public/mojom/interest_group/interest_group_types.mojom.h"
 #include "url/gurl.h"
 #include "url/origin.h"
@@ -70,7 +71,7 @@
                          const GURL& decision_logic_url,
                          const absl::optional<GURL>& trusted_coding_signals_url,
                          const absl::optional<uint16_t> experiment_group_id,
-                         blink::mojom::AuctionAdConfigNonSharedParams&
+                         const blink::AuctionConfig::NonSharedParams&
                              auction_ad_config_non_shared_params,
                          std::vector<v8::Local<v8::Value>>* args) {
   v8::Isolate* isolate = v8_helper->isolate();
@@ -169,10 +170,9 @@
     std::vector<v8::Local<v8::Value>> component_auction_vector;
     for (const auto& component_auction : component_auctions) {
       if (!AppendAuctionConfig(
-              v8_helper, context, component_auction->decision_logic_url,
-              component_auction->trusted_scoring_signals_url,
-              experiment_group_id,
-              *component_auction->auction_ad_config_non_shared_params,
+              v8_helper, context, component_auction.decision_logic_url,
+              component_auction.trusted_scoring_signals_url,
+              experiment_group_id, component_auction.non_shared_params,
               &component_auction_vector)) {
         return false;
       }
@@ -267,7 +267,7 @@
 void SellerWorklet::ScoreAd(
     const std::string& ad_metadata_json,
     double bid,
-    blink::mojom::AuctionAdConfigNonSharedParamsPtr
+    const blink::AuctionConfig::NonSharedParams&
         auction_ad_config_non_shared_params,
     mojom::ComponentAuctionOtherSellerPtr browser_signals_other_seller,
     const url::Origin& browser_signal_interest_group_owner,
@@ -284,7 +284,7 @@
   score_ad_task->ad_metadata_json = ad_metadata_json;
   score_ad_task->bid = bid;
   score_ad_task->auction_ad_config_non_shared_params =
-      std::move(auction_ad_config_non_shared_params);
+      auction_ad_config_non_shared_params;
   score_ad_task->browser_signals_other_seller =
       std::move(browser_signals_other_seller);
   score_ad_task->browser_signal_interest_group_owner =
@@ -324,7 +324,7 @@
 }
 
 void SellerWorklet::ReportResult(
-    blink::mojom::AuctionAdConfigNonSharedParamsPtr
+    const blink::AuctionConfig::NonSharedParams&
         auction_ad_config_non_shared_params,
     mojom::ComponentAuctionOtherSellerPtr browser_signals_other_seller,
     const url::Origin& browser_signal_interest_group_owner,
@@ -351,7 +351,7 @@
   auto report_result_task = report_result_tasks_.begin();
 
   report_result_task->auction_ad_config_non_shared_params =
-      std::move(auction_ad_config_non_shared_params);
+      auction_ad_config_non_shared_params;
   report_result_task->browser_signals_other_seller =
       std::move(browser_signals_other_seller);
   report_result_task->browser_signal_interest_group_owner =
@@ -426,7 +426,7 @@
 void SellerWorklet::V8State::ScoreAd(
     const std::string& ad_metadata_json,
     double bid,
-    blink::mojom::AuctionAdConfigNonSharedParamsPtr
+    const blink::AuctionConfig::NonSharedParams&
         auction_ad_config_non_shared_params,
     scoped_refptr<TrustedSignals::Result> trusted_scoring_signals,
     mojom::ComponentAuctionOtherSellerPtr browser_signals_other_seller,
@@ -469,7 +469,7 @@
 
   if (!AppendAuctionConfig(v8_helper_.get(), context, decision_logic_url_,
                            trusted_scoring_signals_url_, experiment_group_id_,
-                           *auction_ad_config_non_shared_params, &args)) {
+                           auction_ad_config_non_shared_params, &args)) {
     PostScoreAdCallbackToUserThread(
         std::move(callback), /*score=*/0,
         /*component_auction_modified_bid_params=*/nullptr,
@@ -689,7 +689,7 @@
 }
 
 void SellerWorklet::V8State::ReportResult(
-    blink::mojom::AuctionAdConfigNonSharedParamsPtr
+    const blink::AuctionConfig::NonSharedParams&
         auction_ad_config_non_shared_params,
     mojom::ComponentAuctionOtherSellerPtr browser_signals_other_seller,
     const url::Origin& browser_signal_interest_group_owner,
@@ -722,7 +722,7 @@
   std::vector<v8::Local<v8::Value>> args;
   if (!AppendAuctionConfig(v8_helper_.get(), context, decision_logic_url_,
                            trusted_scoring_signals_url_, experiment_group_id_,
-                           *auction_ad_config_non_shared_params, &args)) {
+                           auction_ad_config_non_shared_params, &args)) {
     PostReportResultCallbackToUserThread(std::move(callback),
                                          /*signals_for_winner=*/absl::nullopt,
                                          /*report_url=*/absl::nullopt,
diff --git a/content/services/auction_worklet/seller_worklet.h b/content/services/auction_worklet/seller_worklet.h
index 52c3a0f8..6194cfc 100644
--- a/content/services/auction_worklet/seller_worklet.h
+++ b/content/services/auction_worklet/seller_worklet.h
@@ -29,6 +29,7 @@
 #include "mojo/public/cpp/bindings/remote.h"
 #include "services/network/public/mojom/url_loader_factory.mojom.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/blink/public/common/interest_group/auction_config.h"
 #include "third_party/blink/public/mojom/interest_group/interest_group_types.mojom-forward.h"
 #include "url/gurl.h"
 #include "url/origin.h"
@@ -80,7 +81,7 @@
   void ScoreAd(
       const std::string& ad_metadata_json,
       double bid,
-      blink::mojom::AuctionAdConfigNonSharedParamsPtr
+      const blink::AuctionConfig::NonSharedParams&
           auction_ad_config_non_shared_params,
       mojom::ComponentAuctionOtherSellerPtr browser_signals_other_seller,
       const url::Origin& browser_signal_interest_group_owner,
@@ -92,7 +93,7 @@
       ScoreAdCallback callback) override;
   void SendPendingSignalsRequests() override;
   void ReportResult(
-      blink::mojom::AuctionAdConfigNonSharedParamsPtr
+      const blink::AuctionConfig::NonSharedParams&
           auction_ad_config_non_shared_params,
       mojom::ComponentAuctionOtherSellerPtr browser_signals_other_seller,
       const url::Origin& browser_signal_interest_group_owner,
@@ -122,8 +123,7 @@
     // safe to access after that happens.
     std::string ad_metadata_json;
     double bid;
-    blink::mojom::AuctionAdConfigNonSharedParamsPtr
-        auction_ad_config_non_shared_params;
+    blink::AuctionConfig::NonSharedParams auction_ad_config_non_shared_params;
     mojom::ComponentAuctionOtherSellerPtr browser_signals_other_seller;
     url::Origin browser_signal_interest_group_owner;
     GURL browser_signal_render_url;
@@ -157,8 +157,7 @@
     // These fields all correspond to the arguments of ReportResult(). They're
     // std::move()ed when calling out to V8State to run Javascript, so are not
     // safe to access after that happens.
-    blink::mojom::AuctionAdConfigNonSharedParamsPtr
-        auction_ad_config_non_shared_params;
+    blink::AuctionConfig::NonSharedParams auction_ad_config_non_shared_params;
     mojom::ComponentAuctionOtherSellerPtr browser_signals_other_seller;
     url::Origin browser_signal_interest_group_owner;
     GURL browser_signal_render_url;
@@ -210,7 +209,7 @@
     void ScoreAd(
         const std::string& ad_metadata_json,
         double bid,
-        blink::mojom::AuctionAdConfigNonSharedParamsPtr
+        const blink::AuctionConfig::NonSharedParams&
             auction_ad_config_non_shared_params,
         scoped_refptr<TrustedSignals::Result> trusted_scoring_signals,
         mojom::ComponentAuctionOtherSellerPtr browser_signals_other_seller,
@@ -223,7 +222,7 @@
         ScoreAdCallbackInternal callback);
 
     void ReportResult(
-        blink::mojom::AuctionAdConfigNonSharedParamsPtr
+        const blink::AuctionConfig::NonSharedParams&
             auction_ad_config_non_shared_params,
         mojom::ComponentAuctionOtherSellerPtr browser_signals_other_seller,
         const url::Origin& browser_signal_interest_group_owner,
diff --git a/content/services/auction_worklet/seller_worklet_unittest.cc b/content/services/auction_worklet/seller_worklet_unittest.cc
index 9023c08..e99e5e5 100644
--- a/content/services/auction_worklet/seller_worklet_unittest.cc
+++ b/content/services/auction_worklet/seller_worklet_unittest.cc
@@ -30,6 +30,7 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/blink/public/common/features.h"
+#include "third_party/blink/public/common/interest_group/auction_config.h"
 #include "third_party/blink/public/mojom/interest_group/interest_group_types.mojom.h"
 #include "url/gurl.h"
 
@@ -137,7 +138,7 @@
     decision_logic_url_ = GURL("https://url.test/");
     trusted_scoring_signals_url_.reset();
     auction_ad_config_non_shared_params_ =
-        blink::mojom::AuctionAdConfigNonSharedParams::New();
+        blink::AuctionConfig::NonSharedParams();
 
     top_window_origin_ = url::Origin::Create(GURL("https://window.test/"));
     experiment_group_id_ = absl::nullopt;
@@ -247,7 +248,7 @@
       const absl::optional<GURL>& expected_debug_win_report_url,
       base::OnceClosure done_closure) {
     seller_worklet->ScoreAd(
-        ad_metadata_, bid_, auction_ad_config_non_shared_params_.Clone(),
+        ad_metadata_, bid_, auction_ad_config_non_shared_params_,
         browser_signals_other_seller_.Clone(),
         browser_signal_interest_group_owner_, browser_signal_render_url_,
         browser_signal_ad_components_, browser_signal_bidding_duration_msecs_,
@@ -303,7 +304,7 @@
   void RunScoreAdOnWorkletExpectingCallbackNeverInvoked(
       mojom::SellerWorklet* seller_worklet) {
     seller_worklet->ScoreAd(
-        ad_metadata_, bid_, auction_ad_config_non_shared_params_.Clone(),
+        ad_metadata_, bid_, auction_ad_config_non_shared_params_,
         browser_signals_other_seller_.Clone(),
         browser_signal_interest_group_owner_, browser_signal_render_url_,
         browser_signal_ad_components_, browser_signal_bidding_duration_msecs_,
@@ -414,7 +415,7 @@
       const std::vector<std::string>& expected_errors,
       base::OnceClosure done_closure) {
     seller_worklet->ReportResult(
-        auction_ad_config_non_shared_params_.Clone(),
+        auction_ad_config_non_shared_params_,
         browser_signals_other_seller_.Clone(),
         browser_signal_interest_group_owner_, browser_signal_render_url_, bid_,
         browser_signal_desireability_,
@@ -446,7 +447,7 @@
   void RunReportResultExpectingCallbackNeverInvoked(
       mojom::SellerWorklet* seller_worklet) {
     seller_worklet->ReportResult(
-        auction_ad_config_non_shared_params_.Clone(),
+        auction_ad_config_non_shared_params_,
         browser_signals_other_seller_.Clone(),
         browser_signal_interest_group_owner_, browser_signal_render_url_, bid_,
         browser_signal_desireability_,
@@ -554,8 +555,7 @@
   double bid_;
   GURL decision_logic_url_;
   absl::optional<GURL> trusted_scoring_signals_url_;
-  blink::mojom::AuctionAdConfigNonSharedParamsPtr
-      auction_ad_config_non_shared_params_;
+  blink::AuctionConfig::NonSharedParams auction_ad_config_non_shared_params_;
   url::Origin top_window_origin_;
   absl::optional<uint16_t> experiment_group_id_;
   mojom::ComponentAuctionOtherSellerPtr browser_signals_other_seller_;
@@ -2068,29 +2068,28 @@
   decision_logic_url_ = GURL("https://example.com/auction.js");
   trusted_scoring_signals_url_ =
       GURL("https://example.com/scoring_signals.json");
-  auction_ad_config_non_shared_params_->interest_group_buyers = {
+  auction_ad_config_non_shared_params_.interest_group_buyers = {
       url::Origin::Create(GURL("https://buyer1.com")),
       url::Origin::Create(GURL("https://another-buyer.com"))};
-  auction_ad_config_non_shared_params_->auction_signals =
+  auction_ad_config_non_shared_params_.auction_signals =
       R"({"is_auction_signals": true})";
-  auction_ad_config_non_shared_params_->seller_signals =
+  auction_ad_config_non_shared_params_.seller_signals =
       R"({"is_seller_signals": true})";
-  auction_ad_config_non_shared_params_->seller_timeout =
-      base::Milliseconds(200);
+  auction_ad_config_non_shared_params_.seller_timeout = base::Milliseconds(200);
   base::flat_map<url::Origin, std::string> per_buyer_signals;
   per_buyer_signals[url::Origin::Create(GURL("https://a.com"))] =
       R"({"signals_a": "A"})";
   per_buyer_signals[url::Origin::Create(GURL("https://b.com"))] =
       R"({"signals_b": "B"})";
-  auction_ad_config_non_shared_params_->per_buyer_signals =
+  auction_ad_config_non_shared_params_.per_buyer_signals =
       std::move(per_buyer_signals);
 
   base::flat_map<url::Origin, base::TimeDelta> per_buyer_timeouts;
   per_buyer_timeouts[url::Origin::Create(GURL("https://a.com"))] =
       base::Milliseconds(100);
-  auction_ad_config_non_shared_params_->per_buyer_timeouts =
+  auction_ad_config_non_shared_params_.per_buyer_timeouts =
       std::move(per_buyer_timeouts);
-  auction_ad_config_non_shared_params_->all_buyers_timeout =
+  auction_ad_config_non_shared_params_.all_buyers_timeout =
       base::Milliseconds(150);
 
   // Add and populate two component auctions, each with one the mandatory
@@ -2099,27 +2098,23 @@
   // non-shared params.
 
   auto& component_auctions =
-      auction_ad_config_non_shared_params_->component_auctions;
+      auction_ad_config_non_shared_params_.component_auctions;
 
-  component_auctions.emplace_back(blink::mojom::AuctionAdConfig::New());
-  component_auctions[0]->auction_ad_config_non_shared_params =
-      blink::mojom::AuctionAdConfigNonSharedParams::New();
-  component_auctions[0]->seller =
-      url::Origin::Create(GURL("http://component1.com"));
-  component_auctions[0]->decision_logic_url =
-      GURL("http://component1.com/script.js");
-  component_auctions[0]->auction_ad_config_non_shared_params->seller_timeout =
+  component_auctions.emplace_back(blink::AuctionConfig());
+  component_auctions[0].seller =
+      url::Origin::Create(GURL("https://component1.com"));
+  component_auctions[0].decision_logic_url =
+      GURL("https://component1.com/script.js");
+  component_auctions[0].non_shared_params.seller_timeout =
       base::Milliseconds(111);
 
-  component_auctions.emplace_back(blink::mojom::AuctionAdConfig::New());
-  component_auctions[1]->auction_ad_config_non_shared_params =
-      blink::mojom::AuctionAdConfigNonSharedParams::New();
-  component_auctions[1]->seller =
-      url::Origin::Create(GURL("http://component2.com"));
-  component_auctions[1]->decision_logic_url =
-      GURL("http://component2.com/script.js");
-  component_auctions[1]->trusted_scoring_signals_url =
-      GURL("http://component2.com/signals.json");
+  component_auctions.emplace_back(blink::AuctionConfig());
+  component_auctions[1].seller =
+      url::Origin::Create(GURL("https://component2.com"));
+  component_auctions[1].decision_logic_url =
+      GURL("https://component2.com/script.js");
+  component_auctions[1].trusted_scoring_signals_url =
+      GURL("https://component2.com/signals.json");
 
   const char kExpectedJson[] =
       R"({"seller":"https://example.com",)"
@@ -2132,12 +2127,12 @@
       R"("perBuyerSignals":{"https://a.com":{"signals_a":"A"},)"
       R"("https://b.com":{"signals_b":"B"}},)"
       R"("perBuyerTimeouts":{"https://a.com":100,"*":150},)"
-      R"("componentAuctions":[{"seller":"http://component1.com",)"
-      R"("decisionLogicUrl":"http://component1.com/script.js",)"
+      R"("componentAuctions":[{"seller":"https://component1.com",)"
+      R"("decisionLogicUrl":"https://component1.com/script.js",)"
       R"("sellerTimeout":111},)"
-      R"({"seller":"http://component2.com",)"
-      R"("decisionLogicUrl":"http://component2.com/script.js",)"
-      R"("trustedScoringSignalsUrl":"http://component2.com/signals.json"}]})";
+      R"({"seller":"https://component2.com",)"
+      R"("decisionLogicUrl":"https://component2.com/script.js",)"
+      R"("trustedScoringSignalsUrl":"https://component2.com/signals.json"}]})";
   RunReportResultCreatedScriptExpectingResult(
       "auctionConfig", /*extra_code=*/std::string(), kExpectedJson,
       /*expected_report_url=*/absl::nullopt);
@@ -2154,7 +2149,7 @@
       /*expected_report_url=*/absl::nullopt);
 
   base::flat_map<url::Origin, base::TimeDelta> per_buyer_timeouts;
-  auction_ad_config_non_shared_params_->per_buyer_timeouts =
+  auction_ad_config_non_shared_params_.per_buyer_timeouts =
       std::move(per_buyer_timeouts);
 
   RunReportResultCreatedScriptExpectingResult(
@@ -2164,7 +2159,7 @@
       R"("perBuyerTimeouts":{}})",
       /*expected_report_url=*/absl::nullopt);
 
-  auction_ad_config_non_shared_params_->all_buyers_timeout =
+  auction_ad_config_non_shared_params_.all_buyers_timeout =
       base::Milliseconds(150);
   RunReportResultCreatedScriptExpectingResult(
       "auctionConfig", /*extra_code=*/std::string(),
@@ -2228,7 +2223,7 @@
     for (int j = 0; j < 2; ++j) {
       base::RunLoop run_loop;
       seller_worklet->ScoreAd(
-          ad_metadata_, bid_, auction_ad_config_non_shared_params_.Clone(),
+          ad_metadata_, bid_, auction_ad_config_non_shared_params_,
           browser_signals_other_seller_.Clone(),
           browser_signal_interest_group_owner_, browser_signal_render_url_,
           browser_signal_ad_components_, browser_signal_bidding_duration_msecs_,
@@ -2254,7 +2249,7 @@
     for (int j = 0; j < 2; ++j) {
       base::RunLoop run_loop;
       seller_worklet->ReportResult(
-          auction_ad_config_non_shared_params_.Clone(),
+          auction_ad_config_non_shared_params_,
           browser_signals_other_seller_.Clone(),
           browser_signal_interest_group_owner_, browser_signal_render_url_,
           bid_, browser_signal_desireability_,
@@ -2286,7 +2281,7 @@
 
   base::WaitableEvent* event_handle = WedgeV8Thread(v8_helper_.get());
   seller_worklet->ScoreAd(
-      ad_metadata_, bid_, auction_ad_config_non_shared_params_.Clone(),
+      ad_metadata_, bid_, auction_ad_config_non_shared_params_,
       browser_signals_other_seller_.Clone(),
       browser_signal_interest_group_owner_, browser_signal_render_url_,
       browser_signal_ad_components_, browser_signal_bidding_duration_msecs_,
@@ -2318,7 +2313,7 @@
 
   base::WaitableEvent* event_handle = WedgeV8Thread(v8_helper_.get());
   seller_worklet->ReportResult(
-      auction_ad_config_non_shared_params_.Clone(),
+      auction_ad_config_non_shared_params_,
       browser_signals_other_seller_.Clone(),
       browser_signal_interest_group_owner_, browser_signal_render_url_, bid_,
       browser_signal_desireability_, browser_signal_highest_scoring_other_bid_,
@@ -2994,7 +2989,7 @@
 TEST_F(SellerWorkletBiddingAndScoringDebugReportingAPIEnabledTest,
        ForDebuggingOnlyReportsInvalidScoreAdParameter) {
   // Auction config param is invalid.
-  auction_ad_config_non_shared_params_->auction_signals = "{invalid json";
+  auction_ad_config_non_shared_params_.auction_signals = "{invalid json";
   RunScoreAdWithJavascriptExpectingResult(
       CreateScoreAdScript(
           "1",
@@ -3002,7 +2997,7 @@
             forDebuggingOnly.reportAdAuctionWin("https://win.url"))"),
       0);
   // Setting it back to default value to avoid affecting following tests.
-  auction_ad_config_non_shared_params_->auction_signals =
+  auction_ad_config_non_shared_params_.auction_signals =
       R"({"is_auction_signals": true})";
 
   // `ad_metadata_` is an invalid json.
@@ -3165,7 +3160,7 @@
   for (int i = 0; i < 2; ++i) {
     base::RunLoop run_loop;
     seller_worklet->ScoreAd(
-        ad_metadata_, i + 1, auction_ad_config_non_shared_params_.Clone(),
+        ad_metadata_, i + 1, auction_ad_config_non_shared_params_,
         browser_signals_other_seller_.Clone(),
         browser_signal_interest_group_owner_, browser_signal_render_url_,
         browser_signal_ad_components_, browser_signal_bidding_duration_msecs_,
diff --git a/content/shell/browser/shell.cc b/content/shell/browser/shell.cc
index 7a6e31a..625474b 100644
--- a/content/shell/browser/shell.cc
+++ b/content/shell/browser/shell.cc
@@ -125,7 +125,7 @@
   // for windows opened from the renderer) then the Shell won't hear about the
   // main frame being created as a WebContentsObservers. This gives the delegate
   // a chance to act on the main frame accordingly.
-  if (raw_web_contents->GetMainFrame()->IsRenderFrameCreated())
+  if (raw_web_contents->GetMainFrame()->IsRenderFrameLive())
     g_platform->MainFrameCreated(shell);
 
   return shell;
diff --git a/content/shell/fuchsia/content_shell.cmx b/content/shell/fuchsia/content_shell.cmx
index b1d2a6fd..6fdf012 100644
--- a/content/shell/fuchsia/content_shell.cmx
+++ b/content/shell/fuchsia/content_shell.cmx
@@ -15,6 +15,7 @@
       "fuchsia.buildinfo.Provider",
       "fuchsia.device.NameProvider",
       "fuchsia.fonts.Provider",
+      "fuchsia.hwinfo.Product",
       "fuchsia.input.virtualkeyboard.ControllerCreator",
       "fuchsia.intl.PropertyProvider",
       "fuchsia.logger.LogSink",
diff --git a/content/test/attribution_simulator_input_parser.cc b/content/test/attribution_simulator_input_parser.cc
index 29424be..c09345c 100644
--- a/content/test/attribution_simulator_input_parser.cc
+++ b/content/test/attribution_simulator_input_parser.cc
@@ -43,11 +43,6 @@
 
 constexpr char kTimestampKey[] = "timestamp";
 
-constexpr char kAggregatableTriggerDataKey[] =
-    "Attribution-Reporting-Register-Aggregatable-Trigger-Data";
-constexpr char kAggregatableValuesKey[] =
-    "Attribution-Reporting-Register-Aggregatable-Values";
-
 class AttributionSimulatorInputParser {
  public:
   AttributionSimulatorInputParser(base::Time offset_time,
@@ -248,8 +243,8 @@
     base::TimeDelta expiry;
     AttributionFilterData filter_data;
 
-    if (!ParseAttributionSource(
-            source_dict,
+    if (!ParseAttributionEvent(
+            source_dict, "Attribution-Reporting-Register-Source",
             base::BindLambdaForTesting([&](const base::Value::Dict& dict) {
               source_event_id = ParseRequiredUint64(dict, "source_event_id");
               destination_origin = ParseOrigin(dict, "destination");
@@ -284,22 +279,34 @@
     if (!EnsureDictionary(trigger))
       return;
 
-    const base::Value::Dict& dict = trigger.GetDict();
+    const base::Value::Dict& trigger_dict = trigger.GetDict();
 
-    base::Time trigger_time = ParseTime(dict, kTimestampKey);
-    url::Origin reporting_origin = ParseOrigin(dict, "reporting_origin");
-    url::Origin destination_origin = ParseOrigin(dict, "destination_origin");
+    base::Time trigger_time = ParseTime(trigger_dict, kTimestampKey);
+    url::Origin reporting_origin =
+        ParseOrigin(trigger_dict, "reporting_origin");
+    url::Origin destination_origin =
+        ParseOrigin(trigger_dict, "destination_origin");
 
-    absl::optional<uint64_t> debug_key =
-        ParseOptionalUint64(dict, "Attribution-Reporting-Trigger-Debug-Key");
-    AttributionFilterData filters =
-        ParseFilterData(dict, "Attribution-Reporting-Filters",
-                        &AttributionFilterData::FromTriggerFilterValues);
-    std::vector<AttributionTrigger::EventTriggerData> event_triggers =
-        ParseEventTriggers(dict);
+    absl::optional<uint64_t> debug_key;
+    AttributionFilterData filters;
+    std::vector<AttributionTrigger::EventTriggerData> event_triggers;
+    AttributionAggregatableTrigger aggregatable_trigger;
 
-    AttributionAggregatableTrigger aggregatable_trigger =
-        ParseAggregatableTrigger(dict);
+    if (!ParseAttributionEvent(
+            trigger_dict,
+            "Attribution-Reporting-Register-Trigger",
+            base::BindLambdaForTesting(
+                [&](const base::Value::Dict& dict) {
+                  debug_key = ParseOptionalUint64(dict, "debug_key");
+                  filters = ParseFilterData(
+                      dict, "filters",
+                      &AttributionFilterData::FromTriggerFilterValues);
+                  event_triggers = ParseEventTriggers(dict);
+
+                  aggregatable_trigger = ParseAggregatableTrigger(dict);
+                }))) {
+      return;
+    }
 
     if (has_error())
       return;
@@ -319,8 +326,7 @@
       const base::Value::Dict& cfg) {
     std::vector<AttributionTrigger::EventTriggerData> event_triggers;
 
-    static constexpr char kKey[] =
-        "Attribution-Reporting-Register-Event-Trigger";
+    static constexpr char kKey[] = "event_trigger_data";
 
     const base::Value* values = cfg.Find(kKey);
     if (!values)
@@ -466,14 +472,13 @@
     return source_type;
   }
 
-  bool ParseAttributionSource(
+  bool ParseAttributionEvent(
       const base::Value::Dict& value,
+      base::StringPiece key,
       base::OnceCallback<void(const base::Value::Dict&)> callback) {
-    static constexpr char kKey[] = "Attribution-Reporting-Register-Source";
+    auto context = PushContext(key);
 
-    auto context = PushContext(kKey);
-
-    const base::Value* dict = value.Find(kKey);
+    const base::Value* dict = value.Find(key);
     if (!dict) {
       *Error() << "must be present";
       return false;
@@ -646,17 +651,16 @@
 
   std::vector<blink::mojom::AttributionAggregatableTriggerDataPtr>
   ParseAggregatableTriggerData(const base::Value::Dict& dict) {
+    static constexpr char kKey[] = "aggregatable_trigger_data";
+
     std::vector<blink::mojom::AttributionAggregatableTriggerDataPtr>
         aggregatable_triggers;
 
-    auto context = PushContext(kAggregatableTriggerDataKey);
-
-    const base::Value* values = dict.Find(kAggregatableTriggerDataKey);
-    if (!values) {
-      *Error() << "must be present";
+    const base::Value* values = dict.Find(kKey);
+    if (!values)
       return aggregatable_triggers;
-    }
 
+    auto context = PushContext(kKey);
     ParseList(
         *values,
         base::BindLambdaForTesting(
@@ -697,15 +701,15 @@
 
   AttributionAggregatableTrigger::Values ParseAggregatableValues(
       const base::Value::Dict& dict) {
+    static constexpr char kKey[] = "aggregatable_values";
+
     AttributionAggregatableTrigger::Values aggregatable_values;
 
-    auto context = PushContext(kAggregatableValuesKey);
-
-    const base::Value* value = dict.Find(kAggregatableValuesKey);
-    if (!value) {
-      *Error() << "must be present";
+    const base::Value* value = dict.Find(kKey);
+    if (!value)
       return aggregatable_values;
-    }
+
+    auto context = PushContext(kKey);
 
     if (!EnsureDictionary(*value))
       return aggregatable_values;
@@ -726,11 +730,6 @@
 
   AttributionAggregatableTrigger ParseAggregatableTrigger(
       const base::Value::Dict& dict) {
-    if (!dict.Find(kAggregatableTriggerDataKey) &&
-        !dict.Find(kAggregatableValuesKey)) {
-      return AttributionAggregatableTrigger();
-    }
-
     auto mojo = blink::mojom::AttributionAggregatableTrigger::New(
         ParseAggregatableTriggerData(dict), ParseAggregatableValues(dict));
 
diff --git a/content/test/attribution_simulator_input_parser_unittest.cc b/content/test/attribution_simulator_input_parser_unittest.cc
index 38bb1229..b7700d3c 100644
--- a/content/test/attribution_simulator_input_parser_unittest.cc
+++ b/content/test/attribution_simulator_input_parser_unittest.cc
@@ -261,11 +261,13 @@
         "timestamp": "1643235576123",
         "reporting_origin": "https://a.r.test",
         "destination_origin": " https://a.d1.test",
-        "trigger_data": "10",
-        "event_source_trigger_data": "3",
-        "priority": "-5",
-        "deduplication_key": "123",
-        "debug_key": "14"
+        "Attribution-Reporting-Register-Trigger": {
+          "trigger_data": "10",
+          "event_source_trigger_data": "3",
+          "priority": "-5",
+          "deduplication_key": "123",
+          "debug_key": "14"
+        }
       }
     ]})json";
 
@@ -286,40 +288,45 @@
       "timestamp": "1643235576123",
       "reporting_origin": "https://a.r.test",
       "destination_origin": " https://a.d1.test",
-      "Attribution-Reporting-Register-Event-Trigger": [
-        {
-          "trigger_data": "10",
-          "priority": "-5",
-          "deduplication_key": "123",
-          "filters": {
-            "x": ["y"]
+      "Attribution-Reporting-Register-Trigger": {
+        "event_trigger_data": [
+          {
+            "trigger_data": "10",
+            "priority": "-5",
+            "deduplication_key": "123",
+            "filters": {
+              "x": ["y"]
+            },
+            "not_filters": {
+              "z": []
+            }
           },
-          "not_filters": {
-            "z": []
-          }
-        },
-        {}
-      ],
-      "Attribution-Reporting-Trigger-Debug-Key": "14",
-      "Attribution-Reporting-Filters": {
-        "a": ["b", "c"],
-        "d": []
+          {}
+        ],
+        "debug_key": "14",
+        "filters": {
+          "a": ["b", "c"],
+          "d": []
+        }
       }
     },
     {
       "timestamp": "1643235575123",
       "reporting_origin": "https://b.r.test",
-      "destination_origin": " https://a.d2.test"
+      "destination_origin": " https://a.d2.test",
+      "Attribution-Reporting-Register-Trigger": {}
     },
     {
       "timestamp": "1643235574123",
       "reporting_origin": "https://b.r.test",
       "destination_origin": " https://a.d2.test",
-      "Attribution-Reporting-Register-Aggregatable-Trigger-Data": [{
-        "source_keys": ["a"],
-        "key_piece": "0x1"
-      }],
-      "Attribution-Reporting-Register-Aggregatable-Values": {"a": 1}
+      "Attribution-Reporting-Register-Trigger": {
+        "aggregatable_trigger_data": [{
+          "source_keys": ["a"],
+          "key_piece": "0x1"
+        }],
+        "aggregatable_values": {"a": 1}
+      }
     }
   ]})json";
 
@@ -424,7 +431,8 @@
     "triggers": [{
       "timestamp": "1643235575123",
       "reporting_origin": "https://b.r.test",
-      "destination_origin": " https://a.d2.test"
+      "destination_origin": " https://a.d2.test",
+      "Attribution-Reporting-Register-Trigger": {}
     }]
   })json";
 
@@ -798,21 +806,24 @@
         R"(["triggers"][0]["timestamp"]: must be an integer number of)",
         R"json({"triggers": [{
           "reporting_origin": "https://a.r.test",
-          "destination_origin": " https://a.d1.test"
+          "destination_origin": " https://a.d1.test",
+          "Attribution-Reporting-Register-Trigger": {}
         }]})json",
     },
     {
         R"(["triggers"][0]["destination_origin"]: must be a valid, secure origin)",
         R"json({"triggers": [{
           "timestamp": "1643235576000",
-          "reporting_origin": "https://a.r.test"
+          "reporting_origin": "https://a.r.test",
+          "Attribution-Reporting-Register-Trigger": {}
         }]})json",
     },
     {
         R"(["triggers"][0]["reporting_origin"]: must be a valid, secure origin)",
         R"json({"triggers": [{
           "timestamp": "1643235576000",
-          "destination_origin": " https://a.d1.test"
+          "destination_origin": " https://a.d1.test",
+          "Attribution-Reporting-Register-Trigger": {}
         }]})json",
     },
     {
@@ -820,120 +831,132 @@
         R"json({"triggers": ""})json",
     },
     {
-        R"(["triggers"][0]["Attribution-Reporting-Register-Event-Trigger"]: must be a list)",
+        R"(["triggers"][0]["Attribution-Reporting-Register-Trigger"]: must be present)",
+        R"json({"triggers": [{}]})json",
+    },
+    {
+        R"(["triggers"][0]["Attribution-Reporting-Register-Trigger"]: must be a dictionary)",
         R"json({"triggers": [{
-          "timestamp": "1643235576000",
-          "reporting_origin": "https://a.r.test",
-          "destination_origin": " https://a.d1.test",
-          "Attribution-Reporting-Register-Event-Trigger": 1
+          "Attribution-Reporting-Register-Trigger": ""
         }]})json",
     },
     {
-        R"(["triggers"][0]["Attribution-Reporting-Register-Aggregatable-Trigger-Data"]: must be a list)",
+        R"(["triggers"][0]["Attribution-Reporting-Register-Trigger"]["event_trigger_data"]: must be a list)",
         R"json({"triggers": [{
-          "timestamp": "1643235576000",
-          "reporting_origin": "https://a.r.test",
-          "destination_origin": " https://a.d1.test",
-          "Attribution-Reporting-Register-Aggregatable-Trigger-Data": 5
-        }]})json",
-    },
-    {
-        R"(["triggers"][0]["Attribution-Reporting-Register-Aggregatable-Trigger-Data"][0]: must be a dictionary)",
-        R"json({"triggers": [{
-          "timestamp": "1643235576000",
-          "reporting_origin": "https://a.r.test",
-          "destination_origin": " https://a.d1.test",
-          "Attribution-Reporting-Register-Aggregatable-Trigger-Data": [ 5 ]
-        }]})json",
-    },
-    {
-        R"(["triggers"][0]["Attribution-Reporting-Register-Aggregatable-Trigger-Data"][0]["source_keys"]: must be present)",
-        R"json({"triggers": [{
-          "timestamp": "1643235576000",
-          "reporting_origin": "https://a.r.test",
-          "destination_origin": " https://a.d1.test",
-          "Attribution-Reporting-Register-Aggregatable-Trigger-Data": [{}]
-        }]})json",
-    },
-    {
-        R"(["triggers"][0]["Attribution-Reporting-Register-Aggregatable-Trigger-Data"][0]["source_keys"]: must be a list)",
-        R"json({"triggers": [{
-          "timestamp": "1643235576000",
-          "reporting_origin": "https://a.r.test",
-          "destination_origin": " https://a.d1.test",
-          "Attribution-Reporting-Register-Aggregatable-Trigger-Data": [{
-            "source_keys": "a"
-          }]
-        }]})json",
-    },
-    {
-        R"(["triggers"][0]["Attribution-Reporting-Register-Aggregatable-Trigger-Data"][0]["source_keys"][0]: must be a string)",
-        R"json({"triggers": [{
-          "timestamp": "1643235576000",
-          "reporting_origin": "https://a.r.test",
-          "destination_origin": " https://a.d1.test",
-          "Attribution-Reporting-Register-Aggregatable-Trigger-Data": [{
-            "source_keys": [ 5 ]
-          }]
-        }]})json",
-    },
-    {
-        R"(["triggers"][0]["Attribution-Reporting-Register-Aggregatable-Trigger-Data"][0]["key_piece"]: must be a uint128 formatted as a base-16 string)",
-        R"json({"triggers": [{
-          "timestamp": "1643235576000",
-          "reporting_origin": "https://a.r.test",
-          "destination_origin": " https://a.d1.test",
-          "Attribution-Reporting-Register-Aggregatable-Trigger-Data": [{
-            "source_keys": [ "a" ],
-            "key_piece": "0xG"
-          }]
-        }]})json",
-    },
-    {
-        R"(["triggers"][0]["Attribution-Reporting-Register-Aggregatable-Values"]: must be a dictionary)",
-        R"json({"triggers": [{
-          "timestamp": "1643235576000",
-          "reporting_origin": "https://a.r.test",
-          "destination_origin": " https://a.d1.test",
-          "Attribution-Reporting-Register-Aggregatable-Values": 5
-        }]})json",
-    },
-    {
-        R"(["triggers"][0]["Attribution-Reporting-Register-Aggregatable-Values"]["a"]: must be a positive integer)",
-        R"json({"triggers": [{
-          "timestamp": "1643235576000",
-          "reporting_origin": "https://a.r.test",
-          "destination_origin": " https://a.d1.test",
-          "Attribution-Reporting-Register-Aggregatable-Values": {
-            "a": -5
+          "Attribution-Reporting-Register-Trigger": {
+            "timestamp": "1643235576000",
+            "reporting_origin": "https://a.r.test",
+            "destination_origin": " https://a.d1.test",
+            "event_trigger_data": 1
           }
         }]})json",
     },
     {
-        R"(["triggers"][0]["Attribution-Reporting-Register-Event-Trigger"][0]: must be a dictionary)",
+        R"(["triggers"][0]["Attribution-Reporting-Register-Trigger"]["aggregatable_trigger_data"]: must be a list)",
+        R"json({"triggers": [{
+          "Attribution-Reporting-Register-Trigger": {
+            "timestamp": "1643235576000",
+            "reporting_origin": "https://a.r.test",
+            "destination_origin": " https://a.d1.test",
+            "aggregatable_trigger_data": 5
+          }
+        }]})json",
+    },
+    {
+        R"(["triggers"][0]["Attribution-Reporting-Register-Trigger"]["aggregatable_trigger_data"][0]: must be a dictionary)",
+        R"json({"triggers": [{
+          "Attribution-Reporting-Register-Trigger": {
+            "timestamp": "1643235576000",
+            "reporting_origin": "https://a.r.test",
+            "destination_origin": " https://a.d1.test",
+            "aggregatable_trigger_data": [ 5 ]
+          }
+        }]})json",
+    },
+    {
+        R"(["triggers"][0]["Attribution-Reporting-Register-Trigger"]["aggregatable_trigger_data"][0]["source_keys"]: must be present)",
+        R"json({"triggers": [{
+          "Attribution-Reporting-Register-Trigger": {
+            "timestamp": "1643235576000",
+            "reporting_origin": "https://a.r.test",
+            "destination_origin": " https://a.d1.test",
+            "aggregatable_trigger_data": [{}]
+          }
+        }]})json",
+    },
+    {
+        R"(["triggers"][0]["Attribution-Reporting-Register-Trigger"]["aggregatable_trigger_data"][0]["source_keys"]: must be a list)",
+        R"json({"triggers": [{
+          "Attribution-Reporting-Register-Trigger": {
+            "timestamp": "1643235576000",
+            "reporting_origin": "https://a.r.test",
+            "destination_origin": " https://a.d1.test",
+            "aggregatable_trigger_data": [{
+              "source_keys": "a"
+            }]
+          }
+        }]})json",
+    },
+    {
+        R"(["triggers"][0]["Attribution-Reporting-Register-Trigger"]["aggregatable_trigger_data"][0]["source_keys"][0]: must be a string)",
+        R"json({"triggers": [{
+          "Attribution-Reporting-Register-Trigger": {
+            "timestamp": "1643235576000",
+            "reporting_origin": "https://a.r.test",
+            "destination_origin": " https://a.d1.test",
+            "aggregatable_trigger_data": [{
+              "source_keys": [ 5 ]
+            }]
+          }
+        }]})json",
+    },
+    {
+        R"(["triggers"][0]["Attribution-Reporting-Register-Trigger"]["aggregatable_trigger_data"][0]["key_piece"]: must be a uint128 formatted as a base-16 string)",
+        R"json({"triggers": [{
+          "Attribution-Reporting-Register-Trigger": {
+            "timestamp": "1643235576000",
+            "reporting_origin": "https://a.r.test",
+            "destination_origin": " https://a.d1.test",
+            "aggregatable_trigger_data": [{
+              "source_keys": [ "a" ],
+              "key_piece": "0xG"
+            }]
+          }
+        }]})json",
+    },
+    {
+        R"(["triggers"][0]["Attribution-Reporting-Register-Trigger"]["aggregatable_values"]: must be a dictionary)",
+        R"json({"triggers": [{
+          "Attribution-Reporting-Register-Trigger": {
+            "timestamp": "1643235576000",
+            "reporting_origin": "https://a.r.test",
+            "destination_origin": " https://a.d1.test",
+            "aggregatable_values": 5
+          }
+        }]})json",
+    },
+    {
+        R"(["triggers"][0]["Attribution-Reporting-Register-Trigger"]["aggregatable_values"]["a"]: must be a positive integer)",
+        R"json({"triggers": [{
+          "Attribution-Reporting-Register-Trigger": {
+            "timestamp": "1643235576000",
+            "reporting_origin": "https://a.r.test",
+            "destination_origin": " https://a.d1.test",
+            "aggregatable_values": {
+              "a": -5
+            }
+          }
+        }]})json",
+    },
+    {
+        R"(["triggers"][0]["Attribution-Reporting-Register-Trigger"]["event_trigger_data"][0]: must be a dictionary)",
         R"json({"triggers":[{
-          "timestamp": "1643235576000",
-          "reporting_origin": "https://a.r.test",
-          "destination_origin": " https://a.d1.test",
-          "Attribution-Reporting-Register-Event-Trigger":[true]
-        }]})json",
-    },
-    {
-        R"(["triggers"][0]["Attribution-Reporting-Register-Aggregatable-Trigger-Data"]: must be present)",
-        R"json({"triggers": [{
-          "timestamp": "1643235576000",
-          "reporting_origin": "https://a.r.test",
-          "destination_origin": " https://a.d1.test",
-          "Attribution-Reporting-Register-Aggregatable-Values": {}
-        }]})json",
-    },
-    {
-        R"(["triggers"][0]["Attribution-Reporting-Register-Aggregatable-Values"]: must be present)",
-        R"json({"triggers": [{
-          "timestamp": "1643235576000",
-          "reporting_origin": "https://a.r.test",
-          "destination_origin": " https://a.d1.test",
-          "Attribution-Reporting-Register-Aggregatable-Trigger-Data": []
+          "Attribution-Reporting-Register-Trigger": {
+            "timestamp": "1643235576000",
+            "reporting_origin": "https://a.r.test",
+            "destination_origin": " https://a.d1.test",
+            "event_trigger_data":[true]
+          }
         }]})json",
     },
     {
diff --git a/content/test/data/accessibility/html/popup-api-expected-blink.txt b/content/test/data/accessibility/html/popup-api-expected-blink.txt
index 4a84b4fa..1bf4841 100644
--- a/content/test/data/accessibility/html/popup-api-expected-blink.txt
+++ b/content/test/data/accessibility/html/popup-api-expected-blink.txt
@@ -13,24 +13,44 @@
 ++++++button collapsed name='Button pointing to hidden popup'
 ++++++++staticText name='Button pointing to hidden popup'
 ++++++++++inlineTextBox name='Button pointing to hidden popup'
+++++++textField
+++++++++genericContainer
+++++++staticText name='Text input pointing to hidden popup '
+++++++++inlineTextBox name='Text input pointing to hidden popup '
 ++++++genericContainer ignored invisible
 ++++++++staticText ignored name='Popup'
 ++++++button description='Hint' name='Show button pointing to hidden hint' descriptionFrom=popupElement
 ++++++++staticText name='Show button pointing to hidden hint'
 ++++++++++inlineTextBox name='Show button pointing to hidden hint'
+++++++searchBox description='Hint' descriptionFrom=popupElement
+++++++++genericContainer
+++++++staticText name='Search input pointing to hidden hint '
+++++++++inlineTextBox name='Search input pointing to hidden hint '
 ++++++genericContainer ignored invisible
 ++++++++staticText ignored name='Hint'
 ++++++button name='Hide button pointing to hidden async'
 ++++++++staticText name='Hide button pointing to hidden async'
 ++++++++++inlineTextBox name='Hide button pointing to hidden async'
+++++++textField
+++++++++genericContainer
+++++++staticText name='Email input pointing to hidden async '
+++++++++inlineTextBox name='Email input pointing to hidden async '
 ++++++genericContainer ignored invisible
 ++++++++staticText ignored name='Async'
 ++++++button name='Button pointing to showing popup'
 ++++++++staticText name='Button pointing to showing popup'
 ++++++++++inlineTextBox name='Button pointing to showing popup'
+++++++textField
+++++++++genericContainer
+++++++staticText name='Tel input pointing to showing popup '
+++++++++inlineTextBox name='Tel input pointing to showing popup '
 ++++++button description='Hint (nested)' name='Show button pointing to showing hint' descriptionFrom=popupElement
 ++++++++staticText name='Show button pointing to showing hint'
 ++++++++++inlineTextBox name='Show button pointing to showing hint'
+++++++textField description='Hint (nested)' descriptionFrom=popupElement
+++++++++genericContainer
+++++++staticText name='Url input pointing to showing hint '
+++++++++inlineTextBox name='Url input pointing to showing hint '
 ++++++genericContainer
 ++++++++staticText name='Popup'
 ++++++++++inlineTextBox name='Popup'
@@ -40,18 +60,30 @@
 ++++++button name='Hide button pointing to showing async'
 ++++++++staticText name='Hide button pointing to showing async'
 ++++++++++inlineTextBox name='Hide button pointing to showing async'
+++++++textField
+++++++++genericContainer
+++++++staticText name='Text input pointing to showing async '
+++++++++inlineTextBox name='Text input pointing to showing async '
 ++++++genericContainer
 ++++++++staticText name='Async'
 ++++++++++inlineTextBox name='Async'
 ++++++button name='Button pointing to non-popup'
 ++++++++staticText name='Button pointing to non-popup'
 ++++++++++inlineTextBox name='Button pointing to non-popup'
+++++++textField
+++++++++genericContainer
+++++++staticText name='Text input pointing to non-popup'
+++++++++inlineTextBox name='Text input pointing to non-popup'
 ++++++genericContainer
 ++++++++staticText name='No popup attribute'
 ++++++++++inlineTextBox name='No popup attribute'
 ++++++button name='Button pointing to invalid popup value'
 ++++++++staticText name='Button pointing to invalid popup value'
 ++++++++++inlineTextBox name='Button pointing to invalid popup value'
+++++++textField
+++++++++genericContainer
+++++++staticText name='Text input pointing to invalid popup value'
+++++++++inlineTextBox name='Text input pointing to invalid popup value'
 ++++++genericContainer
 ++++++++staticText name='Invalid popup attribute value'
 ++++++++++inlineTextBox name='Invalid popup attribute value'
diff --git a/content/test/data/accessibility/html/popup-api.html b/content/test/data/accessibility/html/popup-api.html
index 7cc493c2..f5331c2 100644
--- a/content/test/data/accessibility/html/popup-api.html
+++ b/content/test/data/accessibility/html/popup-api.html
@@ -1,23 +1,27 @@
-<html>
-<body>
 <div popup=popup>Div popup</div>
 <ul popup=popup role=listbox><li>Listbox role on ul</li></ul>
 <ul popup=popup role=listbox><li>Listbox role on ul</li></ul>
 <details popup=popup><summary>summary/details popup</summary></details>
 
 <button togglepopup=popup1>Button pointing to hidden popup</button>
+<input type=text togglepopup=popup1>Text input pointing to hidden popup</button>
 <div popup=popup id=popup1>Popup</div>
 <button showpopup=hint1>Show button pointing to hidden hint</button>
+<input type=search showpopup=hint1>Search input pointing to hidden hint</button>
 <div popup=hint id=hint1>Hint</div>
 <button hidepopup=async1>Hide button pointing to hidden async</button>
+<input type=email hidepopup=async1>Email input pointing to hidden async</button>
 <div popup=async id=async1>Async</div>
 
 <button togglepopup=popup2>Button pointing to showing popup</button>
+<input type=tel togglepopup=popup2>Tel input pointing to showing popup</button>
 <button showpopup=hint2>Show button pointing to showing hint</button>
+<input type=url showpopup=hint2>Url input pointing to showing hint</button>
 <div popup=popup id=popup2>Popup
   <div popup=hint id=hint2>Hint (nested)</div>
 </div>
 <button hidepopup=async2>Hide button pointing to showing async</button>
+<input type=text hidepopup=async2>Text input pointing to showing async</button>
 <div popup=async id=async2>Async</div>
 <script>
   popup2.showPopup();
@@ -26,8 +30,8 @@
 </script>
 
 <button togglepopup=noattr>Button pointing to non-popup</button>
+<input type=text togglepopup=noattr>Text input pointing to non-popup</button>
 <div id=noattr>No popup attribute</div>
 <button togglepopup=badattr>Button pointing to invalid popup value</button>
+<input type=text togglepopup=badattr>Text input pointing to invalid popup value</button>
 <div id=badattr popup=invalid_value>Invalid popup attribute value</div>
-</body>
-</html>
diff --git a/content/test/data/attribution_reporting/interop/README.md b/content/test/data/attribution_reporting/interop/README.md
index 69929ad0..7f0685b3f 100644
--- a/content/test/data/attribution_reporting/interop/README.md
+++ b/content/test/data/attribution_reporting/interop/README.md
@@ -121,77 +121,79 @@
             "url": "https://reporting.example",
 
             "response": {
-              // Optional list of zero or more event trigger data.
-              "Attribution-Reporting-Register-Event-Trigger": [
-                {
-                  // Optional uint64 formatted as a base-10 string.
-                  // Defaults to 0.
-                  "trigger_data": "3",
+              "Attribution-Reporting-Register-Trigger": {
+                // Optional list of zero or more event trigger data.
+                "event_trigger_data": [
+                  {
+                    // Optional uint64 formatted as a base-10 string.
+                    // Defaults to 0.
+                    "trigger_data": "3",
 
-                  // Optional int64 formatted as a base-10 string.
-                  // Defaults to 0.
-                  "priority": "-456",
+                    // Optional int64 formatted as a base-10 string.
+                    // Defaults to 0.
+                    "priority": "-456",
 
-                  // Optional uint64 formatted as a base-10 string. Defaults to
-                  // null.
-                  "deduplication_key": "654",
+                    // Optional uint64 formatted as a base-10 string. Defaults to
+                    // null.
+                    "deduplication_key": "654",
 
-                  // Optional dictionary of filters and corresponding values.
-                  // Defaults to empty.
-                  "filters": {
-                    "a": ["b", "c"],
-                    "d": []
-                  },
+                    // Optional dictionary of filters and corresponding values.
+                    // Defaults to empty.
+                    "filters": {
+                      "a": ["b", "c"],
+                      "d": []
+                    },
 
-                  // Optional dictionary of negated filters and corresponding
-                  // values. Defaults to empty.
-                  "not_filters": {
-                    "x": ["y"],
-                    "z": []
+                    // Optional dictionary of negated filters and corresponding
+                    // values. Defaults to empty.
+                    "not_filters": {
+                      "x": ["y"],
+                      "z": []
+                    }
                   }
-                }
-              ],
+                ],
 
-              // Optional list of zero or more aggregatable trigger data.
-              "Attribution-Reporting-Register-Aggregatable-Trigger-Data": [
-                {
-                  // Required uint128 formatted as a base-16 string.
-                  "key_piece": "0x10",
+                // Optional list of zero or more aggregatable trigger data.
+                "aggregatable_trigger_data": [
+                  {
+                    // Required uint128 formatted as a base-16 string.
+                    "key_piece": "0x10",
 
-                  // Required list of key identifiers.
-                  "source_keys": ["a"],
+                    // Required list of key identifiers.
+                    "source_keys": ["a"],
 
-                  // Optional dictionary of filters and corresponding values.
-                  // Defaults to empty.
-                  "filters": {
-                    "a": ["b", "c"],
-                    "d": []
-                  },
+                    // Optional dictionary of filters and corresponding values.
+                    // Defaults to empty.
+                    "filters": {
+                      "a": ["b", "c"],
+                      "d": []
+                    },
 
-                  // Optional dictionary of negated filters and corresponding
-                  // values. Defaults to empty.
-                  "not_filters": {
-                    "x": ["y"],
-                    "z": []
+                    // Optional dictionary of negated filters and corresponding
+                    // values. Defaults to empty.
+                    "not_filters": {
+                      "x": ["y"],
+                      "z": []
+                    }
                   }
+                ],
+
+                // Optional dictionary of key identifiers and corresponding
+                // values.
+                "aggregatable_values": {
+                  "a": 123
+                },
+
+                // Optional uint64 formatted as a base-10 string. Defaults to
+                // null.
+                "debug_key": "789",
+
+                // Optional dictionary of filters and corresponding values.
+                // Defaults to empty.
+                "filters": {
+                  "a": ["b", "c"],
+                  "d": []
                 }
-              ],
-
-              // Optional dictionary of key identifiers and corresponding
-              // values.
-              "Attribution-Reporting-Register-Aggregatable-Values": {
-                "a": 123
-              },
-
-              // Optional uint64 formatted as a base-10 string. Defaults to
-              // null.
-              "Attribution-Reporting-Trigger-Debug-Key": "789",
-
-              // Optional dictionary of filters and corresponding values.
-              // Defaults to empty.
-              "Attribution-Reporting-Filters": {
-                "a": ["b", "c"],
-                "d": []
               }
             }
           }
diff --git a/content/test/data/attribution_reporting/interop/basic.json b/content/test/data/attribution_reporting/interop/basic.json
index 0a8b47a..9d258375 100644
--- a/content/test/data/attribution_reporting/interop/basic.json
+++ b/content/test/data/attribution_reporting/interop/basic.json
@@ -30,11 +30,13 @@
         "responses": [{
           "url": "https://reporter.test/register-trigger",
           "response": {
-            "Attribution-Reporting-Register-Event-Trigger": [
-              {
-                "trigger_data": "7"
-              }
-            ]
+            "Attribution-Reporting-Register-Trigger": {
+              "event_trigger_data": [
+                {
+                  "trigger_data": "7"
+                }
+              ]
+            }
           }
         }]
       }
diff --git a/content/test/data/attribution_reporting/interop/basic_aggregatable.json b/content/test/data/attribution_reporting/interop/basic_aggregatable.json
index 3a5aa76c..2382659 100644
--- a/content/test/data/attribution_reporting/interop/basic_aggregatable.json
+++ b/content/test/data/attribution_reporting/interop/basic_aggregatable.json
@@ -36,19 +36,21 @@
         "responses": [{
           "url": "https://reporter.test/register-trigger",
           "response": {
-            "Attribution-Reporting-Register-Event-Trigger": [
-              {
-                "trigger_data": "7"
+            "Attribution-Reporting-Register-Trigger": {
+              "event_trigger_data": [
+                {
+                  "trigger_data": "7"
+                }
+              ],
+              "aggregatable_trigger_data": [
+                {
+                  "source_keys": ["a"],
+                  "key_piece": "0x400"
+                }
+              ],
+              "aggregatable_values": {
+                "a": 123
               }
-            ],
-            "Attribution-Reporting-Register-Aggregatable-Trigger-Data": [
-              {
-                "source_keys": ["a"],
-                "key_piece": "0x400"
-              }
-            ],
-            "Attribution-Reporting-Register-Aggregatable-Values": {
-              "a": 123
             }
           }
         }]
diff --git a/content/test/data/attribution_reporting/interop/deactivated_source.json b/content/test/data/attribution_reporting/interop/deactivated_source.json
index 065296b..453a155 100644
--- a/content/test/data/attribution_reporting/interop/deactivated_source.json
+++ b/content/test/data/attribution_reporting/interop/deactivated_source.json
@@ -49,11 +49,13 @@
         "responses": [{
           "url": "https://reporter.test/register-trigger",
           "response": {
-            "Attribution-Reporting-Register-Event-Trigger": [
-              {
-                "trigger_data": "1"
-              }
-            ]
+            "Attribution-Reporting-Register-Trigger": {
+              "event_trigger_data": [
+                {
+                  "trigger_data": "1"
+                }
+              ]
+            }
           }
         }]
       },
@@ -66,11 +68,13 @@
         "responses": [{
           "url": "https://reporter.test/register-trigger",
           "response": {
-            "Attribution-Reporting-Register-Event-Trigger": [
-              {
-                "trigger_data": "2"
-              }
-            ]
+            "Attribution-Reporting-Register-Trigger": {
+              "event_trigger_data": [
+                {
+                  "trigger_data": "2"
+                }
+              ]
+            }
           }
         }]
       }
diff --git a/content/test/data/attribution_reporting/interop/dedup_key.json b/content/test/data/attribution_reporting/interop/dedup_key.json
index 1be0e61..9dca2d3 100644
--- a/content/test/data/attribution_reporting/interop/dedup_key.json
+++ b/content/test/data/attribution_reporting/interop/dedup_key.json
@@ -30,12 +30,14 @@
         "responses": [{
           "url": "https://reporter.test/register-trigger",
           "response": {
-            "Attribution-Reporting-Register-Event-Trigger": [
-              {
-                "trigger_data": "1",
-                "deduplication_key": "1"
-              }
-            ]
+            "Attribution-Reporting-Register-Trigger": {
+              "event_trigger_data": [
+                {
+                  "trigger_data": "1",
+                  "deduplication_key": "1"
+                }
+              ]
+            }
           }
         }]
       },
@@ -48,12 +50,14 @@
         "responses": [{
           "url": "https://reporter.test/register-trigger",
           "response": {
-            "Attribution-Reporting-Register-Event-Trigger": [
-              {
-                "trigger_data": "2",
-                "deduplication_key": "1"
-              }
-            ]
+            "Attribution-Reporting-Register-Trigger": {
+              "event_trigger_data": [
+                {
+                  "trigger_data": "2",
+                  "deduplication_key": "1"
+                }
+              ]
+            }
           }
         }]
       },
@@ -66,12 +70,14 @@
         "responses": [{
           "url": "https://reporter.test/register-trigger",
           "response": {
-            "Attribution-Reporting-Register-Event-Trigger": [
-              {
-                "trigger_data": "3",
-                "deduplication_key": "2"
-              }
-            ]
+            "Attribution-Reporting-Register-Trigger": {
+              "event_trigger_data": [
+                {
+                  "trigger_data": "3",
+                  "deduplication_key": "2"
+                }
+              ]
+            }
           }
         }]
       }
diff --git a/content/test/data/attribution_reporting/interop/event_source.json b/content/test/data/attribution_reporting/interop/event_source.json
index 88f5e17..2abd77f 100644
--- a/content/test/data/attribution_reporting/interop/event_source.json
+++ b/content/test/data/attribution_reporting/interop/event_source.json
@@ -30,11 +30,13 @@
         "responses": [{
           "url": "https://reporter.test/register-trigger",
           "response": {
-            "Attribution-Reporting-Register-Event-Trigger": [
-              {
-                "trigger_data": "1"
-              }
-            ]
+            "Attribution-Reporting-Register-Trigger": {
+              "event_trigger_data": [
+                {
+                  "trigger_data": "1"
+                }
+              ]
+            }
           }
         }]
       }
diff --git a/content/test/data/attribution_reporting/interop/expired_source.json b/content/test/data/attribution_reporting/interop/expired_source.json
index 40df37d..716b30e 100644
--- a/content/test/data/attribution_reporting/interop/expired_source.json
+++ b/content/test/data/attribution_reporting/interop/expired_source.json
@@ -31,11 +31,13 @@
         "responses": [{
           "url": "https://reporter.test/register-trigger",
           "response": {
-            "Attribution-Reporting-Register-Event-Trigger": [
-              {
-                "trigger_data": "7"
-              }
-            ]
+            "Attribution-Reporting-Register-Trigger": {
+              "event_trigger_data": [
+                {
+                  "trigger_data": "7"
+                }
+              ]
+            }
           }
         }]
       }
diff --git a/content/test/data/attribution_reporting/interop/most_recent_source.json b/content/test/data/attribution_reporting/interop/most_recent_source.json
index d43d3ed..37d3c00 100644
--- a/content/test/data/attribution_reporting/interop/most_recent_source.json
+++ b/content/test/data/attribution_reporting/interop/most_recent_source.json
@@ -47,11 +47,13 @@
         "responses": [{
           "url": "https://reporter.test/register-trigger",
           "response": {
-            "Attribution-Reporting-Register-Event-Trigger": [
-              {
-                "trigger_data": "7"
-              }
-            ]
+            "Attribution-Reporting-Register-Trigger": {
+              "event_trigger_data": [
+                {
+                  "trigger_data": "7"
+                }
+              ]
+            }
           }
         }]
       }
diff --git a/content/test/data/attribution_reporting/interop/same_destination_site.json b/content/test/data/attribution_reporting/interop/same_destination_site.json
index cb83e613..48ce0e5 100644
--- a/content/test/data/attribution_reporting/interop/same_destination_site.json
+++ b/content/test/data/attribution_reporting/interop/same_destination_site.json
@@ -30,11 +30,13 @@
         "responses": [{
           "url": "https://reporter.test/register-trigger",
           "response": {
-            "Attribution-Reporting-Register-Event-Trigger": [
-              {
-                "trigger_data": "7"
-              }
-            ]
+            "Attribution-Reporting-Register-Trigger": {
+              "event_trigger_data": [
+                {
+                  "trigger_data": "7"
+                }
+              ]
+            }
           }
         }]
       },
@@ -47,11 +49,13 @@
         "responses": [{
           "url": "https://reporter.test/register-trigger",
           "response": {
-            "Attribution-Reporting-Register-Event-Trigger": [
-              {
-                "trigger_data": "6"
-              }
-            ]
+            "Attribution-Reporting-Register-Trigger": {
+              "event_trigger_data": [
+                {
+                  "trigger_data": "6"
+                }
+              ]
+            }
           }
         }]
       }
diff --git a/content/test/data/attribution_reporting/interop/source_priority.json b/content/test/data/attribution_reporting/interop/source_priority.json
index 9021e45..514ebfa 100644
--- a/content/test/data/attribution_reporting/interop/source_priority.json
+++ b/content/test/data/attribution_reporting/interop/source_priority.json
@@ -67,11 +67,13 @@
         "responses": [{
           "url": "https://reporter.test/register-trigger",
           "response": {
-            "Attribution-Reporting-Register-Event-Trigger": [
-              {
-                "trigger_data": "7"
-              }
-            ]
+            "Attribution-Reporting-Register-Trigger": {
+              "event_trigger_data": [
+                {
+                  "trigger_data": "7"
+                }
+              ]
+            }
           }
         }]
       }
diff --git a/content/test/data/attribution_reporting/simulator/README.md b/content/test/data/attribution_reporting/simulator/README.md
index 7173467..3d634529 100644
--- a/content/test/data/attribution_reporting/simulator/README.md
+++ b/content/test/data/attribution_reporting/simulator/README.md
@@ -73,74 +73,76 @@
       // Required origin on which the trigger is being registered.
       "destination_origin": "https://destination.example",
 
-      // Optional list of zero or more event trigger data.
-      "Attribution-Reporting-Register-Event-Trigger": [
-        {
-          // Optional uint64 formatted as a base-10 string.
-          // Defaults to 0.
-          "trigger_data": "3",
+      "Attribution-Reporting-Register-Trigger": {
+        // Optional list of zero or more event trigger data.
+        "event_trigger_data": [
+          {
+            // Optional uint64 formatted as a base-10 string.
+            // Defaults to 0.
+            "trigger_data": "3",
 
-          // Optional int64 formatted as a base-10 string.
-          // Defaults to 0.
-          "priority": "-456",
+            // Optional int64 formatted as a base-10 string.
+            // Defaults to 0.
+            "priority": "-456",
 
-          // Optional uint64 formatted as a base-10 string. Defaults to null.
-          "deduplication_key": "654",
+            // Optional uint64 formatted as a base-10 string. Defaults to null.
+            "deduplication_key": "654",
 
-          // Optional dictionary of filters and corresponding values. Defaults
-          // to empty.
-          "filters": {
-            "a": ["b", "c"],
-            "d": []
-          },
+            // Optional dictionary of filters and corresponding values. Defaults
+            // to empty.
+            "filters": {
+              "a": ["b", "c"],
+              "d": []
+            },
 
-          // Optional dictionary of negated filters and corresponding values.
-          // Defaults to empty.
-          "not_filters": {
-            "x": ["y"],
-            "z": []
+            // Optional dictionary of negated filters and corresponding values.
+            // Defaults to empty.
+            "not_filters": {
+              "x": ["y"],
+              "z": []
+            }
           }
-        }
-      ],
+        ],
 
-      // Optional list of zero or more aggregatable trigger data.
-      "Attribution-Reporting-Register-Aggregatable-Trigger-Data": [
-        {
-          // Required uint128 formatted as a base-16 string.
-          "key_piece": "0x10",
+        // Optional list of zero or more aggregatable trigger data.
+        "aggregatable_trigger_data": [
+          {
+            // Required uint128 formatted as a base-16 string.
+            "key_piece": "0x10",
 
-          // Required list of key identifiers.
-          "source_keys": ["a"],
+            // Required list of key identifiers.
+            "source_keys": ["a"],
 
-          // Optional dictionary of filters and corresponding values. Defaults
-          // to empty.
-          "filters": {
-            "a": ["b", "c"],
-            "d": []
-          },
+            // Optional dictionary of filters and corresponding values. Defaults
+            // to empty.
+            "filters": {
+              "a": ["b", "c"],
+              "d": []
+            },
 
-          // Optional dictionary of negated filters and corresponding values.
-          // Defaults to empty.
-          "not_filters": {
-            "x": ["y"],
-            "z": []
+            // Optional dictionary of negated filters and corresponding values.
+            // Defaults to empty.
+            "not_filters": {
+              "x": ["y"],
+              "z": []
+            }
           }
+        ],
+
+        // Optional dictionary of key identifiers and corresponding values.
+        "aggregatable_values": {
+          "a": 123
+        },
+
+        // Optional uint64 formatted as a base-10 string. Defaults to null.
+        "debug_key": "789",
+
+        // Optional dictionary of filters and corresponding values. Defaults to
+        // empty.
+        "filters": {
+          "a": ["b", "c"],
+          "d": []
         }
-      ],
-
-      // Optional dictionary of key identifiers and corresponding values.
-      "Attribution-Reporting-Register-Aggregatable-Values": {
-        "a": 123
-      },
-
-      // Optional uint64 formatted as a base-10 string. Defaults to null.
-      "Attribution-Reporting-Trigger-Debug-Key": "789",
-
-      // Optional dictionary of filters and corresponding values. Defaults to
-      // empty.
-      "Attribution-Reporting-Filters": {
-        "a": ["b", "c"],
-        "d": []
       }
     }
   ],
diff --git a/content/test/data/attribution_reporting/simulator/basic.input.json b/content/test/data/attribution_reporting/simulator/basic.input.json
index a0de459ed..163aa30 100644
--- a/content/test/data/attribution_reporting/simulator/basic.input.json
+++ b/content/test/data/attribution_reporting/simulator/basic.input.json
@@ -16,11 +16,13 @@
       "timestamp": "1643235574123",
       "reporting_origin": "https://r.test",
       "destination_origin": "https://d.test",
-      "Attribution-Reporting-Register-Event-Trigger": [
-        {
-          "trigger_data": "7"
-        }
-      ]
+      "Attribution-Reporting-Register-Trigger": {
+        "event_trigger_data": [
+          {
+            "trigger_data": "7"
+          }
+        ]
+      }
     }
   ]
 }
diff --git a/content/test/data/attribution_reporting/simulator/basic_aggregatable.input.json b/content/test/data/attribution_reporting/simulator/basic_aggregatable.input.json
index ed631c8..8cfe8ce 100644
--- a/content/test/data/attribution_reporting/simulator/basic_aggregatable.input.json
+++ b/content/test/data/attribution_reporting/simulator/basic_aggregatable.input.json
@@ -22,19 +22,21 @@
       "timestamp": "1643235574123",
       "reporting_origin": "https://r.test",
       "destination_origin": "https://d.test",
-      "Attribution-Reporting-Register-Event-Trigger": [
-        {
-          "trigger_data": "7"
+      "Attribution-Reporting-Register-Trigger": {
+        "event_trigger_data": [
+          {
+            "trigger_data": "7"
+          }
+        ],
+        "aggregatable_trigger_data": [
+          {
+            "source_keys": ["a"],
+            "key_piece": "0x400"
+          }
+        ],
+        "aggregatable_values": {
+          "a": 123
         }
-      ],
-      "Attribution-Reporting-Register-Aggregatable-Trigger-Data": [
-        {
-          "source_keys": ["a"],
-          "key_piece": "0x400"
-        }
-      ],
-      "Attribution-Reporting-Register-Aggregatable-Values": {
-        "a": 123
       }
     }
   ]
diff --git a/content/test/data/attribution_reporting/simulator/basic_iso8601.input.json b/content/test/data/attribution_reporting/simulator/basic_iso8601.input.json
index a0de459ed..163aa30 100644
--- a/content/test/data/attribution_reporting/simulator/basic_iso8601.input.json
+++ b/content/test/data/attribution_reporting/simulator/basic_iso8601.input.json
@@ -16,11 +16,13 @@
       "timestamp": "1643235574123",
       "reporting_origin": "https://r.test",
       "destination_origin": "https://d.test",
-      "Attribution-Reporting-Register-Event-Trigger": [
-        {
-          "trigger_data": "7"
-        }
-      ]
+      "Attribution-Reporting-Register-Trigger": {
+        "event_trigger_data": [
+          {
+            "trigger_data": "7"
+          }
+        ]
+      }
     }
   ]
 }
diff --git a/content/test/data/attribution_reporting/simulator/clear_data.input.json b/content/test/data/attribution_reporting/simulator/clear_data.input.json
index 5327fd7f..3ac46fa9 100644
--- a/content/test/data/attribution_reporting/simulator/clear_data.input.json
+++ b/content/test/data/attribution_reporting/simulator/clear_data.input.json
@@ -26,21 +26,25 @@
       "timestamp": "1643235575000",
       "reporting_origin": "https://r.test",
       "destination_origin": "https://d1.test",
-      "Attribution-Reporting-Register-Event-Trigger": [
-        {
-          "trigger_data": "6"
-        }
-      ]
+      "Attribution-Reporting-Register-Trigger": {
+        "event_trigger_data": [
+          {
+            "trigger_data": "6"
+          }
+        ]
+      }
     },
     {
       "timestamp": "1643235575000",
       "reporting_origin": "https://r.test",
       "destination_origin": "https://d2.test",
-      "Attribution-Reporting-Register-Event-Trigger": [
-        {
-          "trigger_data": "7"
-        }
-      ]
+      "Attribution-Reporting-Register-Trigger": {
+        "event_trigger_data": [
+          {
+            "trigger_data": "7"
+          }
+        ]
+      }
     }
   ],
   "data_clears": [
diff --git a/content/test/data/attribution_reporting/simulator/clear_data.output.json b/content/test/data/attribution_reporting/simulator/clear_data.output.json
index 58b8a55f..d22e1bdc 100644
--- a/content/test/data/attribution_reporting/simulator/clear_data.output.json
+++ b/content/test/data/attribution_reporting/simulator/clear_data.output.json
@@ -22,11 +22,13 @@
         "timestamp": "1643235575000",
         "reporting_origin": "https://r.test",
         "destination_origin": "https://d1.test",
-        "Attribution-Reporting-Register-Event-Trigger": [
-          {
-            "trigger_data": "6"
-          }
-        ]
+        "Attribution-Reporting-Register-Trigger": {
+          "event_trigger_data": [
+            {
+              "trigger_data": "6"
+            }
+          ]
+        }
       }
     }
   ]
diff --git a/content/test/data/attribution_reporting/simulator/debug.input.json b/content/test/data/attribution_reporting/simulator/debug.input.json
index 06c75305f..e9fc4ae 100644
--- a/content/test/data/attribution_reporting/simulator/debug.input.json
+++ b/content/test/data/attribution_reporting/simulator/debug.input.json
@@ -17,23 +17,27 @@
       "timestamp": "1643235574000",
       "reporting_origin": "https://r.test",
       "destination_origin": "https://d.test",
-      "Attribution-Reporting-Register-Event-Trigger": [
-        {
-          "trigger_data": "7"
-        }
-      ],
-      "Attribution-Reporting-Trigger-Debug-Key": "333"
+      "Attribution-Reporting-Register-Trigger": {
+        "event_trigger_data": [
+          {
+            "trigger_data": "7"
+          }
+        ],
+        "debug_key": "333"
+      }
     },
     {
       "timestamp": "1643235575000",
       "reporting_origin": "https://r.test",
       "destination_origin": "https://d.test",
-      "Attribution-Reporting-Register-Event-Trigger": [
-        {
-          "trigger_data": "6"
-        }
-      ],
-      "Attribution-Reporting-Trigger-Debug-Key": "444"
+      "Attribution-Reporting-Register-Trigger": {
+        "event_trigger_data": [
+          {
+            "trigger_data": "6"
+          }
+        ],
+        "debug_key": "444"
+      }
     }
   ],
   "cookies": [
diff --git a/content/test/data/attribution_reporting/simulator/debug_skip_check.input.json b/content/test/data/attribution_reporting/simulator/debug_skip_check.input.json
index 5189c7b3..f639e4c1 100644
--- a/content/test/data/attribution_reporting/simulator/debug_skip_check.input.json
+++ b/content/test/data/attribution_reporting/simulator/debug_skip_check.input.json
@@ -17,12 +17,14 @@
       "timestamp": "1643235574000",
       "reporting_origin": "https://r.test",
       "destination_origin": "https://d.test",
-      "Attribution-Reporting-Register-Event-Trigger": [
-        {
-          "trigger_data": "7"
-        }
-      ],
-      "Attribution-Reporting-Trigger-Debug-Key": "333"
+      "Attribution-Reporting-Register-Trigger": {
+        "event_trigger_data": [
+          {
+            "trigger_data": "7"
+          }
+        ],
+        "debug_key": "333"
+      }
     }
   ]
 }
diff --git a/content/test/data/attribution_reporting/simulator/no_delay.input.json b/content/test/data/attribution_reporting/simulator/no_delay.input.json
index 5d90189b..97e6171 100644
--- a/content/test/data/attribution_reporting/simulator/no_delay.input.json
+++ b/content/test/data/attribution_reporting/simulator/no_delay.input.json
@@ -16,11 +16,13 @@
       "timestamp": "1643235574000",
       "reporting_origin": "https://r.test",
       "destination_origin": "https://d.test",
-      "Attribution-Reporting-Register-Event-Trigger": [
-        {
-          "trigger_data": "7"
-        }
-      ]
+      "Attribution-Reporting-Register-Trigger": {
+        "event_trigger_data": [
+          {
+            "trigger_data": "7"
+          }
+        ]
+      }
     }
   ]
 }
diff --git a/content/test/data/attribution_reporting/simulator/rejected_triggers.input.json b/content/test/data/attribution_reporting/simulator/rejected_triggers.input.json
index c3a8be1..42275a9 100644
--- a/content/test/data/attribution_reporting/simulator/rejected_triggers.input.json
+++ b/content/test/data/attribution_reporting/simulator/rejected_triggers.input.json
@@ -22,73 +22,85 @@
       "timestamp": "1643235572000",
       "reporting_origin": "https://r.test",
       "destination_origin": "https://d.test",
-      "Attribution-Reporting-Register-Event-Trigger": [
-        {
-          "trigger_data": "1"
-        }
-      ]
+      "Attribution-Reporting-Register-Trigger": {
+        "event_trigger_data": [
+          {
+            "trigger_data": "1"
+          }
+        ]
+      }
     },
     {
       "timestamp": "1643235574000",
       "reporting_origin": "https://r.test",
       "destination_origin": "https://d.test",
-      "Attribution-Reporting-Register-Event-Trigger": [
-        {
-          "trigger_data": "2",
-          "deduplication_key": "22"
-        }
-      ]
+      "Attribution-Reporting-Register-Trigger": {
+        "event_trigger_data": [
+          {
+            "trigger_data": "2",
+            "deduplication_key": "22"
+          }
+        ]
+      }
     },
     {
       "timestamp": "1643235575000",
       "reporting_origin": "https://r.test",
       "destination_origin": "https://d.test",
-      "Attribution-Reporting-Register-Event-Trigger": [
-        {
-          "trigger_data": "3",
-          "deduplication_key": "22"
-        }
-      ]
+      "Attribution-Reporting-Register-Trigger": {
+        "event_trigger_data": [
+          {
+            "trigger_data": "3",
+            "deduplication_key": "22"
+          }
+        ]
+      }
     },
     {
       "timestamp": "1643235576000",
       "reporting_origin": "https://r.test",
       "destination_origin": "https://d.test",
-      "Attribution-Reporting-Register-Event-Trigger": [
-        {
-          "trigger_data": "4",
-          "priority": "100"
-        }
-      ]
+      "Attribution-Reporting-Register-Trigger": {
+        "event_trigger_data": [
+          {
+            "trigger_data": "4",
+            "priority": "100"
+          }
+        ]
+      }
     },
     {
       "timestamp": "1643235577000",
       "reporting_origin": "https://r.test",
       "destination_origin": "https://d.test",
-      "Attribution-Reporting-Register-Event-Trigger": [
-        {
-          "trigger_data": "5",
-          "priority": "100"
-        }
-      ]
+      "Attribution-Reporting-Register-Trigger": {
+        "event_trigger_data": [
+          {
+            "trigger_data": "5",
+            "priority": "100"
+          }
+        ]
+      }
     },
     {
       "timestamp": "1643235578000",
       "reporting_origin": "https://r.test",
       "destination_origin": "https://d.test",
-      "Attribution-Reporting-Register-Event-Trigger": [
-        {
-          "trigger_data": "6"
+      "Attribution-Reporting-Register-Trigger": {
+        "event_trigger_data": [
+          {
+            "trigger_data": "6"
+          }
+        ],
+        "aggregatable_trigger_data": [
+          {
+            "source_keys": ["a"],
+            "key_piece": "0x400"
+          }
+        ],
+        "aggregatable_values": {
+          "b": 123
         }
-      ],
-      "Attribution-Reporting-Register-Aggregatable-Trigger-Data": [
-        {
-          "source_keys": ["a"],
-          "key_piece": "0x400"
-        }
-      ],
-      "Attribution-Reporting-Register-Aggregatable-Values": {
-        "b": 123
       }
     }
   ]
diff --git a/content/test/data/attribution_reporting/simulator/rejected_triggers.output.json b/content/test/data/attribution_reporting/simulator/rejected_triggers.output.json
index 93a416abf..db2b9a8 100644
--- a/content/test/data/attribution_reporting/simulator/rejected_triggers.output.json
+++ b/content/test/data/attribution_reporting/simulator/rejected_triggers.output.json
@@ -6,11 +6,13 @@
         "timestamp": "1643235572000",
         "reporting_origin": "https://r.test",
         "destination_origin": "https://d.test",
-        "Attribution-Reporting-Register-Event-Trigger": [
-          {
-            "trigger_data": "1"
-          }
-        ]
+        "Attribution-Reporting-Register-Trigger": {
+          "event_trigger_data": [
+            {
+              "trigger_data": "1"
+            }
+          ]
+        }
       }
     },
     {
@@ -19,12 +21,14 @@
         "timestamp": "1643235575000",
         "reporting_origin": "https://r.test",
         "destination_origin": "https://d.test",
-        "Attribution-Reporting-Register-Event-Trigger": [
-          {
-            "trigger_data": "3",
-            "deduplication_key": "22"
-          }
-        ]
+        "Attribution-Reporting-Register-Trigger": {
+          "event_trigger_data": [
+            {
+              "trigger_data": "3",
+              "deduplication_key": "22"
+            }
+          ]
+        }
       }
     },
     {
@@ -34,19 +38,21 @@
         "timestamp": "1643235578000",
         "reporting_origin": "https://r.test",
         "destination_origin": "https://d.test",
-        "Attribution-Reporting-Register-Event-Trigger": [
-          {
-            "trigger_data": "6"
+        "Attribution-Reporting-Register-Trigger": {
+          "event_trigger_data": [
+            {
+              "trigger_data": "6"
+            }
+          ],
+          "aggregatable_trigger_data": [
+            {
+              "source_keys": ["a"],
+              "key_piece": "0x400"
+            }
+          ],
+          "aggregatable_values": {
+            "b": 123
           }
-        ],
-        "Attribution-Reporting-Register-Aggregatable-Trigger-Data": [
-          {
-            "source_keys": ["a"],
-            "key_piece": "0x400"
-          }
-        ],
-        "Attribution-Reporting-Register-Aggregatable-Values": {
-          "b": 123
         }
       }
     }
diff --git a/content/test/data/attribution_reporting/simulator/replaced_reports.input.json b/content/test/data/attribution_reporting/simulator/replaced_reports.input.json
index e46b3b25..81844650 100644
--- a/content/test/data/attribution_reporting/simulator/replaced_reports.input.json
+++ b/content/test/data/attribution_reporting/simulator/replaced_reports.input.json
@@ -16,22 +16,26 @@
       "timestamp": "1643235574000",
       "reporting_origin": "https://r.test",
       "destination_origin": "https://d.test",
-      "Attribution-Reporting-Register-Event-Trigger": [
-        {
-          "trigger_data": "0"
-        }
-      ]
+      "Attribution-Reporting-Register-Trigger": {
+        "event_trigger_data": [
+          {
+            "trigger_data": "0"
+          }
+        ]
+      }
     },
     {
       "timestamp": "1643235575000",
       "reporting_origin": "https://r.test",
       "destination_origin": "https://d.test",
-      "Attribution-Reporting-Register-Event-Trigger": [
-        {
-          "trigger_data": "1",
-          "priority": "5"
-        }
-      ]
+      "Attribution-Reporting-Register-Trigger": {
+        "event_trigger_data": [
+          {
+            "trigger_data": "1",
+            "priority": "5"
+          }
+        ]
+      }
     }
   ]
 }
diff --git a/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt
index f8f6df28..7feae46 100644
--- a/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt
@@ -397,15 +397,17 @@
 crbug.com/1321305 [ mac release apple-apple-m1 ] Pixel_WebGPUImportVideoFrame [ Failure ]
 crbug.com/1321305 [ mac release apple-apple-m1 ] Pixel_WebGPUImportVideoFrameOffscreenCanvas [ Failure ]
 
-# WebGPU pixel tests fail because current expectations are incorrect. Disabled to allow work to progress.
-crbug.com/1325002 [ mac ] Pixel_WebGPUImportVideoFrame* [ Failure ]
-crbug.com/1325002 [ win ] Pixel_WebGPUImportVideoFrame* [ Failure ]
-crbug.com/1325002 [ linux ] Pixel_VulkanSwiftShader_WebGPUImportVideoFrame* [ Failure ]
-
-# Vulkan Swiftshader WebGPU interop flipped destination image
+# Vulkan Swiftshader WebGPU interop - flipped destination image
 crbug.com/1307787 [ linux skia-renderer-vulkan ] Pixel_VulkanSwiftShader_WebGPUCopyExternalImage* [ Failure ]
 
-# Vulkan Swiftshader WebGPU unaccelerated OffscreenCanvas missing source image
+# Vulkan Swiftshader WebGPU interop - missing destination image
+crbug.com/1307787 [ linux skia-renderer-vulkan ] Pixel_VulkanSwiftShader_WebGPUImportVideoFrame [ Failure ]
+
+# Vulkan Swiftshader WebGPU interop - missing source and destination images
+crbug.com/1307787 [ linux skia-renderer-vulkan ] Pixel_VulkanSwiftShader_WebGPUImportVideoFrameOffscreenCanvas [ Failure ]
+crbug.com/1307787 [ linux skia-renderer-vulkan ] Pixel_VulkanSwiftShader_WebGPUImportVideoFrameUnaccelerated [ Failure ]
+
+# Vulkan Swiftshader WebGPU interop - missing source image
 crbug.com/1307787 [ linux skia-renderer-vulkan ] Pixel_VulkanSwiftShader_WebGPUImportVideoFrameUnacceleratedOffscreenCanvas [ Failure ]
 
 crbug.com/1203317 [ android android-shield-android-tv no-passthrough ] Pixel_OffscreenCanvasWebGLPaintAfterResize [ RetryOnFailure ]
diff --git a/content/test/web_contents_observer_consistency_checker.cc b/content/test/web_contents_observer_consistency_checker.cc
index 8bc2b59..75cf451 100644
--- a/content/test/web_contents_observer_consistency_checker.cc
+++ b/content/test/web_contents_observer_consistency_checker.cc
@@ -66,9 +66,6 @@
                  << Format(render_frame_host);
   }
 
-  CHECK(render_frame_host->IsRenderFrameCreated())
-      << "RenderFrameCreated was called for a RenderFrameHost that has not been"
-         "marked created.";
   CHECK(render_frame_host->GetProcess()->IsInitializedAndNotDead())
       << "RenderFrameCreated was called for a RenderFrameHost whose render "
          "process is not currently live, so there's no way for the RenderFrame "
@@ -95,9 +92,6 @@
 void WebContentsObserverConsistencyChecker::RenderFrameDeleted(
     RenderFrameHost* render_frame_host) {
   CHECK(!web_contents_destroyed_);
-  CHECK(!render_frame_host->IsRenderFrameCreated())
-      << "RenderFrameDeleted was called for a RenderFrameHost that is"
-         "(still) marked as created.";
   CHECK(!render_frame_host->IsRenderFrameLive())
       << "RenderFrameDeleted was called for a RenderFrameHost that is"
          "still live.";
diff --git a/content/web_test/browser/web_test_tts_platform.cc b/content/web_test/browser/web_test_tts_platform.cc
index 9ab1a544..769fd19 100644
--- a/content/web_test/browser/web_test_tts_platform.cc
+++ b/content/web_test/browser/web_test_tts_platform.cc
@@ -76,6 +76,8 @@
     const GURL& source_url,
     std::vector<content::VoiceData>* out_voices) {}
 
+void WebTestTtsPlatform::RefreshVoices() {}
+
 WebTestTtsPlatform::WebTestTtsPlatform() = default;
 
 WebTestTtsPlatform::~WebTestTtsPlatform() = default;
diff --git a/content/web_test/browser/web_test_tts_platform.h b/content/web_test/browser/web_test_tts_platform.h
index 1dd35c37..536b500 100644
--- a/content/web_test/browser/web_test_tts_platform.h
+++ b/content/web_test/browser/web_test_tts_platform.h
@@ -45,6 +45,7 @@
       content::BrowserContext* browser_context,
       const GURL& source_url,
       std::vector<content::VoiceData>* out_voices) override;
+  void RefreshVoices() override;
 
  private:
   WebTestTtsPlatform();
diff --git a/extensions/browser/api/networking_private/networking_private_chromeos.cc b/extensions/browser/api/networking_private/networking_private_chromeos.cc
index 45328e3f..554c7ce 100644
--- a/extensions/browser/api/networking_private/networking_private_chromeos.cc
+++ b/extensions/browser/api/networking_private/networking_private_chromeos.cc
@@ -12,6 +12,8 @@
 #include "base/containers/contains.h"
 #include "base/logging.h"
 #include "base/values.h"
+#include "chromeos/ash/components/network/onc/network_onc_utils.h"
+#include "chromeos/ash/components/network/onc/onc_translator.h"
 #include "chromeos/components/onc/onc_signature.h"
 #include "chromeos/login/login_state/login_state.h"
 #include "chromeos/network/device_state.h"
@@ -24,8 +26,6 @@
 #include "chromeos/network/network_state.h"
 #include "chromeos/network/network_state_handler.h"
 #include "chromeos/network/network_util.h"
-#include "chromeos/network/onc/network_onc_utils.h"
-#include "chromeos/network/onc/onc_translator.h"
 #include "chromeos/network/portal_detector/network_portal_detector.h"
 #include "components/onc/onc_constants.h"
 #include "components/proxy_config/proxy_prefs.h"
diff --git a/extensions/browser/api/networking_private/networking_private_event_router_chromeos.cc b/extensions/browser/api/networking_private/networking_private_event_router_chromeos.cc
index a725efc0..9c2604a 100644
--- a/extensions/browser/api/networking_private/networking_private_event_router_chromeos.cc
+++ b/extensions/browser/api/networking_private/networking_private_event_router_chromeos.cc
@@ -5,6 +5,7 @@
 #include "extensions/browser/api/networking_private/networking_private_event_router.h"
 
 #include "base/json/json_writer.h"
+#include "chromeos/ash/components/network/onc/onc_translator.h"
 #include "chromeos/components/onc/onc_signature.h"
 #include "chromeos/network/device_state.h"
 #include "chromeos/network/network_certificate_handler.h"
@@ -12,7 +13,6 @@
 #include "chromeos/network/network_state.h"
 #include "chromeos/network/network_state_handler.h"
 #include "chromeos/network/network_state_handler_observer.h"
-#include "chromeos/network/onc/onc_translator.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
 #include "components/onc/onc_constants.h"
 #include "content/public/browser/browser_context.h"
diff --git a/fuchsia/engine/BUILD.gn b/fuchsia/engine/BUILD.gn
index f44bcbaa..d639c3e 100644
--- a/fuchsia/engine/BUILD.gn
+++ b/fuchsia/engine/BUILD.gn
@@ -638,6 +638,7 @@
       ] ]
   use_cfv2 = false
   additional_manifest_fragments = [
+    "//build/config/fuchsia/test/audio_capabilities.test-cmx",
     "//build/config/fuchsia/test/network_capabilities.test-cmx",
     "//build/config/fuchsia/test/read_debug_data.test-cmx",
     "//build/config/fuchsia/test/vulkan_capabilities.test-cmx",
diff --git a/fuchsia/engine/browser/frame_impl.cc b/fuchsia/engine/browser/frame_impl.cc
index d815a97..09ddc4b 100644
--- a/fuchsia/engine/browser/frame_impl.cc
+++ b/fuchsia/engine/browser/frame_impl.cc
@@ -996,8 +996,9 @@
   // to the view-tree to be displayed. See https://crbug.com/1109270
 }
 
-void FrameImpl::SetMediaSessionId(uint64_t session_id) {
-  media_session_id_ = session_id;
+void FrameImpl::SetMediaSettings(
+    fuchsia::web::FrameMediaSettings media_settings) {
+  media_settings_ = std::move(media_settings);
 }
 
 void FrameImpl::MediaStartedPlaying(const MediaPlayerInfo& video_type,
diff --git a/fuchsia/engine/browser/frame_impl.h b/fuchsia/engine/browser/frame_impl.h
index 50631a4..ea127a2 100644
--- a/fuchsia/engine/browser/frame_impl.h
+++ b/fuchsia/engine/browser/frame_impl.h
@@ -86,8 +86,8 @@
   FrameImpl(const FrameImpl&) = delete;
   FrameImpl& operator=(const FrameImpl&) = delete;
 
-  absl::optional<uint64_t> media_session_id() const {
-    return media_session_id_;
+  const fuchsia::web::FrameMediaSettings& media_settings() const {
+    return media_settings_;
   }
 
   FramePermissionController* permission_controller() {
@@ -273,7 +273,8 @@
       SetUrlRequestRewriteRulesCallback callback) override;
   void EnableHeadlessRendering() override;
   void DisableHeadlessRendering() override;
-  void SetMediaSessionId(uint64_t session_id) override;
+  void SetMediaSettings(
+      fuchsia::web::FrameMediaSettings media_settings) override;
   void ForceContentDimensions(
       std::unique_ptr<fuchsia::ui::gfx::vec2> web_dips) override;
   void SetPermissionState(fuchsia::web::PermissionDescriptor permission,
@@ -387,9 +388,7 @@
   FramePermissionController permission_controller_;
   std::unique_ptr<NavigationPolicyHandler> navigation_policy_handler_;
 
-  // Session ID to use for fuchsia.media.AudioConsumer. Set with
-  // SetMediaSessionId().
-  absl::optional<uint64_t> media_session_id_;
+  fuchsia::web::FrameMediaSettings media_settings_;
 
   // Stored settings for web contents in the current Frame.
   fuchsia::web::ContentAreaSettings content_area_settings_;
diff --git a/fuchsia/engine/browser/web_engine_media_resource_provider_impl.cc b/fuchsia/engine/browser/web_engine_media_resource_provider_impl.cc
index 1e0fc518..a997d9b 100644
--- a/fuchsia/engine/browser/web_engine_media_resource_provider_impl.cc
+++ b/fuchsia/engine/browser/web_engine_media_resource_provider_impl.cc
@@ -36,7 +36,8 @@
     ShouldUseAudioConsumerCallback callback) {
   auto* frame_impl = FrameImpl::FromRenderFrameHost(render_frame_host());
   DCHECK(frame_impl);
-  std::move(callback).Run(frame_impl->media_session_id().has_value());
+  std::move(callback).Run(
+      frame_impl->media_settings().has_audio_consumer_session_id());
 }
 
 void WebEngineMediaResourceProviderImpl::CreateAudioConsumer(
@@ -55,12 +56,13 @@
   auto* frame_impl = FrameImpl::FromRenderFrameHost(render_frame_host());
   DCHECK(frame_impl);
 
-  if (!frame_impl->media_session_id().has_value()) {
+  if (!frame_impl->media_settings().has_audio_consumer_session_id()) {
     LOG(WARNING) << "Renderer tried creating AudioConsumer for a Frame without "
                     "media_session_id().";
     return;
   }
 
-  factory->CreateAudioConsumer(frame_impl->media_session_id().value(),
-                               std::move(request));
+  factory->CreateAudioConsumer(
+      frame_impl->media_settings().audio_consumer_session_id(),
+      std::move(request));
 }
diff --git a/fuchsia/engine/test/web_engine_shell.cmx b/fuchsia/engine/test/web_engine_shell.cmx
index 7b303c6..a14d635 100644
--- a/fuchsia/engine/test/web_engine_shell.cmx
+++ b/fuchsia/engine/test/web_engine_shell.cmx
@@ -16,6 +16,7 @@
       "fuchsia.feedback.ComponentDataRegister",
       "fuchsia.feedback.CrashReportingProductRegister",
       "fuchsia.fonts.Provider",
+      "fuchsia.hwinfo.Product",
       "fuchsia.input.virtualkeyboard.ControllerCreator",
       "fuchsia.intl.PropertyProvider",
       "fuchsia.logger.LogSink",
diff --git a/fuchsia/engine/web_engine_integration_test.cc b/fuchsia/engine/web_engine_integration_test.cc
index 298ab77..0009cf59 100644
--- a/fuchsia/engine/web_engine_integration_test.cc
+++ b/fuchsia/engine/web_engine_integration_test.cc
@@ -12,6 +12,7 @@
 
 #include "base/fuchsia/fuchsia_logging.h"
 #include "base/fuchsia/mem_buffer_util.h"
+#include "base/fuchsia/process_context.h"
 #include "base/strings/stringprintf.h"
 #include "base/test/test_future.h"
 #include "build/build_config.h"
@@ -59,32 +60,6 @@
   void RunPermissionTest(bool grant);
 };
 
-// Configures the default filtered service directory with a fake AudioConsumer
-// service for testing.
-class WebEngineIntegrationMediaTest : public WebEngineIntegrationTest {
- protected:
-  WebEngineIntegrationMediaTest()
-      : fake_audio_consumer_service_(filtered_service_directory()
-                                         .outgoing_directory()
-                                         ->GetOrCreateDirectory("svc")),
-        fake_audio_device_enumerator_(filtered_service_directory()
-                                          .outgoing_directory()
-                                          ->GetOrCreateDirectory("svc")) {}
-
-  // Returns a CreateContextParams that has AUDIO feature, and the "testdata"
-  // content directory provider configured.
-  fuchsia::web::CreateContextParams ContextParamsWithAudioAndTestData() {
-    fuchsia::web::CreateContextParams create_params =
-        TestContextParamsWithTestData();
-    *create_params.mutable_features() |=
-        fuchsia::web::ContextFeatureFlags::AUDIO;
-    return create_params;
-  }
-
-  media::FakeAudioConsumerService fake_audio_consumer_service_;
-  media::FakeAudioDeviceEnumerator fake_audio_device_enumerator_;
-};
-
 class WebEngineIntegrationUserAgentTest : public WebEngineIntegrationTest {
  protected:
   GURL GetEchoUserAgentUrl() {
@@ -344,11 +319,54 @@
   navigation_listener()->RunUntilUrlAndTitleEquals(kUrl, kTitle);
 }
 
-TEST_F(WebEngineIntegrationMediaTest, PlayAudio) {
-  CreateContextAndFrame(ContextParamsWithAudioAndTestData());
+// Configures the default filtered service directory with a fake AudioConsumer
+// service for testing.
+class WebEngineIntegrationMediaTest : public WebEngineIntegrationTest {
+ protected:
+  WebEngineIntegrationMediaTest()
+      : fake_audio_consumer_service_(filtered_service_directory()
+                                         .outgoing_directory()
+                                         ->GetOrCreateDirectory("svc")) {
+    auto* outgoing_directory =
+        filtered_service_directory().outgoing_directory();
 
-  static const uint16_t kTestMediaSessionId = 43;
-  frame_->SetMediaSessionId(kTestMediaSessionId);
+    // Publish fake AudioDeviceEnumerator.
+    outgoing_directory
+        ->RemovePublicService<fuchsia::media::AudioDeviceEnumerator>();
+    fake_audio_device_enumerator_.emplace(
+        outgoing_directory->GetOrCreateDirectory("svc"));
+
+    // Intercept `fuchsia::media::Audio` connections in order to count them.
+    outgoing_directory->RemovePublicService<fuchsia::media::Audio>();
+    zx_status_t status = outgoing_directory->AddPublicService(
+        fidl::InterfaceRequestHandler<fuchsia::media::Audio>(
+            [this](auto request) {
+              ++num_audio_connections_;
+              base::ComponentContextForProcess()->svc()->Connect(
+                  std::move(request));
+            }));
+    ZX_CHECK(status == ZX_OK, status) << "AddPublicService";
+  }
+
+  // Returns a CreateContextParams that has AUDIO feature, and the "testdata"
+  // content directory provider configured.
+  fuchsia::web::CreateContextParams ContextParamsWithAudioAndTestData() {
+    fuchsia::web::CreateContextParams create_params =
+        TestContextParamsWithTestData();
+    *create_params.mutable_features() |=
+        fuchsia::web::ContextFeatureFlags::AUDIO;
+    return create_params;
+  }
+
+  media::FakeAudioConsumerService fake_audio_consumer_service_;
+  absl::optional<media::FakeAudioDeviceEnumerator>
+      fake_audio_device_enumerator_;
+
+  size_t num_audio_connections_ = 0;
+};
+
+TEST_F(WebEngineIntegrationMediaTest, PlayAudioToAudioRenderer) {
+  CreateContextAndFrame(ContextParamsWithAudioAndTestData());
 
   ASSERT_NO_FATAL_FAILURE(LoadUrlAndExpectResponse(
       "fuchsia-dir://testdata/play_audio.html",
@@ -356,6 +374,27 @@
 
   navigation_listener()->RunUntilTitleEquals("ended");
 
+  EXPECT_EQ(num_audio_connections_, 1U);
+  EXPECT_EQ(fake_audio_consumer_service_.num_instances(), 0U);
+}
+
+TEST_F(WebEngineIntegrationMediaTest, PlayAudioToAudioConsumer) {
+  CreateContextAndFrame(ContextParamsWithAudioAndTestData());
+
+  // Send `FrameMediaSettings` with `audio_consumer_session_id`. This enables
+  // `AudioConsumer`.
+  static const uint16_t kTestMediaSessionId = 43;
+  fuchsia::web::FrameMediaSettings media_settings;
+  media_settings.set_audio_consumer_session_id(kTestMediaSessionId);
+  frame_->SetMediaSettings(std::move(media_settings));
+
+  ASSERT_NO_FATAL_FAILURE(LoadUrlAndExpectResponse(
+      "fuchsia-dir://testdata/play_audio.html",
+      cr_fuchsia::CreateLoadUrlParamsWithUserActivation()));
+
+  navigation_listener()->RunUntilTitleEquals("ended");
+
+  EXPECT_EQ(num_audio_connections_, 0U);
   ASSERT_EQ(fake_audio_consumer_service_.num_instances(), 1U);
 
   auto pos = fake_audio_consumer_service_.instance(0)->GetMediaPosition();
@@ -376,40 +415,29 @@
       TestContextParamsWithTestData();
   CreateContextAndFrame(std::move(create_params));
 
-  bool is_requested = false;
-  zx_status_t status =
-      filtered_service_directory()
-          .outgoing_directory()
-          ->RemovePublicService<fuchsia::media::SessionAudioConsumerFactory>();
-  ZX_CHECK(status == ZX_OK, status) << "RemovePublicService";
-  status = filtered_service_directory().outgoing_directory()->AddPublicService(
-      fidl::InterfaceRequestHandler<
-          fuchsia::media::SessionAudioConsumerFactory>(
-          [&is_requested](auto request) { is_requested = true; }));
-  ZX_CHECK(status == ZX_OK, status) << "AddPublicService";
-
-  static const uint16_t kTestMediaSessionId = 1;
-  frame_->SetMediaSessionId(kTestMediaSessionId);
-
   ASSERT_NO_FATAL_FAILURE(LoadUrlAndExpectResponse(
       "fuchsia-dir://testdata/play_audio.html",
       cr_fuchsia::CreateLoadUrlParamsWithUserActivation()));
 
-  navigation_listener()->RunUntilTitleEquals("media element error");
-  EXPECT_FALSE(is_requested);
+  // The file is still expected to play to the end.
+  navigation_listener()->RunUntilTitleEquals("ended");
+
+  EXPECT_EQ(fake_audio_consumer_service_.num_instances(), 0U);
+  EXPECT_EQ(num_audio_connections_, 0U);
 }
 
 TEST_F(WebEngineIntegrationMediaTest, PlayVideo) {
   CreateContextAndFrame(ContextParamsWithAudioAndTestData());
 
-  static const uint16_t kTestMediaSessionId = 1;
-  frame_->SetMediaSessionId(kTestMediaSessionId);
-
   ASSERT_NO_FATAL_FAILURE(LoadUrlAndExpectResponse(
       kAutoplayVp9OpusToEndUrl,
       cr_fuchsia::CreateLoadUrlParamsWithUserActivation()));
 
   navigation_listener()->RunUntilTitleEquals("ended");
+
+  // Audio should be sent to AudioRenderer (created though `fuchsia.web.Audio`).
+  EXPECT_EQ(num_audio_connections_, 1U);
+  EXPECT_EQ(fake_audio_consumer_service_.num_instances(), 0U);
 }
 
 void WebEngineIntegrationTest::RunPermissionTest(bool grant) {
@@ -601,12 +629,13 @@
        HardwareVideoDecoderFlag_Provided) {
   // Check that the CodecFactory service is requested.
   base::RunLoop codec_connected_run_loop;
-  zx_status_t status =
-      filtered_service_directory().outgoing_directory()->AddPublicService(
-          fidl::InterfaceRequestHandler<fuchsia::mediacodec::CodecFactory>(
-              [&codec_connected_run_loop](auto request) {
-                codec_connected_run_loop.Quit();
-              }));
+  auto* outgoing_directory = filtered_service_directory().outgoing_directory();
+  outgoing_directory->RemovePublicService<fuchsia::mediacodec::CodecFactory>();
+  zx_status_t status = outgoing_directory->AddPublicService(
+      fidl::InterfaceRequestHandler<fuchsia::mediacodec::CodecFactory>(
+          [&codec_connected_run_loop](auto request) {
+            codec_connected_run_loop.Quit();
+          }));
   ZX_CHECK(status == ZX_OK, status) << "AddPublicService";
 
   // The VULKAN flag is required for hardware video decoders to be available.
@@ -617,9 +646,6 @@
       fuchsia::web::ContextFeatureFlags::HARDWARE_VIDEO_DECODER;
   CreateContextAndFrame(std::move(create_params));
 
-  static const uint16_t kTestMediaSessionId = 1;
-  frame_->SetMediaSessionId(kTestMediaSessionId);
-
   ASSERT_NO_FATAL_FAILURE(LoadUrlAndExpectResponse(
       kAutoplayVp9OpusToEndUrl,
       cr_fuchsia::CreateLoadUrlParamsWithUserActivation()));
@@ -631,19 +657,17 @@
 // The video should use software decoders and still play.
 TEST_F(WebEngineIntegrationMediaTest, HardwareVideoDecoderFlag_NotProvided) {
   bool is_requested = false;
-  zx_status_t status =
-      filtered_service_directory().outgoing_directory()->AddPublicService(
-          fidl::InterfaceRequestHandler<fuchsia::mediacodec::CodecFactory>(
-              [&is_requested](auto request) { is_requested = true; }));
+  auto* outgoing_directory = filtered_service_directory().outgoing_directory();
+  outgoing_directory->RemovePublicService<fuchsia::mediacodec::CodecFactory>();
+  zx_status_t status = outgoing_directory->AddPublicService(
+      fidl::InterfaceRequestHandler<fuchsia::mediacodec::CodecFactory>(
+          [&is_requested](auto request) { is_requested = true; }));
   ZX_CHECK(status == ZX_OK, status) << "AddPublicService";
 
   fuchsia::web::CreateContextParams create_params =
       ContextParamsWithAudioAndTestData();
   CreateContextAndFrame(std::move(create_params));
 
-  static const uint16_t kTestMediaSessionId = 1;
-  frame_->SetMediaSessionId(kTestMediaSessionId);
-
   ASSERT_NO_FATAL_FAILURE(LoadUrlAndExpectResponse(
       kAutoplayVp9OpusToEndUrl,
       cr_fuchsia::CreateLoadUrlParamsWithUserActivation()));
diff --git a/fuchsia/engine/web_instance.cmx b/fuchsia/engine/web_instance.cmx
index d6a05145..4163a99 100644
--- a/fuchsia/engine/web_instance.cmx
+++ b/fuchsia/engine/web_instance.cmx
@@ -15,6 +15,7 @@
       "fuchsia.camera3.DeviceWatcher",
       "fuchsia.device.NameProvider",
       "fuchsia.fonts.Provider",
+      "fuchsia.hwinfo.Product",
       "fuchsia.input.virtualkeyboard.ControllerCreator",
       "fuchsia.intl.PropertyProvider",
       "fuchsia.legacymetrics.MetricsRecorder",
diff --git a/fuchsia/engine/web_instance_host/web_instance_host.cc b/fuchsia/engine/web_instance_host/web_instance_host.cc
index 7dbf2514..96417238 100644
--- a/fuchsia/engine/web_instance_host/web_instance_host.cc
+++ b/fuchsia/engine/web_instance_host/web_instance_host.cc
@@ -428,12 +428,12 @@
   // at:
   //   https://fuchsia.dev/reference/fidl/fuchsia.web#CreateContextParams.service_directory
   std::vector<std::string> services{
-      "fuchsia.buildinfo.Provider", "fuchsia.device.NameProvider",
-      "fuchsia.fonts.Provider",     "fuchsia.intl.PropertyProvider",
-      "fuchsia.logger.LogSink",     "fuchsia.memorypressure.Provider",
-      "fuchsia.process.Launcher",
+      "fuchsia.buildinfo.Provider",      "fuchsia.device.NameProvider",
+      "fuchsia.fonts.Provider",          "fuchsia.hwinfo.Product",
+      "fuchsia.intl.PropertyProvider",   "fuchsia.logger.LogSink",
+      "fuchsia.memorypressure.Provider", "fuchsia.process.Launcher",
       "fuchsia.settings.Display",  // Used if preferred theme is DEFAULT.
-      "fuchsia.sysmem.Allocator",   "fuchsia.ui.scenic.Scenic"};
+      "fuchsia.sysmem.Allocator",        "fuchsia.ui.scenic.Scenic"};
 
   // TODO(crbug.com/1209031): Provide these conditionally, once corresponding
   // ContextFeatureFlags have been defined.
diff --git a/fuchsia/runners/cast/cast_component.cc b/fuchsia/runners/cast/cast_component.cc
index 1d6ecad2..d79c10c 100644
--- a/fuchsia/runners/cast/cast_component.cc
+++ b/fuchsia/runners/cast/cast_component.cc
@@ -64,7 +64,7 @@
     return false;
   if (!initial_url_rewrite_rules)
     return false;
-  if (!media_session_id)
+  if (!media_settings)
     return false;
   return true;
 }
@@ -85,7 +85,7 @@
           std::move(params.initial_url_rewrite_rules.value())),
       api_bindings_client_(std::move(params.api_bindings_client)),
       application_context_(params.application_context.Bind()),
-      media_session_id_(params.media_session_id.value()),
+      media_settings_(std::move(params.media_settings.value())),
       headless_disconnect_watch_(FROM_HERE) {
   base::AutoReset<bool> constructor_active_reset(&constructor_active_, true);
 }
@@ -146,7 +146,7 @@
   });
   OnRewriteRulesReceived(std::move(initial_url_rewrite_rules_));
 
-  frame()->SetMediaSessionId(media_session_id_);
+  frame()->SetMediaSettings(std::move(media_settings_));
   frame()->ConfigureInputTypes(fuchsia::web::InputTypes::ALL,
                                fuchsia::web::AllowInputState::DENY);
   if (application_config_.has_initial_min_console_log_severity()) {
diff --git a/fuchsia/runners/cast/cast_component.h b/fuchsia/runners/cast/cast_component.h
index d1ed3b0e..53056c1 100644
--- a/fuchsia/runners/cast/cast_component.h
+++ b/fuchsia/runners/cast/cast_component.h
@@ -63,7 +63,7 @@
         application_context;
     absl::optional<std::vector<fuchsia::web::UrlRequestRewriteRule>>
         initial_url_rewrite_rules;
-    absl::optional<uint64_t> media_session_id;
+    absl::optional<fuchsia::web::FrameMediaSettings> media_settings;
   };
 
   // See WebComponent documentation for details of |debug_name| and |runner|.
@@ -138,7 +138,7 @@
   std::unique_ptr<ApiBindingsClient> api_bindings_client_;
   std::unique_ptr<ApplicationControllerImpl> application_controller_;
   chromium::cast::ApplicationContextPtr application_context_;
-  uint64_t media_session_id_ = 0;
+  fuchsia::web::FrameMediaSettings media_settings_;
   zx::eventpair headless_view_token_;
   base::MessagePumpForIO::ZxHandleWatchController headless_disconnect_watch_;
 };
diff --git a/fuchsia/runners/cast/cast_runner.cc b/fuchsia/runners/cast/cast_runner.cc
index de07f0d..fffc18b 100644
--- a/fuchsia/runners/cast/cast_runner.cc
+++ b/fuchsia/runners/cast/cast_runner.cc
@@ -50,6 +50,7 @@
     // "fuchsia.camera3.DeviceWatcher" is redirected to the agent.
     "fuchsia.device.NameProvider",
     "fuchsia.fonts.Provider",
+    "fuchsia.hwinfo.Product",
     "fuchsia.input.virtualkeyboard.ControllerCreator",
     "fuchsia.intl.PropertyProvider",
     // "fuchsia.legacymetrics.MetricsRecorder" is redirected to the agent.
diff --git a/fuchsia/runners/cast/cast_runner.cmx b/fuchsia/runners/cast/cast_runner.cmx
index 9757e704..137974ab 100644
--- a/fuchsia/runners/cast/cast_runner.cmx
+++ b/fuchsia/runners/cast/cast_runner.cmx
@@ -15,6 +15,7 @@
       "fuchsia.feedback.ComponentDataRegister",
       "fuchsia.feedback.CrashReportingProductRegister",
       "fuchsia.fonts.Provider",
+      "fuchsia.hwinfo.Product",
       "fuchsia.input.virtualkeyboard.ControllerCreator",
       "fuchsia.intl.PropertyProvider",
       "fuchsia.logger.LogSink",
diff --git a/fuchsia/runners/cast/cast_runner_integration_test.cc b/fuchsia/runners/cast/cast_runner_integration_test.cc
index f7ff292..962b247 100644
--- a/fuchsia/runners/cast/cast_runner_integration_test.cc
+++ b/fuchsia/runners/cast/cast_runner_integration_test.cc
@@ -142,7 +142,7 @@
  private:
   // chromium::cast::ApplicationContext implementation.
   void GetMediaSessionId(GetMediaSessionIdCallback callback) override {
-    callback(0);
+    callback(1);
   }
   void SetApplicationController(
       fidl::InterfaceHandle<chromium::cast::ApplicationController> controller)
diff --git a/fuchsia/runners/cast/fidl/application_config.fidl b/fuchsia/runners/cast/fidl/application_config.fidl
index 4406206..8667be21 100644
--- a/fuchsia/runners/cast/fidl/application_config.fidl
+++ b/fuchsia/runners/cast/fidl/application_config.fidl
@@ -6,6 +6,7 @@
 library chromium.cast;
 
 using fuchsia.diagnostics;
+using fuchsia.media;
 using fuchsia.ui.gfx;
 using fuchsia.web;
 
@@ -44,7 +45,12 @@
     /// [`fuchsia.logger/LogSink`] configured for this application. If not set, no
     /// messages will be logged.
     // TODO(crbug.com/1088094): Deprecate once there is another mechanism.
-   10: initial_min_console_log_severity fuchsia.diagnostics.Severity;
+    10: initial_min_console_log_severity fuchsia.diagnostics.Severity;
+
+    /// The usage for [`fuchsia.media/AudioRenderer`] created by the app. If not
+    /// specified, then audio will be played through
+    /// [`fuchsia.media/AudioConsumer`].
+    11: audio_renderer_usage fuchsia.media.AudioRenderUsage;
 };
 
 /// Service interface for working with application configurations.
diff --git a/fuchsia/runners/cast/fidl/application_context.fidl b/fuchsia/runners/cast/fidl/application_context.fidl
index e6bc30fb..c5de66fe 100644
--- a/fuchsia/runners/cast/fidl/application_context.fidl
+++ b/fuchsia/runners/cast/fidl/application_context.fidl
@@ -7,7 +7,9 @@
 
 @discoverable
 protocol ApplicationContext {
-    /// Returns session_id to use in the AudioConsumer API.
+    /// Returns `session_id` to use in the AudioConsumer API. May return 0
+    /// if there is no media session associated with the app. In this case
+    /// audio will be rendered through AudioRenderer.
     GetMediaSessionId() -> (struct {
         media_session_id uint64;
     });
diff --git a/fuchsia/runners/cast/pending_cast_component.cc b/fuchsia/runners/cast/pending_cast_component.cc
index f67ec439..bf5e60f8 100644
--- a/fuchsia/runners/cast/pending_cast_component.cc
+++ b/fuchsia/runners/cast/pending_cast_component.cc
@@ -110,10 +110,24 @@
     ZX_LOG(ERROR, status) << "ApplicationContext disconnected.";
     delegate_->CancelPendingComponent(this);
   });
-  application_context_->GetMediaSessionId([this](uint64_t session_id) {
-    params_.media_session_id = session_id;
-    MaybeLaunchComponent();
-  });
+
+  if (params_.application_config.has_audio_renderer_usage()) {
+    DCHECK(!params_.media_settings);
+    params_.media_settings = fuchsia::web::FrameMediaSettings{};
+    params_.media_settings->set_renderer_usage(
+        params_.application_config.audio_renderer_usage());
+  } else {
+    // If `audio_renderer_usage` is not specified then `AudioConsumer` is used
+    // for that app. We need to fetch `session_id` in that case.
+    application_context_->GetMediaSessionId([this](uint64_t session_id) {
+      DCHECK(!params_.media_settings);
+      params_.media_settings = fuchsia::web::FrameMediaSettings{};
+      if (session_id > 0)
+        params_.media_settings->set_audio_consumer_session_id(session_id);
+
+      MaybeLaunchComponent();
+    });
+  }
 }
 
 void PendingCastComponent::OnApiBindingsInitialized() {
diff --git a/fuchsia/runners/web/web_runner.cmx b/fuchsia/runners/web/web_runner.cmx
index cf99c070..c98e487 100644
--- a/fuchsia/runners/web/web_runner.cmx
+++ b/fuchsia/runners/web/web_runner.cmx
@@ -14,6 +14,7 @@
       "fuchsia.feedback.ComponentDataRegister",
       "fuchsia.feedback.CrashReportingProductRegister",
       "fuchsia.fonts.Provider",
+      "fuchsia.hwinfo.Product",
       "fuchsia.input.virtualkeyboard.ControllerCreator",
       "fuchsia.intl.PropertyProvider",
       "fuchsia.logger.LogSink",
diff --git a/infra/config/generated/builders/ci/Win x64 Builder/properties.json b/infra/config/generated/builders/ci/Win x64 Builder/properties.json
index 44d3421e..4d0b417 100644
--- a/infra/config/generated/builders/ci/Win x64 Builder/properties.json
+++ b/infra/config/generated/builders/ci/Win x64 Builder/properties.json
@@ -98,8 +98,8 @@
               "project": "chromium"
             },
             "builder_spec": {
-              "build_gs_bucket": "chromium-fyi-archive",
-              "builder_group": "chromium.fyi",
+              "build_gs_bucket": "chromium-win-archive",
+              "builder_group": "chromium.win",
               "execution_mode": "TEST",
               "legacy_chromium_config": {
                 "apply_configs": [
diff --git a/infra/config/generated/builders/ci/Win11 Tests x64/properties.json b/infra/config/generated/builders/ci/Win11 Tests x64/properties.json
index 86694361..13fd9d19 100644
--- a/infra/config/generated/builders/ci/Win11 Tests x64/properties.json
+++ b/infra/config/generated/builders/ci/Win11 Tests x64/properties.json
@@ -37,8 +37,8 @@
               "project": "chromium"
             },
             "builder_spec": {
-              "build_gs_bucket": "chromium-fyi-archive",
-              "builder_group": "chromium.fyi",
+              "build_gs_bucket": "chromium-win-archive",
+              "builder_group": "chromium.win",
               "execution_mode": "TEST",
               "legacy_chromium_config": {
                 "apply_configs": [
@@ -86,6 +86,9 @@
       "v.test_suite"
     ]
   },
-  "builder_group": "chromium.fyi",
-  "recipe": "chromium"
+  "builder_group": "chromium.win",
+  "recipe": "chromium",
+  "sheriff_rotations": [
+    "chromium"
+  ]
 }
\ No newline at end of file
diff --git a/infra/config/generated/builders/reclient/Linux Builder reclient test/properties.json b/infra/config/generated/builders/reclient/Linux Builder reclient test/properties.json
index fa5b27b..669bb9e 100644
--- a/infra/config/generated/builders/reclient/Linux Builder reclient test/properties.json
+++ b/infra/config/generated/builders/reclient/Linux Builder reclient test/properties.json
@@ -43,7 +43,7 @@
     }
   },
   "$build/reclient": {
-    "instance": "goma-foundry-experiments",
+    "instance": "rbe-chromium-trusted-test",
     "metrics_project": "chromium-reclient-metrics"
   },
   "$recipe_engine/resultdb/test_presentation": {
diff --git a/infra/config/generated/builders/reclient/Simple Chrome Builder reclient test/properties.json b/infra/config/generated/builders/reclient/Simple Chrome Builder reclient test/properties.json
index 69f2d9c..441ce26f1 100644
--- a/infra/config/generated/builders/reclient/Simple Chrome Builder reclient test/properties.json
+++ b/infra/config/generated/builders/reclient/Simple Chrome Builder reclient test/properties.json
@@ -47,7 +47,7 @@
     }
   },
   "$build/reclient": {
-    "instance": "goma-foundry-experiments",
+    "instance": "rbe-chromium-trusted-test",
     "metrics_project": "chromium-reclient-metrics"
   },
   "$recipe_engine/resultdb/test_presentation": {
diff --git a/infra/config/generated/builders/reclient/Win x64 Builder reclient test/properties.json b/infra/config/generated/builders/reclient/Win x64 Builder reclient test/properties.json
index c978c0db..2e0af97c 100644
--- a/infra/config/generated/builders/reclient/Win x64 Builder reclient test/properties.json
+++ b/infra/config/generated/builders/reclient/Win x64 Builder reclient test/properties.json
@@ -44,7 +44,7 @@
     }
   },
   "$build/reclient": {
-    "instance": "goma-foundry-experiments",
+    "instance": "rbe-chromium-trusted-test",
     "metrics_project": "chromium-reclient-metrics"
   },
   "$recipe_engine/resultdb/test_presentation": {
diff --git a/infra/config/generated/builders/try/win11-x64-fyi-rel/properties.json b/infra/config/generated/builders/try/win11-x64-fyi-rel/properties.json
index 741ef58e..db8d3402 100644
--- a/infra/config/generated/builders/try/win11-x64-fyi-rel/properties.json
+++ b/infra/config/generated/builders/try/win11-x64-fyi-rel/properties.json
@@ -37,8 +37,8 @@
               "project": "chromium"
             },
             "builder_spec": {
-              "build_gs_bucket": "chromium-fyi-archive",
-              "builder_group": "chromium.fyi",
+              "build_gs_bucket": "chromium-win-archive",
+              "builder_group": "chromium.win",
               "execution_mode": "TEST",
               "legacy_chromium_config": {
                 "apply_configs": [
diff --git a/infra/config/generated/luci/cr-buildbucket.cfg b/infra/config/generated/luci/cr-buildbucket.cfg
index 35844765..65f0064 100644
--- a/infra/config/generated/luci/cr-buildbucket.cfg
+++ b/infra/config/generated/luci/cr-buildbucket.cfg
@@ -21068,7 +21068,7 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Windows-10"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.chromium.ci"
       dimensions: "ssd:0"
       exe {
@@ -21097,11 +21097,14 @@
         '      }'
         '    }'
         '  },'
-        '  "builder_group": "chromium.fyi",'
+        '  "builder_group": "chromium.win",'
         '  "led_builder_is_bootstrapped": true,'
-        '  "recipe": "chromium"'
+        '  "recipe": "chromium",'
+        '  "sheriff_rotations": ['
+        '    "chromium"'
+        '  ]'
         '}'
-      execution_timeout_secs: 36000
+      execution_timeout_secs: 10800
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
diff --git a/infra/config/generated/luci/luci-milo.cfg b/infra/config/generated/luci/luci-milo.cfg
index 43383ac..238366a 100644
--- a/infra/config/generated/luci/luci-milo.cfg
+++ b/infra/config/generated/luci/luci-milo.cfg
@@ -141,6 +141,11 @@
     short_name: "w10"
   }
   builders {
+    name: "buildbucket/luci.chromium.ci/Win11 Tests x64"
+    category: "chromium.win|release|tester"
+    short_name: "w11"
+  }
+  builders {
     name: "buildbucket/luci.chromium.ci/Win x64 Builder (dbg)"
     category: "chromium.win|debug|builder"
     short_name: "64"
@@ -7851,10 +7856,6 @@
     category: "win10"
   }
   builders {
-    name: "buildbucket/luci.chromium.ci/Win11 Tests x64"
-    category: "win11"
-  }
-  builders {
     name: "buildbucket/luci.chromium.ci/win32-arm64-rel"
     category: "win32|arm64"
   }
@@ -14098,6 +14099,11 @@
     short_name: "w10"
   }
   builders {
+    name: "buildbucket/luci.chromium.ci/Win11 Tests x64"
+    category: "release|tester"
+    short_name: "w11"
+  }
+  builders {
     name: "buildbucket/luci.chromium.ci/Win x64 Builder (dbg)"
     category: "debug|builder"
     short_name: "64"
diff --git a/infra/config/generated/sheriff-rotations/chromium.txt b/infra/config/generated/sheriff-rotations/chromium.txt
index b7117b0..bcffceae 100644
--- a/infra/config/generated/sheriff-rotations/chromium.txt
+++ b/infra/config/generated/sheriff-rotations/chromium.txt
@@ -45,6 +45,7 @@
 ci/Win x64 Builder
 ci/Win x64 Builder (dbg)
 ci/Win10 Tests x64
+ci/Win11 Tests x64
 ci/Win7 (32) Tests
 ci/Win7 Tests (1)
 ci/Windows deterministic
diff --git a/infra/config/subprojects/chromium/ci/chromium.fyi.star b/infra/config/subprojects/chromium/ci/chromium.fyi.star
index ed62f69..dc1af2ff 100644
--- a/infra/config/subprojects/chromium/ci/chromium.fyi.star
+++ b/infra/config/subprojects/chromium/ci/chromium.fyi.star
@@ -1605,39 +1605,6 @@
 )
 
 ci.builder(
-    name = "Win11 Tests x64",
-    builder_spec = builder_config.builder_spec(
-        execution_mode = builder_config.execution_mode.TEST,
-        gclient_config = builder_config.gclient_config(
-            config = "chromium",
-            apply_configs = [
-                "use_clang_coverage",
-            ],
-        ),
-        chromium_config = builder_config.chromium_config(
-            config = "chromium",
-            apply_configs = [
-                "mb",
-            ],
-            build_config = builder_config.build_config.RELEASE,
-            target_bits = 64,
-            target_platform = builder_config.target_platform.WIN,
-        ),
-        build_gs_bucket = "chromium-fyi-archive",
-    ),
-    builderless = True,
-    console_view_entry = consoles.console_view_entry(
-        category = "win11",
-    ),
-    goma_backend = None,
-    main_console_view = None,
-    # TODO(gbeaty) Investigate if this needs to run on windows, if not switch to
-    # ci.thin_tester
-    os = os.WINDOWS_10,
-    triggered_by = ["ci/Win x64 Builder"],
-)
-
-ci.builder(
     name = "win32-arm64-rel",
     console_view_entry = consoles.console_view_entry(
         category = "win32|arm64",
diff --git a/infra/config/subprojects/chromium/ci/chromium.win.star b/infra/config/subprojects/chromium/ci/chromium.win.star
index 8d2027c..21e64356 100644
--- a/infra/config/subprojects/chromium/ci/chromium.win.star
+++ b/infra/config/subprojects/chromium/ci/chromium.win.star
@@ -320,6 +320,35 @@
     triggered_by = ["ci/Win x64 Builder"],
 )
 
+ci.thin_tester(
+    name = "Win11 Tests x64",
+    builder_spec = builder_config.builder_spec(
+        execution_mode = builder_config.execution_mode.TEST,
+        gclient_config = builder_config.gclient_config(
+            config = "chromium",
+            apply_configs = [
+                "use_clang_coverage",
+            ],
+        ),
+        chromium_config = builder_config.chromium_config(
+            config = "chromium",
+            apply_configs = [
+                "mb",
+            ],
+            build_config = builder_config.build_config.RELEASE,
+            target_bits = 64,
+            target_platform = builder_config.target_platform.WIN,
+        ),
+        build_gs_bucket = "chromium-win-archive",
+    ),
+    console_view_entry = consoles.console_view_entry(
+        category = "release|tester",
+        short_name = "w11",
+    ),
+    triggered_by = ["ci/Win x64 Builder"],
+    tree_closing = False,
+)
+
 ci.builder(
     name = "Windows deterministic",
     console_view_entry = consoles.console_view_entry(
diff --git a/infra/config/subprojects/reclient/reclient.star b/infra/config/subprojects/reclient/reclient.star
index ad898d6..6d678b5 100644
--- a/infra/config/subprojects/reclient/reclient.star
+++ b/infra/config/subprojects/reclient/reclient.star
@@ -74,7 +74,7 @@
     return fyi_reclient_staging_builder(
         name = name,
         console_view_category = console_view_category,
-        reclient_instance = "goma-foundry-experiments",
+        reclient_instance = "rbe-chromium-trusted-test",
         **kwargs
     )
 
diff --git a/ios/chrome/browser/ui/follow/first_follow_view_controller.mm b/ios/chrome/browser/ui/follow/first_follow_view_controller.mm
index 1b914a42..c0aa8d8 100644
--- a/ios/chrome/browser/ui/follow/first_follow_view_controller.mm
+++ b/ios/chrome/browser/ui/follow/first_follow_view_controller.mm
@@ -23,7 +23,7 @@
 namespace {
 
 constexpr CGFloat customSpacingBeforeImageIfNoToolbar = 24;
-constexpr CGFloat customSpacingAfterImage = 1;
+constexpr CGFloat customSpacingAfterImage = 24;
 
 }  // namespace
 
@@ -72,4 +72,18 @@
   [super viewDidLoad];
 }
 
+#pragma mark - ConfirmationAlertViewController
+
+- (void)updateStylingForSecondaryTitleLabel:(UILabel*)secondaryTitleLabel {
+  secondaryTitleLabel.font =
+      [UIFont preferredFontForTextStyle:UIFontTextStyleBody];
+  secondaryTitleLabel.textColor = [UIColor colorNamed:kTextSecondaryColor];
+}
+
+- (void)updateStylingForSubtitleLabel:(UILabel*)subtitleLabel {
+  subtitleLabel.font =
+      [UIFont preferredFontForTextStyle:UIFontTextStyleFootnote];
+  subtitleLabel.textColor = [UIColor colorNamed:kTextTertiaryColor];
+}
+
 @end
diff --git a/ios/chrome/browser/web/https_only_mode_egtest.mm b/ios/chrome/browser/web/https_only_mode_egtest.mm
index 795b2c6..d2773280 100644
--- a/ios/chrome/browser/web/https_only_mode_egtest.mm
+++ b/ios/chrome/browser/web/https_only_mode_egtest.mm
@@ -497,11 +497,9 @@
   // elements. Not currently supported by MetricsAppInterface.
 }
 
-// Navigate to an HTTP URL and allowlist the URL. Then clear browsing data via
-// UI settings. This should clear the HTTP allowlist.
-// Disable the test due to try bot failure.
-// TODO (crbug.com/1328537): please re-enable it after fix.
-- (void)DISABLED_testUpgrade_RemoveBrowsingData_ShouldClearAllowlist {
+// Navigate to an HTTP URL and allowlist the URL. Then clear browsing data.
+// This should clear the HTTP allowlist.
+- (void)testUpgrade_RemoveBrowsingData_ShouldClearAllowlist {
   [HttpsOnlyModeAppInterface setHTTPPortForTesting:self.testServer->port()];
   [HttpsOnlyModeAppInterface
       setHTTPSPortForTesting:self.badHTTPSServer->port()];
@@ -524,15 +522,16 @@
   [ChromeEarlGrey waitForWebStateContainingText:"HTTP_RESPONSE"];
   [self assertFailedUpgrade:1];
 
-  // Clear the allowlist by clearing the browsing data.
-  [ChromeEarlGreyUI clearAllBrowsingData];
+  // Clear the allowlist by clearing the browsing data. This clears the history
+  // programmatically, so it won't automatically reload the tabs.
+  [ChromeEarlGrey clearBrowsingHistory];
 
-  // Clearing the browsing data automatically reloads tabs. Check that the
-  // interstitial is showing.
+  // Reloading the should show the interstitial again.
+  [ChromeEarlGrey reload];
   [ChromeEarlGrey waitForWebStateContainingText:kInterstitialText];
   [self assertFailedUpgrade:2];
 
-  // Reloading the should show the interstitial again.
+  // Reload once more.
   [ChromeEarlGrey reload];
   [ChromeEarlGrey waitForWebStateContainingText:kInterstitialText];
   [self assertFailedUpgrade:3];
diff --git a/ios/chrome/common/ui/confirmation_alert/confirmation_alert_view_controller.h b/ios/chrome/common/ui/confirmation_alert/confirmation_alert_view_controller.h
index 14fd3e4c..9d5545d 100644
--- a/ios/chrome/common/ui/confirmation_alert/confirmation_alert_view_controller.h
+++ b/ios/chrome/common/ui/confirmation_alert/confirmation_alert_view_controller.h
@@ -92,6 +92,14 @@
 // The action handler for interactions in this View Controller.
 @property(nonatomic, weak) id<ConfirmationAlertActionHandler> actionHandler;
 
+// Updates the style of the secondary title label. The default implementation
+// does nothing, but subclasses can override to customize the styling if needed.
+- (void)updateStylingForSecondaryTitleLabel:(UILabel*)secondaryTitleLabel;
+
+// Updates the style of the subtitle label. The default implementation does
+// nothing, but subclasses can override to customize the styling if needed.
+- (void)updateStylingForSubtitleLabel:(UILabel*)subtitleLabel;
+
 @end
 
 #endif  // IOS_CHROME_COMMON_UI_CONFIRMATION_ALERT_CONFIRMATION_ALERT_VIEW_CONTROLLER_H_
diff --git a/ios/chrome/common/ui/confirmation_alert/confirmation_alert_view_controller.mm b/ios/chrome/common/ui/confirmation_alert/confirmation_alert_view_controller.mm
index 285772b7..8176a70 100644
--- a/ios/chrome/common/ui/confirmation_alert/confirmation_alert_view_controller.mm
+++ b/ios/chrome/common/ui/confirmation_alert/confirmation_alert_view_controller.mm
@@ -355,6 +355,16 @@
   [super updateViewConstraints];
 }
 
+- (void)updateStylingForSecondaryTitleLabel:(UILabel*)secondaryTitleLabel {
+  // The subclass needs to overwrite this method if it wants a different style
+  // than the default.
+}
+
+- (void)updateStylingForSubtitleLabel:(UILabel*)subtitleLabel {
+  // The subclass need to overwrite this method if it wants a different style
+  // than the default.
+}
+
 #pragma mark - UIToolbarDelegate
 
 - (UIBarPosition)positionForBar:(id<UIBarPositioning>)bar {
@@ -576,6 +586,7 @@
   secondaryTitle.textColor = [UIColor colorNamed:kTextPrimaryColor];
   secondaryTitle.accessibilityIdentifier =
       kConfirmationAlertSecondaryTitleAccessibilityIdentifier;
+  [self updateStylingForSecondaryTitleLabel:secondaryTitle];
   return secondaryTitle;
 }
 
@@ -587,6 +598,7 @@
   subtitle.textColor = [UIColor colorNamed:kTextSecondaryColor];
   subtitle.accessibilityIdentifier =
       kConfirmationAlertSubtitleAccessibilityIdentifier;
+  [self updateStylingForSubtitleLabel:subtitle];
   return subtitle;
 }
 
@@ -616,7 +628,7 @@
   UIStackView* stackView =
       [[UIStackView alloc] initWithArrangedSubviews:subviews];
   [stackView setCustomSpacing:self.customSpacingAfterImage
-                    afterView:self.imageView];
+                    afterView:self.imageContainerView];
 
   if (self.imageHasFixedSize) {
     stackView.alignment = UIStackViewAlignmentCenter;
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1
index 37301303..0d8322f7 100644
--- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-49dea002efe50d5b2afa7801722f528fae4690cd
\ No newline at end of file
+3ffa9f20af779101683df99836b761fd89f43c55
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1
index 6bcf3fa9..dd83092 100644
--- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-c464b40893e172acc8f4b1bdabe3b6fb97837b10
\ No newline at end of file
+52e880781f1879e60a722e5997b9635d08101e49
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1
index 68e388c..d8debfb 100644
--- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-9c3d82a1f8a9777182f787071c8b27532608b656
\ No newline at end of file
+a9a93893e61e91c2e90107b868a2c04943c809ea
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1
index 8f4b2696..6104c66 100644
--- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-5c2addb7d3571a84098ee850138227b6868242c2
\ No newline at end of file
+8f69268988df5bef7f9eb4df06f7a3cdfa45a448
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.ios.zip.sha1
index ac11efc1..4cd11bfa 100644
--- a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-bd8c0cf3119062a12e0beef8b9dedff1d379cf1a
\ No newline at end of file
+7c60bf5755b7e85edf6da605865a8490d46217e4
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.iossimulator.zip.sha1
index cfa68ef..247778b2 100644
--- a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-d05f25eb80a9d6d458b3ef2170b23eb66ad46355
\ No newline at end of file
+e6f2bc03dbdaad5c65ee945205af004deebd29d6
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1
index 7661a887..6ffd005 100644
--- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-e245fa17945ff53c9d1bfdf7043c2e67de976cae
\ No newline at end of file
+ae0af028a3e73e4b57db305881e440a118c8bf07
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1
index c318f95..170ff3e 100644
--- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-ea905e765ee27aec5170b53c7dc86df4b85d00b9
\ No newline at end of file
+70c9175177555752adc2fcc38431c03d8bf01321
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1
index b84828d..0be0221 100644
--- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-b3bb22c0370139194dc85c8e5a6a42792dbfe868
\ No newline at end of file
+717878862ded05ee8cc0bbd164b1d80a4a48a087
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1
index c6812589..12ebad2 100644
--- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-fa67459c82ae15f891186788655fefca6936243f
\ No newline at end of file
+0d8869de893986a8dc56028232fc25bc834fa320
\ No newline at end of file
diff --git a/ios/web/find_in_page/find_in_page_manger_impl_unittest.mm b/ios/web/find_in_page/find_in_page_manger_impl_unittest.mm
index f393d1e6..dc75afc1 100644
--- a/ios/web/find_in_page/find_in_page_manger_impl_unittest.mm
+++ b/ios/web/find_in_page/find_in_page_manger_impl_unittest.mm
@@ -113,11 +113,11 @@
     return fake_delegate_.state();
   }));
   ASSERT_EQ(2ul, frame_with_one_match_ptr->GetJavaScriptCallHistory().size());
-  EXPECT_EQ("__gCrWeb.findInPage.selectAndScrollToVisibleMatch(0);",
+  EXPECT_EQ(u"__gCrWeb.findInPage.selectAndScrollToVisibleMatch(0);",
             frame_with_one_match_ptr->GetJavaScriptCallHistory()[1]);
-  EXPECT_EQ("__gCrWeb.findInPage.findString(\"foo\", 100.0);",
+  EXPECT_EQ(u"__gCrWeb.findInPage.findString(\"foo\", 100.0);",
             frame_with_one_match_ptr->GetJavaScriptCallHistory()[0]);
-  EXPECT_EQ("__gCrWeb.findInPage.findString(\"foo\", 100.0);",
+  EXPECT_EQ(u"__gCrWeb.findInPage.findString(\"foo\", 100.0);",
             frame_with_two_matches_ptr->GetLastJavaScriptCall());
   EXPECT_EQ(3, fake_delegate_.state()->match_count);
 }
@@ -142,12 +142,12 @@
     base::RunLoop().RunUntilIdle();
     return fake_delegate_.state();
   }));
-  EXPECT_EQ("__gCrWeb.findInPage.findString(\"foo\", 100.0);",
+  EXPECT_EQ(u"__gCrWeb.findInPage.findString(\"foo\", 100.0);",
             frame_with_null_result_ptr->GetLastJavaScriptCall());
   ASSERT_EQ(2ul, frame_with_one_match_ptr->GetJavaScriptCallHistory().size());
-  EXPECT_EQ("__gCrWeb.findInPage.selectAndScrollToVisibleMatch(0);",
+  EXPECT_EQ(u"__gCrWeb.findInPage.selectAndScrollToVisibleMatch(0);",
             frame_with_one_match_ptr->GetJavaScriptCallHistory()[1]);
-  EXPECT_EQ("__gCrWeb.findInPage.findString(\"foo\", 100.0);",
+  EXPECT_EQ(u"__gCrWeb.findInPage.findString(\"foo\", 100.0);",
             frame_with_one_match_ptr->GetJavaScriptCallHistory()[0]);
   EXPECT_EQ(1, fake_delegate_.state()->match_count);
 }
@@ -179,11 +179,11 @@
     return fake_delegate_.state();
   }));
   ASSERT_EQ(3ul, frame_with_two_matches_ptr->GetJavaScriptCallHistory().size());
-  EXPECT_EQ("__gCrWeb.findInPage.selectAndScrollToVisibleMatch(0);",
+  EXPECT_EQ(u"__gCrWeb.findInPage.selectAndScrollToVisibleMatch(0);",
             frame_with_two_matches_ptr->GetJavaScriptCallHistory()[2]);
-  EXPECT_EQ("__gCrWeb.findInPage.findString(\"foo\", 100.0);",
+  EXPECT_EQ(u"__gCrWeb.findInPage.findString(\"foo\", 100.0);",
             frame_with_two_matches_ptr->GetJavaScriptCallHistory()[1]);
-  EXPECT_EQ("__gCrWeb.findInPage.findString(\"foo\", 100.0);",
+  EXPECT_EQ(u"__gCrWeb.findInPage.findString(\"foo\", 100.0);",
             frame_with_two_matches_ptr->GetJavaScriptCallHistory()[0]);
   EXPECT_EQ(2, fake_delegate_.state()->match_count);
 }
@@ -228,9 +228,9 @@
   }));
 
   ASSERT_EQ(2ul, frame_with_one_match_ptr->GetJavaScriptCallHistory().size());
-  EXPECT_EQ("__gCrWeb.findInPage.selectAndScrollToVisibleMatch(0);",
+  EXPECT_EQ(u"__gCrWeb.findInPage.selectAndScrollToVisibleMatch(0);",
             frame_with_one_match_ptr->GetJavaScriptCallHistory()[1]);
-  EXPECT_EQ("__gCrWeb.findInPage.findString(\"foo\", 100.0);",
+  EXPECT_EQ(u"__gCrWeb.findInPage.findString(\"foo\", 100.0);",
             frame_with_one_match_ptr->GetJavaScriptCallHistory()[0]);
   EXPECT_EQ(1, fake_delegate_.state()->match_count);
 }
@@ -259,13 +259,13 @@
     return fake_delegate_.state();
   }));
   ASSERT_EQ(3ul, frame_with_two_matches_ptr->GetJavaScriptCallHistory().size());
-  EXPECT_EQ("__gCrWeb.findInPage.selectAndScrollToVisibleMatch(0);",
+  EXPECT_EQ(u"__gCrWeb.findInPage.selectAndScrollToVisibleMatch(0);",
             frame_with_two_matches_ptr->GetJavaScriptCallHistory()[2]);
-  EXPECT_EQ("__gCrWeb.findInPage.pumpSearch(100.0);",
+  EXPECT_EQ(u"__gCrWeb.findInPage.pumpSearch(100.0);",
             frame_with_two_matches_ptr->GetJavaScriptCallHistory()[1]);
-  EXPECT_EQ("__gCrWeb.findInPage.findString(\"foo\", 100.0);",
+  EXPECT_EQ(u"__gCrWeb.findInPage.findString(\"foo\", 100.0);",
             frame_with_two_matches_ptr->GetJavaScriptCallHistory()[0]);
-  EXPECT_EQ("__gCrWeb.findInPage.findString(\"foo\", 100.0);",
+  EXPECT_EQ(u"__gCrWeb.findInPage.findString(\"foo\", 100.0);",
             frame_with_one_match_ptr->GetLastJavaScriptCall());
   EXPECT_EQ(3, fake_delegate_.state()->match_count);
 }
@@ -280,7 +280,7 @@
 
   GetFindInPageManager()->Find(@"foo", FindInPageOptions::FindInPageSearch);
 
-  EXPECT_EQ("__gCrWeb.findInPage.findString(\"foo\", 100.0);",
+  EXPECT_EQ(u"__gCrWeb.findInPage.findString(\"foo\", 100.0);",
             frame_with_one_match_ptr->GetLastJavaScriptCall());
   base::RunLoop().RunUntilIdle();
 }
@@ -332,7 +332,7 @@
   }));
   ASSERT_EQ(1ul,
             frame_with_zero_matches_ptr->GetJavaScriptCallHistory().size());
-  EXPECT_EQ("__gCrWeb.findInPage.findString(\"foo\", 100.0);",
+  EXPECT_EQ(u"__gCrWeb.findInPage.findString(\"foo\", 100.0);",
             frame_with_zero_matches_ptr->GetJavaScriptCallHistory()[0]);
   EXPECT_EQ(0, fake_delegate_.state()->match_count);
   EXPECT_EQ(-1, fake_delegate_.state()->index);
@@ -354,9 +354,9 @@
     return fake_delegate_.state();
   }));
   ASSERT_EQ(2ul, frame_with_two_matches_ptr->GetJavaScriptCallHistory().size());
-  EXPECT_EQ("__gCrWeb.findInPage.selectAndScrollToVisibleMatch(0);",
+  EXPECT_EQ(u"__gCrWeb.findInPage.selectAndScrollToVisibleMatch(0);",
             frame_with_two_matches_ptr->GetJavaScriptCallHistory()[1]);
-  EXPECT_EQ("__gCrWeb.findInPage.findString(\"foo\", 100.0);",
+  EXPECT_EQ(u"__gCrWeb.findInPage.findString(\"foo\", 100.0);",
             frame_with_two_matches_ptr->GetJavaScriptCallHistory()[0]);
   EXPECT_EQ(0, fake_delegate_.state()->index);
 }
@@ -377,9 +377,9 @@
     return fake_delegate_.state();
   }));
   ASSERT_EQ(2ul, frame_with_two_matches_ptr->GetJavaScriptCallHistory().size());
-  EXPECT_EQ("__gCrWeb.findInPage.selectAndScrollToVisibleMatch(0);",
+  EXPECT_EQ(u"__gCrWeb.findInPage.selectAndScrollToVisibleMatch(0);",
             frame_with_two_matches_ptr->GetJavaScriptCallHistory()[1]);
-  EXPECT_EQ("__gCrWeb.findInPage.findString(\"foo\", 100.0);",
+  EXPECT_EQ(u"__gCrWeb.findInPage.findString(\"foo\", 100.0);",
             frame_with_two_matches_ptr->GetJavaScriptCallHistory()[0]);
 
   GetFindInPageManager()->Find(@"foo", FindInPageOptions::FindInPageNext);
@@ -388,7 +388,7 @@
     base::RunLoop().RunUntilIdle();
     return fake_delegate_.state()->index > -1;
   }));
-  EXPECT_EQ("__gCrWeb.findInPage.selectAndScrollToVisibleMatch(1);",
+  EXPECT_EQ(u"__gCrWeb.findInPage.selectAndScrollToVisibleMatch(1);",
             frame_with_two_matches_ptr->GetLastJavaScriptCall());
   EXPECT_EQ(1, fake_delegate_.state()->index);
 }
@@ -414,7 +414,7 @@
     return fake_delegate_.state();
   }));
   EXPECT_EQ(0, fake_delegate_.state()->index);
-  EXPECT_EQ("__gCrWeb.findInPage.selectAndScrollToVisibleMatch(0);",
+  EXPECT_EQ(u"__gCrWeb.findInPage.selectAndScrollToVisibleMatch(0);",
             frame_with_one_match_ptr->GetLastJavaScriptCall());
 
   GetFindInPageManager()->Find(@"foo", FindInPageOptions::FindInPageNext);
@@ -423,7 +423,7 @@
     return fake_delegate_.state();
   }));
   EXPECT_EQ(1, fake_delegate_.state()->index);
-  EXPECT_EQ("__gCrWeb.findInPage.selectAndScrollToVisibleMatch(0);",
+  EXPECT_EQ(u"__gCrWeb.findInPage.selectAndScrollToVisibleMatch(0);",
             frame_with_two_matches_ptr->GetLastJavaScriptCall());
 
   GetFindInPageManager()->Find(@"foo", FindInPageOptions::FindInPageNext);
@@ -432,7 +432,7 @@
     return fake_delegate_.state();
   }));
   EXPECT_EQ(2, fake_delegate_.state()->index);
-  EXPECT_EQ("__gCrWeb.findInPage.selectAndScrollToVisibleMatch(1);",
+  EXPECT_EQ(u"__gCrWeb.findInPage.selectAndScrollToVisibleMatch(1);",
             frame_with_two_matches_ptr->GetLastJavaScriptCall());
 
   GetFindInPageManager()->Find(@"foo", FindInPageOptions::FindInPageNext);
@@ -441,7 +441,7 @@
     return fake_delegate_.state();
   }));
   EXPECT_EQ(0, fake_delegate_.state()->index);
-  EXPECT_EQ("__gCrWeb.findInPage.selectAndScrollToVisibleMatch(0);",
+  EXPECT_EQ(u"__gCrWeb.findInPage.selectAndScrollToVisibleMatch(0);",
             frame_with_one_match_ptr->GetLastJavaScriptCall());
 }
 
@@ -467,7 +467,7 @@
     return fake_delegate_.state();
   }));
   EXPECT_EQ(0, fake_delegate_.state()->index);
-  EXPECT_EQ("__gCrWeb.findInPage.selectAndScrollToVisibleMatch(0);",
+  EXPECT_EQ(u"__gCrWeb.findInPage.selectAndScrollToVisibleMatch(0);",
             frame_with_one_match_ptr->GetLastJavaScriptCall());
 
   GetFindInPageManager()->Find(@"foo", FindInPageOptions::FindInPagePrevious);
@@ -477,7 +477,7 @@
     return fake_delegate_.state();
   }));
   EXPECT_EQ(2, fake_delegate_.state()->index);
-  EXPECT_EQ("__gCrWeb.findInPage.selectAndScrollToVisibleMatch(1);",
+  EXPECT_EQ(u"__gCrWeb.findInPage.selectAndScrollToVisibleMatch(1);",
             frame_with_two_matches_ptr->GetLastJavaScriptCall());
 
   GetFindInPageManager()->Find(@"foo", FindInPageOptions::FindInPagePrevious);
@@ -487,7 +487,7 @@
     return fake_delegate_.state();
   }));
   EXPECT_EQ(1, fake_delegate_.state()->index);
-  EXPECT_EQ("__gCrWeb.findInPage.selectAndScrollToVisibleMatch(0);",
+  EXPECT_EQ(u"__gCrWeb.findInPage.selectAndScrollToVisibleMatch(0);",
             frame_with_two_matches_ptr->GetLastJavaScriptCall());
 
   GetFindInPageManager()->Find(@"foo", FindInPageOptions::FindInPagePrevious);
@@ -497,7 +497,7 @@
     return fake_delegate_.state();
   }));
   EXPECT_EQ(0, fake_delegate_.state()->index);
-  EXPECT_EQ("__gCrWeb.findInPage.selectAndScrollToVisibleMatch(0);",
+  EXPECT_EQ(u"__gCrWeb.findInPage.selectAndScrollToVisibleMatch(0);",
             frame_with_one_match_ptr->GetLastJavaScriptCall());
 }
 
@@ -521,12 +521,12 @@
     base::RunLoop().RunUntilIdle();
     return fake_delegate_.state();
   }));
-  EXPECT_EQ("__gCrWeb.findInPage.findString(\"foo\", 100.0);",
+  EXPECT_EQ(u"__gCrWeb.findInPage.findString(\"foo\", 100.0);",
             frame_with_two_matches_ptr->GetLastJavaScriptCall());
   ASSERT_EQ(2ul, frame_with_one_match_ptr->GetJavaScriptCallHistory().size());
-  EXPECT_EQ("__gCrWeb.findInPage.selectAndScrollToVisibleMatch(0);",
+  EXPECT_EQ(u"__gCrWeb.findInPage.selectAndScrollToVisibleMatch(0);",
             frame_with_one_match_ptr->GetJavaScriptCallHistory()[1]);
-  EXPECT_EQ("__gCrWeb.findInPage.findString(\"foo\", 100.0);",
+  EXPECT_EQ(u"__gCrWeb.findInPage.findString(\"foo\", 100.0);",
             frame_with_one_match_ptr->GetJavaScriptCallHistory()[0]);
 
   GetFindInPageManager()->Find(@"foo", FindInPageOptions::FindInPagePrevious);
@@ -535,7 +535,7 @@
     base::RunLoop().RunUntilIdle();
     return fake_delegate_.state()->index == 2;
   }));
-  EXPECT_EQ("__gCrWeb.findInPage.selectAndScrollToVisibleMatch(1);",
+  EXPECT_EQ(u"__gCrWeb.findInPage.selectAndScrollToVisibleMatch(1);",
             frame_with_two_matches_ptr->GetLastJavaScriptCall());
 }
 
@@ -580,7 +580,7 @@
     return fake_delegate_.state();
   }));
   ASSERT_EQ(2ul, frame_with_one_match_ptr->GetJavaScriptCallHistory().size());
-  EXPECT_EQ("__gCrWeb.findInPage.selectAndScrollToVisibleMatch(0);",
+  EXPECT_EQ(u"__gCrWeb.findInPage.selectAndScrollToVisibleMatch(0);",
             frame_with_one_match_ptr->GetJavaScriptCallHistory()[1]);
 
   RemoveWebFrame(kMainFakeFrameId);
@@ -590,7 +590,7 @@
     base::RunLoop().RunUntilIdle();
     return fake_delegate_.state()->index == 0;
   }));
-  EXPECT_EQ("__gCrWeb.findInPage.selectAndScrollToVisibleMatch(0);",
+  EXPECT_EQ(u"__gCrWeb.findInPage.selectAndScrollToVisibleMatch(0);",
             frame_with_two_matches_ptr->GetLastJavaScriptCall());
 }
 
diff --git a/ios/web/public/test/fakes/fake_web_frame.h b/ios/web/public/test/fakes/fake_web_frame.h
index dcc2ace..afd7f82 100644
--- a/ios/web/public/test/fakes/fake_web_frame.h
+++ b/ios/web/public/test/fakes/fake_web_frame.h
@@ -38,11 +38,14 @@
   static std::unique_ptr<FakeWebFrame> CreateChildWebFrame(
       GURL security_origin);
 
-  // Returns the most recent JavaScript handler call made to this frame.
-  virtual std::string GetLastJavaScriptCall() const = 0;
+  // Returns the most recent JavaScript call made to this frame.
+  virtual std::u16string GetLastJavaScriptCall() const = 0;
 
   // Returns |javascript_calls|. Use LastJavaScriptCall() if possible.
-  virtual const std::vector<std::string>& GetJavaScriptCallHistory() = 0;
+  virtual const std::vector<std::u16string>& GetJavaScriptCallHistory() = 0;
+
+  // Clears the history of javascript calls sent to this frame.
+  virtual void ClearJavaScriptCallHistory() = 0;
 
   // Sets the browser state associated with this frame.
   virtual void set_browser_state(BrowserState* browser_state) = 0;
diff --git a/ios/web/test/fakes/fake_web_frame_impl.cc b/ios/web/test/fakes/fake_web_frame_impl.cc
index c0787cdf..566c01ca 100644
--- a/ios/web/test/fakes/fake_web_frame_impl.cc
+++ b/ios/web/test/fakes/fake_web_frame_impl.cc
@@ -10,6 +10,7 @@
 #include "base/bind.h"
 #include "base/callback.h"
 #include "base/json/json_writer.h"
+#include "base/strings/utf_string_conversions.h"
 #include "base/values.h"
 #include "ios/web/public/thread/web_task_traits.h"
 #include "ios/web/public/thread/web_thread.h"
@@ -90,18 +91,19 @@
     call_java_script_function_callback_.Run();
   }
 
-  std::string javascript_call = std::string("__gCrWeb." + name + "(");
+  std::u16string javascript_call =
+      std::u16string(u"__gCrWeb." + base::UTF8ToUTF16(name) + u"(");
   bool first = true;
   for (auto& param : parameters) {
     if (!first) {
-      javascript_call += ", ";
+      javascript_call += u", ";
     }
     first = false;
     std::string paramString;
     base::JSONWriter::Write(param, &paramString);
-    javascript_call += paramString;
+    javascript_call += base::UTF8ToUTF16(paramString);
   }
-  javascript_call += ");";
+  javascript_call += u");";
   java_script_calls_.push_back(javascript_call);
   return can_call_function_;
 }
@@ -145,18 +147,21 @@
 }
 
 bool FakeWebFrameImpl::ExecuteJavaScript(const std::u16string& script) {
+  java_script_calls_.push_back(script);
   return false;
 }
 
 bool FakeWebFrameImpl::ExecuteJavaScript(
     const std::u16string& script,
     base::OnceCallback<void(const base::Value*)> callback) {
+  java_script_calls_.push_back(script);
   return false;
 }
 
 bool FakeWebFrameImpl::ExecuteJavaScript(
     const std::u16string& script,
     base::OnceCallback<void(const base::Value*, bool)> callback) {
+  java_script_calls_.push_back(script);
   return false;
 }
 
@@ -170,14 +175,19 @@
   return last_received_content_world_;
 }
 
-std::string FakeWebFrameImpl::GetLastJavaScriptCall() const {
-  return java_script_calls_.size() == 0 ? "" : java_script_calls_.back();
+std::u16string FakeWebFrameImpl::GetLastJavaScriptCall() const {
+  return java_script_calls_.size() == 0 ? u"" : java_script_calls_.back();
 }
 
-const std::vector<std::string>& FakeWebFrameImpl::GetJavaScriptCallHistory() {
+const std::vector<std::u16string>&
+FakeWebFrameImpl::GetJavaScriptCallHistory() {
   return java_script_calls_;
 }
 
+void FakeWebFrameImpl::ClearJavaScriptCallHistory() {
+  java_script_calls_.clear();
+}
+
 void FakeWebFrameImpl::set_browser_state(BrowserState* browser_state) {
   browser_state_ = browser_state;
 }
diff --git a/ios/web/test/fakes/fake_web_frame_impl.h b/ios/web/test/fakes/fake_web_frame_impl.h
index f21d3de3..556a98b 100644
--- a/ios/web/test/fakes/fake_web_frame_impl.h
+++ b/ios/web/test/fakes/fake_web_frame_impl.h
@@ -50,8 +50,9 @@
       base::OnceCallback<void(const base::Value*, bool)> callback) override;
 
   // FakeWebFrame:
-  std::string GetLastJavaScriptCall() const override;
-  const std::vector<std::string>& GetJavaScriptCallHistory() override;
+  std::u16string GetLastJavaScriptCall() const override;
+  const std::vector<std::u16string>& GetJavaScriptCallHistory() override;
+  void ClearJavaScriptCallHistory() override;
   void set_browser_state(BrowserState* browser_state) override;
   void AddJsResultForFunctionCall(base::Value* js_result,
                                   const std::string& function_name) override;
@@ -98,7 +99,7 @@
   GURL security_origin_;
   // Vector holding history of all javascript handler calls made in this frame.
   // The calls are sorted with the most recent appended at the end.
-  std::vector<std::string> java_script_calls_;
+  std::vector<std::u16string> java_script_calls_;
   // The return value of CanCallJavaScriptFunction().
   bool can_call_function_ = true;
   // When set to true, will force calls to CallJavaScriptFunction to fail with
diff --git a/media/audio/fuchsia/audio_output_stream_fuchsia.cc b/media/audio/fuchsia/audio_output_stream_fuchsia.cc
index d2f112e6..3ec94836 100644
--- a/media/audio/fuchsia/audio_output_stream_fuchsia.cc
+++ b/media/audio/fuchsia/audio_output_stream_fuchsia.cc
@@ -24,6 +24,8 @@
 
 fuchsia::media::AudioRenderUsage GetStreamUsage(
     const AudioParameters& parameters) {
+  // TODO(crbug.com/1253010) In WebEngine: use `audio_renderer_usage` from the
+  // `FrameMediaSettings` for the current web frame.
   if (parameters.latency_tag() == AudioLatency::LATENCY_RTC)
     return fuchsia::media::AudioRenderUsage::COMMUNICATION;
   return fuchsia::media::AudioRenderUsage::MEDIA;
diff --git a/media/base/decoder_status.cc b/media/base/decoder_status.cc
index 777aed6..caba2464 100644
--- a/media/base/decoder_status.cc
+++ b/media/base/decoder_status.cc
@@ -30,7 +30,6 @@
     STRINGIFY(DecoderStatus::Codes::kMalformedBitstream);
     STRINGIFY(DecoderStatus::Codes::kFailedToGetDecoderBuffer);
     STRINGIFY(DecoderStatus::Codes::kDecoderStreamInErrorState);
-    STRINGIFY(DecoderStatus::Codes::kDecoderStreamReinitFailed);
     STRINGIFY(DecoderStatus::Codes::kDecoderStreamDemuxerError);
     STRINGIFY(DecoderStatus::Codes::kUnsupportedProfile);
     STRINGIFY(DecoderStatus::Codes::kUnsupportedCodec);
diff --git a/media/base/decoder_status.h b/media/base/decoder_status.h
index bb7ec80..1142cb6 100644
--- a/media/base/decoder_status.h
+++ b/media/base/decoder_status.h
@@ -28,7 +28,6 @@
     kMalformedBitstream = 104,
     kFailedToGetDecoderBuffer = 107,
     kDecoderStreamInErrorState = 108,
-    kDecoderStreamReinitFailed = 109,
     kDecoderStreamDemuxerError = 110,
     kKeyFrameRequired = 111,
     kMissingTimestamp = 112,
diff --git a/media/base/status.h b/media/base/status.h
index 78ac9c5..345a31a7 100644
--- a/media/base/status.h
+++ b/media/base/status.h
@@ -538,7 +538,12 @@
 
     // Return constref of the value, if we have one.
     // Callers should ensure that this |has_value()|.
-    const OtherType& operator->() {
+    const OtherType& operator->() const {
+      CHECK(value_);
+      return std::get<0>(*value_);
+    }
+
+    const OtherType& operator*() const {
       CHECK(value_);
       return std::get<0>(*value_);
     }
diff --git a/media/filters/decoder_selector.cc b/media/filters/decoder_selector.cc
index d13c5264..23a28d21 100644
--- a/media/filters/decoder_selector.cc
+++ b/media/filters/decoder_selector.cc
@@ -152,7 +152,7 @@
   DVLOG(2) << __func__;
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   if (select_decoder_cb_)
-    ReturnNullDecoder();
+    ReturnSelectionError(DecoderStatus::Codes::kFailed);
 }
 
 template <DemuxerStream::Type StreamType>
@@ -174,9 +174,10 @@
 }
 
 template <DemuxerStream::Type StreamType>
-void DecoderSelector<StreamType>::SelectDecoder(
+void DecoderSelector<StreamType>::SelectDecoderInternal(
     SelectDecoderCB select_decoder_cb,
-    typename Decoder::OutputCB output_cb) {
+    typename Decoder::OutputCB output_cb,
+    bool needs_new_decoders) {
   DVLOG(2) << __func__;
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(select_decoder_cb);
@@ -191,19 +192,37 @@
 
   if (!config_.IsValidConfig()) {
     DLOG(ERROR) << "Invalid stream config";
-    ReturnNullDecoder();
+    ReturnSelectionError(DecoderStatus::Codes::kUnsupportedConfig);
     return;
   }
 
-  // If this is the first selection (ever or since FinalizeDecoderSelection()),
-  // start selection with the full list of potential decoders.
-  if (!is_selecting_decoders_) {
-    is_selecting_decoders_ = true;
+  if (needs_new_decoders) {
     decoder_selection_start_ = base::TimeTicks::Now();
+    decode_failure_reinit_cause_ = absl::nullopt;
     CreateDecoders();
   }
 
-  InitializeDecoder();
+  GetAndInitializeNextDecoder();
+}
+
+template <DemuxerStream::Type StreamType>
+void DecoderSelector<StreamType>::BeginDecoderSelection(
+    SelectDecoderCB select_decoder_cb,
+    typename Decoder::OutputCB output_cb) {
+  SelectDecoderInternal(std::move(select_decoder_cb), std::move(output_cb),
+                        /*needs_new_decoders = */ true);
+}
+
+template <DemuxerStream::Type StreamType>
+void DecoderSelector<StreamType>::ResumeDecoderSelection(
+    SelectDecoderCB select_decoder_cb,
+    typename Decoder::OutputCB output_cb,
+    DecoderStatus&& reinit_cause) {
+  DVLOG(2) << __func__;
+  if (!decode_failure_reinit_cause_.has_value())
+    decode_failure_reinit_cause_ = std::move(reinit_cause);
+  SelectDecoderInternal(std::move(select_decoder_cb), std::move(output_cb),
+                        /*needs_new_decoders = */ false);
 }
 
 template <DemuxerStream::Type StreamType>
@@ -211,7 +230,6 @@
   DVLOG(2) << __func__;
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(!select_decoder_cb_);
-  is_selecting_decoders_ = false;
 
   const std::string decoder_type = is_platform_decoder_ ? "HW" : "SW";
   const std::string stream_type =
@@ -263,9 +281,7 @@
   // Decoders inserted directly should be given priority over those returned by
   // |create_decoders_cb_|.
   decoders_.insert(decoders_.begin(), std::move(decoder));
-
-  if (is_selecting_decoders_)
-    FilterAndSortAvailableDecoders();
+  FilterAndSortAvailableDecoders();
 }
 
 template <DemuxerStream::Type StreamType>
@@ -287,7 +303,7 @@
 }
 
 template <DemuxerStream::Type StreamType>
-void DecoderSelector<StreamType>::InitializeDecoder() {
+void DecoderSelector<StreamType>::GetAndInitializeNextDecoder() {
   DVLOG(2) << __func__;
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(!decoder_);
@@ -300,7 +316,11 @@
       return;
     }
 
-    ReturnNullDecoder();
+    if (decode_failure_reinit_cause_.has_value()) {
+      ReturnSelectionError(std::move(*decode_failure_reinit_cause_));
+    } else {
+      ReturnSelectionError(DecoderStatus::Codes::kUnsupportedConfig);
+    }
     return;
   }
 
@@ -323,35 +343,35 @@
 template <DemuxerStream::Type StreamType>
 void DecoderSelector<StreamType>::OnDecoderInitializeDone(
     DecoderStatus status) {
+  DCHECK(decoder_);
   DVLOG(2) << __func__ << ": " << decoder_->GetDecoderType()
            << " success=" << static_cast<int>(status.code());
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
   if (!status.is_ok()) {
-    // TODO(tmathmeyer) this was too noisy in media log. Batch all the logs
-    // together and then send them as an informational notice instead of
-    // using NotifyError.
+    // Note: Don't track this decode status, as it is the result of
+    // initialization failure.
     MEDIA_LOG(INFO, media_log_)
         << "Failed to initialize " << decoder_->GetDecoderType();
 
     // Try the next decoder on the list.
-    decoder_.reset();
-    InitializeDecoder();
+    decoder_ = nullptr;
+    GetAndInitializeNextDecoder();
     return;
   }
 
-  RunSelectDecoderCB();
+  RunSelectDecoderCB(std::move(decoder_));
 }
 
 template <DemuxerStream::Type StreamType>
-void DecoderSelector<StreamType>::ReturnNullDecoder() {
+void DecoderSelector<StreamType>::ReturnSelectionError(DecoderStatus error) {
   DVLOG(1) << __func__ << ": No decoder selected";
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(!error.is_ok());
 
   decrypting_demuxer_stream_.reset();
-  decoder_.reset();
   decoders_.clear();
-  RunSelectDecoderCB();
+  RunSelectDecoderCB(std::move(error));
 }
 
 template <DemuxerStream::Type StreamType>
@@ -380,7 +400,8 @@
 
   if (status != PIPELINE_OK) {
     // Since we already tried every potential decoder without DDS, give up.
-    ReturnNullDecoder();
+    ReturnSelectionError(
+        {DecoderStatus::Codes::kUnsupportedEncryptionMode, std::move(status)});
     return;
   }
 
@@ -395,24 +416,26 @@
 
   // Try decoder selection again now that DDS is being used.
   CreateDecoders();
-  InitializeDecoder();
+  GetAndInitializeNextDecoder();
 }
 
 template <DemuxerStream::Type StreamType>
-void DecoderSelector<StreamType>::RunSelectDecoderCB() {
+void DecoderSelector<StreamType>::RunSelectDecoderCB(
+    DecoderOrError decoder_or_error) {
   DCHECK(select_decoder_cb_);
   TRACE_EVENT_ASYNC_END2(
       "media", kSelectDecoderTrace, this, "type",
       DemuxerStream::GetTypeName(StreamType), "decoder",
       base::StringPrintf(
           "%s (%s)",
-          decoder_ ? GetDecoderName(decoder_->GetDecoderType()).c_str()
-                   : "null",
+          decoder_or_error.has_value()
+              ? GetDecoderName(decoder_or_error->GetDecoderType()).c_str()
+              : "null",
           decrypting_demuxer_stream_ ? "encrypted" : "unencrypted"));
 
   task_runner_->PostTask(
       FROM_HERE,
-      base::BindOnce(std::move(select_decoder_cb_), std::move(decoder_),
+      base::BindOnce(std::move(select_decoder_cb_), std::move(decoder_or_error),
                      std::move(decrypting_demuxer_stream_)));
 }
 
diff --git a/media/filters/decoder_selector.h b/media/filters/decoder_selector.h
index 152785b..de60499 100644
--- a/media/filters/decoder_selector.h
+++ b/media/filters/decoder_selector.h
@@ -54,6 +54,7 @@
   typedef DecoderStreamTraits<StreamType> StreamTraits;
   typedef typename StreamTraits::DecoderType Decoder;
   typedef typename StreamTraits::DecoderConfigType DecoderConfig;
+  using DecoderOrError = DecoderStatus::Or<std::unique_ptr<Decoder>>;
 
   // Callback to create a list of decoders to select from.
   // TODO(xhwang): Use a DecoderFactory to create decoders one by one as needed,
@@ -76,7 +77,7 @@
   // The caller should call DecryptingDemuxerStream::Reset() before
   // calling Decoder::Reset() to release any pending decryption or read.
   using SelectDecoderCB =
-      base::OnceCallback<void(std::unique_ptr<Decoder>,
+      base::OnceCallback<void(DecoderOrError,
                               std::unique_ptr<DecryptingDemuxerStream>)>;
 
   DecoderSelector() = delete;
@@ -98,17 +99,27 @@
                   WaitingCB waiting_cb);
 
   // Selects and initializes a decoder, which will be returned via
-  // |select_decoder_cb| posted to |task_runner|. Subsequent calls to
-  // SelectDecoder() will return different decoder instances, until all
-  // potential decoders have been exhausted.
+  // |select_decoder_cb| posted to |task_runner|. In the event that a selected
+  // decoder fails to decode, |ResumeDecoderSelection| may be used to get
+  // another one.
   //
   // When the caller determines that decoder selection has succeeded (eg.
   // because the decoder decoded a frame successfully), it should call
   // FinalizeDecoderSelection().
   //
+  // |SelectDecoderCB| may be called with an error if no decoders are available.
+  //
   // Must not be called while another selection is pending.
-  void SelectDecoder(SelectDecoderCB select_decoder_cb,
-                     typename Decoder::OutputCB output_cb);
+  void BeginDecoderSelection(SelectDecoderCB select_decoder_cb,
+                             typename Decoder::OutputCB output_cb);
+
+  // When a client was provided with a decoder that fails to decode after
+  // being successfully initialized, it should request a new decoder via
+  // this method rather than |SelectDecoder|. This allows the pipeline to
+  // report the root cause of decoder failure.
+  void ResumeDecoderSelection(SelectDecoderCB select_decoder_cb,
+                              typename Decoder::OutputCB output_cb,
+                              DecoderStatus&& reinit_cause);
 
   // Signals that decoder selection has been completed (successfully). Future
   // calls to SelectDecoder() will select from the full list of decoders.
@@ -131,13 +142,16 @@
 
  private:
   void CreateDecoders();
-  void InitializeDecoder();
+  void GetAndInitializeNextDecoder();
   void OnDecoderInitializeDone(DecoderStatus status);
-  void ReturnNullDecoder();
+  void ReturnSelectionError(DecoderStatus error);
   void InitializeDecryptingDemuxerStream();
   void OnDecryptingDemuxerStreamInitializeDone(PipelineStatus status);
-  void RunSelectDecoderCB();
+  void RunSelectDecoderCB(DecoderOrError decoder_or_error);
   void FilterAndSortAvailableDecoders();
+  void SelectDecoderInternal(SelectDecoderCB select_decoder_cb,
+                             typename Decoder::OutputCB output_cb,
+                             bool needs_new_decoders);
 
   scoped_refptr<base::SequencedTaskRunner> task_runner_;
   SEQUENCE_CHECKER(sequence_checker_);
@@ -153,10 +167,9 @@
 
   // Overall decoder selection state.
   DecoderConfig config_;
-  bool is_selecting_decoders_ = false;
   std::vector<std::unique_ptr<Decoder>> decoders_;
 
-  // State for a single SelectDecoder() invocation.
+  // State for a single GetAndInitializeNextDecoder() invocation.
   SelectDecoderCB select_decoder_cb_;
   typename Decoder::OutputCB output_cb_;
   std::unique_ptr<Decoder> decoder_;
@@ -169,6 +182,11 @@
   base::TimeTicks decoder_selection_start_;
   base::TimeTicks codec_change_start_;
 
+  // Used to keep track of the original failure-to-decode reason so that if
+  // playback fails entirely, we have a root cause to point to, rather than
+  // failing due to running out of more acceptable decoders.
+  absl::optional<DecoderStatus> decode_failure_reinit_cause_ = absl::nullopt;
+
   base::WeakPtrFactory<DecoderSelector> weak_this_factory_{this};
 };
 
diff --git a/media/filters/decoder_selector_unittest.cc b/media/filters/decoder_selector_unittest.cc
index 4951e02a..4310170f 100644
--- a/media/filters/decoder_selector_unittest.cc
+++ b/media/filters/decoder_selector_unittest.cc
@@ -305,14 +305,16 @@
                  void(std::unique_ptr<DecryptingDemuxerStream>));
 
   void OnDecoderSelectedThunk(
-      std::unique_ptr<Decoder> decoder,
+      typename Selector::DecoderOrError decoder,
       std::unique_ptr<DecryptingDemuxerStream> decrypting_demuxer_stream) {
     // Report only the type or id of the decoder, since that's what the tests
     // care about. The decoder will be destructed immediately.
-    if (decoder && decoder->GetDecoderType() == DecoderType::kTesting) {
+    if (decoder.has_value() &&
+        decoder->GetDecoderType() == DecoderType::kTesting) {
       OnDecoderSelected(
-          static_cast<MockDecoder*>(decoder.get())->GetDecoderId());
-    } else if (decoder) {
+          static_cast<MockDecoder*>(std::move(decoder).value().get())
+              ->GetDecoderId());
+    } else if (decoder.has_value()) {
       OnDecoderSelected(decoder->GetDecoderType());
     } else {
       NoDecoderSelected();
@@ -429,13 +431,26 @@
     TypeParam::UseHighQualityEncryptedDecoderConfig(demuxer_stream_);
   }
 
-  void SelectDecoder() {
-    decoder_selector_->SelectDecoder(
-        base::BindOnce(&Self::OnDecoderSelectedThunk, base::Unretained(this)),
-        base::BindRepeating(&Self::OnOutput, base::Unretained(this)));
+  void SelectNextDecoder() {
+    if (is_selecting_) {
+      decoder_selector_->ResumeDecoderSelection(
+          base::BindOnce(&Self::OnDecoderSelectedThunk, base::Unretained(this)),
+          base::BindRepeating(&Self::OnOutput, base::Unretained(this)),
+          DecoderStatus::Codes::kFailed);
+    } else {
+      decoder_selector_->BeginDecoderSelection(
+          base::BindOnce(&Self::OnDecoderSelectedThunk, base::Unretained(this)),
+          base::BindRepeating(&Self::OnOutput, base::Unretained(this)));
+    }
+    is_selecting_ = true;
     RunUntilIdle();
   }
 
+  void FinalizeDecoderSelection() {
+    decoder_selector_->FinalizeDecoderSelection();
+    is_selecting_ = false;
+  }
+
   void RunUntilIdle() { task_environment_.RunUntilIdle(); }
 
   base::test::TaskEnvironment task_environment_;
@@ -449,6 +464,7 @@
   std::unique_ptr<Selector> decoder_selector_;
 
   bool use_decrypting_decoder_ = false;
+  bool is_selecting_ = false;
   std::vector<MockDecoderArgs> mock_decoders_to_create_;
 };
 
@@ -468,7 +484,7 @@
   this->CreateDecoderSelector();
 
   EXPECT_CALL(*this, NoDecoderSelected());
-  this->SelectDecoder();
+  this->SelectNextDecoder();
 }
 
 TYPED_TEST(DecoderSelectorTest, ClearStream_NoClearDecoder) {
@@ -477,7 +493,7 @@
   this->CreateDecoderSelector();
 
   EXPECT_CALL(*this, NoDecoderSelected());
-  this->SelectDecoder();
+  this->SelectNextDecoder();
 }
 
 TYPED_TEST(DecoderSelectorTest, ClearStream_OneClearDecoder) {
@@ -486,7 +502,7 @@
   this->CreateDecoderSelector();
 
   EXPECT_CALL(*this, OnDecoderSelected(kDecoder1));
-  this->SelectDecoder();
+  this->SelectNextDecoder();
 }
 
 TYPED_TEST(DecoderSelectorTest, ClearStream_InternalFallback) {
@@ -496,7 +512,7 @@
   this->CreateDecoderSelector();
 
   EXPECT_CALL(*this, OnDecoderSelected(kDecoder2));
-  this->SelectDecoder();
+  this->SelectNextDecoder();
 }
 
 TYPED_TEST(DecoderSelectorTest, ClearStream_ExternalFallback) {
@@ -506,13 +522,13 @@
   this->CreateDecoderSelector();
 
   EXPECT_CALL(*this, OnDecoderSelected(kDecoder1));
-  this->SelectDecoder();
+  this->SelectNextDecoder();
 
   EXPECT_CALL(*this, OnDecoderSelected(kDecoder2));
-  this->SelectDecoder();
+  this->SelectNextDecoder();
 
   EXPECT_CALL(*this, NoDecoderSelected());
-  this->SelectDecoder();
+  this->SelectNextDecoder();
 }
 
 TYPED_TEST(DecoderSelectorTest, ClearStream_FinalizeDecoderSelection) {
@@ -522,12 +538,12 @@
   this->CreateDecoderSelector();
 
   EXPECT_CALL(*this, OnDecoderSelected(kDecoder1));
-  this->SelectDecoder();
+  this->SelectNextDecoder();
 
-  this->decoder_selector_->FinalizeDecoderSelection();
+  this->FinalizeDecoderSelection();
 
   EXPECT_CALL(*this, OnDecoderSelected(kDecoder1));
-  this->SelectDecoder();
+  this->SelectNextDecoder();
 }
 
 // Tests that platform decoders are prioritized for
@@ -544,16 +560,16 @@
       base::BindRepeating(TypeParam::MockDecoderPriorityCB));
 
   EXPECT_CALL(*this, OnDecoderSelected(kDecoder1));
-  this->SelectDecoder();
+  this->SelectNextDecoder();
   EXPECT_CALL(*this, OnDecoderSelected(kDecoder3));
-  this->SelectDecoder();
+  this->SelectNextDecoder();
   EXPECT_CALL(*this, OnDecoderSelected(kDecoder2));
-  this->SelectDecoder();
+  this->SelectNextDecoder();
   EXPECT_CALL(*this, OnDecoderSelected(kDecoder4));
-  this->SelectDecoder();
+  this->SelectNextDecoder();
 
   EXPECT_CALL(*this, NoDecoderSelected());
-  this->SelectDecoder();
+  this->SelectNextDecoder();
 }
 
 // Tests that non-platform decoders are prioritized for
@@ -570,16 +586,16 @@
       base::BindRepeating(TypeParam::MockDecoderPriorityCB));
 
   EXPECT_CALL(*this, OnDecoderSelected(kDecoder2));
-  this->SelectDecoder();
+  this->SelectNextDecoder();
   EXPECT_CALL(*this, OnDecoderSelected(kDecoder4));
-  this->SelectDecoder();
+  this->SelectNextDecoder();
   EXPECT_CALL(*this, OnDecoderSelected(kDecoder1));
-  this->SelectDecoder();
+  this->SelectNextDecoder();
   EXPECT_CALL(*this, OnDecoderSelected(kDecoder3));
-  this->SelectDecoder();
+  this->SelectNextDecoder();
 
   EXPECT_CALL(*this, NoDecoderSelected());
-  this->SelectDecoder();
+  this->SelectNextDecoder();
 }
 
 // Tests that platform and non-platform decoders remain in the order they are
@@ -597,16 +613,16 @@
       base::BindRepeating(TypeParam::NormalDecoderPriorityCB));
 
   EXPECT_CALL(*this, OnDecoderSelected(kDecoder1));
-  this->SelectDecoder();
+  this->SelectNextDecoder();
   EXPECT_CALL(*this, OnDecoderSelected(kDecoder2));
-  this->SelectDecoder();
+  this->SelectNextDecoder();
   EXPECT_CALL(*this, OnDecoderSelected(kDecoder3));
-  this->SelectDecoder();
+  this->SelectNextDecoder();
   EXPECT_CALL(*this, OnDecoderSelected(kDecoder4));
-  this->SelectDecoder();
+  this->SelectNextDecoder();
 
   EXPECT_CALL(*this, NoDecoderSelected());
-  this->SelectDecoder();
+  this->SelectNextDecoder();
 }
 
 TYPED_TEST(DecoderSelectorTest, ClearStream_SkipAllDecoders) {
@@ -621,7 +637,7 @@
       base::BindRepeating(TypeParam::SkipDecoderPriorityCB));
 
   EXPECT_CALL(*this, NoDecoderSelected());
-  this->SelectDecoder();
+  this->SelectNextDecoder();
 }
 
 TYPED_TEST(DecoderSelectorTest, ClearStream_ForceHardwareDecoders) {
@@ -637,11 +653,11 @@
   this->CreateDecoderSelector();
 
   EXPECT_CALL(*this, OnDecoderSelected(kDecoder1));
-  this->SelectDecoder();
+  this->SelectNextDecoder();
   EXPECT_CALL(*this, OnDecoderSelected(kDecoder3));
-  this->SelectDecoder();
+  this->SelectNextDecoder();
   EXPECT_CALL(*this, NoDecoderSelected());
-  this->SelectDecoder();
+  this->SelectNextDecoder();
 }
 
 // Tests the production predicate for `DecoderSelector<DemuxerStream::VIDEO>`
@@ -661,15 +677,15 @@
   this->CreateDecoderSelector();
 
   EXPECT_CALL(*this, OnDecoderSelected(kDecoder2));
-  this->SelectDecoder();
+  this->SelectNextDecoder();
   EXPECT_CALL(*this, OnDecoderSelected(kDecoder4));
-  this->SelectDecoder();
+  this->SelectNextDecoder();
   EXPECT_CALL(*this, OnDecoderSelected(kDecoder1));
-  this->SelectDecoder();
+  this->SelectNextDecoder();
   EXPECT_CALL(*this, OnDecoderSelected(kDecoder3));
-  this->SelectDecoder();
+  this->SelectNextDecoder();
   EXPECT_CALL(*this, NoDecoderSelected());
-  this->SelectDecoder();
+  this->SelectNextDecoder();
 }
 
 // Tests the production predicate for `DecoderSelector<DemuxerStream::VIDEO>`
@@ -689,15 +705,15 @@
   this->CreateDecoderSelector();
 
   EXPECT_CALL(*this, OnDecoderSelected(kDecoder1));
-  this->SelectDecoder();
+  this->SelectNextDecoder();
   EXPECT_CALL(*this, OnDecoderSelected(kDecoder3));
-  this->SelectDecoder();
+  this->SelectNextDecoder();
   EXPECT_CALL(*this, OnDecoderSelected(kDecoder2));
-  this->SelectDecoder();
+  this->SelectNextDecoder();
   EXPECT_CALL(*this, OnDecoderSelected(kDecoder4));
-  this->SelectDecoder();
+  this->SelectNextDecoder();
   EXPECT_CALL(*this, NoDecoderSelected());
-  this->SelectDecoder();
+  this->SelectNextDecoder();
 }
 
 // Tests for encrypted streams.
@@ -721,7 +737,7 @@
   this->CreateDecoderSelector();
 
   EXPECT_CALL(*this, NoDecoderSelected());
-  this->SelectDecoder();
+  this->SelectNextDecoder();
 }
 
 // Tests that for an encrypted stream, platform decoders are prioritized for
@@ -738,16 +754,16 @@
       base::BindRepeating(TypeParam::MockDecoderPriorityCB));
 
   EXPECT_CALL(*this, OnDecoderSelected(kDecoder1));
-  this->SelectDecoder();
+  this->SelectNextDecoder();
   EXPECT_CALL(*this, OnDecoderSelected(kDecoder3));
-  this->SelectDecoder();
+  this->SelectNextDecoder();
   EXPECT_CALL(*this, OnDecoderSelected(kDecoder2));
-  this->SelectDecoder();
+  this->SelectNextDecoder();
   EXPECT_CALL(*this, OnDecoderSelected(kDecoder4));
-  this->SelectDecoder();
+  this->SelectNextDecoder();
 
   EXPECT_CALL(*this, NoDecoderSelected());
-  this->SelectDecoder();
+  this->SelectNextDecoder();
 }
 
 // Tests that for an encrypted stream, non-platform decoders are prioritized for
@@ -764,16 +780,16 @@
       base::BindRepeating(TypeParam::MockDecoderPriorityCB));
 
   EXPECT_CALL(*this, OnDecoderSelected(kDecoder2));
-  this->SelectDecoder();
+  this->SelectNextDecoder();
   EXPECT_CALL(*this, OnDecoderSelected(kDecoder4));
-  this->SelectDecoder();
+  this->SelectNextDecoder();
   EXPECT_CALL(*this, OnDecoderSelected(kDecoder1));
-  this->SelectDecoder();
+  this->SelectNextDecoder();
   EXPECT_CALL(*this, OnDecoderSelected(kDecoder3));
-  this->SelectDecoder();
+  this->SelectNextDecoder();
 
   EXPECT_CALL(*this, NoDecoderSelected());
-  this->SelectDecoder();
+  this->SelectNextDecoder();
 }
 
 // Tests that platform and non-platform decoders remain in the order they are
@@ -791,16 +807,16 @@
       base::BindRepeating(TypeParam::NormalDecoderPriorityCB));
 
   EXPECT_CALL(*this, OnDecoderSelected(kDecoder1));
-  this->SelectDecoder();
+  this->SelectNextDecoder();
   EXPECT_CALL(*this, OnDecoderSelected(kDecoder2));
-  this->SelectDecoder();
+  this->SelectNextDecoder();
   EXPECT_CALL(*this, OnDecoderSelected(kDecoder3));
-  this->SelectDecoder();
+  this->SelectNextDecoder();
   EXPECT_CALL(*this, OnDecoderSelected(kDecoder4));
-  this->SelectDecoder();
+  this->SelectNextDecoder();
 
   EXPECT_CALL(*this, NoDecoderSelected());
-  this->SelectDecoder();
+  this->SelectNextDecoder();
 }
 
 TYPED_TEST(DecoderSelectorTest, EncryptedStream_SkipAllDecoders) {
@@ -815,7 +831,7 @@
       base::BindRepeating(TypeParam::SkipDecoderPriorityCB));
 
   EXPECT_CALL(*this, NoDecoderSelected());
-  this->SelectDecoder();
+  this->SelectNextDecoder();
 }
 
 TYPED_TEST(DecoderSelectorTest, EncryptedStream_ForceHardwareDecoders) {
@@ -831,9 +847,9 @@
   this->CreateDecoderSelector();
 
   EXPECT_CALL(*this, OnDecoderSelected(kDecoder3));
-  this->SelectDecoder();
+  this->SelectNextDecoder();
   EXPECT_CALL(*this, NoDecoderSelected());
-  this->SelectDecoder();
+  this->SelectNextDecoder();
 }
 
 TYPED_TEST(DecoderSelectorTest, EncryptedStream_NoDecryptor_OneClearDecoder) {
@@ -843,7 +859,7 @@
   this->CreateDecoderSelector();
 
   EXPECT_CALL(*this, NoDecoderSelected());
-  this->SelectDecoder();
+  this->SelectNextDecoder();
 }
 
 TYPED_TEST(DecoderSelectorTest, EncryptedStream_NoDecryptor_InternalFallback) {
@@ -854,7 +870,7 @@
   this->CreateDecoderSelector();
 
   EXPECT_CALL(*this, OnDecoderSelected(kDecoder2));
-  this->SelectDecoder();
+  this->SelectNextDecoder();
 }
 
 TYPED_TEST(DecoderSelectorTest, EncryptedStream_NoDecryptor_ExternalFallback) {
@@ -865,10 +881,10 @@
   this->CreateDecoderSelector();
 
   EXPECT_CALL(*this, OnDecoderSelected(kDecoder1));
-  this->SelectDecoder();
+  this->SelectNextDecoder();
 
   EXPECT_CALL(*this, OnDecoderSelected(kDecoder2));
-  this->SelectDecoder();
+  this->SelectNextDecoder();
 }
 
 TYPED_TEST(DecoderSelectorTest,
@@ -880,12 +896,12 @@
   this->CreateDecoderSelector();
 
   EXPECT_CALL(*this, OnDecoderSelected(kDecoder1));
-  this->SelectDecoder();
+  this->SelectNextDecoder();
 
-  this->decoder_selector_->FinalizeDecoderSelection();
+  this->FinalizeDecoderSelection();
 
   EXPECT_CALL(*this, OnDecoderSelected(kDecoder1));
-  this->SelectDecoder();
+  this->SelectNextDecoder();
 }
 
 TYPED_TEST(DecoderSelectorTest, EncryptedStream_DecryptOnly_NoDecoder) {
@@ -894,7 +910,7 @@
   this->CreateDecoderSelector();
 
   EXPECT_CALL(*this, NoDecoderSelected());
-  this->SelectDecoder();
+  this->SelectNextDecoder();
 }
 
 TYPED_TEST(DecoderSelectorTest, EncryptedStream_DecryptOnly_OneClearDecoder) {
@@ -905,7 +921,7 @@
 
   EXPECT_CALL(*this, OnDecoderSelected(kDecoder1));
   EXPECT_CALL(*this, OnDemuxerStreamSelected(NotNull()));
-  this->SelectDecoder();
+  this->SelectNextDecoder();
 }
 
 TYPED_TEST(DecoderSelectorTest, EncryptedStream_DecryptOnly_InternalFallback) {
@@ -918,7 +934,7 @@
   EXPECT_CALL(*this, OnDecoderSelected(kDecoder2));
   EXPECT_CALL(*this, OnDemuxerStreamSelected(NotNull()));
 
-  this->SelectDecoder();
+  this->SelectNextDecoder();
 }
 
 TYPED_TEST(DecoderSelectorTest,
@@ -936,13 +952,13 @@
         saved_dds = std::move(dds);
       });
 
-  this->SelectDecoder();
+  this->SelectNextDecoder();
 
-  this->decoder_selector_->FinalizeDecoderSelection();
+  this->FinalizeDecoderSelection();
 
   // DDS is reused.
   EXPECT_CALL(*this, OnDecoderSelected(kDecoder1));
-  this->SelectDecoder();
+  this->SelectNextDecoder();
 }
 
 TYPED_TEST(DecoderSelectorTest, EncryptedStream_DecryptAndDecode) {
@@ -964,7 +980,7 @@
   EXPECT_CALL(*this, OnDemuxerStreamSelected(NotNull()));
 #endif  // !BUILDFLAG(IS_ANDROID)
 
-  this->SelectDecoder();
+  this->SelectNextDecoder();
 }
 
 TYPED_TEST(DecoderSelectorTest,
@@ -979,7 +995,7 @@
 #if !BUILDFLAG(IS_ANDROID)
   // DecryptingDecoder is selected immediately.
   EXPECT_CALL(*this, OnDecoderSelected(TestFixture::DecoderType::kDecrypting));
-  this->SelectDecoder();
+  this->SelectNextDecoder();
 #endif  // !BUILDFLAG(IS_ANDROID)
 
   // On fallback, a DecryptingDemuxerStream will be created.
@@ -989,11 +1005,11 @@
       .WillOnce([&](std::unique_ptr<DecryptingDemuxerStream> dds) {
         saved_dds = std::move(dds);
       });
-  this->SelectDecoder();
+  this->SelectNextDecoder();
 
   // The DecryptingDemuxerStream should be reused.
   EXPECT_CALL(*this, OnDecoderSelected(kDecoder2));
-  this->SelectDecoder();
+  this->SelectNextDecoder();
 }
 
 TYPED_TEST(DecoderSelectorTest, ClearToEncryptedStream_DecryptOnly) {
@@ -1003,14 +1019,14 @@
   this->CreateDecoderSelector();
 
   EXPECT_CALL(*this, OnDecoderSelected(kDecoder1));
-  this->SelectDecoder();
+  this->SelectNextDecoder();
 
-  this->decoder_selector_->FinalizeDecoderSelection();
+  this->FinalizeDecoderSelection();
   this->UseEncryptedDecoderConfig();
 
   EXPECT_CALL(*this, OnDecoderSelected(kDecoder1));
   EXPECT_CALL(*this, OnDemuxerStreamSelected(NotNull()));
-  this->SelectDecoder();
+  this->SelectNextDecoder();
 }
 
 // Tests the production predicate for `DecoderSelector<DemuxerStream::VIDEO>`
@@ -1030,11 +1046,11 @@
   this->CreateDecoderSelector();
 
   EXPECT_CALL(*this, OnDecoderSelected(kDecoder4));
-  this->SelectDecoder();
+  this->SelectNextDecoder();
   EXPECT_CALL(*this, OnDecoderSelected(kDecoder3));
-  this->SelectDecoder();
+  this->SelectNextDecoder();
   EXPECT_CALL(*this, NoDecoderSelected());
-  this->SelectDecoder();
+  this->SelectNextDecoder();
 }
 
 // Tests the production predicate for `DecoderSelector<DemuxerStream::VIDEO>`
@@ -1054,11 +1070,11 @@
   this->CreateDecoderSelector();
 
   EXPECT_CALL(*this, OnDecoderSelected(kDecoder3));
-  this->SelectDecoder();
+  this->SelectNextDecoder();
   EXPECT_CALL(*this, OnDecoderSelected(kDecoder4));
-  this->SelectDecoder();
+  this->SelectNextDecoder();
   EXPECT_CALL(*this, NoDecoderSelected());
-  this->SelectDecoder();
+  this->SelectNextDecoder();
 }
 
 // Tests we always use resolution-based rules for RTC.
@@ -1077,7 +1093,7 @@
   this->CreateDecoderSelector();
 
   EXPECT_CALL(*this, OnDecoderSelected(kDecoder2));
-  this->SelectDecoder();
+  this->SelectNextDecoder();
 }
 
 // Non-platform decoders should be used for RTC unless enabled by a switch.
@@ -1094,7 +1110,7 @@
   this->CreateDecoderSelector();
 
   EXPECT_CALL(*this, OnDecoderSelected(kDecoder1)).Times(0);
-  this->SelectDecoder();
+  this->SelectNextDecoder();
 }
 
 // Platform decoders should be allowed for RTC without the sw switch.
@@ -1111,7 +1127,7 @@
   this->CreateDecoderSelector();
 
   EXPECT_CALL(*this, OnDecoderSelected(kDecoder1));
-  this->SelectDecoder();
+  this->SelectNextDecoder();
 }
 
 // Non-platform decoders should be allowed for RTC if enabled by a switch.
@@ -1128,7 +1144,7 @@
   this->CreateDecoderSelector();
 
   EXPECT_CALL(*this, OnDecoderSelected(kDecoder1));
-  this->SelectDecoder();
+  this->SelectNextDecoder();
 }
 
 }  // namespace media
diff --git a/media/filters/decoder_stream.cc b/media/filters/decoder_stream.cc
index 92bf8ff..84dc02c 100644
--- a/media/filters/decoder_stream.cc
+++ b/media/filters/decoder_stream.cc
@@ -169,7 +169,7 @@
                                std::move(waiting_cb));
 
   state_ = STATE_INITIALIZING;
-  SelectDecoder();
+  BeginDecoderSelection();
 }
 
 template <DemuxerStream::Type StreamType>
@@ -186,8 +186,8 @@
   TRACE_EVENT_ASYNC_BEGIN0("media", GetReadTraceString<StreamType>(), this);
   if (state_ == STATE_ERROR) {
     read_cb_ = BindToCurrentLoop(std::move(read_cb));
-    // TODO(crbug.com/1129662): Consider attaching a caused-by of the original
-    // error as well.
+    // OnDecodeDone, OnBufferReady, and CompleteDecoderReinitialization all set
+    // STATE_ERROR and call SatisfyRead, passing the error back to a ReadCB.
     SatisfyRead(DecoderStatus::Codes::kDecoderStreamInErrorState);
     return;
   }
@@ -337,8 +337,8 @@
 }
 
 template <DemuxerStream::Type StreamType>
-void DecoderStream<StreamType>::SelectDecoder() {
-  decoder_selector_.SelectDecoder(
+void DecoderStream<StreamType>::BeginDecoderSelection() {
+  decoder_selector_.BeginDecoderSelection(
       base::BindOnce(&DecoderStream<StreamType>::OnDecoderSelected,
                      weak_factory_.GetWeakPtr()),
       base::BindRepeating(&DecoderStream<StreamType>::OnDecodeOutputReady,
@@ -346,12 +346,23 @@
 }
 
 template <DemuxerStream::Type StreamType>
+void DecoderStream<StreamType>::ResumeDecoderSelection(
+    DecoderStatus&& reinit_cause) {
+  decoder_selector_.ResumeDecoderSelection(
+      base::BindOnce(&DecoderStream<StreamType>::OnDecoderSelected,
+                     weak_factory_.GetWeakPtr()),
+      base::BindRepeating(&DecoderStream<StreamType>::OnDecodeOutputReady,
+                          fallback_weak_factory_.GetWeakPtr()),
+      std::move(reinit_cause));
+}
+
+template <DemuxerStream::Type StreamType>
 void DecoderStream<StreamType>::OnDecoderSelected(
-    std::unique_ptr<Decoder> selected_decoder,
+    DecoderStatus::Or<std::unique_ptr<Decoder>> decoder_or_error,
     std::unique_ptr<DecryptingDemuxerStream> decrypting_demuxer_stream) {
   FUNCTION_DVLOG(1) << ": "
-                    << (selected_decoder
-                            ? GetDecoderName(selected_decoder->GetDecoderType())
+                    << (decoder_or_error.has_value()
+                            ? GetDecoderName(decoder_or_error->GetDecoderType())
                             : "No decoder selected.");
   DCHECK(task_runner_->RunsTasksInCurrentSequence());
   DCHECK(state_ == STATE_INITIALIZING || state_ == STATE_REINITIALIZING_DECODER)
@@ -377,13 +388,14 @@
     cdm_context_ = nullptr;
   }
 
-  decoder_ = std::move(selected_decoder);
-  if (decoder_change_observer_cb_)
-    decoder_change_observer_cb_.Run(decoder_.get());
+  if (decoder_change_observer_cb_) {
+    decoder_change_observer_cb_.Run(
+        decoder_or_error.has_value() ? (*decoder_or_error).get() : nullptr);
+  }
 
   // TODO(tguilbert): crbug.com/603713 support config changes on decoder reinit.
   if (received_config_change_during_reinit_) {
-    CompleteDecoderReinitialization(false);
+    CompleteDecoderReinitialization(DecoderStatus::Codes::kInterrupted);
     return;
   }
 
@@ -391,18 +403,24 @@
   // never successfully outputed a frame).
   fallback_buffers_ = pending_buffers_;
 
-  if (!decoder_) {
+  if (decoder_or_error.has_error()) {
     if (state_ == STATE_INITIALIZING) {
       state_ = STATE_UNINITIALIZED;
       MEDIA_LOG(ERROR, media_log_)
           << GetStreamTypeString() << " decoder initialization failed";
       std::move(init_cb_).Run(false);
+      // Node that |decoder_or_error| is not actually lost in this case, as
+      // DecoderSelector is keeping track of it to use in case there are no
+      // successfully initialized decoders.
     } else {
-      CompleteDecoderReinitialization(false);
+      CompleteDecoderReinitialization(std::move(decoder_or_error).error());
     }
     return;
   }
 
+  DCHECK(decoder_or_error.has_value());
+  decoder_ = std::move(decoder_or_error).value();
+
   // Send logs and statistics updates including the decoder name.
   traits_->SetIsPlatformDecoder(decoder_->IsPlatformDecoder());
   traits_->SetIsDecryptingDemuxerStream(!!decrypting_demuxer_stream_);
@@ -428,7 +446,7 @@
       << traits_->GetDecoderConfig(stream_).AsHumanReadableString();
 
   if (state_ == STATE_REINITIALIZING_DECODER) {
-    CompleteDecoderReinitialization(true);
+    CompleteDecoderReinitialization(OkStatus());
     return;
   }
 
@@ -609,7 +627,7 @@
         pending_decode_requests_ = 0;
         decoding_eos_ = false;
         state_ = STATE_REINITIALIZING_DECODER;
-        SelectDecoder();
+        ResumeDecoderSelection(std::move(status));
       } else {
         media_log_->NotifyError(status);
         MEDIA_LOG(ERROR, media_log_)
@@ -760,6 +778,8 @@
         << GetStreamTypeString() << " demuxer stream read error!";
     pending_buffers_.clear();
     ClearOutputs();
+    // TODO(crbug.com/c/1326324): Convert |status| into a typed status so that
+    // it can be set as a cause here.
     if (read_cb_)
       SatisfyRead(DecoderStatus::Codes::kDecoderStreamDemuxerError);
   }
@@ -867,16 +887,17 @@
 
   state_ = STATE_REINITIALIZING_DECODER;
   decoder_selector_.PrependDecoder(std::move(decoder_));
-  SelectDecoder();
+  BeginDecoderSelection();
 }
 
 template <DemuxerStream::Type StreamType>
-void DecoderStream<StreamType>::CompleteDecoderReinitialization(bool success) {
+void DecoderStream<StreamType>::CompleteDecoderReinitialization(
+    DecoderStatus status) {
   FUNCTION_DVLOG(2);
   DCHECK(task_runner_->RunsTasksInCurrentSequence());
   DCHECK_EQ(state_, STATE_REINITIALIZING_DECODER);
 
-  state_ = success ? STATE_NORMAL : STATE_ERROR;
+  state_ = status.is_ok() ? STATE_NORMAL : STATE_ERROR;
 
   if (reset_cb_) {
     std::move(reset_cb_).Run();
@@ -889,7 +910,7 @@
   if (state_ == STATE_ERROR) {
     MEDIA_LOG(ERROR, media_log_)
         << GetStreamTypeString() << " decoder reinitialization failed";
-    SatisfyRead(DecoderStatus::Codes::kDecoderStreamReinitFailed);
+    SatisfyRead(std::move(status));
     return;
   }
 
diff --git a/media/filters/decoder_stream.h b/media/filters/decoder_stream.h
index 894949c..a66a9f4 100644
--- a/media/filters/decoder_stream.h
+++ b/media/filters/decoder_stream.h
@@ -173,13 +173,14 @@
   // Returns true if one more decode request can be submitted to the decoder.
   bool CanDecodeMore() const;
 
-  void SelectDecoder();
+  void BeginDecoderSelection();
+  void ResumeDecoderSelection(DecoderStatus&& reinit_cause);
 
   // Called when |decoder_selector| selected the |selected_decoder|.
   // |decrypting_demuxer_stream| was also populated if a DecryptingDemuxerStream
   // is created to help decrypt the encrypted stream.
   void OnDecoderSelected(
-      std::unique_ptr<Decoder> selected_decoder,
+      DecoderStatus::Or<std::unique_ptr<Decoder>> decoder_or_error,
       std::unique_ptr<DecryptingDemuxerStream> decrypting_demuxer_stream);
 
   // Satisfy pending |read_cb_| with |result|.
@@ -214,7 +215,7 @@
 
   void ReinitializeDecoder();
 
-  void CompleteDecoderReinitialization(bool success);
+  void CompleteDecoderReinitialization(DecoderStatus status);
 
   void ResetDecoder();
   void OnDecoderReset();
diff --git a/media/gpu/v4l2/test/av1_decoder.cc b/media/gpu/v4l2/test/av1_decoder.cc
index 399e1cf2..a0f65621 100644
--- a/media/gpu/v4l2/test/av1_decoder.cc
+++ b/media/gpu/v4l2/test/av1_decoder.cc
@@ -4,6 +4,8 @@
 
 #include "media/gpu/v4l2/test/av1_decoder.h"
 
+#include <linux/media/av1-ctrls.h>
+
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
 #include "media/filters/ivf_parser.h"
@@ -18,6 +20,61 @@
               "Too many CAPTURE buffers are used. The number of CAPTURE "
               "buffers is currently assumed to be no larger than 16.");
 
+// TODO(stevecho): Remove this provision when av1-ctrls.h includes linux/bits.h.
+#ifndef BIT
+#define BIT(nr) (1U << (nr))
+#endif
+
+inline void conditionally_set_flags(__u8* flags,
+                                    const bool condition,
+                                    const bool mask) {
+  *flags |= (condition ? mask : 0);
+}
+
+// Section 5.9.11. Loop filter params syntax in AV1 spec.
+// https://aomediacodec.github.io/av1-spec/av1-spec.pdf
+// Note that |update_ref_delta| and |update_mode_delta| flags in the spec
+// are not needed for V4L2 AV1 API.
+// TODO(stevecho): sanity check data structures in libgav1 against the AV1 spec.
+void FillLoopFilterParams(struct v4l2_av1_loop_filter* v4l2_lf,
+                          const libgav1::LoopFilter& lf) {
+  conditionally_set_flags(&v4l2_lf->flags, lf.delta_enabled,
+                          V4L2_AV1_LOOP_FILTER_FLAG_DELTA_ENABLED);
+  conditionally_set_flags(&v4l2_lf->flags, lf.delta_update,
+                          V4L2_AV1_LOOP_FILTER_FLAG_DELTA_UPDATE);
+
+  static_assert(std::size(decltype(v4l2_lf->level){}) == libgav1::kFrameLfCount,
+                "Invalid size of loop filter level (strength) array");
+  for (size_t i = 0; i < libgav1::kFrameLfCount; i++)
+    v4l2_lf->level[i] = base::checked_cast<__u8>(lf.level[i]);
+
+  v4l2_lf->sharpness = lf.sharpness;
+
+  static_assert(std::size(decltype(v4l2_lf->ref_deltas){}) ==
+                    libgav1::kNumReferenceFrameTypes,
+                "Invalid size of ref deltas array");
+  for (size_t i = 0; i < libgav1::kNumReferenceFrameTypes; i++)
+    v4l2_lf->ref_deltas[i] = lf.ref_deltas[i];
+
+  static_assert(std::size(decltype(v4l2_lf->mode_deltas){}) ==
+                    libgav1::kLoopFilterMaxModeDeltas,
+                "Invalid size of mode deltas array");
+  for (size_t i = 0; i < libgav1::kLoopFilterMaxModeDeltas; i++)
+    v4l2_lf->mode_deltas[i] = lf.mode_deltas[i];
+}
+
+// Section 5.9.18. Loop filter delta parameters syntax.
+// Note that |delta_lf_res| in |v4l2_av1_loop_filter| corresponds to
+// |delta_lf.scale| in the frame header defined in libgav1.
+void FillLoopFilterDeltaParams(struct v4l2_av1_loop_filter* v4l2_lf,
+                               const libgav1::Delta& delta_lf) {
+  conditionally_set_flags(&v4l2_lf->flags, delta_lf.present,
+                          V4L2_AV1_LOOP_FILTER_FLAG_DELTA_LF_PRESENT);
+
+  v4l2_lf->delta_lf_res = delta_lf.scale;
+  v4l2_lf->delta_lf_multi = delta_lf.multi;
+}
+
 Av1Decoder::Av1Decoder(std::unique_ptr<IvfParser> ivf_parser,
                        std::unique_ptr<V4L2IoctlShim> v4l2_ioctl,
                        std::unique_ptr<V4L2Queue> OUTPUT_queue,
@@ -257,7 +314,14 @@
   }
 
   // TODO(b/228534730): add changes to prepare parameters for V4L2 AV1 stateless
-  // decoding
+  // decoding and make VIDIOC_S_EXT_CTRLS v4l2 ioctl call
+  struct v4l2_ctrl_av1_frame_header v4l2_frame_params = {};
+
+  FillLoopFilterParams(&v4l2_frame_params.loop_filter,
+                       current_frame_header.loop_filter);
+
+  FillLoopFilterDeltaParams(&v4l2_frame_params.loop_filter,
+                            current_frame_header.delta_lf);
 
   if (!v4l2_ioctl_->MediaRequestIocQueue(OUTPUT_queue_))
     LOG(FATAL) << "MEDIA_REQUEST_IOC_QUEUE failed.";
diff --git a/media/gpu/v4l2/test/av1_decoder.h b/media/gpu/v4l2/test/av1_decoder.h
index cad35a0..5f8de6333 100644
--- a/media/gpu/v4l2/test/av1_decoder.h
+++ b/media/gpu/v4l2/test/av1_decoder.h
@@ -8,6 +8,10 @@
 #include "media/gpu/v4l2/test/v4l2_ioctl_shim.h"
 #include "media/gpu/v4l2/test/video_decoder.h"
 
+// TODO(b/234019411): Move this include to v4l2_stateless_decoder.cc
+// once the bug is fixed.
+#include <linux/media/av1-ctrls.h>
+
 #include <set>
 
 #include "media/filters/ivf_parser.h"
@@ -23,16 +27,11 @@
 #define ANALYZER_ALLOW_UNUSED(var) static_cast<void>(var);
 
 // TODO(stevecho): This is temporary until the change to define
-// V4L2_PIX_FMT_AV1_FRAME lands in videodev2.h.
+// V4L2_PIX_FMT_AV1 lands in videodev2.h.
 // https://patchwork.linuxtv.org/project/linux-media/patch/20210810220552.298140-2-daniel.almeida@collabora.com/
 #ifndef V4L2_PIX_FMT_AV1
 #define V4L2_PIX_FMT_AV1 v4l2_fourcc('A', 'V', '0', '1') /* AV1 */
 #endif
-#ifndef V4L2_PIX_FMT_AV1_FRAME
-#define V4L2_PIX_FMT_AV1_FRAME                        \
-  v4l2_fourcc('A', 'V', '1', 'F') /* AV1 parsed frame \
-                                   */
-#endif
 
 namespace media {
 
diff --git a/media/mojo/services/stable_video_decoder_service.cc b/media/mojo/services/stable_video_decoder_service.cc
index 36b0eab..2cbdedf 100644
--- a/media/mojo/services/stable_video_decoder_service.cc
+++ b/media/mojo/services/stable_video_decoder_service.cc
@@ -8,7 +8,10 @@
 
 StableVideoDecoderService::StableVideoDecoderService(
     std::unique_ptr<mojom::VideoDecoder> dst_video_decoder)
-    : dst_video_decoder_(std::move(dst_video_decoder)) {
+    : video_decoder_client_receiver_(this),
+      media_log_receiver_(this),
+      stable_video_frame_handle_releaser_receiver_(this),
+      dst_video_decoder_(std::move(dst_video_decoder)) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   CHECK(!!dst_video_decoder_);
 }
@@ -32,7 +35,31 @@
     mojo::ScopedDataPipeConsumerHandle decoder_buffer_pipe,
     const gfx::ColorSpace& target_color_space) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  NOTIMPLEMENTED();
+  if (video_decoder_client_receiver_.is_bound()) {
+    mojo::ReportBadMessage("Construct() already called");
+    return;
+  }
+
+  DCHECK(!video_decoder_client_receiver_.is_bound());
+  DCHECK(!stable_video_decoder_client_remote_.is_bound());
+  stable_video_decoder_client_remote_.Bind(
+      std::move(stable_video_decoder_client_remote));
+
+  DCHECK(!media_log_receiver_.is_bound());
+  DCHECK(!stable_media_log_remote_.is_bound());
+  stable_media_log_remote_.Bind(std::move(stable_media_log_remote));
+
+  DCHECK(!video_frame_handle_releaser_remote_);
+  DCHECK(!stable_video_frame_handle_releaser_receiver_.is_bound());
+  stable_video_frame_handle_releaser_receiver_.Bind(
+      std::move(stable_video_frame_handle_releaser_receiver));
+
+  dst_video_decoder_->Construct(
+      video_decoder_client_receiver_.BindNewEndpointAndPassRemote(),
+      media_log_receiver_.BindNewPipeAndPassRemote(),
+      video_frame_handle_releaser_remote_.BindNewPipeAndPassReceiver(),
+      std::move(decoder_buffer_pipe), mojom::CommandBufferId::New(),
+      target_color_space);
 }
 
 void StableVideoDecoderService::Initialize(
@@ -56,4 +83,34 @@
   NOTIMPLEMENTED();
 }
 
+void StableVideoDecoderService::ReleaseVideoFrame(
+    const base::UnguessableToken& release_token) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  NOTIMPLEMENTED();
+}
+
+void StableVideoDecoderService::OnVideoFrameDecoded(
+    const scoped_refptr<VideoFrame>& frame,
+    bool can_read_without_stalling,
+    const absl::optional<base::UnguessableToken>& release_token) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  NOTIMPLEMENTED();
+}
+
+void StableVideoDecoderService::OnWaiting(WaitingReason reason) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  NOTIMPLEMENTED();
+}
+
+void StableVideoDecoderService::RequestOverlayInfo(
+    bool restart_for_transitions) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  NOTREACHED();
+}
+
+void StableVideoDecoderService::AddLogRecord(const MediaLogRecord& event) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  NOTIMPLEMENTED();
+}
+
 }  // namespace media
diff --git a/media/mojo/services/stable_video_decoder_service.h b/media/mojo/services/stable_video_decoder_service.h
index 5282507..4593d04 100644
--- a/media/mojo/services/stable_video_decoder_service.h
+++ b/media/mojo/services/stable_video_decoder_service.h
@@ -7,9 +7,14 @@
 
 #include "base/sequence_checker.h"
 #include "base/thread_annotations.h"
+#include "media/mojo/mojom/media_log.mojom.h"
 #include "media/mojo/mojom/stable/stable_video_decoder.mojom.h"
 #include "media/mojo/mojom/video_decoder.mojom.h"
 #include "media/mojo/services/media_mojo_export.h"
+#include "mojo/public/cpp/bindings/associated_receiver.h"
+#include "mojo/public/cpp/bindings/associated_remote.h"
+#include "mojo/public/cpp/bindings/receiver.h"
+#include "mojo/public/cpp/bindings/remote.h"
 
 namespace media {
 
@@ -34,7 +39,10 @@
 // StableVideoDecoderService should also check incoming data due to similar
 // concerns.
 class MEDIA_MOJO_EXPORT StableVideoDecoderService
-    : public stable::mojom::StableVideoDecoder {
+    : public stable::mojom::StableVideoDecoder,
+      public stable::mojom::VideoFrameHandleReleaser,
+      public mojom::VideoDecoderClient,
+      public mojom::MediaLog {
  public:
   explicit StableVideoDecoderService(
       std::unique_ptr<mojom::VideoDecoder> dst_video_decoder);
@@ -62,7 +70,45 @@
               DecodeCallback callback) final;
   void Reset(ResetCallback callback) final;
 
+  // mojom::stable::VideoFrameHandleReleaser implementation.
+  void ReleaseVideoFrame(const base::UnguessableToken& release_token) final;
+
+  // mojom::VideoDecoderClient implementation.
+  void OnVideoFrameDecoded(
+      const scoped_refptr<VideoFrame>& frame,
+      bool can_read_without_stalling,
+      const absl::optional<base::UnguessableToken>& release_token) final;
+  void OnWaiting(WaitingReason reason) final;
+  void RequestOverlayInfo(bool restart_for_transitions) final;
+
+  // mojom::MediaLog implementation.
+  void AddLogRecord(const MediaLogRecord& event) final;
+
  private:
+  // Incoming calls from the |dst_video_decoder_| to
+  // |video_decoder_client_receiver_| are forwarded to
+  // |stable_video_decoder_client_remote_|.
+  mojo::AssociatedReceiver<mojom::VideoDecoderClient>
+      video_decoder_client_receiver_ GUARDED_BY_CONTEXT(sequence_checker_);
+  mojo::AssociatedRemote<stable::mojom::VideoDecoderClient>
+      stable_video_decoder_client_remote_ GUARDED_BY_CONTEXT(sequence_checker_);
+
+  // Incoming calls from the |dst_video_decoder_| to |media_log_receiver_| are
+  // forwarded to |stable_media_log_remote_|.
+  mojo::Receiver<mojom::MediaLog> media_log_receiver_
+      GUARDED_BY_CONTEXT(sequence_checker_);
+  mojo::Remote<stable::mojom::MediaLog> stable_media_log_remote_
+      GUARDED_BY_CONTEXT(sequence_checker_);
+
+  // Incoming requests from the client to
+  // |stable_video_frame_handle_releaser_receiver_| are forwarded to
+  // |video_frame_handle_releaser_remote_|.
+  mojo::Receiver<stable::mojom::VideoFrameHandleReleaser>
+      stable_video_frame_handle_releaser_receiver_
+          GUARDED_BY_CONTEXT(sequence_checker_);
+  mojo::Remote<mojom::VideoFrameHandleReleaser>
+      video_frame_handle_releaser_remote_ GUARDED_BY_CONTEXT(sequence_checker_);
+
   // The incoming stable::mojom::StableVideoDecoder requests are forwarded to
   // |dst_video_decoder_|.
   std::unique_ptr<mojom::VideoDecoder> dst_video_decoder_
diff --git a/media/mojo/services/stable_video_decoder_service_unittest.cc b/media/mojo/services/stable_video_decoder_service_unittest.cc
index c8ac1170..3f24b23 100644
--- a/media/mojo/services/stable_video_decoder_service_unittest.cc
+++ b/media/mojo/services/stable_video_decoder_service_unittest.cc
@@ -5,9 +5,13 @@
 #include "media/mojo/services/stable_video_decoder_service.h"
 #include "base/test/mock_callback.h"
 #include "base/test/task_environment.h"
+#include "media/mojo/common/mojo_decoder_buffer_converter.h"
+#include "media/mojo/mojom/media_log.mojom.h"
 #include "media/mojo/mojom/video_decoder.mojom.h"
 #include "media/mojo/services/stable_video_decoder_factory_service.h"
+#include "mojo/public/cpp/bindings/associated_receiver.h"
 #include "mojo/public/cpp/bindings/remote.h"
+#include "mojo/public/cpp/system/data_pipe.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -21,6 +25,29 @@
 
 namespace {
 
+class MockVideoFrameHandleReleaser : public mojom::VideoFrameHandleReleaser {
+ public:
+  explicit MockVideoFrameHandleReleaser(
+      mojo::PendingReceiver<mojom::VideoFrameHandleReleaser>
+          video_frame_handle_releaser)
+      : video_frame_handle_releaser_receiver_(
+            this,
+            std::move(video_frame_handle_releaser)) {}
+  MockVideoFrameHandleReleaser(const MockVideoFrameHandleReleaser&) = delete;
+  MockVideoFrameHandleReleaser& operator=(const MockVideoFrameHandleReleaser&) =
+      delete;
+  ~MockVideoFrameHandleReleaser() override = default;
+
+  // mojom::VideoFrameHandleReleaser implementation.
+  MOCK_METHOD2(ReleaseVideoFrame,
+               void(const base::UnguessableToken& release_token,
+                    const gpu::SyncToken& release_sync_token));
+
+ private:
+  mojo::Receiver<mojom::VideoFrameHandleReleaser>
+      video_frame_handle_releaser_receiver_;
+};
+
 class MockVideoDecoder : public mojom::VideoDecoder {
  public:
   MockVideoDecoder() = default;
@@ -28,17 +55,40 @@
   MockVideoDecoder& operator=(const MockVideoDecoder&) = delete;
   ~MockVideoDecoder() override = default;
 
+  mojo::AssociatedRemote<mojom::VideoDecoderClient> TakeClientRemote() {
+    return std::move(client_remote_);
+  }
+  mojo::Remote<mojom::MediaLog> TakeMediaLogRemote() {
+    return std::move(media_log_remote_);
+  }
+  std::unique_ptr<StrictMock<MockVideoFrameHandleReleaser>>
+  TakeVideoFrameHandleReleaser() {
+    return std::move(video_frame_handle_releaser_);
+  }
+  std::unique_ptr<MojoDecoderBufferReader> TakeMojoDecoderBufferReader() {
+    return std::move(mojo_decoder_buffer_reader_);
+  };
+
   // mojom::VideoDecoder implementation.
   MOCK_METHOD1(GetSupportedConfigs, void(GetSupportedConfigsCallback callback));
-  MOCK_METHOD6(
-      Construct,
-      void(mojo::PendingAssociatedRemote<mojom::VideoDecoderClient> client,
-           mojo::PendingRemote<mojom::MediaLog> media_log,
-           mojo::PendingReceiver<mojom::VideoFrameHandleReleaser>
-               video_frame_handle_receiver,
-           mojo::ScopedDataPipeConsumerHandle decoder_buffer_pipe,
-           mojom::CommandBufferIdPtr command_buffer_id,
-           const gfx::ColorSpace& target_color_space));
+  void Construct(
+      mojo::PendingAssociatedRemote<mojom::VideoDecoderClient> client,
+      mojo::PendingRemote<mojom::MediaLog> media_log,
+      mojo::PendingReceiver<mojom::VideoFrameHandleReleaser>
+          video_frame_handle_releaser,
+      mojo::ScopedDataPipeConsumerHandle decoder_buffer_pipe,
+      mojom::CommandBufferIdPtr command_buffer_id,
+      const gfx::ColorSpace& target_color_space) final {
+    client_remote_.Bind(std::move(client));
+    media_log_remote_.Bind(std::move(media_log));
+    video_frame_handle_releaser_ =
+        std::make_unique<StrictMock<MockVideoFrameHandleReleaser>>(
+            std::move(video_frame_handle_releaser));
+    DoConstruct(std::move(command_buffer_id), target_color_space);
+  }
+  MOCK_METHOD2(DoConstruct,
+               void(mojom::CommandBufferIdPtr command_buffer_id,
+                    const gfx::ColorSpace& target_color_space));
   MOCK_METHOD4(Initialize,
                void(const VideoDecoderConfig& config,
                     bool low_delay,
@@ -48,8 +98,168 @@
                void(mojom::DecoderBufferPtr buffer, DecodeCallback callback));
   MOCK_METHOD1(Reset, void(ResetCallback callback));
   MOCK_METHOD1(OnOverlayInfoChanged, void(const OverlayInfo& overlay_info));
+
+ private:
+  mojo::AssociatedRemote<mojom::VideoDecoderClient> client_remote_;
+  mojo::Remote<mojom::MediaLog> media_log_remote_;
+  std::unique_ptr<StrictMock<MockVideoFrameHandleReleaser>>
+      video_frame_handle_releaser_;
+  std::unique_ptr<MojoDecoderBufferReader> mojo_decoder_buffer_reader_;
 };
 
+class MockStableVideoDecoderClient : public stable::mojom::VideoDecoderClient {
+ public:
+  explicit MockStableVideoDecoderClient(
+      mojo::PendingAssociatedReceiver<stable::mojom::VideoDecoderClient>
+          pending_receiver)
+      : receiver_(this, std::move(pending_receiver)) {}
+  MockStableVideoDecoderClient(const MockStableVideoDecoderClient&) = delete;
+  MockStableVideoDecoderClient& operator=(const MockStableVideoDecoderClient&) =
+      delete;
+  ~MockStableVideoDecoderClient() override = default;
+
+  // stable::mojom::VideoDecoderClient implementation.
+  MOCK_METHOD3(OnVideoFrameDecoded,
+               void(const scoped_refptr<VideoFrame>& frame,
+                    bool can_read_without_stalling,
+                    const base::UnguessableToken& release_token));
+  MOCK_METHOD1(OnWaiting, void(WaitingReason reason));
+
+ private:
+  mojo::AssociatedReceiver<stable::mojom::VideoDecoderClient> receiver_;
+};
+
+class MockStableMediaLog : public stable::mojom::MediaLog {
+ public:
+  explicit MockStableMediaLog(
+      mojo::PendingReceiver<stable::mojom::MediaLog> pending_receiver)
+      : receiver_(this, std::move(pending_receiver)) {}
+  MockStableMediaLog(const MockStableMediaLog&) = delete;
+  MockStableMediaLog& operator=(const MockStableMediaLog&) = delete;
+  ~MockStableMediaLog() override = default;
+
+  // stable::mojom::MediaLog implementation.
+  MOCK_METHOD1(AddLogRecord, void(const MediaLogRecord& event));
+
+ private:
+  mojo::Receiver<stable::mojom::MediaLog> receiver_;
+};
+
+// AuxiliaryEndpoints groups the endpoints that support the operation of a
+// StableVideoDecoderService and that come from the Construct() call. That way,
+// tests can easily poke at one endpoint and set expectations on the other. For
+// example, a test might want to simulate the scenario in which a frame has been
+// decoded by the underlying mojom::VideoDecoder. In this case, the test can
+// call |video_decoder_client_remote|->OnVideoFrameDecoded() and then set an
+// expectation on |mock_stable_video_decoder_client|->OnVideoFrameDecoded().
+struct AuxiliaryEndpoints {
+  // |video_decoder_client_remote| is the client that the underlying
+  // mojom::VideoDecoder receives through the Construct() call. Tests can make
+  // calls on it and those calls should ultimately be received by the
+  // |mock_stable_video_decoder_client|.
+  mojo::AssociatedRemote<mojom::VideoDecoderClient> video_decoder_client_remote;
+  std::unique_ptr<StrictMock<MockStableVideoDecoderClient>>
+      mock_stable_video_decoder_client;
+
+  // |media_log_remote| is the MediaLog that the underlying mojom::VideoDecoder
+  // receives through the Construct() call. Tests can make calls on it and those
+  // calls should ultimately be received by the |mock_stable_media_log|.
+  mojo::Remote<mojom::MediaLog> media_log_remote;
+  std::unique_ptr<StrictMock<MockStableMediaLog>> mock_stable_media_log;
+
+  // Tests can use |stable_video_frame_handle_releaser_remote| to simulate
+  // releasing a VideoFrame.
+  // |mock_video_frame_handle_releaser| is the VideoFrameHandleReleaser that's
+  // setup when the underlying mojom::VideoDecoder receives a Construct() call.
+  // Tests can make calls on |stable_video_frame_handle_releaser_remote| and
+  // they should be ultimately received by the
+  // |mock_video_frame_handle_releaser|.
+  mojo::Remote<stable::mojom::VideoFrameHandleReleaser>
+      stable_video_frame_handle_releaser_remote;
+  std::unique_ptr<StrictMock<MockVideoFrameHandleReleaser>>
+      mock_video_frame_handle_releaser;
+
+  // |mojo_decoder_buffer_reader| wraps the reading end of the data pipe that
+  // the underlying mojom::VideoDecoder receives through the Construct() call.
+  // Tests can write data using the |mojo_decoder_buffer_writer| and that data
+  // should be ultimately received by the |mojo_decoder_buffer_reader|.
+  std::unique_ptr<MojoDecoderBufferWriter> mojo_decoder_buffer_writer;
+  std::unique_ptr<MojoDecoderBufferReader> mojo_decoder_buffer_reader;
+};
+
+// Calls Construct() on |stable_video_decoder_remote| and, if
+// |expect_construct_call| is true, expects a corresponding Construct() call on
+// |mock_video_decoder| which is assumed to be the backing decoder of
+// |stable_video_decoder_remote|. Returns nullptr if the expectations on
+// |mock_video_decoder| are violated. Otherwise, returns an AuxiliaryEndpoints
+// instance that contains the supporting endpoints that tests can use to
+// interact with the auxiliary interfaces used by the
+// |stable_video_decoder_remote|.
+std::unique_ptr<AuxiliaryEndpoints> ConstructStableVideoDecoder(
+    mojo::Remote<stable::mojom::StableVideoDecoder>&
+        stable_video_decoder_remote,
+    StrictMock<MockVideoDecoder>& mock_video_decoder,
+    bool expect_construct_call) {
+  constexpr gfx::ColorSpace kTargetColorSpace = gfx::ColorSpace::CreateSRGB();
+  if (expect_construct_call) {
+    EXPECT_CALL(mock_video_decoder,
+                DoConstruct(/*command_buffer_id=*/_,
+                            /*target_color_space=*/kTargetColorSpace));
+  }
+  mojo::PendingAssociatedRemote<stable::mojom::VideoDecoderClient>
+      stable_video_decoder_client_remote;
+  auto mock_stable_video_decoder_client =
+      std::make_unique<StrictMock<MockStableVideoDecoderClient>>(
+          stable_video_decoder_client_remote
+              .InitWithNewEndpointAndPassReceiver());
+
+  mojo::PendingRemote<stable::mojom::MediaLog> stable_media_log_remote;
+  auto mock_stable_media_log = std::make_unique<StrictMock<MockStableMediaLog>>(
+      stable_media_log_remote.InitWithNewPipeAndPassReceiver());
+
+  mojo::Remote<stable::mojom::VideoFrameHandleReleaser>
+      video_frame_handle_releaser_remote;
+
+  mojo::ScopedDataPipeConsumerHandle remote_consumer_handle;
+  std::unique_ptr<MojoDecoderBufferWriter> mojo_decoder_buffer_writer =
+      MojoDecoderBufferWriter::Create(
+          GetDefaultDecoderBufferConverterCapacity(DemuxerStream::VIDEO),
+          &remote_consumer_handle);
+
+  stable_video_decoder_remote->Construct(
+      std::move(stable_video_decoder_client_remote),
+      std::move(stable_media_log_remote),
+      video_frame_handle_releaser_remote.BindNewPipeAndPassReceiver(),
+      std::move(remote_consumer_handle), kTargetColorSpace);
+  stable_video_decoder_remote.FlushForTesting();
+
+  if (!Mock::VerifyAndClearExpectations(&mock_video_decoder))
+    return nullptr;
+
+  auto auxiliary_endpoints = std::make_unique<AuxiliaryEndpoints>();
+
+  auxiliary_endpoints->video_decoder_client_remote =
+      mock_video_decoder.TakeClientRemote();
+  auxiliary_endpoints->mock_stable_video_decoder_client =
+      std::move(mock_stable_video_decoder_client);
+
+  auxiliary_endpoints->media_log_remote =
+      mock_video_decoder.TakeMediaLogRemote();
+  auxiliary_endpoints->mock_stable_media_log = std::move(mock_stable_media_log);
+
+  auxiliary_endpoints->stable_video_frame_handle_releaser_remote =
+      std::move(video_frame_handle_releaser_remote);
+  auxiliary_endpoints->mock_video_frame_handle_releaser =
+      mock_video_decoder.TakeVideoFrameHandleReleaser();
+
+  auxiliary_endpoints->mojo_decoder_buffer_writer =
+      std::move(mojo_decoder_buffer_writer);
+  auxiliary_endpoints->mojo_decoder_buffer_reader =
+      mock_video_decoder.TakeMojoDecoderBufferReader();
+
+  return auxiliary_endpoints;
+}
+
 class StableVideoDecoderServiceTest : public testing::Test {
  public:
   StableVideoDecoderServiceTest() {
@@ -125,6 +335,38 @@
   }
 }
 
+// Tests that a call to stable::mojom::VideoDecoder::Construct() call gets
+// routed correctly to the underlying mojom::VideoDecoder.
+TEST_F(StableVideoDecoderServiceTest, StableVideoDecoderCanBeConstructed) {
+  auto mock_video_decoder = std::make_unique<StrictMock<MockVideoDecoder>>();
+  auto* mock_video_decoder_raw = mock_video_decoder.get();
+  auto stable_video_decoder_remote =
+      CreateStableVideoDecoder(std::move(mock_video_decoder));
+  ASSERT_TRUE(stable_video_decoder_remote.is_bound());
+  ASSERT_TRUE(stable_video_decoder_remote.is_connected());
+  ASSERT_TRUE(ConstructStableVideoDecoder(stable_video_decoder_remote,
+                                          *mock_video_decoder_raw,
+                                          /*expect_construct_call=*/true));
+}
+
+// Tests that if two calls to stable::mojom::VideoDecoder::Construct() are made,
+// only one is routed to the underlying mojom::VideoDecoder.
+TEST_F(StableVideoDecoderServiceTest,
+       StableVideoDecoderCannotBeConstructedTwice) {
+  auto mock_video_decoder = std::make_unique<StrictMock<MockVideoDecoder>>();
+  auto* mock_video_decoder_raw = mock_video_decoder.get();
+  auto stable_video_decoder_remote =
+      CreateStableVideoDecoder(std::move(mock_video_decoder));
+  ASSERT_TRUE(stable_video_decoder_remote.is_bound());
+  ASSERT_TRUE(stable_video_decoder_remote.is_connected());
+  EXPECT_TRUE(ConstructStableVideoDecoder(stable_video_decoder_remote,
+                                          *mock_video_decoder_raw,
+                                          /*expect_construct_call=*/true));
+  EXPECT_TRUE(ConstructStableVideoDecoder(stable_video_decoder_remote,
+                                          *mock_video_decoder_raw,
+                                          /*expect_construct_call=*/false));
+}
+
 }  // namespace
 
 }  // namespace media
diff --git a/media/renderers/video_renderer_impl.cc b/media/renderers/video_renderer_impl.cc
index 9b0e9cb..2623b95 100644
--- a/media/renderers/video_renderer_impl.cc
+++ b/media/renderers/video_renderer_impl.cc
@@ -575,12 +575,16 @@
     default:
       // Anything other than `kOk` or `kAborted` is treated as an error.
       DCHECK(result.has_error());
-      auto status = result.code() == DecoderStatus::Codes::kDisconnected
-                        ? PIPELINE_ERROR_DISCONNECTED
-                        : PIPELINE_ERROR_DECODE;
+
+      PipelineStatus::Codes code =
+          result.code() == DecoderStatus::Codes::kDisconnected
+              ? PIPELINE_ERROR_DISCONNECTED
+              : PIPELINE_ERROR_DECODE;
+      PipelineStatus status = {code, std::move(result).error()};
       task_runner_->PostTask(
-          FROM_HERE, base::BindOnce(&VideoRendererImpl::OnPlaybackError,
-                                    weak_factory_.GetWeakPtr(), status));
+          FROM_HERE,
+          base::BindOnce(&VideoRendererImpl::OnPlaybackError,
+                         weak_factory_.GetWeakPtr(), std::move(status)));
       return;
   }
 
diff --git a/net/third_party/quiche/BUILD.gn b/net/third_party/quiche/BUILD.gn
index 446faac..75a6a64 100644
--- a/net/third_party/quiche/BUILD.gn
+++ b/net/third_party/quiche/BUILD.gn
@@ -33,7 +33,10 @@
 config("quiche_internal_config") {
   cflags = []
   if (is_clang) {
-    cflags += [ "-Wno-unused-private-field" ]
+    cflags += [
+      "-Wno-unused-private-field",
+      "-Wno-sign-compare",
+    ]
   }
 }
 
diff --git a/printing/test_printing_context.cc b/printing/test_printing_context.cc
index f4b694ec..cce3e1a 100644
--- a/printing/test_printing_context.cc
+++ b/printing/test_printing_context.cc
@@ -17,6 +17,7 @@
 #include "printing/mojom/print.mojom.h"
 #include "printing/print_settings.h"
 #include "printing/printing_context.h"
+#include "printing/units.h"
 #include "ui/gfx/geometry/size.h"
 
 #if BUILDFLAG(IS_WIN)
@@ -103,8 +104,11 @@
 }
 
 gfx::Size TestPrintingContext::GetPdfPaperSizeDeviceUnits() {
-  NOTIMPLEMENTED();
-  return gfx::Size();
+  // Default to A4 paper size, which is an alternative to Letter size that is
+  // often used as the fallback size for some platform-specific
+  // implementations.
+  return gfx::Size(kA4WidthInch * settings_->device_units_per_inch(),
+                   kA4HeightInch * settings_->device_units_per_inch());
 }
 
 mojom::ResultCode TestPrintingContext::UpdatePrinterSettings(
diff --git a/remoting/protocol/webrtc_video_encoder_factory.cc b/remoting/protocol/webrtc_video_encoder_factory.cc
index 6234bcd..8f08b6a4 100644
--- a/remoting/protocol/webrtc_video_encoder_factory.cc
+++ b/remoting/protocol/webrtc_video_encoder_factory.cc
@@ -9,6 +9,7 @@
 #include "remoting/protocol/video_channel_state_observer.h"
 #include "remoting/protocol/webrtc_video_encoder_wrapper.h"
 #include "third_party/webrtc/api/video_codecs/sdp_video_format.h"
+#include "third_party/webrtc/api/video_codecs/video_codec.h"
 #include "third_party/webrtc/api/video_codecs/vp9_profile.h"
 
 #if defined(USE_H264_ENCODER)
@@ -19,19 +20,20 @@
 
 WebrtcVideoEncoderFactory::WebrtcVideoEncoderFactory()
     : main_task_runner_(base::ThreadTaskRunnerHandle::Get()) {
-  formats_.push_back(webrtc::SdpVideoFormat("VP8"));
-  formats_.push_back(webrtc::SdpVideoFormat("VP9"));
-  formats_.push_back(
-      webrtc::SdpVideoFormat("VP9", {{webrtc::kVP9FmtpProfileId, "1"}}));
+  formats_.emplace_back(webrtc::SdpVideoFormat("VP8"));
+  formats_.emplace_back(webrtc::SdpVideoFormat("VP9"));
+  formats_.emplace_back(webrtc::SdpVideoFormat(
+      "VP9", {{webrtc::kVP9FmtpProfileId,
+               webrtc::VP9ProfileToString(webrtc::VP9Profile::kProfile1)}}));
 #if defined(USE_H264_ENCODER)
   // This call will query the underlying media classes to determine whether
   // hardware encoding is supported or not. We use a default resolution and
   // framerate so the call doesn't fail due to invalid params.
   if (WebrtcVideoEncoderGpu::IsSupportedByH264({{1920, 1080}, 30})) {
-    formats_.push_back(webrtc::SdpVideoFormat("H264"));
+    formats_.emplace_back(webrtc::SdpVideoFormat("H264"));
   }
 #endif
-  formats_.push_back(webrtc::SdpVideoFormat("AV1"));
+  formats_.emplace_back(webrtc::SdpVideoFormat("AV1"));
 }
 
 WebrtcVideoEncoderFactory::~WebrtcVideoEncoderFactory() = default;
diff --git a/remoting/protocol/webrtc_video_encoder_wrapper.cc b/remoting/protocol/webrtc_video_encoder_wrapper.cc
index 74313d24..49840f86 100644
--- a/remoting/protocol/webrtc_video_encoder_wrapper.cc
+++ b/remoting/protocol/webrtc_video_encoder_wrapper.cc
@@ -22,6 +22,7 @@
 #include "remoting/codec/webrtc_video_encoder_vpx.h"
 #include "remoting/protocol/video_channel_state_observer.h"
 #include "remoting/protocol/webrtc_video_frame_adapter.h"
+#include "third_party/webrtc/api/video_codecs/sdp_video_format.h"
 #include "third_party/webrtc/api/video_codecs/vp9_profile.h"
 #include "third_party/webrtc/modules/desktop_capture/desktop_frame.h"
 #include "third_party/webrtc/modules/video_coding/include/video_codec_interface.h"
@@ -101,9 +102,10 @@
       encoder_ = WebrtcVideoEncoderVpx::CreateForVP8();
       break;
     case webrtc::kVideoCodecVP9: {
-      const auto iter = format.parameters.find(webrtc::kVP9FmtpProfileId);
-      bool lossless_color =
-          iter != format.parameters.end() && iter->second == "1";
+      absl::optional<webrtc::VP9Profile> profile =
+          webrtc::ParseSdpForVP9Profile(format.parameters);
+      bool lossless_color = profile.has_value() &&
+                            profile.value() == webrtc::VP9Profile::kProfile1;
       VLOG(0) << "Creating VP9 encoder, lossless_color="
               << (lossless_color ? "true" : "false");
       encoder_ = WebrtcVideoEncoderVpx::CreateForVP9();
@@ -399,11 +401,8 @@
     NOTREACHED();
 #endif
   } else if (frame.codec == webrtc::kVideoCodecAV1) {
-#if defined(USE_AV1_ENCODER)
     // TODO(joedow): Set codec specific params for AV1 here.
-#else
-    NOTREACHED();
-#endif
+    NOTIMPLEMENTED();
   } else {
     NOTREACHED();
   }
diff --git a/sandbox/policy/BUILD.gn b/sandbox/policy/BUILD.gn
index 9a9c8714..6b3f411 100644
--- a/sandbox/policy/BUILD.gn
+++ b/sandbox/policy/BUILD.gn
@@ -155,6 +155,7 @@
       "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.buildinfo",
       "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.camera3",
       "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.fonts",
+      "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.hwinfo",
       "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.intl",
       "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.logger",
 
diff --git a/sandbox/policy/fuchsia/sandbox_policy_fuchsia.cc b/sandbox/policy/fuchsia/sandbox_policy_fuchsia.cc
index 36169b3..3f5cbb9 100644
--- a/sandbox/policy/fuchsia/sandbox_policy_fuchsia.cc
+++ b/sandbox/policy/fuchsia/sandbox_policy_fuchsia.cc
@@ -12,6 +12,7 @@
 #include <fuchsia/buildinfo/cpp/fidl.h>
 #include <fuchsia/camera3/cpp/fidl.h>
 #include <fuchsia/fonts/cpp/fidl.h>
+#include <fuchsia/hwinfo/cpp/fidl.h>
 #include <fuchsia/intl/cpp/fidl.h>
 #include <fuchsia/logger/cpp/fidl.h>
 #include <fuchsia/media/cpp/fidl.h>
@@ -74,8 +75,11 @@
 // clang-format off
 constexpr auto kMinimalServices = base::make_span((const char* const[]){
     // TODO(crbug.com/1286960): Remove this and/or intl below if an alternative
-    // solution does not require access to the service in all processes.
+    // solution does not require access to the service in all processes. For now
+    // these services are made available everywhere because they are required by
+    // base::SysInfo.
     fuchsia::buildinfo::Provider::Name_,
+    fuchsia::hwinfo::Product::Name_,
 
 // DebugData service is needed only for profiling.
 #if BUILDFLAG(CLANG_PROFILING)
diff --git a/services/data_decoder/public/cpp/data_decoder.cc b/services/data_decoder/public/cpp/data_decoder.cc
index 762b3e3..cdb14344 100644
--- a/services/data_decoder/public/cpp/data_decoder.cc
+++ b/services/data_decoder/public/cpp/data_decoder.cc
@@ -43,8 +43,9 @@
 template <typename T, typename V>
 class ValueParseRequest : public base::RefCounted<ValueParseRequest<T, V>> {
  public:
-  explicit ValueParseRequest(DataDecoder::ResultCallback<V> callback)
-      : callback_(std::move(callback)) {}
+  ValueParseRequest(DataDecoder::ResultCallback<V> callback,
+                    scoped_refptr<DataDecoder::CancellationFlag> is_cancelled)
+      : callback_(std::move(callback)), is_cancelled_(is_cancelled) {}
 
   ValueParseRequest(const ValueParseRequest&) = delete;
   ValueParseRequest& operator=(const ValueParseRequest&) = delete;
@@ -68,7 +69,7 @@
   // Handles a successful parse from the service.
   void OnServiceValueOrError(absl::optional<V> value,
                              const absl::optional<std::string>& error) {
-    if (!callback())
+    if (!callback() || is_cancelled_->data)
       return;
 
     DataDecoder::ResultOrError<V> result;
@@ -96,6 +97,9 @@
   ~ValueParseRequest() = default;
 
   void OnRemoteDisconnected() {
+    if (is_cancelled_->data)
+      return;
+
     if (callback()) {
       std::move(callback())
           .Run(DataDecoder::ResultOrError<V>::Error(
@@ -105,6 +109,7 @@
 
   mojo::Remote<T> remote_;
   DataDecoder::ResultCallback<V> callback_;
+  scoped_refptr<DataDecoder::CancellationFlag> is_cancelled_;
 };
 
 #if BUILDFLAG(IS_IOS)
@@ -126,8 +131,12 @@
 
 #if BUILDFLAG(IS_ANDROID) || BUILDFLAG(BUILD_RUST_JSON_PARSER)
 
-void ParsingComplete(DataDecoder::ValueParseCallback callback,
+void ParsingComplete(scoped_refptr<DataDecoder::CancellationFlag> is_cancelled,
+                     DataDecoder::ValueParseCallback callback,
                      base::JSONReader::ValueWithError value_with_error) {
+  if (is_cancelled->data)
+    return;
+
   if (!value_with_error.value) {
     std::move(callback).Run(
         DataDecoder::ValueOrError::Error(value_with_error.error_message));
@@ -141,12 +150,15 @@
 
 }  // namespace
 
-DataDecoder::DataDecoder() : idle_timeout_(kServiceProcessIdleTimeoutDefault) {}
+DataDecoder::DataDecoder() : DataDecoder(kServiceProcessIdleTimeoutDefault) {}
 
 DataDecoder::DataDecoder(base::TimeDelta idle_timeout)
-    : idle_timeout_(idle_timeout) {}
+    : idle_timeout_(idle_timeout),
+      cancel_requests_(new CancellationFlag(false)) {}
 
-DataDecoder::~DataDecoder() = default;
+DataDecoder::~DataDecoder() {
+  cancel_requests_->data = true;
+}
 
 mojom::DataDecoderService* DataDecoder::GetService() {
   // Lazily start an instance of the service if possible and necessary.
@@ -184,13 +196,18 @@
                 json, base::JSON_PARSE_RFC);
           },
           json),
-      base::BindOnce(&ParsingComplete, std::move(callback)));
+      base::BindOnce(&ParsingComplete, cancel_requests_, std::move(callback)));
 #elif BUILDFLAG(IS_ANDROID)
   // For Android, if the full Rust parser is not available, we use the
   // in-process sanitizer and then parse in-process.
   JsonSanitizer::Sanitize(
       json, base::BindOnce(
-                [](ValueParseCallback callback, JsonSanitizer::Result result) {
+                [](ValueParseCallback callback,
+                   scoped_refptr<CancellationFlag> is_cancelled,
+                   JsonSanitizer::Result result) {
+                  if (is_cancelled->data)
+                    return;
+
                   if (!result.value) {
                     std::move(callback).Run(ValueOrError::Error(*result.error));
                     return;
@@ -199,15 +216,15 @@
                   base::JSONReader::ValueWithError value_with_error =
                       base::JSONReader::ReadAndReturnValueWithError(
                           *result.value, base::JSON_PARSE_RFC);
-                  ParsingComplete(std::move(callback),
+                  ParsingComplete(is_cancelled, std::move(callback),
                                   std::move(value_with_error));
                 },
-                std::move(callback)));
+                std::move(callback), cancel_requests_));
 #else
   // Parse JSON out-of-process.
   auto request =
       base::MakeRefCounted<ValueParseRequest<mojom::JsonParser, base::Value>>(
-          std::move(callback));
+          std::move(callback), cancel_requests_);
   GetService()->BindJsonParser(request->BindRemote());
   request->remote()->Parse(
       json, base::JSON_PARSE_RFC,
@@ -240,7 +257,7 @@
     ValueParseCallback callback) {
   auto request =
       base::MakeRefCounted<ValueParseRequest<mojom::XmlParser, base::Value>>(
-          std::move(callback));
+          std::move(callback), cancel_requests_);
   GetService()->BindXmlParser(request->BindRemote());
   request->remote()->Parse(
       xml, whitespace_behavior,
@@ -273,7 +290,7 @@
                           GzipperCallback callback) {
   auto request = base::MakeRefCounted<
       ValueParseRequest<mojom::Gzipper, mojo_base::BigBuffer>>(
-      std::move(callback));
+      std::move(callback), cancel_requests_);
   GetService()->BindGzipper(request->BindRemote());
   request->remote()->Deflate(
       data,
@@ -287,7 +304,7 @@
                           GzipperCallback callback) {
   auto request = base::MakeRefCounted<
       ValueParseRequest<mojom::Gzipper, mojo_base::BigBuffer>>(
-      std::move(callback));
+      std::move(callback), cancel_requests_);
   GetService()->BindGzipper(request->BindRemote());
   request->remote()->Inflate(
       data, max_uncompressed_size,
@@ -300,7 +317,7 @@
                                GzipperCallback callback) {
   auto request = base::MakeRefCounted<
       ValueParseRequest<mojom::Gzipper, mojo_base::BigBuffer>>(
-      std::move(callback));
+      std::move(callback), cancel_requests_);
   GetService()->BindGzipper(request->BindRemote());
   request->remote()->Compress(
       data,
@@ -313,7 +330,7 @@
                                  GzipperCallback callback) {
   auto request = base::MakeRefCounted<
       ValueParseRequest<mojom::Gzipper, mojo_base::BigBuffer>>(
-      std::move(callback));
+      std::move(callback), cancel_requests_);
   GetService()->BindGzipper(request->BindRemote());
   request->remote()->Uncompress(
       data,
diff --git a/services/data_decoder/public/cpp/data_decoder.h b/services/data_decoder/public/cpp/data_decoder.h
index 0b25df9..cea5b7f8 100644
--- a/services/data_decoder/public/cpp/data_decoder.h
+++ b/services/data_decoder/public/cpp/data_decoder.h
@@ -8,6 +8,7 @@
 #include <string>
 
 #include "base/callback_forward.h"
+#include "base/memory/ref_counted.h"
 #include "base/time/time.h"
 #include "base/values.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
@@ -89,6 +90,8 @@
   using GzipperCallback =
       base::OnceCallback<void(ResultOrError<mojo_base::BigBuffer>)>;
 
+  using CancellationFlag = base::RefCountedData<bool>;
+
   // Returns a raw interface to the service instance. This launches an instance
   // of the service process if possible on the current platform, or returns a
   // connection to the in-process instance of in a test environment using
@@ -171,6 +174,13 @@
   // This instance's connection to the service. This connection is lazily
   // established and may be reset after long periods of idle time.
   mojo::Remote<mojom::DataDecoderService> service_;
+
+  // Cancellation flag for any outstanding requests. When a request is
+  // started, it takes a reference to this flag. Upon the destruction of this
+  // instance, the flag is set to `true`. Any outstanding requests should check
+  // this flag, and if it is `true`, they should not run the callback, per
+  // the API guarantees above.
+  scoped_refptr<CancellationFlag> cancel_requests_;
 };
 
 }  // namespace data_decoder
diff --git a/storage/browser/quota/OWNERS b/storage/browser/quota/OWNERS
index 2d495dc6..22981a3 100644
--- a/storage/browser/quota/OWNERS
+++ b/storage/browser/quota/OWNERS
@@ -3,6 +3,7 @@
 
 # Secondary
 asully@chromium.org
+estade@chromium.org
 jarrydg@chromium.org
 jsbell@chromium.org
 
diff --git a/testing/buildbot/chrome.json b/testing/buildbot/chrome.json
index 6064b795..2f5520fe 100644
--- a/testing/buildbot/chrome.json
+++ b/testing/buildbot/chrome.json
@@ -1943,7 +1943,7 @@
       {
         "args": [],
         "cros_board": "atlas",
-        "cros_img": "atlas-release/R102-14695.68.0",
+        "cros_img": "atlas-release/R103-14816.25.0",
         "name": "lacros_all_tast_tests ATLAS_RELEASE_BETA",
         "resultdb": {
           "enable": true,
@@ -1991,7 +1991,7 @@
       {
         "args": [],
         "cros_board": "eve",
-        "cros_img": "eve-release/R102-14695.55.0",
+        "cros_img": "eve-release/R103-14816.25.0",
         "name": "lacros_all_tast_tests EVE_RELEASE_BETA",
         "resultdb": {
           "enable": true,
@@ -2084,7 +2084,7 @@
       {
         "args": [],
         "cros_board": "hana",
-        "cros_img": "hana-release/R102-14695.68.0",
+        "cros_img": "hana-release/R103-14816.25.0",
         "name": "lacros_all_tast_tests HANA_RELEASE_BETA",
         "resultdb": {
           "enable": true,
@@ -2132,7 +2132,7 @@
       {
         "args": [],
         "cros_board": "jacuzzi",
-        "cros_img": "jacuzzi-release/R102-14695.68.0",
+        "cros_img": "jacuzzi-release/R103-14816.25.0",
         "name": "lacros_all_tast_tests JACUZZI_RELEASE_BETA",
         "resultdb": {
           "enable": true,
@@ -2184,7 +2184,7 @@
           "--test-launcher-filter-file=../../testing/buildbot/filters/lacros-arm.ozone_unittests.filter"
         ],
         "cros_board": "hana",
-        "cros_img": "hana-release/R102-14695.68.0",
+        "cros_img": "hana-release/R103-14816.25.0",
         "name": "ozone_unittests HANA_RELEASE_BETA",
         "resultdb": {
           "enable": true,
@@ -2235,7 +2235,7 @@
           "--test-launcher-filter-file=../../testing/buildbot/filters/lacros-arm.ozone_unittests.filter"
         ],
         "cros_board": "jacuzzi",
-        "cros_img": "jacuzzi-release/R102-14695.68.0",
+        "cros_img": "jacuzzi-release/R103-14816.25.0",
         "name": "ozone_unittests JACUZZI_RELEASE_BETA",
         "resultdb": {
           "enable": true,
@@ -2286,7 +2286,7 @@
           "--test-launcher-filter-file=../../testing/buildbot/filters/lacros-arm.viz_unittests.filter"
         ],
         "cros_board": "hana",
-        "cros_img": "hana-release/R102-14695.68.0",
+        "cros_img": "hana-release/R103-14816.25.0",
         "name": "viz_unittests HANA_RELEASE_BETA",
         "resultdb": {
           "enable": true,
@@ -2337,7 +2337,7 @@
           "--test-launcher-filter-file=../../testing/buildbot/filters/lacros-arm.viz_unittests.filter"
         ],
         "cros_board": "jacuzzi",
-        "cros_img": "jacuzzi-release/R102-14695.68.0",
+        "cros_img": "jacuzzi-release/R103-14816.25.0",
         "name": "viz_unittests JACUZZI_RELEASE_BETA",
         "resultdb": {
           "enable": true,
diff --git a/testing/buildbot/chromium.android.fyi.json b/testing/buildbot/chromium.android.fyi.json
index fbc26c5..85009ce7 100644
--- a/testing/buildbot/chromium.android.fyi.json
+++ b/testing/buildbot/chromium.android.fyi.json
@@ -8240,15 +8240,15 @@
       {
         "args": [
           "--additional-apk=apks/WebLayerShellSystemWebView.apk",
+          "--webview-apk-path=apks/SystemWebView.apk",
           "--test-runner-outdir",
           ".",
+          "--client-outdir",
+          "../../weblayer_instrumentation_test_M102/out/Release",
           "--implementation-outdir",
           ".",
           "--test-expectations",
           "../../weblayer/browser/android/javatests/skew/expectations.txt",
-          "--webview-apk-path=apks/SystemWebView.apk",
-          "--client-outdir",
-          "../../weblayer_instrumentation_test_M102/out/Release",
           "--client-version=102",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
@@ -8274,7 +8274,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M102",
-              "revision": "version:102.0.5005.75"
+              "revision": "version:102.0.5005.76"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -8750,15 +8750,15 @@
       {
         "args": [
           "--additional-apk=apks/WebLayerShellSystemWebView.apk",
+          "--webview-apk-path=apks/AOSP_SystemWebView.apk",
           "--test-runner-outdir",
           ".",
           "--client-outdir",
           ".",
-          "--test-expectations",
-          "../../weblayer/browser/android/javatests/skew/expectations.txt",
-          "--webview-apk-path=apks/AOSP_SystemWebView.apk",
           "--implementation-outdir",
           "../../weblayer_instrumentation_test_M102/out/Release",
+          "--test-expectations",
+          "../../weblayer/browser/android/javatests/skew/expectations.txt",
           "--impl-version=102",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
@@ -8784,7 +8784,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M102",
-              "revision": "version:102.0.5005.75"
+              "revision": "version:102.0.5005.76"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
diff --git a/testing/buildbot/chromium.android.json b/testing/buildbot/chromium.android.json
index 1876a04..fc4bf367 100644
--- a/testing/buildbot/chromium.android.json
+++ b/testing/buildbot/chromium.android.json
@@ -46214,15 +46214,15 @@
       {
         "args": [
           "--additional-apk=apks/WebLayerShellSystemWebView.apk",
+          "--webview-apk-path=apks/SystemWebView.apk",
           "--test-runner-outdir",
           ".",
+          "--client-outdir",
+          "../../weblayer_instrumentation_test_M102/out/Release",
           "--implementation-outdir",
           ".",
           "--test-expectations",
           "../../weblayer/browser/android/javatests/skew/expectations.txt",
-          "--webview-apk-path=apks/SystemWebView.apk",
-          "--client-outdir",
-          "../../weblayer_instrumentation_test_M102/out/Release",
           "--client-version=102",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
@@ -46248,7 +46248,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M102",
-              "revision": "version:102.0.5005.75"
+              "revision": "version:102.0.5005.76"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -46724,15 +46724,15 @@
       {
         "args": [
           "--additional-apk=apks/WebLayerShellSystemWebView.apk",
+          "--webview-apk-path=apks/AOSP_SystemWebView.apk",
           "--test-runner-outdir",
           ".",
           "--client-outdir",
           ".",
-          "--test-expectations",
-          "../../weblayer/browser/android/javatests/skew/expectations.txt",
-          "--webview-apk-path=apks/AOSP_SystemWebView.apk",
           "--implementation-outdir",
           "../../weblayer_instrumentation_test_M102/out/Release",
+          "--test-expectations",
+          "../../weblayer/browser/android/javatests/skew/expectations.txt",
           "--impl-version=102",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
@@ -46758,7 +46758,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M102",
-              "revision": "version:102.0.5005.75"
+              "revision": "version:102.0.5005.76"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -47238,15 +47238,15 @@
       {
         "args": [
           "--additional-apk=apks/ChromePublic.apk",
+          "--webview-apk-path=apks/SystemWebView.apk",
           "--test-runner-outdir",
           ".",
+          "--client-outdir",
+          "../../weblayer_instrumentation_test_M102/out/Release",
           "--implementation-outdir",
           ".",
           "--test-expectations",
           "../../weblayer/browser/android/javatests/skew/expectations.txt",
-          "--webview-apk-path=apks/SystemWebView.apk",
-          "--client-outdir",
-          "../../weblayer_instrumentation_test_M102/out/Release",
           "--client-version=102",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
@@ -47272,7 +47272,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M102",
-              "revision": "version:102.0.5005.75"
+              "revision": "version:102.0.5005.76"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -47748,15 +47748,15 @@
       {
         "args": [
           "--additional-apk=apks/ChromePublic.apk",
+          "--webview-apk-path=apks/AOSP_SystemWebView.apk",
           "--test-runner-outdir",
           ".",
           "--client-outdir",
           ".",
-          "--test-expectations",
-          "../../weblayer/browser/android/javatests/skew/expectations.txt",
-          "--webview-apk-path=apks/AOSP_SystemWebView.apk",
           "--implementation-outdir",
           "../../weblayer_instrumentation_test_M102/out/Release",
+          "--test-expectations",
+          "../../weblayer/browser/android/javatests/skew/expectations.txt",
           "--impl-version=102",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
@@ -47782,7 +47782,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M102",
-              "revision": "version:102.0.5005.75"
+              "revision": "version:102.0.5005.76"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -48330,15 +48330,15 @@
       {
         "args": [
           "--additional-apk=apks/WebLayerShellSystemWebView.apk",
+          "--webview-apk-path=apks/SystemWebView.apk",
           "--test-runner-outdir",
           ".",
+          "--client-outdir",
+          "../../weblayer_instrumentation_test_M102/out/Release",
           "--implementation-outdir",
           ".",
           "--test-expectations",
           "../../weblayer/browser/android/javatests/skew/expectations.txt",
-          "--webview-apk-path=apks/SystemWebView.apk",
-          "--client-outdir",
-          "../../weblayer_instrumentation_test_M102/out/Release",
           "--client-version=102",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
@@ -48364,7 +48364,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M102",
-              "revision": "version:102.0.5005.75"
+              "revision": "version:102.0.5005.76"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -48840,15 +48840,15 @@
       {
         "args": [
           "--additional-apk=apks/WebLayerShellSystemWebView.apk",
+          "--webview-apk-path=apks/SystemWebView.apk",
           "--test-runner-outdir",
           ".",
           "--client-outdir",
           ".",
-          "--test-expectations",
-          "../../weblayer/browser/android/javatests/skew/expectations.txt",
-          "--webview-apk-path=apks/SystemWebView.apk",
           "--implementation-outdir",
           "../../weblayer_instrumentation_test_M102/out/Release",
+          "--test-expectations",
+          "../../weblayer/browser/android/javatests/skew/expectations.txt",
           "--impl-version=102",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
@@ -48874,7 +48874,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M102",
-              "revision": "version:102.0.5005.75"
+              "revision": "version:102.0.5005.76"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -49422,15 +49422,15 @@
       {
         "args": [
           "--additional-apk=apks/WebLayerShellSystemWebView.apk",
+          "--webview-apk-path=apks/SystemWebView.apk",
           "--test-runner-outdir",
           ".",
+          "--client-outdir",
+          "../../weblayer_instrumentation_test_M102/out/Release",
           "--implementation-outdir",
           ".",
           "--test-expectations",
           "../../weblayer/browser/android/javatests/skew/expectations.txt",
-          "--webview-apk-path=apks/SystemWebView.apk",
-          "--client-outdir",
-          "../../weblayer_instrumentation_test_M102/out/Release",
           "--client-version=102",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
@@ -49456,7 +49456,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M102",
-              "revision": "version:102.0.5005.75"
+              "revision": "version:102.0.5005.76"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -49932,15 +49932,15 @@
       {
         "args": [
           "--additional-apk=apks/WebLayerShellSystemWebView.apk",
+          "--webview-apk-path=apks/SystemWebView.apk",
           "--test-runner-outdir",
           ".",
           "--client-outdir",
           ".",
-          "--test-expectations",
-          "../../weblayer/browser/android/javatests/skew/expectations.txt",
-          "--webview-apk-path=apks/SystemWebView.apk",
           "--implementation-outdir",
           "../../weblayer_instrumentation_test_M102/out/Release",
+          "--test-expectations",
+          "../../weblayer/browser/android/javatests/skew/expectations.txt",
           "--impl-version=102",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
@@ -49966,7 +49966,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M102",
-              "revision": "version:102.0.5005.75"
+              "revision": "version:102.0.5005.76"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
diff --git a/testing/buildbot/chromium.chromiumos.json b/testing/buildbot/chromium.chromiumos.json
index bf7f9c2..8b37cc2 100644
--- a/testing/buildbot/chromium.chromiumos.json
+++ b/testing/buildbot/chromium.chromiumos.json
@@ -5801,38 +5801,6 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v102.0.5005.56/test_ash_chrome"
-        ],
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "interactive_ui_tests Lacros version skew testing ash 102.0.5005.56",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v102.0.5005.56",
-              "revision": "version:102.0.5005.56"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-18.04"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 3
-        },
-        "test": "interactive_ui_tests",
-        "test_id_prefix": "ninja://chrome/test:interactive_ui_tests/",
-        "variant_id": "Lacros version skew testing ash 102.0.5005.56"
-      },
-      {
-        "args": [
-          "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter",
           "--ash-chrome-path-override=../../lacros_version_skew_tests_v103.0.5060.15/test_ash_chrome"
         ],
         "isolate_profile_data": true,
@@ -5865,6 +5833,38 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter",
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v103.0.5060.22/test_ash_chrome"
+        ],
+        "isolate_profile_data": true,
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "interactive_ui_tests Lacros version skew testing ash 103.0.5060.22",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
+              "location": "lacros_version_skew_tests_v103.0.5060.22",
+              "revision": "version:103.0.5060.22"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-18.04"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 3
+        },
+        "test": "interactive_ui_tests",
+        "test_id_prefix": "ninja://chrome/test:interactive_ui_tests/",
+        "variant_id": "Lacros version skew testing ash 103.0.5060.22"
+      },
+      {
+        "args": [
+          "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter",
           "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5085.0/test_ash_chrome"
         ],
         "isolate_profile_data": true,
@@ -5968,37 +5968,6 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v102.0.5005.56/test_ash_chrome"
-        ],
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "lacros_chrome_browsertests Lacros version skew testing ash 102.0.5005.56",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v102.0.5005.56",
-              "revision": "version:102.0.5005.56"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-18.04"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "lacros_chrome_browsertests",
-        "test_id_prefix": "ninja://chrome/test:lacros_chrome_browsertests/",
-        "variant_id": "Lacros version skew testing ash 102.0.5005.56"
-      },
-      {
-        "args": [
-          "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
           "--ash-chrome-path-override=../../lacros_version_skew_tests_v103.0.5060.15/test_ash_chrome"
         ],
         "isolate_profile_data": true,
@@ -6030,6 +5999,37 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v103.0.5060.22/test_ash_chrome"
+        ],
+        "isolate_profile_data": true,
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "lacros_chrome_browsertests Lacros version skew testing ash 103.0.5060.22",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
+              "location": "lacros_version_skew_tests_v103.0.5060.22",
+              "revision": "version:103.0.5060.22"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-18.04"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "lacros_chrome_browsertests",
+        "test_id_prefix": "ninja://chrome/test:lacros_chrome_browsertests/",
+        "variant_id": "Lacros version skew testing ash 103.0.5060.22"
+      },
+      {
+        "args": [
+          "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
           "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5085.0/test_ash_chrome"
         ],
         "isolate_profile_data": true,
@@ -6114,37 +6114,6 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v102.0.5005.56/test_ash_chrome"
-        ],
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "lacros_chrome_browsertests_run_in_series Lacros version skew testing ash 102.0.5005.56",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v102.0.5005.56",
-              "revision": "version:102.0.5005.56"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-18.04"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "lacros_chrome_browsertests_run_in_series",
-        "test_id_prefix": "ninja://chrome/test:lacros_chrome_browsertests_run_in_series/",
-        "variant_id": "Lacros version skew testing ash 102.0.5005.56"
-      },
-      {
-        "args": [
-          "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
           "--ash-chrome-path-override=../../lacros_version_skew_tests_v103.0.5060.15/test_ash_chrome"
         ],
         "isolate_profile_data": true,
@@ -6176,6 +6145,37 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v103.0.5060.22/test_ash_chrome"
+        ],
+        "isolate_profile_data": true,
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "lacros_chrome_browsertests_run_in_series Lacros version skew testing ash 103.0.5060.22",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
+              "location": "lacros_version_skew_tests_v103.0.5060.22",
+              "revision": "version:103.0.5060.22"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-18.04"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "lacros_chrome_browsertests_run_in_series",
+        "test_id_prefix": "ninja://chrome/test:lacros_chrome_browsertests_run_in_series/",
+        "variant_id": "Lacros version skew testing ash 103.0.5060.22"
+      },
+      {
+        "args": [
+          "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
           "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5085.0/test_ash_chrome"
         ],
         "isolate_profile_data": true,
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json
index 488f28d..9b98eb7 100644
--- a/testing/buildbot/chromium.fyi.json
+++ b/testing/buildbot/chromium.fyi.json
@@ -87983,33 +87983,6 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v102.0.5005.56/test_ash_chrome"
-        ],
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "interactive_ui_tests Lacros version skew testing ash 102.0.5005.56",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v102.0.5005.56",
-              "revision": "version:102.0.5005.56"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 3
-        },
-        "test": "interactive_ui_tests",
-        "test_id_prefix": "ninja://chrome/test:interactive_ui_tests/",
-        "variant_id": "Lacros version skew testing ash 102.0.5005.56"
-      },
-      {
-        "args": [
-          "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter",
           "--ash-chrome-path-override=../../lacros_version_skew_tests_v103.0.5060.15/test_ash_chrome"
         ],
         "isolate_profile_data": true,
@@ -88037,6 +88010,33 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter",
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v103.0.5060.22/test_ash_chrome"
+        ],
+        "isolate_profile_data": true,
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "interactive_ui_tests Lacros version skew testing ash 103.0.5060.22",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
+              "location": "lacros_version_skew_tests_v103.0.5060.22",
+              "revision": "version:103.0.5060.22"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 3
+        },
+        "test": "interactive_ui_tests",
+        "test_id_prefix": "ninja://chrome/test:interactive_ui_tests/",
+        "variant_id": "Lacros version skew testing ash 103.0.5060.22"
+      },
+      {
+        "args": [
+          "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter",
           "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5085.0/test_ash_chrome"
         ],
         "isolate_profile_data": true,
@@ -88120,32 +88120,6 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v102.0.5005.56/test_ash_chrome"
-        ],
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "lacros_chrome_browsertests Lacros version skew testing ash 102.0.5005.56",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v102.0.5005.56",
-              "revision": "version:102.0.5005.56"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "lacros_chrome_browsertests",
-        "test_id_prefix": "ninja://chrome/test:lacros_chrome_browsertests/",
-        "variant_id": "Lacros version skew testing ash 102.0.5005.56"
-      },
-      {
-        "args": [
-          "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
           "--ash-chrome-path-override=../../lacros_version_skew_tests_v103.0.5060.15/test_ash_chrome"
         ],
         "isolate_profile_data": true,
@@ -88172,6 +88146,32 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v103.0.5060.22/test_ash_chrome"
+        ],
+        "isolate_profile_data": true,
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "lacros_chrome_browsertests Lacros version skew testing ash 103.0.5060.22",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
+              "location": "lacros_version_skew_tests_v103.0.5060.22",
+              "revision": "version:103.0.5060.22"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "lacros_chrome_browsertests",
+        "test_id_prefix": "ninja://chrome/test:lacros_chrome_browsertests/",
+        "variant_id": "Lacros version skew testing ash 103.0.5060.22"
+      },
+      {
+        "args": [
+          "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
           "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5085.0/test_ash_chrome"
         ],
         "isolate_profile_data": true,
@@ -88241,32 +88241,6 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v102.0.5005.56/test_ash_chrome"
-        ],
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "lacros_chrome_browsertests_run_in_series Lacros version skew testing ash 102.0.5005.56",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v102.0.5005.56",
-              "revision": "version:102.0.5005.56"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "lacros_chrome_browsertests_run_in_series",
-        "test_id_prefix": "ninja://chrome/test:lacros_chrome_browsertests_run_in_series/",
-        "variant_id": "Lacros version skew testing ash 102.0.5005.56"
-      },
-      {
-        "args": [
-          "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
           "--ash-chrome-path-override=../../lacros_version_skew_tests_v103.0.5060.15/test_ash_chrome"
         ],
         "isolate_profile_data": true,
@@ -88293,6 +88267,32 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v103.0.5060.22/test_ash_chrome"
+        ],
+        "isolate_profile_data": true,
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "lacros_chrome_browsertests_run_in_series Lacros version skew testing ash 103.0.5060.22",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
+              "location": "lacros_version_skew_tests_v103.0.5060.22",
+              "revision": "version:103.0.5060.22"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "lacros_chrome_browsertests_run_in_series",
+        "test_id_prefix": "ninja://chrome/test:lacros_chrome_browsertests_run_in_series/",
+        "variant_id": "Lacros version skew testing ash 103.0.5060.22"
+      },
+      {
+        "args": [
+          "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
           "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5085.0/test_ash_chrome"
         ],
         "isolate_profile_data": true,
@@ -89588,38 +89588,6 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v102.0.5005.56/test_ash_chrome"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "interactive_ui_tests Lacros version skew testing ash 102.0.5005.56",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v102.0.5005.56",
-              "revision": "version:102.0.5005.56"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-18.04",
-              "ssd": "0"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 3
-        },
-        "test": "interactive_ui_tests",
-        "test_id_prefix": "ninja://chrome/test:interactive_ui_tests/",
-        "variant_id": "Lacros version skew testing ash 102.0.5005.56"
-      },
-      {
-        "args": [
-          "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter",
           "--ash-chrome-path-override=../../lacros_version_skew_tests_v103.0.5060.15/test_ash_chrome"
         ],
         "merge": {
@@ -89652,6 +89620,38 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter",
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v103.0.5060.22/test_ash_chrome"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "interactive_ui_tests Lacros version skew testing ash 103.0.5060.22",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
+              "location": "lacros_version_skew_tests_v103.0.5060.22",
+              "revision": "version:103.0.5060.22"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-18.04",
+              "ssd": "0"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 3
+        },
+        "test": "interactive_ui_tests",
+        "test_id_prefix": "ninja://chrome/test:interactive_ui_tests/",
+        "variant_id": "Lacros version skew testing ash 103.0.5060.22"
+      },
+      {
+        "args": [
+          "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter",
           "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5085.0/test_ash_chrome"
         ],
         "merge": {
@@ -89755,37 +89755,6 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v102.0.5005.56/test_ash_chrome"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "lacros_chrome_browsertests Lacros version skew testing ash 102.0.5005.56",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v102.0.5005.56",
-              "revision": "version:102.0.5005.56"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-18.04",
-              "ssd": "0"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "lacros_chrome_browsertests",
-        "test_id_prefix": "ninja://chrome/test:lacros_chrome_browsertests/",
-        "variant_id": "Lacros version skew testing ash 102.0.5005.56"
-      },
-      {
-        "args": [
-          "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
           "--ash-chrome-path-override=../../lacros_version_skew_tests_v103.0.5060.15/test_ash_chrome"
         ],
         "merge": {
@@ -89817,6 +89786,37 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v103.0.5060.22/test_ash_chrome"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "lacros_chrome_browsertests Lacros version skew testing ash 103.0.5060.22",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
+              "location": "lacros_version_skew_tests_v103.0.5060.22",
+              "revision": "version:103.0.5060.22"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-18.04",
+              "ssd": "0"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "lacros_chrome_browsertests",
+        "test_id_prefix": "ninja://chrome/test:lacros_chrome_browsertests/",
+        "variant_id": "Lacros version skew testing ash 103.0.5060.22"
+      },
+      {
+        "args": [
+          "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
           "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5085.0/test_ash_chrome"
         ],
         "merge": {
@@ -89901,37 +89901,6 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v102.0.5005.56/test_ash_chrome"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "lacros_chrome_browsertests_run_in_series Lacros version skew testing ash 102.0.5005.56",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v102.0.5005.56",
-              "revision": "version:102.0.5005.56"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-18.04",
-              "ssd": "0"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "lacros_chrome_browsertests_run_in_series",
-        "test_id_prefix": "ninja://chrome/test:lacros_chrome_browsertests_run_in_series/",
-        "variant_id": "Lacros version skew testing ash 102.0.5005.56"
-      },
-      {
-        "args": [
-          "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
           "--ash-chrome-path-override=../../lacros_version_skew_tests_v103.0.5060.15/test_ash_chrome"
         ],
         "merge": {
@@ -89963,6 +89932,37 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v103.0.5060.22/test_ash_chrome"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "lacros_chrome_browsertests_run_in_series Lacros version skew testing ash 103.0.5060.22",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
+              "location": "lacros_version_skew_tests_v103.0.5060.22",
+              "revision": "version:103.0.5060.22"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-18.04",
+              "ssd": "0"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "lacros_chrome_browsertests_run_in_series",
+        "test_id_prefix": "ninja://chrome/test:lacros_chrome_browsertests_run_in_series/",
+        "variant_id": "Lacros version skew testing ash 103.0.5060.22"
+      },
+      {
+        "args": [
+          "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
           "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5085.0/test_ash_chrome"
         ],
         "merge": {
@@ -91421,38 +91421,6 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v102.0.5005.56/test_ash_chrome"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "interactive_ui_tests Lacros version skew testing ash 102.0.5005.56",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v102.0.5005.56",
-              "revision": "version:102.0.5005.56"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-18.04",
-              "ssd": "0"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 3
-        },
-        "test": "interactive_ui_tests",
-        "test_id_prefix": "ninja://chrome/test:interactive_ui_tests/",
-        "variant_id": "Lacros version skew testing ash 102.0.5005.56"
-      },
-      {
-        "args": [
-          "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter",
           "--ash-chrome-path-override=../../lacros_version_skew_tests_v103.0.5060.15/test_ash_chrome"
         ],
         "merge": {
@@ -91485,6 +91453,38 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter",
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v103.0.5060.22/test_ash_chrome"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "interactive_ui_tests Lacros version skew testing ash 103.0.5060.22",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
+              "location": "lacros_version_skew_tests_v103.0.5060.22",
+              "revision": "version:103.0.5060.22"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-18.04",
+              "ssd": "0"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 3
+        },
+        "test": "interactive_ui_tests",
+        "test_id_prefix": "ninja://chrome/test:interactive_ui_tests/",
+        "variant_id": "Lacros version skew testing ash 103.0.5060.22"
+      },
+      {
+        "args": [
+          "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter",
           "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5085.0/test_ash_chrome"
         ],
         "merge": {
@@ -91588,37 +91588,6 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v102.0.5005.56/test_ash_chrome"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "lacros_chrome_browsertests Lacros version skew testing ash 102.0.5005.56",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v102.0.5005.56",
-              "revision": "version:102.0.5005.56"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-18.04",
-              "ssd": "0"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "lacros_chrome_browsertests",
-        "test_id_prefix": "ninja://chrome/test:lacros_chrome_browsertests/",
-        "variant_id": "Lacros version skew testing ash 102.0.5005.56"
-      },
-      {
-        "args": [
-          "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
           "--ash-chrome-path-override=../../lacros_version_skew_tests_v103.0.5060.15/test_ash_chrome"
         ],
         "merge": {
@@ -91650,6 +91619,37 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v103.0.5060.22/test_ash_chrome"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "lacros_chrome_browsertests Lacros version skew testing ash 103.0.5060.22",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
+              "location": "lacros_version_skew_tests_v103.0.5060.22",
+              "revision": "version:103.0.5060.22"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-18.04",
+              "ssd": "0"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "lacros_chrome_browsertests",
+        "test_id_prefix": "ninja://chrome/test:lacros_chrome_browsertests/",
+        "variant_id": "Lacros version skew testing ash 103.0.5060.22"
+      },
+      {
+        "args": [
+          "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
           "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5085.0/test_ash_chrome"
         ],
         "merge": {
@@ -91734,37 +91734,6 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v102.0.5005.56/test_ash_chrome"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "lacros_chrome_browsertests_run_in_series Lacros version skew testing ash 102.0.5005.56",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v102.0.5005.56",
-              "revision": "version:102.0.5005.56"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-18.04",
-              "ssd": "0"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "lacros_chrome_browsertests_run_in_series",
-        "test_id_prefix": "ninja://chrome/test:lacros_chrome_browsertests_run_in_series/",
-        "variant_id": "Lacros version skew testing ash 102.0.5005.56"
-      },
-      {
-        "args": [
-          "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
           "--ash-chrome-path-override=../../lacros_version_skew_tests_v103.0.5060.15/test_ash_chrome"
         ],
         "merge": {
@@ -91796,6 +91765,37 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v103.0.5060.22/test_ash_chrome"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "lacros_chrome_browsertests_run_in_series Lacros version skew testing ash 103.0.5060.22",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
+              "location": "lacros_version_skew_tests_v103.0.5060.22",
+              "revision": "version:103.0.5060.22"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-18.04",
+              "ssd": "0"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "lacros_chrome_browsertests_run_in_series",
+        "test_id_prefix": "ninja://chrome/test:lacros_chrome_browsertests_run_in_series/",
+        "variant_id": "Lacros version skew testing ash 103.0.5060.22"
+      },
+      {
+        "args": [
+          "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
           "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5085.0/test_ash_chrome"
         ],
         "merge": {
@@ -92495,37 +92495,6 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v102.0.5005.56/test_ash_chrome"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "interactive_ui_tests Lacros version skew testing ash 102.0.5005.56",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v102.0.5005.56",
-              "revision": "version:102.0.5005.56"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-18.04"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 3
-        },
-        "test": "interactive_ui_tests",
-        "test_id_prefix": "ninja://chrome/test:interactive_ui_tests/",
-        "variant_id": "Lacros version skew testing ash 102.0.5005.56"
-      },
-      {
-        "args": [
-          "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter",
           "--ash-chrome-path-override=../../lacros_version_skew_tests_v103.0.5060.15/test_ash_chrome"
         ],
         "merge": {
@@ -92557,6 +92526,37 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter",
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v103.0.5060.22/test_ash_chrome"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "interactive_ui_tests Lacros version skew testing ash 103.0.5060.22",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
+              "location": "lacros_version_skew_tests_v103.0.5060.22",
+              "revision": "version:103.0.5060.22"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-18.04"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 3
+        },
+        "test": "interactive_ui_tests",
+        "test_id_prefix": "ninja://chrome/test:interactive_ui_tests/",
+        "variant_id": "Lacros version skew testing ash 103.0.5060.22"
+      },
+      {
+        "args": [
+          "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter",
           "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5085.0/test_ash_chrome"
         ],
         "merge": {
diff --git a/testing/buildbot/internal.chromeos.fyi.json b/testing/buildbot/internal.chromeos.fyi.json
index aa54377..7e886592 100644
--- a/testing/buildbot/internal.chromeos.fyi.json
+++ b/testing/buildbot/internal.chromeos.fyi.json
@@ -1150,7 +1150,7 @@
       {
         "args": [],
         "cros_board": "octopus",
-        "cros_img": "octopus-release/R102-14695.68.0",
+        "cros_img": "octopus-release/R103-14816.25.0",
         "name": "lacros_fyi_tast_tests OCTOPUS_RELEASE_BETA",
         "swarming": {},
         "tast_expr": "(\"group:mainline\" && \"dep:lacros\" && !informational)",
@@ -1196,7 +1196,7 @@
       {
         "args": [],
         "cros_board": "octopus",
-        "cros_img": "octopus-release/R102-14695.68.0",
+        "cros_img": "octopus-release/R103-14816.25.0",
         "name": "ozone_unittests OCTOPUS_RELEASE_BETA",
         "swarming": {},
         "test": "ozone_unittests",
@@ -1250,7 +1250,7 @@
       {
         "args": [],
         "cros_board": "strongbad",
-        "cros_img": "strongbad-release/R102-14695.68.0",
+        "cros_img": "strongbad-release/R103-14816.25.0",
         "name": "lacros_all_tast_tests STRONGBAD_RELEASE_BETA",
         "swarming": {},
         "tast_expr": "(\"group:mainline\" && \"dep:lacros\" && !informational)",
@@ -1296,7 +1296,7 @@
       {
         "args": [],
         "cros_board": "strongbad",
-        "cros_img": "strongbad-release/R102-14695.68.0",
+        "cros_img": "strongbad-release/R103-14816.25.0",
         "name": "ozone_unittests STRONGBAD_RELEASE_BETA",
         "swarming": {},
         "test": "ozone_unittests",
@@ -1340,7 +1340,7 @@
       {
         "args": [],
         "cros_board": "strongbad",
-        "cros_img": "strongbad-release/R102-14695.68.0",
+        "cros_img": "strongbad-release/R103-14816.25.0",
         "name": "viz_unittests STRONGBAD_RELEASE_BETA",
         "swarming": {},
         "test": "viz_unittests",
diff --git a/testing/buildbot/variants.pyl b/testing/buildbot/variants.pyl
index ce2bd9c..da3aa55 100644
--- a/testing/buildbot/variants.pyl
+++ b/testing/buildbot/variants.pyl
@@ -52,15 +52,15 @@
   },
   'LACROS_VERSION_SKEW_BETA': {
     'args': [
-      '--ash-chrome-path-override=../../lacros_version_skew_tests_v102.0.5005.56/test_ash_chrome',
+      '--ash-chrome-path-override=../../lacros_version_skew_tests_v103.0.5060.22/test_ash_chrome',
     ],
-    'identifier': 'Lacros version skew testing ash 102.0.5005.56',
+    'identifier': 'Lacros version skew testing ash 103.0.5060.22',
     'swarming': {
       'cipd_packages': [
         {
           'cipd_package': 'chromium/testing/linux-ash-chromium/x86_64/ash.zip',
-          'location': 'lacros_version_skew_tests_v102.0.5005.56',
-          'revision': 'version:102.0.5005.56',
+          'location': 'lacros_version_skew_tests_v103.0.5060.22',
+          'revision': 'version:103.0.5060.22',
         },
       ],
     },
@@ -486,16 +486,16 @@
   },
   'WEBLAYER_10_AND_M_IMPL_SKEW_TESTS_NTH_MINUS_ONE_MILESTONE': {
     'args': [
+      '--webview-apk-path=apks/AOSP_SystemWebView.apk',
       '--test-runner-outdir',
       '.',
       '--client-outdir',
       '.',
-      '--test-expectations',
-      '../../weblayer/browser/android/javatests/skew/expectations.txt',
-      '--webview-apk-path=apks/AOSP_SystemWebView.apk',
       '--implementation-outdir',
       '../../weblayer_instrumentation_test_M102/out/Release',
-      '--impl-version=102'
+      '--test-expectations',
+      '../../weblayer/browser/android/javatests/skew/expectations.txt',
+      '--impl-version=102',
     ],
     'identifier': 'with_impl_from_102',
     'swarming': {
@@ -503,10 +503,10 @@
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M102',
-          'revision': 'version:102.0.5005.75'
+          'revision': 'version:102.0.5005.76',
         }
-      ]
-    }
+      ],
+    },
   },
   'WEBLAYER_10_AND_M_IMPL_SKEW_TESTS_NTH_MINUS_TWO_MILESTONE': {
     'args': [
@@ -630,16 +630,16 @@
   },
   'WEBLAYER_IMPL_SKEW_TESTS_NTH_MINUS_ONE_MILESTONE': {
     'args': [
+      '--webview-apk-path=apks/SystemWebView.apk',
       '--test-runner-outdir',
       '.',
       '--client-outdir',
       '.',
-      '--test-expectations',
-      '../../weblayer/browser/android/javatests/skew/expectations.txt',
-      '--webview-apk-path=apks/SystemWebView.apk',
       '--implementation-outdir',
       '../../weblayer_instrumentation_test_M102/out/Release',
-      '--impl-version=102'
+      '--test-expectations',
+      '../../weblayer/browser/android/javatests/skew/expectations.txt',
+      '--impl-version=102',
     ],
     'identifier': 'with_impl_from_102',
     'swarming': {
@@ -647,10 +647,10 @@
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M102',
-          'revision': 'version:102.0.5005.75'
+          'revision': 'version:102.0.5005.76',
         }
-      ]
-    }
+      ],
+    },
   },
   'WEBLAYER_IMPL_SKEW_TESTS_NTH_MINUS_TWO_MILESTONE': {
     'args': [
@@ -774,16 +774,16 @@
   },
   'WEBLAYER_CLIENT_SKEW_TESTS_NTH_MINUS_ONE_MILESTONE': {
     'args': [
+      '--webview-apk-path=apks/SystemWebView.apk',
       '--test-runner-outdir',
       '.',
+      '--client-outdir',
+      '../../weblayer_instrumentation_test_M102/out/Release',
       '--implementation-outdir',
       '.',
       '--test-expectations',
       '../../weblayer/browser/android/javatests/skew/expectations.txt',
-      '--webview-apk-path=apks/SystemWebView.apk',
-      '--client-outdir',
-      '../../weblayer_instrumentation_test_M102/out/Release',
-      '--client-version=102'
+      '--client-version=102',
     ],
     'identifier': 'with_client_from_102',
     'swarming': {
@@ -791,10 +791,10 @@
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M102',
-          'revision': 'version:102.0.5005.75'
+          'revision': 'version:102.0.5005.76',
         }
-      ]
-    }
+      ],
+    },
   },
   'WEBLAYER_CLIENT_SKEW_TESTS_NTH_MINUS_TWO_MILESTONE': {
     'args': [
@@ -913,8 +913,8 @@
   'CROS_ATLAS_RELEASE_BETA': {
     'skylab': {
       'cros_board': 'atlas',
-      'cros_chrome_version': '102.0.5005.56',
-      'cros_img': 'atlas-release/R102-14695.68.0',
+      'cros_chrome_version': '103.0.5060.22',
+      'cros_img': 'atlas-release/R103-14816.25.0',
     },
     'enabled': True,
     'identifier': 'ATLAS_RELEASE_BETA',
@@ -940,8 +940,8 @@
   'CROS_EVE_RELEASE_BETA': {
     'skylab': {
       'cros_board': 'eve',
-      'cros_chrome_version': '102.0.5005.48',
-      'cros_img': 'eve-release/R102-14695.55.0',
+      'cros_chrome_version': '103.0.5060.22',
+      'cros_img': 'eve-release/R103-14816.25.0',
     },
     'enabled': True,
     'identifier': 'EVE_RELEASE_BETA',
@@ -976,8 +976,8 @@
   'CROS_HANA_RELEASE_BETA': {
     'skylab': {
       'cros_board': 'hana',
-      'cros_chrome_version': '102.0.5005.56',
-      'cros_img': 'hana-release/R102-14695.68.0',
+      'cros_chrome_version': '103.0.5060.22',
+      'cros_img': 'hana-release/R103-14816.25.0',
     },
     'enabled': True,
     'identifier': 'HANA_RELEASE_BETA',
@@ -1003,8 +1003,8 @@
   'CROS_JACUZZI_RELEASE_BETA': {
     'skylab': {
       'cros_board': 'jacuzzi',
-      'cros_chrome_version': '102.0.5005.56',
-      'cros_img': 'jacuzzi-release/R102-14695.68.0',
+      'cros_chrome_version': '103.0.5060.22',
+      'cros_img': 'jacuzzi-release/R103-14816.25.0',
     },
     'enabled': True,
     'identifier': 'JACUZZI_RELEASE_BETA',
@@ -1039,8 +1039,8 @@
   'CROS_OCTOPUS_RELEASE_BETA': {
     'skylab': {
       'cros_board': 'octopus',
-      'cros_chrome_version': '102.0.5005.56',
-      'cros_img': 'octopus-release/R102-14695.68.0',
+      'cros_chrome_version': '103.0.5060.22',
+      'cros_img': 'octopus-release/R103-14816.25.0',
     },
     'enabled': True,
     'identifier': 'OCTOPUS_RELEASE_BETA',
@@ -1075,8 +1075,8 @@
   'CROS_STRONGBAD_RELEASE_BETA': {
     'skylab': {
       'cros_board': 'strongbad',
-      'cros_chrome_version': '102.0.5005.56',
-      'cros_img': 'strongbad-release/R102-14695.68.0',
+      'cros_chrome_version': '103.0.5060.22',
+      'cros_img': 'strongbad-release/R103-14816.25.0',
     },
     'enabled': True,
     'identifier': 'STRONGBAD_RELEASE_BETA',
diff --git a/third_party/blink/common/BUILD.gn b/third_party/blink/common/BUILD.gn
index 732c092..ccb0729b 100644
--- a/third_party/blink/common/BUILD.gn
+++ b/third_party/blink/common/BUILD.gn
@@ -136,6 +136,8 @@
     "input/web_mouse_wheel_event.cc",
     "input/web_pointer_event.cc",
     "input/web_touch_event.cc",
+    "interest_group/auction_config.cc",
+    "interest_group/auction_config_mojom_traits.cc",
     "interest_group/interest_group.cc",
     "interest_group/interest_group_mojom_traits.cc",
     "link_to_text/link_to_text_mojom_traits.cc",
@@ -331,6 +333,7 @@
     "indexeddb/indexeddb_key_unittest.cc",
     "input/synthetic_web_input_event_builders_unittest.cc",
     "input/web_input_event_unittest.cc",
+    "interest_group/auction_config_mojom_traits_test.cc",
     "interest_group/interest_group_mojom_traits_test.cc",
     "loader/inter_process_time_ticks_converter_unittest.cc",
     "loader/mime_sniffing_throttle_unittest.cc",
diff --git a/third_party/blink/common/interest_group/auction_config.cc b/third_party/blink/common/interest_group/auction_config.cc
new file mode 100644
index 0000000..6823f4c
--- /dev/null
+++ b/third_party/blink/common/interest_group/auction_config.cc
@@ -0,0 +1,53 @@
+// Copyright 2022 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/public/common/interest_group/auction_config.h"
+
+namespace blink {
+
+AuctionConfig::NonSharedParams::NonSharedParams() = default;
+AuctionConfig::NonSharedParams::NonSharedParams(const NonSharedParams&) =
+    default;
+AuctionConfig::NonSharedParams::NonSharedParams(NonSharedParams&&) = default;
+AuctionConfig::NonSharedParams::~NonSharedParams() = default;
+
+AuctionConfig::NonSharedParams& AuctionConfig::NonSharedParams::operator=(
+    const NonSharedParams&) = default;
+AuctionConfig::NonSharedParams& AuctionConfig::NonSharedParams::operator=(
+    NonSharedParams&&) = default;
+
+bool AuctionConfig::NonSharedParams::operator==(
+    const NonSharedParams& other) const {
+  return std::tie(interest_group_buyers, auction_signals, seller_signals,
+                  seller_timeout, per_buyer_signals, per_buyer_timeouts,
+                  all_buyers_timeout, per_buyer_group_limits,
+                  all_buyers_group_limit, component_auctions) !=
+         std::tie(other.interest_group_buyers, other.auction_signals,
+                  other.seller_signals, other.seller_timeout,
+                  other.per_buyer_signals, other.per_buyer_timeouts,
+                  other.all_buyers_timeout, other.per_buyer_group_limits,
+                  other.all_buyers_group_limit, other.component_auctions);
+}
+
+AuctionConfig::AuctionConfig() = default;
+AuctionConfig::AuctionConfig(const AuctionConfig&) = default;
+AuctionConfig::AuctionConfig(AuctionConfig&&) = default;
+AuctionConfig::~AuctionConfig() = default;
+
+AuctionConfig& AuctionConfig::operator=(const AuctionConfig&) = default;
+AuctionConfig& AuctionConfig::operator=(AuctionConfig&&) = default;
+
+bool AuctionConfig::operator==(const AuctionConfig& other) const {
+  return std::tie(seller, decision_logic_url, trusted_scoring_signals_url,
+                  non_shared_params, seller_experiment_group_id,
+                  all_buyer_experiment_group_id,
+                  per_buyer_experiment_group_ids) !=
+         std::tie(other.seller, other.decision_logic_url,
+                  other.trusted_scoring_signals_url, other.non_shared_params,
+                  other.seller_experiment_group_id,
+                  other.all_buyer_experiment_group_id,
+                  other.per_buyer_experiment_group_ids);
+}
+
+}  // namespace blink
diff --git a/third_party/blink/common/interest_group/auction_config_mojom_traits.cc b/third_party/blink/common/interest_group/auction_config_mojom_traits.cc
new file mode 100644
index 0000000..be33650
--- /dev/null
+++ b/third_party/blink/common/interest_group/auction_config_mojom_traits.cc
@@ -0,0 +1,102 @@
+// Copyright 2022 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/public/common/interest_group/auction_config_mojom_traits.h"
+
+#include "base/time/time.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/blink/public/common/interest_group/auction_config.h"
+#include "third_party/blink/public/mojom/interest_group/interest_group_types.mojom.h"
+#include "url/gurl.h"
+#include "url/origin.h"
+#include "url/url_constants.h"
+
+namespace mojo {
+
+namespace {
+
+// Helper to check if `url` is HTTPS and has the specified origin. Used to
+// validate seller URLs can be used with the seller's origin.
+bool IsHttpsAndMatchesOrigin(const GURL& seller_url,
+                             const url::Origin& seller_origin) {
+  return seller_url.scheme() == url::kHttpsScheme &&
+         url::Origin::Create(seller_url) == seller_origin;
+}
+
+}  // namespace
+
+bool StructTraits<blink::mojom::AuctionAdConfigNonSharedParamsDataView,
+                  blink::AuctionConfig::NonSharedParams>::
+    Read(blink::mojom::AuctionAdConfigNonSharedParamsDataView data,
+         blink::AuctionConfig::NonSharedParams* out) {
+  if (!data.ReadInterestGroupBuyers(&out->interest_group_buyers) ||
+      !data.ReadAuctionSignals(&out->auction_signals) ||
+      !data.ReadSellerSignals(&out->seller_signals) ||
+      !data.ReadSellerTimeout(&out->seller_timeout) ||
+      !data.ReadPerBuyerSignals(&out->per_buyer_signals) ||
+      !data.ReadPerBuyerTimeouts(&out->per_buyer_timeouts) ||
+      !data.ReadAllBuyersTimeout(&out->all_buyers_timeout) ||
+      !data.ReadPerBuyerGroupLimits(&out->per_buyer_group_limits) ||
+      !data.ReadComponentAuctions(&out->component_auctions)) {
+    return false;
+  }
+
+  out->all_buyers_group_limit = data.all_buyers_group_limit();
+
+  if (out->interest_group_buyers) {
+    for (const auto& buyer : *out->interest_group_buyers) {
+      // Buyers must be HTTPS.
+      if (buyer.scheme() != url::kHttpsScheme)
+        return false;
+    }
+  }
+
+  for (const auto& component_auction : out->component_auctions) {
+    // Component auctions may not have their own nested component auctions.
+    if (!component_auction.non_shared_params.component_auctions.empty()) {
+      return false;
+    }
+  }
+
+  return true;
+}
+
+bool StructTraits<blink::mojom::AuctionAdConfigDataView, blink::AuctionConfig>::
+    Read(blink::mojom::AuctionAdConfigDataView data,
+         blink::AuctionConfig* out) {
+  if (!data.ReadSeller(&out->seller) ||
+      !data.ReadDecisionLogicUrl(&out->decision_logic_url) ||
+      !data.ReadTrustedScoringSignalsUrl(&out->trusted_scoring_signals_url) ||
+      !data.ReadAuctionAdConfigNonSharedParams(&out->non_shared_params) ||
+      !data.ReadPerBuyerExperimentGroupIds(
+          &out->per_buyer_experiment_group_ids)) {
+    return false;
+  }
+
+  if (data.has_seller_experiment_group_id())
+    out->seller_experiment_group_id = data.seller_experiment_group_id();
+
+  if (data.has_all_buyer_experiment_group_id())
+    out->all_buyer_experiment_group_id = data.all_buyer_experiment_group_id();
+
+  // Seller must be HTTPS. This also excludes opaque origins, for which scheme()
+  // returns an empty string.
+  if (out->seller.scheme() != url::kHttpsScheme)
+    return false;
+
+  // `decision_logic_url` and, if present, `trusted_scoring_signals_url` must
+  // share the seller's origin, and must be HTTPS. Need to explicitly check the
+  // scheme because some non-HTTPS URLs may have HTTPS origins (e.g., blob
+  // URLs).
+  if (!IsHttpsAndMatchesOrigin(out->decision_logic_url, out->seller) ||
+      (out->trusted_scoring_signals_url &&
+       !IsHttpsAndMatchesOrigin(*out->trusted_scoring_signals_url,
+                                out->seller))) {
+    return false;
+  }
+
+  return true;
+}
+
+}  // namespace mojo
diff --git a/third_party/blink/common/interest_group/auction_config_mojom_traits_test.cc b/third_party/blink/common/interest_group/auction_config_mojom_traits_test.cc
new file mode 100644
index 0000000..0b38dbc8
--- /dev/null
+++ b/third_party/blink/common/interest_group/auction_config_mojom_traits_test.cc
@@ -0,0 +1,182 @@
+// Copyright 2022 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/public/common/interest_group/auction_config_mojom_traits.h"
+
+#include <string>
+#include <vector>
+
+#include "base/containers/flat_map.h"
+#include "base/time/time.h"
+#include "mojo/public/cpp/test_support/test_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/blink/public/common/interest_group/auction_config.h"
+#include "third_party/blink/public/mojom/interest_group/interest_group_types.mojom.h"
+#include "url/gurl.h"
+#include "url/origin.h"
+
+namespace blink {
+
+namespace {
+
+// Creates a minimal valid AuctionConfig, with a seller and the passed in
+// decision logic URL. Seller is derived from `decision_logic_url`.
+AuctionConfig CreateBasicConfig(
+    const GURL& decision_logic_url = GURL("https://seller.test/foo")) {
+  AuctionConfig auction_config;
+  auction_config.seller = url::Origin::Create(decision_logic_url);
+  auction_config.decision_logic_url = decision_logic_url;
+  return auction_config;
+}
+
+// Creates an AuctionConfig with all fields except `component_auctions`
+// populated.
+AuctionConfig CreateFullConfig() {
+  AuctionConfig auction_config = CreateBasicConfig();
+
+  auction_config.trusted_scoring_signals_url = GURL("https://seller.test/bar");
+  auction_config.seller_experiment_group_id = 1;
+  auction_config.all_buyer_experiment_group_id = 2;
+
+  url::Origin buyer = url::Origin::Create(GURL("https://buyer.test"));
+  auction_config.per_buyer_experiment_group_ids[buyer] = 3;
+
+  AuctionConfig::NonSharedParams& non_shared_params =
+      auction_config.non_shared_params;
+  non_shared_params.interest_group_buyers.emplace();
+  non_shared_params.interest_group_buyers->push_back(buyer);
+  non_shared_params.auction_signals = "[4]";
+  non_shared_params.seller_signals = "[5]";
+  non_shared_params.seller_timeout = base::Seconds(6);
+  non_shared_params.per_buyer_signals.emplace();
+  (*non_shared_params.per_buyer_signals)[buyer] = "[7]";
+  non_shared_params.per_buyer_timeouts.emplace();
+  (*non_shared_params.per_buyer_timeouts)[buyer] = base::Seconds(8);
+  non_shared_params.all_buyers_timeout = base::Seconds(9);
+  non_shared_params.per_buyer_group_limits[buyer] = 10;
+  non_shared_params.all_buyers_group_limit = 11;
+
+  return auction_config;
+}
+
+// Attempts to serialize and then deserialize `auction_config`, returning true
+// if deserialization succeeded. On success, also checks that the resulting
+// config matches the original config.
+bool SerializeAndDeserialize(const AuctionConfig& auction_config) {
+  AuctionConfig auction_config_clone;
+  bool success =
+      mojo::test::SerializeAndDeserialize<blink::mojom::AuctionAdConfig>(
+          auction_config, auction_config_clone);
+
+  if (success)
+    EXPECT_EQ(auction_config, auction_config_clone);
+  return success;
+}
+
+TEST(AuctionConfigMojomTraitsTest, Empty) {
+  AuctionConfig auction_config;
+  EXPECT_FALSE(SerializeAndDeserialize(auction_config));
+}
+
+TEST(AuctionConfigMojomTraitsTest, Basic) {
+  AuctionConfig auction_config = CreateBasicConfig();
+  EXPECT_TRUE(SerializeAndDeserialize(auction_config));
+}
+
+TEST(AuctionConfigMojomTraitsTest, SellerNotHttps) {
+  AuctionConfig auction_config = CreateBasicConfig(GURL("http://seller.test"));
+  EXPECT_FALSE(SerializeAndDeserialize(auction_config));
+}
+
+TEST(AuctionConfigMojomTraitsTest, SellerDecisionUrlMismatch) {
+  AuctionConfig auction_config = CreateBasicConfig(GURL("http://seller.test"));
+  // Different origin than seller, but same scheme.
+  auction_config.decision_logic_url = GURL("https://not.seller.test/foo");
+  EXPECT_FALSE(SerializeAndDeserialize(auction_config));
+
+  auction_config = CreateBasicConfig(GURL("https://seller.test"));
+  // This blob URL should be considered same-origin to the seller, but the
+  // scheme is wrong.
+  auction_config.decision_logic_url = GURL("blob:https://seller.test/foo");
+  ASSERT_EQ(auction_config.seller,
+            url::Origin::Create(auction_config.decision_logic_url));
+  EXPECT_FALSE(SerializeAndDeserialize(auction_config));
+}
+
+TEST(AuctionConfigMojomTraitsTest, SellerScoringSignalsUrlMismatch) {
+  AuctionConfig auction_config = CreateBasicConfig(GURL("http://seller.test"));
+  // Different origin than seller, but same scheme.
+  auction_config.trusted_scoring_signals_url =
+      GURL("https://not.seller.test/foo");
+  EXPECT_FALSE(SerializeAndDeserialize(auction_config));
+
+  auction_config = CreateBasicConfig(GURL("https://seller.test"));
+  // This blob URL should be considered same-origin to the seller, but the
+  // scheme is wrong.
+  auction_config.trusted_scoring_signals_url =
+      GURL("blob:https://seller.test/foo");
+  ASSERT_EQ(auction_config.seller,
+            url::Origin::Create(*auction_config.trusted_scoring_signals_url));
+  EXPECT_FALSE(SerializeAndDeserialize(auction_config));
+}
+
+TEST(AuctionConfigMojomTraitsTest, FullConfig) {
+  AuctionConfig auction_config = CreateFullConfig();
+  EXPECT_TRUE(SerializeAndDeserialize(auction_config));
+}
+
+TEST(AuctionConfigMojomTraitsTest, BuyerNotHttps) {
+  AuctionConfig auction_config = CreateBasicConfig();
+  auction_config.non_shared_params.interest_group_buyers.emplace();
+  auction_config.non_shared_params.interest_group_buyers->push_back(
+      url::Origin::Create(GURL("http://buyer.test")));
+  EXPECT_FALSE(SerializeAndDeserialize(auction_config));
+}
+
+TEST(AuctionConfigMojomTraitsTest, BuyerNotHttpsMultipleBuyers) {
+  AuctionConfig auction_config = CreateBasicConfig();
+  auction_config.non_shared_params.interest_group_buyers.emplace();
+  auction_config.non_shared_params.interest_group_buyers->push_back(
+      url::Origin::Create(GURL("https://buyer1.test")));
+  auction_config.non_shared_params.interest_group_buyers->push_back(
+      url::Origin::Create(GURL("http://buyer2.test")));
+  EXPECT_FALSE(SerializeAndDeserialize(auction_config));
+}
+
+TEST(AuctionConfigMojomTraitsTest, ComponentAuctionUrlHttps) {
+  AuctionConfig auction_config = CreateBasicConfig();
+  auction_config.non_shared_params.component_auctions.emplace_back(
+      CreateBasicConfig(GURL("http://seller.test")));
+  EXPECT_FALSE(SerializeAndDeserialize(auction_config));
+}
+
+TEST(AuctionConfigMojomTraitsTest, ComponentAuctionTooDeep) {
+  AuctionConfig auction_config = CreateBasicConfig();
+  auction_config.non_shared_params.component_auctions.emplace_back(
+      CreateBasicConfig());
+  auction_config.non_shared_params.component_auctions[0]
+      .non_shared_params.component_auctions.emplace_back(CreateBasicConfig());
+  EXPECT_FALSE(SerializeAndDeserialize(auction_config));
+}
+
+TEST(AuctionConfigMojomTraitsTest, ComponentAuctionSuccessSingleBasic) {
+  AuctionConfig auction_config = CreateBasicConfig();
+  auction_config.non_shared_params.component_auctions.emplace_back(
+      CreateBasicConfig());
+  EXPECT_TRUE(SerializeAndDeserialize(auction_config));
+}
+
+TEST(AuctionConfigMojomTraitsTest, ComponentAuctionSuccessMutipleFull) {
+  AuctionConfig auction_config = CreateFullConfig();
+  auction_config.non_shared_params.component_auctions.emplace_back(
+      CreateFullConfig());
+  auction_config.non_shared_params.component_auctions.emplace_back(
+      CreateFullConfig());
+  EXPECT_TRUE(SerializeAndDeserialize(auction_config));
+}
+
+}  // namespace
+
+}  // namespace blink
diff --git a/third_party/blink/public/common/BUILD.gn b/third_party/blink/public/common/BUILD.gn
index 611c6cf..d10f539 100644
--- a/third_party/blink/public/common/BUILD.gn
+++ b/third_party/blink/public/common/BUILD.gn
@@ -171,6 +171,8 @@
     "input/web_touch_event.h",
     "input/web_touch_point.h",
     "interest_group/ad_auction_constants.h",
+    "interest_group/auction_config.h",
+    "interest_group/auction_config_mojom_traits.h",
     "interest_group/interest_group.h",
     "interest_group/interest_group_mojom_traits.h",
     "link_to_text/link_to_text_mojom_traits.h",
diff --git a/third_party/blink/public/common/interest_group/auction_config.h b/third_party/blink/public/common/interest_group/auction_config.h
new file mode 100644
index 0000000..573d1af
--- /dev/null
+++ b/third_party/blink/public/common/interest_group/auction_config.h
@@ -0,0 +1,116 @@
+// Copyright 2022 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_PUBLIC_COMMON_INTEREST_GROUP_AUCTION_CONFIG_H_
+#define THIRD_PARTY_BLINK_PUBLIC_COMMON_INTEREST_GROUP_AUCTION_CONFIG_H_
+
+#include <stdint.h>
+
+#include <limits>
+#include <string>
+#include <vector>
+
+#include "base/containers/flat_map.h"
+#include "base/time/time.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/blink/public/common/common_export.h"
+#include "url/gurl.h"
+#include "url/origin.h"
+
+namespace blink {
+
+// AuctionConfig class used by FLEDGE auctions. Typemapped to
+// blink::mojom::AuctionAdConfig, primarily so the typemap can include validity
+// checks on the origins of the provided URLs. Not called blink::AuctionConfig
+// because a class of that name is already created from auction_ad_config.idl.
+//
+// All URLs and origins must be HTTPS.
+struct BLINK_COMMON_EXPORT AuctionConfig {
+  // Subset of AuctionConfig that is not shared by all auctions that are
+  // using the same SellerWorklet object (so it's "not shared" between
+  // AuctionConfigs that share the same SellerWorklet). Other AuctionConfig
+  // parameters all must be the same for two auctions to share a Sellerworklet.
+  //
+  // Typemapped to blink::mojom::AuctionAdConfigNonSharedParams.
+  struct BLINK_COMMON_EXPORT NonSharedParams {
+    NonSharedParams();
+    NonSharedParams(const NonSharedParams&);
+    NonSharedParams(NonSharedParams&&);
+    ~NonSharedParams();
+
+    NonSharedParams& operator=(const NonSharedParams&);
+    NonSharedParams& operator=(NonSharedParams&&);
+
+    // Provided for testing.
+    bool operator==(const NonSharedParams& other) const;
+
+    // Owners of interest groups allowed to participate in the auction.
+    absl::optional<std::vector<url::Origin>> interest_group_buyers;
+
+    // Opaque JSON data, passed as object to all worklets.
+    absl::optional<std::string> auction_signals;
+
+    // Opaque JSON data, passed as object to the seller worklet.
+    absl::optional<std::string> seller_signals;
+
+    // The value restricts the runtime of the seller's scoreAd() script.
+    absl::optional<base::TimeDelta> seller_timeout;
+
+    // Value is opaque JSON data, passed as object to particular buyers.
+    absl::optional<base::flat_map<url::Origin, std::string>> per_buyer_signals;
+
+    // Values restrict the runtime of particular buyer's generateBid() scripts.
+    absl::optional<base::flat_map<url::Origin, base::TimeDelta>>
+        per_buyer_timeouts;
+
+    // The value restricts generateBid() script's runtime of all buyers with
+    // unspecified timeouts, if present.
+    absl::optional<base::TimeDelta> all_buyers_timeout;
+
+    // Values restrict the number of bidding interest groups for a particular
+    // buyer that can participate in an auction. Values must be greater than 0.
+    base::flat_map<url::Origin, std::uint16_t> per_buyer_group_limits;
+
+    // Limit on the number of bidding interest groups for any buyer. Must be
+    // greater than 0. Defaults to the largest uint16 value, which is fine
+    // in our case since the backend storage applies a lower limit.
+    std::uint16_t all_buyers_group_limit =
+        std::numeric_limits<std::uint16_t>::max();
+
+    // Nested auctions whose results will also be fed to `seller`. Only the top
+    // level auction config can have component auctions.
+    std::vector<AuctionConfig> component_auctions;
+  };
+
+  AuctionConfig();
+  AuctionConfig(const AuctionConfig&);
+  AuctionConfig(AuctionConfig&&);
+  ~AuctionConfig();
+
+  AuctionConfig& operator=(const AuctionConfig&);
+  AuctionConfig& operator=(AuctionConfig&&);
+
+  // Provided for testing.
+  bool operator==(const AuctionConfig& other) const;
+
+  // Seller running the auction.
+  url::Origin seller;
+
+  // Both URLS, if present, must be same-origin to `seller`.
+  GURL decision_logic_url;
+  absl::optional<GURL> trusted_scoring_signals_url;
+
+  // Other parameters are grouped in a struct that is passed to SellerWorklets.
+  NonSharedParams non_shared_params;
+
+  // Identifier for an experiment group, used when getting trusted
+  // signals (and as part of AuctionConfig given to worklets).
+  absl::optional<uint16_t> seller_experiment_group_id;
+  absl::optional<uint16_t> all_buyer_experiment_group_id;
+  base::flat_map<url::Origin, uint16_t> per_buyer_experiment_group_ids;
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_PUBLIC_COMMON_INTEREST_GROUP_AUCTION_CONFIG_H_
diff --git a/third_party/blink/public/common/interest_group/auction_config_mojom_traits.h b/third_party/blink/public/common/interest_group/auction_config_mojom_traits.h
new file mode 100644
index 0000000..1d986cd
--- /dev/null
+++ b/third_party/blink/public/common/interest_group/auction_config_mojom_traits.h
@@ -0,0 +1,131 @@
+// Copyright 2022 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_PUBLIC_COMMON_INTEREST_GROUP_AUCTION_CONFIG_MOJOM_TRAITS_H_
+#define THIRD_PARTY_BLINK_PUBLIC_COMMON_INTEREST_GROUP_AUCTION_CONFIG_MOJOM_TRAITS_H_
+
+#include <string>
+#include <vector>
+
+#include "base/time/time.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/blink/public/common/common_export.h"
+#include "third_party/blink/public/common/interest_group/auction_config.h"
+#include "third_party/blink/public/mojom/interest_group/interest_group_types.mojom-forward.h"
+#include "url/gurl.h"
+#include "url/origin.h"
+
+namespace mojo {
+
+template <>
+struct BLINK_COMMON_EXPORT
+    StructTraits<blink::mojom::AuctionAdConfigNonSharedParamsDataView,
+                 blink::AuctionConfig::NonSharedParams> {
+  static const absl::optional<std::vector<url::Origin>>& interest_group_buyers(
+      const blink::AuctionConfig::NonSharedParams& params) {
+    return params.interest_group_buyers;
+  }
+
+  static const absl::optional<std::string>& auction_signals(
+      const blink::AuctionConfig::NonSharedParams& params) {
+    return params.auction_signals;
+  }
+
+  static const absl::optional<std::string>& seller_signals(
+      const blink::AuctionConfig::NonSharedParams& params) {
+    return params.seller_signals;
+  }
+
+  static absl::optional<base::TimeDelta> seller_timeout(
+      const blink::AuctionConfig::NonSharedParams& params) {
+    return params.seller_timeout;
+  }
+
+  static const absl::optional<base::flat_map<url::Origin, std::string>>&
+  per_buyer_signals(const blink::AuctionConfig::NonSharedParams& params) {
+    return params.per_buyer_signals;
+  }
+
+  static const absl::optional<base::flat_map<url::Origin, base::TimeDelta>>&
+  per_buyer_timeouts(const blink::AuctionConfig::NonSharedParams& params) {
+    return params.per_buyer_timeouts;
+  }
+
+  static const absl::optional<base::TimeDelta>& all_buyers_timeout(
+      const blink::AuctionConfig::NonSharedParams& params) {
+    return params.all_buyers_timeout;
+  }
+
+  static const base::flat_map<url::Origin, std::uint16_t>&
+  per_buyer_group_limits(const blink::AuctionConfig::NonSharedParams& params) {
+    return params.per_buyer_group_limits;
+  }
+
+  static std::uint16_t all_buyers_group_limit(
+      const blink::AuctionConfig::NonSharedParams& params) {
+    return params.all_buyers_group_limit;
+  }
+
+  static const std::vector<blink::AuctionConfig>& component_auctions(
+      const blink::AuctionConfig::NonSharedParams& params) {
+    return params.component_auctions;
+  }
+
+  static bool Read(blink::mojom::AuctionAdConfigNonSharedParamsDataView data,
+                   blink::AuctionConfig::NonSharedParams* out);
+};
+
+template <>
+struct BLINK_COMMON_EXPORT
+    StructTraits<blink::mojom::AuctionAdConfigDataView, blink::AuctionConfig> {
+  static const url::Origin& seller(const blink::AuctionConfig& config) {
+    return config.seller;
+  }
+
+  static const GURL& decision_logic_url(const blink::AuctionConfig& config) {
+    return config.decision_logic_url;
+  }
+
+  static const absl::optional<GURL>& trusted_scoring_signals_url(
+      const blink::AuctionConfig& config) {
+    return config.trusted_scoring_signals_url;
+  }
+
+  static const blink::AuctionConfig::NonSharedParams&
+  auction_ad_config_non_shared_params(const blink::AuctionConfig& config) {
+    return config.non_shared_params;
+  }
+
+  static bool has_seller_experiment_group_id(
+      const blink::AuctionConfig& config) {
+    return config.seller_experiment_group_id.has_value();
+  }
+
+  static std::int16_t seller_experiment_group_id(
+      const blink::AuctionConfig& config) {
+    return config.seller_experiment_group_id.value_or(0);
+  }
+
+  static bool has_all_buyer_experiment_group_id(
+      const blink::AuctionConfig& config) {
+    return config.all_buyer_experiment_group_id.has_value();
+  }
+
+  static std::int16_t all_buyer_experiment_group_id(
+      const blink::AuctionConfig& config) {
+    return config.all_buyer_experiment_group_id.value_or(0);
+  }
+
+  static const base::flat_map<url::Origin, uint16_t>&
+  per_buyer_experiment_group_ids(const blink::AuctionConfig& config) {
+    return config.per_buyer_experiment_group_ids;
+  }
+
+  static bool Read(blink::mojom::AuctionAdConfigDataView data,
+                   blink::AuctionConfig* out);
+};
+
+}  // namespace mojo
+
+#endif  // THIRD_PARTY_BLINK_PUBLIC_COMMON_INTEREST_GROUP_AUCTION_CONFIG_MOJOM_TRAITS_H_
diff --git a/third_party/blink/public/mojom/BUILD.gn b/third_party/blink/public/mojom/BUILD.gn
index 6008ee0..dce260b2 100644
--- a/third_party/blink/public/mojom/BUILD.gn
+++ b/third_party/blink/public/mojom/BUILD.gn
@@ -515,6 +515,26 @@
     {
       types = [
         {
+          mojom = "blink.mojom.AuctionAdConfigNonSharedParams"
+          cpp = "::blink::AuctionConfig::NonSharedParams"
+        },
+      ]
+      traits_headers = [ "//third_party/blink/public/common/interest_group/auction_config_mojom_traits.h" ]
+      traits_public_deps = [ "//url/mojom:mojom_traits" ]
+    },
+    {
+      types = [
+        {
+          mojom = "blink.mojom.AuctionAdConfig"
+          cpp = "::blink::AuctionConfig"
+        },
+      ]
+      traits_headers = [ "//third_party/blink/public/common/interest_group/auction_config_mojom_traits.h" ]
+      traits_public_deps = [ "//url/mojom:mojom_traits" ]
+    },
+    {
+      types = [
+        {
           mojom = "blink.mojom.ClipboardBuffer"
           cpp = "::ui::ClipboardBuffer"
         },
diff --git a/third_party/blink/public/mojom/interest_group/interest_group_types.mojom b/third_party/blink/public/mojom/interest_group/interest_group_types.mojom
index 8eee162a..caddd77 100644
--- a/third_party/blink/public/mojom/interest_group/interest_group_types.mojom
+++ b/third_party/blink/public/mojom/interest_group/interest_group_types.mojom
@@ -55,12 +55,13 @@
 // parameters all must be the same for two auctions to share a Sellerworklet.
 struct AuctionAdConfigNonSharedParams {
   // Owners of interest groups allowed to participate in the auction.
+  // Must all be HTTPS.
   array<url.mojom.Origin>? interest_group_buyers;
 
-  // Opaque JSON data, passed as object to auction worklet.
+  // Opaque JSON data, passed as object to all worklets.
   string? auction_signals;
 
-  // Opaque JSON data, passed as object to auction worklet.
+  // Opaque JSON data, passed as object to the seller worklet.
   string? seller_signals;
 
   // The value restricts the runtime of the seller's scoreAd() script.
@@ -100,7 +101,7 @@
 // https://github.com/WICG/turtledove/blob/main/FLEDGE.md#21-initiating-an-on-device-auction
 struct AuctionAdConfig {
   // The entity running the ad auction. Unlike for interest groups, `seller`
-  // *doesn't* need to match the the current frame URL's origin since the
+  // *doesn't* need to match the current frame URL's origin since the
   // `decision_logic_url` determines the behavior of the auction. This allows
   // the publisher page embedding the ad to call runAdAuction() directly if it
   // desires, rather than requiring the runAdAuction() call to be made inside a
@@ -109,7 +110,7 @@
   // seller and publisher could be different entities, or the same entity.
   url.mojom.Origin seller;
 
-  // `decision_logic_url`'s origin must match the the seller's origin.
+  // `decision_logic_url`'s origin must match the seller's origin.
   url.mojom.Url decision_logic_url;
 
   // Base URL for per-bid data passed to the seller worklet. Must be same
diff --git a/third_party/blink/public/mojom/navigation/navigation_api_history_entry_arrays.mojom b/third_party/blink/public/mojom/navigation/navigation_api_history_entry_arrays.mojom
index 1dbc48a..b0f376a 100644
--- a/third_party/blink/public/mojom/navigation/navigation_api_history_entry_arrays.mojom
+++ b/third_party/blink/public/mojom/navigation/navigation_api_history_entry_arrays.mojom
@@ -18,7 +18,7 @@
   mojo_base.mojom.String16 url;
   int64 item_sequence_number;
   int64 document_sequence_number;
-  mojo_base.mojom.String16 state;
+  mojo_base.mojom.String16? state;
 };
 
 // A same-origin subset of the back/forward list exposed by the
diff --git a/third_party/blink/public/web/web_local_frame_client.h b/third_party/blink/public/web/web_local_frame_client.h
index 5954411..97c7d87 100644
--- a/third_party/blink/public/web/web_local_frame_client.h
+++ b/third_party/blink/public/web/web_local_frame_client.h
@@ -452,6 +452,10 @@
   // Called when a frame's page lifecycle state gets updated.
   virtual void DidSetPageLifecycleState() {}
 
+  // Immediately notifies the browser of a change in the current HistoryItem.
+  // Prefer DidUpdateCurrentHistoryItem().
+  virtual void NotifyCurrentHistoryItemChanged() {}
+
   // Called upon update to scroll position, document state, and other
   // non-navigational events related to the data held by WebHistoryItem.
   // WARNING: This method may be called very frequently.
diff --git a/third_party/blink/renderer/bindings/core/v8/v8_script_runner.cc b/third_party/blink/renderer/bindings/core/v8/v8_script_runner.cc
index 8762e2c..2ccfd313 100644
--- a/third_party/blink/renderer/bindings/core/v8/v8_script_runner.cc
+++ b/third_party/blink/renderer/bindings/core/v8/v8_script_runner.cc
@@ -650,46 +650,6 @@
   return ScriptEvaluationResult::FromClassicExceptionRethrown();
 }
 
-v8::MaybeLocal<v8::Value> V8ScriptRunner::CompileAndRunInternalScript(
-    v8::Isolate* isolate,
-    ScriptState* script_state,
-    const ClassicScript& classic_script) {
-  DCHECK_EQ(isolate, script_state->GetIsolate());
-
-  const ReferrerScriptInfo referrer_info(classic_script.BaseUrl(),
-                                         classic_script.FetchOptions());
-  v8::Local<v8::Data> host_defined_options =
-      referrer_info.ToV8HostDefinedOptions(isolate, classic_script.SourceUrl());
-
-  v8::ScriptCompiler::CompileOptions compile_options;
-  V8CodeCache::ProduceCacheOptions produce_cache_options;
-  v8::ScriptCompiler::NoCacheReason no_cache_reason;
-  std::tie(compile_options, produce_cache_options, no_cache_reason) =
-      V8CodeCache::GetCompileOptions(mojom::blink::V8CacheOptions::kDefault,
-                                     classic_script);
-  // Currently internal scripts don't have cache handlers. So we should not
-  // produce cache for them.
-  DCHECK_EQ(produce_cache_options,
-            V8CodeCache::ProduceCacheOptions::kNoProduceCache);
-  v8::Local<v8::Script> script;
-  if (!V8ScriptRunner::CompileScript(script_state, classic_script,
-                                     compile_options, no_cache_reason,
-                                     host_defined_options)
-           .ToLocal(&script))
-    return v8::MaybeLocal<v8::Value>();
-
-  TRACE_EVENT0("v8", "v8.run");
-  RuntimeCallStatsScopedTracer rcs_scoped_tracer(isolate);
-  RUNTIME_CALL_TIMER_SCOPE(isolate, RuntimeCallStats::CounterId::kV8);
-  v8::Isolate::SafeForTerminationScope safe_for_termination(isolate);
-  v8::MicrotasksScope microtasks_scope(
-      isolate, ToMicrotaskQueue(script_state),
-      v8::MicrotasksScope::kDoNotRunMicrotasks);
-  v8::MaybeLocal<v8::Value> result = script->Run(isolate->GetCurrentContext());
-  CHECK(!isolate->IsDead());
-  return result;
-}
-
 v8::MaybeLocal<v8::Value> V8ScriptRunner::CallAsConstructor(
     v8::Isolate* isolate,
     v8::Local<v8::Object> constructor,
diff --git a/third_party/blink/renderer/bindings/core/v8/v8_script_runner.h b/third_party/blink/renderer/bindings/core/v8/v8_script_runner.h
index fe4ab37..3d96451 100644
--- a/third_party/blink/renderer/bindings/core/v8/v8_script_runner.h
+++ b/third_party/blink/renderer/bindings/core/v8/v8_script_runner.h
@@ -125,8 +125,6 @@
                                                     ClassicScript*,
                                                     ExecuteScriptPolicy,
                                                     RethrowErrorsOption);
-  static v8::MaybeLocal<v8::Value>
-  CompileAndRunInternalScript(v8::Isolate*, ScriptState*, const ClassicScript&);
   static v8::MaybeLocal<v8::Value> CallAsConstructor(
       v8::Isolate*,
       v8::Local<v8::Object>,
diff --git a/third_party/blink/renderer/core/frame/local_frame_client.h b/third_party/blink/renderer/core/frame/local_frame_client.h
index 6fb2409..c5b2d86 100644
--- a/third_party/blink/renderer/core/frame/local_frame_client.h
+++ b/third_party/blink/renderer/core/frame/local_frame_client.h
@@ -303,6 +303,12 @@
   virtual bool AllowScriptExtensions() = 0;
 
   virtual void DidChangeScrollOffset() {}
+
+  // Immediately notifies the browser of a change in the current HistoryItem.
+  // Prefer DidUpdateCurrentHistoryItem().
+  virtual void NotifyCurrentHistoryItemChanged() {}
+  // Notifies the browser of a change in the current HistoryItem on a timer,
+  // allowing batching of updates.
   virtual void DidUpdateCurrentHistoryItem() {}
 
   // Called when a content-initiated, main frame navigation to a data URL is
diff --git a/third_party/blink/renderer/core/frame/local_frame_client_impl.cc b/third_party/blink/renderer/core/frame/local_frame_client_impl.cc
index 40042e1..da7bd0f 100644
--- a/third_party/blink/renderer/core/frame/local_frame_client_impl.cc
+++ b/third_party/blink/renderer/core/frame/local_frame_client_impl.cc
@@ -290,9 +290,13 @@
     web_frame_->Client()->DidChangeScrollOffset();
 }
 
-void LocalFrameClientImpl::DidUpdateCurrentHistoryItem() {
+void LocalFrameClientImpl::NotifyCurrentHistoryItemChanged() {
   if (web_frame_->Client())
-    web_frame_->Client()->DidUpdateCurrentHistoryItem();
+    web_frame_->Client()->NotifyCurrentHistoryItemChanged();
+}
+
+void LocalFrameClientImpl::DidUpdateCurrentHistoryItem() {
+  web_frame_->Client()->DidUpdateCurrentHistoryItem();
 }
 
 bool LocalFrameClientImpl::AllowContentInitiatedDataUrlNavigations(
diff --git a/third_party/blink/renderer/core/frame/local_frame_client_impl.h b/third_party/blink/renderer/core/frame/local_frame_client_impl.h
index 1f929d9..f2e228ab 100644
--- a/third_party/blink/renderer/core/frame/local_frame_client_impl.h
+++ b/third_party/blink/renderer/core/frame/local_frame_client_impl.h
@@ -193,6 +193,7 @@
   WebRemotePlaybackClient* CreateWebRemotePlaybackClient(
       HTMLMediaElement&) override;
   void DidChangeScrollOffset() override;
+  void NotifyCurrentHistoryItemChanged() override;
   void DidUpdateCurrentHistoryItem() override;
 
   bool AllowContentInitiatedDataUrlNavigations(const KURL&) override;
diff --git a/third_party/blink/renderer/core/html/media/autoplay_policy.cc b/third_party/blink/renderer/core/html/media/autoplay_policy.cc
index 862314da..871d825 100644
--- a/third_party/blink/renderer/core/html/media/autoplay_policy.cc
+++ b/third_party/blink/renderer/core/html/media/autoplay_policy.cc
@@ -347,7 +347,8 @@
         return;
 
       if (self->element_->can_autoplay_ && self->element_->Autoplay()) {
-        self->element_->PauseInternal();
+        self->element_->PauseInternal(
+            HTMLMediaElement::PlayPromiseError::kPaused_AutoplayAutoPause);
         self->element_->can_autoplay_ = true;
       }
     };
diff --git a/third_party/blink/renderer/core/html/media/html_media_element.cc b/third_party/blink/renderer/core/html/media/html_media_element.cc
index 05b0138..b4fbf82 100644
--- a/third_party/blink/renderer/core/html/media/html_media_element.cc
+++ b/third_party/blink/renderer/core/html/media/html_media_element.cc
@@ -1735,7 +1735,7 @@
   ScheduleEvent(event_type_names::kError);
 
   // 6 - Reject pending play promises with NotSupportedError.
-  ScheduleRejectPlayPromises(DOMExceptionCode::kNotSupportedError);
+  ScheduleRejectPlayPromises(PlayPromiseError::kNotSupported);
 
   CloseMediaSource();
 
@@ -2745,10 +2745,10 @@
   DVLOG(2) << "pause(" << *this << ")";
 
   autoplay_policy_->StopAutoplayMutedWhenVisible();
-  PauseInternal();
+  PauseInternal(PlayPromiseError::kPaused_PauseCalled);
 }
 
-void HTMLMediaElement::PauseInternal() {
+void HTMLMediaElement::PauseInternal(PlayPromiseError code) {
   DVLOG(3) << "pauseInternal(" << *this << ")";
 
   if (network_state_ == kNetworkEmpty)
@@ -2767,7 +2767,7 @@
     // time to accurately reflect movie time at the moment we paused.
     SetOfficialPlaybackPosition(CurrentPlaybackPosition());
 
-    ScheduleRejectPlayPromises(DOMExceptionCode::kAbortError);
+    ScheduleRejectPlayPromises(code);
   }
 
   UpdatePlayState();
@@ -3004,7 +3004,7 @@
                         WebFeature::kHTMLMediaElementPauseAtFragmentEnd);
       // changes paused to true and fires a simple event named pause at the
       // media element.
-      PauseInternal();
+      PauseInternal(PlayPromiseError::kPaused_EndOfPlayback);
     }
   }
 
@@ -3590,7 +3590,7 @@
         // media element.
         paused_ = true;
         ScheduleEvent(event_type_names::kPause);
-        ScheduleRejectPlayPromises(DOMExceptionCode::kAbortError);
+        ScheduleRejectPlayPromises(PlayPromiseError::kPaused_EndOfPlayback);
       }
       // Queue a task to fire a simple event named ended at the media element.
       ScheduleEvent(event_type_names::kEnded);
@@ -4378,7 +4378,7 @@
                 WrapWeakPersistent(this)));
 }
 
-void HTMLMediaElement::ScheduleRejectPlayPromises(DOMExceptionCode code) {
+void HTMLMediaElement::ScheduleRejectPlayPromises(PlayPromiseError code) {
   // TODO(mlamouri): per spec, we should create a new task but we can't create
   // a new cancellable task without cancelling the previous one. There are two
   // approaches then: cancel the previous task and create a new one with the
@@ -4418,20 +4418,38 @@
 }
 
 void HTMLMediaElement::RejectScheduledPlayPromises() {
-  // TODO(mlamouri): the message is generated based on the code because
-  // arguments can't be passed to a cancellable task. In order to save space
-  // used by the object, the string isn't saved.
-  DCHECK(play_promise_error_code_ == DOMExceptionCode::kAbortError ||
-         play_promise_error_code_ == DOMExceptionCode::kNotSupportedError);
-  if (play_promise_error_code_ == DOMExceptionCode::kAbortError) {
-    RejectPlayPromisesInternal(DOMExceptionCode::kAbortError,
-                               "The play() request was interrupted by a call "
-                               "to pause(). https://goo.gl/LdLk22");
-  } else {
-    RejectPlayPromisesInternal(
-        DOMExceptionCode::kNotSupportedError,
-        "Failed to load because no supported source was found.");
+  switch (play_promise_error_code_) {
+    case PlayPromiseError::kNotSupported:
+      return RejectPlayPromisesInternal(
+          DOMExceptionCode::kNotSupportedError,
+          "Failed to load because no supported source was found.");
+    case PlayPromiseError::kPaused_Unknown:
+      return RejectPlayPromisesInternal(
+          DOMExceptionCode::kAbortError,
+          "The play() request was interrupted because the media paused. "
+          "https://goo.gl/LdLk22");
+    case PlayPromiseError::kPaused_PauseCalled:
+      return RejectPlayPromisesInternal(
+          DOMExceptionCode::kAbortError,
+          "The play() request was interrupted by a call to pause(). "
+          "https://goo.gl/LdLk22");
+    case PlayPromiseError::kPaused_EndOfPlayback:
+      return RejectPlayPromisesInternal(
+          DOMExceptionCode::kAbortError,
+          "The play() request was interrupted by end of playback. "
+          "https://goo.gl/LdLk22");
+    case PlayPromiseError::kPaused_RemovedFromDocument:
+      return RejectPlayPromisesInternal(
+          DOMExceptionCode::kAbortError,
+          "The play() request was interrupted because the media was removed "
+          "from the document. https://goo.gl/LdLk22");
+    case PlayPromiseError::kPaused_AutoplayAutoPause:
+      return RejectPlayPromisesInternal(
+          DOMExceptionCode::kAbortError,
+          "The play() request was interrupted because autoplaying media was "
+          "auto-paused. https://goo.gl/LdLk22");
   }
+  NOTREACHED();
 }
 
 void HTMLMediaElement::RejectPlayPromises(DOMExceptionCode code,
@@ -4445,7 +4463,6 @@
                                                   const String& message) {
   DCHECK(code == DOMExceptionCode::kAbortError ||
          code == DOMExceptionCode::kNotSupportedError);
-
   for (auto& resolver : play_promise_reject_list_)
     resolver->Reject(MakeGarbageCollected<DOMException>(code, message));
 
@@ -4459,7 +4476,7 @@
   // Video should not pause when playing in Picture-in-Picture and subsequently
   // removed from the Document.
   if (!PictureInPictureController::IsElementInPictureInPicture(this))
-    PauseInternal();
+    PauseInternal(PlayPromiseError::kPaused_RemovedFromDocument);
 }
 
 void HTMLMediaElement::AudioSourceProviderImpl::Wrap(
@@ -4553,7 +4570,7 @@
 }
 
 void HTMLMediaElement::PausePlayback() {
-  PauseInternal();
+  PauseInternal(PlayPromiseError::kPaused_Unknown);
 }
 
 void HTMLMediaElement::DidPlayerStartPlaying() {
@@ -4652,7 +4669,7 @@
           frame, mojom::blink::UserActivationNotificationType::kInteraction);
     }
   }
-  PauseInternal();
+  PauseInternal(PlayPromiseError::kPaused_Unknown);
 }
 
 void HTMLMediaElement::RequestSeekForward(base::TimeDelta seek_time) {
diff --git a/third_party/blink/renderer/core/html/media/html_media_element.h b/third_party/blink/renderer/core/html/media/html_media_element.h
index 6a5f862..2da15b7 100644
--- a/third_party/blink/renderer/core/html/media/html_media_element.h
+++ b/third_party/blink/renderer/core/html/media/html_media_element.h
@@ -113,6 +113,15 @@
   static constexpr double kMinPlaybackRate = 0.0625;
   static constexpr double kMaxPlaybackRate = 16.0;
 
+  enum class PlayPromiseError {
+    kNotSupported,
+    kPaused_Unknown,
+    kPaused_PauseCalled,
+    kPaused_EndOfPlayback,
+    kPaused_RemovedFromDocument,
+    kPaused_AutoplayAutoPause,
+  };
+
   bool IsMediaElement() const override { return true; }
 
   static MIMETypeRegistry::SupportsType GetSupportsType(const ContentType&);
@@ -599,7 +608,7 @@
   void PlayInternal();
 
   // This does not stop autoplay visibility observation.
-  void PauseInternal();
+  void PauseInternal(PlayPromiseError code);
 
   void UpdatePlayState();
   bool PotentiallyPlaying() const;
@@ -646,7 +655,7 @@
   void AudioTracksTimerFired(TimerBase*);
 
   void ScheduleResolvePlayPromises();
-  void ScheduleRejectPlayPromises(DOMExceptionCode);
+  void ScheduleRejectPlayPromises(PlayPromiseError);
   void ScheduleNotifyPlaying();
   void ResolveScheduledPlayPromises();
   void RejectScheduledPlayPromises();
@@ -802,7 +811,7 @@
   TaskHandle play_promise_reject_task_handle_;
   HeapVector<Member<ScriptPromiseResolver>> play_promise_resolve_list_;
   HeapVector<Member<ScriptPromiseResolver>> play_promise_reject_list_;
-  DOMExceptionCode play_promise_error_code_;
+  PlayPromiseError play_promise_error_code_;
 
   // HTMLMediaElement and its MediaElementAudioSourceNode in case it is provided
   // die together.
diff --git a/third_party/blink/renderer/core/input/event_handler.cc b/third_party/blink/renderer/core/input/event_handler.cc
index 7231b5e..bc64875 100644
--- a/third_party/blink/renderer/core/input/event_handler.cc
+++ b/third_party/blink/renderer/core/input/event_handler.cc
@@ -915,9 +915,9 @@
        event_result == WebInputEventResult::kNotHandled) ||
       mev.GetScrollbar()) {
     mouse_event_manager_->SetCapturesDragging(true);
-    // Main frames don't implicitly capture mouse input on MouseDown, just
-    // subframes do (regardless of whether local or remote).
-    if (!frame_->IsMainFrame())
+    // Outermost main frames don't implicitly capture mouse input on MouseDown,
+    // all subframes do (regardless of whether local or remote or fenced).
+    if (frame_->IsAttached() && !frame_->IsOutermostMainFrame())
       CaptureMouseEventsToWidget(true);
   } else {
     mouse_event_manager_->SetCapturesDragging(false);
diff --git a/third_party/blink/renderer/core/input/pointer_event_manager.cc b/third_party/blink/renderer/core/input/pointer_event_manager.cc
index f992e8b..459f9e7 100644
--- a/third_party/blink/renderer/core/input/pointer_event_manager.cc
+++ b/third_party/blink/renderer/core/input/pointer_event_manager.cc
@@ -667,11 +667,6 @@
         mojom::blink::UserActivationNotificationType::kInteraction);
   }
 
-  if (event.GetType() == WebInputEvent::Type::kPointerDown) {
-    touch_event_manager_->UpdateTouchAttributeMapsForPointerDown(
-        event, pointer_event_target);
-  }
-
   WebInputEventResult result = DispatchTouchPointerEvent(
       event, coalesced_events, predicted_events, pointer_event_target);
 
diff --git a/third_party/blink/renderer/core/input/touch_event_manager.cc b/third_party/blink/renderer/core/input/touch_event_manager.cc
index f7af7beb..856961e3 100644
--- a/third_party/blink/renderer/core/input/touch_event_manager.cc
+++ b/third_party/blink/renderer/core/input/touch_event_manager.cc
@@ -624,6 +624,12 @@
     return;
   }
 
+  // In touch event model only touch starts can set the target and after that
+  // the touch event always goes to that target.
+  if (event.GetType() == WebInputEvent::Type::kPointerDown) {
+    UpdateTouchAttributeMapsForPointerDown(event, pointer_event_target);
+  }
+
   // We might not receive the down action for a touch point. In that case we
   // would have never added them to |touch_attribute_map_| or hit-tested
   // them. For those just keep them in the map with a null target. Later they
diff --git a/third_party/blink/renderer/core/input/touch_event_manager.h b/third_party/blink/renderer/core/input/touch_event_manager.h
index 8843365..6075f79 100644
--- a/third_party/blink/renderer/core/input/touch_event_manager.h
+++ b/third_party/blink/renderer/core/input/touch_event_manager.h
@@ -48,14 +48,6 @@
   // Returns whether there is any touch on the screen.
   bool IsAnyTouchActive() const;
 
-  // Keeps track of attributes of the touch point in the
-  // |touch_points_attributes_| map and computes the effective touch-action
-  // value, after possibly performing a hit-test if the original hit test result
-  // was not inside capturing frame |touch_sequence_document_| for touch events.
-  void UpdateTouchAttributeMapsForPointerDown(
-      const WebPointerEvent&,
-      const event_handling_util::PointerEventTarget&);
-
  private:
   // Class represending one touch point event with its coalesced events and
   // related attributes.
@@ -83,6 +75,14 @@
   Touch* CreateDomTouch(const TouchPointAttributes*, bool* known_target);
   void AllTouchesReleasedCleanup();
 
+  // Keeps track of attributes of the touch point in the
+  // |touch_points_attributes_| map and does the hit-testing if the original hit
+  // test result was not inside capturing frame |touch_sequence_document_| for
+  // touch events.
+  void UpdateTouchAttributeMapsForPointerDown(
+      const WebPointerEvent&,
+      const event_handling_util::PointerEventTarget&);
+
   // This is triggered either by VSync signal to send one touch event per frame
   // accumulating all move events or by discrete events pointerdown/up/cancel.
   WebInputEventResult DispatchTouchEventFromAccumulatdTouchPoints();
@@ -135,9 +135,6 @@
   // action which is sent to the browser after handling each dispatched
   // 'touchstart' is the intersection of all the previously calculated effective
   // touch action values during the sequence.
-  //
-  // TODO(https://crbug.com/844493): This seems incomplete code, should be
-  // removed.
   absl::optional<TouchAction> delayed_effective_touch_action_;
 };
 
diff --git a/third_party/blink/renderer/core/inspector/dev_tools_host.cc b/third_party/blink/renderer/core/inspector/dev_tools_host.cc
index f120ca33..16f4fe2 100644
--- a/third_party/blink/renderer/core/inspector/dev_tools_host.cc
+++ b/third_party/blink/renderer/core/inspector/dev_tools_host.cc
@@ -122,19 +122,9 @@
 void DevToolsHost::EvaluateScript(const String& expression) {
   if (ScriptForbiddenScope::IsScriptForbidden())
     return;
-  ScriptState* script_state = ToScriptStateForMainWorld(frontend_frame_);
-  if (!script_state)
-    return;
-  ScriptState::Scope scope(script_state);
-  v8::MicrotasksScope microtasks(script_state->GetIsolate(),
-                                 v8::MicrotasksScope::kRunMicrotasks);
-  // `kDoNotSanitize` is used for internal scripts for keeping the existing
-  // behavior.
-  V8ScriptRunner::CompileAndRunInternalScript(
-      script_state->GetIsolate(), script_state,
-      *ClassicScript::CreateUnspecifiedScript(
-          expression, ScriptSourceLocationType::kInternal,
-          SanitizeScriptErrors::kDoNotSanitize));
+  ClassicScript::CreateUnspecifiedScript(expression,
+                                         ScriptSourceLocationType::kInternal)
+      ->RunScriptOnScriptState(ToScriptStateForMainWorld(frontend_frame_));
 }
 
 void DevToolsHost::DisconnectClient() {
diff --git a/third_party/blink/renderer/core/inspector/thread_debugger.cc b/third_party/blink/renderer/core/inspector/thread_debugger.cc
index 0ab58e2..2f2023f 100644
--- a/third_party/blink/renderer/core/inspector/thread_debugger.cc
+++ b/third_party/blink/renderer/core/inspector/thread_debugger.cc
@@ -9,6 +9,7 @@
 #include "base/check.h"
 #include "base/rand_util.h"
 #include "third_party/blink/public/mojom/frame/user_activation_notification_type.mojom-blink.h"
+#include "third_party/blink/renderer/bindings/core/v8/script_evaluation_result.h"
 #include "third_party/blink/renderer/bindings/core/v8/source_location.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_blob.h"
@@ -516,18 +517,22 @@
       "function getAccessibleRole(node) { [Command Line API] }",
       v8::SideEffectType::kHasNoSideEffect);
 
-  v8::Local<v8::Value> function_value;
-  // `kDoNotSanitize` is used for internal scripts for keeping the existing
-  // behavior.
-  bool success = V8ScriptRunner::CompileAndRunInternalScript(
-                     isolate_, ScriptState::From(context),
-                     *ClassicScript::CreateUnspecifiedScript(
-                         "(function(e) { console.log(e.type, e); })",
-                         ScriptSourceLocationType::kInternal,
-                         SanitizeScriptErrors::kDoNotSanitize))
-                     .ToLocal(&function_value) &&
-                 function_value->IsFunction();
-  DCHECK(success);
+  ScriptEvaluationResult result =
+      ClassicScript::CreateUnspecifiedScript(
+          "(function(e) { console.log(e.type, e); })",
+          ScriptSourceLocationType::kInternal)
+          ->RunScriptOnScriptStateAndReturnValue(ScriptState::From(context));
+  if (result.GetResultType() != ScriptEvaluationResult::ResultType::kSuccess) {
+    // On pages where scripting is disabled or CSP sandbox directive is used,
+    // this can be blocked and thus early exited here.
+    // This is probably OK because `monitorEvents()` console API is anyway not
+    // working on such pages. For more discussion see
+    // https://crrev.com/c/3258735/9/third_party/blink/renderer/core/inspector/thread_debugger.cc#529
+    return;
+  }
+
+  v8::Local<v8::Value> function_value = result.GetSuccessValue();
+  DCHECK(function_value->IsFunction());
   CreateFunctionPropertyWithData(
       context, object, "monitorEvents", ThreadDebugger::MonitorEventsCallback,
       function_value,
diff --git a/third_party/blink/renderer/core/layout/ng/ng_ink_overflow.cc b/third_party/blink/renderer/core/layout/ng/ng_ink_overflow.cc
index c577509..298373a 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_ink_overflow.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_ink_overflow.cc
@@ -554,16 +554,7 @@
     float resolved_thickness = decoration_info.ResolvedThickness();
 
     if (has_underline) {
-      // Don't apply text-underline-offset to overline.
-      Length line_offset =
-          flip_underline_and_overline ? Length() : decoration.UnderlineOffset();
-
-      const int paint_underline_offset =
-          decoration_offset.ComputeUnderlineOffset(
-              underline_position, decoration_info.Style().ComputedFontSize(),
-              decoration_info.FontData(), line_offset, resolved_thickness);
-      decoration_info.SetLineData(TextDecorationLine::kUnderline,
-                                  paint_underline_offset);
+      decoration_info.SetUnderlineLineData(decoration, decoration_offset);
       accumulated_bound.Union(decoration_info.Bounds());
     }
     if (has_overline) {
diff --git a/third_party/blink/renderer/core/loader/anchor_element_interaction_test.cc b/third_party/blink/renderer/core/loader/anchor_element_interaction_test.cc
index 29526fd4..ba251cd 100644
--- a/third_party/blink/renderer/core/loader/anchor_element_interaction_test.cc
+++ b/third_party/blink/renderer/core/loader/anchor_element_interaction_test.cc
@@ -11,6 +11,7 @@
 #include "third_party/blink/public/common/input/web_mouse_event.h"
 #include "third_party/blink/public/mojom/loader/anchor_element_interaction_host.mojom-blink.h"
 #include "third_party/blink/renderer/core/dom/element.h"
+#include "third_party/blink/renderer/core/execution_context/execution_context.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
 #include "third_party/blink/renderer/core/html/html_anchor_element.h"
 #include "third_party/blink/renderer/core/input/event_handler.h"
@@ -219,4 +220,34 @@
   EXPECT_EQ(expected_url, url_received);
 }
 
+TEST_F(AnchorElementInteractionTest, DestroyedContext) {
+  String source("https://example.com/p1");
+  SimRequest main_resource(source, "text/html");
+  LoadURL(source);
+  main_resource.Complete(R"HTML(
+    <a href='https://anchor1.com/'>
+      <div style='padding: 0px; width: 400px; height: 400px;'></div>
+    </a>
+  )HTML");
+
+  // Make sure getting pointer events after the execution context has been
+  // destroyed but before the document has been destroyed doesn't cause a crash.
+  GetDocument().GetExecutionContext()->NotifyContextDestroyed();
+  WebPointerEvent event(
+      WebInputEvent::Type::kPointerDown,
+      WebPointerProperties(1, WebPointerProperties::PointerType::kTouch,
+                           WebPointerProperties::Button::kLeft,
+                           gfx::PointF(100, 100), gfx::PointF(100, 100)),
+      1, 1);
+  GetDocument().GetFrame()->GetEventHandler().HandlePointerEvent(
+      event, Vector<WebPointerEvent>(), Vector<WebPointerEvent>());
+  GetDocument().GetFrame()->GetEventHandler().DispatchBufferedTouchEvents();
+
+  base::RunLoop().RunUntilIdle();
+  KURL expected_url = KURL("https://anchor1.com/");
+  EXPECT_EQ(1u, hosts_.size());
+  absl::optional<KURL> url_received = hosts_[0]->url_received_;
+  EXPECT_FALSE(url_received.has_value());
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/loader/anchor_element_interaction_tracker.cc b/third_party/blink/renderer/core/loader/anchor_element_interaction_tracker.cc
index d1ff5a2a..2c3c73c 100644
--- a/third_party/blink/renderer/core/loader/anchor_element_interaction_tracker.cc
+++ b/third_party/blink/renderer/core/loader/anchor_element_interaction_tracker.cc
@@ -58,6 +58,13 @@
   if (url.IsEmpty()) {
     return;
   }
+
+  // interaction_host_ might become unbound: Android's low memory detector
+  // sometimes call NotifyContextDestroyed to save memory. This unbinds mojo
+  // pipes using that ExecutionContext even if those pages can still navigate.
+  if (!interaction_host_.is_bound()) {
+    return;
+  }
   interaction_host_->OnPointerDown(url);
 }
 
diff --git a/third_party/blink/renderer/core/navigation_api/navigation_api.cc b/third_party/blink/renderer/core/navigation_api/navigation_api.cc
index 72ae9db..254977e 100644
--- a/third_party/blink/renderer/core/navigation_api/navigation_api.cc
+++ b/third_party/blink/renderer/core/navigation_api/navigation_api.cc
@@ -340,7 +340,7 @@
   return MakeGarbageCollected<NavigationHistoryEntry>(
       GetSupplementable(), entry->key, entry->id, KURL(entry->url),
       entry->document_sequence_number,
-      SerializedScriptValue::Create(entry->state));
+      entry->state ? SerializedScriptValue::Create(entry->state) : nullptr);
 }
 
 // static
diff --git a/third_party/blink/renderer/core/navigation_api/navigation_history_entry.cc b/third_party/blink/renderer/core/navigation_api/navigation_history_entry.cc
index 29341520..b1ce616 100644
--- a/third_party/blink/renderer/core/navigation_api/navigation_history_entry.cc
+++ b/third_party/blink/renderer/core/navigation_api/navigation_history_entry.cc
@@ -8,6 +8,7 @@
 #include "third_party/blink/renderer/bindings/core/v8/serialization/serialized_script_value.h"
 #include "third_party/blink/renderer/core/event_target_names.h"
 #include "third_party/blink/renderer/core/frame/local_dom_window.h"
+#include "third_party/blink/renderer/core/frame/local_frame_client.h"
 #include "third_party/blink/renderer/core/loader/document_loader.h"
 #include "third_party/blink/renderer/core/navigation_api/navigation_api.h"
 
@@ -70,6 +71,12 @@
   state_ = state;
   DomWindow()->document()->Loader()->GetHistoryItem()->SetNavigationApiState(
       state);
+  // Force the new state object to be synced to the browser process immediately.
+  // The state object needs to be available as soon as possible in case a
+  // new navigation commits soon, so that browser has the best chance of having
+  // the up-to-date state object when constructing the arrays of non-current
+  // NavigationHistoryEntries.
+  DomWindow()->GetFrame()->Client()->NotifyCurrentHistoryItemChanged();
 }
 
 const AtomicString& NavigationHistoryEntry::InterfaceName() const {
diff --git a/third_party/blink/renderer/core/paint/ng/ng_table_painters.cc b/third_party/blink/renderer/core/paint/ng/ng_table_painters.cc
index dbe7c34..f67aaf3 100644
--- a/third_party/blink/renderer/core/paint/ng/ng_table_painters.cc
+++ b/third_party/blink/renderer/core/paint/ng/ng_table_painters.cc
@@ -203,6 +203,9 @@
       return NGTableCollapsedEdge(*this, borders_.EdgesPerRow());
     }
   }
+  NGTableCollapsedEdge EmptyEdge() const {
+    return NGTableCollapsedEdge(borders_, UINT_MAX);
+  }
 
   NGTableCollapsedEdge& operator++() {
     DCHECK_NE(edge_index_, UINT_MAX);
@@ -241,6 +244,8 @@
 // Examined edge should shrink/expand its size to fill the joints.
 void ComputeEdgeJoints(const NGTableBorders& collapsed_borders,
                        const NGTableCollapsedEdge& edge,
+                       bool is_over_edge_fragmentation_boundary,
+                       bool is_under_edge_fragmentation_boundary,
                        LogicalSize& start_joint,
                        LogicalSize& end_joint,
                        bool& start_wins,
@@ -261,13 +266,23 @@
   // Find winner for the start of the inline edge.
   NGTableCollapsedEdge before_edge = edge.EdgeBeforeStartIntersection();
   NGTableCollapsedEdge after_edge = edge.EdgeAfterStartIntersection();
-  NGTableCollapsedEdge over_edge = edge.EdgeOverStartIntersection();
-  NGTableCollapsedEdge under_edge = edge.EdgeUnderStartIntersection();
+  NGTableCollapsedEdge over_edge = is_over_edge_fragmentation_boundary
+                                       ? edge.EmptyEdge()
+                                       : edge.EdgeOverStartIntersection();
+  NGTableCollapsedEdge under_edge =
+      is_under_edge_fragmentation_boundary && edge.IsInlineAxis()
+          ? edge.EmptyEdge()
+          : edge.EdgeUnderStartIntersection();
 
   int inline_compare =
       NGTableCollapsedEdge::CompareForPaint(before_edge, after_edge);
   start_joint.block_size = inline_compare == 1 ? before_edge.BorderWidth()
                                                : after_edge.BorderWidth();
+  if (is_over_edge_fragmentation_boundary ||
+      (is_under_edge_fragmentation_boundary && edge.IsInlineAxis())) {
+    start_joint.block_size = LayoutUnit();
+  }
+
   // Compare over and under edges.
   int block_compare =
       NGTableCollapsedEdge::CompareForPaint(over_edge, under_edge);
@@ -287,13 +302,21 @@
   // Find the winner for the end joint of the inline edge.
   before_edge = edge.EdgeBeforeEndIntersection();
   after_edge = edge.EdgeAfterEndIntersection();
-  over_edge = edge.EdgeOverEndIntersection();
-  under_edge = edge.EdgeUnderEndIntersection();
+  over_edge = is_over_edge_fragmentation_boundary && edge.IsInlineAxis()
+                  ? edge.EmptyEdge()
+                  : edge.EdgeOverEndIntersection();
+  under_edge = is_under_edge_fragmentation_boundary
+                   ? edge.EmptyEdge()
+                   : edge.EdgeUnderEndIntersection();
 
   inline_compare =
       NGTableCollapsedEdge::CompareForPaint(before_edge, after_edge);
   end_joint.block_size = inline_compare == 1 ? before_edge.BorderWidth()
                                              : after_edge.BorderWidth();
+  if ((is_over_edge_fragmentation_boundary && edge.IsInlineAxis()) ||
+      is_under_edge_fragmentation_boundary) {
+    end_joint.block_size = LayoutUnit();
+  }
 
   block_compare = NGTableCollapsedEdge::CompareForPaint(over_edge, under_edge);
   end_joint.inline_size =
@@ -437,7 +460,27 @@
 
 namespace {
 
-bool IsFirstRowFragmented(const NGPhysicalBoxFragment& section) {
+const NGPhysicalFragment* StartSection(const NGPhysicalBoxFragment& table) {
+  for (const auto& child : table.Children()) {
+    if (!child->IsTableNGSection())
+      continue;
+    return child.get();
+  }
+  return nullptr;
+}
+
+const NGPhysicalFragment* EndSection(const NGPhysicalBoxFragment& table) {
+  const auto children = table.Children();
+  for (auto it = children.rbegin(); it != children.rend(); ++it) {
+    const auto& child = *it;
+    if (!child->IsTableNGSection())
+      continue;
+    return child.get();
+  }
+  return nullptr;
+}
+
+bool IsStartRowFragmented(const NGPhysicalBoxFragment& section) {
   for (const auto& child : section.Children()) {
     if (!child->IsTableNGRow())
       continue;
@@ -446,7 +489,7 @@
   return false;
 }
 
-bool IsLastRowFragmented(const NGPhysicalBoxFragment& section) {
+bool IsEndRowFragmented(const NGPhysicalBoxFragment& section) {
   const auto children = section.Children();
   for (auto it = children.rbegin(); it != children.rend(); ++it) {
     const auto& child = *it;
@@ -480,6 +523,13 @@
   AutoDarkMode auto_dark_mode(PaintAutoDarkMode(
       fragment_.Style(), DarkModeFilter::ElementRole::kBackground));
 
+  const wtf_size_t edges_per_row = collapsed_borders->EdgesPerRow();
+  const wtf_size_t total_row_count =
+      collapsed_borders->EdgeCount() / edges_per_row;
+
+  const auto* start_section = StartSection(fragment_);
+  const auto* end_section = EndSection(fragment_);
+
   // We paint collapsed-borders section-by-section for fragmentation purposes.
   // This means that we need to track the final row we've painted in each
   // section to avoid double painting.
@@ -495,16 +545,26 @@
     if (!section_start_row_index)
       continue;
 
-    bool is_first_row_fragmented = IsFirstRowFragmented(section);
-    bool is_last_row_fragmented = IsLastRowFragmented(section);
+    const auto& section_row_offsets = *section.TableSectionRowOffsets();
+    const wtf_size_t start_edge_index =
+        *section_start_row_index * edges_per_row;
+
+    // Determine if we have (table) content in the next/previous fragmentainer.
+    // We'll use this information to paint "half" borders if required.
+    bool has_content_in_previous_fragmentainer =
+        (start_section == &section) && (*section_start_row_index > 0u);
+    bool has_content_in_next_fragmentainer =
+        (end_section == &section) &&
+        (*section_start_row_index + section_row_offsets.size() <
+         total_row_count);
+
+    // If our row was fragmented we skip painting the borders at that edge.
+    bool is_start_row_fragmented = IsStartRowFragmented(section);
+    bool is_end_row_fragmented = IsEndRowFragmented(section);
 
     WritingModeConverter converter(fragment_.Style().GetWritingDirection(),
                                    section.Size());
 
-    const auto& section_row_offsets = *section.TableSectionRowOffsets();
-    const wtf_size_t start_edge_index =
-        *section_start_row_index * collapsed_borders->EdgesPerRow();
-
     for (NGTableCollapsedEdge edge =
              NGTableCollapsedEdge(*collapsed_borders, start_edge_index);
          edge.Exists(); ++edge) {
@@ -523,8 +583,10 @@
       if (!edge.CanPaint())
         continue;
 
-      bool is_row_start_fragmented =
-          is_first_row_fragmented && fragment_table_row == 0u;
+      bool is_start_row = fragment_table_row == 0u;
+      bool is_start_fragmented = is_start_row && is_start_row_fragmented;
+      bool is_start_at_fragmentation_boundary =
+          is_start_row && has_content_in_previous_fragmentainer;
 
       const LayoutUnit row_start_offset =
           section_row_offsets[fragment_table_row];
@@ -550,25 +612,34 @@
           continue;
         }
 
-        bool is_row_end_fragmented =
-            is_last_row_fragmented &&
-            fragment_table_row == section_row_offsets.size() - 1u;
+        bool is_end_row = fragment_table_row == section_row_offsets.size() - 1u;
+        bool is_end_fragmented = is_end_row && is_end_row_fragmented;
+        bool is_end_at_fragmentation_boundary =
+            is_end_row && has_content_in_next_fragmentainer;
 
         // If the current row has been fragmented, omit the inline border.
-        if (is_row_start_fragmented || is_row_end_fragmented)
+        if (is_start_fragmented || is_end_fragmented)
           continue;
 
         inline_start = column_start_offset;
         inline_size = collapsed_borders_geometry->columns[table_column + 1] -
                       column_start_offset;
-        block_size = edge.BorderWidth();
-        block_start = row_start_offset - edge.BorderWidth() / 2;
+        block_start = is_start_at_fragmentation_boundary
+                          ? row_start_offset
+                          : row_start_offset - edge.BorderWidth() / 2;
+        block_size = is_start_at_fragmentation_boundary ||
+                             is_end_at_fragmentation_boundary
+                         ? edge.BorderWidth() / 2
+                         : edge.BorderWidth();
+
         LogicalSize start_joint;
         LogicalSize end_joint;
         bool start_wins;
         bool end_wins;
-        ComputeEdgeJoints(*collapsed_borders, edge, start_joint, end_joint,
-                          start_wins, end_wins);
+        ComputeEdgeJoints(*collapsed_borders, edge,
+                          is_start_at_fragmentation_boundary,
+                          is_end_at_fragmentation_boundary, start_joint,
+                          end_joint, start_wins, end_wins);
         if (start_wins) {
           inline_start -= start_joint.inline_size / 2;
           inline_size += start_joint.inline_size / 2;
@@ -586,22 +657,27 @@
         if (fragment_table_row + 1 >= section_row_offsets.size())
           continue;
 
-        bool is_row_end_fragmented =
-            is_last_row_fragmented &&
+        bool is_end_row =
             fragment_table_row + 1u == section_row_offsets.size() - 1u;
+        bool is_end_fragmented = is_end_row && is_end_row_fragmented;
+        bool is_end_at_fragmentation_boundary =
+            is_end_row && has_content_in_next_fragmentainer;
 
         block_start = row_start_offset;
         block_size =
             section_row_offsets[fragment_table_row + 1] - row_start_offset;
         inline_start = column_start_offset - edge.BorderWidth() / 2;
         inline_size = edge.BorderWidth();
+
         LogicalSize start_joint;
         LogicalSize end_joint;
         bool start_wins;
         bool end_wins;
-        ComputeEdgeJoints(*collapsed_borders, edge, start_joint, end_joint,
-                          start_wins, end_wins);
-        if (is_row_start_fragmented) {
+        ComputeEdgeJoints(*collapsed_borders, edge,
+                          is_start_at_fragmentation_boundary,
+                          is_end_at_fragmentation_boundary, start_joint,
+                          end_joint, start_wins, end_wins);
+        if (is_start_fragmented) {
           // We don't need to perform any adjustment if we've been start
           // fragmented as there isn't a joint here.
         } else if (start_wins) {
@@ -611,7 +687,7 @@
           block_start += start_joint.block_size / 2;
           block_size -= start_joint.block_size / 2;
         }
-        if (is_row_end_fragmented) {
+        if (is_end_fragmented) {
           // We don't need to perform any adjustment if we've been end
           // fragmented as there isn't a joint here.
         } else if (end_wins) {
diff --git a/third_party/blink/renderer/core/paint/text_decoration_info.cc b/third_party/blink/renderer/core/paint/text_decoration_info.cc
index e488be68..a9322ea 100644
--- a/third_party/blink/renderer/core/paint/text_decoration_info.cc
+++ b/third_party/blink/renderer/core/paint/text_decoration_info.cc
@@ -2,8 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "build/build_config.h"
 #include "third_party/blink/renderer/core/paint/text_decoration_info.h"
+
+#include "build/build_config.h"
+#include "third_party/blink/renderer/core/layout/text_decoration_offset_base.h"
 #include "third_party/blink/renderer/core/paint/text_paint_style.h"
 #include "third_party/blink/renderer/core/style/computed_style.h"
 #include "third_party/blink/renderer/platform/geometry/length_functions.h"
@@ -214,6 +216,23 @@
   }
 }
 
+void TextDecorationInfo::SetUnderlineLineData(
+    const AppliedTextDecoration& decoration,
+    const TextDecorationOffsetBase& decoration_offset) {
+  ResolvedUnderlinePosition underline_position = UnderlinePosition();
+  Length line_offset;
+  if (UNLIKELY(underline_position == ResolvedUnderlinePosition::kOver)) {
+    // Don't apply text-underline-offset to overlines. |line_offset| is zero.
+    underline_position = ResolvedUnderlinePosition::kUnder;
+  } else {
+    line_offset = decoration.UnderlineOffset();
+  }
+  const int paint_underline_offset = decoration_offset.ComputeUnderlineOffset(
+      underline_position, ComputedFontSize(), FontData(), line_offset,
+      ResolvedThickness());
+  SetLineData(TextDecorationLine::kUnderline, paint_underline_offset);
+}
+
 ETextDecorationStyle TextDecorationInfo::DecorationStyle() const {
   if (IsSpellingOrGrammarError()) {
 #if BUILDFLAG(IS_MAC)
diff --git a/third_party/blink/renderer/core/paint/text_decoration_info.h b/third_party/blink/renderer/core/paint/text_decoration_info.h
index add088c4..245a064 100644
--- a/third_party/blink/renderer/core/paint/text_decoration_info.h
+++ b/third_party/blink/renderer/core/paint/text_decoration_info.h
@@ -24,6 +24,7 @@
 class ComputedStyle;
 class Font;
 class SimpleFontData;
+class TextDecorationOffsetBase;
 
 enum class ResolvedUnderlinePosition {
   kNearAlphabeticBaselineAuto,
@@ -65,6 +66,8 @@
   // through. Must be called before trying to paint or compute bounds
   // for a line.
   void SetLineData(TextDecorationLine line, float line_offset);
+  void SetUnderlineLineData(const AppliedTextDecoration& decoration,
+                            const TextDecorationOffsetBase& decoration_offset);
 
   // These methods do not depend on SetDecorationIndex
   LayoutUnit Width() const { return width_; }
diff --git a/third_party/blink/renderer/core/paint/text_painter_base.cc b/third_party/blink/renderer/core/paint/text_painter_base.cc
index e48bcd3..2f6ddb8b 100644
--- a/third_party/blink/renderer/core/paint/text_painter_base.cc
+++ b/third_party/blink/renderer/core/paint/text_painter_base.cc
@@ -257,7 +257,7 @@
 
     decoration_info.SetDecorationIndex(applied_decoration_index);
 
-    float resolved_thickness = decoration_info.ResolvedThickness();
+    const float resolved_thickness = decoration_info.ResolvedThickness();
     context.SetStrokeThickness(resolved_thickness);
 
     if (is_spelling_error || is_grammar_error) {
@@ -280,16 +280,7 @@
     }
 
     if (has_underline && decoration_info.FontData()) {
-      // Don't apply text-underline-offset to overline.
-      Length line_offset =
-          flip_underline_and_overline ? Length() : decoration.UnderlineOffset();
-
-      const int paint_underline_offset =
-          decoration_offset.ComputeUnderlineOffset(
-              underline_position, decoration_info.ComputedFontSize(),
-              decoration_info.FontData(), line_offset, resolved_thickness);
-      decoration_info.SetLineData(TextDecorationLine::kUnderline,
-                                  paint_underline_offset);
+      decoration_info.SetUnderlineLineData(decoration, decoration_offset);
       PaintDecorationUnderOrOverLine(context, decoration_info,
                                      TextDecorationLine::kUnderline, flags);
     }
diff --git a/third_party/blink/renderer/modules/accessibility/ax_node_object.cc b/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
index 33ea700..6f2ef23 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
@@ -69,6 +69,7 @@
 #include "third_party/blink/renderer/core/html/fenced_frame/html_fenced_frame_element.h"
 #include "third_party/blink/renderer/core/html/forms/html_button_element.h"
 #include "third_party/blink/renderer/core/html/forms/html_field_set_element.h"
+#include "third_party/blink/renderer/core/html/forms/html_form_control_element.h"
 #include "third_party/blink/renderer/core/html/forms/html_input_element.h"
 #include "third_party/blink/renderer/core/html/forms/html_label_element.h"
 #include "third_party/blink/renderer/core/html/forms/html_legend_element.h"
@@ -1837,12 +1838,11 @@
                : kExpandedCollapsed;
   }
 
-  // For buttons that contain the |togglepopup|, |showpopup|, or |hidepopup|
-  // popup triggering attributes, and the pointed-to element is a valid popup
-  // with type kPopup, then set aria-expanded=false when the popup is hidden,
-  // and aria-expanded=true when it is showing.
-  if (auto* button = DynamicTo<HTMLButtonElement>(element)) {
-    if (auto popup = button->togglePopupElement().element;
+  // For form controls that act as triggering elements for popups of type
+  // kPopup, then set aria-expanded=false when the popup is hidden, and
+  // aria-expanded=true when it is showing.
+  if (auto* form_control = DynamicTo<HTMLFormControlElement>(element)) {
+    if (auto popup = form_control->togglePopupElement().element;
         popup && popup->PopupType() == PopupValueType::kPopup) {
       return popup->popupOpen() ? kExpandedExpanded : kExpandedCollapsed;
     }
@@ -5600,11 +5600,10 @@
     }
   }
 
-  // For buttons that contain the |togglepopup|, |showpopup|, or |hidepopup|
-  // popup triggering attributes, and the pointed-to element is a valid popup
-  // with type kHint, then set aria-describedby to the hint popup.
-  if (auto* button = DynamicTo<HTMLButtonElement>(element)) {
-    auto popup = button->togglePopupElement();
+  // For form controls that act as triggering elements for popups of type kHint,
+  // then set aria-describedby to the hint popup.
+  if (auto* form_control = DynamicTo<HTMLFormControlElement>(element)) {
+    auto popup = form_control->togglePopupElement();
     if (popup.element && popup.element->PopupType() == PopupValueType::kHint) {
       description_from = ax::mojom::blink::DescriptionFrom::kPopupElement;
       if (description_sources) {
diff --git a/third_party/blink/renderer/modules/webaudio/analyser_handler.cc b/third_party/blink/renderer/modules/webaudio/analyser_handler.cc
index b6eff793..7af7ae2 100644
--- a/third_party/blink/renderer/modules/webaudio/analyser_handler.cc
+++ b/third_party/blink/renderer/modules/webaudio/analyser_handler.cc
@@ -12,12 +12,19 @@
 
 namespace blink {
 
+namespace {
+
+constexpr unsigned kDefaultNumberOfInputChannels = 2;
+constexpr unsigned kDefaultNumberOfOutputChannels = 1;
+
+}  // namespace
+
 AnalyserHandler::AnalyserHandler(AudioNode& node, float sample_rate)
     : AudioBasicInspectorHandler(kNodeTypeAnalyser, node, sample_rate),
       analyser_(
           node.context()->GetDeferredTaskHandler().RenderQuantumFrames()) {
-  channel_count_ = 2;
-  AddOutput(1);
+  channel_count_ = kDefaultNumberOfInputChannels;
+  AddOutput(kDefaultNumberOfOutputChannels);
 
   Initialize();
 }
diff --git a/third_party/blink/renderer/modules/webaudio/audio_basic_processor_handler.cc b/third_party/blink/renderer/modules/webaudio/audio_basic_processor_handler.cc
index 3d1ec0b..a4196e5 100644
--- a/third_party/blink/renderer/modules/webaudio/audio_basic_processor_handler.cc
+++ b/third_party/blink/renderer/modules/webaudio/audio_basic_processor_handler.cc
@@ -32,6 +32,12 @@
 
 namespace blink {
 
+namespace {
+
+constexpr unsigned kDefaultNumberOfOutputChannels = 1;
+
+}  // namespace
+
 AudioBasicProcessorHandler::AudioBasicProcessorHandler(
     NodeType node_type,
     AudioNode& node,
@@ -40,7 +46,7 @@
     : AudioHandler(node_type, node, sample_rate),
       processor_(std::move(processor)) {
   AddInput();
-  AddOutput(1);
+  AddOutput(kDefaultNumberOfOutputChannels);
 }
 
 AudioBasicProcessorHandler::~AudioBasicProcessorHandler() {
diff --git a/third_party/blink/renderer/modules/webaudio/audio_basic_processor_handler_test.cc b/third_party/blink/renderer/modules/webaudio/audio_basic_processor_handler_test.cc
index 8fc7bf34d..f7bee8e0 100644
--- a/third_party/blink/renderer/modules/webaudio/audio_basic_processor_handler_test.cc
+++ b/third_party/blink/renderer/modules/webaudio/audio_basic_processor_handler_test.cc
@@ -15,8 +15,12 @@
 
 namespace blink {
 
+namespace {
+
 // Rendering size for these tests.  This is the WebAudio default rendering size.
-const unsigned kRenderQuantumFrames = 128;
+constexpr unsigned kRenderQuantumFrames = 128;
+
+}  // namespace
 
 class MockAudioProcessor final : public AudioProcessor {
  public:
diff --git a/third_party/blink/renderer/modules/webaudio/audio_buffer_source_handler.cc b/third_party/blink/renderer/modules/webaudio/audio_buffer_source_handler.cc
index e7a6f94..18f1931 100644
--- a/third_party/blink/renderer/modules/webaudio/audio_buffer_source_handler.cc
+++ b/third_party/blink/renderer/modules/webaudio/audio_buffer_source_handler.cc
@@ -21,12 +21,20 @@
 
 namespace blink {
 
-const double kDefaultGrainDuration = 0.020;  // 20ms
+namespace {
+
+constexpr double kDefaultGrainDuration = 0.020;  // 20ms
 
 // Arbitrary upper limit on playback rate.
 // Higher than expected rates can be useful when playing back oversampled
 // buffers to minimize linear interpolation aliasing.
-const double kMaxRate = 1024;
+constexpr double kMaxRate = 1024.0;
+
+// Default to mono. A call to setBuffer() will set the number of output
+// channels to that of the buffer.
+constexpr unsigned kDefaultNumberOfOutputChannels = 1;
+
+}  // namespace
 
 AudioBufferSourceHandler::AudioBufferSourceHandler(
     AudioNode& node,
@@ -39,9 +47,7 @@
       playback_rate_(&playback_rate),
       detune_(&detune),
       grain_duration_(kDefaultGrainDuration) {
-  // Default to mono. A call to setBuffer() will set the number of output
-  // channels to that of the buffer.
-  AddOutput(1);
+  AddOutput(kDefaultNumberOfOutputChannels);
 
   Initialize();
 }
diff --git a/third_party/blink/renderer/modules/webaudio/audio_buffer_source_node.cc b/third_party/blink/renderer/modules/webaudio/audio_buffer_source_node.cc
index 81e525c..566168c 100644
--- a/third_party/blink/renderer/modules/webaudio/audio_buffer_source_node.cc
+++ b/third_party/blink/renderer/modules/webaudio/audio_buffer_source_node.cc
@@ -42,20 +42,27 @@
 
 namespace blink {
 
+namespace {
+
+constexpr double kDefaultPlaybackRateValue = 1.0;
+constexpr double kDefaultDetuneValue = 0.0;
+
+}  // namespace
+
 AudioBufferSourceNode::AudioBufferSourceNode(BaseAudioContext& context)
     : AudioScheduledSourceNode(context),
       playback_rate_(AudioParam::Create(
           context,
           Uuid(),
           AudioParamHandler::kParamTypeAudioBufferSourcePlaybackRate,
-          1.0,
+          kDefaultPlaybackRateValue,
           AudioParamHandler::AutomationRate::kControl,
           AudioParamHandler::AutomationRateMode::kFixed)),
       detune_(AudioParam::Create(
           context,
           Uuid(),
           AudioParamHandler::kParamTypeAudioBufferSourceDetune,
-          0.0,
+          kDefaultDetuneValue,
           AudioParamHandler::AutomationRate::kControl,
           AudioParamHandler::AutomationRateMode::kFixed)) {
   SetHandler(AudioBufferSourceHandler::Create(*this, context.sampleRate(),
diff --git a/third_party/blink/renderer/modules/webaudio/audio_context.cc b/third_party/blink/renderer/modules/webaudio/audio_context.cc
index 138289a..cfed8b2 100644
--- a/third_party/blink/renderer/modules/webaudio/audio_context.cc
+++ b/third_party/blink/renderer/modules/webaudio/audio_context.cc
@@ -47,15 +47,15 @@
 
 namespace blink {
 
+namespace {
+
 // Number of AudioContexts still alive.  It's incremented when an
 // AudioContext is created and decremented when the context is closed.
-static unsigned g_hardware_context_count = 0;
+unsigned hardware_context_count = 0;
 
 // A context ID that is incremented for each context that is created.
 // This initializes the internal id for the context.
-static unsigned g_context_id = 0;
-
-namespace {
+unsigned context_id = 0;
 
 // When the client does not have enough permission, the outputLatency property
 // is quantized by 8ms to reduce the precision for privacy concerns.
@@ -96,7 +96,7 @@
   return builder.ToString();
 }
 
-static bool IsAudible(const AudioBus* rendered_data) {
+bool IsAudible(const AudioBus* rendered_data) {
   // Compute the energy in each channel and sum up the energy in each channel
   // for the total energy.
   float energy = 0;
@@ -174,7 +174,7 @@
   SCOPED_UMA_HISTOGRAM_TIMER("WebAudio.AudioContext.CreateTime");
   AudioContext* audio_context =
       MakeGarbageCollected<AudioContext>(document, latency_hint, sample_rate);
-  ++g_hardware_context_count;
+  ++hardware_context_count;
   audio_context->UpdateStateIfNeeded();
 
   // This starts the audio thread. The destination node's
@@ -192,7 +192,7 @@
   }
 #if DEBUG_AUDIONODE_REFERENCES
   fprintf(stderr, "[%16p]: AudioContext::AudioContext(): %u #%u\n",
-          audio_context, audio_context->context_id_, g_hardware_context_count);
+          audio_context, audio_context->context_id_, hardware_context_count);
 #endif
 
   base::UmaHistogramSparse("WebAudio.AudioContext.MaxChannelsAvailable",
@@ -207,7 +207,7 @@
                            const WebAudioLatencyHint& latency_hint,
                            absl::optional<float> sample_rate)
     : BaseAudioContext(&document, kRealtimeContext),
-      context_id_(g_context_id++),
+      context_id_(context_id++),
       audio_context_manager_(document.GetExecutionContext()),
       permission_service_(document.GetExecutionContext()),
       permission_receiver_(this, document.GetExecutionContext()) {
@@ -271,9 +271,9 @@
 
 void AudioContext::Uninitialize() {
   DCHECK(IsMainThread());
-  DCHECK_NE(g_hardware_context_count, 0u);
+  DCHECK_NE(hardware_context_count, 0u);
   SendLogMessage(String::Format("%s", __func__));
-  --g_hardware_context_count;
+  --hardware_context_count;
   StopRendering();
   DidClose();
   RecordAutoplayMetrics();
diff --git a/third_party/blink/renderer/modules/webaudio/audio_context_autoplay_test.cc b/third_party/blink/renderer/modules/webaudio/audio_context_autoplay_test.cc
index be2d93fd..61675c9 100644
--- a/third_party/blink/renderer/modules/webaudio/audio_context_autoplay_test.cc
+++ b/third_party/blink/renderer/modules/webaudio/audio_context_autoplay_test.cc
@@ -67,7 +67,7 @@
   size_t AudioHardwareBufferSize() override { return 128; }
 };
 
-}  // anonymous namespace
+}  // namespace
 
 class AudioContextAutoplayTest
     : public testing::TestWithParam<AutoplayPolicy::Type> {
diff --git a/third_party/blink/renderer/modules/webaudio/audio_context_test.cc b/third_party/blink/renderer/modules/webaudio/audio_context_test.cc
index ee486ca..3560c7c 100644
--- a/third_party/blink/renderer/modules/webaudio/audio_context_test.cc
+++ b/third_party/blink/renderer/modules/webaudio/audio_context_test.cc
@@ -23,7 +23,8 @@
 namespace blink {
 
 namespace {
-static bool web_audio_device_paused_;
+
+bool web_audio_device_paused_;
 
 class MockWebAudioDeviceForAudioContext : public WebAudioDevice {
  public:
@@ -85,7 +86,7 @@
   size_t AudioHardwareBufferSize() override { return 128; }
 };
 
-}  // anonymous namespace
+}  // namespace
 
 class AudioContextTest : public PageTestBase {
  protected:
diff --git a/third_party/blink/renderer/modules/webaudio/audio_listener.cc b/third_party/blink/renderer/modules/webaudio/audio_listener.cc
index 7aefa197..dfefe90 100644
--- a/third_party/blink/renderer/modules/webaudio/audio_listener.cc
+++ b/third_party/blink/renderer/modules/webaudio/audio_listener.cc
@@ -36,69 +36,83 @@
 
 namespace blink {
 
+namespace {
+
+constexpr double kDefaultPositionXValue = 0.0;
+constexpr double kDefaultPositionYValue = 0.0;
+constexpr double kDefaultPositionZValue = 0.0;
+constexpr double kDefaultForwardXValue = 0.0;
+constexpr double kDefaultForwardYValue = 0.0;
+constexpr double kDefaultForwardZValue = -1.0;
+constexpr double kDefaultUpXValue = 0.0;
+constexpr double kDefaultUpYValue = 1.0;
+constexpr double kDefaultUpZValue = 0.0;
+
+}  // namespace
+
 AudioListener::AudioListener(BaseAudioContext& context)
     : InspectorHelperMixin(context.GraphTracer(), context.Uuid()),
       position_x_(AudioParam::Create(
           context,
           Uuid(),
           AudioParamHandler::kParamTypeAudioListenerPositionX,
-          0.0,
+          kDefaultPositionXValue,
           AudioParamHandler::AutomationRate::kAudio,
           AudioParamHandler::AutomationRateMode::kVariable)),
       position_y_(AudioParam::Create(
           context,
           Uuid(),
           AudioParamHandler::kParamTypeAudioListenerPositionY,
-          0.0,
+          kDefaultPositionYValue,
           AudioParamHandler::AutomationRate::kAudio,
           AudioParamHandler::AutomationRateMode::kVariable)),
       position_z_(AudioParam::Create(
           context,
           Uuid(),
           AudioParamHandler::kParamTypeAudioListenerPositionZ,
-          0.0,
+          kDefaultPositionZValue,
           AudioParamHandler::AutomationRate::kAudio,
           AudioParamHandler::AutomationRateMode::kVariable)),
       forward_x_(
           AudioParam::Create(context,
                              Uuid(),
                              AudioParamHandler::kParamTypeAudioListenerForwardX,
-                             0.0,
+                             kDefaultForwardXValue,
                              AudioParamHandler::AutomationRate::kAudio,
                              AudioParamHandler::AutomationRateMode::kVariable)),
       forward_y_(
           AudioParam::Create(context,
                              Uuid(),
                              AudioParamHandler::kParamTypeAudioListenerForwardY,
-                             0.0,
+                             kDefaultForwardYValue,
                              AudioParamHandler::AutomationRate::kAudio,
                              AudioParamHandler::AutomationRateMode::kVariable)),
       forward_z_(
           AudioParam::Create(context,
                              Uuid(),
                              AudioParamHandler::kParamTypeAudioListenerForwardZ,
-                             -1.0,
+                             kDefaultForwardZValue,
                              AudioParamHandler::AutomationRate::kAudio,
                              AudioParamHandler::AutomationRateMode::kVariable)),
       up_x_(
           AudioParam::Create(context,
                              Uuid(),
                              AudioParamHandler::kParamTypeAudioListenerUpX,
-                             0.0,
+                             kDefaultUpXValue,
                              AudioParamHandler::AutomationRate::kAudio,
                              AudioParamHandler::AutomationRateMode::kVariable)),
       up_y_(
           AudioParam::Create(context,
                              Uuid(),
                              AudioParamHandler::kParamTypeAudioListenerUpY,
-                             1.0,
+                             kDefaultUpYValue,
                              AudioParamHandler::AutomationRate::kAudio,
                              AudioParamHandler::AutomationRateMode::kVariable)),
       up_z_(
           AudioParam::Create(context,
                              Uuid(),
                              AudioParamHandler::kParamTypeAudioListenerUpZ,
-                             0.0,
+                             kDefaultUpZValue,
                              AudioParamHandler::AutomationRate::kAudio,
                              AudioParamHandler::AutomationRateMode::kVariable)),
       position_x_values_(
diff --git a/third_party/blink/renderer/modules/webaudio/audio_param_handler.cc b/third_party/blink/renderer/modules/webaudio/audio_param_handler.cc
index de057de..6c931b88 100644
--- a/third_party/blink/renderer/modules/webaudio/audio_param_handler.cc
+++ b/third_party/blink/renderer/modules/webaudio/audio_param_handler.cc
@@ -24,13 +24,15 @@
 
 namespace blink {
 
-const double AudioParamHandler::kDefaultSmoothingConstant = 0.05;
-const double AudioParamHandler::kSnapThreshold = 0.001;
+namespace {
+
+constexpr double kDefaultSmoothingConstant = 0.05;
+constexpr double kSnapThreshold = 0.001;
 
 // Replace NaN values in `values` with `default_value`.
-static void HandleNaNValues(float* values,
-                            unsigned number_of_values,
-                            float default_value) {
+void HandleNaNValues(float* values,
+                     unsigned number_of_values,
+                     float default_value) {
   unsigned k = 0;
 #if defined(ARCH_CPU_X86_FAMILY)
   if (number_of_values >= 4) {
@@ -72,6 +74,8 @@
   }
 }
 
+}  // namespace
+
 AudioParamHandler::AudioParamHandler(BaseAudioContext& context,
                                      AudioParamType param_type,
                                      double default_value,
diff --git a/third_party/blink/renderer/modules/webaudio/audio_param_handler.h b/third_party/blink/renderer/modules/webaudio/audio_param_handler.h
index a866fc6..e9c2bb0 100644
--- a/third_party/blink/renderer/modules/webaudio/audio_param_handler.h
+++ b/third_party/blink/renderer/modules/webaudio/audio_param_handler.h
@@ -98,9 +98,6 @@
   // Return a nice name for the AudioParam.
   String GetParamName() const;
 
-  static const double kDefaultSmoothingConstant;
-  static const double kSnapThreshold;
-
   static scoped_refptr<AudioParamHandler> Create(BaseAudioContext& context,
                                                  AudioParamType param_type,
                                                  double default_value,
diff --git a/third_party/blink/renderer/modules/webaudio/audio_param_timeline.cc b/third_party/blink/renderer/modules/webaudio/audio_param_timeline.cc
index 9e336c0..a873c2b1 100644
--- a/third_party/blink/renderer/modules/webaudio/audio_param_timeline.cc
+++ b/third_party/blink/renderer/modules/webaudio/audio_param_timeline.cc
@@ -47,16 +47,18 @@
 
 namespace blink {
 
-// For a SetTarget event, we want the event to terminate eventually so that
-// we can stop using the timeline to compute the values.  See
+namespace {
+
+// For a SetTarget event, we want the event to terminate eventually so that we
+// can stop using the timeline to compute the values.  See
 // `HasSetTargetConverged()` for the algorithm.  `kSetTargetThreshold` is
 // exp(-`kTimeConstantsToConverge`).
-const float kTimeConstantsToConverge = 10;
-const float kSetTargetThreshold = 4.539992976248485e-05;
+constexpr float kTimeConstantsToConverge = 10.0f;
+constexpr float kSetTargetThreshold = 4.539992976248485e-05f;
 
-static bool IsNonNegativeAudioParamTime(double time,
-                                        ExceptionState& exception_state,
-                                        String message = "Time") {
+bool IsNonNegativeAudioParamTime(double time,
+                                 ExceptionState& exception_state,
+                                 String message = "Time") {
   if (time >= 0) {
     return true;
   }
@@ -67,9 +69,9 @@
   return false;
 }
 
-static bool IsPositiveAudioParamTime(double time,
-                                     ExceptionState& exception_state,
-                                     String message) {
+bool IsPositiveAudioParamTime(double time,
+                              ExceptionState& exception_state,
+                              String message) {
   if (time > 0) {
     return true;
   }
@@ -82,11 +84,11 @@
 // Test that for a SetTarget event, the current value is close enough
 // to the target value that we can consider the event to have
 // converged to the target.
-static bool HasSetTargetConverged(float value,
-                                  float target,
-                                  double current_time,
-                                  double start_time,
-                                  double time_constant) {
+bool HasSetTargetConverged(float value,
+                           float target,
+                           double current_time,
+                           double start_time,
+                           double time_constant) {
   // Converged if enough time constants (`kTimeConstantsToConverge`) have passed
   // since the start of the event.
   if (current_time > start_time + kTimeConstantsToConverge * time_constant) {
@@ -109,6 +111,8 @@
   return false;
 }
 
+}  // namespace
+
 String AudioParamTimeline::EventToString(const ParamEvent& event) const {
   // The default arguments for most automation methods is the value and the
   // time.
diff --git a/third_party/blink/renderer/modules/webaudio/audio_scheduled_source_handler.cc b/third_party/blink/renderer/modules/webaudio/audio_scheduled_source_handler.cc
index c7c6e2c..0971963 100644
--- a/third_party/blink/renderer/modules/webaudio/audio_scheduled_source_handler.cc
+++ b/third_party/blink/renderer/modules/webaudio/audio_scheduled_source_handler.cc
@@ -20,8 +20,6 @@
 
 namespace blink {
 
-const double AudioScheduledSourceHandler::kUnknownTime = -1;
-
 AudioScheduledSourceHandler::AudioScheduledSourceHandler(NodeType node_type,
                                                          AudioNode& node,
                                                          float sample_rate)
diff --git a/third_party/blink/renderer/modules/webaudio/audio_scheduled_source_handler.h b/third_party/blink/renderer/modules/webaudio/audio_scheduled_source_handler.h
index 710b4e1..1f40b83 100644
--- a/third_party/blink/renderer/modules/webaudio/audio_scheduled_source_handler.h
+++ b/third_party/blink/renderer/modules/webaudio/audio_scheduled_source_handler.h
@@ -131,15 +131,13 @@
   double end_time_;  // in seconds
 
   // Magic value indicating that the time (start or end) has not yet been set.
-  static const double kUnknownTime;
+  static constexpr double kUnknownTime = -1.0;
 
   // Number of extra frames to use when determining if a source node can be
   // stopped.  This should be at least one rendering quantum, but we add one
   // more quantum for good measure.  This doesn't need to be extra precise, just
   // more than one rendering quantum.  See `HandleStoppableSourceNode()`.
-  // FIXME: Expose the rendering quantum somehow instead of hardwiring a value
-  // here.
-  static const int kExtraStopFrames = 256;
+  static constexpr int kExtraStopFrames = 256;
 
  private:
   // This is accessed by both the main thread and audio thread.  Use the setter
diff --git a/third_party/blink/renderer/modules/webaudio/audio_worklet_global_scope.h b/third_party/blink/renderer/modules/webaudio/audio_worklet_global_scope.h
index fc6f2d4..95048d58 100644
--- a/third_party/blink/renderer/modules/webaudio/audio_worklet_global_scope.h
+++ b/third_party/blink/renderer/modules/webaudio/audio_worklet_global_scope.h
@@ -128,7 +128,7 @@
   std::unique_ptr<ProcessorCreationParams> processor_creation_params_;
 
   size_t current_frame_ = 0;
-  float sample_rate_ = 0.0;
+  float sample_rate_ = 0.0f;
 
   // Default initialized to generate a distinct token for this worklet.
   const AudioWorkletToken token_;
diff --git a/third_party/blink/renderer/modules/webaudio/audio_worklet_global_scope_test.cc b/third_party/blink/renderer/modules/webaudio/audio_worklet_global_scope_test.cc
index c1e038d..f52ec6a 100644
--- a/third_party/blink/renderer/modules/webaudio/audio_worklet_global_scope_test.cc
+++ b/third_party/blink/renderer/modules/webaudio/audio_worklet_global_scope_test.cc
@@ -51,7 +51,7 @@
 
 namespace {
 
-static const size_t kRenderQuantumFrames = 128;
+constexpr size_t kRenderQuantumFrames = 128;
 
 }  // namespace
 
diff --git a/third_party/blink/renderer/modules/webaudio/audio_worklet_handler.cc b/third_party/blink/renderer/modules/webaudio/audio_worklet_handler.cc
index d5a91263..950a897 100644
--- a/third_party/blink/renderer/modules/webaudio/audio_worklet_handler.cc
+++ b/third_party/blink/renderer/modules/webaudio/audio_worklet_handler.cc
@@ -33,6 +33,12 @@
 
 namespace blink {
 
+namespace {
+
+constexpr unsigned kDefaultNumberOfOutputChannels = 1;
+
+}  // namespace
+
 AudioWorkletHandler::AudioWorkletHandler(
     AudioNode& node,
     float sample_rate,
@@ -63,7 +69,7 @@
   for (unsigned i = 0; i < options->numberOfOutputs(); ++i) {
     // If `options->outputChannelCount` unspecified, all outputs are mono.
     AddOutput(is_output_channel_count_given_ ? options->outputChannelCount()[i]
-                                             : 1);
+                                             : kDefaultNumberOfOutputChannels);
   }
   // Same for the outputs as well.
   outputs_.ReserveInitialCapacity(options->numberOfOutputs());
diff --git a/third_party/blink/renderer/modules/webaudio/base_audio_context.h b/third_party/blink/renderer/modules/webaudio/base_audio_context.h
index 2c7df9c..f813c1c 100644
--- a/third_party/blink/renderer/modules/webaudio/base_audio_context.h
+++ b/third_party/blink/renderer/modules/webaudio/base_audio_context.h
@@ -390,7 +390,7 @@
  private:
   // This is considering 32 is large enough for multiple channels audio.
   // It is somewhat arbitrary and could be increased if necessary.
-  enum { kMaxNumberOfChannels = 32 };
+  static constexpr uint32_t kMaxNumberOfChannels = 32;
 
   void Clear();
 
diff --git a/third_party/blink/renderer/modules/webaudio/biquad_dsp_kernel.cc b/third_party/blink/renderer/modules/webaudio/biquad_dsp_kernel.cc
index b0995f47f..ffd599b 100644
--- a/third_party/blink/renderer/modules/webaudio/biquad_dsp_kernel.cc
+++ b/third_party/blink/renderer/modules/webaudio/biquad_dsp_kernel.cc
@@ -31,7 +31,9 @@
 
 namespace blink {
 
-static bool hasConstantValues(float* values, int frames_to_process) {
+namespace {
+
+bool HasConstantValues(float* values, int frames_to_process) {
   // TODO(rtoy): Use SIMD to optimize this.  This would speed up
   // processing by a factor of 4 because we can process 4 floats at a
   // time.
@@ -46,6 +48,8 @@
   return true;
 }
 
+}  // namespace
+
 void BiquadDSPKernel::UpdateCoefficientsIfNecessary(int frames_to_process) {
   if (GetBiquadProcessor()->FilterCoefficientsDirty()) {
     float cutoff_frequency[RenderQuantumFrames()];
@@ -72,10 +76,10 @@
       // to compute filter coefficients for each frame since they would be the
       // same as the first.
       bool isConstant =
-          hasConstantValues(cutoff_frequency, frames_to_process) &&
-          hasConstantValues(q, frames_to_process) &&
-          hasConstantValues(gain, frames_to_process) &&
-          hasConstantValues(detune, frames_to_process);
+          HasConstantValues(cutoff_frequency, frames_to_process) &&
+          HasConstantValues(q, frames_to_process) &&
+          HasConstantValues(gain, frames_to_process) &&
+          HasConstantValues(detune, frames_to_process);
 
       UpdateCoefficients(isConstant ? 1 : frames_to_process, cutoff_frequency,
                          q, gain, detune);
@@ -154,7 +158,7 @@
   // this, limit the maximum to this value so that we don't keep such
   // nodes alive "forever".
   // TODO: What is a reasonable upper limit?
-  const double kMaxTailTime = 30;
+  constexpr double kMaxTailTime = 30.0;
 
   double sample_rate = SampleRate();
   double tail =
diff --git a/third_party/blink/renderer/modules/webaudio/biquad_filter_node.cc b/third_party/blink/renderer/modules/webaudio/biquad_filter_node.cc
index f3937c2..79aff99 100644
--- a/third_party/blink/renderer/modules/webaudio/biquad_filter_node.cc
+++ b/third_party/blink/renderer/modules/webaudio/biquad_filter_node.cc
@@ -36,40 +36,52 @@
 
 namespace blink {
 
+namespace {
+
+constexpr double kDefaultFrequencyValue = 350.0;
+constexpr float kMinFrequencyValue = 0.0f;
+constexpr double kDefaultQValue = 1.0;
+constexpr double kDefaultGainValue = 0.0;
+constexpr float kMinGainValue = std::numeric_limits<float>::lowest();
+constexpr double kDefaultDetuneValue = 0.0;
+
+}  // namespace
+
 BiquadFilterNode::BiquadFilterNode(BaseAudioContext& context)
     : AudioNode(context),
       frequency_(
           AudioParam::Create(context,
                              Uuid(),
                              AudioParamHandler::kParamTypeBiquadFilterFrequency,
-                             350.0,
+                             kDefaultFrequencyValue,
                              AudioParamHandler::AutomationRate::kAudio,
                              AudioParamHandler::AutomationRateMode::kVariable,
-                             0,
-                             context.sampleRate() / 2)),
+                             kMinFrequencyValue,
+                             /*max_value=*/context.sampleRate() / 2)),
       q_(AudioParam::Create(context,
                             Uuid(),
                             AudioParamHandler::kParamTypeBiquadFilterQ,
-                            1.0,
+                            kDefaultQValue,
                             AudioParamHandler::AutomationRate::kAudio,
                             AudioParamHandler::AutomationRateMode::kVariable)),
-      gain_(AudioParam::Create(context,
-                               Uuid(),
-                               AudioParamHandler::kParamTypeBiquadFilterGain,
-                               0.0,
-                               AudioParamHandler::AutomationRate::kAudio,
-                               AudioParamHandler::AutomationRateMode::kVariable,
-                               std::numeric_limits<float>::lowest(),
-                               40 * log10f(std::numeric_limits<float>::max()))),
-      detune_(
-          AudioParam::Create(context,
-                             Uuid(),
-                             AudioParamHandler::kParamTypeBiquadFilterDetune,
-                             0.0,
-                             AudioParamHandler::AutomationRate::kAudio,
-                             AudioParamHandler::AutomationRateMode::kVariable,
-                             -1200 * log2f(std::numeric_limits<float>::max()),
-                             1200 * log2f(std::numeric_limits<float>::max()))) {
+      gain_(AudioParam::Create(
+          context,
+          Uuid(),
+          AudioParamHandler::kParamTypeBiquadFilterGain,
+          kDefaultGainValue,
+          AudioParamHandler::AutomationRate::kAudio,
+          AudioParamHandler::AutomationRateMode::kVariable,
+          kMinGainValue,
+          /*max_value=*/40 * log10f(std::numeric_limits<float>::max()))),
+      detune_(AudioParam::Create(
+          context,
+          Uuid(),
+          AudioParamHandler::kParamTypeBiquadFilterDetune,
+          kDefaultDetuneValue,
+          AudioParamHandler::AutomationRate::kAudio,
+          AudioParamHandler::AutomationRateMode::kVariable,
+          /*min_value=*/-1200 * log2f(std::numeric_limits<float>::max()),
+          /*max_value=*/1200 * log2f(std::numeric_limits<float>::max()))) {
   SetHandler(BiquadFilterHandler::Create(*this, context.sampleRate(),
                                          frequency_->Handler(), q_->Handler(),
                                          gain_->Handler(), detune_->Handler()));
diff --git a/third_party/blink/renderer/modules/webaudio/channel_merger_handler.cc b/third_party/blink/renderer/modules/webaudio/channel_merger_handler.cc
index 4baad5ba..a23abd8 100644
--- a/third_party/blink/renderer/modules/webaudio/channel_merger_handler.cc
+++ b/third_party/blink/renderer/modules/webaudio/channel_merger_handler.cc
@@ -11,12 +11,18 @@
 
 namespace blink {
 
+namespace {
+
+constexpr unsigned kNumberOfInputChannels = 1;
+
+}  // namespace
+
 ChannelMergerHandler::ChannelMergerHandler(AudioNode& node,
                                            float sample_rate,
                                            unsigned number_of_inputs)
     : AudioHandler(kNodeTypeChannelMerger, node, sample_rate) {
   // These properties are fixed for the node and cannot be changed by user.
-  channel_count_ = 1;
+  channel_count_ = kNumberOfInputChannels;
   SetInternalChannelCountMode(kExplicit);
 
   // Create the requested number of inputs.
diff --git a/third_party/blink/renderer/modules/webaudio/channel_merger_node.cc b/third_party/blink/renderer/modules/webaudio/channel_merger_node.cc
index 740a789..885419d 100644
--- a/third_party/blink/renderer/modules/webaudio/channel_merger_node.cc
+++ b/third_party/blink/renderer/modules/webaudio/channel_merger_node.cc
@@ -37,6 +37,13 @@
 
 namespace blink {
 
+namespace {
+
+// The default number of inputs for the merger node is 6.
+constexpr unsigned kDefaultNumberOfInputs = 6;
+
+}  // namespace
+
 ChannelMergerNode::ChannelMergerNode(BaseAudioContext& context,
                                      unsigned number_of_inputs)
     : AudioNode(context) {
@@ -48,8 +55,7 @@
                                              ExceptionState& exception_state) {
   DCHECK(IsMainThread());
 
-  // The default number of inputs for the merger node is 6.
-  return Create(context, 6, exception_state);
+  return Create(context, kDefaultNumberOfInputs, exception_state);
 }
 
 ChannelMergerNode* ChannelMergerNode::Create(BaseAudioContext& context,
diff --git a/third_party/blink/renderer/modules/webaudio/channel_splitter_handler.cc b/third_party/blink/renderer/modules/webaudio/channel_splitter_handler.cc
index 8847ee2..76fd1206 100644
--- a/third_party/blink/renderer/modules/webaudio/channel_splitter_handler.cc
+++ b/third_party/blink/renderer/modules/webaudio/channel_splitter_handler.cc
@@ -11,6 +11,12 @@
 
 namespace blink {
 
+namespace {
+
+constexpr unsigned kNumberOfOutputChannels = 1;
+
+}  // namespace
+
 ChannelSplitterHandler::ChannelSplitterHandler(AudioNode& node,
                                                float sample_rate,
                                                unsigned number_of_outputs)
@@ -24,7 +30,7 @@
   // Create a fixed number of outputs (able to handle the maximum number of
   // channels fed to an input).
   for (unsigned i = 0; i < number_of_outputs; ++i) {
-    AddOutput(1);
+    AddOutput(kNumberOfOutputChannels);
   }
 
   Initialize();
diff --git a/third_party/blink/renderer/modules/webaudio/channel_splitter_node.cc b/third_party/blink/renderer/modules/webaudio/channel_splitter_node.cc
index 36c3f0f9..44dc7348 100644
--- a/third_party/blink/renderer/modules/webaudio/channel_splitter_node.cc
+++ b/third_party/blink/renderer/modules/webaudio/channel_splitter_node.cc
@@ -34,6 +34,13 @@
 
 namespace blink {
 
+namespace {
+
+// Default number of outputs for the splitter node is 6.
+constexpr unsigned kDefaultNumberOfOutputs = 6;
+
+}  // namespace
+
 ChannelSplitterNode::ChannelSplitterNode(BaseAudioContext& context,
                                          unsigned number_of_outputs)
     : AudioNode(context) {
@@ -46,8 +53,7 @@
     ExceptionState& exception_state) {
   DCHECK(IsMainThread());
 
-  // Default number of outputs for the splitter node is 6.
-  return Create(context, 6, exception_state);
+  return Create(context, kDefaultNumberOfOutputs, exception_state);
 }
 
 ChannelSplitterNode* ChannelSplitterNode::Create(
diff --git a/third_party/blink/renderer/modules/webaudio/constant_source_handler.cc b/third_party/blink/renderer/modules/webaudio/constant_source_handler.cc
index fbd27ce..8cddbaf 100644
--- a/third_party/blink/renderer/modules/webaudio/constant_source_handler.cc
+++ b/third_party/blink/renderer/modules/webaudio/constant_source_handler.cc
@@ -13,7 +13,7 @@
 namespace {
 
 // A ConstantSource is always mono.
-constexpr unsigned kNumberOfOutputs = 1;
+constexpr unsigned kNumberOfOutputChannels = 1;
 
 }  // namespace
 
@@ -23,7 +23,7 @@
     : AudioScheduledSourceHandler(kNodeTypeConstantSource, node, sample_rate),
       offset_(&offset),
       sample_accurate_values_(GetDeferredTaskHandler().RenderQuantumFrames()) {
-  AddOutput(kNumberOfOutputs);
+  AddOutput(kNumberOfOutputChannels);
 
   Initialize();
 }
diff --git a/third_party/blink/renderer/modules/webaudio/constant_source_node.cc b/third_party/blink/renderer/modules/webaudio/constant_source_node.cc
index eca6b31..336790a 100644
--- a/third_party/blink/renderer/modules/webaudio/constant_source_node.cc
+++ b/third_party/blink/renderer/modules/webaudio/constant_source_node.cc
@@ -11,13 +11,19 @@
 
 namespace blink {
 
+namespace {
+
+constexpr double kDefaultOffsetValue = 1.0;
+
+}  // namespace
+
 ConstantSourceNode::ConstantSourceNode(BaseAudioContext& context)
     : AudioScheduledSourceNode(context),
       offset_(AudioParam::Create(
           context,
           Uuid(),
           AudioParamHandler::kParamTypeConstantSourceOffset,
-          1,
+          kDefaultOffsetValue,
           AudioParamHandler::AutomationRate::kAudio,
           AudioParamHandler::AutomationRateMode::kVariable)) {
   SetHandler(ConstantSourceHandler::Create(*this, context.sampleRate(),
diff --git a/third_party/blink/renderer/modules/webaudio/convolver_handler.cc b/third_party/blink/renderer/modules/webaudio/convolver_handler.cc
index 8ea1bdc..b831d37 100644
--- a/third_party/blink/renderer/modules/webaudio/convolver_handler.cc
+++ b/third_party/blink/renderer/modules/webaudio/convolver_handler.cc
@@ -16,6 +16,10 @@
 #include "third_party/blink/renderer/platform/bindings/exception_messages.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
 
+namespace blink {
+
+namespace {
+
 // Note about empirical tuning:
 // The maximum FFT size affects reverb performance and accuracy.
 // If the reverb is single-threaded and processes entirely in the real-time
@@ -23,17 +27,20 @@
 // a good value.  But, the Reverb object is multi-threaded, so we want this as
 // high as possible without losing too much accuracy.  Very large FFTs will have
 // worse phase errors. Given these constraints 32768 is a good compromise.
-const unsigned MaxFFTSize = 32768;
+constexpr unsigned kMaxFftSize = 32768;
 
-namespace blink {
+constexpr unsigned kDefaultNumberOfInputChannels = 2;
+constexpr unsigned kDefaultNumberOfOutputChannels = 1;
+
+}  // namespace
 
 ConvolverHandler::ConvolverHandler(AudioNode& node, float sample_rate)
     : AudioHandler(kNodeTypeConvolver, node, sample_rate) {
   AddInput();
-  AddOutput(1);
+  AddOutput(kDefaultNumberOfOutputChannels);
 
   // Node-specific default mixing rules.
-  channel_count_ = 2;
+  channel_count_ = kDefaultNumberOfInputChannels;
   SetInternalChannelCountMode(kClampedMax);
   SetInternalChannelInterpretation(AudioBus::kSpeakers);
 
@@ -165,7 +172,7 @@
   // Create the reverb with the given impulse response.
   std::unique_ptr<Reverb> reverb = std::make_unique<Reverb>(
       buffer_bus.get(), GetDeferredTaskHandler().RenderQuantumFrames(),
-      MaxFFTSize, Context() && Context()->HasRealtimeConstraint(), normalize_);
+      kMaxFftSize, Context() && Context()->HasRealtimeConstraint(), normalize_);
 
   {
     // The context must be locked since changing the buffer can
diff --git a/third_party/blink/renderer/modules/webaudio/cpu/arm/oscillator_kernel_neon.cc b/third_party/blink/renderer/modules/webaudio/cpu/arm/oscillator_kernel_neon.cc
index 6db3f21..674cfd6b4 100644
--- a/third_party/blink/renderer/modules/webaudio/cpu/arm/oscillator_kernel_neon.cc
+++ b/third_party/blink/renderer/modules/webaudio/cpu/arm/oscillator_kernel_neon.cc
@@ -13,9 +13,11 @@
 namespace blink {
 
 #if defined(CPU_ARM_NEON)
-static float32x4_t WrapVirtualIndexVector(float32x4_t x,
-                                          float32x4_t wave_size,
-                                          float32x4_t inv_wave_size) {
+namespace {
+
+float32x4_t WrapVirtualIndexVector(float32x4_t x,
+                                   float32x4_t wave_size,
+                                   float32x4_t inv_wave_size) {
   // r = x/wave_size, f = truncate(r), truncating towards 0
   const float32x4_t r = vmulq_f32(x, inv_wave_size);
   int32x4_t f = vcvtq_s32_f32(r);
@@ -27,13 +29,15 @@
   return vsubq_f32(x, vmulq_f32(vcvtq_f32_s32(f), wave_size));
 }
 
-static ALWAYS_INLINE double WrapVirtualIndex(double virtual_index,
-                                             unsigned periodic_wave_size,
-                                             double inv_periodic_wave_size) {
+ALWAYS_INLINE double WrapVirtualIndex(double virtual_index,
+                                      unsigned periodic_wave_size,
+                                      double inv_periodic_wave_size) {
   return virtual_index -
          floor(virtual_index * inv_periodic_wave_size) * periodic_wave_size;
 }
 
+}  // namespace
+
 std::tuple<int, double> OscillatorHandler::ProcessKRateVector(
     int n,
     float* dest_p,
diff --git a/third_party/blink/renderer/modules/webaudio/cpu/x86/oscillator_kernel_sse2.cc b/third_party/blink/renderer/modules/webaudio/cpu/x86/oscillator_kernel_sse2.cc
index 4ecd3e6..c6a3fdd 100644
--- a/third_party/blink/renderer/modules/webaudio/cpu/x86/oscillator_kernel_sse2.cc
+++ b/third_party/blink/renderer/modules/webaudio/cpu/x86/oscillator_kernel_sse2.cc
@@ -10,9 +10,11 @@
 
 namespace blink {
 
-static __m128 WrapVirtualIndexVector(__m128 x,
-                                     __m128 wave_size,
-                                     __m128 inv_wave_size) {
+namespace {
+
+__m128 WrapVirtualIndexVector(__m128 x,
+                              __m128 wave_size,
+                              __m128 inv_wave_size) {
   // Wrap the virtual index `x` to the range 0 to wave_size - 1.  This is done
   // by computing `x` - floor(`x`/`wave_size`)*`wave_size`.
   //
@@ -40,9 +42,9 @@
   return _mm_sub_ps(x, _mm_mul_ps(_mm_cvtepi32_ps(f), wave_size));
 }
 
-static __m128d WrapVirtualIndexVectorPd(__m128d x,
-                                        __m128d wave_size,
-                                        __m128d inv_wave_size) {
+__m128d WrapVirtualIndexVectorPd(__m128d x,
+                                 __m128d wave_size,
+                                 __m128d inv_wave_size) {
   // Wrap the virtual index `x` to the range 0 to wave_size - 1.  This is done
   // by computing `x` - floor(`x`/`wave_size`)*`wave_size`.
   //
@@ -74,6 +76,8 @@
   return _mm_sub_pd(x, _mm_mul_pd(_mm_cvtepi32_pd(f), wave_size));
 }
 
+}  // namespace
+
 std::tuple<int, double> OscillatorHandler::ProcessKRateVector(
     int n,
     float* dest_p,
diff --git a/third_party/blink/renderer/modules/webaudio/delay_handler.cc b/third_party/blink/renderer/modules/webaudio/delay_handler.cc
index 48396c8e..7431874 100644
--- a/third_party/blink/renderer/modules/webaudio/delay_handler.cc
+++ b/third_party/blink/renderer/modules/webaudio/delay_handler.cc
@@ -12,6 +12,12 @@
 
 namespace blink {
 
+namespace {
+
+constexpr unsigned kNumberOfChannels = 1;
+
+}  // namespace
+
 DelayHandler::DelayHandler(AudioNode& node,
                            float sample_rate,
                            AudioParamHandler& delay_time,
@@ -22,7 +28,7 @@
           sample_rate,
           std::make_unique<DelayProcessor>(
               sample_rate,
-              1,
+              kNumberOfChannels,
               node.context()->GetDeferredTaskHandler().RenderQuantumFrames(),
               delay_time,
               max_delay_time)) {
diff --git a/third_party/blink/renderer/modules/webaudio/delay_node.cc b/third_party/blink/renderer/modules/webaudio/delay_node.cc
index 4884487e..896d33f 100644
--- a/third_party/blink/renderer/modules/webaudio/delay_node.cc
+++ b/third_party/blink/renderer/modules/webaudio/delay_node.cc
@@ -35,7 +35,11 @@
 
 namespace {
 
-constexpr double kMaximumAllowedDelayTime = 180;
+constexpr double kDefaultDelayTimeValue = 0.0;
+constexpr float kMinDelayTimeValue = 0.0f;
+
+constexpr double kDefaultMaximumDelayTime = 1.0;  // 1 second
+constexpr double kMaximumAllowedDelayTime = 180.0;
 
 }  // namespace
 
@@ -45,10 +49,10 @@
           AudioParam::Create(context,
                              Uuid(),
                              AudioParamHandler::kParamTypeDelayDelayTime,
-                             0.0,
+                             kDefaultDelayTimeValue,
                              AudioParamHandler::AutomationRate::kAudio,
                              AudioParamHandler::AutomationRateMode::kVariable,
-                             0.0,
+                             kMinDelayTimeValue,
                              max_delay_time)) {
   SetHandler(DelayHandler::Create(*this, context.sampleRate(),
                                   delay_time_->Handler(), max_delay_time));
@@ -58,8 +62,7 @@
                              ExceptionState& exception_state) {
   DCHECK(IsMainThread());
 
-  // The default maximum delay time for the delay node is 1 sec.
-  return Create(context, 1, exception_state);
+  return Create(context, kDefaultMaximumDelayTime, exception_state);
 }
 
 DelayNode* DelayNode::Create(BaseAudioContext& context,
diff --git a/third_party/blink/renderer/modules/webaudio/dynamics_compressor_handler.cc b/third_party/blink/renderer/modules/webaudio/dynamics_compressor_handler.cc
index 521a846..f6ebd11a 100644
--- a/third_party/blink/renderer/modules/webaudio/dynamics_compressor_handler.cc
+++ b/third_party/blink/renderer/modules/webaudio/dynamics_compressor_handler.cc
@@ -14,11 +14,15 @@
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
 #include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
 
-// Set output to stereo by default.
-static const unsigned defaultNumberOfOutputChannels = 2;
-
 namespace blink {
 
+namespace {
+
+// Set output to stereo by default.
+constexpr unsigned kDefaultNumberOfOutputChannels = 2;
+
+}  // namespace
+
 DynamicsCompressorHandler::DynamicsCompressorHandler(
     AudioNode& node,
     float sample_rate,
@@ -35,7 +39,7 @@
       attack_(&attack),
       release_(&release) {
   AddInput();
-  AddOutput(defaultNumberOfOutputChannels);
+  AddOutput(kDefaultNumberOfOutputChannels);
 
   SetInternalChannelCountMode(kClampedMax);
 
@@ -104,7 +108,7 @@
 
   AudioHandler::Initialize();
   dynamics_compressor_ = std::make_unique<DynamicsCompressor>(
-      Context()->sampleRate(), defaultNumberOfOutputChannels);
+      Context()->sampleRate(), kDefaultNumberOfOutputChannels);
 }
 
 bool DynamicsCompressorHandler::RequiresTailProcessing() const {
diff --git a/third_party/blink/renderer/modules/webaudio/dynamics_compressor_node.cc b/third_party/blink/renderer/modules/webaudio/dynamics_compressor_node.cc
index bc3d15b..0ec0790 100644
--- a/third_party/blink/renderer/modules/webaudio/dynamics_compressor_node.cc
+++ b/third_party/blink/renderer/modules/webaudio/dynamics_compressor_node.cc
@@ -37,53 +37,77 @@
 
 namespace blink {
 
+namespace {
+
+constexpr double kDefaultThresholdValue = -24.0;
+constexpr float kMinThresholdValue = -100.0f;
+constexpr float kMaxThresholdValue = 0.0f;
+
+constexpr double kDefaultKneeValue = 30.0;
+constexpr float kMinKneeValue = 0.0f;
+constexpr float kMaxKneeValue = 40.0f;
+
+constexpr double kDefaultRatioValue = 12.0;
+constexpr float kMinRatioValue = 1.0f;
+constexpr float kMaxRatioValue = 20.0f;
+
+constexpr double kDefaultAttackValue = 0.003;
+constexpr float kMinAttackValue = 0.0f;
+constexpr float kMaxAttackValue = 1.0f;
+
+constexpr double kDefaultReleaseValue = 0.250;
+constexpr float kMinReleaseValue = 0.0f;
+constexpr float kMaxReleaseValue = 1.0f;
+
+}  // namespace
+
 DynamicsCompressorNode::DynamicsCompressorNode(BaseAudioContext& context)
     : AudioNode(context),
       threshold_(AudioParam::Create(
           context,
           Uuid(),
           AudioParamHandler::kParamTypeDynamicsCompressorThreshold,
-          -24,
+          kDefaultThresholdValue,
           AudioParamHandler::AutomationRate::kControl,
           AudioParamHandler::AutomationRateMode::kFixed,
-          -100,
-          0)),
+          kMinThresholdValue,
+          kMaxThresholdValue)),
       knee_(AudioParam::Create(
           context,
           Uuid(),
           AudioParamHandler::kParamTypeDynamicsCompressorKnee,
-          30,
+          kDefaultKneeValue,
           AudioParamHandler::AutomationRate::kControl,
           AudioParamHandler::AutomationRateMode::kFixed,
-          0,
-          40)),
+          kMinKneeValue,
+          kMaxKneeValue)),
       ratio_(AudioParam::Create(
           context,
           Uuid(),
           AudioParamHandler::kParamTypeDynamicsCompressorRatio,
-          12,
+          kDefaultRatioValue,
           AudioParamHandler::AutomationRate::kControl,
           AudioParamHandler::AutomationRateMode::kFixed,
-          1,
-          20)),
+          kMinRatioValue,
+          kMaxRatioValue)),
       attack_(AudioParam::Create(
           context,
           Uuid(),
           AudioParamHandler::kParamTypeDynamicsCompressorAttack,
-          0.003,
+          kDefaultAttackValue,
           AudioParamHandler::AutomationRate::kControl,
           AudioParamHandler::AutomationRateMode::kFixed,
-          0,
-          1)),
+          kMinAttackValue,
+          kMaxAttackValue)),
       release_(AudioParam::Create(
           context,
           Uuid(),
           AudioParamHandler::kParamTypeDynamicsCompressorRelease,
-          0.250,
+          kDefaultReleaseValue,
           AudioParamHandler::AutomationRate::kControl,
           AudioParamHandler::AutomationRateMode::kFixed,
-          0,
-          1)) {
+          kMinReleaseValue,
+          kMaxReleaseValue)) {
   SetHandler(DynamicsCompressorHandler::Create(
       *this, context.sampleRate(), threshold_->Handler(), knee_->Handler(),
       ratio_->Handler(), attack_->Handler(), release_->Handler()));
diff --git a/third_party/blink/renderer/modules/webaudio/gain_handler.cc b/third_party/blink/renderer/modules/webaudio/gain_handler.cc
index 6f211431..affef5a 100644
--- a/third_party/blink/renderer/modules/webaudio/gain_handler.cc
+++ b/third_party/blink/renderer/modules/webaudio/gain_handler.cc
@@ -11,6 +11,12 @@
 
 namespace blink {
 
+namespace {
+
+constexpr unsigned kNumberOfOutputChannels = 1;
+
+}  // namespace
+
 GainHandler::GainHandler(AudioNode& node,
                          float sample_rate,
                          AudioParamHandler& gain)
@@ -19,7 +25,7 @@
       sample_accurate_gain_values_(
           GetDeferredTaskHandler().RenderQuantumFrames()) {
   AddInput();
-  AddOutput(1);
+  AddOutput(kNumberOfOutputChannels);
 
   Initialize();
 }
diff --git a/third_party/blink/renderer/modules/webaudio/gain_node.cc b/third_party/blink/renderer/modules/webaudio/gain_node.cc
index 45aa16c..5e697cc6 100644
--- a/third_party/blink/renderer/modules/webaudio/gain_node.cc
+++ b/third_party/blink/renderer/modules/webaudio/gain_node.cc
@@ -31,13 +31,19 @@
 
 namespace blink {
 
+namespace {
+
+constexpr double kDefaultGainValue = 1.0;
+
+}  // namespace
+
 GainNode::GainNode(BaseAudioContext& context)
     : AudioNode(context),
       gain_(AudioParam::Create(
           context,
           Uuid(),
           AudioParamHandler::kParamTypeGainGain,
-          1.0,
+          kDefaultGainValue,
           AudioParamHandler::AutomationRate::kAudio,
           AudioParamHandler::AutomationRateMode::kVariable)) {
   SetHandler(
diff --git a/third_party/blink/renderer/modules/webaudio/iir_filter_handler.cc b/third_party/blink/renderer/modules/webaudio/iir_filter_handler.cc
index e3dfa04b..f34af46f 100644
--- a/third_party/blink/renderer/modules/webaudio/iir_filter_handler.cc
+++ b/third_party/blink/renderer/modules/webaudio/iir_filter_handler.cc
@@ -16,6 +16,12 @@
 
 namespace blink {
 
+namespace {
+
+constexpr uint32_t kNumberOfChannels = 1;
+
+}  // namespace
+
 IIRFilterHandler::IIRFilterHandler(AudioNode& node,
                                    float sample_rate,
                                    const Vector<double>& feedforward_coef,
@@ -27,7 +33,7 @@
           sample_rate,
           std::make_unique<IIRProcessor>(
               sample_rate,
-              1,
+              kNumberOfChannels,
               node.context()->GetDeferredTaskHandler().RenderQuantumFrames(),
               feedforward_coef,
               feedback_coef,
diff --git a/third_party/blink/renderer/modules/webaudio/iir_filter_node.cc b/third_party/blink/renderer/modules/webaudio/iir_filter_node.cc
index 25857bf..57ea5c7 100644
--- a/third_party/blink/renderer/modules/webaudio/iir_filter_node.cc
+++ b/third_party/blink/renderer/modules/webaudio/iir_filter_node.cc
@@ -18,6 +18,8 @@
 
 namespace blink {
 
+namespace {
+
 // Determine if filter is stable based on the feedback coefficients.
 // We compute the reflection coefficients for the filter.  If, at any
 // point, the magnitude of the reflection coefficient is greater than
@@ -32,7 +34,7 @@
 //
 // stopping at A[1](z).  If at any point |k[n]| >= 1, the filter is
 // unstable.
-static bool IsFilterStable(const Vector<double>& feedback_coef) {
+bool IsFilterStable(const Vector<double>& feedback_coef) {
   // Make a copy of the feedback coefficients
   Vector<double> coef(feedback_coef);
   int order = coef.size() - 1;
@@ -66,6 +68,8 @@
   return true;
 }
 
+}  // namespace
+
 IIRFilterNode::IIRFilterNode(BaseAudioContext& context,
                              const Vector<double>& feedforward_coef,
                              const Vector<double>& feedback_coef,
diff --git a/third_party/blink/renderer/modules/webaudio/media_element_audio_source_handler.cc b/third_party/blink/renderer/modules/webaudio/media_element_audio_source_handler.cc
index da584573..71208b9 100644
--- a/third_party/blink/renderer/modules/webaudio/media_element_audio_source_handler.cc
+++ b/third_party/blink/renderer/modules/webaudio/media_element_audio_source_handler.cc
@@ -22,6 +22,14 @@
 
 namespace blink {
 
+namespace {
+
+// Default to stereo. This could change depending on what the media element
+// .src is set to.
+constexpr unsigned kDefaultNumberOfOutputChannels = 2;
+
+}  // namespace
+
 class MediaElementAudioSourceHandlerLocker final {
   STACK_ALLOCATED();
 
@@ -51,9 +59,8 @@
                    node.context()->sampleRate()),
       media_element_(media_element) {
   DCHECK(IsMainThread());
-  // Default to stereo. This could change depending on what the media element
-  // .src is set to.
-  AddOutput(2);
+
+  AddOutput(kDefaultNumberOfOutputChannels);
 
   if (Context()->GetExecutionContext()) {
     task_runner_ = Context()->GetExecutionContext()->GetTaskRunner(
diff --git a/third_party/blink/renderer/modules/webaudio/media_stream_audio_destination_handler.cc b/third_party/blink/renderer/modules/webaudio/media_stream_audio_destination_handler.cc
index b593c0a..5e87396 100644
--- a/third_party/blink/renderer/modules/webaudio/media_stream_audio_destination_handler.cc
+++ b/third_party/blink/renderer/modules/webaudio/media_stream_audio_destination_handler.cc
@@ -14,10 +14,14 @@
 
 namespace blink {
 
+namespace {
+
 // Channel counts greater than 8 are ignored by some audio tracks/sinks (see
 // WebAudioMediaStreamSource), so we set a limit here to avoid anything that
 // could cause a crash.
-static const uint32_t kMaxChannelCount = 8;
+constexpr uint32_t kMaxChannelCountSupported = 8;
+
+}  // namespace
 
 MediaStreamAudioDestinationHandler::MediaStreamAudioDestinationHandler(
     AudioNode& node,
@@ -108,7 +112,7 @@
 }
 
 uint32_t MediaStreamAudioDestinationHandler::MaxChannelCount() const {
-  return kMaxChannelCount;
+  return kMaxChannelCountSupported;
 }
 
 void MediaStreamAudioDestinationHandler::PullInputs(
diff --git a/third_party/blink/renderer/modules/webaudio/media_stream_audio_destination_node.cc b/third_party/blink/renderer/modules/webaudio/media_stream_audio_destination_node.cc
index ca6c4f4..a6fec307 100644
--- a/third_party/blink/renderer/modules/webaudio/media_stream_audio_destination_node.cc
+++ b/third_party/blink/renderer/modules/webaudio/media_stream_audio_destination_node.cc
@@ -39,6 +39,9 @@
 
 namespace {
 
+// Default to stereo; `options` will update it appropriately if needed.
+constexpr uint32_t kDefaultNumberOfChannels = 2;
+
 void DidCreateMediaStreamAndTracks(MediaStreamDescriptor* stream) {
   for (uint32_t i = 0; i < stream->NumberOfAudioComponents(); ++i) {
     MediaStreamUtils::DidCreateMediaStreamTrack(stream->AudioComponent(i));
@@ -127,9 +130,9 @@
   if (!context->CheckExecutionContextAndThrowIfNecessary(exception_state)) {
     return nullptr;
   }
-  // Default to stereo; `options` will update it appropriately if needed.
   MediaStreamAudioDestinationNode* node =
-      MakeGarbageCollected<MediaStreamAudioDestinationNode>(*context, 2);
+      MakeGarbageCollected<MediaStreamAudioDestinationNode>(
+          *context, kDefaultNumberOfChannels);
 
   // Need to handle channelCount here ourselves because the upper
   // limit is different from the normal AudioNode::setChannelCount
diff --git a/third_party/blink/renderer/modules/webaudio/media_stream_audio_source_handler.cc b/third_party/blink/renderer/modules/webaudio/media_stream_audio_source_handler.cc
index 21e52fd..ee4ef38 100644
--- a/third_party/blink/renderer/modules/webaudio/media_stream_audio_source_handler.cc
+++ b/third_party/blink/renderer/modules/webaudio/media_stream_audio_source_handler.cc
@@ -15,7 +15,7 @@
 
 // Default to stereo. This could change depending on the format of the
 // MediaStream's audio track.
-constexpr unsigned kNumberOfOutputChannels = 2;
+constexpr unsigned kDefaultNumberOfOutputChannels = 2;
 
 }  // namespace
 
@@ -27,7 +27,7 @@
                    node.context()->sampleRate()),
       audio_source_provider_(std::move(audio_source_provider)) {
   SendLogMessage(String::Format("%s", __func__));
-  AddOutput(kNumberOfOutputChannels);
+  AddOutput(kDefaultNumberOfOutputChannels);
 
   Initialize();
 }
diff --git a/third_party/blink/renderer/modules/webaudio/offline_audio_worklet_thread.cc b/third_party/blink/renderer/modules/webaudio/offline_audio_worklet_thread.cc
index 4340fdb..d167cec3 100644
--- a/third_party/blink/renderer/modules/webaudio/offline_audio_worklet_thread.cc
+++ b/third_party/blink/renderer/modules/webaudio/offline_audio_worklet_thread.cc
@@ -14,9 +14,9 @@
 
 // Use for ref-counting of all OfflineAudioWorkletThread instances in a
 // process. Incremented by the constructor and decremented by destructor.
-static int ref_count = 0;
+int ref_count = 0;
 
-static void EnsureSharedBackingThread(const ThreadCreationParams& params) {
+void EnsureSharedBackingThread(const ThreadCreationParams& params) {
   DCHECK(IsMainThread());
   DCHECK_EQ(ref_count, 1);
   WorkletThreadHolder<OfflineAudioWorkletThread>::EnsureInstance(params);
diff --git a/third_party/blink/renderer/modules/webaudio/oscillator_handler.cc b/third_party/blink/renderer/modules/webaudio/oscillator_handler.cc
index 964b05fd..8b6091f 100644
--- a/third_party/blink/renderer/modules/webaudio/oscillator_handler.cc
+++ b/third_party/blink/renderer/modules/webaudio/oscillator_handler.cc
@@ -22,22 +22,20 @@
 
 namespace blink {
 
-// Breakpoints where we decide to do linear interpolation, 3-point
-// interpolation or 5-point interpolation.  See DoInterpolation().
-constexpr float kInterpolate2Point = 0.3;
-constexpr float kInterpolate3Point = 0.16;
+namespace {
+
+// An oscillator is always mono.
+constexpr unsigned kNumberOfOutputChannels = 1;
 
 // Convert the detune value (in cents) to a frequency scale multiplier:
 // 2^(d/1200)
-static float DetuneToFrequencyMultiplier(float detune_value) {
+float DetuneToFrequencyMultiplier(float detune_value) {
   return std::exp2(detune_value / 1200);
 }
 
 // Clamp the frequency value to lie with Nyquist frequency. For NaN, arbitrarily
 // clamp to +Nyquist.
-static void ClampFrequency(float* frequency,
-                           int frames_to_process,
-                           float nyquist) {
+void ClampFrequency(float* frequency, int frames_to_process, float nyquist) {
   for (int k = 0; k < frames_to_process; ++k) {
     float f = frequency[k];
 
@@ -49,12 +47,12 @@
   }
 }
 
-static float DoInterpolation(double virtual_read_index,
-                             float incr,
-                             unsigned read_index_mask,
-                             float table_interpolation_factor,
-                             const float* lower_wave_data,
-                             const float* higher_wave_data) {
+float DoInterpolation(double virtual_read_index,
+                      float incr,
+                      unsigned read_index_mask,
+                      float table_interpolation_factor,
+                      const float* lower_wave_data,
+                      const float* higher_wave_data) {
   DCHECK_GE(incr, 0);
   DCHECK(std::isfinite(virtual_read_index));
 
@@ -75,7 +73,7 @@
   // We use Lagrange interpolation because it's relatively simple to
   // implement and fairly inexpensive, and the interpolator always
   // passes through known points.
-  if (incr >= kInterpolate2Point) {
+  if (incr >= OscillatorHandler::kInterpolate2Point) {
     // Increment is fairly large, so we're doing no more than about 3
     // points between each wave table entry. Assume linear
     // interpolation between points is good enough.
@@ -98,7 +96,7 @@
     sample_lower = (1 - interpolation_factor) * sample1_lower +
                    interpolation_factor * sample2_lower;
 
-  } else if (incr >= kInterpolate3Point) {
+  } else if (incr >= OscillatorHandler::kInterpolate3Point) {
     // We're doing about 6 interpolation values between each wave
     // table sample. Just use a 3-point Lagrange interpolator to get a
     // better estimate than just linear.
@@ -154,6 +152,8 @@
   return sample;
 }
 
+}  // namespace
+
 OscillatorHandler::OscillatorHandler(AudioNode& node,
                                      float sample_rate,
                                      const String& oscillator_type,
@@ -183,8 +183,7 @@
     }
   }
 
-  // An oscillator is always mono.
-  AddOutput(1);
+  AddOutput(kNumberOfOutputChannels);
 
   Initialize();
 }
diff --git a/third_party/blink/renderer/modules/webaudio/oscillator_handler.h b/third_party/blink/renderer/modules/webaudio/oscillator_handler.h
index d82cd1a..0fde7fd 100644
--- a/third_party/blink/renderer/modules/webaudio/oscillator_handler.h
+++ b/third_party/blink/renderer/modules/webaudio/oscillator_handler.h
@@ -34,8 +34,8 @@
 
   // Breakpoints where we decide to do linear interpolation, 3-point
   // interpolation or 5-point interpolation.  See DoInterpolation().
-  static constexpr float kInterpolate2Point = 0.3;
-  static constexpr float kInterpolate3Point = 0.16;
+  static constexpr float kInterpolate2Point = 0.3f;
+  static constexpr float kInterpolate3Point = 0.16f;
 
   static scoped_refptr<OscillatorHandler> Create(AudioNode&,
                                                  float sample_rate,
diff --git a/third_party/blink/renderer/modules/webaudio/oscillator_node.cc b/third_party/blink/renderer/modules/webaudio/oscillator_node.cc
index d13b0498..11ed26b 100644
--- a/third_party/blink/renderer/modules/webaudio/oscillator_node.cc
+++ b/third_party/blink/renderer/modules/webaudio/oscillator_node.cc
@@ -42,6 +42,13 @@
 
 namespace blink {
 
+namespace {
+
+constexpr double kDefaultFrequencyValue = 440.0;
+constexpr double kDefaultDetuneValue = 0.0;
+
+}  // namespace
+
 OscillatorNode::OscillatorNode(BaseAudioContext& context,
                                const String& oscillator_type,
                                PeriodicWave* wave_table)
@@ -51,21 +58,21 @@
           AudioParam::Create(context,
                              Uuid(),
                              AudioParamHandler::kParamTypeOscillatorFrequency,
-                             440,
+                             kDefaultFrequencyValue,
                              AudioParamHandler::AutomationRate::kAudio,
                              AudioParamHandler::AutomationRateMode::kVariable,
-                             -context.sampleRate() / 2,
-                             context.sampleRate() / 2)),
+                             /*min_value=*/-context.sampleRate() / 2,
+                             /*max_value=*/context.sampleRate() / 2)),
       // Default to no detuning.
-      detune_(
-          AudioParam::Create(context,
-                             Uuid(),
-                             AudioParamHandler::kParamTypeOscillatorDetune,
-                             0,
-                             AudioParamHandler::AutomationRate::kAudio,
-                             AudioParamHandler::AutomationRateMode::kVariable,
-                             -1200 * log2f(std::numeric_limits<float>::max()),
-                             1200 * log2f(std::numeric_limits<float>::max()))) {
+      detune_(AudioParam::Create(
+          context,
+          Uuid(),
+          AudioParamHandler::kParamTypeOscillatorDetune,
+          kDefaultDetuneValue,
+          AudioParamHandler::AutomationRate::kAudio,
+          AudioParamHandler::AutomationRateMode::kVariable,
+          /*min_value=*/-1200 * log2f(std::numeric_limits<float>::max()),
+          /*max_value=*/1200 * log2f(std::numeric_limits<float>::max()))) {
   SetHandler(
       OscillatorHandler::Create(*this, context.sampleRate(), oscillator_type,
                                 wave_table ? wave_table->impl() : nullptr,
diff --git a/third_party/blink/renderer/modules/webaudio/panner_handler.cc b/third_party/blink/renderer/modules/webaudio/panner_handler.cc
index 20224ff..e58dd58b 100644
--- a/third_party/blink/renderer/modules/webaudio/panner_handler.cc
+++ b/third_party/blink/renderer/modules/webaudio/panner_handler.cc
@@ -20,6 +20,7 @@
 
 namespace {
 
+// A PannerNode only supports 1 or 2 channels
 constexpr unsigned kMinimumOutputChannels = 1;
 constexpr unsigned kMaximumOutputChannels = 2;
 
@@ -675,7 +676,6 @@
   DCHECK(IsMainThread());
   BaseAudioContext::GraphAutoLocker locker(Context());
 
-  // A PannerNode only supports 1 or 2 channels
   if (channel_count >= kMinimumOutputChannels &&
       channel_count <= kMaximumOutputChannels) {
     if (channel_count_ != channel_count) {
diff --git a/third_party/blink/renderer/modules/webaudio/panner_node.cc b/third_party/blink/renderer/modules/webaudio/panner_node.cc
index ca16e03..9090d279 100644
--- a/third_party/blink/renderer/modules/webaudio/panner_node.cc
+++ b/third_party/blink/renderer/modules/webaudio/panner_node.cc
@@ -35,48 +35,59 @@
 
 namespace blink {
 
+namespace {
+
+constexpr double kDefaultPositionXValue = 0.0;
+constexpr double kDefaultPositionYValue = 0.0;
+constexpr double kDefaultPositionZValue = 0.0;
+constexpr double kDefaultOrientationXValue = 1.0;
+constexpr double kDefaultOrientationYValue = 0.0;
+constexpr double kDefaultOrientationZValue = 0.0;
+
+}  // namespace
+
 PannerNode::PannerNode(BaseAudioContext& context)
     : AudioNode(context),
       position_x_(
           AudioParam::Create(context,
                              Uuid(),
                              AudioParamHandler::kParamTypePannerPositionX,
-                             0.0,
+                             kDefaultPositionXValue,
                              AudioParamHandler::AutomationRate::kAudio,
                              AudioParamHandler::AutomationRateMode::kVariable)),
       position_y_(
           AudioParam::Create(context,
                              Uuid(),
                              AudioParamHandler::kParamTypePannerPositionY,
-                             0.0,
+                             kDefaultPositionYValue,
                              AudioParamHandler::AutomationRate::kAudio,
                              AudioParamHandler::AutomationRateMode::kVariable)),
       position_z_(
           AudioParam::Create(context,
                              Uuid(),
                              AudioParamHandler::kParamTypePannerPositionZ,
-                             0.0,
+                             kDefaultPositionZValue,
                              AudioParamHandler::AutomationRate::kAudio,
                              AudioParamHandler::AutomationRateMode::kVariable)),
       orientation_x_(
           AudioParam::Create(context,
                              Uuid(),
                              AudioParamHandler::kParamTypePannerOrientationX,
-                             1.0,
+                             kDefaultOrientationXValue,
                              AudioParamHandler::AutomationRate::kAudio,
                              AudioParamHandler::AutomationRateMode::kVariable)),
       orientation_y_(
           AudioParam::Create(context,
                              Uuid(),
                              AudioParamHandler::kParamTypePannerOrientationY,
-                             0.0,
+                             kDefaultOrientationYValue,
                              AudioParamHandler::AutomationRate::kAudio,
                              AudioParamHandler::AutomationRateMode::kVariable)),
       orientation_z_(
           AudioParam::Create(context,
                              Uuid(),
                              AudioParamHandler::kParamTypePannerOrientationZ,
-                             0.0,
+                             kDefaultOrientationZValue,
                              AudioParamHandler::AutomationRate::kAudio,
                              AudioParamHandler::AutomationRateMode::kVariable)),
       listener_(context.listener()) {
diff --git a/third_party/blink/renderer/modules/webaudio/periodic_wave.cc b/third_party/blink/renderer/modules/webaudio/periodic_wave.cc
index 84619e6..1a37038 100644
--- a/third_party/blink/renderer/modules/webaudio/periodic_wave.cc
+++ b/third_party/blink/renderer/modules/webaudio/periodic_wave.cc
@@ -48,15 +48,19 @@
 
 namespace blink {
 
+namespace {
+
 // The number of bands per octave.  Each octave will have this many entries in
 // the wave tables.
-const unsigned kNumberOfOctaveBands = 3;
+constexpr unsigned kNumberOfOctaveBands = 3;
 
 // The max length of a periodic wave. This must be a power of two greater than
 // or equal to 2048 and must be supported by the FFT routines.
-const unsigned kMaxPeriodicWaveSize = 16384;
+constexpr unsigned kMaxPeriodicWaveSize = 16384;
 
-const float kCentsPerRange = 1200 / kNumberOfOctaveBands;
+constexpr float kCentsPerRange = 1200 / kNumberOfOctaveBands;
+
+}  // namespace
 
 PeriodicWave* PeriodicWave::Create(BaseAudioContext& context,
                                    const Vector<float>& real,
diff --git a/third_party/blink/renderer/modules/webaudio/realtime_analyser.cc b/third_party/blink/renderer/modules/webaudio/realtime_analyser.cc
index 0ae569b..e061fd2 100644
--- a/third_party/blink/renderer/modules/webaudio/realtime_analyser.cc
+++ b/third_party/blink/renderer/modules/webaudio/realtime_analyser.cc
@@ -34,18 +34,6 @@
 
 namespace blink {
 
-const double RealtimeAnalyser::kDefaultSmoothingTimeConstant = 0.8;
-const double RealtimeAnalyser::kDefaultMinDecibels = -100;
-const double RealtimeAnalyser::kDefaultMaxDecibels = -30;
-
-const unsigned RealtimeAnalyser::kDefaultFFTSize = 2048;
-// All FFT implementations are expected to handle power-of-two sizes
-// MinFFTSize <= size <= MaxFFTSize.
-const unsigned RealtimeAnalyser::kMinFFTSize = 32;
-const unsigned RealtimeAnalyser::kMaxFFTSize = 32768;
-const unsigned RealtimeAnalyser::kInputBufferSize =
-    RealtimeAnalyser::kMaxFFTSize * 2;
-
 RealtimeAnalyser::RealtimeAnalyser(unsigned render_quantum_frames)
     : input_buffer_(kInputBufferSize),
       down_mix_bus_(AudioBus::Create(1, render_quantum_frames)),
diff --git a/third_party/blink/renderer/modules/webaudio/realtime_analyser.h b/third_party/blink/renderer/modules/webaudio/realtime_analyser.h
index cd2e0ab..554a6e1 100644
--- a/third_party/blink/renderer/modules/webaudio/realtime_analyser.h
+++ b/third_party/blink/renderer/modules/webaudio/realtime_analyser.h
@@ -41,14 +41,16 @@
   DISALLOW_NEW();
 
  public:
-  static const double kDefaultSmoothingTimeConstant;
-  static const double kDefaultMinDecibels;
-  static const double kDefaultMaxDecibels;
+  static constexpr double kDefaultSmoothingTimeConstant = 0.8;
+  static constexpr double kDefaultMinDecibels = -100.0;
+  static constexpr double kDefaultMaxDecibels = -30.0;
 
-  static const unsigned kDefaultFFTSize;
-  static const unsigned kMinFFTSize;
-  static const unsigned kMaxFFTSize;
-  static const unsigned kInputBufferSize;
+  static constexpr unsigned kDefaultFFTSize = 2048;
+  // All FFT implementations are expected to handle power-of-two sizes
+  // MinFFTSize <= size <= MaxFFTSize.
+  static constexpr unsigned kMinFFTSize = 32;
+  static constexpr unsigned kMaxFFTSize = 32768;
+  static constexpr unsigned kInputBufferSize = kMaxFFTSize * 2;
 
   explicit RealtimeAnalyser(unsigned render_quantum_frames);
 
diff --git a/third_party/blink/renderer/modules/webaudio/realtime_audio_destination_handler.cc b/third_party/blink/renderer/modules/webaudio/realtime_audio_destination_handler.cc
index 25862bbd..c524ec84 100644
--- a/third_party/blink/renderer/modules/webaudio/realtime_audio_destination_handler.cc
+++ b/third_party/blink/renderer/modules/webaudio/realtime_audio_destination_handler.cc
@@ -22,6 +22,12 @@
 
 namespace blink {
 
+namespace {
+
+constexpr unsigned kDefaultNumberOfInputChannels = 2;
+
+}  // namespace
+
 scoped_refptr<RealtimeAudioDestinationHandler>
 RealtimeAudioDestinationHandler::Create(AudioNode& node,
                                         const WebAudioLatencyHint& latency_hint,
@@ -41,7 +47,7 @@
       task_runner_(Context()->GetExecutionContext()->GetTaskRunner(
           TaskType::kInternalMediaRealTime)) {
   // Node-specific default channel count and mixing rules.
-  channel_count_ = 2;
+  channel_count_ = kDefaultNumberOfInputChannels;
   SetInternalChannelCountMode(kExplicit);
   SetInternalChannelInterpretation(AudioBus::kSpeakers);
 }
diff --git a/third_party/blink/renderer/modules/webaudio/realtime_audio_worklet_thread.cc b/third_party/blink/renderer/modules/webaudio/realtime_audio_worklet_thread.cc
index 5e919929..f982dff 100644
--- a/third_party/blink/renderer/modules/webaudio/realtime_audio_worklet_thread.cc
+++ b/third_party/blink/renderer/modules/webaudio/realtime_audio_worklet_thread.cc
@@ -16,9 +16,9 @@
 
 // Use for ref-counting of all RealtimeAudioWorkletThread instances in a
 // process. Incremented by the constructor and decremented by destructor.
-static int ref_count = 0;
+int ref_count = 0;
 
-static void EnsureSharedBackingThread(const ThreadCreationParams& params) {
+void EnsureSharedBackingThread(const ThreadCreationParams& params) {
   DCHECK(IsMainThread());
   DCHECK_EQ(ref_count, 1);
   WorkletThreadHolder<RealtimeAudioWorkletThread>::EnsureInstance(params);
diff --git a/third_party/blink/renderer/modules/webaudio/script_processor_node.cc b/third_party/blink/renderer/modules/webaudio/script_processor_node.cc
index feecd58..8e3819f5 100644
--- a/third_party/blink/renderer/modules/webaudio/script_processor_node.cc
+++ b/third_party/blink/renderer/modules/webaudio/script_processor_node.cc
@@ -67,8 +67,8 @@
          (buffer_1->sampleRate() == buffer_2->sampleRate());
 }
 
-static uint32_t ChooseBufferSize(uint32_t callback_buffer_size) {
-  // Choose a buffer size based on the audio hardware buffer size. Arbitarily
+uint32_t ChooseBufferSize(uint32_t callback_buffer_size) {
+  // Choose a buffer size based on the audio hardware buffer size. Arbitrarily
   // make it a power of two that is 4 times greater than the hardware buffer
   // size.
   // TODO(crbug.com/855758): What is the best way to choose this?
diff --git a/third_party/blink/renderer/modules/webaudio/semi_realtime_audio_worklet_thread.cc b/third_party/blink/renderer/modules/webaudio/semi_realtime_audio_worklet_thread.cc
index 3210de3..15518a2b 100644
--- a/third_party/blink/renderer/modules/webaudio/semi_realtime_audio_worklet_thread.cc
+++ b/third_party/blink/renderer/modules/webaudio/semi_realtime_audio_worklet_thread.cc
@@ -16,9 +16,9 @@
 
 // Use for ref-counting of all SemiRealtimeAudioWorkletThread instances in a
 // process. Incremented by the constructor and decremented by destructor.
-static int ref_count = 0;
+int ref_count = 0;
 
-static void EnsureSharedBackingThread(const ThreadCreationParams& params) {
+void EnsureSharedBackingThread(const ThreadCreationParams& params) {
   DCHECK(IsMainThread());
   DCHECK_EQ(ref_count, 1);
   WorkletThreadHolder<SemiRealtimeAudioWorkletThread>::EnsureInstance(params);
diff --git a/third_party/blink/renderer/modules/webaudio/stereo_panner_handler.cc b/third_party/blink/renderer/modules/webaudio/stereo_panner_handler.cc
index f5b54b0..df6832f2 100644
--- a/third_party/blink/renderer/modules/webaudio/stereo_panner_handler.cc
+++ b/third_party/blink/renderer/modules/webaudio/stereo_panner_handler.cc
@@ -17,6 +17,14 @@
 
 namespace blink {
 
+namespace {
+
+// A PannerNode only supports 1 or 2 channels
+constexpr unsigned kMinimumOutputChannels = 1;
+constexpr unsigned kMaximumOutputChannels = 2;
+
+}  // namespace
+
 StereoPannerHandler::StereoPannerHandler(AudioNode& node,
                                          float sample_rate,
                                          AudioParamHandler& pan)
@@ -25,11 +33,11 @@
       sample_accurate_pan_values_(
           GetDeferredTaskHandler().RenderQuantumFrames()) {
   AddInput();
-  AddOutput(2);
+  AddOutput(kMaximumOutputChannels);
 
   // The node-specific default mixing rules declare that StereoPannerNode
   // can handle mono to stereo and stereo to stereo conversion.
-  channel_count_ = 2;
+  channel_count_ = kMaximumOutputChannels;
   SetInternalChannelCountMode(kClampedMax);
   SetInternalChannelInterpretation(AudioBus::kSpeakers);
 
@@ -104,8 +112,8 @@
   DCHECK(IsMainThread());
   BaseAudioContext::GraphAutoLocker locker(Context());
 
-  // A PannerNode only supports 1 or 2 channels
-  if (channel_count > 0 && channel_count <= 2) {
+  if (channel_count >= kMinimumOutputChannels &&
+      channel_count <= kMaximumOutputChannels) {
     if (channel_count_ != channel_count) {
       channel_count_ = channel_count;
       if (InternalChannelCountMode() != kMax) {
@@ -116,8 +124,8 @@
     exception_state.ThrowDOMException(
         DOMExceptionCode::kNotSupportedError,
         ExceptionMessages::IndexOutsideRange<uint32_t>(
-            "channelCount", channel_count, 1,
-            ExceptionMessages::kInclusiveBound, 2,
+            "channelCount", channel_count, kMinimumOutputChannels,
+            ExceptionMessages::kInclusiveBound, kMaximumOutputChannels,
             ExceptionMessages::kInclusiveBound));
   }
 }
diff --git a/third_party/blink/renderer/modules/webaudio/stereo_panner_node.cc b/third_party/blink/renderer/modules/webaudio/stereo_panner_node.cc
index b9d3aa9b..f10467a3 100644
--- a/third_party/blink/renderer/modules/webaudio/stereo_panner_node.cc
+++ b/third_party/blink/renderer/modules/webaudio/stereo_panner_node.cc
@@ -17,16 +17,24 @@
 
 namespace blink {
 
+namespace {
+
+constexpr double kDefaultPanValue = 0.0;
+constexpr float kMinPanValue = -1.0f;
+constexpr float kMaxPanValue = 1.0f;
+
+}  // namespace
+
 StereoPannerNode::StereoPannerNode(BaseAudioContext& context)
     : AudioNode(context),
       pan_(AudioParam::Create(context,
                               Uuid(),
                               AudioParamHandler::kParamTypeStereoPannerPan,
-                              0,
+                              kDefaultPanValue,
                               AudioParamHandler::AutomationRate::kAudio,
                               AudioParamHandler::AutomationRateMode::kVariable,
-                              -1,
-                              1)) {
+                              kMinPanValue,
+                              kMaxPanValue)) {
   SetHandler(StereoPannerHandler::Create(*this, context.sampleRate(),
                                          pan_->Handler()));
 }
diff --git a/third_party/blink/renderer/modules/webaudio/wave_shaper_handler.cc b/third_party/blink/renderer/modules/webaudio/wave_shaper_handler.cc
index bc32aae..e203760 100644
--- a/third_party/blink/renderer/modules/webaudio/wave_shaper_handler.cc
+++ b/third_party/blink/renderer/modules/webaudio/wave_shaper_handler.cc
@@ -12,6 +12,12 @@
 
 namespace blink {
 
+namespace {
+
+constexpr unsigned kNumberOfChannels = 1;
+
+}  // namespace
+
 WaveShaperHandler::WaveShaperHandler(AudioNode& node, float sample_rate)
     : AudioBasicProcessorHandler(
           kNodeTypeWaveShaper,
@@ -19,7 +25,7 @@
           sample_rate,
           std::make_unique<WaveShaperProcessor>(
               sample_rate,
-              1,
+              kNumberOfChannels,
               node.context()->GetDeferredTaskHandler().RenderQuantumFrames())) {
   Initialize();
 }
diff --git a/third_party/blink/renderer/modules/webcodecs/decoder_selector.cc b/third_party/blink/renderer/modules/webcodecs/decoder_selector.cc
index c7fcef2..09726b98 100644
--- a/third_party/blink/renderer/modules/webcodecs/decoder_selector.cc
+++ b/third_party/blink/renderer/modules/webcodecs/decoder_selector.cc
@@ -104,9 +104,9 @@
   demuxer_stream_->Configure(config);
   demuxer_stream_->set_low_delay(low_delay);
 
-  // media::DecoderSelector will call back with a null decoder if selection is
+  // media::DecoderSelector will call back with a DecoderStatus if selection is
   // in progress when it is destructed.
-  impl_.SelectDecoder(
+  impl_.BeginDecoderSelection(
       WTF::Bind(&DecoderSelector<StreamType>::OnDecoderSelected,
                 weak_factory_.GetWeakPtr(), std::move(select_decoder_cb)),
       output_cb_);
@@ -130,7 +130,7 @@
 template <media::DemuxerStream::Type StreamType>
 void DecoderSelector<StreamType>::OnDecoderSelected(
     SelectDecoderCB select_decoder_cb,
-    std::unique_ptr<Decoder> decoder,
+    DecoderOrError decoder_or_error,
     std::unique_ptr<media::DecryptingDemuxerStream> decrypting_demuxer_stream) {
   DCHECK(!decrypting_demuxer_stream);
 
@@ -140,7 +140,11 @@
   // (configure() no longer takes a promise).
   impl_.FinalizeDecoderSelection();
 
-  std::move(select_decoder_cb).Run(std::move(decoder));
+  if (decoder_or_error.has_error()) {
+    std::move(select_decoder_cb).Run(nullptr);
+  } else {
+    std::move(select_decoder_cb).Run(std::move(decoder_or_error).value());
+  }
 }
 
 template class MODULES_EXPORT DecoderSelector<media::DemuxerStream::VIDEO>;
diff --git a/third_party/blink/renderer/modules/webcodecs/decoder_selector.h b/third_party/blink/renderer/modules/webcodecs/decoder_selector.h
index 86aae0d6..d7e79a4 100644
--- a/third_party/blink/renderer/modules/webcodecs/decoder_selector.h
+++ b/third_party/blink/renderer/modules/webcodecs/decoder_selector.h
@@ -25,6 +25,8 @@
   typedef media::DecoderStreamTraits<StreamType> StreamTraits;
   typedef typename StreamTraits::DecoderType Decoder;
   typedef typename StreamTraits::DecoderConfigType DecoderConfig;
+  using DecoderOrError =
+      typename media::DecoderSelector<StreamType>::DecoderOrError;
 
   // Callback to create a list of decoders to select from.
   using CreateDecodersCB =
@@ -63,7 +65,7 @@
 
   // Proxy SelectDecoderCB from impl_ to our |select_decoder_cb|.
   void OnDecoderSelected(SelectDecoderCB select_decoder_cb,
-                         std::unique_ptr<Decoder> decoder,
+                         DecoderOrError decoder_or_error,
                          std::unique_ptr<media::DecryptingDemuxerStream>);
 
   // Implements heavy lifting for decoder selection.
diff --git a/third_party/blink/renderer/platform/wtf/cross_thread_copier.h b/third_party/blink/renderer/platform/wtf/cross_thread_copier.h
index 39630b4..35e8989d 100644
--- a/third_party/blink/renderer/platform/wtf/cross_thread_copier.h
+++ b/third_party/blink/renderer/platform/wtf/cross_thread_copier.h
@@ -97,16 +97,10 @@
 };
 
 template <wtf_size_t inlineCapacity, typename Allocator>
-struct CrossThreadCopier<Vector<String, inlineCapacity, Allocator>> {
+struct CrossThreadCopier<Vector<String, inlineCapacity, Allocator>>
+    : public CrossThreadCopierPassThrough<
+          Vector<String, inlineCapacity, Allocator>> {
   STATIC_ONLY(CrossThreadCopier);
-  using Type = Vector<String, inlineCapacity, Allocator>;
-  static Type Copy(const Type& value) {
-    Type result;
-    result.ReserveInitialCapacity(value.size());
-    for (const auto& element : value)
-      result.push_back(element.IsolatedCopy());
-    return result;
-  }
 };
 
 template <>
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 6e6bdd8..c24387a 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -876,6 +876,11 @@
 crbug.com/1299442 [ Mac11 ] virtual/off-main-thread-css-paint/external/wpt/css/css-paint-api/paint-arguments.https.html [ Failure ]
 crbug.com/1299442 [ Mac10.15 ] virtual/off-main-thread-css-paint/external/wpt/css/css-paint-api/registered-property-value-011.https.html [ Failure ]
 
+# No explanation for this apart from different GPU back ends or the like.
+# The tests could be moved to WPT and use fuzzy matching.
+crbug.com/1329571 [ Mac11 ] paint/overflow/transformed-video-clipping.html [ Failure Pass ]
+crbug.com/1329571 [ Linux ] paint/overflow/transformed-video-clipping.html [ Failure Pass ]
+
 # ====== Paint team owned tests to here ======
 
 # WPT speculative-parsing tests failing
@@ -1574,6 +1579,7 @@
 virtual/layout_ng_table_frag/external/wpt/css/css-break/table/break-before-table-cell-child.html [ Pass ]
 virtual/layout_ng_table_frag/external/wpt/css/css-break/table/table-cell-expansion-003.html [ Pass ]
 virtual/layout_ng_table_frag/external/wpt/css/css-break/table/table-col-paint-htb-ltr.html [ Pass ]
+virtual/layout_ng_table_frag/external/wpt/css/css-break/table/table-collapsed-borders-paint-at-boundary.tentative.html [ Pass ]
 virtual/layout_ng_table_frag/external/wpt/css/css-break/table/table-collapsed-borders-paint-htb-ltr.html [ Pass ]
 virtual/layout_ng_table_frag/external/wpt/css/css-break/table/table-collapsed-borders-paint-vlr-rtl.html [ Pass ]
 virtual/layout_ng_table_frag/external/wpt/css/css-break/table/table-collapsed-borders-paint-vrl-ltr.html [ Pass ]
@@ -3816,6 +3822,7 @@
 crbug.com/1078927 external/wpt/css/css-break/table/break-before-table-cell-child.html [ Failure ]
 crbug.com/1078927 external/wpt/css/css-break/table/table-cell-expansion-003.html [ Failure ]
 crbug.com/1078927 external/wpt/css/css-break/table/table-col-paint-htb-ltr.html [ Failure ]
+crbug.com/1078927 external/wpt/css/css-break/table/table-collapsed-borders-paint-at-boundary.tentative.html [ Failure ]
 crbug.com/1078927 external/wpt/css/css-break/table/table-collapsed-borders-paint-htb-ltr.html [ Failure ]
 crbug.com/1078927 external/wpt/css/css-break/table/table-collapsed-borders-paint-vlr-rtl.html [ Failure ]
 crbug.com/1078927 external/wpt/css/css-break/table/table-collapsed-borders-paint-vrl-ltr.html [ Failure ]
@@ -5890,6 +5897,8 @@
 crbug.com/1215949 external/wpt/pointerevents/pointerevent_iframe-touch-action-none_touch.html [ Pass Timeout ]
 crbug.com/1216139 http/tests/devtools/bfcache/bfcache-elements-update.js [ Failure Pass ]
 
+crbug.com/1314739 external/wpt/pointerevents/pointerevent_touch-action-modified_touch.html [ Timeout ]
+
 # Sheriff 2021-06-10
 crbug.com/1177996 [ Mac10.15 ] storage/websql/database-lock-after-reload.html [ Failure Pass ]
 
@@ -6917,3 +6926,4 @@
 
 # Sheriff 2022-05-26
 crbug.com/1322004 http/tests/inspector-protocol/conversion/insecure-subresource.js [ Pass Timeout ]
+crbug.com/1329596 virtual/gpu-rasterization/images/jpeg-with-non-interleaved-dc-channels.html [ Failure Pass ]
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/table/table-collapsed-borders-paint-at-boundary-ref.html b/third_party/blink/web_tests/external/wpt/css/css-break/table/table-collapsed-borders-paint-at-boundary-ref.html
new file mode 100644
index 0000000..04d6434
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-break/table/table-collapsed-borders-paint-at-boundary-ref.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<style>
+.multicol {
+  width: 500px;
+  height: 100px;
+  columns: 5;
+  column-fill: auto;
+  gap: 10px;
+  padding: 10px;
+  border: solid 3px;
+}
+</style>
+<div class="multicol">
+  <div style="background: dodgerblue; height: 150px;"></div>
+  <div style="height: 35px; border: solid 10px lime; border-bottom-width: 5px; margin-left: 2.5px; margin-right: 2.5px;"></div>
+  <div style="height: 95px; border: solid black; border-width: 0 10px 5px 15px; margin-right: 2.5px;">
+    <div style="border-top: solid lime 5px;"></div>
+  </div>
+  <div style="height: 5px; background: black; border-right: solid blue 15px; margin-left: 2.5px;"></div>
+  <div style="height: 40px; border: solid blue; border-width: 0 15px 10px 10px; margin-left: 2.5px;"></div>
+  <div style="height: 40px; border: solid blue; border-width: 0 10px 5px 10px; margin-left: 2.5px; margin-right: 2.5px;"></div>
+  <div style="height: 70px; border: solid blue; border-width: 5px 10px 10px 10px; margin-left: 2.5px; margin-right: 2.5px;"></div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/table/table-collapsed-borders-paint-at-boundary.tentative.html b/third_party/blink/web_tests/external/wpt/css/css-break/table/table-collapsed-borders-paint-at-boundary.tentative.html
new file mode 100644
index 0000000..0227b571
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-break/table/table-collapsed-borders-paint-at-boundary.tentative.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<link rel="match" href="table-collapsed-borders-paint-at-boundary-ref.html">
+<link rel="help" href="https://drafts.csswg.org/css-tables-3/#fragmentation">
+<link rel="help" href="https://drafts.csswg.org/css-tables-3/#rendering">
+<style>
+.multicol {
+  width: 500px;
+  height: 100px;
+  columns: 5;
+  column-fill: auto;
+  gap: 10px;
+  padding: 10px;
+  border: solid 3px;
+}
+</style>
+<div class="multicol">
+  <table style="border-collapse: collapse; width: 100%;">
+    <caption style="background: dodgerblue; height: 150px;"></caption>
+    <tbody>
+      <tr style="height: 45px;">
+        <td style="border: solid 10px lime;"></td>
+      </tr>
+      <tr style="height: 100px;">
+        <td style="border: solid 10px; border-left-width: 15px;"></td>
+      </tr>
+    </tbody>
+    <tbody>
+      <tr style="height: 50px;">
+        <td style="border: solid blue 10px; border-right-width: 15px;"></td>
+      </tr>
+      <tr style="height: 50px;">
+        <td style="border: solid blue 10px;"></td>
+      </tr>
+      <tr style="height: 80px;">
+        <td style="border: solid blue 10px;"></td>
+      </tr>
+    </tbody>
+  </table>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/navigation-api/state/cross-document-getState-undefined.html b/third_party/blink/web_tests/external/wpt/navigation-api/state/cross-document-getState-undefined.html
new file mode 100644
index 0000000..cbd5cdd5
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/navigation-api/state/cross-document-getState-undefined.html
@@ -0,0 +1,17 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<iframe id="i" src="/common/blank.html"></iframe>
+<script>
+async_test(t => {
+  window.onload = t.step_func(() => {
+    assert_equals(i.contentWindow.navigation.entries().length, 1);
+    i.contentWindow.location.href = "?1";
+    i.onload = t.step_func_done(() => {
+      assert_equals(i.contentWindow.navigation.entries().length, 2);
+      assert_equals(i.contentWindow.navigation.currentEntry.index, 1);
+      assert_equals(i.contentWindow.navigation.entries()[0].getState(), undefined);
+    });
+  });
+}, "Default behavior for entry.getState() for a non-current cross-document entry");
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/navigation-api/state/cross-document-getState.html b/third_party/blink/web_tests/external/wpt/navigation-api/state/cross-document-getState.html
new file mode 100644
index 0000000..aedbc471
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/navigation-api/state/cross-document-getState.html
@@ -0,0 +1,19 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<iframe id="i" src="/common/blank.html"></iframe>
+<script>
+async_test(t => {
+  window.onload = t.step_func(() => {
+    i.contentWindow.navigation.updateCurrentEntry({ state: { data: "value" } });
+    assert_equals(i.contentWindow.navigation.entries().length, 1);
+    i.contentWindow.location.href = "?1";
+    i.onload = t.step_func_done(() => {
+      assert_equals(i.contentWindow.navigation.entries().length, 2);
+      assert_equals(i.contentWindow.navigation.currentEntry.index, 1);
+      assert_not_equals(i.contentWindow.navigation.entries()[0].getState(), undefined);
+      assert_equals(i.contentWindow.navigation.entries()[0].getState().data, "value");
+    });
+  });
+}, "entry.getState() still works for a non-current cross-document entry");
+</script>
diff --git a/third_party/ipcz/include/ipcz/ipcz.h b/third_party/ipcz/include/ipcz/ipcz.h
index d363d388..c8c3b53 100644
--- a/third_party/ipcz/include/ipcz/ipcz.h
+++ b/third_party/ipcz/include/ipcz/ipcz.h
@@ -196,14 +196,13 @@
 typedef uint32_t IpczTransportActivityFlags;
 
 // If set, the driver encountered an unrecoverable error using the transport and
-// ipcz should discard it. This also implies that the driver will not invoke the
-// activity handler again for the same transport. In such cases, ipcz does not
-// invoke DeactivateTransport(), as the transport's deactivation is implied by
-// the error notification.
+// ipcz should discard it. Note that the driver is free to issue such
+// notifications many times as long as it remans active, but ipcz will generally
+// request deactivation ASAP once an error is signaled.
 #define IPCZ_TRANSPORT_ACTIVITY_ERROR IPCZ_FLAG_BIT(0)
 
 // When ipcz wants to deactivate a transport, it invokes the driver's
-// DeactivateTransport function. Once the driver has finished any clean up and
+// DeactivateTransport() function. Once the driver has finished any clean up and
 // can ensure that the transport's activity handler will no longer be invoked,
 // it must then invoke the activity handler one final time with this flag set.
 // This finalizes deactivation and allows ipcz to free any associated resources.
@@ -224,12 +223,14 @@
 //
 // If the driver encounters an unrecoverable error while performing I/O on the
 // transport, it should invoke this with the IPCZ_TRANSPORT_ACTIVITY_ERROR flag
-// to instigate immediate destruction of the transport. This implies that the
-// driver will not invoke this function ever again for `transport`, and that the
-// transport is automatically deactivated without an explicit call to the
-// driver's DeactivateTransport() function.
+// to instigate deactivation of the transport by ipcz via a subsequent
+// DeactivateTransport() call.
 //
 // `options` is currently unused and must be null.
+//
+// NOTE: It is the driver's responsibility to ensure that calls to this function
+// for the same value of `transport` are mututally exclusive. Overlapping calls
+// are unsafe and will result in undefined behavior.
 typedef IpczResult(IPCZ_API* IpczTransportActivityHandler)(
     IpczHandle transport,                    // in
     const void* data,                        // in
@@ -395,10 +396,15 @@
       uint32_t flags,                                 // in
       const void* options);                           // in
 
-  // Called by ipcz to deactivate a transport. Once this returns successfully,
-  // the driver must make no further calls into this transport's activity
-  // handler. ipcz may continue to use the transport for outgoing transmissions
-  // until the driver's Close() is also called on `driver_transport`.
+  // Called by ipcz to deactivate a transport. The driver does not need to
+  // complete deactivation synchronously, but it must begin to deactivate the
+  // transport and must invoke the transport's activity handler one final time
+  // with IPCZ_TRANSPORT_ACTIVITY_DEACTIVATED once finished. Beyond that point,
+  // the activity handler must no longer be invoked for that transport.
+  //
+  // Note that even after deactivatoin, ipcz may continue to call into the
+  // transport until it's closed with an explicit call to the driver's Close()
+  // by ipcz.
   IpczResult(IPCZ_API* DeactivateTransport)(
       IpczDriverHandle driver_transport,  // in
       uint32_t flags,                     // in
diff --git a/third_party/ipcz/src/reference_drivers/single_process_reference_driver.cc b/third_party/ipcz/src/reference_drivers/single_process_reference_driver.cc
index 3eab5f0..888d4f5a 100644
--- a/third_party/ipcz/src/reference_drivers/single_process_reference_driver.cc
+++ b/third_party/ipcz/src/reference_drivers/single_process_reference_driver.cc
@@ -35,30 +35,97 @@
                    IpczTransportActivityHandler activity_handler)
       : transport_(transport), activity_handler_(activity_handler) {}
 
-  IpczResult Notify(absl::Span<const uint8_t> data,
-                    absl::Span<const IpczDriverHandle> handles) {
-    IpczResult result =
-        activity_handler_(transport_, data.data(), data.size(), handles.data(),
-                          handles.size(), IPCZ_NO_FLAGS, nullptr);
-    if (result != IPCZ_RESULT_OK && result != IPCZ_RESULT_UNIMPLEMENTED) {
-      NotifyError();
-    }
-    return result;
+  void Notify(absl::Span<const uint8_t> data,
+              absl::Span<const IpczDriverHandle> handles) {
+    DoNotify(IPCZ_NO_FLAGS, data, handles);
   }
 
-  IpczResult NotifyError() {
-    return activity_handler_(transport_, nullptr, 0, nullptr, 0,
-                             IPCZ_TRANSPORT_ACTIVITY_ERROR, nullptr);
-  }
+  void NotifyError() { DoNotify(IPCZ_TRANSPORT_ACTIVITY_ERROR); }
 
  private:
   ~TransportWrapper() override {
-    activity_handler_(transport_, nullptr, 0, nullptr, 0,
-                      IPCZ_TRANSPORT_ACTIVITY_DEACTIVATED, nullptr);
+    // Since this is destruction, we can safely assume the invocation will be
+    // exclusive. Otherwise someone is mismanaging a reference count or has
+    // a UAF bug.
+    DoNotifyExclusive(IPCZ_TRANSPORT_ACTIVITY_DEACTIVATED, {}, {});
+  }
+
+  // Helper to serialize the invocation of potentially overlapping or reentrant
+  // notifications from this transport.
+  void DoNotify(IpczTransportActivityFlags flags,
+                absl::Span<const uint8_t> data = {},
+                absl::Span<const IpczDriverHandle> handles = {}) {
+    {
+      absl::MutexLock lock(&mutex_);
+      if (in_notification_) {
+        DeferredNotification notification = {.flags = flags};
+        if (!data.empty()) {
+          notification.data = std::vector<uint8_t>(data.begin(), data.end());
+        }
+        if (!handles.empty()) {
+          notification.handles =
+              std::vector<IpczDriverHandle>(handles.begin(), handles.end());
+        }
+        deferred_notifications_.push_back(std::move(notification));
+        return;
+      }
+
+      in_notification_ = true;
+    }
+
+    DoNotifyExclusive(flags, data, handles);
+
+    // Now flush any notifications that queued while this one was in progress.
+    // This continues until we complete an iteration with no new notifications
+    // being queued.
+    for (;;) {
+      std::vector<DeferredNotification> notifications;
+      {
+        absl::MutexLock lock(&mutex_);
+        if (deferred_notifications_.empty()) {
+          in_notification_ = false;
+          return;
+        }
+
+        notifications.swap(deferred_notifications_);
+      }
+
+      for (const auto& n : notifications) {
+        DoNotifyExclusive(n.flags, n.data, n.handles);
+      }
+    }
+  }
+
+  // Invokes the activity handler unguarded. The caller must ensure that this is
+  // mututally exclusive with any other invocation of the method.
+  void DoNotifyExclusive(IpczTransportActivityFlags flags,
+                         absl::Span<const uint8_t> data,
+                         absl::Span<const IpczDriverHandle> handles) {
+    const IpczResult result = activity_handler_(
+        transport_, data.empty() ? nullptr : data.data(), data.size(),
+        handles.empty() ? nullptr : handles.data(), handles.size(), flags,
+        nullptr);
+    if (result != IPCZ_RESULT_OK && result != IPCZ_RESULT_UNIMPLEMENTED) {
+      NotifyError();
+    }
   }
 
   const IpczHandle transport_;
   const IpczTransportActivityHandler activity_handler_;
+
+  // Queues copies of any pending notifications which were issued while another
+  // notification was already in progress, either concurrently or reentrantly.
+  // The queue is always flushed completely as the active notification stack
+  // unwinds.
+  struct DeferredNotification {
+    IpczTransportActivityFlags flags;
+    std::vector<uint8_t> data;
+    std::vector<IpczDriverHandle> handles;
+  };
+  absl::Mutex mutex_;
+  bool in_notification_ ABSL_GUARDED_BY(mutex_) = false;
+  std::vector<DeferredNotification> deferred_notifications_
+      ABSL_GUARDED_BY(mutex_);
 };
 
 struct SavedMessage {
diff --git a/third_party/r8/3pp/fetch.py b/third_party/r8/3pp/fetch.py
index 3d14dfa..e01ff54 100755
--- a/third_party/r8/3pp/fetch.py
+++ b/third_party/r8/3pp/fetch.py
@@ -5,8 +5,10 @@
 
 import argparse
 import datetime
+import hashlib
 import json
 import os
+import pathlib
 import urllib.request
 
 # I have arbitrarily chosen 100 as a number much more than the number of commits
@@ -55,13 +57,29 @@
     return None
 
 
+def compute_patch_hash():
+  this_dir = pathlib.Path(__file__).parent
+  md5 = hashlib.md5()
+  for p in sorted(this_dir.glob('patches/*.patch')):
+    md5.update(p.read_bytes())
+  # Include install.py so that it triggers changes as well.
+  md5.update((this_dir / 'install.sh').read_bytes())
+  # Shorten to avoid really long version strings. Given the low number of patch
+  # files, 10 digits is more than sufficient.
+  return md5.hexdigest()[:10]
+
+
 def do_latest():
     commit_hash = get_commit_before_today()
     assert commit_hash is not None
-    print(commit_hash)
+    patch_hash = compute_patch_hash()
+    # Include hash of patch files so that 3pp bot will create a new version when
+    # they change.
+    print(f'{commit_hash}-{patch_hash}')
 
 
-def get_download_url(sha):
+def get_download_url(version):
+    sha = version.split('-')[0]
     partial_manifest = {
         'url': [_ARCHIVE_URL.format(sha)],
         'ext': '.tar.gz',
@@ -71,7 +89,7 @@
 
 def main():
     ap = argparse.ArgumentParser()
-    sub = ap.add_subparsers()
+    sub = ap.add_subparsers(required=True)
 
     latest = sub.add_parser("latest")
     latest.set_defaults(func=do_latest)
diff --git a/tools/clang/scripts/update.py b/tools/clang/scripts/update.py
index 43cf1e36..3f89c4b 100755
--- a/tools/clang/scripts/update.py
+++ b/tools/clang/scripts/update.py
@@ -35,7 +35,7 @@
 # https://chromium.googlesource.com/chromium/src/+/main/docs/updating_clang.md
 # Reverting problematic clang rolls is safe, though.
 # This is the output of `git describe` and is usable as a commit-ish.
-CLANG_REVISION = 'llvmorg-15-init-10717-ge00cbbec'
+CLANG_REVISION = 'llvmorg-15-init-11359-gca27f3e3'
 CLANG_SUB_REVISION = 1
 
 PACKAGE_VERSION = '%s-%s' % (CLANG_REVISION, CLANG_SUB_REVISION)
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 14fb5533..3ad316d8 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -4152,7 +4152,7 @@
   <int value="139" label="Terminated by SIGSEGV">
     MINIJAIL_ERR_SIG_BASE + SIGSEGV from libminijail
   </int>
-  <int value="251" label="Cannot mount file in mount namespace">
+  <int value="251" label="Cannot bind-mount file">
     MINIJAIL_ERR_MOUNT from libminijail
   </int>
   <int value="253" label="Seccomp violation">
@@ -22474,6 +22474,45 @@
   <int value="19" label="Failed to clean up ephemeral cryptohome"/>
 </enum>
 
+<enum name="CryptohomeErrorHashed">
+<!--
+  This enum is intended to be populated automatically by platform2/src/cryptohome/error/tool/location_db.py. It only populate values that indeed occurs in the wild, and the set of such values is gathered from the logs.
+
+  The labels are of the format: <Error Location>/<Error Location Enum Value> ...
+  Whereby Error Location and enum values are defined in platform2/cryptohome/error/locations.h
+  -->
+
+  <int value="706395249"
+      label="kLocUserDataAuthSessionNotFoundInExtendAuthSession/288"/>
+  <int value="4104955507"
+      label="kLocUserDataAuthSessionNotFoundInAuthAuthSession/286"/>
+</enum>
+
+<enum name="CryptohomeErrorLocation">
+<!--
+  This enum is intended to be populated automatically by platform2/src/cryptohome/error/tool/location_db.py. It populates all values found in the cryptohome code base.
+
+  The labels are the Cryptohome Error Location enum defined in platform2/cryptohome/error/locations.h
+  -->
+
+  <int value="286" label="kLocUserDataAuthSessionNotFoundInAuthAuthSession"/>
+  <int value="288" label="kLocUserDataAuthSessionNotFoundInExtendAuthSession"/>
+</enum>
+
+<enum name="CryptohomeErrorLocationWithTPMError">
+<!--
+  This enum is intended to be populated automatically by platform2/src/cryptohome/error/tool/location_db.py. It populates values that indeed occurs in the wild, and the set of such values is gathered through actual UMA data.
+
+  The labels are of the format: <Error Location>-<TPM Error Code>
+  Whereby Error Location are defined in platform2/cryptohome/error/locations.h
+  -->
+
+  <int value="12845195"
+      label="kLocTpmAuthBlockUtilsGetPubkeyFailedInPubkeyHash-TPM_RC_HANDLE"/>
+  <int value="12845218"
+      label="kLocTpmAuthBlockUtilsGetPubkeyFailedInPubkeyHash-TPM_RC_BAD_AUTH"/>
+</enum>
+
 <enum name="CryptohomeFetchUssExperimentConfigStatus">
   <int value="0" label="Enabled"/>
   <int value="1" label="Disabled"/>
@@ -42709,36 +42748,33 @@
 
 <enum name="FuseArchiveError">
   <int value="0" label="Success">EXIT_SUCCESS</int>
-  <int value="1" label="Generic Failure">EXIT_CODE_GENERIC_FAILURE</int>
-  <int value="11" label="Cannot Open Archive">
-    EXIT_CODE_CANNOT_OPEN_ARCHIVE
-  </int>
-  <int value="20" label="Passphrase Required">
-    EXIT_CODE_PASSPHRASE_REQUIRED
-  </int>
-  <int value="21" label="Passphrase Incorrect">
-    EXIT_CODE_PASSPHRASE_INCORRECT
-  </int>
-  <int value="22" label="Passphrase Not Supported">
+  <int value="1" label="Generic error">EXIT_CODE_GENERIC_FAILURE</int>
+  <int value="11" label="Cannot open">EXIT_CODE_CANNOT_OPEN_ARCHIVE</int>
+  <int value="20" label="Missing password">EXIT_CODE_PASSPHRASE_REQUIRED</int>
+  <int value="21" label="Bad password">EXIT_CODE_PASSPHRASE_INCORRECT</int>
+  <int value="22" label="Encryption method not supported">
     EXIT_CODE_PASSPHRASE_NOT_SUPPORTED
   </int>
-  <int value="30" label="Invalid Archive">EXIT_CODE_INVALID_RAW_ARCHIVE</int>
-  <int value="31" label="Invalid Archive Header">
+  <int value="30" label="Invalid archive">EXIT_CODE_INVALID_RAW_ARCHIVE</int>
+  <int value="31" label="Invalid archive header">
     EXIT_CODE_INVALID_ARCHIVE_HEADER
   </int>
-  <int value="32" label="Invalid Archive Contents">
+  <int value="32" label="Invalid archive contents">
     EXIT_CODE_INVALID_ARCHIVE_CONTENTS
   </int>
   <int value="132" label="Terminated by SIGILL">
-    MINIJAIL_ERR_SIG_BASE + SIGILL from libminijail
+    MINIJAIL_ERR_SIG_BASE (128) + SIGILL (4) from libminijail
   </int>
   <int value="134" label="Terminated by SIGABRT">
-    MINIJAIL_ERR_SIG_BASE + SIGABRT from libminijail
+    MINIJAIL_ERR_SIG_BASE (128) + SIGABRT (6) from libminijail
   </int>
   <int value="139" label="Terminated by SIGSEGV">
-    MINIJAIL_ERR_SIG_BASE + SIGSEGV from libminijail
+    MINIJAIL_ERR_SIG_BASE (128) + SIGSEGV (11) from libminijail
   </int>
-  <int value="251" label="Cannot mount file in mount namespace">
+  <int value="143" label="Terminated by SIGTERM">
+    MINIJAIL_ERR_SIG_BASE (128) + SIGTERM (15) from libminijail
+  </int>
+  <int value="251" label="Cannot bind-mount file">
     MINIJAIL_ERR_MOUNT from libminijail
   </int>
   <int value="253" label="Seccomp violation">
@@ -42747,38 +42783,56 @@
 </enum>
 
 <enum name="FuseZipError">
-  <int value="0" label="Success">Hardcoded in fuse-zip</int>
+  <int value="0" label="Success">EXIT_SUCCESS</int>
   <int value="11" label="Multipart ZIP">
-    ZIP_ER_BASE from fuse-zip + ZIP_ER_MULTIDISK from libzip
+    ZIP_ER_BASE (10) from fuse-zip + ZIP_ER_MULTIDISK (1) from libzip
   </int>
   <int value="15" label="Cannot read">
-    ZIP_ER_BASE from fuse-zip + ZIP_ER_READ from libzip
+    ZIP_ER_BASE (10) from fuse-zip + ZIP_ER_READ (5) from libzip
+  </int>
+  <int value="19" label="No such file">
+    ZIP_ER_BASE (10) from fuse-zip + ZIP_ER_NOENT (9) from libzip
   </int>
   <int value="21" label="Cannot open">
-    ZIP_ER_BASE from fuse-zip + ZIP_ER_OPEN from libzip
+    ZIP_ER_BASE (10) from fuse-zip + ZIP_ER_OPEN (11) from libzip
+  </int>
+  <int value="23" label="Malloc failure">
+    ZIP_ER_BASE (10) from fuse-zip + ZIP_ER_MEMORY (13) from libzip
+  </int>
+  <int value="26" label="Compression method not supported">
+    ZIP_ER_BASE (10) from fuse-zip + ZIP_ER_COMPNOTSUPP (16) from libzip
+  </int>
+  <int value="27" label="Premature end of file">
+    ZIP_ER_BASE (10) from fuse-zip + ZIP_ER_EOF (17) from libzip
   </int>
   <int value="29" label="Not a ZIP">
-    ZIP_ER_BASE from fuse-zip + ZIP_ER_NOZIP from libzip
+    ZIP_ER_BASE (10) from fuse-zip + ZIP_ER_NOZIP (19) from libzip
   </int>
   <int value="31" label="Inconsistent ZIP">
-    ZIP_ER_BASE from fuse-zip + ZIP_ER_INCONS from libzip
+    ZIP_ER_BASE (10) from fuse-zip + ZIP_ER_INCONS (21) from libzip
+  </int>
+  <int value="34" label="Encryption method not supported">
+    ZIP_ER_BASE (10) from fuse-zip + ZIP_ER_ENCRNOTSUPP (24) from libzip
   </int>
   <int value="36" label="Missing password">
-    ZIP_ER_BASE from fuse-zip + ZIP_ER_NOPASSWD from libzip
+    ZIP_ER_BASE (10) from fuse-zip + ZIP_ER_NOPASSWD (26) from libzip
   </int>
   <int value="37" label="Bad password">
-    ZIP_ER_BASE from fuse-zip + ZIP_ER_WRONGPASSWD from libzip
+    ZIP_ER_BASE (10) from fuse-zip + ZIP_ER_WRONGPASSWD (27) from libzip
   </int>
   <int value="132" label="Terminated by SIGILL">
-    MINIJAIL_ERR_SIG_BASE + SIGILL from libminijail
+    MINIJAIL_ERR_SIG_BASE (128) + SIGILL (4) from libminijail
   </int>
   <int value="134" label="Terminated by SIGABRT">
-    MINIJAIL_ERR_SIG_BASE + SIGABRT from libminijail
+    MINIJAIL_ERR_SIG_BASE (128) + SIGABRT (6) from libminijail
   </int>
   <int value="139" label="Terminated by SIGSEGV">
-    MINIJAIL_ERR_SIG_BASE + SIGSEGV from libminijail
+    MINIJAIL_ERR_SIG_BASE (128) + SIGSEGV (11) from libminijail
   </int>
-  <int value="251" label="Cannot mount file in mount namespace">
+  <int value="143" label="Terminated by SIGTERM">
+    MINIJAIL_ERR_SIG_BASE (128) + SIGTERM (15) from libminijail
+  </int>
+  <int value="251" label="Cannot bind-mount file">
     MINIJAIL_ERR_MOUNT from libminijail
   </int>
   <int value="253" label="Seccomp violation">
@@ -55221,8 +55275,6 @@
   <int value="-1668306615" label="CrostiniUseDlc:enabled"/>
   <int value="-1667822423" label="ContextMenuGoogleLensChip:disabled"/>
   <int value="-1666652919" label="MagnifierPanningImprovements:disabled"/>
-  <int value="-1665720309"
-      label="ArcNativeBridge64BitSupportExperiment:disabled"/>
   <int value="-1664795930" label="StorageAccessAPI:disabled"/>
   <int value="-1664290318" label="ComputePressure:enabled"/>
   <int value="-1663410466" label="top-document-isolation"/>
@@ -56070,8 +56122,6 @@
   <int value="-1151615978" label="BorealisBigGl:disabled"/>
   <int value="-1151476162" label="LacrosMergeIcuDataFile:disabled"/>
   <int value="-1151014496" label="NearbySharingDeviceContacts:disabled"/>
-  <int value="-1150881704"
-      label="ArcNativeBridge64BitSupportExperiment:enabled"/>
   <int value="-1146814438" label="TabbedAppOverflowMenuIcons:enabled"/>
   <int value="-1146310028" label="HardwareSecureDecryptionFallback:enabled"/>
   <int value="-1145905507" label="SendTabToSelfWhenSignedIn:disabled"/>
@@ -57531,6 +57581,7 @@
   <int value="-167744090" label="EnableHomeLauncher:enabled"/>
   <int value="-167420098" label="WebBluetoothNewPermissionsBackend:enabled"/>
   <int value="-165756594" label="enable-touch-feedback"/>
+  <int value="-165712979" label="AdaptiveChargingForTesting:enabled"/>
   <int value="-165006916" label="EnableNeuralPalmDetectionFilter:enabled"/>
   <int value="-164673139" label="ForceShowContinueSection:disabled"/>
   <int value="-164539906"
@@ -58043,6 +58094,7 @@
   <int value="178693406" label="LockScreenMediaControls:disabled"/>
   <int value="179083323" label="HelpAppBackgroundPage:disabled"/>
   <int value="179871410" label="ui-debug-tools:disabled"/>
+  <int value="180040169" label="OneGroupPerRenderer:disabled"/>
   <int value="180074362" label="memory-pressure-thresholds"/>
   <int value="181150000" label="CrosVmCupsProxy:enabled"/>
   <int value="182358203" label="AddToHomescreenIPH:enabled"/>
@@ -58527,6 +58579,7 @@
   <int value="494939785" label="InsecureFormSubmissionInterstitial:disabled"/>
   <int value="495435958" label="PageInfoAboutThisSiteMoreInfo:enabled"/>
   <int value="496667708" label="ArcInputOverlay:disabled"/>
+  <int value="497039057" label="AdaptiveChargingForTesting:disabled"/>
   <int value="497137719" label="OmniboxVoiceSearchAlwaysVisible:disabled"/>
   <int value="497150691" label="AssistEmojiEnhanced:disabled"/>
   <int value="500177932" label="ArcSmartTextSelection:disabled"/>
@@ -59401,6 +59454,7 @@
   <int value="1077758422" label="LeakDetectionUnauthenticated:disabled"/>
   <int value="1079032226" label="ParallelDownloading:enabled"/>
   <int value="1079302639" label="ArcEnableWebAppShare:disabled"/>
+  <int value="1080108820" label="OneGroupPerRenderer:enabled"/>
   <int value="1081546525" label="ash-enable-docked-windows"/>
   <int value="1082054180" label="PersistentWindowBounds:disabled"/>
   <int value="1082840061" label="LinkDoctorDeprecationAndroid:disabled"/>
@@ -80047,15 +80101,18 @@
   </int>
   <int value="24" label="Bad password">ERAR_BAD_PASSWORD from libunrar</int>
   <int value="132" label="Terminated by SIGILL">
-    MINIJAIL_ERR_SIG_BASE + SIGILL from libminijail
+    MINIJAIL_ERR_SIG_BASE (128) + SIGILL (4) from libminijail
   </int>
   <int value="134" label="Terminated by SIGABRT">
-    MINIJAIL_ERR_SIG_BASE + SIGABRT from libminijail
+    MINIJAIL_ERR_SIG_BASE (128) + SIGABRT (6) from libminijail
   </int>
   <int value="139" label="Terminated by SIGSEGV">
-    MINIJAIL_ERR_SIG_BASE + SIGSEGV from libminijail
+    MINIJAIL_ERR_SIG_BASE (128) + SIGSEGV (11) from libminijail
   </int>
-  <int value="251" label="Cannot mount file in mount namespace">
+  <int value="143" label="Terminated by SIGTERM">
+    MINIJAIL_ERR_SIG_BASE (128) + SIGTERM (15) from libminijail
+  </int>
+  <int value="251" label="Cannot bind-mount file">
     MINIJAIL_ERR_MOUNT from libminijail
   </int>
   <int value="253" label="Seccomp violation">
diff --git a/tools/metrics/histograms/metadata/METRIC_REVIEWER_OWNERS b/tools/metrics/histograms/metadata/METRIC_REVIEWER_OWNERS
index 0c81ee7a..0b0d121 100644
--- a/tools/metrics/histograms/metadata/METRIC_REVIEWER_OWNERS
+++ b/tools/metrics/histograms/metadata/METRIC_REVIEWER_OWNERS
@@ -59,6 +59,7 @@
 manukh@chromium.org
 mcrouse@chromium.org
 mhasank@chromium.org
+mjwilson@chromium.org
 mlippautz@chromium.org
 mthiesse@chromium.org
 mutexlox@chromium.org
diff --git a/tools/metrics/histograms/metadata/chromeos/histograms.xml b/tools/metrics/histograms/metadata/chromeos/histograms.xml
index 2b9acaa..2d6b6a5 100644
--- a/tools/metrics/histograms/metadata/chromeos/histograms.xml
+++ b/tools/metrics/histograms/metadata/chromeos/histograms.xml
@@ -1897,22 +1897,21 @@
   </token>
 </histogram>
 
-<histogram name="ChromeOS.Zram.{HugePageActivityMetric}" units="%"
+<histogram name="ChromeOS.Zram.{HugePageActivityMetric}" units="pages"
     expires_after="2022-11-10">
   <owner>ctshao@google.com</owner>
   <owner>raging@google.com</owner>
   <owner>bgeffon@chromium.org</owner>
   <owner>chromeos-memory@google.com</owner>
   <summary>
-    Records activity of {HugePageActivityMetric} (i.e., incompressible) in a
-    period of time. Recorded every 10s from boot to shutdown. Period is the time
-    between recordings (10s).
+    Records the {HugePageActivityMetric} in a period of time. Recorded every 10s
+    from boot to shutdown. Period is the time between recordings (10s).
   </summary>
   <token key="HugePageActivityMetric">
     <variant name="HugePagesRemoved"
-        summary="Number of pages removed in a period"/>
+        summary="number of incompressible(huge) pages removed"/>
     <variant name="HugePagesStored"
-        summary="Number of pages added in a period"/>
+        summary="number of incompressible(huge) pages added"/>
   </token>
 </histogram>
 
diff --git a/tools/metrics/histograms/metadata/cryptohome/histograms.xml b/tools/metrics/histograms/metadata/cryptohome/histograms.xml
index d7ab447..cf72dd3 100644
--- a/tools/metrics/histograms/metadata/cryptohome/histograms.xml
+++ b/tools/metrics/histograms/metadata/cryptohome/histograms.xml
@@ -214,6 +214,147 @@
   <summary>Records the result of triggering disk cleanup.</summary>
 </histogram>
 
+<histogram name="Cryptohome.Error.AllLocations" enum="CryptohomeErrorLocation"
+    expires_after="2023-05-01">
+  <owner>zuan@chromium.org</owner>
+  <owner>cros-hwsec+uma@chromium.org</owner>
+  <summary>
+    Background context on this histogram: A CryptohomeError in this context
+    refers to an error that occurred in response to a dbus request to selected
+    cryptohomed APIs. The set of APIs with this CryptohomeError is generally
+    those related to authentication, and contains CryptohomeErrorInfo field in
+    the dbus Reply protobuf. Currently that includes methods that are called
+    during login. A CryptohomeError consists of a linear stack of
+    ErrorLocations, each one of them is an actual line in the cryptohomed
+    codebase which the error passed through, and could have certain attributes,
+    known as ErrorAction attached to them. The ErrorLocation in the bottom of
+    the stack could optionally have a TPM Error Code associated, if the source
+    of error is TPM related.
+
+    For this histogram, for every CryptohomeError that occurs, we individually
+    report each individual ErrorLocation's corresponding enum value when an
+    error ocurred.
+
+    This is useful to cryptohome developers in the sense that they'll be able to
+    know which lines that handle error in cryptohomed has been triggered due to
+    an error.
+  </summary>
+</histogram>
+
+<histogram name="Cryptohome.Error.DevUnexpectedState"
+    enum="CryptohomeErrorLocation" expires_after="2023-05-01">
+  <owner>zuan@chromium.org</owner>
+  <owner>cros-hwsec+uma@chromium.org</owner>
+  <summary>
+    Background context on this histogram: A CryptohomeError in this context
+    refers to an error that occurred in response to a dbus request to selected
+    cryptohomed APIs. The set of APIs with this CryptohomeError is generally
+    those related to authentication, and contains CryptohomeErrorInfo field in
+    the dbus Reply protobuf. Currently that includes methods that are called
+    during login. A CryptohomeError consists of a linear stack of
+    ErrorLocations, each one of them is an actual line in the cryptohomed
+    codebase which the error passed through, and could have certain attributes,
+    known as ErrorAction attached to them. The ErrorLocation in the bottom of
+    the stack could optionally have a TPM Error Code associated, if the source
+    of error is TPM related.
+
+    For this histogram, for every CryptohomeError, we individually report all
+    ErrorLocation that has the kDevCheckUnexpectedState ErrorAction associated
+    with it. The kDevCheckUnexpectedState ErrorAction is used by developers when
+    the developer believes that this error should never occur, so from a
+    notification standpoint, it functions similar to a DCHECK(), except it never
+    crashes cryptohome and handles the error in a graceful manner.
+
+    This is useful to cryptohome developers because they'll be alerted when any
+    of the situations that the developers doesn't expect to occur occurs.
+  </summary>
+</histogram>
+
+<histogram name="Cryptohome.Error.HashedStack" enum="CryptohomeErrorHashed"
+    expires_after="2023-05-01">
+  <owner>zuan@chromium.org</owner>
+  <owner>cros-hwsec+uma@chromium.org</owner>
+  <summary>
+    Background context on this histogram: A CryptohomeError in this context
+    refers to an error that occurred in response to a dbus request to selected
+    cryptohomed APIs. The set of APIs with this CryptohomeError is generally
+    those related to authentication, and contains CryptohomeErrorInfo field in
+    the dbus Reply protobuf. Currently that includes methods that are called
+    during login. A CryptohomeError consists of a linear stack of
+    ErrorLocations, each one of them is an actual line in the cryptohomed
+    codebase which the error passed through, and could have certain attributes,
+    known as ErrorAction attached to them. The ErrorLocation in the bottom of
+    the stack could optionally have a TPM Error Code associated, if the source
+    of error is TPM related.
+
+    For this histogram, we take the entire CryptohomeError's linear stack of
+    ErrorLocation, and hash the entire stack, then we report the hash. The
+    original stack of ErrorLocation is logged.
+
+    This is useful to cryptohome developers because we're able to classify the
+    CryptohomeError by the content of the stack, and identify new ways the error
+    can occur if they do occur.
+  </summary>
+</histogram>
+
+<histogram name="Cryptohome.Error.LeafErrorWithoutTPM"
+    enum="CryptohomeErrorLocation" expires_after="2023-05-01">
+  <owner>zuan@chromium.org</owner>
+  <owner>cros-hwsec+uma@chromium.org</owner>
+  <summary>
+    Background context on this histogram: A CryptohomeError in this context
+    refers to an error that occurred in response to a dbus request to selected
+    cryptohomed APIs. The set of APIs with this CryptohomeError is generally
+    those related to authentication, and contains CryptohomeErrorInfo field in
+    the dbus Reply protobuf. Currently that includes methods that are called
+    during login. A CryptohomeError consists of a linear stack of
+    ErrorLocations, each one of them is an actual line in the cryptohomed
+    codebase which the error passed through, and could have certain attributes,
+    known as ErrorAction attached to them. The ErrorLocation in the bottom of
+    the stack could optionally have a TPM Error Code associated, if the source
+    of error is TPM related.
+
+    For this histogram, for every CryptohomeError that occurred, we report the
+    last ErrorLocation in the stack of ErrorLocation in the CryptohomeError, if
+    the last ErrorLocation does not have a TPM Error Code associated with it.
+
+    This is useful to cryptohome developers because frequently, the last
+    ErrorLocation in the stack would be able to uniquely identify the error, and
+    this histogram doesn't involve hashing, so it would be more convenient for
+    the developers.
+  </summary>
+</histogram>
+
+<histogram name="Cryptohome.Error.LeafErrorWithTPM"
+    enum="CryptohomeErrorLocationWithTPMError" expires_after="2023-05-01">
+  <owner>zuan@chromium.org</owner>
+  <owner>cros-hwsec+uma@chromium.org</owner>
+  <summary>
+    Background context on this histogram: A CryptohomeError in this context
+    refers to an error that occurred in response to a dbus request to selected
+    cryptohomed APIs. The set of APIs with this CryptohomeError is generally
+    those related to authentication, and contains CryptohomeErrorInfo field in
+    the dbus Reply protobuf. Currently that includes methods that are called
+    during login. A CryptohomeError consists of a linear stack of
+    ErrorLocations, each one of them is an actual line in the cryptohomed
+    codebase which the error passed through, and could have certain attributes,
+    known as ErrorAction attached to them. The ErrorLocation in the bottom of
+    the stack could optionally have a TPM Error Code associated, if the source
+    of error is TPM related.
+
+    For this histogram, for every CryptohomeError that occurred, we report the
+    last ErrorLocation and the second last ErrorLocation in the CryptohomeError,
+    if the last ErrorLocation have a TPM Error Code associated with it. The
+    upper and lower 16-bit value of the reported value contains the second last
+    ErrorLocation and the TPM Error Code, correspondingly.
+
+    This is useful to cryptohome developers because frequently, the last and
+    second last ErrorLocation in the stack would be able to uniquely identify
+    the error, and this histogram doesn't involve hashing, so it woul dbe more
+    convenient for the developers.
+  </summary>
+</histogram>
+
 <histogram name="Cryptohome.Errors" enum="CryptohomeError"
     expires_after="2022-09-25">
   <owner>apronin@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/media/histograms.xml b/tools/metrics/histograms/metadata/media/histograms.xml
index 15691e8..11d9b43 100644
--- a/tools/metrics/histograms/metadata/media/histograms.xml
+++ b/tools/metrics/histograms/metadata/media/histograms.xml
@@ -665,7 +665,7 @@
 </histogram>
 
 <histogram name="Media.Audio.Output.Win.{AudioOutputMethod}Error"
-    enum="Hresult" expires_after="M103">
+    enum="Hresult" expires_after="M107">
   <owner>dalecurtis@chromium.org</owner>
   <owner>media-dev@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/stability/histograms.xml b/tools/metrics/histograms/metadata/stability/histograms.xml
index 711f3117..f5f54fe 100644
--- a/tools/metrics/histograms/metadata/stability/histograms.xml
+++ b/tools/metrics/histograms/metadata/stability/histograms.xml
@@ -22,6 +22,16 @@
 
 <histograms>
 
+<histogram name="Stability.Android.MinidumpUploadingTime" units="ms"
+    expires_after="2022-11-30">
+  <owner>shaktisahu@chromium.org</owner>
+  <owner>src/components/minidump_uploader/OWNERS</owner>
+  <summary>
+    Records the time (uptimeMillis) taken by a minidump uploading task to
+    complete. Recorded when the task finished callback is invoked.
+  </summary>
+</histogram>
+
 <histogram name="Stability.Android.OomKillReverseRank" units="rank"
     expires_after="2022-11-13">
   <owner>boliu@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/web_audio/OWNERS b/tools/metrics/histograms/metadata/web_audio/OWNERS
index dd691ad6..91c0afc 100644
--- a/tools/metrics/histograms/metadata/web_audio/OWNERS
+++ b/tools/metrics/histograms/metadata/web_audio/OWNERS
@@ -2,4 +2,5 @@
 
 # Prefer sending CLs to the owners listed below.
 # Use chromium-metrics-reviews@google.com as a backup.
-hongchan@chromium.org
\ No newline at end of file
+hongchan@chromium.org
+mjwilson@chromium.org
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json
index 97a5b07..3b80a51 100644
--- a/tools/perf/core/perfetto_binary_roller/binary_deps.json
+++ b/tools/perf/core/perfetto_binary_roller/binary_deps.json
@@ -5,24 +5,24 @@
             "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux_arm64/49b4b5dcbc312d8d2c3751cf29238b8efeb4e494/trace_processor_shell"
         },
         "win": {
-            "hash": "29b498fe190b44dbb5f5297e6f2723e6f04691af",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/1b28e4be5bc4b001da649e1432c196f5f4103d2a/trace_processor_shell.exe"
+            "hash": "bf1e019be579d5602acdd94a058aa88379866c1d",
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/105e4c338e523b1974bfffb2422262736b91336e/trace_processor_shell.exe"
         },
         "linux_arm": {
             "hash": "58893933be305d3bfe0a72ebebcacde2ac3ca893",
             "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux_arm/49b4b5dcbc312d8d2c3751cf29238b8efeb4e494/trace_processor_shell"
         },
         "mac": {
-            "hash": "b32506de4326c12ad01743553237c74d0e3e6836",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/mac/1b28e4be5bc4b001da649e1432c196f5f4103d2a/trace_processor_shell"
+            "hash": "0033d86be5f8a2574ade41cf2ab22286b09e1699",
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/mac/105e4c338e523b1974bfffb2422262736b91336e/trace_processor_shell"
         },
         "mac_arm64": {
             "hash": "e1ad4861384b06d911a65f035317914b8cc975c6",
             "full_remote_path": "perfetto-luci-artifacts/v25.0/mac-arm64/trace_processor_shell"
         },
         "linux": {
-            "hash": "5ee364b52fed5a2035c89230d0cbb4f1b7b21950",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/1b28e4be5bc4b001da649e1432c196f5f4103d2a/trace_processor_shell"
+            "hash": "a69c29ff4a09e03fde1c602209535753632a1965",
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/fecea55a5bc59c274e8200d06cd68260832d5591/trace_processor_shell"
         }
     },
     "power_profile.sql": {
diff --git a/tools/style_variable_generator/tests/style_variable_generator_test.py b/tools/style_variable_generator/tests/style_variable_generator_test.py
index 29884000..c229ed1 100755
--- a/tools/style_variable_generator/tests/style_variable_generator_test.py
+++ b/tools/style_variable_generator/tests/style_variable_generator_test.py
@@ -36,7 +36,7 @@
     def AddJSONFilesToModel(self, files):
         relpaths_from_cwd = [
             os.path.relpath(os.path.join(os.path.dirname(__file__), f),
-                            os.getcwd()) for f in files
+                            os.getcwd()).replace('\\', '/') for f in files
         ]
         self.generator.AddJSONFilesToModel(relpaths_from_cwd)
 
diff --git a/ui/file_manager/file_manager/background/js/volume_manager_impl.js b/ui/file_manager/file_manager/background/js/volume_manager_impl.js
index 1b72789..a1e2b0e 100644
--- a/ui/file_manager/file_manager/background/js/volume_manager_impl.js
+++ b/ui/file_manager/file_manager/background/js/volume_manager_impl.js
@@ -268,7 +268,7 @@
           case VolumeManagerCommon.VolumeError.NEED_PASSWORD:
           case VolumeManagerCommon.VolumeError.CANCELLED:
           default:
-            console.error('Cannot mount (redacted):', status);
+            console.warn('Cannot mount (redacted):', status);
             console.debug(`Cannot mount '${sourcePath}':`, status);
             this.finishRequest_(requestKey, status);
             return;
@@ -299,7 +299,7 @@
           }
 
           default:
-            console.error('Cannot unmount (redacted):', status);
+            console.warn('Cannot unmount (redacted):', status);
             console.debug(`Cannot unmount '${volumeId}':`, status);
             this.finishRequest_(requestKey, status);
             return;
diff --git a/ui/file_manager/file_manager/background/js/volume_manager_unittest.m.js b/ui/file_manager/file_manager/background/js/volume_manager_unittest.m.js
index 2853f3f8..7e95a63b 100644
--- a/ui/file_manager/file_manager/background/js/volume_manager_unittest.m.js
+++ b/ui/file_manager/file_manager/background/js/volume_manager_unittest.m.js
@@ -302,6 +302,52 @@
   reportPromise(test(), callback);
 }
 
+export function testCancelMountingArchive(callback) {
+  const test = async () => {
+    // Set states of mock fileManagerPrivate APIs.
+    const mountSourcePath = '/usr/local/home/test/Downloads/foobar.zip';
+    chrome.fileManagerPrivate.mountSourcePath_ = mountSourcePath;
+    chrome.fileManagerPrivate.fileSystemMap_['archive:foobar.zip'] =
+        new MockFileSystem('archive:foobar.zip');
+
+    const volumeManager = await volumeManagerFactory.getInstance();
+
+    // Drive + Downloads + Android.
+    const numberOfVolumes = 3;
+    await waitAllVolumes(volumeManager);
+
+    setTimeout(
+        () => mockChrome.fileManagerPrivate.onMountCompleted.dispatchEvent({
+          eventType: 'mount',
+          status: VolumeManagerCommon.VolumeError.CANCELLED,
+          volumeMetadata: {
+            volumeId: null,
+            volumeLabel: null,
+            volumeType: null,
+            isReadOnly: null,
+            sourcePath: mountSourcePath,
+            profile: null,
+            configurable: null,
+            watchable: null,
+            source: null,
+          },
+        }));
+
+    try {
+      await volumeManager.mountArchive(
+          'filesystem:chrome-extension://extensionid/external/' +
+              'Downloads-test/foobar.zip',
+          'My Password');
+    } catch (error) {
+      assertEquals(error, VolumeManagerCommon.VolumeError.CANCELLED);
+    }
+
+    assertEquals(numberOfVolumes, volumeManager.volumeInfoList.length);
+  };
+
+  reportPromise(test(), callback);
+}
+
 export async function testGetCurrentProfileVolumeInfo(done) {
   const volumeManager = await volumeManagerFactory.getInstance();
   await waitAllVolumes(volumeManager);
diff --git a/ui/file_manager/file_manager/foreground/js/file_manager_commands.js b/ui/file_manager/file_manager/foreground/js/file_manager_commands.js
index e9e4e27..bf7f7c3 100644
--- a/ui/file_manager/file_manager/foreground/js/file_manager_commands.js
+++ b/ui/file_manager/file_manager/foreground/js/file_manager_commands.js
@@ -689,7 +689,7 @@
       try {
         await fileManager.volumeManager.unmount(volume);
       } catch (error) {
-        console.error(`Cannot unmount (redacted):`, error);
+        console.warn('Cannot unmount (redacted):', error);
         console.debug(`Cannot unmount '${volume.volumeId}':`, error);
         if (error != VolumeManagerCommon.VolumeError.PATH_NOT_MOUNTED) {
           errorCallback(volume.volumeType);
diff --git a/ui/file_manager/file_manager/foreground/js/file_tasks.js b/ui/file_manager/file_manager/foreground/js/file_tasks.js
index a08fb0c..69b82e0 100644
--- a/ui/file_manager/file_manager/foreground/js/file_tasks.js
+++ b/ui/file_manager/file_manager/foreground/js/file_tasks.js
@@ -494,7 +494,7 @@
             verbButtonLabel = 'OPEN_WITH_VERB_BUTTON_LABEL';
             break;
           default:
-            console.error('Invalid task verb: ' + task.verb + '.');
+            console.error('Invalid task verb: ' + task.verb);
         }
         if (verbButtonLabel) {
           task.label = loadTimeData.getStringF(verbButtonLabel, task.title);
@@ -1031,7 +1031,7 @@
       item.state = ProgressItemState.ERROR;
       this.progressCenter_.updateItem(item);
 
-      console.error('Cannot mount (redacted):', error);
+      console.warn('Cannot mount (redacted):', error);
       console.debug(`Cannot mount '${url}':`, error);
     }
   }
diff --git a/ui/native_theme/BUILD.gn b/ui/native_theme/BUILD.gn
index 33ca89b..051ecc99 100644
--- a/ui/native_theme/BUILD.gn
+++ b/ui/native_theme/BUILD.gn
@@ -124,7 +124,7 @@
 test("native_theme_unittests") {
   use_xvfb = use_xvfb_in_this_config
 
-  sources = []
+  sources = [ "native_theme_features_unittest.cc" ]
 
   if (use_aura) {
     sources += [ "native_theme_aura_unittest.cc" ]
diff --git a/ui/native_theme/native_theme_features.cc b/ui/native_theme/native_theme_features.cc
index 502de5b68..77a70108 100644
--- a/ui/native_theme/native_theme_features.cc
+++ b/ui/native_theme/native_theme_features.cc
@@ -24,6 +24,14 @@
 const base::Feature kOverlayScrollbar{"OverlayScrollbar",
                                       kOverlayScrollbarFeatureState};
 
+// Fluent scrollbars aim to modernize the Chromium scrollbars (both overlay
+// and non-overlay) to fit the Windows 11 Fluent design language. For now,
+// the feature will only support Windows platform and can be later available
+// on Linux as well. The feature is currently in development and disabled
+// by default.
+const base::Feature kFluentScrollbar{"FluentScrollbar",
+                                     base::FEATURE_DISABLED_BY_DEFAULT};
+
 }  // namespace features
 
 namespace ui {
@@ -32,4 +40,14 @@
   return base::FeatureList::IsEnabled(features::kOverlayScrollbar);
 }
 
+bool IsFluentScrollbarEnabled() {
+// Currently, the feature is only supported on Windows.
+#if BUILDFLAG(IS_WIN)
+  return IsOverlayScrollbarEnabled() &&
+         base::FeatureList::IsEnabled(features::kFluentScrollbar);
+#else
+  return false;
+#endif
+}
+
 }  // namespace ui
diff --git a/ui/native_theme/native_theme_features.h b/ui/native_theme/native_theme_features.h
index 85e8985..4a686b3 100644
--- a/ui/native_theme/native_theme_features.h
+++ b/ui/native_theme/native_theme_features.h
@@ -13,12 +13,14 @@
 namespace features {
 
 NATIVE_THEME_EXPORT extern const base::Feature kOverlayScrollbar;
+NATIVE_THEME_EXPORT extern const base::Feature kFluentScrollbar;
 
 }  // namespace features
 
 namespace ui {
 
 NATIVE_THEME_EXPORT bool IsOverlayScrollbarEnabled();
+NATIVE_THEME_EXPORT bool IsFluentScrollbarEnabled();
 
 }  // namespace ui
 
diff --git a/ui/native_theme/native_theme_features_unittest.cc b/ui/native_theme/native_theme_features_unittest.cc
new file mode 100644
index 0000000..9aecc882
--- /dev/null
+++ b/ui/native_theme/native_theme_features_unittest.cc
@@ -0,0 +1,46 @@
+// Copyright 2022 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/native_theme/native_theme_features.h"
+
+#include "base/test/scoped_feature_list.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace ui {
+
+// The unit test verifies that the Fluent scrollbar feature is enabled
+// on Windows when both overlay and fluent scrollbar base features are enabled.
+TEST(FluentScrollbarFeatureStateTest, Enabled) {
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitWithFeatures(
+      {features::kOverlayScrollbar, features::kFluentScrollbar}, {});
+
+  EXPECT_TRUE(IsOverlayScrollbarEnabled());
+#if BUILDFLAG(IS_WIN)
+  EXPECT_TRUE(IsFluentScrollbarEnabled());
+#else
+  EXPECT_FALSE(IsFluentScrollbarEnabled());
+#endif
+}
+
+// The unit test verifies that the Fluent scrollbar feature is disabled when
+// kOverlayScrollbar base feature is disabled no matter the state of
+// kFluentScrollbar.
+TEST(FluentScrollbarFeatureStateTest, Disabled) {
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitWithFeatures({features::kFluentScrollbar},
+                                       {features::kOverlayScrollbar});
+
+  EXPECT_FALSE(IsOverlayScrollbarEnabled());
+  EXPECT_FALSE(IsFluentScrollbarEnabled());
+}
+
+// The unit test verifies that the Fluent scrollbar feature is disabled
+// by default on all platforms.
+TEST(FluentScrollbarFeatureStateTest, DisabledByDefault) {
+  EXPECT_FALSE(base::FeatureList::IsEnabled(features::kFluentScrollbar));
+  EXPECT_FALSE(IsFluentScrollbarEnabled());
+}
+
+}  // namespace ui
diff --git a/ui/qt/BUILD.gn b/ui/qt/BUILD.gn
index 42c4ed81..a75de440 100644
--- a/ui/qt/BUILD.gn
+++ b/ui/qt/BUILD.gn
@@ -88,6 +88,7 @@
   deps = [
     ":qt_interface",
     "//base",
+    "//skia",
     "//ui/gfx",
     "//ui/native_theme",
     "//ui/shell_dialogs",
diff --git a/ui/qt/DEPS b/ui/qt/DEPS
index 9542741..bfa0817c 100644
--- a/ui/qt/DEPS
+++ b/ui/qt/DEPS
@@ -1,4 +1,5 @@
 include_rules = [
+  "+third_party/skia",
   "+ui/gfx",
   "+ui/shell_dialogs",
   "+ui/views",
diff --git a/ui/qt/qt_interface.cc b/ui/qt/qt_interface.cc
index 71de03b6..c75978a 100644
--- a/ui/qt/qt_interface.cc
+++ b/ui/qt/qt_interface.cc
@@ -32,4 +32,38 @@
   free(str_);
 }
 
+Buffer::Buffer() = default;
+
+Buffer::Buffer(const uint8_t* data, size_t size)
+    : data_(static_cast<uint8_t*>(malloc(size))), size_(size) {
+  memcpy(data_, data, size);
+}
+
+Buffer::Buffer(Buffer&& other) {
+  data_ = other.data_;
+  size_ = other.size_;
+  other.data_ = nullptr;
+  other.size_ = 0;
+}
+
+Buffer& Buffer::operator=(Buffer&& other) {
+  free(data_);
+  data_ = other.data_;
+  size_ = other.size_;
+  other.data_ = nullptr;
+  other.size_ = 0;
+  return *this;
+}
+
+Buffer::~Buffer() {
+  free(data_);
+}
+
+uint8_t* Buffer::Take() {
+  uint8_t* data = data_;
+  data_ = nullptr;
+  size_ = 0;
+  return data;
+}
+
 }  // namespace qt
diff --git a/ui/qt/qt_interface.h b/ui/qt/qt_interface.h
index b0b4fb2..569af9e 100644
--- a/ui/qt/qt_interface.h
+++ b/ui/qt/qt_interface.h
@@ -7,6 +7,9 @@
 
 // This file shouldn't include any standard C++ headers (directly or indirectly)
 
+#include <stdint.h>
+#include <stdlib.h>
+
 namespace qt {
 
 // std::string cannot be passed over the library boundary, so this class acts
@@ -20,12 +23,33 @@
   ~String();
 
   // May be nullptr.
-  char* c_str() { return str_; }
+  const char* c_str() const { return str_; }
 
  private:
   char* str_ = nullptr;
 };
 
+// A generic bag of bytes.
+class Buffer {
+ public:
+  Buffer();
+  // Creates a copy of `data`.
+  Buffer(const uint8_t* data, size_t size);
+  Buffer(Buffer&& other);
+  Buffer& operator=(Buffer&& other);
+  ~Buffer();
+
+  // Take ownership of the data in this buffer (resetting `this`).
+  uint8_t* Take();
+
+  uint8_t* data() { return data_; }
+  size_t size() const { return size_; }
+
+ private:
+  uint8_t* data_ = nullptr;
+  size_t size_ = 0;
+};
+
 enum class FontHinting {
   kDefault,
   kNone,
@@ -47,6 +71,14 @@
   int weight;
 };
 
+struct Image {
+  int width = 0;
+  int height = 0;
+  float scale = 1.0f;
+  // The data is stored as ARGB32 (premultiplied).
+  Buffer data_argb;
+};
+
 class QtInterface {
  public:
   class Delegate {
@@ -64,6 +96,7 @@
   virtual double GetScaleFactor() const = 0;
   virtual FontRenderParams GetFontRenderParams() const = 0;
   virtual FontDescription GetFontDescription() const = 0;
+  virtual Image GetIconForContentType(const String& content_type, int size) = 0;
 };
 
 }  // namespace qt
diff --git a/ui/qt/qt_shim.cc b/ui/qt/qt_shim.cc
index 2bac272..89c77435 100644
--- a/ui/qt/qt_shim.cc
+++ b/ui/qt/qt_shim.cc
@@ -8,6 +8,9 @@
 
 #include <QApplication>
 #include <QFont>
+#include <QIcon>
+#include <QMimeDatabase>
+#include <QMimeType>
 
 namespace qt {
 
@@ -74,6 +77,26 @@
   };
 }
 
+Image QtShim::GetIconForContentType(const String& content_type, int size) {
+  QMimeDatabase db;
+  for (const char* mime : {content_type.c_str(), "application/octet-stream"}) {
+    auto mt = db.mimeTypeForName(mime);
+    for (const auto& name : {mt.iconName(), mt.genericIconName()}) {
+      auto icon = QIcon::fromTheme(name);
+      auto pixmap = icon.pixmap(size);
+      auto image = pixmap.toImage();
+      if (image.format() != QImage::Format_ARGB32_Premultiplied)
+        image = image.convertToFormat(QImage::Format_ARGB32_Premultiplied);
+      if (auto bytes = image.sizeInBytes()) {
+        return {image.width(), image.height(),
+                static_cast<float>(image.devicePixelRatio()),
+                Buffer(image.bits(), bytes)};
+      }
+    }
+  }
+  return {};
+}
+
 void QtShim::FontChanged(const QFont& font) {
   delegate_->FontChanged();
 }
diff --git a/ui/qt/qt_shim.h b/ui/qt/qt_shim.h
index 9fe764b..d565363 100644
--- a/ui/qt/qt_shim.h
+++ b/ui/qt/qt_shim.h
@@ -26,6 +26,7 @@
   double GetScaleFactor() const override;
   FontRenderParams GetFontRenderParams() const override;
   FontDescription GetFontDescription() const override;
+  Image GetIconForContentType(const String& content_type, int size) override;
 
  private slots:
   void FontChanged(const QFont& font);
diff --git a/ui/qt/qt_ui.cc b/ui/qt/qt_ui.cc
index eea9484..cb72bb1 100644
--- a/ui/qt/qt_ui.cc
+++ b/ui/qt/qt_ui.cc
@@ -11,11 +11,14 @@
 #include "base/cxx17_backports.h"
 #include "base/notreached.h"
 #include "base/path_service.h"
+#include "third_party/skia/include/core/SkBitmap.h"
 #include "ui/gfx/color_palette.h"
 #include "ui/gfx/font.h"
 #include "ui/gfx/font_render_params.h"
 #include "ui/gfx/font_render_params_linux.h"
 #include "ui/gfx/image/image.h"
+#include "ui/gfx/image/image_skia_rep.h"
+#include "ui/gfx/image/image_skia_source.h"
 #include "ui/qt/qt_interface.h"
 #include "ui/shell_dialogs/select_file_policy.h"
 #include "ui/views/controls/button/label_button_border.h"
@@ -191,8 +194,21 @@
 gfx::Image QtUi::GetIconForContentType(const std::string& content_type,
                                        int size,
                                        float scale) const {
-  NOTIMPLEMENTED_LOG_ONCE();
-  return gfx::Image();
+  Image image =
+      shim_->GetIconForContentType(String(content_type.c_str()), size * scale);
+  if (!image.data_argb.size())
+    return {};
+
+  SkImageInfo image_info = SkImageInfo::Make(
+      image.width, image.height, kBGRA_8888_SkColorType, kPremul_SkAlphaType);
+  SkBitmap bitmap;
+  bitmap.installPixels(
+      image_info, image.data_argb.Take(), image_info.minRowBytes(),
+      [](void* data, void*) { free(data); }, nullptr);
+  gfx::ImageSkia image_skia =
+      gfx::ImageSkia::CreateFromBitmap(bitmap, image.scale);
+  image_skia.MakeThreadSafe();
+  return gfx::Image(image_skia);
 }
 
 QtUi::WindowFrameAction QtUi::GetWindowFrameAction(
diff --git a/ui/views/controls/webview/webview.cc b/ui/views/controls/webview/webview.cc
index a87b20d..e259cf28 100644
--- a/ui/views/controls/webview/webview.cc
+++ b/ui/views/controls/webview/webview.cc
@@ -106,7 +106,7 @@
     wc_owner_.reset();
   AttachWebContentsNativeView();
 
-  if (replacement && replacement->GetMainFrame()->IsRenderFrameCreated()) {
+  if (replacement && replacement->GetMainFrame()->IsRenderFrameLive()) {
     SetUpNewMainFrame(replacement->GetMainFrame());
   } else {
     LostMainFrame();
@@ -138,7 +138,7 @@
   DCHECK(!max_size.IsEmpty());
   min_size_ = min_size;
   max_size_ = max_size;
-  if (web_contents() && web_contents()->GetMainFrame()->IsRenderFrameCreated())
+  if (web_contents() && web_contents()->GetMainFrame()->IsRenderFrameLive())
     MaybeEnableAutoResize(web_contents()->GetMainFrame());
 }
 
@@ -340,7 +340,7 @@
   // yet. If the DCHECK fires, then we would need to handle the initial main
   // frame when it its renderer frame is created.
   if (!old_host) {
-    DCHECK(!new_host->IsRenderFrameCreated());
+    DCHECK(!new_host->IsRenderFrameLive());
     return;
   }
 
@@ -472,7 +472,7 @@
 }
 
 void WebView::MaybeEnableAutoResize(content::RenderFrameHost* frame_host) {
-  DCHECK(frame_host->IsRenderFrameCreated());
+  DCHECK(frame_host->IsRenderFrameLive());
   if (!max_size_.IsEmpty())
     frame_host->GetView()->EnableAutoResize(min_size_, max_size_);
 }
diff --git a/ui/views/widget/native_widget_mac.mm b/ui/views/widget/native_widget_mac.mm
index a56db90..42b1de8 100644
--- a/ui/views/widget/native_widget_mac.mm
+++ b/ui/views/widget/native_widget_mac.mm
@@ -711,8 +711,8 @@
 }
 
 void NativeWidgetMac::SetCursor(const ui::Cursor& cursor) {
-  if (GetInProcessNSWindowBridge())
-    GetInProcessNSWindowBridge()->SetCursor(cursor);
+  if (GetNSWindowMojo())
+    GetNSWindowMojo()->SetCursor(cursor);
 }
 
 void NativeWidgetMac::ShowEmojiPanel() {
diff --git a/ui/webui/resources/BUILD.gn b/ui/webui/resources/BUILD.gn
index ce220f6..7468e5a 100644
--- a/ui/webui/resources/BUILD.gn
+++ b/ui/webui/resources/BUILD.gn
@@ -93,12 +93,12 @@
     deps += [
       ":build_cros_styles_grdp",
       "//ash/webui/common/resources:build_grdp",
-      "//chromeos/components/cros_elements:build_grdp",
+      "//chromeos/ash/components/cros_elements:build_grdp",
       "//third_party/material_web_components:build_grdp",
       "//third_party/web-animations-js:build_grdp",
     ]
     grdp_files += [
-      "$root_gen_dir/chromeos/components/cros_elements/cros_elements_resources.grdp",
+      "$root_gen_dir/chromeos/ash/components/cros_elements/cros_elements_resources.grdp",
       "$root_gen_dir/ui/webui/resources/cros_styles_resources.grdp",
       "$root_gen_dir/third_party/web-animations-js/web_animations_resources.grdp",
       "$root_gen_dir/third_party/material_web_components/material_web_components_resources.grdp",
diff --git a/ui/webui/resources/cr_components/history_clusters/cluster.html b/ui/webui/resources/cr_components/history_clusters/cluster.html
index d8d22b9..58db643c 100644
--- a/ui/webui/resources/cr_components/history_clusters/cluster.html
+++ b/ui/webui/resources/cr_components/history_clusters/cluster.html
@@ -103,7 +103,7 @@
     <div id="label"></div>
     <div class="timestamp-and-menu">
       <div class="timestamp">[[cluster.visits.0.relativeDate]]</div>
-      <menu-container is-top-menu is-cluster-menu></menu-container>
+      <menu-container></menu-container>
     </div>
   </div>
   <template is="dom-repeat" items="[[visibleVisits_]]">
diff --git a/ui/webui/resources/cr_components/history_clusters/cluster.ts b/ui/webui/resources/cr_components/history_clusters/cluster.ts
index a29e37d..0d55010 100644
--- a/ui/webui/resources/cr_components/history_clusters/cluster.ts
+++ b/ui/webui/resources/cr_components/history_clusters/cluster.ts
@@ -17,8 +17,8 @@
 
 import {BrowserProxyImpl} from './browser_proxy.js';
 import {getTemplate} from './cluster.html.js';
-import {Cluster, PageCallbackRouter, URLVisit} from './history_clusters.mojom-webui.js';
-import {ClusterAction, MetricsProxyImpl, VisitAction} from './metrics_proxy.js';
+import {Cluster, ClusterAction, PageCallbackRouter, URLVisit, VisitAction} from './history_clusters.mojom-webui.js';
+import {MetricsProxyImpl} from './metrics_proxy.js';
 import {insertHighlightedTextWithMatchesIntoElement} from './utils.js';
 
 /**
@@ -151,16 +151,16 @@
 
   private onRelatedSearchClicked_() {
     MetricsProxyImpl.getInstance().recordClusterAction(
-        ClusterAction.RELATED_SEARCH_CLICKED, this.index);
+        ClusterAction.kRelatedSearchClicked, this.index);
   }
 
   private onVisitClicked_(event: CustomEvent<URLVisit>) {
     MetricsProxyImpl.getInstance().recordClusterAction(
-        ClusterAction.VISIT_CLICKED, this.index);
+        ClusterAction.kVisitClicked, this.index);
 
     const visit = event.detail;
     MetricsProxyImpl.getInstance().recordVisitAction(
-        VisitAction.CLICKED, this.getVisitIndex_(visit),
+        VisitAction.kClicked, this.getVisitIndex_(visit),
         MetricsProxyImpl.getVisitType(visit));
   }
 
@@ -176,7 +176,7 @@
         visitsToOpen);
 
     MetricsProxyImpl.getInstance().recordClusterAction(
-        ClusterAction.OPENED_IN_TAB_GROUP, this.index);
+        ClusterAction.kOpenedInTabGroup, this.index);
   }
 
   private onRemoveAllVisits_() {
@@ -193,7 +193,7 @@
     // place to record the metric.
     const visit = event.detail;
     MetricsProxyImpl.getInstance().recordVisitAction(
-        VisitAction.DELETED, this.getVisitIndex_(visit),
+        VisitAction.kDeleted, this.getVisitIndex_(visit),
         MetricsProxyImpl.getVisitType(visit));
 
     this.dispatchEvent(new CustomEvent('remove-visits', {
@@ -218,7 +218,7 @@
     this.expanded_ = !this.expanded_;
 
     MetricsProxyImpl.getInstance().recordClusterAction(
-        ClusterAction.RELATED_VISITS_VISIBILITY_TOGGLED, this.index);
+        ClusterAction.kRelatedVisitsVisibilityToggled, this.index);
 
     // Dispatch an event to notify the parent elements of a resize. Note that
     // this simple solution only works because the child iron-collapse has
@@ -266,7 +266,7 @@
       }));
 
       MetricsProxyImpl.getInstance().recordClusterAction(
-          ClusterAction.DELETED, this.index);
+          ClusterAction.kDeleted, this.index);
     } else {
       this.set('cluster.visits', remainingVisits);
     }
diff --git a/ui/webui/resources/cr_components/history_clusters/history_clusters.mojom b/ui/webui/resources/cr_components/history_clusters/history_clusters.mojom
index 1726e12..d448764 100644
--- a/ui/webui/resources/cr_components/history_clusters/history_clusters.mojom
+++ b/ui/webui/resources/cr_components/history_clusters/history_clusters.mojom
@@ -35,6 +35,38 @@
   kSearchResultsPage,
 };
 
+/**
+ * The following enums must be kept in sync with their respective variants in
+ * //tools/metrics/histograms/metadata/history/histograms.xml and
+ * //components/history_clusters/core/cluster_metrics_utils.h
+ */
+
+// Actions that can be performed on clusters.
+enum ClusterAction {
+  kDeleted,
+  kOpenedInTabGroup,
+  kRelatedSearchClicked,
+  kRelatedVisitsVisibilityToggled,
+  kVisitClicked,
+};
+
+// Actions that can be performed on related search items.
+enum RelatedSearchAction {
+  kClicked,
+};
+
+// Actions that can be performed on visits.
+enum VisitAction {
+  kClicked,
+  kDeleted,
+};
+
+// Types of visits that can be shown and acted on.
+enum VisitType {
+  kSRP,
+  kNonSRP
+};
+
 // Represents the most recent visit to a URL within a Cluster. Visits for which
 // there are more recent visits to the same (or a qualifying near-duplicate) URL
 // within the Cluster are omitted.
@@ -151,6 +183,20 @@
 
   // Requests to open the URLs in `visits` in a new tab group.
   OpenVisitUrlsInTabGroup(array<URLVisit> visits);
+
+  // Records visit actions.
+  RecordVisitAction(VisitAction visit_action,
+                    uint32 visit_index,
+                    VisitType visit_type);
+
+  // Records related search click action.
+  RecordRelatedSearchAction(RelatedSearchAction action, uint32 visit_index);
+
+  // Records cluster actions.
+  RecordClusterAction(ClusterAction cluster_action, uint32 cluster_index);
+
+  // Records that the journeys visibility was toggled.
+  RecordToggledVisibility(bool visible);
 };
 
 // WebUI-side handler for requests from the browser.
diff --git a/ui/webui/resources/cr_components/history_clusters/menu_container.html b/ui/webui/resources/cr_components/history_clusters/menu_container.html
index b387076..ddb8b2b 100644
--- a/ui/webui/resources/cr_components/history_clusters/menu_container.html
+++ b/ui/webui/resources/cr_components/history_clusters/menu_container.html
@@ -1,19 +1,7 @@
 <style include="history-clusters-shared-style">
-  :host(:not([is-top-menu])) #openAllButton,
-  :host(:not([is-top-menu])) #removeAllButton {
-    display: none;
-  }
-
   #actionMenuButton {
-    --cr-icon-button-margin-end: 8px;
-  }
-
-  :host([is-top-menu]) #actionMenuButton {
     --cr-icon-button-icon-size: 24px;
-  }
-
-  :host([is-cluster-menu]) #removeSelfButton {
-    display: none;
+    --cr-icon-button-margin-end: 8px;
   }
 </style>
 
@@ -29,12 +17,9 @@
           on-click="onOpenAllButtonClick_">
         $i18n{openAllInTabGroup}
       </button>
-      <button id="removeSelfButton" class="dropdown-item"
-          on-click="onRemoveSelfButtonClick_">
-        $i18n{removeFromHistory}
-      </button>
       <button id="removeAllButton" class="dropdown-item"
-          on-click="onRemoveAllButtonClick_">
+          on-click="onRemoveAllButtonClick_"
+          hidden="[[!allowDeletingHistory_]]">
         $i18n{removeAllFromHistory}
       </button>
     </cr-action-menu>
diff --git a/ui/webui/resources/cr_components/history_clusters/menu_container.ts b/ui/webui/resources/cr_components/history_clusters/menu_container.ts
index 2dd9daa..94310c8 100644
--- a/ui/webui/resources/cr_components/history_clusters/menu_container.ts
+++ b/ui/webui/resources/cr_components/history_clusters/menu_container.ts
@@ -4,12 +4,14 @@
 
 import './history_clusters_shared_style.css.js';
 import '../../cr_elements/cr_action_menu/cr_action_menu.js';
+import '../../cr_elements/cr_icon_button/cr_icon_button.m.js';
 import '../../cr_elements/cr_lazy_render/cr_lazy_render.m.js';
 
 import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 import {CrActionMenuElement} from '../../cr_elements/cr_action_menu/cr_action_menu.js';
 import {CrLazyRenderElement} from '../../cr_elements/cr_lazy_render/cr_lazy_render.m.js';
+import {loadTimeData} from '../../js/load_time_data.m.js';
 
 import {URLVisit} from './history_clusters.mojom-webui.js';
 import {getTemplate} from './menu_container.html.js';
@@ -48,6 +50,15 @@
        * The visit associated with this menu.
        */
       visit: Object,
+
+      /**
+       * Usually this is true, but this can be false if deleting history is
+       * prohibited by Enterprise policy.
+       */
+      allowDeletingHistory_: {
+        type: Boolean,
+        value: () => loadTimeData.getBoolean('allowDeletingHistory'),
+      },
     };
   }
 
@@ -56,17 +67,18 @@
   //============================================================================
 
   visit: URLVisit;
+  private allowDeletingHistory_: boolean;
 
   //============================================================================
   // Event handlers
   //============================================================================
 
-  private onActionMenuButtonClick_(event: MouseEvent) {
+  private onActionMenuButtonClick_(event: Event) {
     this.$.actionMenu.get().showAt(this.$.actionMenuButton);
     event.preventDefault();  // Prevent default browser action (navigation).
   }
 
-  private onOpenAllButtonClick_(event: MouseEvent) {
+  private onOpenAllButtonClick_(event: Event) {
     event.preventDefault();  // Prevent default browser action (navigation).
 
     this.dispatchEvent(new CustomEvent('open-all-visits', {
@@ -77,7 +89,7 @@
     this.$.actionMenu.get().close();
   }
 
-  private onRemoveAllButtonClick_(event: MouseEvent) {
+  private onRemoveAllButtonClick_(event: Event) {
     event.preventDefault();  // Prevent default browser action (navigation).
 
     this.dispatchEvent(new CustomEvent('remove-all-visits', {
@@ -87,18 +99,6 @@
 
     this.$.actionMenu.get().close();
   }
-
-  private onRemoveSelfButtonClick_(event: MouseEvent) {
-    event.preventDefault();  // Prevent default browser action (navigation).
-
-    this.dispatchEvent(new CustomEvent('remove-visit', {
-      bubbles: true,
-      composed: true,
-      detail: this.visit,
-    }));
-
-    this.$.actionMenu.get().close();
-  }
 }
 
 customElements.define(MenuContainerElement.is, MenuContainerElement);
diff --git a/ui/webui/resources/cr_components/history_clusters/metrics_proxy.ts b/ui/webui/resources/cr_components/history_clusters/metrics_proxy.ts
index bdc38fb..d001f6e 100644
--- a/ui/webui/resources/cr_components/history_clusters/metrics_proxy.ts
+++ b/ui/webui/resources/cr_components/history_clusters/metrics_proxy.ts
@@ -2,39 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {Annotation, URLVisit} from './history_clusters.mojom-webui.js';
+import {BrowserProxyImpl} from './browser_proxy.js';
+import {Annotation, ClusterAction, RelatedSearchAction, URLVisit, VisitAction, VisitType} from './history_clusters.mojom-webui.js';
 
 /**
  * @fileoverview This file provides an abstraction layer for logging metrics for
  * mocking in tests.
  */
 
-/**
- * The following enums must be kept in sync with their respective variants in
- * //tools/metrics/histograms/metadata/history/histograms.xml
- */
-export enum ClusterAction {
-  DELETED = 'Deleted',
-  OPENED_IN_TAB_GROUP = 'OpenedInTabGroup',
-  RELATED_SEARCH_CLICKED = 'RelatedSearchClicked',
-  RELATED_VISITS_VISIBILITY_TOGGLED = 'RelatedVisitsVisibilityToggled',
-  VISIT_CLICKED = 'VisitClicked',
-}
-
-export enum RelatedSearchAction {
-  CLICKED = 'Clicked',
-}
-
-export enum VisitAction {
-  CLICKED = 'Clicked',
-  DELETED = 'Deleted',
-}
-
-export enum VisitType {
-  NON_SRP = 'nonSRP',
-  SRP = 'SRP',
-}
-
 export interface MetricsProxy {
   recordClusterAction(action: ClusterAction, index: number): void;
   recordRelatedSearchAction(action: RelatedSearchAction, index: number): void;
@@ -44,25 +19,21 @@
 
 export class MetricsProxyImpl implements MetricsProxy {
   recordClusterAction(action: ClusterAction, index: number) {
-    chrome.metricsPrivate.recordMediumCount(
-        `History.Clusters.UIActions.Cluster.${action}`, index);
+    BrowserProxyImpl.getInstance().handler.recordClusterAction(action, index);
   }
 
   recordRelatedSearchAction(action: RelatedSearchAction, index: number) {
-    chrome.metricsPrivate.recordMediumCount(
-        `History.Clusters.UIActions.RelatedSearch.${action}`, index);
+    BrowserProxyImpl.getInstance().handler.recordRelatedSearchAction(
+        action, index);
   }
 
   recordToggledVisibility(visible: boolean) {
-    chrome.metricsPrivate.recordBoolean(
-        'History.Clusters.UIActions.ToggledVisibility', visible);
+    BrowserProxyImpl.getInstance().handler.recordToggledVisibility(visible);
   }
 
   recordVisitAction(action: VisitAction, index: number, type: VisitType) {
-    chrome.metricsPrivate.recordMediumCount(
-        `History.Clusters.UIActions.Visit.${action}`, index);
-    chrome.metricsPrivate.recordMediumCount(
-        `History.Clusters.UIActions.${type}Visit.${action}`, index);
+    BrowserProxyImpl.getInstance().handler.recordVisitAction(
+        action, index, type);
   }
 
   static getInstance(): MetricsProxy {
@@ -79,8 +50,8 @@
    */
   static getVisitType(visit: URLVisit): VisitType {
     return visit.annotations.includes(Annotation.kSearchResultsPage) ?
-        VisitType.SRP :
-        VisitType.NON_SRP;
+        VisitType.kSRP :
+        VisitType.kNonSRP;
   }
 }
 
diff --git a/ui/webui/resources/cr_components/history_clusters/search_query.ts b/ui/webui/resources/cr_components/history_clusters/search_query.ts
index a92235a..ba00f45 100644
--- a/ui/webui/resources/cr_components/history_clusters/search_query.ts
+++ b/ui/webui/resources/cr_components/history_clusters/search_query.ts
@@ -6,8 +6,8 @@
 
 import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
-import {SearchQuery} from './history_clusters.mojom-webui.js';
-import {MetricsProxyImpl, RelatedSearchAction} from './metrics_proxy.js';
+import {RelatedSearchAction, SearchQuery} from './history_clusters.mojom-webui.js';
+import {MetricsProxyImpl} from './metrics_proxy.js';
 import {OpenWindowProxyImpl} from './open_window_proxy.js';
 import {getTemplate} from './search_query.html.js';
 
@@ -60,7 +60,7 @@
 
   private onAuxClick_() {
     MetricsProxyImpl.getInstance().recordRelatedSearchAction(
-        RelatedSearchAction.CLICKED, this.index);
+        RelatedSearchAction.kClicked, this.index);
 
     // Notify the parent <history-cluster> element of this event.
     this.dispatchEvent(new CustomEvent('related-search-clicked', {
diff --git a/ui/webui/resources/cr_components/history_clusters/url_visit.html b/ui/webui/resources/cr_components/history_clusters/url_visit.html
index 00b2c2a..79c6d1c2 100644
--- a/ui/webui/resources/cr_components/history_clusters/url_visit.html
+++ b/ui/webui/resources/cr_components/history_clusters/url_visit.html
@@ -10,13 +10,14 @@
     background-color: var(--cr-hover-background-color);
   }
 
-  .timestamp-and-menu {
+  #actionMenuButton {
     opacity: 0; /* Hides the element while keeping it in tab order. */
     position: absolute; /* Surrender its layout space to other elements. */
+    --cr-icon-button-margin-end: 8px;
   }
 
-  :host(:hover) .timestamp-and-menu,
-  .timestamp-and-menu:focus-within {
+  :host(:hover) #actionMenuButton,
+  #actionMenuButton:focus-within {
     opacity: 1;
     position: static;
   }
@@ -107,8 +108,20 @@
       </span>
     </div>
   </a>
-  <div class="timestamp-and-menu">
-    <menu-container cluster-index="[[clusterIndex]]" visit="[[visit]]">
-    </menu-container>
-  </div>
+  <cr-icon-button id="actionMenuButton" class="icon-more-vert"
+      title="$i18n{actionMenuDescription}" aria-haspopup="menu"
+      on-click="onActionMenuButtonClick_"
+      hidden="[[!allowDeletingHistory_]]">
+  </cr-icon-button>
 </div>
+
+<cr-lazy-render id="actionMenu">
+  <template>
+    <cr-action-menu role-description="$i18n{actionMenuDescription}">
+      <button id="removeSelfButton" class="dropdown-item"
+          on-click="onRemoveSelfButtonClick_">
+        $i18n{removeFromHistory}
+      </button>
+    </cr-action-menu>
+  </template>
+</cr-lazy-render>
diff --git a/ui/webui/resources/cr_components/history_clusters/url_visit.ts b/ui/webui/resources/cr_components/history_clusters/url_visit.ts
index 9464c90..9fd1aad 100644
--- a/ui/webui/resources/cr_components/history_clusters/url_visit.ts
+++ b/ui/webui/resources/cr_components/history_clusters/url_visit.ts
@@ -2,14 +2,16 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import './menu_container.js';
 import './page_favicon.js';
 import './history_clusters_shared_style.css.js';
 import '../../cr_elements/cr_action_menu/cr_action_menu.js';
+import '../../cr_elements/cr_icon_button/cr_icon_button.m.js';
 import '../../cr_elements/cr_lazy_render/cr_lazy_render.m.js';
 
 import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
+import {CrActionMenuElement} from '../../cr_elements/cr_action_menu/cr_action_menu.js';
+import {CrLazyRenderElement} from '../../cr_elements/cr_lazy_render/cr_lazy_render.m.js';
 import {loadTimeData} from '../../js/load_time_data.m.js';
 
 import {Annotation, URLVisit} from './history_clusters.mojom-webui.js';
@@ -39,6 +41,8 @@
 
 interface VisitRowElement {
   $: {
+    actionMenu: CrLazyRenderElement<CrActionMenuElement>,
+    actionMenuButton: HTMLElement,
     title: HTMLElement,
     url: HTMLElement,
   };
@@ -74,6 +78,15 @@
       },
 
       /**
+       * Usually this is true, but this can be false if deleting history is
+       * prohibited by Enterprise policy.
+       */
+      allowDeletingHistory_: {
+        type: Boolean,
+        value: () => loadTimeData.getBoolean('allowDeletingHistory'),
+      },
+
+      /**
        * Debug info for the visit.
        */
       debugInfo_: {
@@ -110,6 +123,7 @@
   query: string;
   visit: URLVisit;
   private annotations_: Array<string>;
+  private allowDeletingHistory_: boolean;
   private debugInfo_: string;
   private unusedTitle_: string;
   private unusedVisibleUrl_: string;
@@ -127,7 +141,7 @@
     }));
   }
 
-  private onClick_(event: MouseEvent) {
+  private onClick_(event: Event) {
     // Ignore previously handled events.
     if (event.defaultPrevented) {
       return;
@@ -153,6 +167,23 @@
     OpenWindowProxyImpl.getInstance().open(this.visit.normalizedUrl.url);
   }
 
+  private onActionMenuButtonClick_(event: Event) {
+    this.$.actionMenu.get().showAt(this.$.actionMenuButton);
+    event.preventDefault();  // Prevent default browser action (navigation).
+  }
+
+  private onRemoveSelfButtonClick_(event: Event) {
+    event.preventDefault();  // Prevent default browser action (navigation).
+
+    this.dispatchEvent(new CustomEvent('remove-visit', {
+      bubbles: true,
+      composed: true,
+      detail: this.visit,
+    }));
+
+    this.$.actionMenu.get().close();
+  }
+
   //============================================================================
   // Helper methods
   //============================================================================