diff --git a/DEPS b/DEPS
index a0161d55..99ce721 100644
--- a/DEPS
+++ b/DEPS
@@ -245,19 +245,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': '17d90a09a8e664c4705c18f4abba7d0844d3457d',
+  'skia_revision': '466df1e697fde6471ec7c6e3d233410089b6fe8b',
   # 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': '00fa871817c2c6415a1681852b8f2d174365e18d',
+  'v8_revision': '4e822ed7404569f3e018b87ad6150cd82f8e760c',
   # 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': '8eafe491237a2c675c25307c8b409c51e3c9af1a',
+  'angle_revision': '31942507f384ba51cc76a02c9d01c86af9b22013',
   # 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': 'f6bdbed95b3703d782c2a2a537092afb39455442',
+  'swiftshader_revision': '9b1a72a8d2266960a0f036a39c644314c52877d3',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
@@ -320,7 +320,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling devtools-frontend
   # and whatever else without interference from each other.
-  'devtools_frontend_revision': '6d99a3476de7ea8c240956d511e03172d7410bc9',
+  'devtools_frontend_revision': '0177fa779100b8680f43719785dc1ef7c3958ac7',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libprotobuf-mutator
   # and whatever else without interference from each other.
@@ -360,7 +360,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': 'd6a6e9c3c7e61bb42eb590ad4ce69bb2f237f3da',
+  'dawn_revision': '605354395ec6430dd7fd067c41f047aaee6dcc69',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -722,7 +722,7 @@
       'packages': [
         {
           'package': 'chromium/rts/model/linux-amd64',
-          'version': 'dg2x4u0Mz4Ao0TGHBWyhUHKZGT9PEnEAY0lmIhO93bcC',
+          'version': 'dJz8tEOt07Wm-e1YRjIIWIW7oXy5p4EflfvEhugDv1kC',
         },
       ],
       'dep_type': 'cipd',
@@ -733,7 +733,7 @@
       'packages': [
         {
           'package': 'chromium/rts/model/mac-amd64',
-          'version': 'KQBG1Ib12Sj8gzaHsUNWC-rG0pzVEeajCDbCDJg4m2sC',
+          'version': 'Dn1Bw7bTwbCty8wYGzqgIVMIwzfIkhiRXuSjRogWHmYC',
         },
       ],
       'dep_type': 'cipd',
@@ -1024,7 +1024,7 @@
   # Tools used when building Chrome for Chrome OS. This affects both the Simple
   # Chrome workflow, as well as the chromeos-chrome ebuild.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '47a8e92463afb9c78b13fc843888606e9e63588e',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '1f7096347c6a1fd0da859387f476218a03215682',
       'condition': 'checkout_chromeos',
   },
 
@@ -1427,7 +1427,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' + 'd7dfde628cc24c1ae8a0fa46f91878794def8640',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' + '80ed61adcb0af00e74c890e359eef2d3536289fb',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3',
@@ -1609,7 +1609,7 @@
   'src/third_party/usrsctp/usrsctplib':
     Var('chromium_git') + '/external/github.com/sctplab/usrsctp' + '@' + '62d7d0c928c9a040dce96aa2f16c00e7e67d59cb',
 
-  'src/third_party/vulkan-deps': '{chromium_git}/vulkan-deps@4cda77455291d1e03d7788414562135c8d142209',
+  'src/third_party/vulkan-deps': '{chromium_git}/vulkan-deps@85f43409872d6984849dca746335747ca5b6819e',
 
   'src/third_party/vulkan_memory_allocator':
     Var('chromium_git') + '/external/github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator.git' + '@' + 'ebe84bec02c041d28f902da0214bf442743fc907',
@@ -1648,7 +1648,7 @@
     Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + '72561785742eab5afee480368659cde144bca33c',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '80569ea2be144d9ac7088a7bef178c3a679f05e3',
+    Var('webrtc_git') + '/src.git' + '@' + '094e042edb47fdd82baba85e4b8ff8a0c6b6619e',
 
   'src/third_party/libgifcodec':
      Var('skia_git') + '/libgifcodec' + '@'+  Var('libgifcodec_revision'),
@@ -1718,7 +1718,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@eb8737f8a776711bae40977bfc02525b3006999e',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@2d0c33981edd03fadf8adcf3bd69578501853d46',
     'condition': 'checkout_src_internal',
   },
 
@@ -1759,7 +1759,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/media_app/app',
-        'version': '7d6J4YFJkQXaSC8ubPEfNtVxZorP9XEaohyNLvJDez0C',
+        'version': 'u3v3oqVCd4xvpeQtTrOfZ-1Yj1bM1Vf-Ol1pVezZB2YC',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
diff --git a/android_webview/BUILD.gn b/android_webview/BUILD.gn
index ebf1304e..7d7b117 100644
--- a/android_webview/BUILD.gn
+++ b/android_webview/BUILD.gn
@@ -951,13 +951,12 @@
     if (use_v8_context_snapshot) {
       renaming_sources += [ "$root_out_dir/v8_context_snapshot_32.bin" ]
       renaming_destinations += [ "v8_context_snapshot_$arch_suffix.bin" ]
-    }
-    if (!use_v8_context_snapshot || include_both_v8_snapshots) {
+    } else {
       _secondary_abi_out_dir =
           get_label_info("//v8($android_secondary_abi_toolchain)",
                          "root_out_dir")
-      renaming_sources += [ "$_secondary_abi_out_dir/snapshot_blob.bin" ]
-      renaming_destinations += [ "snapshot_blob_$arch_suffix.bin" ]
+      renaming_sources = [ "$_secondary_abi_out_dir/snapshot_blob.bin" ]
+      renaming_destinations = [ "snapshot_blob_$arch_suffix.bin" ]
     }
     disable_compression = true
     deps = [
@@ -971,8 +970,7 @@
   deps = []
   if (use_v8_context_snapshot) {
     deps += [ "//tools/v8_context_snapshot:v8_context_snapshot_assets" ]
-  }
-  if (!use_v8_context_snapshot || include_both_v8_snapshots) {
+  } else {
     deps += [ "//v8:v8_external_startup_data_assets" ]
   }
 }
@@ -981,8 +979,7 @@
   deps = [ "//third_party/icu:icu_assets" ]
   if (use_v8_context_snapshot) {
     deps += [ "//tools/v8_context_snapshot:v8_context_snapshot_assets" ]
-  }
-  if (!use_v8_context_snapshot || include_both_v8_snapshots) {
+  } else {
     deps += [ "//v8:v8_external_startup_data_assets" ]
   }
 }
diff --git a/android_webview/browser/BUILD.gn b/android_webview/browser/BUILD.gn
index 08ea6b9..a0697e3 100644
--- a/android_webview/browser/BUILD.gn
+++ b/android_webview/browser/BUILD.gn
@@ -107,6 +107,8 @@
     "aw_web_ui_controller_factory.h",
     "component_updater/loader_policies/aw_apps_package_names_allowlist_component_loader_policy.cc",
     "component_updater/loader_policies/aw_apps_package_names_allowlist_component_loader_policy.h",
+    "component_updater/loader_policies/empty_component_loader_policy.cc",
+    "component_updater/loader_policies/empty_component_loader_policy.h",
     "component_updater/loader_policies/origin_trials_component_loader_policy.cc",
     "component_updater/loader_policies/origin_trials_component_loader_policy.h",
     "component_updater/origin_trials_component_loader.cc",
diff --git a/android_webview/browser/component_updater/loader_policies/empty_component_loader_policy.cc b/android_webview/browser/component_updater/loader_policies/empty_component_loader_policy.cc
new file mode 100644
index 0000000..fffc77f
--- /dev/null
+++ b/android_webview/browser/component_updater/loader_policies/empty_component_loader_policy.cc
@@ -0,0 +1,66 @@
+// 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 "android_webview/browser/component_updater/loader_policies/empty_component_loader_policy.h"
+
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "android_webview/common/aw_features.h"
+#include "base/containers/flat_map.h"
+#include "base/cxx17_backports.h"
+#include "base/feature_list.h"
+#include "base/files/scoped_file.h"
+#include "base/values.h"
+#include "base/version.h"
+#include "components/component_updater/android/component_loader_policy.h"
+
+namespace android_webview {
+
+namespace {
+
+// Persisted to logs, should never change.
+constexpr char kEmptyComponentLoaderPolicyMetricsSuffix[] =
+    "WebViewEmptyComponent";
+
+// A fake SHA256 PublicKey of jebgalgnebhfojomionfpkfelancnnkf
+const uint8_t kFakePublicKeySHA256[32] = {
+    0x94, 0x16, 0x0b, 0x6d, 0x41, 0x75, 0xe9, 0xec, 0x8e, 0xd5, 0xfa,
+    0x54, 0xb0, 0xd2, 0xdd, 0xa5, 0x6e, 0x05, 0x6b, 0xe8, 0x73, 0x47,
+    0xf6, 0xc4, 0x11, 0x9f, 0xbc, 0xb3, 0x09, 0xb3, 0x5b, 0x40};
+
+}  // namespace
+
+void EmptyComponentLoaderPolicy::ComponentLoaded(
+    const base::Version& /*version*/,
+    base::flat_map<std::string, base::ScopedFD>& /*fd_map*/,
+    std::unique_ptr<base::DictionaryValue> /*manifest*/) {}
+
+void EmptyComponentLoaderPolicy::ComponentLoadFailed(
+    component_updater::ComponentLoadResult /*error*/) {}
+
+void EmptyComponentLoaderPolicy::GetHash(std::vector<uint8_t>* hash) const {
+  hash->assign(kFakePublicKeySHA256,
+               kFakePublicKeySHA256 + base::size(kFakePublicKeySHA256));
+}
+
+std::string EmptyComponentLoaderPolicy::GetMetricsSuffix() const {
+  return kEmptyComponentLoaderPolicyMetricsSuffix;
+}
+
+void LoadEmptyComponent(
+    component_updater::ComponentLoaderPolicyVector& policies) {
+  if (!base::FeatureList::IsEnabled(
+          android_webview::features::kWebViewEmptyComponent)) {
+    return;
+  }
+
+  policies.push_back(std::make_unique<EmptyComponentLoaderPolicy>());
+}
+
+}  // namespace android_webview
diff --git a/android_webview/browser/component_updater/loader_policies/empty_component_loader_policy.h b/android_webview/browser/component_updater/loader_policies/empty_component_loader_policy.h
new file mode 100644
index 0000000..146e03ff
--- /dev/null
+++ b/android_webview/browser/component_updater/loader_policies/empty_component_loader_policy.h
@@ -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.
+
+#ifndef ANDROID_WEBVIEW_BROWSER_COMPONENT_UPDATER_LOADER_POLICIES_EMPTY_COMPONENT_LOADER_POLICY_H_
+#define ANDROID_WEBVIEW_BROWSER_COMPONENT_UPDATER_LOADER_POLICIES_EMPTY_COMPONENT_LOADER_POLICY_H_
+
+#include <stdint.h>
+
+#include <string>
+#include <vector>
+
+#include "base/containers/flat_map.h"
+#include "base/files/scoped_file.h"
+#include "components/component_updater/android/component_loader_policy.h"
+
+namespace base {
+class DictionaryValue;
+class Version;
+}  // namespace base
+
+namespace android_webview {
+
+// A fake empty component to run experiment to measure component updater
+// performance impact.
+// TODO(crbug.com/1288006): remove this when the experiment is over.
+class EmptyComponentLoaderPolicy
+    : public component_updater::ComponentLoaderPolicy {
+ public:
+  EmptyComponentLoaderPolicy() = default;
+  ~EmptyComponentLoaderPolicy() override = default;
+
+  EmptyComponentLoaderPolicy(const EmptyComponentLoaderPolicy&) = delete;
+  EmptyComponentLoaderPolicy& operator=(const EmptyComponentLoaderPolicy&) =
+      delete;
+
+  // The following methods override ComponentLoaderPolicy.
+  void ComponentLoaded(
+      const base::Version& version,
+      base::flat_map<std::string, base::ScopedFD>& fd_map,
+      std::unique_ptr<base::DictionaryValue> manifest) override;
+  void ComponentLoadFailed(
+      component_updater::ComponentLoadResult error) override;
+  void GetHash(std::vector<uint8_t>* hash) const override;
+  std::string GetMetricsSuffix() const override;
+};
+
+void LoadEmptyComponent(
+    component_updater::ComponentLoaderPolicyVector& policies);
+
+}  // namespace android_webview
+
+#endif  // ANDROID_WEBVIEW_BROWSER_COMPONENT_UPDATER_LOADER_POLICIES_EMPTY_COMPONENT_LOADER_POLICY_H_
diff --git a/android_webview/browser/component_updater/registration.cc b/android_webview/browser/component_updater/registration.cc
index 2ca51ff..2dab5751 100644
--- a/android_webview/browser/component_updater/registration.cc
+++ b/android_webview/browser/component_updater/registration.cc
@@ -5,6 +5,7 @@
 #include "android_webview/browser/component_updater/registration.h"
 
 #include "android_webview/browser/component_updater/loader_policies/aw_apps_package_names_allowlist_component_loader_policy.h"
+#include "android_webview/browser/component_updater/loader_policies/empty_component_loader_policy.h"
 #include "android_webview/browser/component_updater/origin_trials_component_loader.h"
 #include "android_webview/browser/component_updater/trust_token_key_commitments_component_loader.h"
 
@@ -15,6 +16,7 @@
   LoadTrustTokenKeyCommitmentsComponent(policies);
   LoadOriginTrialsComponent(policies);
   LoadPackageNamesAllowlistComponent(policies);
+  LoadEmptyComponent(policies);
   return policies;
 }
 
diff --git a/android_webview/common/aw_features.cc b/android_webview/common/aw_features.cc
index 7b4acfe..eddc64a 100644
--- a/android_webview/common/aw_features.cc
+++ b/android_webview/common/aw_features.cc
@@ -33,6 +33,11 @@
 const base::Feature kWebViewDisplayCutout{"WebViewDisplayCutout",
                                           base::FEATURE_ENABLED_BY_DEFAULT};
 
+// Fake empty component to measure component updater performance impact on
+// WebView clients.
+const base::Feature kWebViewEmptyComponent{"WebViewEmptyComponentLoaderPolicy",
+                                           base::FEATURE_DISABLED_BY_DEFAULT};
+
 // When enabled, passive mixed content (Audio/Video/Image subresources loaded
 // over HTTP on HTTPS sites) will be autoupgraded to HTTPS, and the load will be
 // blocked if the resource fails to load over HTTPS. This only affects apps that
diff --git a/android_webview/common/aw_features.h b/android_webview/common/aw_features.h
index e0ecc5ee..43a4fac 100644
--- a/android_webview/common/aw_features.h
+++ b/android_webview/common/aw_features.h
@@ -19,6 +19,7 @@
 extern const base::Feature kWebViewConnectionlessSafeBrowsing;
 extern const base::Feature kWebViewDarkModeMatchTheme;
 extern const base::Feature kWebViewDisplayCutout;
+extern const base::Feature kWebViewEmptyComponent;
 extern const base::Feature kWebViewExtraHeadersSameOriginOnly;
 extern const base::Feature kWebViewForceDarkModeMatchTheme;
 extern const base::Feature kWebViewJavaJsBridgeMojo;
diff --git a/android_webview/lib/aw_main_delegate.cc b/android_webview/lib/aw_main_delegate.cc
index 4ddf028..b2da692 100644
--- a/android_webview/lib/aw_main_delegate.cc
+++ b/android_webview/lib/aw_main_delegate.cc
@@ -162,16 +162,18 @@
     if (AwDrawFnImpl::IsUsingVulkan())
       cl->AppendSwitch(switches::kWebViewDrawFunctorUsesVulkan);
 
-#if !defined(USE_V8_CONTEXT_SNAPSHOT) || defined(INCLUDE_BOTH_V8_SNAPSHOTS)
-    // The snapshot for USE_V8_CONTEXT_SNAPSHOT is handled in the renderer.
+#if defined(USE_V8_CONTEXT_SNAPSHOT)
+    const gin::V8SnapshotFileType file_type =
+        gin::V8SnapshotFileType::kWithAdditionalContext;
+#else
     const gin::V8SnapshotFileType file_type = gin::V8SnapshotFileType::kDefault;
+#endif
     base::android::RegisterApkAssetWithFileDescriptorStore(
         content::kV8Snapshot32DataDescriptor,
         gin::V8Initializer::GetSnapshotFilePath(true, file_type));
     base::android::RegisterApkAssetWithFileDescriptorStore(
         content::kV8Snapshot64DataDescriptor,
         gin::V8Initializer::GetSnapshotFilePath(false, file_type));
-#endif
   }
 
   if (cl->HasSwitch(switches::kWebViewSandboxedRenderer)) {
diff --git a/android_webview/test/BUILD.gn b/android_webview/test/BUILD.gn
index de509f5..5900ff0 100644
--- a/android_webview/test/BUILD.gn
+++ b/android_webview/test/BUILD.gn
@@ -163,8 +163,7 @@
 
   if (use_v8_context_snapshot) {
     deps += [ "//tools/v8_context_snapshot:v8_context_snapshot_assets" ]
-  }
-  if (!use_v8_context_snapshot || include_both_v8_snapshots) {
+  } else {
     deps += [ "//v8:v8_external_startup_data_assets" ]
   }
 }
@@ -592,8 +591,7 @@
 
   if (use_v8_context_snapshot) {
     deps += [ "//tools/v8_context_snapshot:v8_context_snapshot_assets" ]
-  }
-  if (!use_v8_context_snapshot || include_both_v8_snapshots) {
+  } else {
     deps += [ "//v8:v8_external_startup_data_assets" ]
   }
 }
diff --git a/ash/accessibility/chromevox/touch_exploration_manager.cc b/ash/accessibility/chromevox/touch_exploration_manager.cc
index 1efca099..3ed63aa 100644
--- a/ash/accessibility/chromevox/touch_exploration_manager.cc
+++ b/ash/accessibility/chromevox/touch_exploration_manager.cc
@@ -255,7 +255,7 @@
 
 bool TouchExplorationManager::VolumeAdjustSoundEnabled() {
   return !base::CommandLine::ForCurrentProcess()->HasSwitch(
-      chromeos::switches::kDisableVolumeAdjustSound);
+      switches::kDisableVolumeAdjustSound);
 }
 
 }  // namespace ash
diff --git a/ash/app_list/BUILD.gn b/ash/app_list/BUILD.gn
index a84d0a8..2642950f 100644
--- a/ash/app_list/BUILD.gn
+++ b/ash/app_list/BUILD.gn
@@ -62,6 +62,8 @@
     "views/app_list_reorder_undo_container_view.h",
     "views/app_list_view.cc",
     "views/app_list_view.h",
+    "views/app_list_view_util.cc",
+    "views/app_list_view_util.h",
     "views/apps_container_view.cc",
     "views/apps_container_view.h",
     "views/apps_grid_context_menu.cc",
diff --git a/ash/app_list/test/app_list_test_helper.cc b/ash/app_list/test/app_list_test_helper.cc
index 4329f8b..0aa1cf63 100644
--- a/ash/app_list/test/app_list_test_helper.cc
+++ b/ash/app_list/test/app_list_test_helper.cc
@@ -90,6 +90,12 @@
   WaitUntilIdle();
 }
 
+void AppListTestHelper::StartSlideAnimationOnBubbleAppsPage(
+    views::View* view,
+    int vertical_offset) {
+  GetBubbleAppsPage()->SlideViewIntoPosition(view, vertical_offset);
+}
+
 void AppListTestHelper::CheckVisibility(bool visible) {
   EXPECT_EQ(visible, app_list_controller_->IsVisible());
   EXPECT_EQ(visible, app_list_controller_->GetTargetVisibility(absl::nullopt));
diff --git a/ash/app_list/test/app_list_test_helper.h b/ash/app_list/test/app_list_test_helper.h
index 0fc41d9..aad4092b 100644
--- a/ash/app_list/test/app_list_test_helper.h
+++ b/ash/app_list/test/app_list_test_helper.h
@@ -74,6 +74,10 @@
   // until animation finishes.
   void ToggleAndRunLoop(uint64_t display_id, AppListShowSource show_source);
 
+  // Slides a bubble apps page's component using a layer animation.
+  void StartSlideAnimationOnBubbleAppsPage(views::View* view,
+                                           int vertical_offset);
+
   // Check the visibility value of the app list and its target.
   // Fails in tests if either one doesn't match |visible|.
   // DEPRECATED: Prefer to EXPECT_TRUE or EXPECT_FALSE the visibility directly,
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 69eb3cf..13e8038 100644
--- a/ash/app_list/views/app_list_bubble_apps_page.cc
+++ b/ash/app_list/views/app_list_bubble_apps_page.cc
@@ -14,6 +14,7 @@
 #include "ash/app_list/app_list_view_delegate.h"
 #include "ash/app_list/model/app_list_model.h"
 #include "ash/app_list/views/app_list_reorder_undo_container_view.h"
+#include "ash/app_list/views/app_list_view_util.h"
 #include "ash/app_list/views/continue_section_view.h"
 #include "ash/app_list/views/recent_apps_view.h"
 #include "ash/app_list/views/scrollable_apps_grid_view.h"
@@ -67,6 +68,13 @@
 // Insets for the separator between the continue section and apps.
 constexpr gfx::Insets kSeparatorInsets(0, 12);
 
+// A slide animation's duration.
+constexpr base::TimeDelta kSlideAnimationDuration = base::Milliseconds(250);
+
+// A slide animation's tween type.
+constexpr gfx::Tween::Type kSlideAnimationTweenType =
+    gfx::Tween::LINEAR_OUT_SLOW_IN;
+
 }  // namespace
 
 AppListBubbleAppsPage::AppListBubbleAppsPage(
@@ -223,65 +231,12 @@
   // animation. No need to use SlideViewIntoPosition() because this view always
   // has a layer.
   StartSlideInAnimation(
-      scrollable_apps_grid_view_, vertical_offset,
+      scrollable_apps_grid_view_, vertical_offset, kSlideAnimationDuration,
+      kSlideAnimationTweenType,
       base::BindRepeating(&AppListBubbleAppsPage::OnAppsGridViewAnimationEnded,
                           weak_factory_.GetWeakPtr()));
 }
 
-void AppListBubbleAppsPage::SlideViewIntoPosition(views::View* view,
-                                                  int vertical_offset) {
-  // Abort any in-progress layer animation. Views might have temporary layers
-  // during animations that are cleaned up at the end. The code below needs to
-  // know the final desired layer state.
-  if (view->layer()) {
-    DCHECK(view->layer()->GetAnimator());
-    view->layer()->GetAnimator()->AbortAllAnimations();
-  }
-
-  // Add a layer for the view if it doesn't have one at baseline.
-  const bool create_layer = !view->layer();
-  if (create_layer) {
-    view->SetPaintToLayer();
-    view->layer()->SetFillsBoundsOpaquely(false);
-  }
-
-  // If we created a layer for the view, undo that when the animation ends.
-  // The underlying views don't expose weak pointers directly, so use a weak
-  // pointer to this view, which owns its children.
-  auto cleanup = create_layer ? base::BindRepeating(
-                                    &AppListBubbleAppsPage::DestroyLayerForView,
-                                    weak_factory_.GetWeakPtr(), view)
-                              : base::DoNothing();
-  StartSlideInAnimation(view, vertical_offset, cleanup);
-}
-
-void AppListBubbleAppsPage::StartSlideInAnimation(
-    views::View* view,
-    int vertical_offset,
-    base::RepeatingClosure cleanup) {
-  DCHECK(view->layer());
-
-  // Animation spec:
-  //
-  // Y Position: Down (offset) → End position
-  // Duration: 250ms
-  // Ease: (0.00, 0.00, 0.20, 1.00)
-
-  // Set the initial offset via a layer transform.
-  gfx::Transform translate_down;
-  translate_down.Translate(0, vertical_offset);
-  view->layer()->SetTransform(translate_down);
-
-  // Animate the transform back to the identity transform.
-  constexpr gfx::Transform kIdentity;
-  views::AnimationBuilder()
-      .OnEnded(cleanup)
-      .OnAborted(cleanup)
-      .Once()
-      .SetDuration(base::Milliseconds(250))
-      .SetTransform(view, kIdentity, gfx::Tween::LINEAR_OUT_SLOW_IN);
-}
-
 void AppListBubbleAppsPage::StartHideAnimation() {
   // Remove the gradient mask from the scroll view to improve performance.
   gradient_helper_.reset();
@@ -401,6 +356,27 @@
   gradient_helper_->UpdateGradientZone();
 }
 
+void AppListBubbleAppsPage::SlideViewIntoPosition(views::View* view,
+                                                  int vertical_offset) {
+  // Animation spec:
+  //
+  // Y Position: Down (offset) → End position
+  // Duration: 250ms
+  // Ease: (0.00, 0.00, 0.20, 1.00)
+
+  const bool create_layer = PrepareForLayerAnimation(view);
+
+  // If we created a layer for the view, undo that when the animation ends.
+  // The underlying views don't expose weak pointers directly, so use a weak
+  // pointer to this view, which owns its children.
+  auto cleanup = create_layer ? base::BindRepeating(
+                                    &AppListBubbleAppsPage::DestroyLayerForView,
+                                    weak_factory_.GetWeakPtr(), view)
+                              : base::DoNothing();
+  StartSlideInAnimation(view, vertical_offset, kSlideAnimationDuration,
+                        kSlideAnimationTweenType, cleanup);
+}
+
 BEGIN_METADATA(AppListBubbleAppsPage, views::View)
 END_METADATA
 
diff --git a/ash/app_list/views/app_list_bubble_apps_page.h b/ash/app_list/views/app_list_bubble_apps_page.h
index 8fa76615..178b307 100644
--- a/ash/app_list/views/app_list_bubble_apps_page.h
+++ b/ash/app_list/views/app_list_bubble_apps_page.h
@@ -59,11 +59,6 @@
   // Starts the launcher show animation.
   void StartShowAnimation();
 
-  // Animates `view` using a layer animation. Creates the layer if needed. The
-  // layer is pushed down by `vertical_offset` at the start of the animation and
-  // animates back to its original position. Public for testing.
-  void SlideViewIntoPosition(views::View* view, int vertical_offset);
-
   // Starts the launcher hide animation. None of the child views animate, but
   // this disables the scroll view gradient mask to improve performance.
   void StartHideAnimation();
@@ -119,13 +114,6 @@
 
   void UpdateSeparatorVisibility();
 
-  // Starts a vertical slide animation for `view` with `vertical_offset` as the
-  // initial offset. The view must already have a layer. Runs the `cleanup`
-  // callback when the animation ends or aborts.
-  void StartSlideInAnimation(views::View* view,
-                             int vertical_offset,
-                             base::RepeatingClosure cleanup);
-
   // Destroys the layer for `view`. Not static so it can be used with weak
   // pointers.
   void DestroyLayerForView(views::View* view);
@@ -133,6 +121,11 @@
   // Callback for when the apps grid view animation ends.
   void OnAppsGridViewAnimationEnded();
 
+  // Animates `view` using a layer animation. Creates the layer if needed. The
+  // layer is pushed down by `vertical_offset` at the start of the animation and
+  // animates back to its original position.
+  void SlideViewIntoPosition(views::View* view, int vertical_offset);
+
   views::ScrollView* scroll_view_ = nullptr;
   ContinueSectionView* continue_section_ = nullptr;
   RecentAppsView* recent_apps_ = nullptr;
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 f76701f..dd41cb75 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
@@ -38,14 +38,13 @@
   // Trigger a slide animation.
   ui::ScopedAnimationDurationScaleMode duration(
       ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
-  auto* apps_page = helper->GetBubbleAppsPage();
   constexpr int kVerticalOffset = 20;
-  apps_page->SlideViewIntoPosition(recent_apps, kVerticalOffset);
+  helper->StartSlideAnimationOnBubbleAppsPage(recent_apps, kVerticalOffset);
   ASSERT_TRUE(recent_apps->layer());
   EXPECT_TRUE(recent_apps->layer()->GetAnimator()->is_animating());
 
   // While that animation is running, run another animation.
-  apps_page->SlideViewIntoPosition(recent_apps, kVerticalOffset);
+  helper->StartSlideAnimationOnBubbleAppsPage(recent_apps, kVerticalOffset);
   auto* compositor = recent_apps->layer()->GetCompositor();
   while (recent_apps->layer() &&
          recent_apps->layer()->GetAnimator()->is_animating()) {
diff --git a/ash/app_list/views/app_list_view_util.cc b/ash/app_list/views/app_list_view_util.cc
new file mode 100644
index 0000000..a3f2dc6a
--- /dev/null
+++ b/ash/app_list/views/app_list_view_util.cc
@@ -0,0 +1,77 @@
+// 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 "ash/app_list/views/app_list_view_util.h"
+
+#include "base/callback.h"
+#include "ui/compositor/layer.h"
+#include "ui/compositor/layer_animator.h"
+#include "ui/views/animation/animation_builder.h"
+#include "ui/views/view.h"
+
+namespace ash {
+
+bool PrepareForLayerAnimation(views::View* view) {
+  // Abort any in-progress layer animation. Views might have temporary layers
+  // during animations that are cleaned up at the end. The code below needs to
+  // know the final desired layer state.
+  if (view->layer()) {
+    DCHECK(view->layer()->GetAnimator());
+    view->layer()->GetAnimator()->AbortAllAnimations();
+  }
+
+  // Add a layer for the view if it doesn't have one at baseline.
+  // NOTE: animation abortion may trigger the callback that deletes the layer.
+  // Therefore `created_layer` should be calculated after aborting animations.
+  const bool create_layer = !view->layer();
+  if (create_layer) {
+    view->SetPaintToLayer();
+    view->layer()->SetFillsBoundsOpaquely(false);
+  }
+
+  return create_layer;
+}
+
+void StartSlideInAnimation(views::View* view,
+                           int vertical_offset,
+                           const base::TimeDelta& time_delta,
+                           gfx::Tween::Type tween_type,
+                           base::RepeatingClosure cleanup) {
+  DCHECK(view->layer());
+
+  // Set the initial offset via a layer transform.
+  gfx::Transform translate_down;
+  translate_down.Translate(0, vertical_offset);
+  view->layer()->SetTransform(translate_down);
+
+  // Animate the transform back to the identity transform.
+  views::AnimationBuilder()
+      .OnEnded(cleanup)
+      .OnAborted(cleanup)
+      .Once()
+      .SetTransform(view, gfx::Transform(), tween_type)
+      .SetDuration(time_delta);
+}
+
+void SlideViewIntoPositionWithSequenceBlock(
+    views::View* view,
+    int vertical_offset,
+    const absl::optional<base::TimeDelta>& time_delta,
+    gfx::Tween::Type tween_type,
+    views::AnimationSequenceBlock* sequence_block) {
+  DCHECK(view->layer());
+
+  // Set the initial offset via a layer transform.
+  gfx::Transform translate_down;
+  translate_down.Translate(0, vertical_offset);
+  view->layer()->SetTransform(translate_down);
+
+  // Animate the transform back to the identity transform.
+  sequence_block->SetTransform(view, gfx::Transform(), tween_type);
+
+  if (time_delta)
+    sequence_block->SetDuration(*time_delta);
+}
+
+}  // namespace ash
diff --git a/ash/app_list/views/app_list_view_util.h b/ash/app_list/views/app_list_view_util.h
new file mode 100644
index 0000000..9adf031
--- /dev/null
+++ b/ash/app_list/views/app_list_view_util.h
@@ -0,0 +1,47 @@
+// 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 ASH_APP_LIST_VIEWS_APP_LIST_VIEW_UTIL_H_
+#define ASH_APP_LIST_VIEWS_APP_LIST_VIEW_UTIL_H_
+
+#include "base/callback_forward.h"
+#include "ui/gfx/animation/tween.h"
+
+namespace base {
+class TimeDelta;
+}  // namespace base
+
+namespace views {
+class View;
+class AnimationSequenceBlock;
+}  // namespace views
+
+namespace ash {
+
+// Prepares for a layer animation on `view`. Returns whether a layer is created
+// for `view`.
+bool PrepareForLayerAnimation(views::View* view);
+
+// Starts a vertical slide animation for `view` with `vertical_offset` as the
+// initial offset. The view must already have a layer. Runs the `end_callback`
+// when the animation ends or aborts.
+void StartSlideInAnimation(views::View* view,
+                           int vertical_offset,
+                           const base::TimeDelta& time_delta,
+                           gfx::Tween::Type tween_type,
+                           base::RepeatingClosure end_callback);
+
+// Similar to the method above. But use a specified animation sequence block
+// instead of creating a new one. Sets the animation duration if `time_delta`
+// is meaningful. NOTE: `sequence_block` can only set duration once.
+void SlideViewIntoPositionWithSequenceBlock(
+    views::View* view,
+    int vertical_offset,
+    const absl::optional<base::TimeDelta>& time_delta,
+    gfx::Tween::Type tween_type,
+    views::AnimationSequenceBlock* sequence_block);
+
+}  // namespace ash
+
+#endif  // ASH_APP_LIST_VIEWS_APP_LIST_VIEW_UTIL_H_
diff --git a/ash/components/arc/arc_util.cc b/ash/components/arc/arc_util.cc
index d6999304..bc2f8fb2 100644
--- a/ash/components/arc/arc_util.cc
+++ b/ash/components/arc/arc_util.cc
@@ -91,9 +91,9 @@
 bool IsArcAvailable() {
   const auto* command_line = base::CommandLine::ForCurrentProcess();
 
-  if (command_line->HasSwitch(chromeos::switches::kArcAvailability)) {
+  if (command_line->HasSwitch(ash::switches::kArcAvailability)) {
     const std::string value =
-        command_line->GetSwitchValueASCII(chromeos::switches::kArcAvailability);
+        command_line->GetSwitchValueASCII(ash::switches::kArcAvailability);
     DCHECK(value == kAvailabilityNone || value == kAvailabilityInstalled ||
            value == kAvailabilityOfficiallySupported)
         << "Unknown flag value: " << value;
@@ -105,20 +105,20 @@
   // For transition, fallback to old flags.
   // TODO(hidehiko): Remove this and clean up whole this function, when
   // session_manager supports a new flag.
-  return command_line->HasSwitch(chromeos::switches::kEnableArc) ||
-         (command_line->HasSwitch(chromeos::switches::kArcAvailable) &&
+  return command_line->HasSwitch(ash::switches::kEnableArc) ||
+         (command_line->HasSwitch(ash::switches::kArcAvailable) &&
           base::FeatureList::IsEnabled(kEnableArcFeature));
 }
 
 bool IsArcVmEnabled() {
   return base::CommandLine::ForCurrentProcess()->HasSwitch(
-      chromeos::switches::kEnableArcVm);
+      ash::switches::kEnableArcVm);
 }
 
 bool IsArcVmRtVcpuEnabled(uint32_t cpus) {
   // TODO(kansho): remove switch after tast test use Finch instead.
   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
-          chromeos::switches::kEnableArcVmRtVcpu)) {
+          ash::switches::kEnableArcVmRtVcpu)) {
     return true;
   }
   if (cpus == 2 && base::FeatureList::IsEnabled(kRtVcpuDualCore))
@@ -130,17 +130,17 @@
 
 bool IsArcVmUseHugePages() {
   return base::CommandLine::ForCurrentProcess()->HasSwitch(
-      chromeos::switches::kArcVmUseHugePages);
+      ash::switches::kArcVmUseHugePages);
 }
 
 bool IsArcVmDevConfIgnored() {
   return base::CommandLine::ForCurrentProcess()->HasSwitch(
-      chromeos::switches::kIgnoreArcVmDevConf);
+      ash::switches::kIgnoreArcVmDevConf);
 }
 
 bool IsUreadaheadDisabled() {
   return base::CommandLine::ForCurrentProcess()->HasSwitch(
-      chromeos::switches::kArcDisableUreadahead);
+      ash::switches::kArcDisableUreadahead);
 }
 
 ArcVmUreadaheadMode GetArcVmUreadaheadMode(SystemMemoryInfoCallback callback) {
@@ -156,17 +156,17 @@
                                        : ArcVmUreadaheadMode::READAHEAD
                                  : ArcVmUreadaheadMode::DISABLED;
   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
-          chromeos::switches::kArcVmUreadaheadMode)) {
+          ash::switches::kArcVmUreadaheadMode)) {
     const std::string value =
         base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
-            chromeos::switches::kArcVmUreadaheadMode);
+            ash::switches::kArcVmUreadaheadMode);
     if (value == kGenerate) {
       mode = ArcVmUreadaheadMode::GENERATE;
     } else if (value == kDisabled) {
       mode = ArcVmUreadaheadMode::DISABLED;
     } else {
       LOG(ERROR) << "Invalid parameter " << value << " for "
-                 << chromeos::switches::kArcVmUreadaheadMode;
+                 << ash::switches::kArcVmUreadaheadMode;
     }
   }
   return mode;
@@ -174,35 +174,35 @@
 
 bool ShouldArcAlwaysStart() {
   const auto* command_line = base::CommandLine::ForCurrentProcess();
-  if (!command_line->HasSwitch(chromeos::switches::kArcStartMode))
+  if (!command_line->HasSwitch(ash::switches::kArcStartMode))
     return false;
-  return command_line->GetSwitchValueASCII(chromeos::switches::kArcStartMode) ==
+  return command_line->GetSwitchValueASCII(ash::switches::kArcStartMode) ==
          kAlwaysStartWithNoPlayStore;
 }
 
 bool ShouldArcAlwaysStartWithNoPlayStore() {
   return base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
-             chromeos::switches::kArcStartMode) == kAlwaysStartWithNoPlayStore;
+             ash::switches::kArcStartMode) == kAlwaysStartWithNoPlayStore;
 }
 
 bool ShouldShowOptInForTesting() {
   return base::CommandLine::ForCurrentProcess()->HasSwitch(
-      chromeos::switches::kArcForceShowOptInUi);
+      ash::switches::kArcForceShowOptInUi);
 }
 
 bool IsArcKioskAvailable() {
   const auto* command_line = base::CommandLine::ForCurrentProcess();
 
-  if (command_line->HasSwitch(chromeos::switches::kArcAvailability)) {
+  if (command_line->HasSwitch(ash::switches::kArcAvailability)) {
     std::string value =
-        command_line->GetSwitchValueASCII(chromeos::switches::kArcAvailability);
+        command_line->GetSwitchValueASCII(ash::switches::kArcAvailability);
     if (value == kAvailabilityInstalled)
       return true;
     return IsArcAvailable();
   }
 
   // TODO(hidehiko): Remove this when session_manager supports the new flag.
-  if (command_line->HasSwitch(chromeos::switches::kArcAvailable))
+  if (command_line->HasSwitch(ash::switches::kArcAvailable))
     return true;
 
   // If not special kiosk device case, use general ARC check.
@@ -247,7 +247,7 @@
 
 bool IsArcOptInVerificationDisabled() {
   return base::CommandLine::ForCurrentProcess()->HasSwitch(
-      chromeos::switches::kDisableArcOptInVerification);
+      ash::switches::kDisableArcOptInVerification);
 }
 
 absl::optional<int> GetWindowTaskId(const aura::Window* window) {
@@ -296,34 +296,34 @@
 
 bool IsArcForceCacheAppIcon() {
   return base::CommandLine::ForCurrentProcess()->HasSwitch(
-      chromeos::switches::kArcGeneratePlayAutoInstall);
+      ash::switches::kArcGeneratePlayAutoInstall);
 }
 
 bool IsArcDataCleanupOnStartRequested() {
   return base::CommandLine::ForCurrentProcess()->HasSwitch(
-      chromeos::switches::kArcDataCleanupOnStart);
+      ash::switches::kArcDataCleanupOnStart);
 }
 
 bool IsArcAppSyncFlowDisabled() {
   return base::CommandLine::ForCurrentProcess()->HasSwitch(
-      chromeos::switches::kArcDisableAppSync);
+      ash::switches::kArcDisableAppSync);
 }
 
 bool IsArcLocaleSyncDisabled() {
   return base::CommandLine::ForCurrentProcess()->HasSwitch(
-      chromeos::switches::kArcDisableLocaleSync);
+      ash::switches::kArcDisableLocaleSync);
 }
 
 bool IsArcPlayAutoInstallDisabled() {
   return base::CommandLine::ForCurrentProcess()->HasSwitch(
-      chromeos::switches::kArcDisablePlayAutoInstall);
+      ash::switches::kArcDisablePlayAutoInstall);
 }
 
 int32_t GetLcdDensityForDeviceScaleFactor(float device_scale_factor) {
   const auto* command_line = base::CommandLine::ForCurrentProcess();
-  if (command_line->HasSwitch(chromeos::switches::kArcScale)) {
+  if (command_line->HasSwitch(ash::switches::kArcScale)) {
     const std::string dpi_str =
-        command_line->GetSwitchValueASCII(chromeos::switches::kArcScale);
+        command_line->GetSwitchValueASCII(ash::switches::kArcScale);
     int dpi;
     if (base::StringToInt(dpi_str, &dpi))
       return dpi;
diff --git a/ash/components/arc/enterprise/arc_data_snapshotd_manager.cc b/ash/components/arc/enterprise/arc_data_snapshotd_manager.cc
index 8f40d36..1a8a4cb 100644
--- a/ash/components/arc/enterprise/arc_data_snapshotd_manager.cc
+++ b/ash/components/arc/enterprise/arc_data_snapshotd_manager.cc
@@ -53,8 +53,8 @@
 bool IsRestoredSession() {
   auto* command_line = base::CommandLine::ForCurrentProcess();
 
-  return command_line->HasSwitch(chromeos::switches::kLoginUser) &&
-         !command_line->HasSwitch(chromeos::switches::kLoginManager);
+  return command_line->HasSwitch(ash::switches::kLoginUser) &&
+         !command_line->HasSwitch(ash::switches::kLoginManager);
 }
 
 // Returns true if it is the first Chrome start up after reboot.
diff --git a/ash/components/arc/enterprise/arc_data_snapshotd_manager_unittest.cc b/ash/components/arc/enterprise/arc_data_snapshotd_manager_unittest.cc
index c4f0d9e..bdd8e5a 100644
--- a/ash/components/arc/enterprise/arc_data_snapshotd_manager_unittest.cc
+++ b/ash/components/arc/enterprise/arc_data_snapshotd_manager_unittest.cc
@@ -177,7 +177,7 @@
     local_state_.SetInitializationCompleted();
 
     base::CommandLine::ForCurrentProcess()->AppendSwitch(
-        chromeos::switches::kFirstExecAfterBoot);
+        ash::switches::kFirstExecAfterBoot);
   }
 
   void SetUp() override { SetDBusClientAvailability(true /* is_available */); }
@@ -214,7 +214,7 @@
 
   void SetUpRestoredSessionCommandLine() {
     auto* command_line = base::CommandLine::ForCurrentProcess();
-    command_line->AppendSwitch(chromeos::switches::kLoginUser);
+    command_line->AppendSwitch(ash::switches::kLoginUser);
   }
 
   void SetDBusClientAvailability(bool is_available) {
diff --git a/ash/components/arc/session/arc_session_impl.cc b/ash/components/arc/session/arc_session_impl.cc
index 3128d1e..aa5716d 100644
--- a/ash/components/arc/session/arc_session_impl.cc
+++ b/ash/components/arc/session/arc_session_impl.cc
@@ -159,7 +159,7 @@
 void ApplyDisableDownloadProvider(StartParams* params) {
   params->disable_download_provider =
       base::CommandLine::ForCurrentProcess()->HasSwitch(
-          chromeos::switches::kArcDisableDownloadProvider);
+          ash::switches::kArcDisableDownloadProvider);
 }
 
 void ApplyDisableUreadahed(StartParams* params) {
@@ -472,10 +472,10 @@
   }
 
   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
-          chromeos::switches::kArcPlayStoreAutoUpdate)) {
+          ash::switches::kArcPlayStoreAutoUpdate)) {
     const std::string value =
         base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
-            chromeos::switches::kArcPlayStoreAutoUpdate);
+            ash::switches::kArcPlayStoreAutoUpdate);
     if (value == kOn) {
       params.play_store_auto_update =
           StartParams::PlayStoreAutoUpdate::AUTO_UPDATE_ON;
@@ -486,25 +486,25 @@
       VLOG(1) << "Play Store auto-update is forced off";
     } else {
       LOG(ERROR) << "Invalid parameter " << value << " for "
-                 << chromeos::switches::kArcPlayStoreAutoUpdate;
+                 << ash::switches::kArcPlayStoreAutoUpdate;
     }
   }
 
   params.arc_disable_system_default_app =
       base::CommandLine::ForCurrentProcess()->HasSwitch(
-          chromeos::switches::kArcDisableSystemDefaultApps);
+          ash::switches::kArcDisableSystemDefaultApps);
   if (params.arc_disable_system_default_app)
     VLOG(1) << "System default app(s) are disabled";
 
   params.disable_media_store_maintenance =
       base::CommandLine::ForCurrentProcess()->HasSwitch(
-          chromeos::switches::kArcDisableMediaStoreMaintenance);
+          ash::switches::kArcDisableMediaStoreMaintenance);
   if (params.disable_media_store_maintenance)
     VLOG(1) << "MediaStore maintenance task(s) are disabled";
 
   params.arc_generate_play_auto_install =
       base::CommandLine::ForCurrentProcess()->HasSwitch(
-          chromeos::switches::kArcGeneratePlayAutoInstall);
+          ash::switches::kArcGeneratePlayAutoInstall);
 
   VLOG(1) << "Starting ARC mini instance with lcd_density="
           << params.lcd_density
diff --git a/ash/components/arc/session/arc_session_impl_unittest.cc b/ash/components/arc/session/arc_session_impl_unittest.cc
index bf4b21bd..229260a 100644
--- a/ash/components/arc/session/arc_session_impl_unittest.cc
+++ b/ash/components/arc/session/arc_session_impl_unittest.cc
@@ -697,7 +697,7 @@
 }
 
 struct PackagesCacheModeState {
-  // Possible values for chromeos::switches::kArcPackagesCacheMode
+  // Possible values for ash::switches::kArcPackagesCacheMode
   const char* chrome_switch;
   bool full_container;
   UpgradeParams::PackageCacheMode expected_packages_cache_mode;
@@ -724,7 +724,7 @@
   const PackagesCacheModeState& state = GetParam();
   if (state.chrome_switch) {
     base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
-    command_line->AppendSwitchASCII(chromeos::switches::kArcPackagesCacheMode,
+    command_line->AppendSwitchASCII(ash::switches::kArcPackagesCacheMode,
                                     state.chrome_switch);
   }
 
@@ -750,7 +750,7 @@
 
   if (GetParam()) {
     base::CommandLine::ForCurrentProcess()->AppendSwitch(
-        chromeos::switches::kArcDisableGmsCoreCache);
+        ash::switches::kArcDisableGmsCoreCache);
   }
 
   arc_session->StartMiniInstance();
@@ -893,7 +893,7 @@
 TEST_F(ArcSessionImplTest, DisableUreadahead) {
   base::CommandLine* const command_line =
       base::CommandLine::ForCurrentProcess();
-  command_line->AppendSwitch(chromeos::switches::kArcDisableUreadahead);
+  command_line->AppendSwitch(ash::switches::kArcDisableUreadahead);
   auto arc_session = CreateArcSession();
   arc_session->StartMiniInstance();
   base::RunLoop().RunUntilIdle();
diff --git a/ash/components/arc/session/arc_upgrade_params.cc b/ash/components/arc/session/arc_upgrade_params.cc
index 10989b529..db9b3be 100644
--- a/ash/components/arc/session/arc_upgrade_params.cc
+++ b/ash/components/arc/session/arc_upgrade_params.cc
@@ -17,7 +17,7 @@
   // Set packages cache mode coming from autotests.
   const std::string packages_cache_mode_string =
       base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
-          chromeos::switches::kArcPackagesCacheMode);
+          ash::switches::kArcPackagesCacheMode);
   if (packages_cache_mode_string == kPackagesCacheModeSkipCopy)
     return UpgradeParams::PackageCacheMode::SKIP_SETUP_COPY_ON_INIT;
   if (packages_cache_mode_string == kPackagesCacheModeCopy)
@@ -35,7 +35,7 @@
           !base::FeatureList::IsEnabled(arc::kBootCompletedBroadcastFeature)),
       packages_cache_mode(GetPackagesCacheMode()),
       skip_gms_core_cache(base::CommandLine::ForCurrentProcess()->HasSwitch(
-          chromeos::switches::kArcDisableGmsCoreCache)),
+          ash::switches::kArcDisableGmsCoreCache)),
       enable_arc_nearby_share(
           base::FeatureList::IsEnabled(arc::kEnableArcNearbyShare)) {}
 
diff --git a/ash/components/arc/session/arc_vm_client_adapter.cc b/ash/components/arc/session/arc_vm_client_adapter.cc
index ed702a61..4a0eb6d 100644
--- a/ash/components/arc/session/arc_vm_client_adapter.cc
+++ b/ash/components/arc/session/arc_vm_client_adapter.cc
@@ -136,11 +136,11 @@
     const StartParams& start_params) {
   const auto* command_line = base::CommandLine::ForCurrentProcess();
   const bool is_houdini_available =
-      command_line->HasSwitch(chromeos::switches::kEnableHoudini) ||
-      command_line->HasSwitch(chromeos::switches::kEnableHoudini64);
+      command_line->HasSwitch(ash::switches::kEnableHoudini) ||
+      command_line->HasSwitch(ash::switches::kEnableHoudini64);
   const bool is_ndk_translation_available =
-      command_line->HasSwitch(chromeos::switches::kEnableNdkTranslation) ||
-      command_line->HasSwitch(chromeos::switches::kEnableNdkTranslation64);
+      command_line->HasSwitch(ash::switches::kEnableNdkTranslation) ||
+      command_line->HasSwitch(ash::switches::kEnableNdkTranslation64);
 
   if (!is_houdini_available && !is_ndk_translation_available)
     return ArcBinaryTranslationType::NONE;
diff --git a/ash/components/arc/test/arc_util_test_support.cc b/ash/components/arc/test/arc_util_test_support.cc
index f330c1c..f230f37 100644
--- a/ash/components/arc/test/arc_util_test_support.cc
+++ b/ash/components/arc/test/arc_util_test_support.cc
@@ -24,11 +24,11 @@
 
 void SetArcAlwaysStartWithoutPlayStoreForTesting() {
   base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
-      chromeos::switches::kArcStartMode, kAlwaysStartWithNoPlayStore);
+      ash::switches::kArcStartMode, kAlwaysStartWithNoPlayStore);
 }
 
 void SetArcAvailableCommandLineForTesting(base::CommandLine* command_line) {
-  command_line->AppendSwitchASCII(chromeos::switches::kArcAvailability,
+  command_line->AppendSwitchASCII(ash::switches::kArcAvailability,
                                   kAvailabilityOfficiallySupported);
 }
 
diff --git a/ash/components/tether/host_scan_scheduler_impl.cc b/ash/components/tether/host_scan_scheduler_impl.cc
index 5a18c23..c4d3acae 100644
--- a/ash/components/tether/host_scan_scheduler_impl.cc
+++ b/ash/components/tether/host_scan_scheduler_impl.cc
@@ -83,7 +83,7 @@
 
 void HostScanSchedulerImpl::AttemptScanIfOffline() {
   const chromeos::NetworkTypePattern network_type_pattern =
-      chromeos::switches::ShouldTetherHostScansIgnoreWiredConnections()
+      switches::ShouldTetherHostScansIgnoreWiredConnections()
           ? chromeos::NetworkTypePattern::Wireless()
           : chromeos::NetworkTypePattern::Default();
   const chromeos::NetworkState* first_network =
diff --git a/ash/components/tether/host_scanner_impl.cc b/ash/components/tether/host_scanner_impl.cc
index 45017e31..81fb2a0 100644
--- a/ash/components/tether/host_scanner_impl.cc
+++ b/ash/components/tether/host_scanner_impl.cc
@@ -267,7 +267,7 @@
 
 bool HostScannerImpl::CanAvailableHostNotificationBeShown() {
   const chromeos::NetworkTypePattern network_type_pattern =
-      chromeos::switches::ShouldTetherHostScansIgnoreWiredConnections()
+      switches::ShouldTetherHostScansIgnoreWiredConnections()
           ? chromeos::NetworkTypePattern::Wireless()
           : chromeos::NetworkTypePattern::Default();
   // Note: If a network is active (i.e., connecting or connected), it will be
diff --git a/ash/constants/ash_features.cc b/ash/constants/ash_features.cc
index 07e7dd26..78b362c 100644
--- a/ash/constants/ash_features.cc
+++ b/ash/constants/ash_features.cc
@@ -479,6 +479,11 @@
 const base::Feature kEnableDnsProxy{"EnableDnsProxy",
                                     base::FEATURE_DISABLED_BY_DEFAULT};
 
+// Enables displaying additional OpenVPN configuration values on the network
+// details page.
+const base::Feature kExtendedOpenVpnSettings{"ExtendedOpenVpnSettings",
+                                             base::FEATURE_DISABLED_BY_DEFAULT};
+
 // Enables setting the device hostname.
 const base::Feature kEnableHostnameSetting{"EnableHostnameSetting",
                                            base::FEATURE_DISABLED_BY_DEFAULT};
@@ -1510,6 +1515,10 @@
   return base::FeatureList::IsEnabled(kESimPolicy);
 }
 
+bool IsExtendedOpenVpnSettingsEnabled() {
+  return base::FeatureList::IsEnabled(kExtendedOpenVpnSettings);
+}
+
 bool IsFamilyLinkOnSchoolDeviceEnabled() {
   return base::FeatureList::IsEnabled(kFamilyLinkOnSchoolDevice);
 }
diff --git a/ash/constants/ash_features.h b/ash/constants/ash_features.h
index 9a94b5f3..a84c789 100644
--- a/ash/constants/ash_features.h
+++ b/ash/constants/ash_features.h
@@ -220,6 +220,8 @@
 COMPONENT_EXPORT(ASH_CONSTANTS)
 extern const base::Feature kEnforceAshExtensionKeeplist;
 COMPONENT_EXPORT(ASH_CONSTANTS)
+extern const base::Feature kExtendedOpenVpnSettings;
+COMPONENT_EXPORT(ASH_CONSTANTS)
 extern const base::Feature kEolWarningNotifications;
 COMPONENT_EXPORT(ASH_CONSTANTS) extern const base::Feature kESimPolicy;
 COMPONENT_EXPORT(ASH_CONSTANTS) extern const base::Feature kExoLockNotification;
@@ -550,6 +552,7 @@
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsEcheSWAResizingEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsEcheSWADebugModeEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsESimPolicyEnabled();
+COMPONENT_EXPORT(ASH_CONSTANTS) bool IsExtendedOpenVpnSettingsEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsFamilyLinkOnSchoolDeviceEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsFastPairEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsFileManagerFuseBoxEnabled();
diff --git a/ash/constants/ash_switches.cc b/ash/constants/ash_switches.cc
index b4127d6..37a2470 100644
--- a/ash/constants/ash_switches.cc
+++ b/ash/constants/ash_switches.cc
@@ -12,7 +12,7 @@
 #include "base/strings/string_number_conversions.h"
 #include "base/time/time.h"
 
-namespace chromeos {
+namespace ash {
 namespace switches {
 
 namespace {
@@ -954,4 +954,4 @@
 }
 
 }  // namespace switches
-}  // namespace chromeos
+}  // namespace ash
diff --git a/ash/constants/ash_switches.h b/ash/constants/ash_switches.h
index b3cc218..66cd7690 100644
--- a/ash/constants/ash_switches.h
+++ b/ash/constants/ash_switches.h
@@ -12,8 +12,7 @@
 class TimeDelta;
 }
 
-namespace chromeos {
-namespace switches {
+namespace ash::switches {
 
 // Prefer adding Features over switches. Features go in ash_features.h.
 //
@@ -347,15 +346,27 @@
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsUsingShelfAutoDim();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool ShouldClearFastInkBuffer();
 
-}  // namespace switches
-}  // namespace chromeos
+}  // namespace ash::switches
 
 // TODO(https://crbug.com/1164001): remove after //chrome/browser/chromeos
 // source migration is finished.
-namespace ash {
-namespace switches {
-using namespace ::chromeos::switches;
-}
-}  // namespace ash
+namespace chromeos::switches {
+using ::ash::switches::IsOsInstallAllowed;
+using ::ash::switches::IsRevenBranding;
+using ::ash::switches::kAppOemManifestFile;
+using ::ash::switches::kArcTosHostForTests;
+using ::ash::switches::kDisableGaiaServices;
+using ::ash::switches::kEnableOobeTestAPI;
+using ::ash::switches::kEnableTouchCalibrationSetting;
+using ::ash::switches::kForceSystemCompositorMode;
+using ::ash::switches::kHasChromeOSKeyboard;
+using ::ash::switches::kLoginManager;
+using ::ash::switches::kOobeEulaUrlForTests;
+using ::ash::switches::kOobeScreenshotDirectory;
+using ::ash::switches::kOobeSkipPostLogin;
+using ::ash::switches::kPublicAccountsSamlAclUrl;
+using ::ash::switches::kSamlPasswordChangeUrl;
+using ::ash::switches::kShowOobeDevOverlay;
+}  // namespace chromeos::switches
 
 #endif  // ASH_CONSTANTS_ASH_SWITCHES_H_
diff --git a/ash/display/display_configuration_observer.cc b/ash/display/display_configuration_observer.cc
index 1aec7ae..3dbb700 100644
--- a/ash/display/display_configuration_observer.cc
+++ b/ash/display/display_configuration_observer.cc
@@ -30,7 +30,7 @@
   Shell::Get()->tablet_mode_controller()->AddObserver(this);
   // Update the display pref with the initial power state.
   base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
-  if (command_line->HasSwitch(chromeos::switches::kFirstExecAfterBoot))
+  if (command_line->HasSwitch(switches::kFirstExecAfterBoot))
     Shell::Get()->display_prefs()->MaybeStoreDisplayPrefs();
 }
 
diff --git a/ash/display/display_prefs.cc b/ash/display/display_prefs.cc
index 8cda92a..b7f93f37 100644
--- a/ash/display/display_prefs.cc
+++ b/ash/display/display_prefs.cc
@@ -858,7 +858,7 @@
   // powerd fails to send us one over D-Bus. Otherwise, we won't restore
   // displays correctly after retaking control when changing virtual terminals.
   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
-          chromeos::switches::kFirstExecAfterBoot)) {
+          switches::kFirstExecAfterBoot)) {
     Shell::Get()->display_configurator()->InitializeDisplayPowerState();
     return;
   }
diff --git a/ash/display/display_prefs_unittest.cc b/ash/display/display_prefs_unittest.cc
index 17b2c506..4b56a995 100644
--- a/ash/display/display_prefs_unittest.cc
+++ b/ash/display/display_prefs_unittest.cc
@@ -329,7 +329,7 @@
       chromeos::DISPLAY_POWER_INTERNAL_OFF_EXTERNAL_ON);
 
   base::CommandLine::ForCurrentProcess()->AppendSwitch(
-      chromeos::switches::kFirstExecAfterBoot);
+      switches::kFirstExecAfterBoot);
   LoadDisplayPreferences();
 
   // requested_power_state_ should be chromeos::DISPLAY_POWER_ALL_ON at boot
@@ -1392,7 +1392,7 @@
   LoggedInAsUser();
 
   base::CommandLine::ForCurrentProcess()->AppendSwitch(
-      chromeos::switches::kFirstExecAfterBoot);
+      switches::kFirstExecAfterBoot);
 
   const int64_t internal_display_id =
       display::test::DisplayManagerTestApi(display_manager())
@@ -1490,7 +1490,7 @@
   LoggedInAsUser();
 
   base::CommandLine::ForCurrentProcess()->AppendSwitch(
-      chromeos::switches::kFirstExecAfterBoot);
+      switches::kFirstExecAfterBoot);
 
   const int64_t internal_display_id =
       display::test::DisplayManagerTestApi(display_manager())
diff --git a/ash/drag_drop/drag_drop_controller.cc b/ash/drag_drop/drag_drop_controller.cc
index d4f5f06..8b8772d 100644
--- a/ash/drag_drop/drag_drop_controller.cc
+++ b/ash/drag_drop/drag_drop_controller.cc
@@ -87,6 +87,7 @@
                    aura::client::DragUpdateInfo& drag_info,
                    base::OnceClosure drop_cb) {
   DCHECK(drag_data);
+
   if (ui::DataTransferPolicyController::HasInstance()) {
     ui::DataTransferPolicyController::Get()->DropIfAllowed(
         drag_data->GetSource(), &drag_info.data_endpoint, std::move(drop_cb));
@@ -355,16 +356,13 @@
     event->StopPropagation();
     return;
   }
-  // If the event ET_MOUSE_RELEASED is received we must allow the event
-  // to propagate so that the target window eventually releases capture.
-  bool stop_propagation = true;
+
   auto translated_event = ConvertEvent(translated_target, *event);
   switch (translated_event->type()) {
     case ui::ET_MOUSE_DRAGGED:
       DragUpdate(translated_target, *translated_event.get());
       break;
     case ui::ET_MOUSE_RELEASED:
-      stop_propagation = false;
       Drop(translated_target, *translated_event.get());
       break;
     default:
@@ -381,8 +379,7 @@
     toplevel_window_drag_delegate_->OnToplevelWindowDragEvent(
         translated_event.get());
 
-  if (stop_propagation)
-    event->StopPropagation();
+  event->StopPropagation();
 }
 
 void DragDropController::OnTouchEvent(ui::TouchEvent* event) {
diff --git a/ash/drag_drop/drag_drop_controller_unittest.cc b/ash/drag_drop/drag_drop_controller_unittest.cc
index fa50c8a..d475b8f 100644
--- a/ash/drag_drop/drag_drop_controller_unittest.cc
+++ b/ash/drag_drop/drag_drop_controller_unittest.cc
@@ -1755,42 +1755,4 @@
             delegate.state());
 }
 
-TEST_F(DragDropControllerTest,
-       ToplevelWindowDragDelegateForwardsMouseReleased) {
-  std::unique_ptr<aura::Window> window(CreateTestWindowInShellWithDelegate(
-      aura::test::TestWindowDelegate::CreateSelfDestroyingDelegate(), -1,
-      gfx::Rect(0, 0, 100, 100)));
-
-  // Emulate a drag session completion to verify that the ET_MOUSE_RELEASED
-  // event can release capture.
-  {
-    TestToplevelWindowDragDelegate delegate;
-    drag_drop_controller_->set_toplevel_window_drag_delegate(&delegate);
-
-    ui::test::EventGenerator generator(window->GetRootWindow(), window.get());
-    generator.PressLeftButton();
-
-    auto data(std::make_unique<ui::OSExchangeData>());
-    drag_drop_controller_->StartDragAndDrop(
-        std::move(data), window->GetRootWindow(), window.get(),
-        gfx::Point(5, 5), ui::DragDropTypes::DRAG_MOVE,
-        ui::mojom::DragEventSource::kMouse);
-
-    // Send a fake mouse up event and assert that it can continue
-    // propagation.
-    auto event = std::make_unique<ui::MouseEvent>(
-        ui::ET_MOUSE_RELEASED, gfx::Point(5, 5), gfx::Point(5, 5),
-        ui::EventTimeForNow(), 0, 0);
-    ui::Event::DispatcherApi(event.get()).set_phase(ui::EP_PRETARGET);
-    ui::Event::DispatcherApi(event.get())
-        .set_target(window->GetToplevelWindow());
-
-    drag_drop_controller_->OnMouseEvent(event.get());
-
-    EXPECT_EQ(TestToplevelWindowDragDelegate::State::kDragDroppedInvoked,
-              delegate.state());
-    EXPECT_FALSE(event.get()->stopped_propagation());
-  }
-}
-
 }  // namespace ash
diff --git a/ash/login/ui/lock_screen.cc b/ash/login/ui/lock_screen.cc
index 89dc430..51339bcd 100644
--- a/ash/login/ui/lock_screen.cc
+++ b/ash/login/ui/lock_screen.cc
@@ -86,7 +86,7 @@
   auto initial_note_action_state =
       Shell::Get()->tray_action()->GetLockScreenNoteState();
   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
-          chromeos::switches::kShowLoginDevOverlay)) {
+          switches::kShowLoginDevOverlay)) {
     auto debug_view =
         std::make_unique<LockDebugView>(initial_note_action_state, type_);
     contents_view_ = debug_view->lock();
diff --git a/ash/quick_pair/feature_status_tracker/BUILD.gn b/ash/quick_pair/feature_status_tracker/BUILD.gn
index 07ec85d0..9e99884e 100644
--- a/ash/quick_pair/feature_status_tracker/BUILD.gn
+++ b/ash/quick_pair/feature_status_tracker/BUILD.gn
@@ -46,6 +46,7 @@
   testonly = true
 
   sources = [
+    "fake_bluetooth_adapter.cc",
     "fake_bluetooth_adapter.h",
     "fake_feature_status_tracker.cc",
     "fake_feature_status_tracker.h",
diff --git a/ash/quick_pair/feature_status_tracker/bluetooth_enabled_provider.cc b/ash/quick_pair/feature_status_tracker/bluetooth_enabled_provider.cc
index 5c67ba4..2186cbb 100644
--- a/ash/quick_pair/feature_status_tracker/bluetooth_enabled_provider.cc
+++ b/ash/quick_pair/feature_status_tracker/bluetooth_enabled_provider.cc
@@ -23,16 +23,33 @@
 void BluetoothEnabledProvider::AdapterPoweredChanged(
     device::BluetoothAdapter* adapter,
     bool powered) {
+  if (!HasHardwareSupport()) {
+    SetEnabledAndInvokeCallback(/*is_enabled=*/false);
+    return;
+  }
+
   SetEnabledAndInvokeCallback(powered);
 }
 
 void BluetoothEnabledProvider::OnAdapterReceived(
     scoped_refptr<device::BluetoothAdapter> adapter) {
   adapter_ = adapter;
-  adapter_observation_.Observe(adapter_.get());
 
+  if (!HasHardwareSupport()) {
+    SetEnabledAndInvokeCallback(/*is_enabled=*/false);
+    return;
+  }
+
+  adapter_observation_.Observe(adapter_.get());
   SetEnabledAndInvokeCallback(adapter_->IsPowered());
 }
 
+bool BluetoothEnabledProvider::HasHardwareSupport() {
+  return adapter_.get() && adapter_->IsPresent() &&
+         adapter_->GetLowEnergyScanSessionHardwareOffloadingStatus() ==
+             device::BluetoothAdapter::
+                 LowEnergyScanSessionHardwareOffloadingStatus::kSupported;
+}
+
 }  // namespace quick_pair
 }  // namespace ash
diff --git a/ash/quick_pair/feature_status_tracker/bluetooth_enabled_provider.h b/ash/quick_pair/feature_status_tracker/bluetooth_enabled_provider.h
index 57f32b8..f5fa5f85 100644
--- a/ash/quick_pair/feature_status_tracker/bluetooth_enabled_provider.h
+++ b/ash/quick_pair/feature_status_tracker/bluetooth_enabled_provider.h
@@ -29,6 +29,8 @@
  private:
   void OnAdapterReceived(scoped_refptr<device::BluetoothAdapter> adapter);
 
+  bool HasHardwareSupport();
+
   base::ScopedObservation<device::BluetoothAdapter,
                           device::BluetoothAdapter::Observer>
       adapter_observation_{this};
diff --git a/ash/quick_pair/feature_status_tracker/bluetooth_enabled_provider_unittest.cc b/ash/quick_pair/feature_status_tracker/bluetooth_enabled_provider_unittest.cc
index 6213ea9..cc3ad5d 100644
--- a/ash/quick_pair/feature_status_tracker/bluetooth_enabled_provider_unittest.cc
+++ b/ash/quick_pair/feature_status_tracker/bluetooth_enabled_provider_unittest.cc
@@ -72,5 +72,15 @@
   EXPECT_TRUE(provider_->is_enabled());
 }
 
+TEST_F(BluetoothEnabledProviderTest, NoHardwareSupport) {
+  adapter().SetHardwareOffloadingStatus(
+      device::BluetoothAdapter::LowEnergyScanSessionHardwareOffloadingStatus::
+          kNotSupported);
+  EXPECT_FALSE(provider_->is_enabled());
+
+  adapter().NotifyPoweredChanged(true);
+  EXPECT_FALSE(provider_->is_enabled());
+}
+
 }  // namespace quick_pair
 }  // namespace ash
diff --git a/ash/quick_pair/feature_status_tracker/fake_bluetooth_adapter.cc b/ash/quick_pair/feature_status_tracker/fake_bluetooth_adapter.cc
new file mode 100644
index 0000000..798ba06
--- /dev/null
+++ b/ash/quick_pair/feature_status_tracker/fake_bluetooth_adapter.cc
@@ -0,0 +1,34 @@
+// 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 "ash/quick_pair/feature_status_tracker/fake_bluetooth_adapter.h"
+
+namespace ash {
+namespace quick_pair {
+
+void FakeBluetoothAdapter::NotifyPoweredChanged(bool powered) {
+  device::BluetoothAdapter::NotifyAdapterPoweredChanged(powered);
+}
+
+bool FakeBluetoothAdapter::IsPresent() const {
+  return is_bluetooth_present_;
+}
+
+void FakeBluetoothAdapter::SetBluetoothIsPresent(bool present) {
+  is_bluetooth_present_ = present;
+}
+
+device::BluetoothAdapter::LowEnergyScanSessionHardwareOffloadingStatus
+FakeBluetoothAdapter::GetLowEnergyScanSessionHardwareOffloadingStatus() {
+  return hardware_offloading_status_;
+}
+
+void FakeBluetoothAdapter::SetHardwareOffloadingStatus(
+    device::BluetoothAdapter::LowEnergyScanSessionHardwareOffloadingStatus
+        hardware_offloading_status) {
+  hardware_offloading_status_ = hardware_offloading_status;
+}
+
+}  // namespace quick_pair
+}  // namespace ash
diff --git a/ash/quick_pair/feature_status_tracker/fake_bluetooth_adapter.h b/ash/quick_pair/feature_status_tracker/fake_bluetooth_adapter.h
index b71527d..6ef2f5ac 100644
--- a/ash/quick_pair/feature_status_tracker/fake_bluetooth_adapter.h
+++ b/ash/quick_pair/feature_status_tracker/fake_bluetooth_adapter.h
@@ -14,12 +14,26 @@
 class FakeBluetoothAdapter
     : public testing::NiceMock<device::MockBluetoothAdapter> {
  public:
-  void NotifyPoweredChanged(bool powered) {
-    device::BluetoothAdapter::NotifyAdapterPoweredChanged(powered);
-  }
+  void NotifyPoweredChanged(bool powered);
+
+  bool IsPresent() const override;
+
+  void SetBluetoothIsPresent(bool present);
+
+  device::BluetoothAdapter::LowEnergyScanSessionHardwareOffloadingStatus
+  GetLowEnergyScanSessionHardwareOffloadingStatus() override;
+
+  void SetHardwareOffloadingStatus(
+      device::BluetoothAdapter::LowEnergyScanSessionHardwareOffloadingStatus
+          hardware_offloading_status);
 
  private:
   ~FakeBluetoothAdapter() = default;
+
+  bool is_bluetooth_present_ = true;
+  device::BluetoothAdapter::LowEnergyScanSessionHardwareOffloadingStatus
+      hardware_offloading_status_ = device::BluetoothAdapter::
+          LowEnergyScanSessionHardwareOffloadingStatus::kSupported;
 };
 
 }  // namespace quick_pair
diff --git a/ash/root_window_controller.cc b/ash/root_window_controller.cc
index e330d34..a9ab051 100644
--- a/ash/root_window_controller.cc
+++ b/ash/root_window_controller.cc
@@ -1260,7 +1260,7 @@
   const bool is_boot_splash_screen =
       root_window_type == RootWindowType::PRIMARY &&
       base::CommandLine::ForCurrentProcess()->HasSwitch(
-          chromeos::switches::kFirstExecAfterBoot);
+          switches::kFirstExecAfterBoot);
   if (is_boot_splash_screen)
     color = kChromeOsBootColor;
   system_wallpaper_ =
diff --git a/ash/shelf/shelf_tooltip_manager.cc b/ash/shelf/shelf_tooltip_manager.cc
index 4366f54..1716ee1 100644
--- a/ash/shelf/shelf_tooltip_manager.cc
+++ b/ash/shelf/shelf_tooltip_manager.cc
@@ -75,8 +75,7 @@
   const ShelfAlignment alignment = shelf_->alignment();
   const SkColor shelf_background_color =
       shelf_->shelf_widget()->GetShelfBackgroundColor();
-  if (chromeos::switches::ShouldShowShelfHoverPreviews() &&
-      open_windows.size() > 0) {
+  if (switches::ShouldShowShelfHoverPreviews() && open_windows.size() > 0) {
     bubble_ = new ShelfTooltipPreviewBubble(view, open_windows, this, alignment,
                                             shelf_background_color);
   } else {
diff --git a/ash/system/bluetooth/tray_bluetooth_helper_legacy_unittest.cc b/ash/system/bluetooth/tray_bluetooth_helper_legacy_unittest.cc
index e9c3fa5..43572e8 100644
--- a/ash/system/bluetooth/tray_bluetooth_helper_legacy_unittest.cc
+++ b/ash/system/bluetooth/tray_bluetooth_helper_legacy_unittest.cc
@@ -262,7 +262,7 @@
 // enabled.
 TEST_F(TrayBluetoothHelperLegacyTest, UnfilteredBluetoothDevices) {
   base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
-  cmd_line->AppendSwitch(chromeos::switches::kUnfilteredBluetoothDevices);
+  cmd_line->AppendSwitch(switches::kUnfilteredBluetoothDevices);
 
   // Set Bluetooth discovery simulation delay to 0 so the test doesn't have to
   // wait or use timers.
diff --git a/ash/system/phonehub/notification_opt_in_view.cc b/ash/system/phonehub/notification_opt_in_view.cc
index f38ebfa..5fc4b5b4d 100644
--- a/ash/system/phonehub/notification_opt_in_view.cc
+++ b/ash/system/phonehub/notification_opt_in_view.cc
@@ -49,8 +49,9 @@
 void NotificationOptInView::SetUpButtonPressed() {
   // Opens the notification set up dialog in settings to start the opt in flow.
   LogNotificationOptInEvent(InterstitialScreenEvent::kConfirm);
-  NewWindowDelegate::GetPrimary()->OpenUrl(GURL(kMultideviceSettingsUrl),
-                                           /*from_user_interaction=*/true);
+  // This intentionally uses GetInstance() to open an OS Settings page in ash.
+  NewWindowDelegate::GetInstance()->OpenUrl(GURL(kMultideviceSettingsUrl),
+                                            /*from_user_interaction=*/true);
 }
 
 void NotificationOptInView::DismissButtonPressed() {
diff --git a/ash/touch/touch_devices_controller.cc b/ash/touch/touch_devices_controller.cc
index e683d300..58c94b8 100644
--- a/ash/touch/touch_devices_controller.cc
+++ b/ash/touch/touch_devices_controller.cc
@@ -44,7 +44,7 @@
     registry->RegisterBooleanPref(
         prefs::kNaturalScroll,
         base::CommandLine::ForCurrentProcess()->HasSwitch(
-            chromeos::switches::kNaturalScrollDefault),
+            switches::kNaturalScrollDefault),
         user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PRIORITY_PREF);
   }
 }
diff --git a/ash/wallpaper/wallpaper_controller_impl.cc b/ash/wallpaper/wallpaper_controller_impl.cc
index 740e4ea..cffa6ba0 100644
--- a/ash/wallpaper/wallpaper_controller_impl.cc
+++ b/ash/wallpaper/wallpaper_controller_impl.cc
@@ -814,13 +814,13 @@
   // The slower initial animation is only applicable if:
   // 1) It's the first run after system boot, not after user sign-out.
   if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
-          chromeos::switches::kFirstExecAfterBoot)) {
+          switches::kFirstExecAfterBoot)) {
     return false;
   }
   // 2) It's at the login screen.
   if (Shell::Get()->session_controller()->IsActiveUserSessionStarted() ||
       !base::CommandLine::ForCurrentProcess()->HasSwitch(
-          chromeos::switches::kLoginManager)) {
+          switches::kLoginManager)) {
     return false;
   }
   // 3) It's the first wallpaper being shown, not for the switching between
@@ -1950,23 +1950,23 @@
   // Guest wallpaper, child wallpaper, customized default wallpaper, and regular
   // default wallpaper.
   if (user_type == user_manager::USER_TYPE_GUEST) {
-    const std::string switch_string =
-        use_small ? chromeos::switches::kGuestWallpaperSmall
-                  : chromeos::switches::kGuestWallpaperLarge;
+    const std::string switch_string = use_small
+                                          ? switches::kGuestWallpaperSmall
+                                          : switches::kGuestWallpaperLarge;
     file_path = command_line->GetSwitchValuePath(switch_string);
   } else if (user_type == user_manager::USER_TYPE_CHILD) {
-    const std::string switch_string =
-        use_small ? chromeos::switches::kChildWallpaperSmall
-                  : chromeos::switches::kChildWallpaperLarge;
+    const std::string switch_string = use_small
+                                          ? switches::kChildWallpaperSmall
+                                          : switches::kChildWallpaperLarge;
     file_path = command_line->GetSwitchValuePath(switch_string);
   } else if (!customized_default_small_path_.empty()) {
     DCHECK(!customized_default_large_path_.empty());
     file_path = use_small ? customized_default_small_path_
                           : customized_default_large_path_;
   } else {
-    const std::string switch_string =
-        use_small ? chromeos::switches::kDefaultWallpaperSmall
-                  : chromeos::switches::kDefaultWallpaperLarge;
+    const std::string switch_string = use_small
+                                          ? switches::kDefaultWallpaperSmall
+                                          : switches::kDefaultWallpaperLarge;
     file_path = command_line->GetSwitchValuePath(switch_string);
   }
 
diff --git a/ash/wallpaper/wallpaper_controller_unittest.cc b/ash/wallpaper/wallpaper_controller_unittest.cc
index bb901a2..f0bdd2c 100644
--- a/ash/wallpaper/wallpaper_controller_unittest.cc
+++ b/ash/wallpaper/wallpaper_controller_unittest.cc
@@ -569,29 +569,29 @@
     base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
     const base::FilePath small_file =
         default_wallpaper_path.Append(kDefaultSmallWallpaperName);
-    command_line->AppendSwitchASCII(chromeos::switches::kDefaultWallpaperSmall,
+    command_line->AppendSwitchASCII(switches::kDefaultWallpaperSmall,
                                     small_file.value());
     const base::FilePath large_file =
         default_wallpaper_path.Append(kDefaultLargeWallpaperName);
-    command_line->AppendSwitchASCII(chromeos::switches::kDefaultWallpaperLarge,
+    command_line->AppendSwitchASCII(switches::kDefaultWallpaperLarge,
                                     large_file.value());
 
     const base::FilePath guest_small_file =
         default_wallpaper_path.Append(kGuestSmallWallpaperName);
-    command_line->AppendSwitchASCII(chromeos::switches::kGuestWallpaperSmall,
+    command_line->AppendSwitchASCII(switches::kGuestWallpaperSmall,
                                     guest_small_file.value());
     const base::FilePath guest_large_file =
         default_wallpaper_path.Append(kGuestLargeWallpaperName);
-    command_line->AppendSwitchASCII(chromeos::switches::kGuestWallpaperLarge,
+    command_line->AppendSwitchASCII(switches::kGuestWallpaperLarge,
                                     guest_large_file.value());
 
     const base::FilePath child_small_file =
         default_wallpaper_path.Append(kChildSmallWallpaperName);
-    command_line->AppendSwitchASCII(chromeos::switches::kChildWallpaperSmall,
+    command_line->AppendSwitchASCII(switches::kChildWallpaperSmall,
                                     child_small_file.value());
     const base::FilePath child_large_file =
         default_wallpaper_path.Append(kChildLargeWallpaperName);
-    command_line->AppendSwitchASCII(chromeos::switches::kChildWallpaperLarge,
+    command_line->AppendSwitchASCII(switches::kChildWallpaperLarge,
                                     child_large_file.value());
 
     const int kWallpaperSize = 2;
@@ -2029,7 +2029,7 @@
   // Show an always-on-top wallpaper.
   const base::FilePath image_path =
       base::CommandLine::ForCurrentProcess()->GetSwitchValuePath(
-          chromeos::switches::kGuestWallpaperLarge);
+          switches::kGuestWallpaperLarge);
   CreateDefaultWallpapers();
   SetBypassDecode();
   ClearWallpaperCount();
@@ -2353,9 +2353,8 @@
 
   // Simulate the login screen after system boot.
   base::CommandLine::ForCurrentProcess()->AppendSwitch(
-      chromeos::switches::kFirstExecAfterBoot);
-  base::CommandLine::ForCurrentProcess()->AppendSwitch(
-      chromeos::switches::kLoginManager);
+      switches::kFirstExecAfterBoot);
+  base::CommandLine::ForCurrentProcess()->AppendSwitch(switches::kLoginManager);
   ClearLogin();
 
   // Show the first wallpaper. Verify that the slower animation should be used.
@@ -2389,8 +2388,7 @@
 
   // Simulate the login screen after user sign-out. Verify that the slower
   // animation should never be used.
-  base::CommandLine::ForCurrentProcess()->AppendSwitch(
-      chromeos::switches::kLoginManager);
+  base::CommandLine::ForCurrentProcess()->AppendSwitch(switches::kLoginManager);
   CreateAndSaveWallpapers(account_id_1);
   ClearLogin();
 
@@ -3103,7 +3101,7 @@
   // Show an always-on-top wallpaper.
   const base::FilePath image_path =
       base::CommandLine::ForCurrentProcess()->GetSwitchValuePath(
-          chromeos::switches::kGuestWallpaperLarge);
+          switches::kGuestWallpaperLarge);
   controller_->ShowAlwaysOnTopWallpaper(image_path);
   RunAllTasksUntilIdle();
   EXPECT_EQ(2, GetWallpaperCount());
diff --git a/ash/webui/diagnostics_ui/backend/input_data_provider_keyboard.cc b/ash/webui/diagnostics_ui/backend/input_data_provider_keyboard.cc
index aab7065..407791a 100644
--- a/ash/webui/diagnostics_ui/backend/input_data_provider_keyboard.cc
+++ b/ash/webui/diagnostics_ui/backend/input_data_provider_keyboard.cc
@@ -391,13 +391,13 @@
 
     result->number_pad_present =
         base::CommandLine::ForCurrentProcess()->HasSwitch(
-            chromeos::switches::kHasNumberPad)
+            switches::kHasNumberPad)
             ? mojom::NumberPadPresence::kPresent
             : mojom::NumberPadPresence::kNotPresent;
 
     // Log if there is contradictory information.
     if (base::CommandLine::ForCurrentProcess()->HasSwitch(
-            chromeos::switches::kHasNumberPad) &&
+            switches::kHasNumberPad) &&
         !device_info->event_device_info.HasNumberpad())
       LOG(ERROR) << "OS believes internal numberpad is implemented, but "
                     "evdev disagrees.";
diff --git a/ash/webui/os_feedback_ui/resources/BUILD.gn b/ash/webui/os_feedback_ui/resources/BUILD.gn
index ad44ff7..16587b5 100644
--- a/ash/webui/os_feedback_ui/resources/BUILD.gn
+++ b/ash/webui/os_feedback_ui/resources/BUILD.gn
@@ -16,8 +16,9 @@
 
 polymer_element_files = [
   "confirmation_page.js",
-  "os_feedback_shared_css.js",
   "help_resources_icons.js",
+  "os_feedback_shared_css.js",
+  "search_page.js",
 ]
 
 generate_grd("build_grd") {
@@ -37,7 +38,16 @@
 js_type_check("closure_compile_module") {
   is_polymer3 = true
   closure_flags = default_closure_args
-  deps = [ ":confirmation_page" ]
+  deps = [
+    ":confirmation_page",
+    ":search_page",
+  ]
+}
+
+js_library("search_page") {
+  deps = [
+    "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
+  ]
 }
 
 js_library("confirmation_page") {
diff --git a/ash/webui/os_feedback_ui/resources/index.html b/ash/webui/os_feedback_ui/resources/index.html
index 4907103..0b8ee8d5 100644
--- a/ash/webui/os_feedback_ui/resources/index.html
+++ b/ash/webui/os_feedback_ui/resources/index.html
@@ -19,8 +19,9 @@
 
 <body>
   <!-- TODO(xiangdongkong): add other steps and manage states-->
-  <confirmation-page></confirmation-page>
+  <search-page></search-page>
 
+  <script type="module" src="search_page.js"></script>
   <script type="module" src="confirmation_page.js"></script>
   <link rel="stylesheet" src="os_feedback_shared_css.js">
 </body>
diff --git a/ash/webui/os_feedback_ui/resources/os_feedback_shared_css.html b/ash/webui/os_feedback_ui/resources/os_feedback_shared_css.html
index c90d36a..3c2175c 100644
--- a/ash/webui/os_feedback_ui/resources/os_feedback_shared_css.html
+++ b/ash/webui/os_feedback_ui/resources/os_feedback_shared_css.html
@@ -20,9 +20,14 @@
       margin: 0 0 24px 0;
     }
 
+    #content {
+      display: flex;
+      height: 456px;
+    }
+
     #title {
-      margin-block-end: 0;
       margin: auto 0 0 0;
+      margin-block-end: 0;
     }
 
     #navButtons {
diff --git a/ash/webui/os_feedback_ui/resources/search_page.html b/ash/webui/os_feedback_ui/resources/search_page.html
new file mode 100644
index 0000000..2c062a9
--- /dev/null
+++ b/ash/webui/os_feedback_ui/resources/search_page.html
@@ -0,0 +1,26 @@
+<style include="os-feedback-shared">
+textarea {
+  height: 168px;
+  width: 520px;
+}
+</style>
+<!--TODO(xiangdongkong): use localized strings -->
+<div id="container">
+  <div id="header">
+    <h1 id="title">Send feedback</h1>
+  </div>
+  <div id="content">
+    <div id="descriptionPane">
+      <p id="descriptionTitle">Description</p>
+      <textarea id="descriptionText" aria-labelledby="descriptionTitle"
+          aria-required="true">
+      </textarea>
+      <p id="descriptionEmptyError" aria-hidden="true" hidden>
+        Description is required
+      </p>
+    </div>
+  </div>
+  <div id="navButtons">
+    <cr-button id="btnContinue" class="action-button">Continue</cr-button>
+  </div>
+</div>
diff --git a/ash/webui/os_feedback_ui/resources/search_page.js b/ash/webui/os_feedback_ui/resources/search_page.js
new file mode 100644
index 0000000..a647942f
--- /dev/null
+++ b/ash/webui/os_feedback_ui/resources/search_page.js
@@ -0,0 +1,26 @@
+// 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 './help_resources_icons.js';
+import './os_feedback_shared_css.js';
+import 'chrome://resources/cr_elements/cr_button/cr_button.m.js';
+
+import {html, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
+/**
+ * @fileoverview
+ * 'search-page' is the first step of the feedback tool. It displays live help
+ *  contents relevant to the text entered by the user.
+ */
+export class SearchPageElement extends PolymerElement {
+  static get is() {
+    return 'search-page';
+  }
+
+  static get template() {
+    return html`{__html_template__}`;
+  }
+}
+
+customElements.define(SearchPageElement.is, SearchPageElement);
diff --git a/ash/webui/personalization_app/personalization_app_ui.cc b/ash/webui/personalization_app/personalization_app_ui.cc
index 7535e718..49e149a 100644
--- a/ash/webui/personalization_app/personalization_app_ui.cc
+++ b/ash/webui/personalization_app/personalization_app_ui.cc
@@ -84,7 +84,11 @@
       {"setAsWallpaper", IDS_PERSONALIZATION_APP_SET_AS_WALLPAPER},
       {"themeLabel", IDS_PERSONALIZATION_APP_THEME_LABEL},
       {"darkColorMode", IDS_PERSONALIZATION_APP_THEME_DARK_COLOR_MODE},
-      {"lightColorMode", IDS_PERSONALIZATION_APP_THEME_LIGHT_COLOR_MODE}};
+      {"lightColorMode", IDS_PERSONALIZATION_APP_THEME_LIGHT_COLOR_MODE},
+      {"zeroImages", IDS_PERSONALIZATION_APP_NO_IMAGES},
+      {"oneImage", IDS_PERSONALIZATION_APP_ONE_IMAGE},
+      {"multipleImages", IDS_PERSONALIZATION_APP_MULTIPLE_IMAGES},
+  };
   source->AddLocalizedStrings(kLocalizedStrings);
 
   if (features::IsWallpaperGooglePhotosIntegrationEnabled()) {
diff --git a/ash/webui/personalization_app/resources/common/utils.ts b/ash/webui/personalization_app/resources/common/utils.ts
index b431268d..dff8596 100644
--- a/ash/webui/personalization_app/resources/common/utils.ts
+++ b/ash/webui/personalization_app/resources/common/utils.ts
@@ -7,6 +7,8 @@
  * code.
  */
 
+import {loadTimeData} from '//resources/js/load_time_data.m.js';
+
 /**
  * Checks if argument is an array with non-zero length.
  */
@@ -65,18 +67,33 @@
 
 type WallpaperSelectionEvent =
     MouseEvent&{type: 'click'}|KeyboardEvent&{key: 'Enter'};
-/**
- * Returns true if this event is a user action to select an item.
- */
+/** Returns true if this event is a user action to select an item. */
 export function isSelectionEvent(event: Event):
     event is WallpaperSelectionEvent {
   return (event instanceof MouseEvent && event.type === 'click') ||
       (event instanceof KeyboardEvent && event.key === 'Enter');
 }
 
-/**
- * Sets a css variable to control the animation delay.
- */
+/** Returns the text to display for a number of images. */
+export function getCountText(x: number|null|undefined): string {
+  switch (x) {
+    case null:
+    case undefined:
+      return '';
+    case 0:
+      return loadTimeData.getString('zeroImages');
+    case 1:
+      return loadTimeData.getString('oneImage');
+    default:
+      if ('number' !== typeof x || x < 0) {
+        console.error('Received an impossible value');
+        return '';
+      }
+      return loadTimeData.getStringF('multipleImages', x);
+  }
+}
+
+/** Returns a css variable to control the animation delay. */
 export function getLoadingPlaceholderAnimationDelay(index: number): string {
   return `--animation-delay: ${index * 83}ms;`;
 }
diff --git a/ash/webui/personalization_app/resources/trusted/personalization_breadcrumb_element.ts b/ash/webui/personalization_app/resources/trusted/personalization_breadcrumb_element.ts
index b34a2a2..9b694cf7 100644
--- a/ash/webui/personalization_app/resources/trusted/personalization_breadcrumb_element.ts
+++ b/ash/webui/personalization_app/resources/trusted/personalization_breadcrumb_element.ts
@@ -19,7 +19,7 @@
 
 import {isNonEmptyArray} from '../common/utils.js';
 
-import {WallpaperCollection} from './personalization_app.mojom-webui.js';
+import {GooglePhotosAlbum, WallpaperCollection} from './personalization_app.mojom-webui.js';
 import {Paths} from './personalization_router_element.js';
 import {WithPersonalizationStore} from './personalization_store.js';
 import {isNonEmptyString} from './utils.js';
@@ -49,12 +49,8 @@
         type: String,
       },
 
-      /**
-       * The current Google Photos album id to display.
-       */
-      googlePhotosAlbumId: {
-        type: String,
-      },
+      /** The current Google Photos album id to display. */
+      googlePhotosAlbumId: String,
 
       /**
        * The current path of the page.
@@ -73,12 +69,8 @@
         type: Array,
       },
 
-      /**
-       * The list of Google Photos albums.
-       */
-      googlePhotosAlbums_: {
-        type: Array,
-      },
+      /** The list of Google Photos albums. */
+      googlePhotosAlbums_: Array,
 
       showBackButton_: {
         type: Boolean,
@@ -92,7 +84,7 @@
   path: string;
   private breadcrumbs_: string[];
   private collections_: WallpaperCollection[]|null;
-  private googlePhotosAlbums_: WallpaperCollection[]|null;
+  private googlePhotosAlbums_: GooglePhotosAlbum[]|null;
   private showBackButton_: boolean;
 
   connectedCallback() {
@@ -105,7 +97,7 @@
 
   private computeBreadcrumbs_(
       path: string, collections: WallpaperCollection[]|null,
-      collectionId: string, googlePhotosAlbums: WallpaperCollection[]|null,
+      collectionId: string, googlePhotosAlbums: GooglePhotosAlbum[]|null,
       googlePhotosAlbumId: string|null): string[] {
     const breadcrumbs = [this.i18n('wallpaperLabel')];
 
@@ -127,7 +119,7 @@
               googlePhotosAlbum =>
                   googlePhotosAlbum.id === googlePhotosAlbumId);
           if (googlePhotosAlbum) {
-            breadcrumbs.push(googlePhotosAlbum.name);
+            breadcrumbs.push(googlePhotosAlbum.title);
           }
         }
         break;
diff --git a/ash/webui/personalization_app/resources/trusted/personalization_router_element.ts b/ash/webui/personalization_app/resources/trusted/personalization_router_element.ts
index 52e9a9c..ef818805 100644
--- a/ash/webui/personalization_app/resources/trusted/personalization_router_element.ts
+++ b/ash/webui/personalization_app/resources/trusted/personalization_router_element.ts
@@ -12,7 +12,7 @@
 import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
 import {html, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
-import {WallpaperCollection} from './personalization_app.mojom-webui.js';
+import {GooglePhotosAlbum, WallpaperCollection} from './personalization_app.mojom-webui.js';
 
 export enum Paths {
   Ambient = '/ambient',
@@ -94,10 +94,8 @@
     this.goToRoute(Paths.CollectionImages, {id: collection.id});
   }
 
-  /**
-   * Navigate to a specific album in the Google Photos collection page.
-   */
-  selectGooglePhotosAlbum(album: WallpaperCollection) {
+  /** Navigate to a specific album in the Google Photos collection page. */
+  selectGooglePhotosAlbum(album: GooglePhotosAlbum) {
     this.goToRoute(
         Paths.GooglePhotosCollection, {googlePhotosAlbumId: album.id});
   }
diff --git a/ash/webui/personalization_app/resources/trusted/wallpaper/google_photos_albums_element.html b/ash/webui/personalization_app/resources/trusted/wallpaper/google_photos_albums_element.html
index a9d3377..cce761e 100644
--- a/ash/webui/personalization_app/resources/trusted/wallpaper/google_photos_albums_element.html
+++ b/ash/webui/personalization_app/resources/trusted/wallpaper/google_photos_albums_element.html
@@ -7,23 +7,17 @@
     height: 100%;
     width: 100%;
   }
-
-  .album {
-    align-items: center;
-    background: rgba(0, 0, 0, 0.12);
-    display: flex;
-    justify-content: center;
-  }
 </style>
 <iron-list id="grid" items="[[albums_]]" as="album" grid>
   <template>
-      <wallpaper-grid-item>
-        <template is="dom-if" if="[[album]]">
-          <div class="album" tabindex$="[[tabIndex]]"
-            on-click="onAlbumSelected_" on-keypress="onAlbumSelected_">
-            [[album.name]]
-          </div>
-        </template>
-      </wallpaper-grid-item>
+    <wallpaper-grid-item
+      class="album"
+      image-src="[[album.preview.url]]"
+      on-click="onAlbumSelected_"
+      on-keypress="onAlbumSelected_"
+      primary-text="[[album.title]]"
+      secondary-text="[[getSecondaryText_(album)]]"
+      tabindex$="[[tabIndex]]">
+    </wallpaper-grid-item>
   </template>
 </iron-list>
diff --git a/ash/webui/personalization_app/resources/trusted/wallpaper/google_photos_albums_element.ts b/ash/webui/personalization_app/resources/trusted/wallpaper/google_photos_albums_element.ts
index 6971dea..d2feb480 100644
--- a/ash/webui/personalization_app/resources/trusted/wallpaper/google_photos_albums_element.ts
+++ b/ash/webui/personalization_app/resources/trusted/wallpaper/google_photos_albums_element.ts
@@ -14,8 +14,8 @@
 import {IronListElement} from 'chrome://resources/polymer/v3_0/iron-list/iron-list.js';
 import {afterNextRender, html} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
-import {isSelectionEvent} from '../../common/utils.js';
-import {WallpaperCollection} from '../personalization_app.mojom-webui.js';
+import {getCountText, isSelectionEvent} from '../../common/utils.js';
+import {GooglePhotosAlbum} from '../personalization_app.mojom-webui.js';
 import {PersonalizationRouter} from '../personalization_router_element.js';
 import {WithPersonalizationStore} from '../personalization_store.js';
 
@@ -50,7 +50,7 @@
   hidden: boolean;
 
   /** The list of albums. */
-  private albums_: WallpaperCollection[]|null|undefined;
+  private albums_: GooglePhotosAlbum[]|null|undefined;
 
   /** Whether the list of albums is currently loading. */
   private albumsLoading_: boolean;
@@ -67,7 +67,7 @@
   }
 
   /** Invoked on selection of an album. */
-  private onAlbumSelected_(e: Event&{model: {album: WallpaperCollection}}) {
+  private onAlbumSelected_(e: Event&{model: {album: GooglePhotosAlbum}}) {
     assert(e.model.album);
     if (isSelectionEvent(e)) {
       PersonalizationRouter.instance().selectGooglePhotosAlbum(e.model.album);
@@ -85,6 +85,11 @@
     // iron-list when this element becomes visible.
     afterNextRender(this, () => this.$.grid.fire('iron-resize'));
   }
+
+  /** Returns the secondary text to display for the specified |album|. */
+  private getSecondaryText_(album: GooglePhotosAlbum): string {
+    return getCountText(album.photoCount);
+  }
 }
 
 customElements.define(GooglePhotosAlbums.is, GooglePhotosAlbums);
diff --git a/ash/webui/personalization_app/resources/trusted/wallpaper/google_photos_collection_element.ts b/ash/webui/personalization_app/resources/trusted/wallpaper/google_photos_collection_element.ts
index d064f078..38b66cc 100644
--- a/ash/webui/personalization_app/resources/trusted/wallpaper/google_photos_collection_element.ts
+++ b/ash/webui/personalization_app/resources/trusted/wallpaper/google_photos_collection_element.ts
@@ -16,7 +16,7 @@
 import {html} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 import {isNonEmptyArray} from '../../common/utils.js';
-import {WallpaperCollection, WallpaperProviderInterface} from '../personalization_app.mojom-webui.js';
+import {GooglePhotosAlbum, WallpaperProviderInterface} from '../personalization_app.mojom-webui.js';
 import {WithPersonalizationStore} from '../personalization_store.js';
 
 import {initializeGooglePhotosData} from './wallpaper_controller.js';
@@ -73,7 +73,7 @@
   hidden: boolean;
 
   /** The list of albums. */
-  private albums_: WallpaperCollection[]|null|undefined;
+  private albums_: GooglePhotosAlbum[]|null|undefined;
 
   /** The list of photos. */
   private photos_: Url[]|null|undefined;
diff --git a/ash/webui/personalization_app/resources/trusted/wallpaper/google_photos_photos_by_album_id_element.html b/ash/webui/personalization_app/resources/trusted/wallpaper/google_photos_photos_by_album_id_element.html
index a421f79..7bea5c4 100644
--- a/ash/webui/personalization_app/resources/trusted/wallpaper/google_photos_photos_by_album_id_element.html
+++ b/ash/webui/personalization_app/resources/trusted/wallpaper/google_photos_photos_by_album_id_element.html
@@ -7,22 +7,12 @@
     height: 100%;
     width: 100%;
   }
-
-  .photo {
-    align-items: center;
-    background: rgba(0, 0, 0, 0.12);
-    display: flex;
-    justify-content: center;
-  }
 </style>
 <iron-list id="grid" items="[[album_]]" as="photo" grid>
   <template>
-      <wallpaper-grid-item>
-        <template is="dom-if" if="[[photo]]">
-          <div class="photo" tabindex$="[[tabIndex]]">
-            [[photo]]
-          </div>
-        </template>
-      </wallpaper-grid-item>
+    <wallpaper-grid-item
+      class="photo"
+      tabindex$="[[tabIndex]]">
+    </wallpaper-grid-item>
   </template>
 </iron-list>
diff --git a/ash/webui/personalization_app/resources/trusted/wallpaper/google_photos_photos_element.html b/ash/webui/personalization_app/resources/trusted/wallpaper/google_photos_photos_element.html
index ecebee8..0ac6075c 100644
--- a/ash/webui/personalization_app/resources/trusted/wallpaper/google_photos_photos_element.html
+++ b/ash/webui/personalization_app/resources/trusted/wallpaper/google_photos_photos_element.html
@@ -19,30 +19,17 @@
   .row:focus-visible {
     outline: 0;
   }
-
-  .photo {
-    align-items: center;
-    background: rgba(0, 0, 0, 0.12);
-    display: flex;
-    justify-content: center;
-  }
-
-  .photo img {
-    flex: 1 1 auto;
-    height: 100%;
-    object-fit: cover;
-    width: 100%;
-  }
 </style>
 <iron-list id="grid" items="[[photosByRow_]]" as="row">
   <template>
     <div class="row" rowindex$="[[index]]" tabindex$="[[tabIndex]]"
       on-focus="onGridRowFocused_" on-keydown="onGridRowKeyDown_">
       <template is="dom-repeat" items="[[row]]" as="photo">
-        <wallpaper-grid-item>
-          <div class="photo" colindex$="[[index]]" tabindex="-1">
-            <img is="cr-auto-img" auto-src="[[photo.url]]"></img>
-          </div>
+        <wallpaper-grid-item
+          class="photo"
+          colindex$="[[index]]"
+          image-src="[[photo.url]]"
+          tabindex="-1">    
         </wallpaper-grid-item>
       </template>
     </div>
diff --git a/ash/webui/personalization_app/resources/trusted/wallpaper/google_photos_photos_element.ts b/ash/webui/personalization_app/resources/trusted/wallpaper/google_photos_photos_element.ts
index f52bdec..655757b 100644
--- a/ash/webui/personalization_app/resources/trusted/wallpaper/google_photos_photos_element.ts
+++ b/ash/webui/personalization_app/resources/trusted/wallpaper/google_photos_photos_element.ts
@@ -7,7 +7,6 @@
  */
 
 import 'chrome://resources/polymer/v3_0/iron-list/iron-list.js';
-import 'chrome://resources/cr_elements/cr_auto_img/cr_auto_img.js';
 import './styles.js';
 import '../../common/styles.js';
 
diff --git a/ash/webui/personalization_app/resources/trusted/wallpaper/wallpaper_actions.ts b/ash/webui/personalization_app/resources/trusted/wallpaper/wallpaper_actions.ts
index bcf0db3..e5e96e71 100644
--- a/ash/webui/personalization_app/resources/trusted/wallpaper/wallpaper_actions.ts
+++ b/ash/webui/personalization_app/resources/trusted/wallpaper/wallpaper_actions.ts
@@ -7,7 +7,7 @@
 import {FilePath} from 'chrome://resources/mojo/mojo/public/mojom/base/file_path.mojom-webui.js';
 import {Url} from 'chrome://resources/mojo/url/mojom/url.mojom-webui.js';
 
-import {CurrentWallpaper, WallpaperCollection, WallpaperImage} from '../personalization_app.mojom-webui.js';
+import {CurrentWallpaper, GooglePhotosAlbum, WallpaperCollection, WallpaperImage} from '../personalization_app.mojom-webui.js';
 import {DisplayableImage} from '../personalization_reducers.js';
 
 /**
@@ -245,13 +245,11 @@
 
 export type SetGooglePhotosAlbumsAction = Action&{
   name: WallpaperActionName.SET_GOOGLE_PHOTOS_ALBUMS;
-  albums: WallpaperCollection[]|null;
+  albums: GooglePhotosAlbum[]|null;
 };
 
-/**
- * Sets the list of Google Photos albums. May be called with null on error.
- */
-export function setGooglePhotosAlbumsAction(albums: WallpaperCollection[]|
+/** Sets the list of Google Photos albums. May be called with null on error. */
+export function setGooglePhotosAlbumsAction(albums: GooglePhotosAlbum[]|
                                             null): SetGooglePhotosAlbumsAction {
   return {albums, name: WallpaperActionName.SET_GOOGLE_PHOTOS_ALBUMS};
 }
diff --git a/ash/webui/personalization_app/resources/trusted/wallpaper/wallpaper_controller.ts b/ash/webui/personalization_app/resources/trusted/wallpaper/wallpaper_controller.ts
index fb1abfc..3466805b 100644
--- a/ash/webui/personalization_app/resources/trusted/wallpaper/wallpaper_controller.ts
+++ b/ash/webui/personalization_app/resources/trusted/wallpaper/wallpaper_controller.ts
@@ -8,7 +8,7 @@
 import {Url} from 'chrome://resources/mojo/url/mojom/url.mojom-webui.js';
 
 import {isNonEmptyArray} from '../../common/utils.js';
-import {WallpaperCollection, WallpaperImage, WallpaperLayout, WallpaperProviderInterface, WallpaperType} from '../personalization_app.mojom-webui.js';
+import {FetchGooglePhotosAlbumsResponse, GooglePhotosAlbum, WallpaperCollection, WallpaperImage, WallpaperLayout, WallpaperProviderInterface, WallpaperType} from '../personalization_app.mojom-webui.js';
 import {PersonalizationStore} from '../personalization_store.js';
 import {isFilePath, isWallpaperImage} from '../utils.js';
 
@@ -82,26 +82,26 @@
 
 /** Fetches the list of Google Photos albums and saves it to the store. */
 async function fetchGooglePhotosAlbums(
-    _: WallpaperProviderInterface, store: PersonalizationStore): Promise<void> {
+    provider: WallpaperProviderInterface,
+    store: PersonalizationStore): Promise<void> {
   store.dispatch(action.beginLoadGooglePhotosAlbumsAction());
 
-  // TODO(dmblack): Create and wire up mojo API. For now, simulate an async
-  // request that returns a list of fictitious Google Photos albums.
-  return new Promise(resolve => setTimeout(() => {
-                       store.dispatch(action.setGooglePhotosAlbumsAction([
-                         '9bd1d7a3-f995-4445-be47-53c5b58ce1cb',
-                         '0ec40478-9712-42e1-b5bf-3e75870ca042',
-                         '0a268a37-877a-4936-81d4-38cc84b0f596',
-                         '27597eb3-a42d-474c-ab39-592680dcf35a',
-                         'bdcd6ba5-ed70-4866-9bc0-87ccf87db08c',
-                       ].map((uuid, i) => {
-                         const album = new WallpaperCollection();
-                         album.id = uuid;
-                         album.name = `Album ${i}`;
-                         return album;
-                       })));
-                       resolve();
-                     }, 1000));
+  let albums: Array<GooglePhotosAlbum>|null = [];
+  let resumeToken: string|null|undefined = null;
+
+  do {
+    const {response} = await provider.fetchGooglePhotosAlbums(resumeToken) as
+        {response: FetchGooglePhotosAlbumsResponse};
+    if (!Array.isArray(response.albums)) {
+      console.warn('Failed to fetch Google Photos albums');
+      albums = null;
+      break;
+    }
+    albums.push(...response.albums);
+    resumeToken = response.resumeToken;
+  } while (resumeToken);
+
+  store.dispatch(action.setGooglePhotosAlbumsAction(albums));
 }
 
 /** Fetches the count of Google Photos photos and saves it to the store. */
diff --git a/ash/webui/personalization_app/resources/trusted/wallpaper/wallpaper_grid_item_element.html b/ash/webui/personalization_app/resources/trusted/wallpaper/wallpaper_grid_item_element.html
index e1b067d..0fcad40 100644
--- a/ash/webui/personalization_app/resources/trusted/wallpaper/wallpaper_grid_item_element.html
+++ b/ash/webui/personalization_app/resources/trusted/wallpaper/wallpaper_grid_item_element.html
@@ -22,17 +22,85 @@
     }
   }
 
-  :host > ::slotted(*) {
+  :host(:focus-visible) {
+    outline: none;
+  }
+
+  .item {
+    background-color: rgba(0, 0, 0, 0.12);
     border-radius: var(--personalization-app-grid-item-border-radius);
     box-sizing: border-box;
     display: block;
     height: 100%;
     overflow: hidden;
+    position: relative;
     width: 100%;
   }
 
-  :host > ::slotted(*:focus-within) {
+  :host(:focus-visible) .item {
     outline: 2px solid var(--cros-focus-ring-color);
   }
+
+  img {
+    bottom: 0;
+    height: 100%;
+    left: 0;
+    object-fit: cover;
+    position: absolute;
+    right: 0;
+    top: 0;
+    width: 100%;
+  }
+
+  .text {
+    background: linear-gradient(
+        rgba(var(--google-grey-900-rgb), 0),
+        rgba(var(--google-grey-900-rgb), 55%));
+    bottom: 0;
+    box-sizing: border-box;
+    display: flex;
+    flex-direction: column;
+    height: 50%;
+    justify-content: flex-end;
+    left: 0;
+    overflow: hidden;
+    padding: 8px;
+    position: absolute;
+    right: 0;
+    white-space: nowrap;
+    width: 100%;
+  }
+
+  .primary-text,
+  .secondary-text {
+    color: white;
+    margin: 0;
+    padding: 0;
+    text-align: center;
+    text-overflow: ellipsis;
+    text-shadow: var(--personalization-app-text-shadow-elevation-1);
+  }
+
+  .primary-text {
+    font: var(--cros-headline-1-font);
+  }
+
+  .secondary-text {
+    font: var(--cros-annotation-2-font);
+  }
 </style>
-<slot></slot>
+<div class="item">
+  <template is="dom-if" if="[[isImageVisible_(imageSrc)]]">
+    <img is="cr-auto-img" auto-src="[[imageSrc]]"></img>
+  </template>
+  <template is="dom-if" if="[[isTextVisible_(primaryText, secondaryText)]]">
+    <div class="text">
+      <template is="dom-if" if="[[isPrimaryTextVisible_(primaryText)]]">
+        <p class="primary-text" title$="[[primaryText]]">[[primaryText]]</p>
+      </template>
+      <template is="dom-if" if="[[isSecondaryTextVisible_(secondaryText)]]">
+        <p class="secondary-text" title$="[[secondaryText]]">[[secondaryText]]</p>
+      </template>
+    </div>
+  </template>
+</div>
diff --git a/ash/webui/personalization_app/resources/trusted/wallpaper/wallpaper_grid_item_element.ts b/ash/webui/personalization_app/resources/trusted/wallpaper/wallpaper_grid_item_element.ts
index efb7771..24e72dd 100644
--- a/ash/webui/personalization_app/resources/trusted/wallpaper/wallpaper_grid_item_element.ts
+++ b/ash/webui/personalization_app/resources/trusted/wallpaper/wallpaper_grid_item_element.ts
@@ -6,11 +6,12 @@
  * @fileoverview Polymer element that displays a single grid item.
  */
 
-import '/common/styles.js';
+import 'chrome://resources/cr_elements/cr_auto_img/cr_auto_img.js';
+import '../../common/styles.js';
 
 import {html, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
-export class WallpaperGridItemElement extends PolymerElement {
+export class WallpaperGridItem extends PolymerElement {
   static get is() {
     return 'wallpaper-grid-item';
   }
@@ -18,6 +19,43 @@
   static get template() {
     return html`{__html_template__}`;
   }
+
+  static get properties() {
+    return {
+      imageSrc: String,
+      primaryText: String,
+      secondaryText: String,
+    };
+  }
+
+  /** The source for the image to render for the grid item. */
+  imageSrc: string|undefined;
+
+  /** The primary text to render for the grid item. */
+  primaryText: string|undefined;
+
+  /** The secondary text to render for the grid item. */
+  secondaryText: string|undefined;
+
+  /** Whether the image is currently visible. */
+  private isImageVisible_() {
+    return !!this.imageSrc?.length;
+  }
+
+  /** Whether the primary text is currently visible. */
+  private isPrimaryTextVisible_() {
+    return !!this.primaryText?.length;
+  }
+
+  /** Whether the secondary text is currently visible. */
+  private isSecondaryTextVisible_() {
+    return !!this.secondaryText?.length;
+  }
+
+  /** Whether any text is currently visible. */
+  private isTextVisible_() {
+    return this.isSecondaryTextVisible_() || this.isPrimaryTextVisible_();
+  }
 }
 
-customElements.define(WallpaperGridItemElement.is, WallpaperGridItemElement);
+customElements.define(WallpaperGridItem.is, WallpaperGridItem);
diff --git a/ash/webui/personalization_app/resources/trusted/wallpaper/wallpaper_state.ts b/ash/webui/personalization_app/resources/trusted/wallpaper/wallpaper_state.ts
index 1ff396f..69434ed 100644
--- a/ash/webui/personalization_app/resources/trusted/wallpaper/wallpaper_state.ts
+++ b/ash/webui/personalization_app/resources/trusted/wallpaper/wallpaper_state.ts
@@ -4,7 +4,7 @@
 import {FilePath} from 'chrome://resources/mojo/mojo/public/mojom/base/file_path.mojom-webui.js';
 import {Url} from 'chrome://resources/mojo/url/mojom/url.mojom-webui.js';
 
-import {CurrentWallpaper, WallpaperCollection, WallpaperImage} from '../personalization_app.mojom-webui.js';
+import {CurrentWallpaper, GooglePhotosAlbum, WallpaperCollection, WallpaperImage} from '../personalization_app.mojom-webui.js';
 
 /**
  * Stores collections and images from backdrop server.
@@ -29,7 +29,7 @@
  */
 export interface GooglePhotosState {
   count: number|null|undefined;
-  albums: WallpaperCollection[]|null|undefined;
+  albums: GooglePhotosAlbum[]|null|undefined;
   photos: Url[]|null|undefined;
   photosByAlbumId: Record<string, unknown[]|null|undefined>;
 }
diff --git a/ash/webui/personalization_app/resources/untrusted/collections_grid.ts b/ash/webui/personalization_app/resources/untrusted/collections_grid.ts
index 4a2e433..9574ec4 100644
--- a/ash/webui/personalization_app/resources/untrusted/collections_grid.ts
+++ b/ash/webui/personalization_app/resources/untrusted/collections_grid.ts
@@ -10,7 +10,7 @@
 import {afterNextRender, html, PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 import {Events, EventType, kMaximumGooglePhotosPreviews, kMaximumLocalImagePreviews} from '../common/constants.js';
-import {getLoadingPlaceholderAnimationDelay, getNumberOfGridItemsPerRow, isNullOrArray, isNullOrNumber, isSelectionEvent} from '../common/utils.js';
+import {getCountText, getLoadingPlaceholderAnimationDelay, getNumberOfGridItemsPerRow, isNullOrArray, isNullOrNumber, isSelectionEvent} from '../common/utils.js';
 import {selectCollection, selectGooglePhotosCollection, selectLocalCollection, validateReceivedData} from '../untrusted/iframe_api.js';
 
 /**
@@ -82,27 +82,6 @@
   };
 }
 
-/**
- * Get the text to display for number of images.
- */
-function getCountText(x: number|null|undefined): string {
-  switch (x) {
-    case undefined:
-    case null:
-      return '';
-    case 0:
-      return loadTimeData.getString('zeroImages');
-    case 1:
-      return loadTimeData.getString('oneImage');
-    default:
-      if ('number' !== typeof x || x < 0) {
-        console.error('Received an impossible value');
-        return '';
-      }
-      return loadTimeData.getStringF('multipleImages', x);
-  }
-}
-
 /** Returns the tile to display for the Google Photos collection. */
 function getGooglePhotosTile(
     googlePhotos: Url[]|null, googlePhotosCount: number|null): ImageTile {
diff --git a/ash/webui/shimless_rma/backend/shimless_rma_service.cc b/ash/webui/shimless_rma/backend/shimless_rma_service.cc
index f6f79228..cce4f555 100644
--- a/ash/webui/shimless_rma/backend/shimless_rma_service.cc
+++ b/ash/webui/shimless_rma/backend/shimless_rma_service.cc
@@ -1100,8 +1100,8 @@
               : "Rebooting after user cancelled RMA.");
     } else {
       VLOG(1) << "Restarting Chrome to bypass RMA after cancel request.";
-      // TODO(gavindodd): Append ::ash::switches::kNoShimlessRma when autolaunch
-      // is implemented.
+      // TODO(gavindodd): Append switches::kNoShimlessRma when autolaunch is
+      // implemented.
       shimless_rma_delegate_->RestartChrome();
     }
   }
diff --git a/ash/webui/shimless_rma/resources/onboarding_landing_page.html b/ash/webui/shimless_rma/resources/onboarding_landing_page.html
index 2745aa7c..35e3c437 100644
--- a/ash/webui/shimless_rma/resources/onboarding_landing_page.html
+++ b/ash/webui/shimless_rma/resources/onboarding_landing_page.html
@@ -17,10 +17,17 @@
         icon="[[getVerificationIcon_(isCompliant_)]]"
         hidden$="[[verificationInProgress_]]" class="small-icon">
       </iron-icon>
-      [[verificationMessage_]]
-    </div>
-    <div hidden$="[[verificationInProgress_]]">
-      <p id="errorMessage" hidden="[[isCompliant_]]">[[errorMessage_]]</p>
+      <span hidden$="[[!verificationInProgress_]]">
+        [[i18n('validatingComponentsText')]]
+      </span>
+      <span hidden$="[[verificationInProgress_]]">
+        <span hidden$="[[!isCompliant_]]">
+          [[i18n('validatedComponentsSuccessText')]]
+        </span>
+        <span inner-h-t-m-l="[[verificationFailedMessage_]]"
+            hidden$="[[isCompliant_]]">
+        </span>
+      </span>
     </div>
   </div>
   <div slot="body">
@@ -29,3 +36,14 @@
     </iron-icon>
   </div>
 </base-page>
+
+<cr-dialog id="unqualifiedComponentsDialog" on-cancel="closeDialog_"
+    ignore-popstate>
+  <div slot="title">[[i18n('unqualifiedComponentsTitle')]]</div>
+  <div slot="body" id="dialogBody">[[componentsList_]]</div>
+  <div slot="button-container">
+    <cr-button class="action-button" on-click="closeDialog_">
+      [[i18n('okButtonLabel')]]
+    </cr-button>
+  </div>
+</cr-dialog>
diff --git a/ash/webui/shimless_rma/resources/onboarding_landing_page.js b/ash/webui/shimless_rma/resources/onboarding_landing_page.js
index a2639b7..3ad72f6 100644
--- a/ash/webui/shimless_rma/resources/onboarding_landing_page.js
+++ b/ash/webui/shimless_rma/resources/onboarding_landing_page.js
@@ -40,17 +40,11 @@
 
   static get properties() {
     return {
-      /** @protected */
-      verificationMessage_: {
-        type: String,
-        value: '',
-      },
-
       /**
-       * Error code from rmad service, not i18n.
+       * List of unqualified components from rmad service, not i18n.
        * @protected
        */
-      errorMessage_: {
+      componentsList_: {
         type: String,
         value: '',
       },
@@ -69,6 +63,12 @@
         type: Boolean,
         value: false,
       },
+
+      /** @protected */
+      verificationFailedMessage_: {
+        type: String,
+        value: '',
+      },
     };
   }
 
@@ -91,7 +91,6 @@
   /** @override */
   ready() {
     super.ready();
-    this.verificationMessage_ = this.i18n('validatingComponentsText');
   }
 
   /** @return {!Promise<StateResult>} */
@@ -118,19 +117,37 @@
    * @param {string} errorMessage
    */
   onHardwareVerificationResult(isCompliant, errorMessage) {
-    if (isCompliant) {
-      this.verificationMessage_ = this.i18n('validatedComponentsSuccessText');
-    } else {
-      this.verificationMessage_ = this.i18n('validatedComponentsFailText');
-      this.errorMessage_ = errorMessage;
-    }
     this.isCompliant_ = isCompliant;
     this.verificationInProgress_ = false;
+
+    if (!this.isCompliant_) {
+      this.componentsList_ = errorMessage;
+      this.setVerificationFailedMessage_();
+    }
+
     this.dispatchEvent(new CustomEvent(
         'disable-next-button',
         {bubbles: true, composed: true, detail: false},
         ));
   }
+
+  /** @private */
+  setVerificationFailedMessage_() {
+    this.verificationFailedMessage_ =
+        this.i18nAdvanced('validatedComponentsFailText', {attrs: ['id']});
+    const linkElement =
+        this.shadowRoot.querySelector('#unqualifiedComponentsLink');
+    linkElement.setAttribute('href', '#');
+    linkElement.addEventListener(
+        'click',
+        () => this.shadowRoot.querySelector('#unqualifiedComponentsDialog')
+                  .showModal());
+  }
+
+  /** @private */
+  closeDialog_() {
+    this.shadowRoot.querySelector('#unqualifiedComponentsDialog').close();
+  }
 }
 
 customElements.define(OnboardingLandingPage.is, OnboardingLandingPage);
diff --git a/ash/webui/shimless_rma/shimless_rma.cc b/ash/webui/shimless_rma/shimless_rma.cc
index 45fd2e0..2e6e3a23 100644
--- a/ash/webui/shimless_rma/shimless_rma.cc
+++ b/ash/webui/shimless_rma/shimless_rma.cc
@@ -76,6 +76,7 @@
       {"backButtonLabel", IDS_SHIMLESS_RMA_BACK_BUTTON},
       {"nextButtonLabel", IDS_SHIMLESS_RMA_NEXT_BUTTON},
       {"skipButtonLabel", IDS_SHIMLESS_RMA_SKIP_BUTTON},
+      {"okButtonLabel", IDS_SHIMLESS_RMA_OK_BUTTON},
       // Landing page
       {"welcomeTitleText", IDS_SHIMLESS_RMA_LANDING_PAGE_TITLE},
       {"beginRmaWarningText", IDS_SHIMLESS_RMA_AUTHORIZED_TECH_ONLY_WARNING},
@@ -85,6 +86,8 @@
       {"validatedComponentsFailText",
        IDS_SHIMLESS_RMA_VALIDATED_COMPONENTS_FAIL},
       {"getStartedButtonLabel", IDS_SHIMLESS_RMA_GET_STARTED_BUTTON_LABEL},
+      {"unqualifiedComponentsTitle",
+       IDS_SHIMLESS_RMA_UNQUALIFIED_COMPONENTS_TITLE},
       // Network connect page
       {"connectNetworkTitleText", IDS_SHIMLESS_RMA_CONNECT_PAGE_TITLE},
       {"connectNetworkDescriptionText",
diff --git a/ash/wm/client_controlled_state.cc b/ash/wm/client_controlled_state.cc
index 3bbda6c..8476543 100644
--- a/ash/wm/client_controlled_state.cc
+++ b/ash/wm/client_controlled_state.cc
@@ -119,6 +119,13 @@
 
 void ClientControlledState::DetachState(WindowState* window_state) {}
 
+#if DCHECK_IS_ON()
+void ClientControlledState::CheckMaximizableCondition(
+    const WindowState* window_state) const {
+  // A client decides when the window should be maximizable.
+}
+#endif  // DCHECK_IS_ON()
+
 void ClientControlledState::HandleWorkspaceEvents(WindowState* window_state,
                                                   const WMEvent* event) {
   if (!delegate_)
diff --git a/ash/wm/client_controlled_state.h b/ash/wm/client_controlled_state.h
index b778e7a3..aa79303 100644
--- a/ash/wm/client_controlled_state.h
+++ b/ash/wm/client_controlled_state.h
@@ -82,6 +82,10 @@
   void AttachState(WindowState* window_state,
                    WindowState::State* previous_state) override;
   void DetachState(WindowState* window_state) override;
+#if DCHECK_IS_ON()
+  void CheckMaximizableCondition(
+      const WindowState* window_state) const override;
+#endif  // DCHECK_IS_ON()
 
   // BaseState:
   void HandleWorkspaceEvents(WindowState* window_state,
diff --git a/ash/wm/tablet_mode/tablet_mode_controller_unittest.cc b/ash/wm/tablet_mode/tablet_mode_controller_unittest.cc
index 9b2535b2..fee4203 100644
--- a/ash/wm/tablet_mode/tablet_mode_controller_unittest.cc
+++ b/ash/wm/tablet_mode/tablet_mode_controller_unittest.cc
@@ -1789,7 +1789,7 @@
   void SetUp() override {
     // We need to simulate the real on-device behavior for some tests.
     base::CommandLine::ForCurrentProcess()->AppendSwitch(
-        chromeos::switches::kForceSystemCompositorMode);
+        switches::kForceSystemCompositorMode);
     TabletModeControllerTest::SetUp();
     // PowerManagerClient callback is a posted task.
     base::RunLoop().RunUntilIdle();
diff --git a/ash/wm/window_state.cc b/ash/wm/window_state.cc
index 5f63dbf24..0f0fc1bb 100644
--- a/ash/wm/window_state.cc
+++ b/ash/wm/window_state.cc
@@ -223,6 +223,16 @@
 
 constexpr base::TimeDelta WindowState::kBoundsChangeSlideDuration;
 
+#if DCHECK_IS_ON()
+void WindowState::State::CheckMaximizableCondition(
+    const WindowState* window_state) const {
+  const aura::Window* window = window_state->window();
+  const gfx::Size max_size = window->delegate()->GetMaximumSize();
+  DCHECK(max_size.IsEmpty() || max_size.width() > kAllowMaximizeThreshold ||
+         max_size.height() > kAllowMaximizeThreshold);
+}
+#endif  // DCHECK_IS_ON()
+
 WindowState::ScopedBoundsChangeAnimation::ScopedBoundsChangeAnimation(
     aura::Window* window,
     BoundsChangeAnimationType bounds_animation_type)
@@ -331,11 +341,8 @@
   bool can_maximize = (window_->GetProperty(aura::client::kResizeBehaviorKey) &
                        aura::client::kResizeBehaviorCanMaximize) != 0;
 #if DCHECK_IS_ON()
-  if (window_->delegate() && can_maximize) {
-    const gfx::Size max_size = window_->delegate()->GetMaximumSize();
-    DCHECK(max_size.IsEmpty() || max_size.width() > kAllowMaximizeThreshold ||
-           max_size.height() > kAllowMaximizeThreshold);
-  }
+  if (window_->delegate() && can_maximize)
+    current_state_->CheckMaximizableCondition(this);
 #endif
   return can_maximize;
 }
diff --git a/ash/wm/window_state.h b/ash/wm/window_state.h
index aaddcf5..9fbcdfc 100644
--- a/ash/wm/window_state.h
+++ b/ash/wm/window_state.h
@@ -90,6 +90,12 @@
 
     // Called when the window is being destroyed.
     virtual void OnWindowDestroying(WindowState* window_state) {}
+
+#if DCHECK_IS_ON()
+    // Check if the window state satisfies the maximizable condition.
+    virtual void CheckMaximizableCondition(
+        const WindowState* window_state) const;
+#endif  // DCHECK_IS_ON()
   };
 
   // Returns the WindowState for |window|. Creates WindowState if it doesn't
diff --git a/base/debug/activity_tracker.h b/base/debug/activity_tracker.h
index 1e965700..b5da6d9f 100644
--- a/base/debug/activity_tracker.h
+++ b/base/debug/activity_tracker.h
@@ -20,6 +20,7 @@
 #include "base/base_export.h"
 #include "base/callback.h"
 #include "base/compiler_specific.h"
+#include "base/dcheck_is_on.h"
 #include "base/gtest_prod_util.h"
 #include "base/location.h"
 #include "base/memory/raw_ptr.h"
@@ -29,10 +30,13 @@
 #include "base/strings/string_piece.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/task/sequenced_task_runner.h"
-#include "base/threading/platform_thread.h"
 #include "base/threading/thread_local.h"
 #include "build/build_config.h"
 
+#if DCHECK_IS_ON()
+#include "base/threading/platform_thread_ref.h"
+#endif
+
 namespace base {
 
 struct PendingTask;
diff --git a/base/pickle.h b/base/pickle.h
index d06be18..4046afc 100644
--- a/base/pickle.h
+++ b/base/pickle.h
@@ -14,7 +14,6 @@
 #include "base/check_op.h"
 #include "base/containers/span.h"
 #include "base/gtest_prod_util.h"
-#include "base/memory/raw_ptr.h"
 #include "base/memory/ref_counted.h"
 #include "base/strings/string_piece.h"
 
@@ -255,7 +254,7 @@
   }
 
   const char* payload() const {
-    return reinterpret_cast<const char*>(header_.get()) + header_size_;
+    return reinterpret_cast<const char*>(header_) + header_size_;
   }
 
   // Returns the address of the byte immediately following the currently valid
@@ -271,7 +270,7 @@
   size_t header_size() const { return header_size_; }
 
   char* mutable_payload() {
-    return reinterpret_cast<char*>(header_.get()) + header_size_;
+    return reinterpret_cast<char*>(header_) + header_size_;
   }
 
   size_t capacity_after_header() const {
@@ -313,7 +312,9 @@
  private:
   friend class PickleIterator;
 
-  raw_ptr<Header> header_;
+  // `header_` is not a raw_ptr<...> for performance reasons (based on analysis
+  // of sampling profiler data).
+  Header* header_;
   size_t header_size_;  // Supports extra data between header and payload.
   // Allocation size of payload (or -1 if allocation is const). Note: this
   // doesn't count the header.
diff --git a/base/threading/thread_checker_impl.cc b/base/threading/thread_checker_impl.cc
index bc0cf65..e0b4205 100644
--- a/base/threading/thread_checker_impl.cc
+++ b/base/threading/thread_checker_impl.cc
@@ -6,6 +6,7 @@
 
 #include "base/check.h"
 #include "base/debug/stack_trace.h"
+#include "base/threading/platform_thread.h"
 #include "base/threading/thread_checker.h"
 #include "base/threading/thread_local.h"
 #include "base/threading/thread_task_runner_handle.h"
diff --git a/base/threading/thread_checker_impl.h b/base/threading/thread_checker_impl.h
index dcba3bd..6b43643 100644
--- a/base/threading/thread_checker_impl.h
+++ b/base/threading/thread_checker_impl.h
@@ -11,7 +11,7 @@
 #include "base/sequence_token.h"
 #include "base/synchronization/lock.h"
 #include "base/thread_annotations.h"
-#include "base/threading/platform_thread.h"
+#include "base/threading/platform_thread_ref.h"
 
 namespace base {
 namespace debug {
diff --git a/base/timer/timer.h b/base/timer/timer.h
index bc1d4a1..4e31daa 100644
--- a/base/timer/timer.h
+++ b/base/timer/timer.h
@@ -135,7 +135,9 @@
 
   // Detects when the scheduled task is deleted before being executed. Null when
   // there is no scheduled task.
-  raw_ptr<TaskDestructionDetector> task_destruction_detector_
+  // `task_destruction_detector_` is not a raw_ptr<...> for performance reasons
+  // (based on analysis of sampling profiler data).
+  TaskDestructionDetector* task_destruction_detector_
       GUARDED_BY_CONTEXT(sequence_checker_);
 
   // If true, |user_task_| is scheduled to run sometime in the future.
diff --git a/base/trace_event/trace_event.h b/base/trace_event/trace_event.h
index f5282a47..c5621c3 100644
--- a/base/trace_event/trace_event.h
+++ b/base/trace_event/trace_event.h
@@ -23,7 +23,6 @@
 #include "base/time/time_override.h"
 #include "base/trace_event/builtin_categories.h"
 #include "base/trace_event/common/trace_event_common.h"
-#include "base/trace_event/log_message.h"
 #include "base/trace_event/thread_instruction_count.h"
 #include "base/trace_event/trace_arguments.h"
 #include "base/trace_event/trace_category.h"
diff --git a/base/win/scoped_com_initializer.cc b/base/win/scoped_com_initializer.cc
index 6d80ff4..ca644c0d 100644
--- a/base/win/scoped_com_initializer.cc
+++ b/base/win/scoped_com_initializer.cc
@@ -6,6 +6,8 @@
 
 #include <wrl/implements.h>
 
+#include <ostream>
+
 #include "base/check_op.h"
 
 namespace base {
diff --git a/build/build_config.h b/build/build_config.h
index ebd66fe..bff78e7 100644
--- a/build/build_config.h
+++ b/build/build_config.h
@@ -30,11 +30,13 @@
 //    COMPILER_MSVC / COMPILER_GCC
 //
 //  Processor:
-//    ARCH_CPU_ARM64 / ARCH_CPU_ARMEL / ARCH_CPU_MIPS / ARCH_CPU_MIPS64 /
-//    ARCH_CPU_MIPS64EL / ARCH_CPU_MIPSEL / ARCH_CPU_PPC64 / ARCH_CPU_S390 /
-//    ARCH_CPU_S390X / ARCH_CPU_X86 / ARCH_CPU_X86_64
+//    ARCH_CPU_ARM64 / ARCH_CPU_ARMEL / ARCH_CPU_LOONG32 / ARCH_CPU_LOONG64 /
+//    ARCH_CPU_MIPS / ARCH_CPU_MIPS64 / ARCH_CPU_MIPS64EL / ARCH_CPU_MIPSEL /
+//    ARCH_CPU_PPC64 / ARCH_CPU_S390 / ARCH_CPU_S390X / ARCH_CPU_X86 /
+//    ARCH_CPU_X86_64
 //  Processor family:
 //    ARCH_CPU_ARM_FAMILY: ARMEL or ARM64
+//    ARCH_CPU_LOONG_FAMILY: LOONG32 or LOONG64
 //    ARCH_CPU_MIPS_FAMILY: MIPS64EL or MIPSEL or MIPS64 or MIPS
 //    ARCH_CPU_PPC64_FAMILY: PPC64
 //    ARCH_CPU_S390_FAMILY: S390 or S390X
@@ -312,6 +314,16 @@
 #define ARCH_CPU_32_BITS 1
 #define ARCH_CPU_BIG_ENDIAN 1
 #endif
+#elif defined(__loongarch32)
+#define ARCH_CPU_LOONG_FAMILY 1
+#define ARCH_CPU_LOONG32 1
+#define ARCH_CPU_32_BITS 1
+#define ARCH_CPU_LITTLE_ENDIAN 1
+#elif defined(__loongarch64)
+#define ARCH_CPU_LOONG_FAMILY 1
+#define ARCH_CPU_LOONG64 1
+#define ARCH_CPU_64_BITS 1
+#define ARCH_CPU_LITTLE_ENDIAN 1
 #else
 #error Please add support for your architecture in build/build_config.h
 #endif
diff --git a/build/config/ios/codesign.py b/build/config/ios/codesign.py
index 5334304..15d25a7 100644
--- a/build/config/ios/codesign.py
+++ b/build/config/ios/codesign.py
@@ -262,10 +262,6 @@
 
   def LoadDefaults(self, defaults):
     for key, value in defaults.items():
-      # TODO(crbug.com/1270127): Re-enable this entitlement after verifying
-      # that it doesn't increase memory usage.
-      if key == "com.apple.developer.kernel.extended-virtual-addressing":
-        continue
       if key not in self._data:
         self._data[key] = value
 
diff --git a/cc/BUILD.gn b/cc/BUILD.gn
index 2ad4752..3392c8b 100644
--- a/cc/BUILD.gn
+++ b/cc/BUILD.gn
@@ -82,6 +82,10 @@
     "layers/content_layer_client.h",
     "layers/deadline_policy.cc",
     "layers/deadline_policy.h",
+    "layers/document_transition_content_layer.cc",
+    "layers/document_transition_content_layer.h",
+    "layers/document_transition_content_layer_impl.cc",
+    "layers/document_transition_content_layer_impl.h",
     "layers/draw_mode.h",
     "layers/draw_properties.cc",
     "layers/draw_properties.h",
@@ -128,10 +132,6 @@
     "layers/scrollbar_layer_base.h",
     "layers/scrollbar_layer_impl_base.cc",
     "layers/scrollbar_layer_impl_base.h",
-    "layers/shared_element_layer.cc",
-    "layers/shared_element_layer.h",
-    "layers/shared_element_layer_impl.cc",
-    "layers/shared_element_layer_impl.h",
     "layers/solid_color_layer.cc",
     "layers/solid_color_layer.h",
     "layers/solid_color_layer_impl.cc",
@@ -838,7 +838,6 @@
   ]
 
   if (skia_support_skottie) {
-    data = [ "//components/viz/test/data/" ]
     sources += [
       "paint/skottie_mru_resource_provider_unittest.cc",
       "paint/skottie_resource_metadata_unittest.cc",
@@ -883,6 +882,8 @@
     "//ui/gl",
     "//ui/gl:test_support",
   ]
+
+  data = [ "//components/viz/test/data/" ]
   data_deps = [
     "//testing/buildbot/filters:cc_unittests_filters",
     "//third_party/mesa_headers",
diff --git a/cc/base/delayed_unique_notifier.h b/cc/base/delayed_unique_notifier.h
index a5b3685..a170415 100644
--- a/cc/base/delayed_unique_notifier.h
+++ b/cc/base/delayed_unique_notifier.h
@@ -9,6 +9,7 @@
 #include "base/memory/raw_ptr.h"
 #include "base/memory/weak_ptr.h"
 #include "base/threading/thread_checker.h"
+#include "base/time/time.h"
 #include "cc/base/base_export.h"
 
 namespace base {
diff --git a/cc/layers/document_transition_content_layer.cc b/cc/layers/document_transition_content_layer.cc
new file mode 100644
index 0000000..623c9e9c
--- /dev/null
+++ b/cc/layers/document_transition_content_layer.cc
@@ -0,0 +1,33 @@
+// 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.
+
+#include "cc/layers/document_transition_content_layer.h"
+
+#include <memory>
+
+#include "base/logging.h"
+#include "cc/layers/document_transition_content_layer_impl.h"
+#include "cc/trees/layer_tree_host.h"
+
+namespace cc {
+
+scoped_refptr<DocumentTransitionContentLayer>
+DocumentTransitionContentLayer::Create(
+    const viz::SharedElementResourceId& resource_id) {
+  return base::WrapRefCounted(new DocumentTransitionContentLayer(resource_id));
+}
+
+DocumentTransitionContentLayer::DocumentTransitionContentLayer(
+    const viz::SharedElementResourceId& resource_id)
+    : resource_id_(resource_id) {}
+
+DocumentTransitionContentLayer::~DocumentTransitionContentLayer() = default;
+
+std::unique_ptr<LayerImpl> DocumentTransitionContentLayer::CreateLayerImpl(
+    LayerTreeImpl* tree_impl) {
+  return DocumentTransitionContentLayerImpl::Create(tree_impl, id(),
+                                                    resource_id_);
+}
+
+}  // namespace cc
diff --git a/cc/layers/document_transition_content_layer.h b/cc/layers/document_transition_content_layer.h
new file mode 100644
index 0000000..a50c24e
--- /dev/null
+++ b/cc/layers/document_transition_content_layer.h
@@ -0,0 +1,47 @@
+// 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.
+
+#ifndef CC_LAYERS_DOCUMENT_TRANSITION_CONTENT_LAYER_H_
+#define CC_LAYERS_DOCUMENT_TRANSITION_CONTENT_LAYER_H_
+
+#include <memory>
+
+#include "base/logging.h"
+#include "cc/cc_export.h"
+#include "cc/layers/layer.h"
+#include "components/viz/common/shared_element_resource_id.h"
+
+namespace cc {
+
+// A layer that renders a texture cached in the Viz process.
+class CC_EXPORT DocumentTransitionContentLayer : public Layer {
+ public:
+  static scoped_refptr<DocumentTransitionContentLayer> Create(
+      const viz::SharedElementResourceId& resource_id);
+
+  DocumentTransitionContentLayer(const DocumentTransitionContentLayer&) =
+      delete;
+  DocumentTransitionContentLayer& operator=(
+      const DocumentTransitionContentLayer&) = delete;
+
+  const viz::SharedElementResourceId& resource_id() const {
+    return resource_id_;
+  }
+
+  // Layer overrides.
+  std::unique_ptr<LayerImpl> CreateLayerImpl(LayerTreeImpl* tree_impl) override;
+
+ protected:
+  explicit DocumentTransitionContentLayer(
+      const viz::SharedElementResourceId& resource_id);
+
+ private:
+  ~DocumentTransitionContentLayer() override;
+
+  const viz::SharedElementResourceId resource_id_;
+};
+
+}  // namespace cc
+
+#endif  // CC_LAYERS_DOCUMENT_TRANSITION_CONTENT_LAYER_H_
diff --git a/cc/layers/shared_element_layer_impl.cc b/cc/layers/document_transition_content_layer_impl.cc
similarity index 66%
rename from cc/layers/shared_element_layer_impl.cc
rename to cc/layers/document_transition_content_layer_impl.cc
index 006e2d9..b1b58b75 100644
--- a/cc/layers/shared_element_layer_impl.cc
+++ b/cc/layers/document_transition_content_layer_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 "cc/layers/shared_element_layer_impl.h"
+#include "cc/layers/document_transition_content_layer_impl.h"
 
 #include "cc/layers/append_quads_data.h"
 #include "cc/layers/layer_impl.h"
@@ -13,29 +13,33 @@
 namespace cc {
 
 // static
-std::unique_ptr<SharedElementLayerImpl> SharedElementLayerImpl::Create(
+std::unique_ptr<DocumentTransitionContentLayerImpl>
+DocumentTransitionContentLayerImpl::Create(
     LayerTreeImpl* tree_impl,
     int id,
     const viz::SharedElementResourceId& resource_id) {
   return base::WrapUnique(
-      new SharedElementLayerImpl(tree_impl, id, resource_id));
+      new DocumentTransitionContentLayerImpl(tree_impl, id, resource_id));
 }
 
-SharedElementLayerImpl::SharedElementLayerImpl(
+DocumentTransitionContentLayerImpl::DocumentTransitionContentLayerImpl(
     LayerTreeImpl* tree_impl,
     int id,
     const viz::SharedElementResourceId& resource_id)
     : LayerImpl(tree_impl, id), resource_id_(resource_id) {}
 
-SharedElementLayerImpl::~SharedElementLayerImpl() = default;
+DocumentTransitionContentLayerImpl::~DocumentTransitionContentLayerImpl() =
+    default;
 
-std::unique_ptr<LayerImpl> SharedElementLayerImpl::CreateLayerImpl(
+std::unique_ptr<LayerImpl> DocumentTransitionContentLayerImpl::CreateLayerImpl(
     LayerTreeImpl* tree_impl) {
-  return SharedElementLayerImpl::Create(tree_impl, id(), resource_id_);
+  return DocumentTransitionContentLayerImpl::Create(tree_impl, id(),
+                                                    resource_id_);
 }
 
-void SharedElementLayerImpl::AppendQuads(viz::CompositorRenderPass* render_pass,
-                                         AppendQuadsData* append_quads_data) {
+void DocumentTransitionContentLayerImpl::AppendQuads(
+    viz::CompositorRenderPass* render_pass,
+    AppendQuadsData* append_quads_data) {
   float device_scale_factor = layer_tree_impl()->device_scale_factor();
 
   gfx::Rect quad_rect(
@@ -62,8 +66,8 @@
   append_quads_data->has_shared_element_resources = true;
 }
 
-const char* SharedElementLayerImpl::LayerTypeAsString() const {
-  return "cc::SharedElementLayerImpl";
+const char* DocumentTransitionContentLayerImpl::LayerTypeAsString() const {
+  return "cc::DocumentTransitionContentLayerImpl";
 }
 
 }  // namespace cc
diff --git a/cc/layers/document_transition_content_layer_impl.h b/cc/layers/document_transition_content_layer_impl.h
new file mode 100644
index 0000000..ab04477
--- /dev/null
+++ b/cc/layers/document_transition_content_layer_impl.h
@@ -0,0 +1,49 @@
+// 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.
+
+#ifndef CC_LAYERS_DOCUMENT_TRANSITION_CONTENT_LAYER_IMPL_H_
+#define CC_LAYERS_DOCUMENT_TRANSITION_CONTENT_LAYER_IMPL_H_
+
+#include <memory>
+
+#include "cc/cc_export.h"
+#include "cc/layers/layer_impl.h"
+#include "components/viz/common/shared_element_resource_id.h"
+
+namespace cc {
+
+class CC_EXPORT DocumentTransitionContentLayerImpl : public LayerImpl {
+ public:
+  static std::unique_ptr<DocumentTransitionContentLayerImpl> Create(
+      LayerTreeImpl* tree_impl,
+      int id,
+      const viz::SharedElementResourceId& resource_id);
+
+  DocumentTransitionContentLayerImpl(
+      const DocumentTransitionContentLayerImpl&) = delete;
+  ~DocumentTransitionContentLayerImpl() override;
+
+  DocumentTransitionContentLayerImpl& operator=(
+      const DocumentTransitionContentLayerImpl&) = delete;
+
+  // LayerImpl overrides.
+  std::unique_ptr<LayerImpl> CreateLayerImpl(LayerTreeImpl* tree_impl) override;
+  void AppendQuads(viz::CompositorRenderPass* render_pass,
+                   AppendQuadsData* append_quads_data) override;
+
+ protected:
+  DocumentTransitionContentLayerImpl(
+      LayerTreeImpl* tree_impl,
+      int id,
+      const viz::SharedElementResourceId& resource_id);
+
+ private:
+  const char* LayerTypeAsString() const override;
+
+  const viz::SharedElementResourceId resource_id_;
+};
+
+}  // namespace cc
+
+#endif  // CC_LAYERS_DOCUMENT_TRANSITION_CONTENT_LAYER_IMPL_H_
diff --git a/cc/layers/shared_element_layer.cc b/cc/layers/shared_element_layer.cc
deleted file mode 100644
index 2a4cc02..0000000
--- a/cc/layers/shared_element_layer.cc
+++ /dev/null
@@ -1,30 +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.
-
-#include "cc/layers/shared_element_layer.h"
-
-#include "cc/layers/shared_element_layer_impl.h"
-#include "cc/trees/layer_tree_host.h"
-
-#include "base/logging.h"
-
-namespace cc {
-
-scoped_refptr<SharedElementLayer> SharedElementLayer::Create(
-    const viz::SharedElementResourceId& resource_id) {
-  return base::WrapRefCounted(new SharedElementLayer(resource_id));
-}
-
-SharedElementLayer::SharedElementLayer(
-    const viz::SharedElementResourceId& resource_id)
-    : resource_id_(resource_id) {}
-
-SharedElementLayer::~SharedElementLayer() = default;
-
-std::unique_ptr<LayerImpl> SharedElementLayer::CreateLayerImpl(
-    LayerTreeImpl* tree_impl) {
-  return SharedElementLayerImpl::Create(tree_impl, id(), resource_id_);
-}
-
-}  // namespace cc
diff --git a/cc/layers/shared_element_layer.h b/cc/layers/shared_element_layer.h
deleted file mode 100644
index e11546b..0000000
--- a/cc/layers/shared_element_layer.h
+++ /dev/null
@@ -1,43 +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.
-
-#ifndef CC_LAYERS_SHARED_ELEMENT_LAYER_H_
-#define CC_LAYERS_SHARED_ELEMENT_LAYER_H_
-
-#include "base/logging.h"
-
-#include "cc/cc_export.h"
-#include "cc/layers/layer.h"
-#include "components/viz/common/shared_element_resource_id.h"
-
-namespace cc {
-
-// A layer that renders a texture cached in the Viz process.
-class CC_EXPORT SharedElementLayer : public Layer {
- public:
-  static scoped_refptr<SharedElementLayer> Create(
-      const viz::SharedElementResourceId& resource_id);
-
-  SharedElementLayer(const SharedElementLayer&) = delete;
-  SharedElementLayer& operator=(const SharedElementLayer&) = delete;
-
-  const viz::SharedElementResourceId& resource_id() const {
-    return resource_id_;
-  }
-
-  // Layer overrides.
-  std::unique_ptr<LayerImpl> CreateLayerImpl(LayerTreeImpl* tree_impl) override;
-
- protected:
-  explicit SharedElementLayer(const viz::SharedElementResourceId& resource_id);
-
- private:
-  ~SharedElementLayer() override;
-
-  const viz::SharedElementResourceId resource_id_;
-};
-
-}  // namespace cc
-
-#endif  // CC_LAYERS_SHARED_ELEMENT_LAYER_H_
diff --git a/cc/layers/shared_element_layer_impl.h b/cc/layers/shared_element_layer_impl.h
deleted file mode 100644
index 92359beac..0000000
--- a/cc/layers/shared_element_layer_impl.h
+++ /dev/null
@@ -1,46 +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.
-
-#ifndef CC_LAYERS_SHARED_ELEMENT_LAYER_IMPL_H_
-#define CC_LAYERS_SHARED_ELEMENT_LAYER_IMPL_H_
-
-#include <memory>
-
-#include "cc/cc_export.h"
-#include "cc/layers/layer_impl.h"
-#include "components/viz/common/shared_element_resource_id.h"
-
-namespace cc {
-
-class CC_EXPORT SharedElementLayerImpl : public LayerImpl {
- public:
-  static std::unique_ptr<SharedElementLayerImpl> Create(
-      LayerTreeImpl* tree_impl,
-      int id,
-      const viz::SharedElementResourceId& resource_id);
-
-  SharedElementLayerImpl(const SharedElementLayerImpl&) = delete;
-  ~SharedElementLayerImpl() override;
-
-  SharedElementLayerImpl& operator=(const SharedElementLayerImpl&) = delete;
-
-  // LayerImpl overrides.
-  std::unique_ptr<LayerImpl> CreateLayerImpl(LayerTreeImpl* tree_impl) override;
-  void AppendQuads(viz::CompositorRenderPass* render_pass,
-                   AppendQuadsData* append_quads_data) override;
-
- protected:
-  SharedElementLayerImpl(LayerTreeImpl* tree_impl,
-                         int id,
-                         const viz::SharedElementResourceId& resource_id);
-
- private:
-  const char* LayerTypeAsString() const override;
-
-  const viz::SharedElementResourceId resource_id_;
-};
-
-}  // namespace cc
-
-#endif  // CC_LAYERS_SHARED_ELEMENT_LAYER_IMPL_H_
diff --git a/cc/paint/oop_pixeltest.cc b/cc/paint/oop_pixeltest.cc
index ef52ee3..34bd340 100644
--- a/cc/paint/oop_pixeltest.cc
+++ b/cc/paint/oop_pixeltest.cc
@@ -8,8 +8,11 @@
 
 #include "base/bind.h"
 #include "base/command_line.h"
+#include "base/files/file_path.h"
 #include "base/memory/raw_ptr.h"
+#include "base/path_service.h"
 #include "base/strings/stringprintf.h"
+#include "base/test/test_switches.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "build/build_config.h"
 #include "cc/base/completion_event.h"
@@ -21,8 +24,10 @@
 #include "cc/paint/paint_image_builder.h"
 #include "cc/raster/playback_image_provider.h"
 #include "cc/raster/raster_source.h"
+#include "cc/test/pixel_comparator.h"
 #include "cc/test/pixel_test_utils.h"
 #include "cc/tiles/gpu_image_decode_cache.h"
+#include "components/viz/test/paths.h"
 #include "components/viz/test/test_in_process_context_provider.h"
 #include "gpu/GLES2/gl2extchromium.h"
 #include "gpu/command_buffer/client/gles2_implementation.h"
@@ -41,6 +46,7 @@
 #include "third_party/khronos/GLES2/gl2ext.h"
 #include "third_party/skia/include/core/SkGraphics.h"
 #include "third_party/skia/include/core/SkSurface.h"
+#include "third_party/skia/include/core/SkTextBlob.h"
 #include "third_party/skia/include/core/SkYUVAInfo.h"
 #include "third_party/skia/include/gpu/GrBackendSurface.h"
 #include "third_party/skia/include/gpu/GrDirectContext.h"
@@ -66,6 +72,14 @@
   return display_item_list;
 }
 
+// Creates a bitmap of |size| filled with pixels of |color|.
+SkBitmap MakeSolidColorBitmap(gfx::Size size, SkColor color) {
+  SkBitmap bitmap;
+  bitmap.allocPixels(SkImageInfo::MakeN32Premul(size.width(), size.height()));
+  bitmap.eraseColor(color);
+  return bitmap;
+}
+
 constexpr size_t kCacheLimitBytes = 1024 * 1024;
 
 class OopPixelTest : public testing::Test,
@@ -383,32 +397,30 @@
     return bitmap;
   }
 
-  void ExpectEquals(SkBitmap actual,
-                    SkBitmap expected,
-                    const char* label = nullptr) {
-    ExactPixelComparator exact(/* discard_alpha */ false);
-    ExpectEquals(actual, expected, exact, label);
+  // Verifies |actual| matches the expected PNG image.
+  void ExpectEquals(const SkBitmap& actual,
+                    const base::FilePath::StringType& ref_filename,
+                    const PixelComparator& comparator =
+                        ExactPixelComparator(/*discard_alpha=*/false)) {
+    base::FilePath test_data_dir;
+    ASSERT_TRUE(
+        base::PathService::Get(viz::Paths::DIR_TEST_DATA, &test_data_dir));
+
+    base::FilePath png_path = test_data_dir.Append(ref_filename);
+
+    auto* cmd = base::CommandLine::ForCurrentProcess();
+    if (cmd->HasSwitch(switches::kRebaselinePixelTests)) {
+      EXPECT_TRUE(WritePNGFile(actual, png_path, true));
+    } else {
+      EXPECT_TRUE(MatchesPNGFile(actual, png_path, comparator));
+    }
   }
 
   void ExpectEquals(SkBitmap actual,
                     SkBitmap expected,
-                    const PixelComparator& comparator,
-                    const char* label = nullptr) {
-    EXPECT_EQ(actual.dimensions(), expected.dimensions());
-
-    // We don't just use MatchesBitmap so that we can control logging output.
-    if (comparator.Compare(actual, expected))
-      return;
-
-    auto expected_url = GetPNGDataUrl(expected);
-    auto actual_url = GetPNGDataUrl(actual);
-    if (label) {
-      ADD_FAILURE() << "\nCase: " << label << "\nExpected: " << expected_url
-                    << "\nActual:   " << actual_url;
-    } else {
-      ADD_FAILURE() << "\nExpected: " << expected_url
-                    << "\nActual:   " << actual_url;
-    }
+                    const PixelComparator& comparator =
+                        ExactPixelComparator(/*discard_alpha=*/false)) {
+    EXPECT_TRUE(MatchesBitmap(actual, expected, comparator));
   }
 
  protected:
@@ -453,18 +465,10 @@
   display_item_list->EndPaintOfUnpaired(rect);
   display_item_list->Finalize();
 
-  std::vector<SkPMColor> expected_pixels(rect.width() * rect.height(),
-                                         SkPreMultiplyARGB(255, 0, 0, 255));
-  SkBitmap expected;
-  expected.installPixels(
-      SkImageInfo::MakeN32Premul(rect.width(), rect.height()),
-      expected_pixels.data(), rect.width() * sizeof(SkColor));
+  SkBitmap expected = MakeSolidColorBitmap(rect.size(), SK_ColorBLUE);
 
-  auto actual_oop = Raster(display_item_list, rect.size());
-  ExpectEquals(actual_oop, expected, "oop");
-
-  auto actual_gpu = RasterExpectedBitmap(display_item_list, rect.size());
-  ExpectEquals(actual_gpu, expected, "gpu");
+  auto actual = Raster(display_item_list, rect.size());
+  ExpectEquals(actual, expected);
 }
 
 TEST_F(OopPixelTest, DrawColorWithTargetColorSpace) {
@@ -480,12 +484,11 @@
   RasterOptions options(rect.size());
   options.color_space = target_color_space;
 
-  auto actual = Raster(display_item_list, options);
-  auto expected = RasterExpectedBitmap(display_item_list, options);
-  ExpectEquals(actual, expected);
+  SkBitmap expected =
+      MakeSolidColorBitmap(rect.size(), SkColorSetARGB(255, 38, 15, 221));
 
-  // Verify conversion.
-  EXPECT_EQ(SkColorSetARGB(255, 38, 15, 221), expected.getColor(0, 0));
+  auto actual = Raster(display_item_list, options);
+  ExpectEquals(actual, expected);
 }
 
 TEST_F(OopPixelTest, DrawRect) {
@@ -572,10 +575,8 @@
       SkIRect::MakeLTRB(output_size.width() / 2, output_size.height() / 2,
                         output_size.width(), output_size.height()));
 
-  auto actual_oop = Raster(display_item_list, output_size);
-  auto actual_gpu = RasterExpectedBitmap(display_item_list, output_size);
-  ExpectEquals(actual_oop, expected);
-  ExpectEquals(actual_gpu, expected);
+  auto actual = Raster(display_item_list, output_size);
+  ExpectEquals(actual, expected);
 }
 
 TEST_P(OopImagePixelTest, DrawImage) {
@@ -613,10 +614,7 @@
   display_item_list->Finalize();
 
   auto actual = Raster(display_item_list, rect.size());
-  auto expected = RasterExpectedBitmap(display_item_list, rect.size());
-  ExpectEquals(actual, expected);
-
-  EXPECT_EQ(actual.getColor(0, 0), SK_ColorMAGENTA);
+  ExpectEquals(actual, FILE_PATH_LITERAL("oop_draw_image.png"));
 }
 
 TEST_P(OopImagePixelTest, DrawImageScaled) {
@@ -1052,9 +1050,7 @@
 
   auto actual = Raster(display_item_list, options);
 
-  options.preclear = false;
-  options.background_color = SK_ColorGREEN;
-  auto expected = RasterExpectedBitmap(display_item_list, options);
+  auto expected = MakeSolidColorBitmap(rect.size(), SK_ColorGREEN);
   ExpectEquals(actual, expected);
 }
 
@@ -1087,7 +1083,6 @@
   auto display_item_list = MakeNoopDisplayItemList();
 
   auto oop_result = Raster(display_item_list, options);
-  auto gpu_result = RasterExpectedBitmap(display_item_list, options);
 
   SkBitmap bitmap;
   bitmap.allocPixelsFlags(
@@ -1110,8 +1105,7 @@
     canvas.drawRect(SkRect::MakeXYWH(0, 6, 9, 2), green);
   }
 
-  ExpectEquals(oop_result, bitmap, "oop");
-  ExpectEquals(gpu_result, bitmap, "gpu");
+  ExpectEquals(oop_result, bitmap);
 }
 
 TEST_F(OopPixelTest, ClearingOpaqueCornerExactEdge) {
@@ -1137,7 +1131,6 @@
   auto display_item_list = MakeNoopDisplayItemList();
 
   auto oop_result = Raster(display_item_list, options);
-  auto gpu_result = RasterExpectedBitmap(display_item_list, options);
 
   SkBitmap bitmap;
   bitmap.allocPixelsFlags(
@@ -1153,8 +1146,7 @@
   canvas.drawRect(SkRect::MakeXYWH(9, 0, 1, 10), green);
   canvas.drawRect(SkRect::MakeXYWH(0, 9, 10, 1), green);
 
-  ExpectEquals(oop_result, bitmap, "oop");
-  ExpectEquals(gpu_result, bitmap, "gpu");
+  ExpectEquals(oop_result, bitmap);
 }
 
 TEST_F(OopPixelTest, ClearingOpaqueCornerPartialRaster) {
@@ -1182,7 +1174,6 @@
   auto display_item_list = MakeNoopDisplayItemList();
 
   auto oop_result = Raster(display_item_list, options);
-  auto gpu_result = RasterExpectedBitmap(display_item_list, options);
 
   SkBitmap bitmap;
   bitmap.allocPixelsFlags(
@@ -1194,8 +1185,7 @@
   SkCanvas canvas(bitmap, SkSurfaceProps{});
   canvas.drawColor(options.preclear_color);
 
-  ExpectEquals(oop_result, bitmap, "oop");
-  ExpectEquals(gpu_result, bitmap, "gpu");
+  ExpectEquals(oop_result, bitmap);
 }
 
 TEST_P(OopClearPixelTest, ClearingOpaqueLeftEdge) {
@@ -1229,7 +1219,6 @@
   auto display_item_list = MakeNoopDisplayItemList();
 
   auto oop_result = Raster(display_item_list, options);
-  auto gpu_result = RasterExpectedBitmap(display_item_list, options);
 
   SkBitmap bitmap;
   bitmap.allocPixelsFlags(
@@ -1250,8 +1239,7 @@
     canvas.drawRect(SkRect::MakeXYWH(0, 0, 1, 10), green);
   }
 
-  ExpectEquals(oop_result, bitmap, "oop");
-  ExpectEquals(gpu_result, bitmap, "gpu");
+  ExpectEquals(oop_result, bitmap);
 }
 
 TEST_P(OopClearPixelTest, ClearingOpaqueRightEdge) {
@@ -1286,7 +1274,6 @@
   auto display_item_list = MakeNoopDisplayItemList();
 
   auto oop_result = Raster(display_item_list, options);
-  auto gpu_result = RasterExpectedBitmap(display_item_list, options);
 
   SkBitmap bitmap;
   bitmap.allocPixelsFlags(
@@ -1307,8 +1294,7 @@
     canvas.drawRect(SkRect::MakeXYWH(2, 0, 2, 10), green);
   }
 
-  ExpectEquals(oop_result, bitmap, "oop");
-  ExpectEquals(gpu_result, bitmap, "gpu");
+  ExpectEquals(oop_result, bitmap);
 }
 
 TEST_P(OopClearPixelTest, ClearingOpaqueTopEdge) {
@@ -1342,7 +1328,6 @@
   auto display_item_list = MakeNoopDisplayItemList();
 
   auto oop_result = Raster(display_item_list, options);
-  auto gpu_result = RasterExpectedBitmap(display_item_list, options);
 
   SkBitmap bitmap;
   bitmap.allocPixelsFlags(
@@ -1364,8 +1349,7 @@
     canvas.drawRect(SkRect::MakeXYWH(0, 0, 10, 1), green);
   }
 
-  ExpectEquals(oop_result, bitmap, "oop");
-  ExpectEquals(gpu_result, bitmap, "gpu");
+  ExpectEquals(oop_result, bitmap);
 }
 
 TEST_P(OopClearPixelTest, ClearingOpaqueBottomEdge) {
@@ -1400,7 +1384,6 @@
   auto display_item_list = MakeNoopDisplayItemList();
 
   auto oop_result = Raster(display_item_list, options);
-  auto gpu_result = RasterExpectedBitmap(display_item_list, options);
 
   SkBitmap bitmap;
   bitmap.allocPixelsFlags(
@@ -1422,8 +1405,7 @@
     canvas.drawRect(SkRect::MakeXYWH(0, 4, 10, 2), green);
   }
 
-  ExpectEquals(oop_result, bitmap, "oop");
-  ExpectEquals(gpu_result, bitmap, "gpu");
+  ExpectEquals(oop_result, bitmap);
 }
 
 TEST_F(OopPixelTest, ClearingOpaqueInternal) {
@@ -1448,7 +1430,6 @@
   auto display_item_list = MakeNoopDisplayItemList();
 
   auto oop_result = Raster(display_item_list, options);
-  auto gpu_result = RasterExpectedBitmap(display_item_list, options);
 
   SkBitmap bitmap;
   bitmap.allocPixelsFlags(
@@ -1461,8 +1442,7 @@
   SkCanvas canvas(bitmap, SkSurfaceProps{});
   canvas.drawColor(options.preclear_color);
 
-  ExpectEquals(oop_result, bitmap, "oop");
-  ExpectEquals(gpu_result, bitmap, "gpu");
+  ExpectEquals(oop_result, bitmap);
 }
 
 TEST_F(OopPixelTest, ClearingTransparentCorner) {
@@ -1484,7 +1464,6 @@
   auto display_item_list = MakeNoopDisplayItemList();
 
   auto oop_result = Raster(display_item_list, options);
-  auto gpu_result = RasterExpectedBitmap(display_item_list, options);
 
   // Because this is rastering the entire tile, clear the entire thing
   // even if the full raster rect doesn't cover the whole resource.
@@ -1497,8 +1476,7 @@
   SkCanvas canvas(bitmap, SkSurfaceProps{});
   canvas.drawColor(SK_ColorTRANSPARENT);
 
-  ExpectEquals(oop_result, bitmap, "oop");
-  ExpectEquals(gpu_result, bitmap, "gpu");
+  ExpectEquals(oop_result, bitmap);
 }
 
 TEST_F(OopPixelTest, ClearingTransparentInternalTile) {
@@ -1524,7 +1502,6 @@
   auto display_item_list = base::MakeRefCounted<DisplayItemList>();
 
   auto oop_result = Raster(display_item_list, options);
-  auto gpu_result = RasterExpectedBitmap(display_item_list, options);
 
   // Because this is rastering the entire tile, clear the entire thing
   // even if the full raster rect doesn't cover the whole resource.
@@ -1537,8 +1514,7 @@
   SkCanvas canvas(bitmap, SkSurfaceProps{});
   canvas.drawColor(SK_ColorTRANSPARENT);
 
-  ExpectEquals(oop_result, bitmap, "oop");
-  ExpectEquals(gpu_result, bitmap, "gpu");
+  ExpectEquals(oop_result, bitmap);
 }
 
 TEST_F(OopPixelTest, ClearingTransparentCornerPartialRaster) {
@@ -1561,7 +1537,6 @@
   auto display_item_list = MakeNoopDisplayItemList();
 
   auto oop_result = Raster(display_item_list, options);
-  auto gpu_result = RasterExpectedBitmap(display_item_list, options);
 
   SkBitmap bitmap;
   bitmap.allocPixelsFlags(
@@ -1577,14 +1552,11 @@
   canvas.clipRect(gfx::RectToSkRect(options.playback_rect));
   canvas.drawColor(SK_ColorTRANSPARENT, SkBlendMode::kSrc);
 
-  ExpectEquals(oop_result, bitmap, "oop");
-  ExpectEquals(gpu_result, bitmap, "gpu");
+  ExpectEquals(oop_result, bitmap);
 }
 
-// Test various bitmap and playback rects in the raster options, to verify
-// that in process (RasterSource) and out of process (GLES2Implementation)
-// raster behave identically.
-TEST_F(OopPixelTest, DrawRectBasicRasterOptions) {
+// Test bitmap and playback rects in the raster options.
+TEST_F(OopPixelTest, DrawRectPlaybackRect) {
   PaintFlags flags;
   flags.setColor(SkColorSetARGB(255, 250, 10, 20));
   gfx::Rect draw_rect(3, 1, 8, 9);
@@ -1595,26 +1567,16 @@
   display_item_list->EndPaintOfUnpaired(draw_rect);
   display_item_list->Finalize();
 
-  std::vector<std::pair<gfx::Rect, gfx::Rect>> input = {
-      {{0, 0, 10, 10}, {0, 0, 10, 10}},
-      {{1, 2, 10, 10}, {4, 2, 5, 6}},
-      {{5, 5, 15, 10}, {5, 5, 10, 10}}};
+  RasterOptions options;
+  options.full_raster_rect = gfx::Rect(1, 2, 10, 10);
+  options.resource_size = options.full_raster_rect.size();
+  options.content_size = gfx::Size(options.full_raster_rect.right(),
+                                   options.full_raster_rect.bottom());
+  options.playback_rect = gfx::Rect(4, 2, 5, 6);
+  options.background_color = SK_ColorMAGENTA;
 
-  for (size_t i = 0; i < input.size(); ++i) {
-    SCOPED_TRACE(base::StringPrintf("Case %zd", i));
-
-    RasterOptions options;
-    options.resource_size = input[i].first.size(),
-    options.full_raster_rect = input[i].first;
-    options.content_size = gfx::Size(options.full_raster_rect.right(),
-                                     options.full_raster_rect.bottom());
-    options.playback_rect = input[i].second;
-    options.background_color = SK_ColorMAGENTA;
-
-    auto actual = Raster(display_item_list, options);
-    auto expected = RasterExpectedBitmap(display_item_list, options);
-    ExpectEquals(actual, expected);
-  }
+  auto actual = Raster(display_item_list, options);
+  ExpectEquals(actual, FILE_PATH_LITERAL("oop_draw_rect_playback_rect.png"));
 }
 
 TEST_F(OopPixelTest, DrawRectScaleTransformOptions) {
@@ -1644,8 +1606,7 @@
   options.post_scale = 2.f;
 
   auto actual = Raster(display_item_list, options);
-  auto expected = RasterExpectedBitmap(display_item_list, options);
-  ExpectEquals(actual, expected);
+  ExpectEquals(actual, FILE_PATH_LITERAL("oop_draw_rect_scale_transform.png"));
 }
 
 TEST_F(OopPixelTest, DrawRectTransformOptionsFullRaster) {
@@ -1677,7 +1638,9 @@
   options.post_scale = 2.f;
 
   auto actual = Raster(display_item_list, options);
-  auto expected = RasterExpectedBitmap(display_item_list, options);
+  auto expected = MakeSolidColorBitmap(options.resource_size,
+                                       SkColorSetARGB(255, 64, 128, 32));
+
   ExpectEquals(actual, expected);
 }
 
@@ -1710,8 +1673,7 @@
   options.post_scale = 2.f;
 
   auto actual = Raster(display_item_list, options);
-  auto expected = RasterExpectedBitmap(display_item_list, options);
-  ExpectEquals(actual, expected);
+  ExpectEquals(actual, FILE_PATH_LITERAL("oop_draw_rect_query.png"));
 }
 
 TEST_F(OopPixelTest, DrawRectColorSpace) {
@@ -1732,8 +1694,10 @@
   display_item_list->EndPaintOfUnpaired(options.full_raster_rect);
   display_item_list->Finalize();
 
+  SkBitmap expected = MakeSolidColorBitmap(options.resource_size,
+                                           SkColorSetARGB(255, 117, 251, 76));
+
   auto actual = Raster(display_item_list, options);
-  auto expected = RasterExpectedBitmap(display_item_list, options);
   ExpectEquals(actual, expected);
 }
 
@@ -2431,9 +2395,8 @@
         /*avg_abs_error_limit=*/255,
         /*max_abs_error_limit=*/255,
         /*small_error_threshold=*/0);
-    auto expected = RasterExpectedBitmap(display_item_list, options);
     auto actual = Raster(display_item_list, options);
-    ExpectEquals(actual, expected, comparator);
+    ExpectEquals(actual, FILE_PATH_LITERAL("oop_path.png"), comparator);
   }
 };
 
@@ -2470,9 +2433,9 @@
   display_item_list->EndPaintOfUnpaired(options.full_raster_rect);
   display_item_list->Finalize();
 
-  auto expected = RasterExpectedBitmap(display_item_list, options);
   auto actual = Raster(display_item_list, options);
-  ExpectEquals(actual, expected);
+  ExpectEquals(actual,
+               FILE_PATH_LITERAL("oop_record_shader_max_texture_size.png"));
 }
 
 INSTANTIATE_TEST_SUITE_P(P, OopImagePixelTest, ::testing::Bool());
diff --git a/cc/tiles/tiling_set_raster_queue_all.h b/cc/tiles/tiling_set_raster_queue_all.h
index d4af6c9..92fd6c67 100644
--- a/cc/tiles/tiling_set_raster_queue_all.h
+++ b/cc/tiles/tiling_set_raster_queue_all.h
@@ -8,7 +8,6 @@
 #include <stddef.h>
 
 #include "base/containers/stack_container.h"
-#include "base/memory/raw_ptr.h"
 #include "cc/cc_export.h"
 #include "cc/tiles/picture_layer_tiling_set.h"
 #include "cc/tiles/prioritized_tile.h"
@@ -195,7 +194,9 @@
   void MakeTilingIterator(IteratorType type, PictureLayerTiling* tiling);
   void AdvanceToNextStage();
 
-  raw_ptr<PictureLayerTilingSet> tiling_set_;
+  // `tiling_set_` is not a raw_ptr<...> for performance reasons (based on
+  // analysis of sampling profiler data).
+  PictureLayerTilingSet* tiling_set_;
 
   struct IterationStage {
     IterationStage(IteratorType type, TilePriority::PriorityBin bin);
diff --git a/chrome/BUILD.gn b/chrome/BUILD.gn
index ce3de57..cf5ccbd 100644
--- a/chrome/BUILD.gn
+++ b/chrome/BUILD.gn
@@ -909,8 +909,7 @@
       if (use_v8_context_snapshot) {
         sources += [ "$root_out_dir/$v8_context_snapshot_filename" ]
         public_deps += [ "//tools/v8_context_snapshot" ]
-      }
-      if (!use_v8_context_snapshot || include_both_v8_snapshots) {
+      } else {
         sources += [ "$root_out_dir/snapshot_blob.bin" ]
       }
     }
diff --git a/chrome/VERSION b/chrome/VERSION
index 8c3e409..31ae570 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=99
 MINOR=0
-BUILD=4838
+BUILD=4839
 PATCH=0
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index 26fdc0dd7..9bec4cc 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -1890,12 +1890,10 @@
 }
 
 java_group("chrome_public_v8_assets") {
-  deps = []
   if (use_v8_context_snapshot) {
-    deps += [ "//tools/v8_context_snapshot:v8_context_snapshot_assets" ]
-  }
-  if (!use_v8_context_snapshot || include_both_v8_snapshots) {
-    deps += [ "//v8:v8_external_startup_data_assets" ]
+    deps = [ "//tools/v8_context_snapshot:v8_context_snapshot_assets" ]
+  } else {
+    deps = [ "//v8:v8_external_startup_data_assets" ]
   }
 }
 
diff --git a/chrome/android/chrome_public_apk_tmpl.gni b/chrome/android/chrome_public_apk_tmpl.gni
index a208e00..d6c610e1 100644
--- a/chrome/android/chrome_public_apk_tmpl.gni
+++ b/chrome/android/chrome_public_apk_tmpl.gni
@@ -214,7 +214,8 @@
       # expensive tasks (eg: proguarding, resource optimization) to be run in
       # parallel by adding dependencies between them (adds around 10-20
       # seconds on my machine).
-      strip_unused_resources = is_official_build
+      # TODO(crbug.com/1287322): Re-enabled and add bot coverage.
+      strip_unused_resources = false
     }
 
     if (!defined(aapt_locale_allowlist)) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperManager.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperManager.java
index 82a327e3..5949e005 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperManager.java
@@ -400,8 +400,11 @@
 
             @Override
             public void didMoveTab(Tab tab, int newIndex, int curIndex) {
+                // For right-direction move, layout helper re-ordering logic
+                // expects destination index = position + 1
                 getStripLayoutHelper(tab.isIncognito())
-                        .tabMoved(time(), tab.getId(), curIndex, newIndex);
+                        .tabMoved(time(), tab.getId(), curIndex,
+                                newIndex > curIndex ? newIndex + 1 : newIndex);
             }
 
             @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/DEPS b/chrome/android/java/src/org/chromium/chrome/browser/tab/DEPS
index 05938e2..d1b637cd 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/DEPS
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/DEPS
@@ -32,6 +32,7 @@
   "+chrome/browser/contextmenu/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuPopulator.java",
   "+chrome/browser/contextmenu/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuPopulatorFactory.java",
   "+chrome/browser/continuous_search/android/java/org/chromium/chrome/browser/continuous_search/ContinuousSearchTabHelper.java",
+  "+chrome/browser/password_check/android/java/src/org/chromium/chrome/browser/password_check/PasswordCheckUkmRecorder.java",
   "+chrome/browser/profiles/android/java/src/org/chromium/chrome/browser/profiles/Profile.java",
   "+chrome/browser/ui/messages/android/java/src/org/chromium/chrome/browser/ui/messages/snackbar/SnackbarManager.java",
   "+chrome/browser/feature_engagement/java/src/org/chromium/chrome/browser/feature_engagement/TrackerFactory.java",
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabHelpers.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabHelpers.java
index 1a771c3..5f28dfdf 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabHelpers.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabHelpers.java
@@ -14,6 +14,7 @@
 import org.chromium.chrome.browser.dom_distiller.TabDistillabilityProvider;
 import org.chromium.chrome.browser.infobar.InfoBarContainer;
 import org.chromium.chrome.browser.media.ui.MediaSessionTabHelper;
+import org.chromium.chrome.browser.password_check.PasswordCheckUkmRecorder;
 import org.chromium.chrome.browser.tab.state.ShoppingPersistedTabData;
 import org.chromium.chrome.browser.tasks.tab_management.PriceTrackingUtilities;
 /**
@@ -38,6 +39,7 @@
         ContinuousSearchTabHelper.createForTab(tab);
         if (ReaderModeManager.isEnabled()) ReaderModeManager.createForTab(tab);
         AutofillAssistantTabHelper.createForTab(tab);
+        PasswordCheckUkmRecorder.createForTab(tab);
 
         // The following will start prefetching data for the price drops feature, so
         // we should only do it if the user is eligible for the feature (e.g. has sync enabled).
diff --git a/chrome/android/modules/chrome_bundle_tmpl.gni b/chrome/android/modules/chrome_bundle_tmpl.gni
index 201937a..5087fda 100644
--- a/chrome/android/modules/chrome_bundle_tmpl.gni
+++ b/chrome/android/modules/chrome_bundle_tmpl.gni
@@ -174,7 +174,8 @@
     validate_services = _enable_chrome_module
 
     # For this to be respected, it must also be set on the base module target.
-    strip_unused_resources = is_official_build
+    # TODO(crbug.com/1287322): Re-enabled and add bot coverage.
+    strip_unused_resources = false
 
     # List of DFMs that are installed by default by wrapper scripts, to make
     # testing easier. This removes the need to manually specify, e.g.,
diff --git a/chrome/app/chrome_crash_reporter_client.cc b/chrome/app/chrome_crash_reporter_client.cc
index 4ef92dc0..8bd4fa3 100644
--- a/chrome/app/chrome_crash_reporter_client.cc
+++ b/chrome/app/chrome_crash_reporter_client.cc
@@ -165,7 +165,7 @@
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
   bool is_guest_session = base::CommandLine::ForCurrentProcess()->HasSwitch(
-      chromeos::switches::kGuestSession);
+      ash::switches::kGuestSession);
   bool is_stable_channel =
       chrome::GetChannel() == version_info::Channel::STABLE;
 
diff --git a/chrome/app/chrome_main_delegate.cc b/chrome/app/chrome_main_delegate.cc
index 483bc3a6..0a25150 100644
--- a/chrome/app/chrome_main_delegate.cc
+++ b/chrome/app/chrome_main_delegate.cc
@@ -869,9 +869,9 @@
   // Initialize primary user homedir (in multi-profile session) as it may be
   // passed as a command line switch.
   base::FilePath homedir;
-  if (command_line.HasSwitch(chromeos::switches::kHomedir)) {
+  if (command_line.HasSwitch(ash::switches::kHomedir)) {
     homedir = base::FilePath(
-        command_line.GetSwitchValueASCII(chromeos::switches::kHomedir));
+        command_line.GetSwitchValueASCII(ash::switches::kHomedir));
     base::PathService::OverrideAndCreateIfNeeded(base::DIR_HOME, homedir, true,
                                                  false);
   }
@@ -881,7 +881,7 @@
   // up a command line to tell it that we want it to recover, and to preserve
   // the original command line. Note: logging at this point is to /var/log/ui.
   if ((base::SysInfo::IsRunningOnChromeOS() &&
-       command_line.HasSwitch(chromeos::switches::kLoginUser)) ||
+       command_line.HasSwitch(ash::switches::kLoginUser)) ||
       command_line.HasSwitch(switches::kDiagnosticsRecovery)) {
     base::CommandLine interim_command_line(command_line.GetProgram());
     const char* const kSwitchNames[] = {switches::kUserDataDir, };
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index ba42c1d..a7c8128 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -37,6 +37,7 @@
 #include "chrome/browser/ash/android_sms/android_sms_switches.h"
 #include "chrome/browser/browser_features.h"
 #include "chrome/browser/commerce/commerce_feature_list.h"
+#include "chrome/browser/feature_guide/notifications/feature_notification_guide_service.h"
 #include "chrome/browser/flag_descriptions.h"
 #include "chrome/browser/login_detection/login_detection_util.h"
 #include "chrome/browser/navigation_predictor/navigation_predictor_features.h"
@@ -429,7 +430,7 @@
     {flag_descriptions::kUpdateMenuTypeUnsupportedOSVersion,
      switches::kForceUpdateMenuType, "unsupported_os_version"},
 };
-#else  // !defined(OS_ANDROID)
+#else   // !defined(OS_ANDROID)
 const FeatureEntry::FeatureParam kReaderModeOfferInSettings[] = {
     {switches::kReaderModeDiscoverabilityParamName,
      switches::kReaderModeOfferInSettings}};
@@ -2420,27 +2421,6 @@
      base::size(kCheckOfflineCapabilityEnforce), nullptr},
 };
 
-const FeatureEntry::FeatureParam kSubresourceRedirectPublicImageHints[] = {
-    {"enable_public_image_hints_based_compression", "true"},
-    {"enable_subresource_server_redirect", "true"},
-    {"enable_login_robots_based_compression", "false"},
-};
-
-const FeatureEntry::FeatureParam
-    kSubresourceRedirectLoginRobotsBasedCompression[] = {
-        {"enable_login_robots_based_compression", "true"},
-        {"enable_subresource_server_redirect", "true"},
-        {"enable_public_image_hints_based_compression", "false"},
-};
-
-const FeatureEntry::FeatureVariation kSubresourceRedirectVariations[] = {
-    {"Public image hints based compression",
-     kSubresourceRedirectPublicImageHints,
-     base::size(kSubresourceRedirectPublicImageHints), nullptr},
-    {"robots.txt allowed image compression in non logged-in pages",
-     kSubresourceRedirectLoginRobotsBasedCompression,
-     base::size(kSubresourceRedirectLoginRobotsBasedCompression), nullptr}};
-
 #if BUILDFLAG(IS_CHROMEOS_ASH)
 const FeatureEntry::FeatureParam kCategoricalSearch_Unranked[] = {
     {"ranking", "none"}};
@@ -3171,6 +3151,10 @@
     {"esim-policy", flag_descriptions::kESimPolicyName,
      flag_descriptions::kESimPolicyDescription, kOsCrOS,
      FEATURE_VALUE_TYPE(chromeos::features::kESimPolicy)},
+    {"extended-open-vpn-settings",
+     flag_descriptions::kExtendedOpenVpnSettingsName,
+     flag_descriptions::kExtendedOpenVpnSettingsDescription, kOsCrOS,
+     FEATURE_VALUE_TYPE(ash::features::kExtendedOpenVpnSettings)},
     {"dns-proxy-enable-doh", flag_descriptions::kDnsProxyEnableDOHName,
      flag_descriptions::kDnsProxyEnableDOHDescription, kOsCrOS,
      FEATURE_VALUE_TYPE(::features::kDnsProxyEnableDOH)},
@@ -3205,7 +3189,7 @@
      FEATURE_VALUE_TYPE(ash::features::kHideShelfControlsInTabletMode)},
     {"shelf-hover-previews", flag_descriptions::kShelfHoverPreviewsName,
      flag_descriptions::kShelfHoverPreviewsDescription, kOsCrOS,
-     SINGLE_VALUE_TYPE(chromeos::switches::kShelfHoverPreviews)},
+     SINGLE_VALUE_TYPE(ash::switches::kShelfHoverPreviews)},
     {"show-bluetooth-debug-log-toggle",
      flag_descriptions::kShowBluetoothDebugLogToggleName,
      flag_descriptions::kShowBluetoothDebugLogToggleDescription, kOsCrOS,
@@ -3653,12 +3637,6 @@
     {"enable-use-zoom-for-dsf", flag_descriptions::kEnableUseZoomForDsfName,
      flag_descriptions::kEnableUseZoomForDsfDescription, kOsAll,
      MULTI_VALUE_TYPE(kEnableUseZoomForDSFChoices)},
-    {"enable-subresource-redirect",
-     flag_descriptions::kEnableSubresourceRedirectName,
-     flag_descriptions::kEnableSubresourceRedirectDescription, kOsAll,
-     FEATURE_WITH_PARAMS_VALUE_TYPE(blink::features::kSubresourceRedirect,
-                                    kSubresourceRedirectVariations,
-                                    "SubresourceRedirect")},
     {"enable-login-detection", flag_descriptions::kEnableLoginDetectionName,
      flag_descriptions::kEnableLoginDetectionDescription, kOsAll,
      FEATURE_VALUE_TYPE(login_detection::kLoginDetection)},
@@ -3884,6 +3862,10 @@
      FEATURE_WITH_PARAMS_VALUE_TYPE(chrome::android::kAddToHomescreenIPH,
                                     kAddToHomescreenIPHVariations,
                                     "AddToHomescreen")},
+    {"feature-notification-guide",
+     flag_descriptions::kFeatureNotificationGuideName,
+     flag_descriptions::kFeatureNotificationGuideDescription, kOsAll,
+     FEATURE_VALUE_TYPE(feature_guide::features::kFeatureNotificationGuide)},
     {"offline-pages-live-page-sharing",
      flag_descriptions::kOfflinePagesLivePageSharingName,
      flag_descriptions::kOfflinePagesLivePageSharingDescription, kOsAndroid,
@@ -4354,7 +4336,7 @@
     {"enable-touchscreen-calibration",
      flag_descriptions::kTouchscreenCalibrationName,
      flag_descriptions::kTouchscreenCalibrationDescription, kOsCrOS,
-     SINGLE_VALUE_TYPE(chromeos::switches::kEnableTouchCalibrationSetting)},
+     SINGLE_VALUE_TYPE(ash::switches::kEnableTouchCalibrationSetting)},
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 #if BUILDFLAG(IS_CHROMEOS_ASH)
     {"audio-url", flag_descriptions::kAudioUrlName,
@@ -6451,6 +6433,9 @@
      flag_descriptions::kNearbySharingSelfShareName,
      flag_descriptions::kNearbySharingSelfShareDescription, kOsCrOS,
      FEATURE_VALUE_TYPE(features::kNearbySharingSelfShare)},
+    {"nearby-sharing-wifilan", flag_descriptions::kNearbySharingWifiLanName,
+     flag_descriptions::kNearbySharingWifiLanDescription, kOsCrOS,
+     FEATURE_VALUE_TYPE(features::kNearbySharingWifiLan)},
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
diff --git a/chrome/browser/android/tab_web_contents_delegate_android.cc b/chrome/browser/android/tab_web_contents_delegate_android.cc
index f0ab922b..94158b7f 100644
--- a/chrome/browser/android/tab_web_contents_delegate_android.cc
+++ b/chrome/browser/android/tab_web_contents_delegate_android.cc
@@ -285,14 +285,6 @@
   return javascript_dialogs::AppModalDialogManager::GetInstance();
 }
 
-void TabWebContentsDelegateAndroid::AdjustPreviewsStateForNavigation(
-    content::WebContents* web_contents,
-    blink::PreviewsState* previews_state) {
-  if (GetDisplayMode(web_contents) != blink::mojom::DisplayMode::kBrowser) {
-    *previews_state = blink::PreviewsTypes::PREVIEWS_OFF;
-  }
-}
-
 void TabWebContentsDelegateAndroid::RequestMediaAccessPermission(
     content::WebContents* web_contents,
     const content::MediaStreamRequest& request,
diff --git a/chrome/browser/android/tab_web_contents_delegate_android.h b/chrome/browser/android/tab_web_contents_delegate_android.h
index 2a3022a..3caa43a9 100644
--- a/chrome/browser/android/tab_web_contents_delegate_android.h
+++ b/chrome/browser/android/tab_web_contents_delegate_android.h
@@ -68,9 +68,6 @@
                            const gfx::RectF& active_rect) override;
   content::JavaScriptDialogManager* GetJavaScriptDialogManager(
       content::WebContents* source) override;
-  void AdjustPreviewsStateForNavigation(
-      content::WebContents* web_contents,
-      blink::PreviewsState* previews_state) override;
   void RequestMediaAccessPermission(
       content::WebContents* web_contents,
       const content::MediaStreamRequest& request,
diff --git a/chrome/browser/apps/intent_helper/chromeos_intent_picker_helpers.cc b/chrome/browser/apps/intent_helper/chromeos_intent_picker_helpers.cc
index 5be304bc..85b8cf1 100644
--- a/chrome/browser/apps/intent_helper/chromeos_intent_picker_helpers.cc
+++ b/chrome/browser/apps/intent_helper/chromeos_intent_picker_helpers.cc
@@ -65,7 +65,7 @@
   // picker if Chrome has been chosen by the user as the platform for this URL.
   // TODO(crbug.com/1225828): Handle this for lacros-chrome as well.
 #if BUILDFLAG(IS_CHROMEOS_ASH)
-  if (chromeos::switches::IsTabletFormFactor()) {
+  if (ash::switches::IsTabletFormFactor()) {
     if (ui_auto_display_service->GetLastUsedPlatformForTablets(url) ==
         IntentPickerAutoDisplayPref::Platform::kChrome) {
       return false;
@@ -163,7 +163,7 @@
     bool should_persist) {
 // TODO(crbug.com/1225828): Handle this for lacros-chrome as well.
 #if BUILDFLAG(IS_CHROMEOS_ASH)
-  if (chromeos::switches::IsTabletFormFactor() && should_persist) {
+  if (ash::switches::IsTabletFormFactor() && should_persist) {
     // On devices of tablet form factor, until the user has decided to persist
     // the setting, the browser-side intent picker should always be seen.
     auto platform = IntentPickerAutoDisplayPref::Platform::kNone;
diff --git a/chrome/browser/ash/app_mode/kiosk_app_manager.cc b/chrome/browser/ash/app_mode/kiosk_app_manager.cc
index 6208911..d573286 100644
--- a/chrome/browser/ash/app_mode/kiosk_app_manager.cc
+++ b/chrome/browser/ash/app_mode/kiosk_app_manager.cc
@@ -274,9 +274,9 @@
   // should not be present for kiosk sessions.
   bool in_policy_switches_block = false;
   const std::string policy_switches_begin =
-      GetSwitchString(switches::kPolicySwitchesBegin);
+      GetSwitchString(chromeos::switches::kPolicySwitchesBegin);
   const std::string policy_switches_end =
-      GetSwitchString(switches::kPolicySwitchesEnd);
+      GetSwitchString(chromeos::switches::kPolicySwitchesEnd);
 
   for (const auto& it : current_command_line->argv()) {
     if (it == policy_switches_begin) {
diff --git a/chrome/browser/ash/app_restore/full_restore_policy_browsertest.cc b/chrome/browser/ash/app_restore/full_restore_policy_browsertest.cc
index 1f4df80..fa4e46ce 100644
--- a/chrome/browser/ash/app_restore/full_restore_policy_browsertest.cc
+++ b/chrome/browser/ash/app_restore/full_restore_policy_browsertest.cc
@@ -36,7 +36,7 @@
          ::full_restore::features::kArcGhostWindow},
         {});
     arc::SetArcAvailableCommandLineForTesting(command_line);
-    command_line->AppendSwitch(chromeos::switches::kEnableArcVm);
+    command_line->AppendSwitch(switches::kEnableArcVm);
   }
 
   void SetUpInProcessBrowserTestFixture() override {
diff --git a/chrome/browser/ash/arc/arc_util.cc b/chrome/browser/ash/arc/arc_util.cc
index 81a2ab3..06a0010 100644
--- a/chrome/browser/ash/arc/arc_util.cc
+++ b/chrome/browser/ash/arc/arc_util.cc
@@ -365,7 +365,7 @@
   if (IsArcPlayStoreEnabledPreferenceManagedForProfile(profile)) {
     if (enabled && !IsArcPlayStoreEnabledForProfile(profile)) {
       LOG(WARNING) << "Attempt to enable disabled by policy ARC.";
-      if (chromeos::switches::IsTabletFormFactor()) {
+      if (ash::switches::IsTabletFormFactor()) {
         VLOG(1) << "Showing contact admin dialog managed user of tablet form "
                    "factor devices.";
         base::ThreadTaskRunnerHandle::Get()->PostTask(
diff --git a/chrome/browser/ash/arc/instance_throttle/arc_switch_throttle_observer.cc b/chrome/browser/ash/arc/instance_throttle/arc_switch_throttle_observer.cc
index f57bbc6..a303ab2 100644
--- a/chrome/browser/ash/arc/instance_throttle/arc_switch_throttle_observer.cc
+++ b/chrome/browser/ash/arc/instance_throttle/arc_switch_throttle_observer.cc
@@ -20,7 +20,7 @@
   // regardless of state of other observers. So we always set throttling as
   // CPU_RESTRICTION_FOREGROUND in the last case, that means no CPU restriction
   // happens.
-  SetActive(chromeos::switches::IsArcCpuRestrictionDisabled());
+  SetActive(ash::switches::IsArcCpuRestrictionDisabled());
 }
 
 }  // namespace arc
diff --git a/chrome/browser/ash/arc/instance_throttle/arc_switch_throttle_observer_unittest.cc b/chrome/browser/ash/arc/instance_throttle/arc_switch_throttle_observer_unittest.cc
index f3e4c0b..9c65fd74 100644
--- a/chrome/browser/ash/arc/instance_throttle/arc_switch_throttle_observer_unittest.cc
+++ b/chrome/browser/ash/arc/instance_throttle/arc_switch_throttle_observer_unittest.cc
@@ -40,7 +40,7 @@
 TEST_F(ArcSwitchThrottleObserverTest, Enforced) {
   base::test::ScopedCommandLine command_line;
   command_line.GetProcessCommandLine()->AppendSwitch(
-      chromeos::switches::kDisableArcCpuRestriction);
+      ash::switches::kDisableArcCpuRestriction);
   ArcSwitchThrottleObserver observer;
   int call_count = 0;
   int active_count = 0;
diff --git a/chrome/browser/ash/arc/policy/arc_policy_util.cc b/chrome/browser/ash/arc/policy/arc_policy_util.cc
index cb199bc..73f8543 100644
--- a/chrome/browser/ash/arc/policy/arc_policy_util.cc
+++ b/chrome/browser/ash/arc/policy/arc_policy_util.cc
@@ -39,7 +39,7 @@
 
 bool IsArcDisabledForEnterprise() {
   return base::CommandLine::ForCurrentProcess()->HasSwitch(
-      chromeos::switches::kEnterpriseDisableArc);
+      ash::switches::kEnterpriseDisableArc);
 }
 
 std::set<std::string> GetRequestedPackagesFromArcPolicy(
diff --git a/chrome/browser/ash/arc/session/arc_session_manager.cc b/chrome/browser/ash/arc/session/arc_session_manager.cc
index eb0820da..93bc6e4 100644
--- a/chrome/browser/ash/arc/session/arc_session_manager.cc
+++ b/chrome/browser/ash/arc/session/arc_session_manager.cc
@@ -473,7 +473,7 @@
 // for the presence of kEnableHoudiniDlc flag in the command line.
 bool IsDlcRequired() {
   return base::CommandLine::ForCurrentProcess()->HasSwitch(
-      chromeos::switches::kEnableHoudiniDlc);
+      ash::switches::kEnableHoudiniDlc);
 }
 
 }  // namespace
@@ -1785,7 +1785,7 @@
   const bool is_arcvm = arc::IsArcVmEnabled();
   bool add_native_bridge_64bit_support = false;
   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
-          chromeos::switches::kArcEnableNativeBridge64BitSupportExperiment)) {
+          ash::switches::kArcEnableNativeBridge64BitSupportExperiment)) {
     PrefService* local_pref_service = g_browser_process->local_state();
     if (base::FeatureList::IsEnabled(
             arc::kNativeBridge64BitSupportExperimentFeature)) {
diff --git a/chrome/browser/ash/arc/session/arc_session_manager_unittest.cc b/chrome/browser/ash/arc/session/arc_session_manager_unittest.cc
index f398122..d5ea31c0 100644
--- a/chrome/browser/ash/arc/session/arc_session_manager_unittest.cc
+++ b/chrome/browser/ash/arc/session/arc_session_manager_unittest.cc
@@ -990,7 +990,7 @@
 TEST_F(ArcSessionManagerTest, DataCleanUpOnFirstStart) {
   base::test::ScopedCommandLine command_line;
   command_line.GetProcessCommandLine()->AppendSwitch(
-      chromeos::switches::kArcDataCleanupOnStart);
+      ash::switches::kArcDataCleanupOnStart);
 
   arc_session_manager()->SetProfile(profile());
   arc_session_manager()->Initialize();
@@ -1024,7 +1024,7 @@
 TEST_F(ArcSessionManagerTest, DataCleanUpOnNextStart) {
   base::test::ScopedCommandLine command_line;
   command_line.GetProcessCommandLine()->AppendSwitch(
-      chromeos::switches::kArcDataCleanupOnStart);
+      ash::switches::kArcDataCleanupOnStart);
 
   PrefService* const prefs = profile()->GetPrefs();
   prefs->SetBoolean(prefs::kArcTermsAccepted, true);
diff --git a/chrome/browser/ash/attestation/platform_verification_flow.cc b/chrome/browser/ash/attestation/platform_verification_flow.cc
index fb943480..403fa50 100644
--- a/chrome/browser/ash/attestation/platform_verification_flow.cc
+++ b/chrome/browser/ash/attestation/platform_verification_flow.cc
@@ -80,7 +80,7 @@
   bool IsInSupportedMode() override {
     base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
     return !command_line->HasSwitch(chromeos::switches::kSystemDevMode) ||
-           command_line->HasSwitch(chromeos::switches::kAllowRAInDevMode);
+           command_line->HasSwitch(switches::kAllowRAInDevMode);
   }
 };
 
diff --git a/chrome/browser/ash/crosapi/browser_loader.cc b/chrome/browser/ash/crosapi/browser_loader.cc
index 821ced4..f7e2f6d 100644
--- a/chrome/browser/ash/crosapi/browser_loader.cc
+++ b/chrome/browser/ash/crosapi/browser_loader.cc
@@ -112,7 +112,7 @@
   // rather than component manager.
   base::FilePath lacros_chrome_path =
       base::CommandLine::ForCurrentProcess()->GetSwitchValuePath(
-          chromeos::switches::kLacrosChromePath);
+          ash::switches::kLacrosChromePath);
   if (!lacros_chrome_path.empty()) {
     OnLoadComplete(std::move(callback),
                    component_updater::CrOSComponentManager::Error::NONE,
diff --git a/chrome/browser/ash/crosapi/browser_manager.cc b/chrome/browser/ash/crosapi/browser_manager.cc
index 25ffc856..8a6e11b 100644
--- a/chrome/browser/ash/crosapi/browser_manager.cc
+++ b/chrome/browser/ash/crosapi/browser_manager.cc
@@ -284,12 +284,12 @@
 
 bool IsKeepAliveDisabledForTesting() {
   return base::CommandLine::ForCurrentProcess()->HasSwitch(
-      chromeos::switches::kDisableLacrosKeepAliveForTesting);
+      ash::switches::kDisableLacrosKeepAliveForTesting);
 }
 
 bool IsLoginLacrosOpeningDisabledForTesting() {
   return base::CommandLine::ForCurrentProcess()->HasSwitch(
-      chromeos::switches::kDisableLoginLacrosOpening);
+      ash::switches::kDisableLoginLacrosOpening);
 }
 
 }  // namespace
@@ -341,7 +341,7 @@
 
   std::string socket_path =
       base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
-          chromeos::switches::kLacrosMojoSocketForTesting);
+          ash::switches::kLacrosMojoSocketForTesting);
   if (!socket_path.empty()) {
     test_mojo_connection_manager_ =
         std::make_unique<crosapi::TestMojoConnectionManager>(
@@ -832,7 +832,7 @@
 
   std::string additional_env =
       base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
-          chromeos::switches::kLacrosChromeAdditionalEnv);
+          ash::switches::kLacrosChromeAdditionalEnv);
   base::StringPairs env_pairs;
   if (base::SplitStringIntoKeyValuePairsUsingSubstr(additional_env, '=', "####",
                                                     &env_pairs)) {
@@ -874,7 +874,7 @@
 
   std::string additional_flags =
       base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
-          chromeos::switches::kLacrosChromeAdditionalArgs);
+          ash::switches::kLacrosChromeAdditionalArgs);
   std::vector<base::StringPiece> delimited_flags =
       base::SplitStringPieceUsingSubstr(additional_flags, "####",
                                         base::TRIM_WHITESPACE,
diff --git a/chrome/browser/ash/crosapi/crosapi_util.cc b/chrome/browser/ash/crosapi/crosapi_util.cc
index ed87d93..3432562 100644
--- a/chrome/browser/ash/crosapi/crosapi_util.cc
+++ b/chrome/browser/ash/crosapi/crosapi_util.cc
@@ -367,7 +367,7 @@
           : crosapi::mojom::BrowserInitParams::InitialKeepAlive::kDisabled;
 
   params->is_unfiltered_bluetooth_device_enabled =
-      chromeos::switches::IsUnfilteredBluetoothDevicesEnabled();
+      ash::switches::IsUnfilteredBluetoothDevicesEnabled();
 
   // Pass the accepted internal urls to lacros. Only accepted urls are allowed
   // to be passed via OpenURL from Lacros to Ash.
diff --git a/chrome/browser/ash/crostini/crostini_features.cc b/chrome/browser/ash/crostini/crostini_features.cc
index 114a104..e48ba11 100644
--- a/chrome/browser/ash/crostini/crostini_features.cc
+++ b/chrome/browser/ash/crostini/crostini_features.cc
@@ -207,7 +207,7 @@
   }
 
   bool kernelnext = base::CommandLine::ForCurrentProcess()->HasSwitch(
-      chromeos::switches::kKernelnextRestrictVMs);
+      ash::switches::kKernelnextRestrictVMs);
   bool kernelnext_override =
       base::FeatureList::IsEnabled(features::kKernelnextVMs);
   if (kernelnext && !kernelnext_override) {
diff --git a/chrome/browser/ash/customization/customization_wallpaper_downloader_browsertest.cc b/chrome/browser/ash/customization/customization_wallpaper_downloader_browsertest.cc
index 76462da..2800ce64 100644
--- a/chrome/browser/ash/customization/customization_wallpaper_downloader_browsertest.cc
+++ b/chrome/browser/ash/customization/customization_wallpaper_downloader_browsertest.cc
@@ -182,8 +182,8 @@
   }
 
   void SetUpCommandLine(base::CommandLine* command_line) override {
-    command_line->AppendSwitch(chromeos::switches::kLoginManager);
-    command_line->AppendSwitchASCII(chromeos::switches::kLoginProfile, "user");
+    command_line->AppendSwitch(switches::kLoginManager);
+    command_line->AppendSwitchASCII(switches::kLoginProfile, "user");
   }
 
   void SetRequiredRetries(size_t retries) { required_retries_ = retries; }
diff --git a/chrome/browser/ash/drive/drive_integration_service_browsertest.cc b/chrome/browser/ash/drive/drive_integration_service_browsertest.cc
index 4fae2c5..ed06e13 100644
--- a/chrome/browser/ash/drive/drive_integration_service_browsertest.cc
+++ b/chrome/browser/ash/drive/drive_integration_service_browsertest.cc
@@ -178,7 +178,7 @@
 class DriveIntegrationServiceWithGaiaDisabledBrowserTest
     : public DriveIntegrationServiceBrowserTest {
   void SetUpCommandLine(base::CommandLine* command_line) override {
-    command_line->AppendSwitch(chromeos::switches::kDisableGaiaServices);
+    command_line->AppendSwitch(ash::switches::kDisableGaiaServices);
   }
 };
 
diff --git a/chrome/browser/ash/drive/file_system_util.cc b/chrome/browser/ash/drive/file_system_util.cc
index 4f73277..7db048e 100644
--- a/chrome/browser/ash/drive/file_system_util.cc
+++ b/chrome/browser/ash/drive/file_system_util.cc
@@ -77,7 +77,7 @@
 
   // Disable Drive for non-Gaia accounts.
   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
-          chromeos::switches::kDisableGaiaServices)) {
+          ash::switches::kDisableGaiaServices)) {
     return false;
   }
   if (!chromeos::LoginState::IsInitialized())
diff --git a/chrome/browser/ash/file_manager/external_filesystem_apitest.cc b/chrome/browser/ash/file_manager/external_filesystem_apitest.cc
index 8ef25c2..5336c7d9 100644
--- a/chrome/browser/ash/file_manager/external_filesystem_apitest.cc
+++ b/chrome/browser/ash/file_manager/external_filesystem_apitest.cc
@@ -529,7 +529,7 @@
     // Don't require policy for our sessions - this is required because
     // this test creates a secondary profile synchronously, so we need to
     // let the policy code know not to expect cached policy.
-    command_line->AppendSwitchASCII(chromeos::switches::kProfileRequiresPolicy,
+    command_line->AppendSwitchASCII(ash::switches::kProfileRequiresPolicy,
                                     "false");
   }
 
diff --git a/chrome/browser/ash/file_manager/file_manager_browsertest_base.cc b/chrome/browser/ash/file_manager/file_manager_browsertest_base.cc
index e7ecc88..c550908 100644
--- a/chrome/browser/ash/file_manager/file_manager_browsertest_base.cc
+++ b/chrome/browser/ash/file_manager/file_manager_browsertest_base.cc
@@ -1724,9 +1724,9 @@
   }
 
   if (options.guest_mode == IN_GUEST_MODE) {
-    command_line->AppendSwitch(chromeos::switches::kGuestSession);
-    command_line->AppendSwitchNative(chromeos::switches::kLoginUser, "$guest");
-    command_line->AppendSwitchASCII(chromeos::switches::kLoginProfile, "user");
+    command_line->AppendSwitch(ash::switches::kGuestSession);
+    command_line->AppendSwitchNative(ash::switches::kLoginUser, "$guest");
+    command_line->AppendSwitchASCII(ash::switches::kLoginProfile, "user");
     command_line->AppendSwitch(switches::kIncognito);
     set_chromeos_user_ = false;
   }
diff --git a/chrome/browser/ash/hats/hats_notification_controller.cc b/chrome/browser/ash/hats/hats_notification_controller.cc
index e591ddc7..a7690a32 100644
--- a/chrome/browser/ash/hats/hats_notification_controller.cc
+++ b/chrome/browser/ash/hats/hats_notification_controller.cc
@@ -78,10 +78,9 @@
 bool IsTestingEnabled(const HatsConfig& hats_config) {
   base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
 
-  if (command_line->HasSwitch(
-          chromeos::switches::kForceHappinessTrackingSystem)) {
+  if (command_line->HasSwitch(switches::kForceHappinessTrackingSystem)) {
     auto switch_value = command_line->GetSwitchValueASCII(
-        chromeos::switches::kForceHappinessTrackingSystem);
+        switches::kForceHappinessTrackingSystem);
     return switch_value.empty() || hats_config.feature.name == switch_value;
   }
 
diff --git a/chrome/browser/ash/kerberos/kerberos_credentials_manager.h b/chrome/browser/ash/kerberos/kerberos_credentials_manager.h
index 5456d1c..2c7b02e67 100644
--- a/chrome/browser/ash/kerberos/kerberos_credentials_manager.h
+++ b/chrome/browser/ash/kerberos/kerberos_credentials_manager.h
@@ -135,7 +135,7 @@
   void ListAccounts(ListAccountsCallback callback);
 
   // Sets the contents of the Kerberos configuration (krb5.conf) to |krb5_conf|
-  // for the account  with given |principal_name|.
+  // for the account with given |principal_name|.
   void SetConfig(std::string principal_name,
                  const std::string& krb5_conf,
                  ResultCallback callback);
diff --git a/chrome/browser/ash/kerberos/kerberos_credentials_manager_factory_browsertest.cc b/chrome/browser/ash/kerberos/kerberos_credentials_manager_factory_browsertest.cc
index 5243178..bb2aad6 100644
--- a/chrome/browser/ash/kerberos/kerberos_credentials_manager_factory_browsertest.cc
+++ b/chrome/browser/ash/kerberos/kerberos_credentials_manager_factory_browsertest.cc
@@ -20,8 +20,7 @@
     : public InProcessBrowserTest {
  protected:
   void SetUpCommandLine(base::CommandLine* command_line) override {
-    command_line->AppendSwitch(
-        chromeos::switches::kIgnoreUserProfileMappingForTests);
+    command_line->AppendSwitch(switches::kIgnoreUserProfileMappingForTests);
   }
 };
 
diff --git a/chrome/browser/ash/login/chrome_restart_request.cc b/chrome/browser/ash/login/chrome_restart_request.cc
index 0500bf8..883d6c5 100644
--- a/chrome/browser/ash/login/chrome_restart_request.cc
+++ b/chrome/browser/ash/login/chrome_restart_request.cc
@@ -182,8 +182,8 @@
     blink::switches::kEnableRasterSideDarkModeForImages,
     blink::switches::kEnableZeroCopy,
     blink::switches::kGpuRasterizationMSAASampleCount,
-    chromeos::switches::kAshPowerButtonPosition,
-    chromeos::switches::kAshSideVolumeButtonPosition,
+    switches::kAshPowerButtonPosition,
+    switches::kAshSideVolumeButtonPosition,
     switches::kDefaultWallpaperLarge,
     switches::kDefaultWallpaperSmall,
     switches::kGuestWallpaperLarge,
@@ -225,7 +225,7 @@
     switches::kLoginProfile,
     switches::kNaturalScrollDefault,
     switches::kRlzPingDelay,
-    switches::kSystemInDevMode,
+    chromeos::switches::kSystemInDevMode,
     policy::switches::kDeviceManagementUrl,
     wm::switches::kWindowAnimationsDisabled,
   };
diff --git a/chrome/browser/ash/login/demo_mode/demo_mode_detector.cc b/chrome/browser/ash/login/demo_mode/demo_mode_detector.cc
index 01cd64e3..0aac3a31 100644
--- a/chrome/browser/ash/login/demo_mode/demo_mode_detector.cc
+++ b/chrome/browser/ash/login/demo_mode/demo_mode_detector.cc
@@ -55,7 +55,7 @@
   // Devices in retail won't be in dev mode, and DUTs (devices under test) often
   // sit unused at OOBE for a while.
   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
-          switches::kSystemDevMode) &&
+          chromeos::switches::kSystemDevMode) &&
       !has_derelict_switch) {
     return;
   }
diff --git a/chrome/browser/ash/login/demo_mode/demo_mode_detector_unittest.cc b/chrome/browser/ash/login/demo_mode/demo_mode_detector_unittest.cc
index a44d5e8..126ef85 100644
--- a/chrome/browser/ash/login/demo_mode/demo_mode_detector_unittest.cc
+++ b/chrome/browser/ash/login/demo_mode/demo_mode_detector_unittest.cc
@@ -141,7 +141,7 @@
   // Set the command line dev mode switch.
   auto command_line_ = std::make_unique<base::test::ScopedCommandLine>();
   command_line_->GetProcessCommandLine()->AppendSwitch(
-      switches::kSystemDevMode);
+      chromeos::switches::kSystemDevMode);
 
   StartDemoModeDetection();
 
diff --git a/chrome/browser/ash/login/enrollment/enrollment_embedded_policy_server_browsertest.cc b/chrome/browser/ash/login/enrollment/enrollment_embedded_policy_server_browsertest.cc
index 652a8a5f..c2d3556 100644
--- a/chrome/browser/ash/login/enrollment/enrollment_embedded_policy_server_browsertest.cc
+++ b/chrome/browser/ash/login/enrollment/enrollment_embedded_policy_server_browsertest.cc
@@ -440,7 +440,7 @@
 IN_PROC_BROWSER_TEST_F(EnrollmentEmbeddedPolicyServerBase,
                        DeviceBlockDevmodeDisallowed) {
   base::CommandLine::ForCurrentProcess()->AppendSwitch(
-      ash::switches::kDisallowPolicyBlockDevMode);
+      switches::kDisallowPolicyBlockDevMode);
   enterprise_management::ChromeDeviceSettingsProto proto;
   proto.mutable_system_settings()->set_block_devmode(true);
   policy_server_.UpdateDevicePolicy(proto);
diff --git a/chrome/browser/ash/login/existing_user_controller.cc b/chrome/browser/ash/login/existing_user_controller.cc
index 323262a..9ee4981 100644
--- a/chrome/browser/ash/login/existing_user_controller.cc
+++ b/chrome/browser/ash/login/existing_user_controller.cc
@@ -219,7 +219,7 @@
 
 bool IsTestingMigrationUI() {
   return base::CommandLine::ForCurrentProcess()->HasSwitch(
-      chromeos::switches::kTestEncryptionMigrationUI);
+      switches::kTestEncryptionMigrationUI);
 }
 
 bool ShouldForceDircrypto(const AccountId& account_id) {
@@ -287,7 +287,7 @@
           user_context.GetAccountId()) ==
           user_manager::ProfileRequiresPolicy::kPolicyRequired ||
       base::CommandLine::ForCurrentProcess()->HasSwitch(
-          chromeos::switches::kProfileRequiresPolicy);
+          switches::kProfileRequiresPolicy);
 
   // Force-migrate all home directories if the user is known to have enterprise
   // policy, otherwise ask the user.
diff --git a/chrome/browser/ash/login/saml/saml_browsertest.cc b/chrome/browser/ash/login/saml/saml_browsertest.cc
index 7af8fd5..63397c77 100644
--- a/chrome/browser/ash/login/saml/saml_browsertest.cc
+++ b/chrome/browser/ash/login/saml/saml_browsertest.cc
@@ -1084,7 +1084,7 @@
   SamlTestBase::SetUpCommandLine(command_line);
 
   // Disable token check to allow offline sign-in for the fake gaia users.
-  command_line->AppendSwitch(chromeos::switches::kDisableGaiaServices);
+  command_line->AppendSwitch(switches::kDisableGaiaServices);
 }
 
 void SAMLPolicyTest::SetSAMLOfflineSigninTimeLimitPolicy(int limit) {
diff --git a/chrome/browser/ash/login/screens/chromevox_hint/chromevox_hint_detector.cc b/chrome/browser/ash/login/screens/chromevox_hint/chromevox_hint_detector.cc
index 7b97543..81bafea 100644
--- a/chrome/browser/ash/login/screens/chromevox_hint/chromevox_hint_detector.cc
+++ b/chrome/browser/ash/login/screens/chromevox_hint/chromevox_hint_detector.cc
@@ -38,7 +38,7 @@
   // This is done so that developers and testers don't repeatedly receive
   // the hint when flashing.
   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
-          switches::kSystemDevMode) &&
+          chromeos::switches::kSystemDevMode) &&
       !switches::IsOOBEChromeVoxHintEnabledForDevMode()) {
     return;
   }
diff --git a/chrome/browser/ash/login/screens/chromevox_hint/chromevox_hint_detector_unittest.cc b/chrome/browser/ash/login/screens/chromevox_hint/chromevox_hint_detector_unittest.cc
index f4176eb..fa35a04 100644
--- a/chrome/browser/ash/login/screens/chromevox_hint/chromevox_hint_detector_unittest.cc
+++ b/chrome/browser/ash/login/screens/chromevox_hint/chromevox_hint_detector_unittest.cc
@@ -116,7 +116,7 @@
 TEST_F(ChromeVoxHintDetectorTest, NoHintInDevMode) {
   auto command_line_ = std::make_unique<base::test::ScopedCommandLine>();
   command_line_->GetProcessCommandLine()->AppendSwitch(
-      switches::kSystemDevMode);
+      chromeos::switches::kSystemDevMode);
   ExpectChromeVoxHintWillNotBeGiven();
   StartDetection();
   runner_->FastForwardBy(kFullIdleDuration);
@@ -127,7 +127,7 @@
 TEST_F(ChromeVoxHintDetectorTest, HintInDevModeWithEnablingSwitch) {
   auto command_line_ = std::make_unique<base::test::ScopedCommandLine>();
   command_line_->GetProcessCommandLine()->AppendSwitch(
-      switches::kSystemDevMode);
+      chromeos::switches::kSystemDevMode);
   command_line_->GetProcessCommandLine()->AppendSwitch(
       switches::kEnableOOBEChromeVoxHintForDevMode);
   ExpectChromeVoxHintWillBeGiven();
@@ -140,7 +140,7 @@
 TEST_F(ChromeVoxHintDetectorTest, NoHintWithDisablingSwitchInDevMode) {
   auto command_line_ = std::make_unique<base::test::ScopedCommandLine>();
   command_line_->GetProcessCommandLine()->AppendSwitch(
-      switches::kSystemDevMode);
+      chromeos::switches::kSystemDevMode);
   command_line_->GetProcessCommandLine()->AppendSwitch(
       switches::kEnableOOBEChromeVoxHintForDevMode);
   command_line_->GetProcessCommandLine()->AppendSwitch(
diff --git a/chrome/browser/ash/login/screens/sync_consent_browsertest.cc b/chrome/browser/ash/login/screens/sync_consent_browsertest.cc
index 2ce7ddf3..ea36055 100644
--- a/chrome/browser/ash/login/screens/sync_consent_browsertest.cc
+++ b/chrome/browser/ash/login/screens/sync_consent_browsertest.cc
@@ -692,8 +692,7 @@
 class SyncConsentTimeoutTest : public SyncConsentTest {
  public:
   void SetUpCommandLine(base::CommandLine* command_line) override {
-    command_line->AppendSwitch(
-        ::chromeos::switches::kOobeTriggerSyncTimeoutForTests);
+    command_line->AppendSwitch(switches::kOobeTriggerSyncTimeoutForTests);
     SyncConsentTest::SetUpCommandLine(command_line);
   }
 };
diff --git a/chrome/browser/ash/login/screens/update_required_screen.cc b/chrome/browser/ash/login/screens/update_required_screen.cc
index e8dccffc..f2a9472 100644
--- a/chrome/browser/ash/login/screens/update_required_screen.cc
+++ b/chrome/browser/ash/login/screens/update_required_screen.cc
@@ -107,7 +107,7 @@
     const chromeos::UpdateEngineClient::EolInfo& info) {
   //  TODO(crbug.com/1020616) : Handle if the device is left on this screen
   //  for long enough to reach Eol.
-  if (chromeos::switches::IsAueReachedForUpdateRequiredForTest() ||
+  if (switches::IsAueReachedForUpdateRequiredForTest() ||
       (!info.eol_date.is_null() && info.eol_date <= clock_->Now())) {
     EnsureScreenIsShown();
     if (view_) {
diff --git a/chrome/browser/ash/login/screens/welcome_screen_browsertest.cc b/chrome/browser/ash/login/screens/welcome_screen_browsertest.cc
index 1d27919..91b3c7f 100644
--- a/chrome/browser/ash/login/screens/welcome_screen_browsertest.cc
+++ b/chrome/browser/ash/login/screens/welcome_screen_browsertest.cc
@@ -152,7 +152,7 @@
   // WelcomeScreenBrowserTest:
   void SetUpCommandLine(base::CommandLine* command_line) override {
     WelcomeScreenBrowserTest::SetUpCommandLine(command_line);
-    command_line->AppendSwitch(switches::kSystemDevMode);
+    command_line->AppendSwitch(chromeos::switches::kSystemDevMode);
   }
 };
 
diff --git a/chrome/browser/ash/login/session/chrome_session_manager_browsertest.cc b/chrome/browser/ash/login/session/chrome_session_manager_browsertest.cc
index 6da3327..80657ed8 100644
--- a/chrome/browser/ash/login/session/chrome_session_manager_browsertest.cc
+++ b/chrome/browser/ash/login/session/chrome_session_manager_browsertest.cc
@@ -349,12 +349,11 @@
   }
 
   void SetUpCommandLine(base::CommandLine* command_line) override {
-    command_line->AppendSwitch(ash::switches::kGuestSession);
+    command_line->AppendSwitch(switches::kGuestSession);
     command_line->AppendSwitch(::switches::kIncognito);
-    command_line->AppendSwitchASCII(ash::switches::kLoginProfile, "hash");
+    command_line->AppendSwitchASCII(switches::kLoginProfile, "hash");
     command_line->AppendSwitchASCII(
-        ash::switches::kLoginUser,
-        user_manager::GuestAccountId().GetUserEmail());
+        switches::kLoginUser, user_manager::GuestAccountId().GetUserEmail());
   }
 
   // Test instance parameters.
diff --git a/chrome/browser/ash/login/session/user_session_manager.cc b/chrome/browser/ash/login/session/user_session_manager.cc
index 1a9ac8fc..b4689ac1 100644
--- a/chrome/browser/ash/login/session/user_session_manager.cc
+++ b/chrome/browser/ash/login/session/user_session_manager.cc
@@ -225,7 +225,7 @@
 base::TimeDelta GetActivityTimeBeforeOnboardingSurvey() {
   auto* command_line = base::CommandLine::ForCurrentProcess();
   const auto& time_switch =
-      chromeos::switches::kTimeBeforeOnboardingSurveyInSecondsForTesting;
+      switches::kTimeBeforeOnboardingSurveyInSecondsForTesting;
 
   if (!command_line->HasSwitch(time_switch)) {
     return kActivityTimeBeforeOnboardingSurvey;
diff --git a/chrome/browser/ash/login/test/session_flags_manager.cc b/chrome/browser/ash/login/test/session_flags_manager.cc
index ea5a2d7..01e217be 100644
--- a/chrome/browser/ash/login/test/session_flags_manager.cc
+++ b/chrome/browser/ash/login/test/session_flags_manager.cc
@@ -61,11 +61,11 @@
 
 void SessionFlagsManager::SetDefaultLoginSwitches(
     const std::vector<Switch>& switches) {
-  default_switches_ = {{switches::kPolicySwitchesBegin, ""}};
+  default_switches_ = {{chromeos::switches::kPolicySwitchesBegin, ""}};
   default_switches_.insert(default_switches_.end(), switches.begin(),
                            switches.end());
   default_switches_.emplace_back(
-      std::make_pair(switches::kPolicySwitchesEnd, ""));
+      std::make_pair(chromeos::switches::kPolicySwitchesEnd, ""));
 }
 
 void SessionFlagsManager::AppendSwitchesToCommandLine(
diff --git a/chrome/browser/ash/login/ui/login_display_host_mojo.cc b/chrome/browser/ash/login/ui/login_display_host_mojo.cc
index 35eebb0a..94e29d10 100644
--- a/chrome/browser/ash/login/ui/login_display_host_mojo.cc
+++ b/chrome/browser/ash/login/ui/login_display_host_mojo.cc
@@ -107,7 +107,7 @@
 
 bool IsLazyWebUILoadingEnabled() {
   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
-          ::chromeos::switches::kEnableOobeTestAPI)) {
+          switches::kEnableOobeTestAPI)) {
     // Load WebUI for the test API explicitly because it's Web API.
     return false;
   }
diff --git a/chrome/browser/ash/login/users/avatar/user_image_manager_browsertest.cc b/chrome/browser/ash/login/users/avatar/user_image_manager_browsertest.cc
index b31e873..52139ff 100644
--- a/chrome/browser/ash/login/users/avatar/user_image_manager_browsertest.cc
+++ b/chrome/browser/ash/login/users/avatar/user_image_manager_browsertest.cc
@@ -184,8 +184,7 @@
     // to avoid having to set up a mock policy server. UserCloudPolicyManager
     // will shut down the profile if there's an error loading the initial
     // policy, so disable this behavior so we can inject policy directly.
-    command_line->AppendSwitch(
-        chromeos::switches::kAllowFailedPolicyFetchForTest);
+    command_line->AppendSwitch(switches::kAllowFailedPolicyFetchForTest);
   }
 
   void SetUpOnMainThread() override {
diff --git a/chrome/browser/ash/login/users/chrome_user_manager.cc b/chrome/browser/ash/login/users/chrome_user_manager.cc
index d6f6d39..8bdfdc49 100644
--- a/chrome/browser/ash/login/users/chrome_user_manager.cc
+++ b/chrome/browser/ash/login/users/chrome_user_manager.cc
@@ -25,7 +25,7 @@
 
 bool ChromeUserManager::IsCurrentUserNew() const {
   base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
-  if (command_line->HasSwitch(chromeos::switches::kForceFirstRunUI))
+  if (command_line->HasSwitch(switches::kForceFirstRunUI))
     return true;
 
   return UserManagerBase::IsCurrentUserNew();
diff --git a/chrome/browser/ash/login/users/chrome_user_manager_impl.cc b/chrome/browser/ash/login/users/chrome_user_manager_impl.cc
index 6695800..e5ac766 100644
--- a/chrome/browser/ash/login/users/chrome_user_manager_impl.cc
+++ b/chrome/browser/ash/login/users/chrome_user_manager_impl.cc
@@ -1368,7 +1368,7 @@
 
 bool ChromeUserManagerImpl::IsFirstExecAfterBoot() const {
   return base::CommandLine::ForCurrentProcess()->HasSwitch(
-      chromeos::switches::kFirstExecAfterBoot);
+      switches::kFirstExecAfterBoot);
 }
 
 void ChromeUserManagerImpl::AsyncRemoveCryptohome(
@@ -1408,7 +1408,7 @@
 bool ChromeUserManagerImpl::HasBrowserRestarted() const {
   base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
   return base::SysInfo::IsRunningOnChromeOS() &&
-         command_line->HasSwitch(chromeos::switches::kLoginUser);
+         command_line->HasSwitch(switches::kLoginUser);
 }
 
 const gfx::ImageSkia& ChromeUserManagerImpl::GetResourceImagekiaNamed(
diff --git a/chrome/browser/ash/login/users/fake_chrome_user_manager.cc b/chrome/browser/ash/login/users/fake_chrome_user_manager.cc
index 7884088..ee625d4a 100644
--- a/chrome/browser/ash/login/users/fake_chrome_user_manager.cc
+++ b/chrome/browser/ash/login/users/fake_chrome_user_manager.cc
@@ -337,7 +337,7 @@
 
 bool FakeChromeUserManager::IsFirstExecAfterBoot() const {
   return base::CommandLine::ForCurrentProcess()->HasSwitch(
-      chromeos::switches::kFirstExecAfterBoot);
+      switches::kFirstExecAfterBoot);
 }
 
 void FakeChromeUserManager::AsyncRemoveCryptohome(
@@ -370,7 +370,7 @@
 bool FakeChromeUserManager::HasBrowserRestarted() const {
   base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
   return base::SysInfo::IsRunningOnChromeOS() &&
-         command_line->HasSwitch(chromeos::switches::kLoginUser);
+         command_line->HasSwitch(switches::kLoginUser);
 }
 
 const gfx::ImageSkia& FakeChromeUserManager::GetResourceImagekiaNamed(
diff --git a/chrome/browser/ash/login/users/user_manager_unittest.cc b/chrome/browser/ash/login/users/user_manager_unittest.cc
index 82a3a94..e127372 100644
--- a/chrome/browser/ash/login/users/user_manager_unittest.cc
+++ b/chrome/browser/ash/login/users/user_manager_unittest.cc
@@ -87,8 +87,7 @@
   void SetUp() override {
     base::CommandLine& command_line = *base::CommandLine::ForCurrentProcess();
     command_line.AppendSwitch(::switches::kTestType);
-    command_line.AppendSwitch(
-        chromeos::switches::kIgnoreUserProfileMappingForTests);
+    command_line.AppendSwitch(switches::kIgnoreUserProfileMappingForTests);
 
     settings_helper_.ReplaceDeviceSettingsProviderWithStub();
 
diff --git a/chrome/browser/ash/policy/active_directory/active_directory_migration_manager.cc b/chrome/browser/ash/policy/active_directory/active_directory_migration_manager.cc
new file mode 100644
index 0000000..100e1ea
--- /dev/null
+++ b/chrome/browser/ash/policy/active_directory/active_directory_migration_manager.cc
@@ -0,0 +1,200 @@
+// 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/policy/active_directory/active_directory_migration_manager.h"
+
+#include <utility>
+
+#include "ash/constants/ash_pref_names.h"
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/time/time.h"
+#include "chrome/common/pref_names.h"
+#include "chromeos/dbus/session_manager/session_manager_client.h"
+#include "components/policy/proto/device_management_backend.pb.h"
+#include "components/prefs/pref_change_registrar.h"
+#include "components/prefs/pref_registry_simple.h"
+#include "components/prefs/pref_service.h"
+#include "components/session_manager/core/session_manager.h"
+#include "content/public/browser/browser_task_traits.h"
+#include "content/public/browser/browser_thread.h"
+
+namespace em = enterprise_management;
+
+namespace policy {
+
+namespace {
+
+// The amount of time we wait before actively checking the preconditions to
+// start the migration again.
+constexpr base::TimeDelta kRetryDelay = base::Hours(1);
+
+// The amount of time we wait before triggering a new powerwash, in case the
+// last request has failed for any reason.
+constexpr base::TimeDelta kPowerwashBackoffTime = base::Days(1);
+
+// Returns true if any user is logged in (session is started).
+bool IsUserLoggedIn() {
+  auto* session_manager = session_manager::SessionManager::Get();
+  return session_manager && session_manager->IsSessionStarted();
+}
+
+}  // namespace
+
+ActiveDirectoryMigrationManager::ActiveDirectoryMigrationManager(
+    PrefService* local_state)
+    : local_state_(local_state) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  DCHECK(local_state_);
+
+  // Listen to user session state changes.
+  auto* session_manager = session_manager::SessionManager::Get();
+  if (session_manager) {
+    session_manager->AddObserver(this);
+  }
+}
+
+ActiveDirectoryMigrationManager::~ActiveDirectoryMigrationManager() {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+  // Stop listening to user session state changes.
+  auto* session_manager = session_manager::SessionManager::Get();
+  if (session_manager) {
+    session_manager->RemoveObserver(this);
+  }
+}
+
+// static
+void ActiveDirectoryMigrationManager::RegisterLocalStatePrefs(
+    PrefRegistrySimple* registry) {
+  registry->RegisterTimePref(prefs::kLastChromadMigrationAttemptTime,
+                             /*default_value=*/base::Time());
+}
+
+void ActiveDirectoryMigrationManager::Init() {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+  // Listen to pref changes.
+  pref_change_registrar_ = std::make_unique<PrefChangeRegistrar>();
+  pref_change_registrar_->Init(local_state_);
+  pref_change_registrar_->Add(
+      prefs::kEnrollmentIdUploadedOnChromad,
+      base::BindRepeating(
+          &ActiveDirectoryMigrationManager::OnEnrollmentIdUploadedPrefChanged,
+          weak_ptr_factory_.GetWeakPtr()));
+  pref_change_registrar_->Add(
+      ash::prefs::kChromadToCloudMigrationEnabled,
+      base::BindRepeating(&ActiveDirectoryMigrationManager::
+                              OnChromadMigrationEnabledPrefChanged,
+                          weak_ptr_factory_.GetWeakPtr()));
+
+  // Check the conditions here as well, because this manager might be
+  // initialized while the pre-conditions are already satisfied.
+  TryToStartMigration();
+}
+
+void ActiveDirectoryMigrationManager::Shutdown() {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+  pref_change_registrar_->RemoveAll();
+}
+
+void ActiveDirectoryMigrationManager::SetStatusCallbackForTesting(
+    StatusCallback callback) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+  status_callback_for_testing_ = std::move(callback);
+}
+
+bool ActiveDirectoryMigrationManager::HasUploadedEnrollmentId() const {
+  return local_state_->GetBoolean(prefs::kEnrollmentIdUploadedOnChromad);
+}
+
+bool ActiveDirectoryMigrationManager::IsChromadMigrationEnabled() const {
+  return local_state_->GetBoolean(ash::prefs::kChromadToCloudMigrationEnabled);
+}
+
+bool ActiveDirectoryMigrationManager::HasBackoffTimePassed() const {
+  base::Time last_migration_attempt_time =
+      local_state_->GetTime(prefs::kLastChromadMigrationAttemptTime);
+  base::Time now = base::Time::Now();
+
+  return now - last_migration_attempt_time > kPowerwashBackoffTime;
+}
+
+void ActiveDirectoryMigrationManager::OnEnrollmentIdUploadedPrefChanged() {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+  TryToStartMigration();
+}
+
+void ActiveDirectoryMigrationManager::OnChromadMigrationEnabledPrefChanged() {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+  TryToStartMigration();
+}
+
+void ActiveDirectoryMigrationManager::OnLoginOrLockScreenVisible() {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+  TryToStartMigration();
+}
+
+void ActiveDirectoryMigrationManager::TryToStartMigration() {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+  bool is_on_login_screen = !IsUserLoggedIn();
+
+  if (is_on_login_screen && HasUploadedEnrollmentId() &&
+      IsChromadMigrationEnabled() && HasBackoffTimePassed()) {
+    StartPowerwash();
+    MaybeRunStatusCallback(/*started=*/true, /*rescheduled=*/false);
+    return;
+  }
+
+  // Theoretically, the following reschedule logic is not necessary. However, it
+  // was added as a fallback, in case any of the signals this class listens is
+  // not triggered as expected. Ultimatelly, we want to avoid inactive devices
+  // getting stuck and not migrating.
+  if (is_on_login_screen && !retry_already_scheduled_) {
+    retry_already_scheduled_ = true;
+    content::GetUIThreadTaskRunner({})->PostDelayedTask(
+        FROM_HERE,
+        base::BindOnce(&ActiveDirectoryMigrationManager::RetryToStartMigration,
+                       weak_ptr_factory_.GetWeakPtr()),
+        kRetryDelay);
+
+    MaybeRunStatusCallback(/*started=*/false, /*rescheduled=*/true);
+    return;
+  }
+
+  MaybeRunStatusCallback(/*started=*/false, /*rescheduled=*/false);
+}
+
+void ActiveDirectoryMigrationManager::RetryToStartMigration() {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+  retry_already_scheduled_ = false;
+  TryToStartMigration();
+}
+
+void ActiveDirectoryMigrationManager::StartPowerwash() {
+  local_state_->SetTime(prefs::kLastChromadMigrationAttemptTime,
+                        base::Time::Now());
+
+  // Unsigned remote powerwash requests are allowed in AD mode.
+  chromeos::SessionManagerClient::Get()->StartRemoteDeviceWipe(
+      em::SignedData());
+}
+
+void ActiveDirectoryMigrationManager::MaybeRunStatusCallback(bool started,
+                                                             bool rescheduled) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+  if (status_callback_for_testing_) {
+    std::move(status_callback_for_testing_).Run(started, rescheduled);
+  }
+}
+
+}  // namespace policy
diff --git a/chrome/browser/ash/policy/active_directory/active_directory_migration_manager.h b/chrome/browser/ash/policy/active_directory/active_directory_migration_manager.h
new file mode 100644
index 0000000..8698838
--- /dev/null
+++ b/chrome/browser/ash/policy/active_directory/active_directory_migration_manager.h
@@ -0,0 +1,110 @@
+// 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_POLICY_ACTIVE_DIRECTORY_ACTIVE_DIRECTORY_MIGRATION_MANAGER_H_
+#define CHROME_BROWSER_ASH_POLICY_ACTIVE_DIRECTORY_ACTIVE_DIRECTORY_MIGRATION_MANAGER_H_
+
+#include <memory>
+
+#include "base/callback.h"
+#include "base/memory/raw_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "components/session_manager/core/session_manager_observer.h"
+
+class PrefChangeRegistrar;
+class PrefRegistrySimple;
+class PrefService;
+
+namespace policy {
+
+// Manages the migration of AD managed devices into cloud management. The goal
+// is to start the migration when (a) the device is on the login screen, (b) the
+// enrollment ID has already been uploaded to DMServer and (c) the
+// `ChromadToCloudMigrationEnabled` policy is enabled. After being constructed,
+// this class listens to changes from the `SessionManager` and from the
+// `kEnrollmentIdUploadedOnChromad` and `kChromadToCloudMigrationEnabled` local
+// state prefs. Additionally, these checks are periodically executed while the
+// device is on the login screen.
+class ActiveDirectoryMigrationManager
+    : public session_manager::SessionManagerObserver {
+ public:
+  explicit ActiveDirectoryMigrationManager(PrefService* local_state);
+
+  ~ActiveDirectoryMigrationManager() override;
+
+  // Disallow copy and assignment.
+  ActiveDirectoryMigrationManager(const ActiveDirectoryMigrationManager&) =
+      delete;
+  ActiveDirectoryMigrationManager& operator=(
+      const ActiveDirectoryMigrationManager&) = delete;
+
+  static void RegisterLocalStatePrefs(PrefRegistrySimple* registry);
+
+  // Registers to prefs changes and tries to start the migration.
+  void Init();
+
+  // Unregisters to prefs changes.
+  void Shutdown();
+
+  // Callback called when the `TryToStartMigration` method is executed. Returns
+  // whether the migrations started or not, and whether a retry was scheduled or
+  // not.
+  using StatusCallback =
+      base::OnceCallback<void(bool started, bool rescheduled)>;
+
+  // Only used for testing.
+  void SetStatusCallbackForTesting(StatusCallback callback);
+
+ private:
+  // Returns true if the enrollment ID has already been uploaded.
+  bool HasUploadedEnrollmentId() const;
+
+  // Returns true if the migration of Chromad devices to cloud management is
+  // enabled.
+  bool IsChromadMigrationEnabled() const;
+
+  // Returns true if the last powerwash attempt happened more than
+  // `kPowerwashBackoffTime` ago.
+  bool HasBackoffTimePassed() const;
+
+  // Pref change handlers.
+  void OnEnrollmentIdUploadedPrefChanged();
+  void OnChromadMigrationEnabledPrefChanged();
+
+  // session_manager::SessionManagerObserver:
+  void OnLoginOrLockScreenVisible() override;
+
+  // Triggers a device powerwash, if the pre-requisites are satisfied. Called
+  // every time one of the three events of interest happens. Also called
+  // periodically while the device is on the login screen.
+  void TryToStartMigration();
+
+  // Executes the same steps as `TryToStartMigration`, but also updates the
+  // value of `retry_already_scheduled_` accordingly.
+  void RetryToStartMigration();
+
+  // Sends a device powerwash request through D-Bus.
+  void StartPowerwash();
+
+  // Runs the `status_callback_for_testing_`, if it's not empty. Passes the
+  // received boolean values to the callback. Only used for testing.
+  void MaybeRunStatusCallback(bool started, bool rescheduled);
+
+  // Local state prefs, not owned.
+  raw_ptr<PrefService> local_state_;
+
+  // Observer for Chromad migration related prefs.
+  std::unique_ptr<PrefChangeRegistrar> pref_change_registrar_;
+
+  bool retry_already_scheduled_ = false;
+
+  StatusCallback status_callback_for_testing_;
+
+  // Must be the last member.
+  base::WeakPtrFactory<ActiveDirectoryMigrationManager> weak_ptr_factory_{this};
+};
+
+}  // namespace policy
+
+#endif  // CHROME_BROWSER_ASH_POLICY_ACTIVE_DIRECTORY_ACTIVE_DIRECTORY_MIGRATION_MANAGER_H_
diff --git a/chrome/browser/ash/policy/active_directory/active_directory_migration_manager_unittest.cc b/chrome/browser/ash/policy/active_directory/active_directory_migration_manager_unittest.cc
new file mode 100644
index 0000000..9301bbf
--- /dev/null
+++ b/chrome/browser/ash/policy/active_directory/active_directory_migration_manager_unittest.cc
@@ -0,0 +1,338 @@
+// 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/policy/active_directory/active_directory_migration_manager.h"
+
+#include "ash/constants/ash_pref_names.h"
+#include "base/bind.h"
+#include "base/memory/raw_ptr.h"
+#include "base/test/task_environment.h"
+#include "base/time/time.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/test/base/scoped_testing_local_state.h"
+#include "chrome/test/base/testing_browser_process.h"
+#include "chromeos/dbus/session_manager/fake_session_manager_client.h"
+#include "components/session_manager/core/session_manager.h"
+#include "content/public/test/browser_task_environment.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace em = enterprise_management;
+
+namespace {
+
+constexpr base::TimeDelta kLongTimeDelta = base::Hours(25);
+constexpr base::TimeDelta kShortTimeDelta = base::Hours(2);
+
+}  // namespace
+
+namespace policy {
+
+class ActiveDirectoryMigrationManagerTest : public testing::Test {
+ public:
+  ActiveDirectoryMigrationManagerTest()
+      : task_environment_(base::test::TaskEnvironment::TimeSource::MOCK_TIME),
+        local_state_(TestingBrowserProcess::GetGlobal()) {
+    chromeos::SessionManagerClient::InitializeFake();
+    session_manager_client_ = chromeos::FakeSessionManagerClient::Get();
+
+    SetEnrollmentIdUploadedPref(/*value=*/false);
+    SetChromadMigrationEnabledPref(/*value=*/false);
+    SetLastMigrationAttemptTimePref(base::Time::Now() - kLongTimeDelta);
+  }
+
+  ~ActiveDirectoryMigrationManagerTest() override {
+    migration_manager_->Shutdown();
+    migration_manager_.reset();
+    chromeos::SessionManagerClient::Shutdown();
+  }
+
+ protected:
+  void ExpectStatus(base::RunLoop* run_loop,
+                    bool expect_started,
+                    bool expect_rescheduled,
+                    bool started,
+                    bool rescheduled) {
+    EXPECT_EQ(expect_started, started);
+    EXPECT_EQ(expect_rescheduled, rescheduled);
+    run_loop->Quit();
+  }
+
+  // Sets expectations for an attempt to start the device migration.
+  void ExpectAttemptToMigrate(base::RunLoop* run_loop,
+                              bool expect_started,
+                              bool expect_rescheduled) {
+    migration_manager_->SetStatusCallbackForTesting(base::BindOnce(
+        &ActiveDirectoryMigrationManagerTest::ExpectStatus,
+        base::Unretained(this), run_loop, expect_started, expect_rescheduled));
+  }
+
+  void CreateMigrationManager() {
+    migration_manager_ =
+        std::make_unique<ActiveDirectoryMigrationManager>(local_state_.Get());
+  }
+
+  // Sets the value of `kEnrollmentIdUploadedOnChromad` pref.
+  void SetEnrollmentIdUploadedPref(bool value) {
+    local_state_.Get()->SetBoolean(prefs::kEnrollmentIdUploadedOnChromad,
+                                   value);
+  }
+
+  // Sets the value of `kChromadToCloudMigrationEnabled` pref.
+  void SetChromadMigrationEnabledPref(bool value) {
+    local_state_.Get()->SetBoolean(ash::prefs::kChromadToCloudMigrationEnabled,
+                                   value);
+  }
+
+  // Sets the value of `kLastChromadMigrationAttemptTime` pref.
+  void SetLastMigrationAttemptTimePref(base::Time value) {
+    local_state_.Get()->SetTime(prefs::kLastChromadMigrationAttemptTime, value);
+  }
+
+  session_manager::SessionManager session_manager_;
+  raw_ptr<chromeos::FakeSessionManagerClient> session_manager_client_;
+  content::BrowserTaskEnvironment task_environment_;
+  base::RunLoop run_loop_first_;
+  base::RunLoop run_loop_following_;
+  ScopedTestingLocalState local_state_;
+  std::unique_ptr<ActiveDirectoryMigrationManager> migration_manager_;
+};
+
+// Starting with preconditions met: the manager triggers the migration.
+TEST_F(ActiveDirectoryMigrationManagerTest, ConditionsAlreadyMetSucceeds) {
+  SetEnrollmentIdUploadedPref(/*value=*/true);
+  SetChromadMigrationEnabledPref(/*value=*/true);
+
+  CreateMigrationManager();
+
+  // Retry not scheduled, because the migration has already started.
+  ExpectAttemptToMigrate(&run_loop_first_, /*expect_started=*/true,
+                         /*expect_rescheduled=*/false);
+
+  migration_manager_->Init();
+  run_loop_first_.Run();
+
+  EXPECT_EQ(session_manager_client_->start_device_wipe_call_count(), 1);
+}
+
+// Login screen opened, but other conditions missing: the manager doesn't
+// trigger the migration.
+TEST_F(ActiveDirectoryMigrationManagerTest, LoginScreenOpenedFails) {
+  CreateMigrationManager();
+
+  // Retry scheduled, because the devices is on the login screen.
+  ExpectAttemptToMigrate(&run_loop_first_, /*expect_started=*/false,
+                         /*expect_rescheduled=*/true);
+
+  migration_manager_->Init();
+  run_loop_first_.Run();
+
+  EXPECT_EQ(session_manager_client_->start_device_wipe_call_count(), 0);
+
+  // Retry not scheduled, because there is still one retry pending.
+  ExpectAttemptToMigrate(&run_loop_following_, /*expect_started=*/false,
+                         /*expect_rescheduled=*/false);
+
+  session_manager_.NotifyLoginOrLockScreenVisible();
+  run_loop_following_.Run();
+
+  EXPECT_EQ(session_manager_client_->start_device_wipe_call_count(), 0);
+}
+
+// Login screen opened, and other conditions met: the manager triggers the
+// migration.
+TEST_F(ActiveDirectoryMigrationManagerTest, LoginScreenOpenedSucceeds) {
+  // Starting with `kLastChromadMigrationAttemptTime` set to a short time ago,
+  // to prevent the migration from starting during the initialization.
+  SetEnrollmentIdUploadedPref(/*value=*/true);
+  SetChromadMigrationEnabledPref(/*value=*/true);
+  SetLastMigrationAttemptTimePref(base::Time::Now() - kShortTimeDelta);
+
+  CreateMigrationManager();
+
+  // Retry scheduled, because the device cannot start a powerwash yet.
+  ExpectAttemptToMigrate(&run_loop_first_, /*expect_started=*/false,
+                         /*expect_rescheduled=*/true);
+
+  migration_manager_->Init();
+  run_loop_first_.Run();
+
+  EXPECT_EQ(session_manager_client_->start_device_wipe_call_count(), 0);
+
+  // Reverting this pref to a long time ago, to allow the migration to start.
+  SetLastMigrationAttemptTimePref(base::Time::Now() - kLongTimeDelta);
+
+  // Retry not scheduled, because the migration has already started.
+  ExpectAttemptToMigrate(&run_loop_following_, /*expect_started=*/true,
+                         /*expect_rescheduled=*/false);
+
+  session_manager_.NotifyLoginOrLockScreenVisible();
+  run_loop_following_.Run();
+
+  EXPECT_EQ(session_manager_client_->start_device_wipe_call_count(), 1);
+}
+
+// EID uploaded, but other conditions missing: the manager doesn't trigger the
+// migration.
+TEST_F(ActiveDirectoryMigrationManagerTest, EnrollmentIdUploadedFails) {
+  // Simulates user logged in.
+  session_manager_.SessionStarted();
+  CreateMigrationManager();
+
+  // Retry not scheduled, because the user is logged in.
+  ExpectAttemptToMigrate(&run_loop_first_, /*expect_started=*/false,
+                         /*expect_rescheduled=*/false);
+
+  EXPECT_EQ(session_manager_client_->start_device_wipe_call_count(), 0);
+
+  migration_manager_->Init();
+  run_loop_first_.Run();
+
+  // Retry not scheduled, because the user is logged in.
+  ExpectAttemptToMigrate(&run_loop_following_, /*expect_started=*/false,
+                         /*expect_rescheduled=*/false);
+
+  SetEnrollmentIdUploadedPref(/*value=*/true);
+  run_loop_following_.Run();
+
+  EXPECT_EQ(session_manager_client_->start_device_wipe_call_count(), 0);
+}
+
+// EID uploaded, and other conditions met: the manager triggers the migration.
+TEST_F(ActiveDirectoryMigrationManagerTest, EnrollmentIdUploadedSucceeds) {
+  SetChromadMigrationEnabledPref(/*value=*/true);
+  CreateMigrationManager();
+
+  // Retry scheduled, because the device is on the login screen.
+  ExpectAttemptToMigrate(&run_loop_first_, /*expect_started=*/false,
+                         /*expect_rescheduled=*/true);
+
+  migration_manager_->Init();
+  run_loop_first_.Run();
+
+  EXPECT_EQ(session_manager_client_->start_device_wipe_call_count(), 0);
+
+  // Retry not scheduled, because the migration has already started.
+  ExpectAttemptToMigrate(&run_loop_following_, /*expect_started=*/true,
+                         /*expect_rescheduled=*/false);
+
+  SetEnrollmentIdUploadedPref(/*value=*/true);
+  run_loop_following_.Run();
+
+  EXPECT_EQ(session_manager_client_->start_device_wipe_call_count(), 1);
+}
+
+// Policy enabled, but other conditions missing: the manager doesn't trigger the
+// migration.
+TEST_F(ActiveDirectoryMigrationManagerTest, PolicyEnabledFails) {
+  // Simulates user logged in.
+  session_manager_.SessionStarted();
+  CreateMigrationManager();
+
+  // Retry not scheduled, because the user is logged in.
+  ExpectAttemptToMigrate(&run_loop_first_, /*expect_started=*/false,
+                         /*expect_rescheduled=*/false);
+
+  migration_manager_->Init();
+  run_loop_first_.Run();
+
+  EXPECT_EQ(session_manager_client_->start_device_wipe_call_count(), 0);
+
+  // Retry not scheduled, because the user is logged in.
+  ExpectAttemptToMigrate(&run_loop_following_, /*expect_started=*/false,
+                         /*expect_rescheduled=*/false);
+
+  SetChromadMigrationEnabledPref(/*value=*/true);
+  run_loop_following_.Run();
+
+  EXPECT_EQ(session_manager_client_->start_device_wipe_call_count(), 0);
+}
+
+// Policy enabled, and other conditions met: the manager triggers the migration.
+TEST_F(ActiveDirectoryMigrationManagerTest, PolicyEnabledSucceeds) {
+  SetEnrollmentIdUploadedPref(/*value=*/true);
+  CreateMigrationManager();
+
+  // Retry scheduled, because the device is on the login screen.
+  ExpectAttemptToMigrate(&run_loop_first_, /*expect_started=*/false,
+                         /*expect_rescheduled=*/true);
+
+  migration_manager_->Init();
+  run_loop_first_.Run();
+
+  EXPECT_EQ(session_manager_client_->start_device_wipe_call_count(), 0);
+
+  // Retry not scheduled, because the migration has already started.
+  ExpectAttemptToMigrate(&run_loop_following_, /*expect_started=*/true,
+                         /*expect_rescheduled=*/false);
+
+  SetChromadMigrationEnabledPref(/*value=*/true);
+  run_loop_following_.Run();
+
+  EXPECT_EQ(session_manager_client_->start_device_wipe_call_count(), 1);
+}
+
+// Policy disabled: the manager doesn't trigger the migration.
+TEST_F(ActiveDirectoryMigrationManagerTest, PolicyDisabledFails) {
+  // Starting with the policy enabled.
+  SetChromadMigrationEnabledPref(/*value=*/true);
+  CreateMigrationManager();
+
+  // Retry scheduled, because the device is on the login screen.
+  ExpectAttemptToMigrate(&run_loop_first_, /*expect_started=*/false,
+                         /*expect_rescheduled=*/true);
+
+  migration_manager_->Init();
+  run_loop_first_.Run();
+
+  EXPECT_EQ(session_manager_client_->start_device_wipe_call_count(), 0);
+
+  // Retry not scheduled, because there is still one retry pending.
+  ExpectAttemptToMigrate(&run_loop_following_, /*expect_started=*/false,
+                         /*expect_rescheduled=*/false);
+
+  SetChromadMigrationEnabledPref(/*value=*/false);
+  run_loop_following_.Run();
+
+  EXPECT_EQ(session_manager_client_->start_device_wipe_call_count(), 0);
+}
+
+// Manager retries to start the migration, after the retry delay has passed.
+TEST_F(ActiveDirectoryMigrationManagerTest, RetryRunAfterSomeTime) {
+  CreateMigrationManager();
+
+  // Retry scheduled, because the device is on the login screen.
+  ExpectAttemptToMigrate(&run_loop_first_, /*expect_started=*/false,
+                         /*expect_rescheduled=*/true);
+
+  migration_manager_->Init();
+  run_loop_first_.Run();
+  EXPECT_EQ(session_manager_client_->start_device_wipe_call_count(), 0);
+
+  // Retry scheduled, because the device is on the login screen.
+  ExpectAttemptToMigrate(&run_loop_following_, /*expect_started=*/false,
+                         /*expect_rescheduled=*/true);
+
+  run_loop_following_.Run();
+  EXPECT_EQ(session_manager_client_->start_device_wipe_call_count(), 0);
+}
+
+// Manager has recently sent a powerwash request: migration is not started.
+TEST_F(ActiveDirectoryMigrationManagerTest, PowerwashRecentlyRequestedFails) {
+  SetEnrollmentIdUploadedPref(/*value=*/true);
+  SetChromadMigrationEnabledPref(/*value=*/true);
+  SetLastMigrationAttemptTimePref(base::Time::Now() - kShortTimeDelta);
+
+  CreateMigrationManager();
+
+  // Retry scheduled, because the device cannot start a powerwash yet.
+  ExpectAttemptToMigrate(&run_loop_first_, /*expect_started=*/false,
+                         /*expect_rescheduled=*/true);
+
+  migration_manager_->Init();
+  run_loop_first_.Run();
+
+  EXPECT_EQ(session_manager_client_->start_device_wipe_call_count(), 0);
+}
+
+}  // namespace policy
diff --git a/chrome/browser/ash/policy/active_directory/component_active_directory_policy_browsertest.cc b/chrome/browser/ash/policy/active_directory/component_active_directory_policy_browsertest.cc
index 2bfe12cd..73fa7c6 100644
--- a/chrome/browser/ash/policy/active_directory/component_active_directory_policy_browsertest.cc
+++ b/chrome/browser/ash/policy/active_directory/component_active_directory_policy_browsertest.cc
@@ -82,13 +82,13 @@
     ExtensionBrowserTest::SetUpCommandLine(command_line);
 
     // Log in as Active Directory user.
-    command_line->AppendSwitchASCII(::chromeos::switches::kLoginUser,
+    command_line->AppendSwitchASCII(::ash::switches::kLoginUser,
                                     ::user_manager::kStubAdUserEmail);
 
     // Without this, user manager code will shut down Chrome since it can't
     // find any policy.
     command_line->AppendSwitchASCII(
-        ::chromeos::switches::kAllowFailedPolicyFetchForTest, "true");
+        ::ash::switches::kAllowFailedPolicyFetchForTest, "true");
   }
 
   void SetUpOnMainThread() override {
diff --git a/chrome/browser/ash/policy/affiliation/affiliation_test_helper.cc b/chrome/browser/ash/policy/affiliation/affiliation_test_helper.cc
index fac85b5..7195dbc 100644
--- a/chrome/browser/ash/policy/affiliation/affiliation_test_helper.cc
+++ b/chrome/browser/ash/policy/affiliation/affiliation_test_helper.cc
@@ -215,13 +215,12 @@
 // static
 void AffiliationTestHelper::AppendCommandLineSwitchesForLoginManager(
     base::CommandLine* command_line) {
-  command_line->AppendSwitch(chromeos::switches::kLoginManager);
-  command_line->AppendSwitch(chromeos::switches::kForceLoginManagerInTests);
+  command_line->AppendSwitch(ash::switches::kLoginManager);
+  command_line->AppendSwitch(ash::switches::kForceLoginManagerInTests);
   // LoginManager tests typically don't stand up a policy test server but
   // instead inject policies directly through a SessionManagerClient. So allow
   // policy fetches to fail - this is expected.
-  command_line->AppendSwitch(
-      chromeos::switches::kAllowFailedPolicyFetchForTest);
+  command_line->AppendSwitch(ash::switches::kAllowFailedPolicyFetchForTest);
 }
 
 }  // namespace policy
diff --git a/chrome/browser/ash/policy/affiliation/user_affiliation_browsertest.cc b/chrome/browser/ash/policy/affiliation/user_affiliation_browsertest.cc
index e4853e2..3f24317 100644
--- a/chrome/browser/ash/policy/affiliation/user_affiliation_browsertest.cc
+++ b/chrome/browser/ash/policy/affiliation/user_affiliation_browsertest.cc
@@ -135,10 +135,10 @@
       const cryptohome::AccountIdentifier cryptohome_id =
           cryptohome::CreateAccountIdentifierFromAccountId(
               affiliation_mixin_.account_id());
-      command_line->AppendSwitchASCII(chromeos::switches::kLoginUser,
+      command_line->AppendSwitchASCII(ash::switches::kLoginUser,
                                       cryptohome_id.account_id());
       command_line->AppendSwitchASCII(
-          chromeos::switches::kLoginProfile,
+          ash::switches::kLoginProfile,
           chromeos::UserDataAuthClient::GetStubSanitizedUsername(
               cryptohome_id));
     }
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 f58e775c..6793de5a 100644
--- a/chrome/browser/ash/policy/core/browser_policy_connector_ash.cc
+++ b/chrome/browser/ash/policy/core/browser_policy_connector_ash.cc
@@ -31,6 +31,7 @@
 #include "chrome/browser/ash/attestation/attestation_ca_client.h"
 #include "chrome/browser/ash/login/enrollment/auto_enrollment_controller.h"
 #include "chrome/browser/ash/notifications/adb_sideloading_policy_change_notification.h"
+#include "chrome/browser/ash/policy/active_directory/active_directory_migration_manager.h"
 #include "chrome/browser/ash/policy/active_directory/active_directory_policy_manager.h"
 #include "chrome/browser/ash/policy/core/device_cloud_policy_store_ash.h"
 #include "chrome/browser/ash/policy/core/device_local_account.h"
@@ -214,8 +215,8 @@
             CreateBackgroundTaskRunner(), url_loader_factory);
     device_local_account_policy_service_->Connect(device_management_service());
   } else if (IsForcedReEnrollmentEnabled()) {
-    // Initialize state keys upload mechanism to DM Server in Active Directory
-    // mode.
+    // Initialize state keys and enrollment ID upload mechanisms to DM Server in
+    // Active Directory mode.
     state_keys_broker_ = std::make_unique<ServerBackedStateKeysBroker>(
         chromeos::SessionManagerClient::Get());
     active_directory_device_state_uploader_ =
@@ -225,6 +226,12 @@
             url_loader_factory, std::make_unique<DMTokenStorage>(local_state),
             local_state);
     active_directory_device_state_uploader_->Init();
+
+    // Initialize the manager that will start the migration of Chromad devices
+    // into cloud management, when all pre-requisites are met.
+    active_directory_migration_manager_ =
+        std::make_unique<ActiveDirectoryMigrationManager>(local_state);
+    active_directory_migration_manager_->Init();
   }
 
   if (device_cloud_policy_manager_) {
@@ -351,6 +358,9 @@
   if (active_directory_device_state_uploader_)
     active_directory_device_state_uploader_->Shutdown();
 
+  if (active_directory_migration_manager_)
+    active_directory_migration_manager_->Shutdown();
+
   if (device_cloud_policy_initializer_)
     device_cloud_policy_initializer_->Shutdown();
 
diff --git a/chrome/browser/ash/policy/core/browser_policy_connector_ash.h b/chrome/browser/ash/policy/core/browser_policy_connector_ash.h
index 8cfd7ed..b799be7 100644
--- a/chrome/browser/ash/policy/core/browser_policy_connector_ash.h
+++ b/chrome/browser/ash/policy/core/browser_policy_connector_ash.h
@@ -45,6 +45,7 @@
 class DeviceActiveDirectoryPolicyManager;
 class DeviceCloudPolicyInitializer;
 class ActiveDirectoryDeviceStateUploader;
+class ActiveDirectoryMigrationManager;
 class DeviceDockMacAddressHandler;
 class DeviceLocalAccountPolicyService;
 class DeviceNamePolicyHandler;
@@ -278,6 +279,8 @@
       nullptr;
   std::unique_ptr<ActiveDirectoryDeviceStateUploader>
       active_directory_device_state_uploader_;
+  std::unique_ptr<ActiveDirectoryMigrationManager>
+      active_directory_migration_manager_;
   PrefService* local_state_ = nullptr;
   std::unique_ptr<DeviceCloudPolicyInitializer>
       device_cloud_policy_initializer_;
diff --git a/chrome/browser/ash/policy/core/device_cloud_policy_browsertest.cc b/chrome/browser/ash/policy/core/device_cloud_policy_browsertest.cc
index 6e3dafc..871b08a 100644
--- a/chrome/browser/ash/policy/core/device_cloud_policy_browsertest.cc
+++ b/chrome/browser/ash/policy/core/device_cloud_policy_browsertest.cc
@@ -271,8 +271,8 @@
 
   void SetUpCommandLine(base::CommandLine* command_line) override {
     DevicePolicyCrosBrowserTest::SetUpCommandLine(command_line);
-    command_line->AppendSwitch(chromeos::switches::kLoginManager);
-    command_line->AppendSwitch(chromeos::switches::kForceLoginManagerInTests);
+    command_line->AppendSwitch(ash::switches::kLoginManager);
+    command_line->AppendSwitch(ash::switches::kForceLoginManagerInTests);
     // The test app has to be allowlisted for sign-in screen.
     // This test is intentionally not migrated to the new
     // kAllowlistedExtensionID switch to test that the deprecated one keeps
diff --git a/chrome/browser/ash/policy/core/device_cloud_policy_manager_ash.cc b/chrome/browser/ash/policy/core/device_cloud_policy_manager_ash.cc
index f3def81..48bc2e0 100644
--- a/chrome/browser/ash/policy/core/device_cloud_policy_manager_ash.cc
+++ b/chrome/browser/ash/policy/core/device_cloud_policy_manager_ash.cc
@@ -155,12 +155,12 @@
 DeviceCloudPolicyManagerAsh::GetZeroTouchEnrollmentMode() {
   base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
   if (!command_line->HasSwitch(
-          chromeos::switches::kEnterpriseEnableZeroTouchEnrollment)) {
+          ash::switches::kEnterpriseEnableZeroTouchEnrollment)) {
     return ZeroTouchEnrollmentMode::DISABLED;
   }
 
   std::string value = command_line->GetSwitchValueASCII(
-      chromeos::switches::kEnterpriseEnableZeroTouchEnrollment);
+      ash::switches::kEnterpriseEnableZeroTouchEnrollment);
   if (value == kZeroTouchEnrollmentForced) {
     return ZeroTouchEnrollmentMode::FORCED;
   }
@@ -171,7 +171,7 @@
     return ZeroTouchEnrollmentMode::ENABLED;
   }
   LOG(WARNING) << "Malformed value \"" << value << "\" for switch --"
-               << chromeos::switches::kEnterpriseEnableZeroTouchEnrollment
+               << ash::switches::kEnterpriseEnableZeroTouchEnrollment
                << ". Ignoring switch.";
   return ZeroTouchEnrollmentMode::DISABLED;
 }
@@ -223,7 +223,7 @@
   // Don't create a MachineCertificateUploader or start the
   // AttestationPolicyObserver if machine cert requests are disabled.
   if (!(base::CommandLine::ForCurrentProcess()->HasSwitch(
-          chromeos::switches::kDisableMachineCertRequest))) {
+          ash::switches::kDisableMachineCertRequest))) {
     machine_certificate_uploader_ =
         std::make_unique<ash::attestation::MachineCertificateUploaderImpl>(
             client());
diff --git a/chrome/browser/ash/policy/core/device_cloud_policy_manager_ash_unittest.cc b/chrome/browser/ash/policy/core/device_cloud_policy_manager_ash_unittest.cc
index 0f4e9bc..d32280f 100644
--- a/chrome/browser/ash/policy/core/device_cloud_policy_manager_ash_unittest.cc
+++ b/chrome/browser/ash/policy/core/device_cloud_policy_manager_ash_unittest.cc
@@ -923,7 +923,7 @@
 TEST_P(DeviceCloudPolicyManagerAshEnrollmentTest, DisableMachineCertReq) {
   // Simulate the flag --disable-machine-cert-request being provided to Chrome.
   base::CommandLine::ForCurrentProcess()->AppendSwitch(
-      chromeos::switches::kDisableMachineCertRequest);
+      ash::switches::kDisableMachineCertRequest);
 
   // Set expectation that a request for a machine cert is never made.
   EXPECT_CALL(
diff --git a/chrome/browser/ash/policy/core/device_local_account_browsertest.cc b/chrome/browser/ash/policy/core/device_local_account_browsertest.cc
index 3710a9c..c2e15e8 100644
--- a/chrome/browser/ash/policy/core/device_local_account_browsertest.cc
+++ b/chrome/browser/ash/policy/core/device_local_account_browsertest.cc
@@ -411,9 +411,9 @@
 
   void SetUpCommandLine(base::CommandLine* command_line) override {
     DevicePolicyCrosBrowserTest::SetUpCommandLine(command_line);
-    command_line->AppendSwitch(chromeos::switches::kLoginManager);
-    command_line->AppendSwitch(chromeos::switches::kForceLoginManagerInTests);
-    command_line->AppendSwitchASCII(chromeos::switches::kLoginProfile, "user");
+    command_line->AppendSwitch(ash::switches::kLoginManager);
+    command_line->AppendSwitch(ash::switches::kForceLoginManagerInTests);
+    command_line->AppendSwitchASCII(ash::switches::kLoginProfile, "user");
   }
 
   void SetUpInProcessBrowserTestFixture() override {
diff --git a/chrome/browser/ash/policy/core/policy_oauth2_token_fetcher.cc b/chrome/browser/ash/policy/core/policy_oauth2_token_fetcher.cc
index 3ba665c4..001739f0 100644
--- a/chrome/browser/ash/policy/core/policy_oauth2_token_fetcher.cc
+++ b/chrome/browser/ash/policy/core/policy_oauth2_token_fetcher.cc
@@ -160,7 +160,7 @@
 void PolicyOAuth2TokenFetcherImpl::StartFetchingRefreshToken() {
   // Don't fetch tokens for test.
   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
-          chromeos::switches::kDisableGaiaServices)) {
+          ash::switches::kDisableGaiaServices)) {
     failed_ = true;
     ForwardPolicyToken(
         std::string(),
diff --git a/chrome/browser/ash/policy/core/user_cloud_policy_manager_ash.cc b/chrome/browser/ash/policy/core/user_cloud_policy_manager_ash.cc
index fe30e2b7..895cb6a 100644
--- a/chrome/browser/ash/policy/core/user_cloud_policy_manager_ash.cc
+++ b/chrome/browser/ash/policy/core/user_cloud_policy_manager_ash.cc
@@ -173,7 +173,7 @@
   // manually injected policy even though the profile itself is synchronously
   // initialized.
   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
-          chromeos::switches::kWaitForInitialPolicyFetchForTest)) {
+          ash::switches::kWaitForInitialPolicyFetchForTest)) {
     waiting_for_policy_fetch_ = true;
   }
 
@@ -253,7 +253,7 @@
     // never be set (because we can't wait).
     CHECK(!waiting_for_policy_fetch_ ||
           base::CommandLine::ForCurrentProcess()->HasSwitch(
-              chromeos::switches::kWaitForInitialPolicyFetchForTest));
+              ash::switches::kWaitForInitialPolicyFetchForTest));
     if (!client()->is_registered() &&
         enforcement_type_ != PolicyEnforcement::kPolicyOptional) {
       // We expected to load policy, but we don't have policy, so exit the
@@ -559,7 +559,7 @@
     // any of those flags set at startup anyway for ephemeral sessions.
     base::CommandLine command_line =
         base::CommandLine(base::CommandLine::NO_PROGRAM);
-    command_line.AppendSwitchASCII(chromeos::switches::kProfileRequiresPolicy,
+    command_line.AppendSwitchASCII(ash::switches::kProfileRequiresPolicy,
                                    policy_required ? "true" : "false");
     base::CommandLine::StringVector flags;
     flags.assign(command_line.argv().begin() + 1, command_line.argv().end());
@@ -590,7 +590,7 @@
 void UserCloudPolicyManagerAsh::FetchPolicyOAuthToken() {
   // By-pass token fetching for test.
   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
-          chromeos::switches::kDisableGaiaServices)) {
+          ash::switches::kDisableGaiaServices)) {
     OnOAuth2PolicyTokenFetched(
         "fake_policy_token",
         GoogleServiceAuthError(GoogleServiceAuthError::NONE));
@@ -623,7 +623,7 @@
   }
 
   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
-          chromeos::switches::kWaitForInitialPolicyFetchForTest)) {
+          ash::switches::kWaitForInitialPolicyFetchForTest)) {
     // Some tests don't want to complete policy initialization until they have
     // manually injected policy. Do not treat this as a policy fetch error.
     return;
diff --git a/chrome/browser/ash/policy/core/user_policy_manager_builder_ash.cc b/chrome/browser/ash/policy/core/user_policy_manager_builder_ash.cc
index 55e4377e..65a5f94f 100644
--- a/chrome/browser/ash/policy/core/user_policy_manager_builder_ash.cc
+++ b/chrome/browser/ash/policy/core/user_policy_manager_builder_ash.cc
@@ -160,9 +160,8 @@
   const bool policy_check_required =
       (requires_policy_user_property == ProfileRequiresPolicy::kUnknown) &&
       !is_stub_user && !is_active_directory &&
-      !command_line->HasSwitch(chromeos::switches::kProfileRequiresPolicy) &&
-      !command_line->HasSwitch(
-          chromeos::switches::kAllowFailedPolicyFetchForTest);
+      !command_line->HasSwitch(ash::switches::kProfileRequiresPolicy) &&
+      !command_line->HasSwitch(ash::switches::kAllowFailedPolicyFetchForTest);
 
   // |force_immediate_load| is true during Chrome restart, or during
   // initialization of stub user profiles when running tests. If we ever get
@@ -183,13 +182,12 @@
   // command-line flag (required for ephemeral users who are not persisted
   // in the known_user database).
   const bool policy_required =
-      !command_line->HasSwitch(
-          chromeos::switches::kAllowFailedPolicyFetchForTest) &&
+      !command_line->HasSwitch(ash::switches::kAllowFailedPolicyFetchForTest) &&
       (is_active_directory ||
        (requires_policy_user_property ==
         ProfileRequiresPolicy::kPolicyRequired) ||
        (command_line->GetSwitchValueASCII(
-            chromeos::switches::kProfileRequiresPolicy) == "true"));
+            ash::switches::kProfileRequiresPolicy) == "true"));
 
   // We should never have |policy_required| and |policy_check_required| both
   // set, since the |policy_required| implies that we already know that
diff --git a/chrome/browser/ash/policy/dev_mode/dev_mode_policy_util.cc b/chrome/browser/ash/policy/dev_mode/dev_mode_policy_util.cc
index f792b266..27efd700 100644
--- a/chrome/browser/ash/policy/dev_mode/dev_mode_policy_util.cc
+++ b/chrome/browser/ash/policy/dev_mode/dev_mode_policy_util.cc
@@ -47,7 +47,7 @@
 
 bool IsDeviceBlockDevModePolicyAllowed() {
   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
-          chromeos::switches::kDisallowPolicyBlockDevMode)) {
+          ash::switches::kDisallowPolicyBlockDevMode)) {
     base::SysInfo::CrashIfChromeOSNonTestImage();
     return false;
   }
diff --git a/chrome/browser/ash/policy/display/display_resolution_handler_browsertest.cc b/chrome/browser/ash/policy/display/display_resolution_handler_browsertest.cc
index d4151ab4..f58f3c2d9 100644
--- a/chrome/browser/ash/policy/display/display_resolution_handler_browsertest.cc
+++ b/chrome/browser/ash/policy/display/display_resolution_handler_browsertest.cc
@@ -173,8 +173,8 @@
       const DeviceDisplayResolutionTestBase&) = delete;
 
   void SetUpCommandLine(base::CommandLine* command_line) override {
-    command_line->AppendSwitch(chromeos::switches::kLoginManager);
-    command_line->AppendSwitch(chromeos::switches::kForceLoginManagerInTests);
+    command_line->AppendSwitch(ash::switches::kLoginManager);
+    command_line->AppendSwitch(ash::switches::kForceLoginManagerInTests);
     command_line->AppendSwitch(switches::kUseFirstDisplayAsInternal);
   }
 
diff --git a/chrome/browser/ash/policy/display/display_rotation_default_handler_browsertest.cc b/chrome/browser/ash/policy/display/display_rotation_default_handler_browsertest.cc
index 8858602..5cb16035 100644
--- a/chrome/browser/ash/policy/display/display_rotation_default_handler_browsertest.cc
+++ b/chrome/browser/ash/policy/display/display_rotation_default_handler_browsertest.cc
@@ -34,8 +34,8 @@
       public testing::WithParamInterface<display::Display::Rotation> {
  protected:
   void SetUpCommandLine(base::CommandLine* command_line) override {
-    command_line->AppendSwitch(chromeos::switches::kLoginManager);
-    command_line->AppendSwitch(chromeos::switches::kForceLoginManagerInTests);
+    command_line->AppendSwitch(ash::switches::kLoginManager);
+    command_line->AppendSwitch(ash::switches::kForceLoginManagerInTests);
   }
 
   void SetRotationPolicy(int rotation) {
diff --git a/chrome/browser/ash/policy/enrollment/device_cloud_policy_initializer_unittest.cc b/chrome/browser/ash/policy/enrollment/device_cloud_policy_initializer_unittest.cc
index a623c99b..36f97a0 100644
--- a/chrome/browser/ash/policy/enrollment/device_cloud_policy_initializer_unittest.cc
+++ b/chrome/browser/ash/policy/enrollment/device_cloud_policy_initializer_unittest.cc
@@ -64,7 +64,7 @@
   const ZeroTouchParam& param = GetParam();
   if (param.enable_zero_touch_flag != nullptr) {
     base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
-        chromeos::switches::kEnterpriseEnableZeroTouchEnrollment,
+        ash::switches::kEnterpriseEnableZeroTouchEnrollment,
         param.enable_zero_touch_flag);
   }
 }
diff --git a/chrome/browser/ash/policy/enrollment/enrollment_handler.cc b/chrome/browser/ash/policy/enrollment/enrollment_handler.cc
index 19c7a6e3..988ad80 100644
--- a/chrome/browser/ash/policy/enrollment/enrollment_handler.cc
+++ b/chrome/browser/ash/policy/enrollment/enrollment_handler.cc
@@ -106,7 +106,7 @@
   return em::DeviceRegisterRequest::FLAVOR_ENROLLMENT_MANUAL;
 }
 
-// A utility funciton of base::ReadFileToString which returns an optional
+// A utility function of base::ReadFileToString which returns an optional
 // string.
 // TODO(mukai): move this to base/files.
 absl::optional<std::string> ReadFileToOptionalString(
@@ -284,7 +284,7 @@
 
   // Currently reven devices don't support sever-backed state keys, but they
   // also don't support FRE/AutoRE so don't block enrollment on the
-  // availablility of state keys.
+  // availability of state keys.
   // TODO(b/208705225): Remove this special case when reven supports state keys.
   if (ash::switches::IsRevenBranding()) {
     LOG(WARNING) << "Skipping state keys.";
diff --git a/chrome/browser/ash/policy/handlers/device_system_use_24hour_clock_browsertest.cc b/chrome/browser/ash/policy/handlers/device_system_use_24hour_clock_browsertest.cc
index cee310d5..963ef6f0d 100644
--- a/chrome/browser/ash/policy/handlers/device_system_use_24hour_clock_browsertest.cc
+++ b/chrome/browser/ash/policy/handlers/device_system_use_24hour_clock_browsertest.cc
@@ -41,7 +41,7 @@
 
   void SetUpCommandLine(base::CommandLine* command_line) override {
     command_line->AppendSwitch(ash::switches::kLoginManager);
-    command_line->AppendSwitch(chromeos::switches::kForceLoginManagerInTests);
+    command_line->AppendSwitch(ash::switches::kForceLoginManagerInTests);
   }
 
   void TearDownOnMainThread() override {
diff --git a/chrome/browser/ash/policy/handlers/minimum_version_policy_handler.cc b/chrome/browser/ash/policy/handlers/minimum_version_policy_handler.cc
index b6d3c581..24fae9c6 100644
--- a/chrome/browser/ash/policy/handlers/minimum_version_policy_handler.cc
+++ b/chrome/browser/ash/policy/handlers/minimum_version_policy_handler.cc
@@ -338,7 +338,7 @@
 
 void MinimumVersionPolicyHandler::OnFetchEolInfo(
     const chromeos::UpdateEngineClient::EolInfo info) {
-  if (!chromeos::switches::IsAueReachedForUpdateRequiredForTest() &&
+  if (!ash::switches::IsAueReachedForUpdateRequiredForTest() &&
       (info.eol_date.is_null() || info.eol_date > update_required_time_)) {
     // End of life is not reached. Start update with |warning_time_|.
     eol_reached_ = false;
diff --git a/chrome/browser/ash/policy/handlers/minimum_version_policy_handler_browsertest.cc b/chrome/browser/ash/policy/handlers/minimum_version_policy_handler_browsertest.cc
index b87e421..dda31d5 100644
--- a/chrome/browser/ash/policy/handlers/minimum_version_policy_handler_browsertest.cc
+++ b/chrome/browser/ash/policy/handlers/minimum_version_policy_handler_browsertest.cc
@@ -767,7 +767,7 @@
       return false;
     // Postpone login host creation.
     base::CommandLine::ForCurrentProcess()->RemoveSwitch(
-        chromeos::switches::kForceLoginManagerInTests);
+        ash::switches::kForceLoginManagerInTests);
     return true;
   }
 };
diff --git a/chrome/browser/ash/policy/handlers/power_policy_browsertest.cc b/chrome/browser/ash/policy/handlers/power_policy_browsertest.cc
index fc2834a..74db98b6 100644
--- a/chrome/browser/ash/policy/handlers/power_policy_browsertest.cc
+++ b/chrome/browser/ash/policy/handlers/power_policy_browsertest.cc
@@ -277,8 +277,8 @@
 void PowerPolicyLoginScreenBrowserTest::SetUpCommandLine(
     base::CommandLine* command_line) {
   PowerPolicyBrowserTestBase::SetUpCommandLine(command_line);
-  command_line->AppendSwitch(chromeos::switches::kLoginManager);
-  command_line->AppendSwitch(chromeos::switches::kForceLoginManagerInTests);
+  command_line->AppendSwitch(ash::switches::kLoginManager);
+  command_line->AppendSwitch(ash::switches::kForceLoginManagerInTests);
 }
 
 void PowerPolicyLoginScreenBrowserTest::SetUpOnMainThread() {
diff --git a/chrome/browser/ash/policy/login/device_login_screen_policy_browsertest.cc b/chrome/browser/ash/policy/login/device_login_screen_policy_browsertest.cc
index 2844c33..e4a6f678 100644
--- a/chrome/browser/ash/policy/login/device_login_screen_policy_browsertest.cc
+++ b/chrome/browser/ash/policy/login/device_login_screen_policy_browsertest.cc
@@ -138,8 +138,8 @@
 void DeviceLoginScreenPolicyBrowsertest::SetUpCommandLine(
     base::CommandLine* command_line) {
   DevicePolicyCrosBrowserTest::SetUpCommandLine(command_line);
-  command_line->AppendSwitch(chromeos::switches::kLoginManager);
-  command_line->AppendSwitch(chromeos::switches::kForceLoginManagerInTests);
+  command_line->AppendSwitch(ash::switches::kLoginManager);
+  command_line->AppendSwitch(ash::switches::kForceLoginManagerInTests);
 }
 
 bool DeviceLoginScreenPolicyBrowsertest::IsPrefManaged(
diff --git a/chrome/browser/ash/policy/login/login_policy_test_base.cc b/chrome/browser/ash/policy/login/login_policy_test_base.cc
index 4938a93..64fc73d 100644
--- a/chrome/browser/ash/policy/login/login_policy_test_base.cc
+++ b/chrome/browser/ash/policy/login/login_policy_test_base.cc
@@ -51,9 +51,8 @@
 
 void LoginPolicyTestBase::SetUpCommandLine(base::CommandLine* command_line) {
   OobeBaseTest::SetUpCommandLine(command_line);
-  command_line->AppendSwitch(chromeos::switches::kDisableGaiaServices);
-  command_line->AppendSwitch(
-      chromeos::switches::kSkipForceOnlineSignInForTesting);
+  command_line->AppendSwitch(ash::switches::kDisableGaiaServices);
+  command_line->AppendSwitch(ash::switches::kSkipForceOnlineSignInForTesting);
 }
 
 void LoginPolicyTestBase::SetUpInProcessBrowserTestFixture() {
diff --git a/chrome/browser/ash/policy/login/login_screen_accessibility_policy_browsertest.cc b/chrome/browser/ash/policy/login/login_screen_accessibility_policy_browsertest.cc
index 6ea5a9f..e1d160e 100644
--- a/chrome/browser/ash/policy/login/login_screen_accessibility_policy_browsertest.cc
+++ b/chrome/browser/ash/policy/login/login_screen_accessibility_policy_browsertest.cc
@@ -145,8 +145,8 @@
 void LoginScreenAccessibilityPolicyBrowsertest::SetUpCommandLine(
     base::CommandLine* command_line) {
   DevicePolicyCrosBrowserTest::SetUpCommandLine(command_line);
-  command_line->AppendSwitch(chromeos::switches::kLoginManager);
-  command_line->AppendSwitch(chromeos::switches::kForceLoginManagerInTests);
+  command_line->AppendSwitch(ash::switches::kLoginManager);
+  command_line->AppendSwitch(ash::switches::kForceLoginManagerInTests);
 }
 
 bool LoginScreenAccessibilityPolicyBrowsertest::IsPrefManaged(
diff --git a/chrome/browser/ash/policy/login/login_screen_default_policy_browsertest.cc b/chrome/browser/ash/policy/login/login_screen_default_policy_browsertest.cc
index 7d63965..2ff9b1f681 100644
--- a/chrome/browser/ash/policy/login/login_screen_default_policy_browsertest.cc
+++ b/chrome/browser/ash/policy/login/login_screen_default_policy_browsertest.cc
@@ -167,8 +167,8 @@
 void LoginScreenDefaultPolicyLoginScreenBrowsertest::SetUpCommandLine(
     base::CommandLine* command_line) {
   LoginScreenDefaultPolicyBrowsertestBase::SetUpCommandLine(command_line);
-  command_line->AppendSwitch(chromeos::switches::kLoginManager);
-  command_line->AppendSwitch(chromeos::switches::kForceLoginManagerInTests);
+  command_line->AppendSwitch(ash::switches::kLoginManager);
+  command_line->AppendSwitch(ash::switches::kForceLoginManagerInTests);
 }
 
 void LoginScreenDefaultPolicyLoginScreenBrowsertest::SetUpOnMainThread() {
diff --git a/chrome/browser/ash/policy/login/signin_profile_extensions_policy_test_base.cc b/chrome/browser/ash/policy/login/signin_profile_extensions_policy_test_base.cc
index 6278473..863049b 100644
--- a/chrome/browser/ash/policy/login/signin_profile_extensions_policy_test_base.cc
+++ b/chrome/browser/ash/policy/login/signin_profile_extensions_policy_test_base.cc
@@ -26,8 +26,8 @@
 void SigninProfileExtensionsPolicyTestBase::SetUpCommandLine(
     base::CommandLine* command_line) {
   DevicePolicyCrosBrowserTest::SetUpCommandLine(command_line);
-  command_line->AppendSwitch(chromeos::switches::kLoginManager);
-  command_line->AppendSwitch(chromeos::switches::kForceLoginManagerInTests);
+  command_line->AppendSwitch(ash::switches::kLoginManager);
+  command_line->AppendSwitch(ash::switches::kForceLoginManagerInTests);
 }
 
 void SigninProfileExtensionsPolicyTestBase::SetUpOnMainThread() {
diff --git a/chrome/browser/ash/policy/networking/policy_certs_browsertest.cc b/chrome/browser/ash/policy/networking/policy_certs_browsertest.cc
index 51464f1..12581aa 100644
--- a/chrome/browser/ash/policy/networking/policy_certs_browsertest.cc
+++ b/chrome/browser/ash/policy/networking/policy_certs_browsertest.cc
@@ -322,7 +322,7 @@
 
   void SetUpCommandLine(base::CommandLine* command_line) {
     command_line->AppendSwitch(
-        chromeos::switches::kIgnoreUserProfileMappingForTests);
+        ash::switches::kIgnoreUserProfileMappingForTests);
   }
 
   // The test should call this before the initial profile is created by chrome.
@@ -697,10 +697,10 @@
 
   void SetUpCommandLine(base::CommandLine* command_line) override {
     DevicePolicyCrosBrowserTest::SetUpCommandLine(command_line);
-    command_line->AppendSwitch(chromeos::switches::kLoginManager);
-    command_line->AppendSwitch(chromeos::switches::kForceLoginManagerInTests);
-    command_line->AppendSwitchASCII(chromeos::switches::kLoginProfile, "user");
-    command_line->AppendSwitch(chromeos::switches::kOobeSkipPostLogin);
+    command_line->AppendSwitch(ash::switches::kLoginManager);
+    command_line->AppendSwitch(ash::switches::kForceLoginManagerInTests);
+    command_line->AppendSwitchASCII(ash::switches::kLoginProfile, "user");
+    command_line->AppendSwitch(ash::switches::kOobeSkipPostLogin);
   }
 
   ash::EmbeddedPolicyTestServerMixin policy_test_server_mixin_{&mixin_host_};
diff --git a/chrome/browser/ash/policy/reporting/arc_app_install_event_log_collector_unittest.cc b/chrome/browser/ash/policy/reporting/arc_app_install_event_log_collector_unittest.cc
index 4f2b7f11..440fbc89 100644
--- a/chrome/browser/ash/policy/reporting/arc_app_install_event_log_collector_unittest.cc
+++ b/chrome/browser/ash/policy/reporting/arc_app_install_event_log_collector_unittest.cc
@@ -248,7 +248,7 @@
     // Check login after restart. No log is expected.
     ArcAppInstallEventLogCollector collector(delegate(), profile(), packages_);
     base::CommandLine::ForCurrentProcess()->AppendSwitch(
-        chromeos::switches::kLoginUser);
+        ash::switches::kLoginUser);
     collector.OnLogin();
     EXPECT_EQ(1, delegate()->add_for_all_count());
   }
diff --git a/chrome/browser/ash/policy/reporting/arc_app_install_event_log_manager.cc b/chrome/browser/ash/policy/reporting/arc_app_install_event_log_manager.cc
index ca3005c..6004b4bbf 100644
--- a/chrome/browser/ash/policy/reporting/arc_app_install_event_log_manager.cc
+++ b/chrome/browser/ash/policy/reporting/arc_app_install_event_log_manager.cc
@@ -79,7 +79,7 @@
   // If --arc-install-event-chrome-log-for-tests is present, write event logs to
   // Chrome log (/var/log/chrome). LOG(ERROR) ensures that logs are written.
   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
-          chromeos::switches::kArcInstallEventChromeLogForTests)) {
+          ash::switches::kArcInstallEventChromeLogForTests)) {
     for (std::string package : packages)
       LOG(ERROR) << "Add ARC install event: " << package << ", "
                  << event.event_type();
diff --git a/chrome/browser/ash/policy/reporting/extension_install_event_log_collector_unittest.cc b/chrome/browser/ash/policy/reporting/extension_install_event_log_collector_unittest.cc
index e1ef1c6..e6b2671 100644
--- a/chrome/browser/ash/policy/reporting/extension_install_event_log_collector_unittest.cc
+++ b/chrome/browser/ash/policy/reporting/extension_install_event_log_collector_unittest.cc
@@ -308,7 +308,7 @@
     ExtensionInstallEventLogCollector collector(registry(), delegate(),
                                                 profile());
     base::CommandLine::ForCurrentProcess()->AppendSwitch(
-        chromeos::switches::kLoginUser);
+        ash::switches::kLoginUser);
     collector.OnLogin();
     EXPECT_EQ(1, delegate()->add_for_all_count());
   }
diff --git a/chrome/browser/ash/policy/reporting/install_event_log_collector_base.cc b/chrome/browser/ash/policy/reporting/install_event_log_collector_base.cc
index 4c5a0af..5a21fe05 100644
--- a/chrome/browser/ash/policy/reporting/install_event_log_collector_base.cc
+++ b/chrome/browser/ash/policy/reporting/install_event_log_collector_base.cc
@@ -48,7 +48,7 @@
 void InstallEventLogCollectorBase::OnLogin() {
   // Don't log in case session is restared or recovered from crash.
   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
-          chromeos::switches::kLoginUser) ||
+          ash::switches::kLoginUser) ||
       ExitTypeService::GetLastSessionExitType(profile_) == ExitType::kCrashed) {
     return;
   }
diff --git a/chrome/browser/ash/policy/reporting/install_event_log_manager.cc b/chrome/browser/ash/policy/reporting/install_event_log_manager.cc
index 49f73308..9ef4f4e 100644
--- a/chrome/browser/ash/policy/reporting/install_event_log_manager.cc
+++ b/chrome/browser/ash/policy/reporting/install_event_log_manager.cc
@@ -49,7 +49,7 @@
 // reduced delay.
 bool FastUploadForTestsEnabled() {
   return base::CommandLine::ForCurrentProcess()->HasSwitch(
-      chromeos::switches::kInstallLogFastUploadForTests);
+      ash::switches::kInstallLogFastUploadForTests);
 }
 
 }  // namespace
diff --git a/chrome/browser/ash/policy/reporting/install_event_log_uploader_base.cc b/chrome/browser/ash/policy/reporting/install_event_log_uploader_base.cc
index d2af7c2..3b9fdfd 100644
--- a/chrome/browser/ash/policy/reporting/install_event_log_uploader_base.cc
+++ b/chrome/browser/ash/policy/reporting/install_event_log_uploader_base.cc
@@ -24,7 +24,7 @@
 // backoff.
 bool FastUploadForTestsEnabled() {
   return base::CommandLine::ForCurrentProcess()->HasSwitch(
-      chromeos::switches::kInstallLogFastUploadForTests);
+      ash::switches::kInstallLogFastUploadForTests);
 }
 
 }  // namespace
diff --git a/chrome/browser/ash/policy/server_backed_state/active_directory_device_state_uploader.h b/chrome/browser/ash/policy/server_backed_state/active_directory_device_state_uploader.h
index b568f35..caedbfe 100644
--- a/chrome/browser/ash/policy/server_backed_state/active_directory_device_state_uploader.h
+++ b/chrome/browser/ash/policy/server_backed_state/active_directory_device_state_uploader.h
@@ -59,7 +59,7 @@
   bool HasUploadedEnrollmentId() const;
 
   // Subscribes to state keys update signal to trigger state keys upload
-  // whenever state keys are updated.
+  // whenever state keys are updated. Also, starts a DM Token request.
   void Init();
 
   // Unsubscribes from state keys update signal.
diff --git a/chrome/browser/ash/settings/cros_settings.cc b/chrome/browser/ash/settings/cros_settings.cc
index 8be3d81..12a5bc9 100644
--- a/chrome/browser/ash/settings/cros_settings.cc
+++ b/chrome/browser/ash/settings/cros_settings.cc
@@ -196,7 +196,7 @@
     bool* wildcard_match,
     const absl::optional<user_manager::UserType>& user_type) const {
   // Skip allowlist check for tests.
-  if (chromeos::switches::ShouldSkipOobePostLogin()) {
+  if (switches::ShouldSkipOobePostLogin()) {
     return true;
   }
 
diff --git a/chrome/browser/ash/system/device_disabling_browsertest.cc b/chrome/browser/ash/system/device_disabling_browsertest.cc
index 6abf3226..3a33dda 100644
--- a/chrome/browser/ash/system/device_disabling_browsertest.cc
+++ b/chrome/browser/ash/system/device_disabling_browsertest.cc
@@ -275,7 +275,7 @@
       return false;
     // Postpone login host creation.
     base::CommandLine::ForCurrentProcess()->RemoveSwitch(
-        chromeos::switches::kForceLoginManagerInTests);
+        switches::kForceLoginManagerInTests);
     return true;
   }
 
diff --git a/chrome/browser/ash/system_extensions/system_extensions_browsertest.cc b/chrome/browser/ash/system_extensions/system_extensions_browsertest.cc
index 82a8eed9..a783b7f 100644
--- a/chrome/browser/ash/system_extensions/system_extensions_browsertest.cc
+++ b/chrome/browser/ash/system_extensions/system_extensions_browsertest.cc
@@ -91,7 +91,7 @@
 
   void SetUpCommandLine(base::CommandLine* command_line) override {
     InProcessBrowserTest::SetUpCommandLine(command_line);
-    command_line->AppendSwitchPath(chromeos::switches::kInstallSystemExtension,
+    command_line->AppendSwitchPath(ash::switches::kInstallSystemExtension,
                                    GetBasicSystemExtensionDir());
   }
 
diff --git a/chrome/browser/ash/system_extensions/system_extensions_install_manager.cc b/chrome/browser/ash/system_extensions/system_extensions_install_manager.cc
index 2fd5086..3fb2fdc 100644
--- a/chrome/browser/ash/system_extensions/system_extensions_install_manager.cc
+++ b/chrome/browser/ash/system_extensions/system_extensions_install_manager.cc
@@ -56,11 +56,11 @@
 
 void SystemExtensionsInstallManager::InstallFromCommandLineIfNecessary() {
   base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
-  if (!command_line->HasSwitch(chromeos::switches::kInstallSystemExtension)) {
+  if (!command_line->HasSwitch(ash::switches::kInstallSystemExtension)) {
     return;
   }
-  base::FilePath system_extension_dir = command_line->GetSwitchValuePath(
-      chromeos::switches::kInstallSystemExtension);
+  base::FilePath system_extension_dir =
+      command_line->GetSwitchValuePath(ash::switches::kInstallSystemExtension);
 
   StartInstallation(
       base::BindOnce(
diff --git a/chrome/browser/ash/tether/fake_tether_service.h b/chrome/browser/ash/tether/fake_tether_service.h
index d9ab087..56eb803 100644
--- a/chrome/browser/ash/tether/fake_tether_service.h
+++ b/chrome/browser/ash/tether/fake_tether_service.h
@@ -12,7 +12,7 @@
 
 // A stub of TetherService that provides an easy way to develop for Tether on
 // non-Chromebooks or without a Tether host. To use, see
-// chromeos::switches::kTetherStub for more details.
+// `switches::kTetherStub` for more details.
 class FakeTetherService : public TetherService {
  public:
   FakeTetherService(
diff --git a/chrome/browser/ash/wallpaper_handlers/wallpaper_handlers.cc b/chrome/browser/ash/wallpaper_handlers/wallpaper_handlers.cc
index fed1416..0a94971 100644
--- a/chrome/browser/ash/wallpaper_handlers/wallpaper_handlers.cc
+++ b/chrome/browser/ash/wallpaper_handlers/wallpaper_handlers.cc
@@ -119,7 +119,7 @@
 // otherwise returns |url| as is. See https://crbug.com/914144.
 std::string MaybeConvertToTestUrl(std::string url) {
   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
-          chromeos::switches::kTestWallpaperServer)) {
+          ash::switches::kTestWallpaperServer)) {
     base::ReplaceFirstSubstringAfterOffset(&url, 0, "clients3",
                                            "chromecast-dev.sandbox");
   } else if (base::FeatureList::IsEnabled(
diff --git a/chrome/browser/ash/web_applications/projector_system_web_app_info.cc b/chrome/browser/ash/web_applications/projector_system_web_app_info.cc
index 3874810b..d09385f 100644
--- a/chrome/browser/ash/web_applications/projector_system_web_app_info.cc
+++ b/chrome/browser/ash/web_applications/projector_system_web_app_info.cc
@@ -64,6 +64,12 @@
   return true;
 }
 
+gfx::Size ProjectorSystemWebAppDelegate::GetMinimumWindowSize() const {
+  // The minimum width matches the minimum width of the Projector viewer left
+  // panel defined in the web component.
+  return {492, 550};
+}
+
 bool ProjectorSystemWebAppDelegate::IsAppEnabled() const {
   if (!IsProjectorAllowedForProfile(profile_))
     return false;
diff --git a/chrome/browser/ash/web_applications/projector_system_web_app_info.h b/chrome/browser/ash/web_applications/projector_system_web_app_info.h
index 7b3dff7..d5949e0 100644
--- a/chrome/browser/ash/web_applications/projector_system_web_app_info.h
+++ b/chrome/browser/ash/web_applications/projector_system_web_app_info.h
@@ -20,6 +20,7 @@
   // web_app::SystemWebAppDelegate:
   std::unique_ptr<WebAppInstallInfo> GetWebAppInfo() const override;
   bool ShouldCaptureNavigations() const override;
+  gfx::Size GetMinimumWindowSize() const override;
   bool IsAppEnabled() const override;
 };
 
diff --git a/chrome/browser/browsing_data/chrome_browsing_data_lifetime_manager.cc b/chrome/browser/browsing_data/chrome_browsing_data_lifetime_manager.cc
index e57e2df..4651554 100644
--- a/chrome/browser/browsing_data/chrome_browsing_data_lifetime_manager.cc
+++ b/chrome/browser/browsing_data/chrome_browsing_data_lifetime_manager.cc
@@ -35,6 +35,7 @@
 #include "content/public/browser/browsing_data_filter_builder.h"
 #include "content/public/browser/browsing_data_remover.h"
 #include "content/public/browser/web_contents.h"
+#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
 #include "url/gurl.h"
 #include "url/origin.h"
 
@@ -218,13 +219,14 @@
 
 base::flat_set<GURL> GetOpenedUrls(Profile* profile) {
   base::flat_set<GURL> result;
+  // TODO (crbug/1288416): Enable this for android.
 #if !BUILDFLAG(IS_ANDROID)
   for (auto* browser : *BrowserList::GetInstance()) {
     if (browser->profile() != profile) {
       continue;
     }
     for (int i = 0; i < browser->tab_strip_model()->count(); ++i) {
-      result.insert(browser->tab_strip_model()->GetWebContentsAt(0)->GetURL());
+      result.insert(browser->tab_strip_model()->GetWebContentsAt(i)->GetURL());
     }
   }
 #endif
@@ -346,7 +348,11 @@
       auto filter_builder = content::BrowsingDataFilterBuilder::Create(
           content::BrowsingDataFilterBuilder::Mode::kPreserve);
       for (const auto& url : GetOpenedUrls(profile_)) {
-        filter_builder->AddRegisterableDomain(url.spec());
+        std::string domain = GetDomainAndRegistry(
+            url, net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES);
+        if (domain.empty())
+          domain = url.host();  // IP address or internal hostname.
+        filter_builder->AddRegisterableDomain(domain);
       }
       remover->RemoveWithFilterAndReply(
           base::Time::Min(), deletion_end_time, filterable_remove_mask,
diff --git a/chrome/browser/browsing_data/chrome_browsing_data_lifetime_manager_browsertest.cc b/chrome/browser/browsing_data/chrome_browsing_data_lifetime_manager_browsertest.cc
index 0d56845..20e9753 100644
--- a/chrome/browser/browsing_data/chrome_browsing_data_lifetime_manager_browsertest.cc
+++ b/chrome/browser/browsing_data/chrome_browsing_data_lifetime_manager_browsertest.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/browsing_data/chrome_browsing_data_lifetime_manager.h"
 
+#include <array>
 #include <memory>
 
 #include "base/files/file_path.h"
@@ -59,6 +60,7 @@
 
 #if !BUILDFLAG(IS_ANDROID)
 #include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_list.h"
 #include "chrome/test/base/ui_test_utils.h"
 #endif
 
@@ -66,6 +68,10 @@
 
 enum class BrowserType { Default, Incognito };
 
+constexpr std::array<const char*, 7> kSiteDataTypes{
+    "Cookie", "LocalStorage",  "SessionStorage", "IndexedDb",
+    "WebSql", "ServiceWorker", "CacheStorage"};
+
 }  // namespace
 
 class ChromeBrowsingDataLifetimeManagerTest
@@ -83,6 +89,7 @@
     GetProfile()->GetPrefs()->Set(syncer::prefs::kSyncManaged,
                                   base::Value(true));
   }
+
   void ApplyBrowsingDataLifetimeDeletion(base::StringPiece pref) {
     auto* browsing_data_lifetime_manager =
         ChromeBrowsingDataLifetimeManagerFactory::GetForProfile(GetProfile());
@@ -92,11 +99,28 @@
     content::BrowsingDataRemoverCompletionObserver completion_observer(remover);
     browsing_data_lifetime_manager->SetBrowsingDataRemoverObserverForTesting(
         &completion_observer);
+    // The pref needs to be cleared so that the browsing data deletion is
+    // triggered even if the same pref value is set twice in a row.
+    GetProfile()->GetPrefs()->ClearPref(
+        browsing_data::prefs::kBrowsingDataLifetime);
     GetProfile()->GetPrefs()->Set(browsing_data::prefs::kBrowsingDataLifetime,
                                   *base::JSONReader::Read(pref));
 
     completion_observer.BlockUntilCompletion();
   }
+
+  void SetupSiteData(content::WebContents* web_contents) {
+    for (const char* data_type : kSiteDataTypes) {
+      SetDataForType(data_type, web_contents);
+      EXPECT_TRUE(HasDataForType(data_type, web_contents));
+    }
+  }
+
+  void CheckSiteData(content::WebContents* web_contents, bool has_site_data) {
+    for (const char* data_type : kSiteDataTypes) {
+      EXPECT_EQ(HasDataForType(data_type), has_site_data) << data_type;
+    }
+  }
 };
 
 class ChromeBrowsingDataLifetimeManagerScheduledRemovalTest
@@ -134,7 +158,10 @@
   EXPECT_TRUE(HasDataForType("Cookie"));
 
   // Expect that cookies are deleted.
+  ASSERT_TRUE(content::NavigateToURL(GetActiveWebContents(),
+                                     GURL(url::kAboutBlankURL)));
   ApplyBrowsingDataLifetimeDeletion(kCookiesPref);
+  ASSERT_TRUE(content::NavigateToURL(GetActiveWebContents(), url));
   EXPECT_FALSE(HasDataForType("Cookie"));
 
   url = embedded_test_server()->GetURL("/cachetime");
@@ -146,6 +173,8 @@
   ASSERT_TRUE(embedded_test_server()->ShutdownAndWaitUntilComplete());
   EXPECT_EQ(net::OK, content::LoadBasicRequest(network_context(), url));
 
+  ASSERT_TRUE(content::NavigateToURL(GetActiveWebContents(),
+                                     GURL(url::kAboutBlankURL)));
   ApplyBrowsingDataLifetimeDeletion(kCachePref);
   EXPECT_NE(net::OK, content::LoadBasicRequest(network_context(), url));
 }
@@ -161,6 +190,18 @@
   DownloadAnItem();
   VerifyDownloadCount(1u);
   ApplyBrowsingDataLifetimeDeletion(kPref);
+  // The download is not deleted since the page where it happened is still
+  // opened.
+  VerifyDownloadCount(1u);
+
+  // Navigate away.
+  GURL url = embedded_test_server()->GetURL("/browsing_data/site_data.html");
+  ASSERT_TRUE(content::NavigateToURL(GetActiveWebContents(), url));
+  VerifyDownloadCount(1u);
+
+  // The download should now be deleted since the page where it happened is not
+  // active.
+  ApplyBrowsingDataLifetimeDeletion(kPref);
   VerifyDownloadCount(0u);
 }
 #endif
@@ -213,21 +254,26 @@
 
   GURL url = embedded_test_server()->GetURL("/browsing_data/site_data.html");
   ASSERT_TRUE(content::NavigateToURL(GetActiveWebContents(), url));
-
-  const std::vector<std::string> kTypes{
-      "Cookie",    "LocalStorage", "FileSystem",    "SessionStorage",
-      "IndexedDb", "WebSql",       "ServiceWorker", "CacheStorage"};
-
-  for (const auto& data_type : kTypes) {
-    SetDataForType(data_type);
-    EXPECT_TRUE(HasDataForType(data_type));
-  }
-
+  SetupSiteData(GetActiveWebContents());
   ApplyBrowsingDataLifetimeDeletion(kPref);
 
-  for (const auto& data_type : kTypes) {
-    EXPECT_FALSE(HasDataForType(data_type)) << data_type;
-  }
+#if !defined(OS_ANDROID)
+  // The site data is not deleted since the page where it happened is still
+  // opened.
+  CheckSiteData(GetActiveWebContents(), /*has_site_data=*/true);
+
+  // Navigate away.
+  ASSERT_TRUE(content::NavigateToURL(GetActiveWebContents(),
+                                     GURL(url::kAboutBlankURL)));
+
+  // The site should now be deleted since the page where it happened is not
+  // active.
+  ApplyBrowsingDataLifetimeDeletion(kPref);
+  ASSERT_TRUE(content::NavigateToURL(GetActiveWebContents(), url));
+#else
+  ApplyBrowsingDataLifetimeDeletion(kPref);
+#endif
+  CheckSiteData(GetActiveWebContents(), /*has_site_data=*/false);
 }
 
 IN_PROC_BROWSER_TEST_P(ChromeBrowsingDataLifetimeManagerScheduledRemovalTest,
@@ -250,6 +296,120 @@
 }
 
 #if !BUILDFLAG(IS_ANDROID)
+IN_PROC_BROWSER_TEST_P(ChromeBrowsingDataLifetimeManagerScheduledRemovalTest,
+                       KeepsOtherTabData) {
+  if (IsIncognito())
+    return;
+
+  static constexpr char kPref[] =
+      R"([{"time_to_live_in_hours": 1, "data_types":
+      ["cookies_and_other_site_data"]}])";
+
+  GURL url = embedded_test_server()->GetURL("/browsing_data/site_data.html");
+  ASSERT_TRUE(content::NavigateToURL(GetActiveWebContents(), url));
+
+  auto* first_tab = GetActiveWebContents();
+  ui_test_utils::NavigateToURLWithDisposition(
+      browser(), url, WindowOpenDisposition::NEW_FOREGROUND_TAB,
+      ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP);
+  auto* second_tab = GetActiveWebContents();
+  DCHECK_NE(first_tab, second_tab);
+
+  SetupSiteData(first_tab);
+  SetupSiteData(second_tab);
+
+  ApplyBrowsingDataLifetimeDeletion(kPref);
+
+  // The site data is not deleted since the page where it happened is still
+  // opened.
+  CheckSiteData(first_tab, /*has_site_data=*/true);
+  CheckSiteData(second_tab, /*has_site_data=*/true);
+
+  // Navigate away first tab.
+  ASSERT_TRUE(content::NavigateToURL(first_tab, GURL(url::kAboutBlankURL)));
+
+  // The site data is not deleted since the domain of the data is still in use.
+  ApplyBrowsingDataLifetimeDeletion(kPref);
+  ASSERT_TRUE(content::NavigateToURL(first_tab, url));
+  CheckSiteData(first_tab, /*has_site_data=*/true);
+  CheckSiteData(second_tab, /*has_site_data=*/true);
+
+  // Navigate away second tab.
+  ASSERT_TRUE(content::NavigateToURL(second_tab, GURL(url::kAboutBlankURL)));
+
+  // The site data is not deleted since the domain of the data is still in use.
+  ApplyBrowsingDataLifetimeDeletion(kPref);
+  ASSERT_TRUE(content::NavigateToURL(second_tab, url));
+  CheckSiteData(first_tab, /*has_site_data=*/true);
+  CheckSiteData(second_tab, /*has_site_data=*/true);
+
+  // Navigate away both tabs.
+  ASSERT_TRUE(content::NavigateToURL(first_tab, GURL(url::kAboutBlankURL)));
+  ASSERT_TRUE(content::NavigateToURL(second_tab, GURL(url::kAboutBlankURL)));
+
+  // The site data is not deleted since the domain of the data is still in use.
+  ApplyBrowsingDataLifetimeDeletion(kPref);
+  ASSERT_TRUE(content::NavigateToURL(first_tab, url));
+  ASSERT_TRUE(content::NavigateToURL(second_tab, url));
+  CheckSiteData(first_tab, /*has_site_data=*/false);
+  CheckSiteData(second_tab, /*has_site_data=*/false);
+}
+
+IN_PROC_BROWSER_TEST_P(ChromeBrowsingDataLifetimeManagerScheduledRemovalTest,
+                       KeepsOtherWindowData) {
+  if (IsIncognito())
+    return;
+
+  static constexpr char kPref[] =
+      R"([{"time_to_live_in_hours": 1, "data_types":
+      ["cookies_and_other_site_data"]}])";
+
+  GURL url = embedded_test_server()->GetURL("/browsing_data/site_data.html");
+  ASSERT_TRUE(content::NavigateToURL(GetActiveWebContents(), url));
+
+  SetupSiteData(GetActiveWebContents());
+
+  ApplyBrowsingDataLifetimeDeletion(kPref);
+
+  // The site data is not deleted since the page where it happened is still
+  // opened.
+  CheckSiteData(GetActiveWebContents(), /*has_site_data=*/true);
+
+  // Open current url in new tab.
+  ui_test_utils::NavigateToURLWithDisposition(
+      browser(), url, WindowOpenDisposition::NEW_WINDOW,
+      ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP);
+
+  EXPECT_EQ(BrowserList::GetInstance()->size(), 2u);
+  content::WebContents* new_tab = nullptr;
+  for (auto* b : *BrowserList::GetInstance()) {
+    if (b != browser())
+      new_tab = b->tab_strip_model()->GetActiveWebContents();
+  }
+
+  ASSERT_TRUE(new_tab);
+  ASSERT_NE(new_tab, GetActiveWebContents());
+
+  // Navigate away current tab.
+  ASSERT_TRUE(content::NavigateToURL(GetActiveWebContents(),
+                                     GURL(url::kAboutBlankURL)));
+
+  // The site data is not deleted since the page's domain is opened in another
+  // tab.
+  ApplyBrowsingDataLifetimeDeletion(kPref);
+  ASSERT_TRUE(content::NavigateToURL(GetActiveWebContents(), url));
+  CheckSiteData(GetActiveWebContents(), /*has_site_data=*/true);
+
+  // Navigate away both tabs.
+  ASSERT_TRUE(content::NavigateToURL(new_tab, GURL(url::kAboutBlankURL)));
+  ASSERT_TRUE(content::NavigateToURL(GetActiveWebContents(),
+                                     GURL(url::kAboutBlankURL)));
+
+  ApplyBrowsingDataLifetimeDeletion(kPref);
+  ASSERT_TRUE(content::NavigateToURL(GetActiveWebContents(), url));
+  CheckSiteData(GetActiveWebContents(), /*has_site_data=*/false);
+}
+
 // Disabled because "autofill::AddTestProfile" times out when sync is disabled.
 IN_PROC_BROWSER_TEST_P(ChromeBrowsingDataLifetimeManagerScheduledRemovalTest,
                        DISABLED_Autofill) {
diff --git a/chrome/browser/chrome_browser_interface_binders.cc b/chrome/browser/chrome_browser_interface_binders.cc
index 6a16c5f..50f2322b 100644
--- a/chrome/browser/chrome_browser_interface_binders.cc
+++ b/chrome/browser/chrome_browser_interface_binders.cc
@@ -409,14 +409,12 @@
   dom_distiller::DomDistillerService* dom_distiller_service =
       dom_distiller::DomDistillerServiceFactory::GetForBrowserContext(
           web_contents->GetBrowserContext());
-  auto* distiller_ui_handle = dom_distiller_service->GetDistillerUIHandle();
 #if defined(OS_ANDROID)
   static_cast<dom_distiller::android::DistillerUIHandleAndroid*>(
-      distiller_ui_handle)
+      dom_distiller_service->GetDistillerUIHandle())
       ->set_render_frame_host(frame_host);
 #endif
-  auto* distilled_page_prefs = dom_distiller_service->GetDistilledPagePrefs();
-  CreateDistillerJavaScriptService(distiller_ui_handle, distilled_page_prefs,
+  CreateDistillerJavaScriptService(dom_distiller_service->GetWeakPtr(),
                                    std::move(receiver));
 }
 
diff --git a/chrome/browser/chrome_browser_main.cc b/chrome/browser/chrome_browser_main.cc
index 524a856..3c2cf26 100644
--- a/chrome/browser/chrome_browser_main.cc
+++ b/chrome/browser/chrome_browser_main.cc
@@ -357,13 +357,13 @@
 }
 #endif  // !defined(OS_ANDROID) && !BUILDFLAG(IS_CHROMEOS_ASH)
 
-// Initializes the primary profile, possibly doing some user prompting to pick
+// Initializes the initial profile, possibly doing some user prompting to pick
 // a fallback profile. Returns either
 // - kBrowserWindow mode with the newly created profile,
 // - kProfilePicker mode indicating that the profile picker should be shown;
 //   the profile is a guest profile in this case, or
 // - kError mode with a nullptr profile if startup should not continue.
-StartupProfileInfo CreatePrimaryProfile(
+StartupProfileInfo CreateInitialProfile(
     const content::MainFunctionParams& parameters,
     const base::FilePath& cur_dir,
     const base::CommandLine& parsed_command_line) {
@@ -429,7 +429,7 @@
     // TODO(lwchkg): What diagnostics do you want to include in the feedback
     // report when an error occurs?
     ShowProfileErrorDialog(error_type, IDS_COULDNT_STARTUP_PROFILE_ERROR,
-                           "Error creating primary profile.");
+                           "Error creating initial profile.");
     return profile_info;
   }
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH) || defined(OS_ANDROID)
@@ -1479,7 +1479,7 @@
   PreProfileInit();
 
 #if BUILDFLAG(ENABLE_NACL)
-  // NaClBrowserDelegateImpl is accessed inside CreatePrimaryProfile().
+  // NaClBrowserDelegateImpl is accessed inside CreateInitialProfile().
   // So make sure to create it before that.
   nacl::NaClBrowser::SetDelegate(std::make_unique<NaClBrowserDelegateImpl>(
       browser_process_->profile_manager()));
@@ -1487,7 +1487,7 @@
 
   // This step is costly and is already measured in Startup.CreateFirstProfile
   // and more directly Profile.CreateAndInitializeProfile.
-  StartupProfileInfo profile_info = CreatePrimaryProfile(
+  StartupProfileInfo profile_info = CreateInitialProfile(
       parameters_, /*cur_dir=*/base::FilePath(), parsed_command_line());
 
   profile_ = profile_info.profile;
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index e365897..5230339 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -2227,7 +2227,7 @@
   // On Chrome OS need to pass primary user homedir (in multi-profiles session).
   base::FilePath homedir;
   base::PathService::Get(base::DIR_HOME, &homedir);
-  command_line->AppendSwitchASCII(chromeos::switches::kHomedir,
+  command_line->AppendSwitchASCII(ash::switches::kHomedir,
                                   homedir.value().c_str());
 #endif
 
@@ -2251,10 +2251,10 @@
     }
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
-    const std::string& login_profile = browser_command_line.GetSwitchValueASCII(
-        chromeos::switches::kLoginProfile);
+    const std::string& login_profile =
+        browser_command_line.GetSwitchValueASCII(ash::switches::kLoginProfile);
     if (!login_profile.empty())
-      command_line->AppendSwitchASCII(chromeos::switches::kLoginProfile,
+      command_line->AppendSwitchASCII(ash::switches::kLoginProfile,
                                       login_profile);
 #endif
 
diff --git a/chrome/browser/chrome_resource_bundle_helper.cc b/chrome/browser/chrome_resource_bundle_helper.cc
index b7f8761..9526348 100644
--- a/chrome/browser/chrome_resource_bundle_helper.cc
+++ b/chrome/browser/chrome_resource_bundle_helper.cc
@@ -44,7 +44,7 @@
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
   base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
-  if (command_line->HasSwitch(chromeos::switches::kLoginManager)) {
+  if (command_line->HasSwitch(ash::switches::kLoginManager)) {
     PrefService* local_state = chrome_feature_list_creator->local_state();
     DCHECK(local_state);
 
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index c56bfd6..7f18993 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -2410,6 +2410,8 @@
     "../ash/plugin_vm/plugin_vm_util.cc",
     "../ash/plugin_vm/plugin_vm_util.h",
     "../ash/policy/active_directory/active_directory_join_delegate.h",
+    "../ash/policy/active_directory/active_directory_migration_manager.cc",
+    "../ash/policy/active_directory/active_directory_migration_manager.h",
     "../ash/policy/active_directory/active_directory_policy_manager.cc",
     "../ash/policy/active_directory/active_directory_policy_manager.h",
     "../ash/policy/active_directory/component_active_directory_policy_retriever.cc",
@@ -4352,6 +4354,7 @@
     "../ash/plugin_vm/plugin_vm_test_helper.cc",
     "../ash/plugin_vm/plugin_vm_test_helper.h",
     "../ash/plugin_vm/plugin_vm_util_unittest.cc",
+    "../ash/policy/active_directory/active_directory_migration_manager_unittest.cc",
     "../ash/policy/active_directory/active_directory_policy_manager_unittest.cc",
     "../ash/policy/active_directory/component_active_directory_policy_retriever_unittest.cc",
     "../ash/policy/active_directory/component_active_directory_policy_service_unittest.cc",
diff --git a/chrome/browser/chromeos/extensions/echo_private_apitest.cc b/chrome/browser/chromeos/extensions/echo_private_apitest.cc
index 6ba5906..79664a8 100644
--- a/chrome/browser/chromeos/extensions/echo_private_apitest.cc
+++ b/chrome/browser/chromeos/extensions/echo_private_apitest.cc
@@ -98,7 +98,8 @@
   // Open and activates tab in the test browser. Returns the ID of the opened
   // tab.
   int OpenAndActivateTab() {
-    AddTabAtIndex(0, GURL("about:blank"), ui::PAGE_TRANSITION_LINK);
+    EXPECT_TRUE(
+        AddTabAtIndex(0, GURL("about:blank"), ui::PAGE_TRANSITION_LINK));
     browser()->tab_strip_model()->ActivateTabAt(
         0, {TabStripModel::GestureType::kOther});
     return extensions::ExtensionTabUtil::GetTabId(
diff --git a/chrome/browser/chromeos/extensions/file_manager/file_system_provider_metrics_util.h b/chrome/browser/chromeos/extensions/file_manager/file_system_provider_metrics_util.h
index 9b39f25..a2e30c6 100644
--- a/chrome/browser/chromeos/extensions/file_manager/file_system_provider_metrics_util.h
+++ b/chrome/browser/chromeos/extensions/file_manager/file_system_provider_metrics_util.h
@@ -12,7 +12,7 @@
 namespace file_manager {
 
 // UMA metric name that tracks the mounted File System Provider.
-constexpr char kFileSystemProviderMountedMetricName[] =
+inline constexpr char kFileSystemProviderMountedMetricName[] =
     "FileBrowser.FileSystemProviderMounted";
 
 // List of known File System Providers and their corresponding UMA enum value.
diff --git a/chrome/browser/chromeos/extensions/file_manager/system_notification_manager.h b/chrome/browser/chromeos/extensions/file_manager/system_notification_manager.h
index 0644443..42e5fa90 100644
--- a/chrome/browser/chromeos/extensions/file_manager/system_notification_manager.h
+++ b/chrome/browser/chromeos/extensions/file_manager/system_notification_manager.h
@@ -77,11 +77,11 @@
 };
 
 // Histogram name for Notification.Show.
-constexpr char kNotificationShowHistogramName[] =
+inline constexpr char kNotificationShowHistogramName[] =
     "FileBrowser.Notification.Show";
 
 // Histogram name for Notification.UserAction.
-constexpr char kNotificationUserActionHistogramName[] =
+inline constexpr char kNotificationUserActionHistogramName[] =
     "FileBrowser.Notification.UserAction";
 
 // Manages creation/deletion and update of system notifications on behalf
diff --git a/chrome/browser/chromeos/extensions/login_screen/login/cleanup/extension_cleanup_handler_browsertest.cc b/chrome/browser/chromeos/extensions/login_screen/login/cleanup/extension_cleanup_handler_browsertest.cc
index 6f7f857..929f3d90 100644
--- a/chrome/browser/chromeos/extensions/login_screen/login/cleanup/extension_cleanup_handler_browsertest.cc
+++ b/chrome/browser/chromeos/extensions/login_screen/login/cleanup/extension_cleanup_handler_browsertest.cc
@@ -61,9 +61,9 @@
 
   void SetUpCommandLine(base::CommandLine* command_line) override {
     DevicePolicyCrosBrowserTest::SetUpCommandLine(command_line);
-    command_line->AppendSwitch(chromeos::switches::kLoginManager);
-    command_line->AppendSwitch(chromeos::switches::kForceLoginManagerInTests);
-    command_line->AppendSwitchASCII(chromeos::switches::kLoginProfile, "user");
+    command_line->AppendSwitch(ash::switches::kLoginManager);
+    command_line->AppendSwitch(ash::switches::kForceLoginManagerInTests);
+    command_line->AppendSwitchASCII(ash::switches::kLoginProfile, "user");
   }
 
   void SetUpOnMainThread() override {
diff --git a/chrome/browser/chromeos/extensions/wallpaper_private_api.cc b/chrome/browser/chromeos/extensions/wallpaper_private_api.cc
index f27a8b3..d2f2dec0 100644
--- a/chrome/browser/chromeos/extensions/wallpaper_private_api.cc
+++ b/chrome/browser/chromeos/extensions/wallpaper_private_api.cc
@@ -85,7 +85,7 @@
 
 bool IsOEMDefaultWallpaper() {
   return base::CommandLine::ForCurrentProcess()->HasSwitch(
-      chromeos::switches::kDefaultWallpaperIsOem);
+      ash::switches::kDefaultWallpaperIsOem);
 }
 
 // Saves |data| as |file_name| to directory with |key|. Return false if the
@@ -531,7 +531,7 @@
     // thumbnail. We should either resize it or include a wallpaper thumbnail in
     // addition to large and small wallpaper resolutions.
     thumbnail_path = base::CommandLine::ForCurrentProcess()->GetSwitchValuePath(
-        chromeos::switches::kDefaultWallpaperSmall);
+        ash::switches::kDefaultWallpaperSmall);
   }
 
   WallpaperFunctionBase::GetNonBlockingTaskRunner()->PostTask(
diff --git a/chrome/browser/component_updater/cros_component_installer_chromeos.cc b/chrome/browser/component_updater/cros_component_installer_chromeos.cc
index 2ffb31b..c371379 100644
--- a/chrome/browser/component_updater/cros_component_installer_chromeos.cc
+++ b/chrome/browser/component_updater/cros_component_installer_chromeos.cc
@@ -14,6 +14,7 @@
 #include "base/strings/string_number_conversions.h"
 #include "base/task/thread_pool.h"
 #include "base/threading/thread_task_runner_handle.h"
+#include "base/time/time.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/component_updater/component_installer_errors.h"
 #include "chrome/browser/component_updater/metadata_table_chromeos.h"
diff --git a/chrome/browser/component_updater/cros_component_installer_chromeos.h b/chrome/browser/component_updater/cros_component_installer_chromeos.h
index c073839..6c2381f 100644
--- a/chrome/browser/component_updater/cros_component_installer_chromeos.h
+++ b/chrome/browser/component_updater/cros_component_installer_chromeos.h
@@ -17,6 +17,10 @@
 #include "components/update_client/update_client.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
+namespace base {
+class TimeTicks;
+}
+
 namespace component_updater {
 
 class ComponentUpdateService;
diff --git a/chrome/browser/devtools/chrome_devtools_manager_delegate.cc b/chrome/browser/devtools/chrome_devtools_manager_delegate.cc
index 564b10f..ea291a92 100644
--- a/chrome/browser/devtools/chrome_devtools_manager_delegate.cc
+++ b/chrome/browser/devtools/chrome_devtools_manager_delegate.cc
@@ -191,7 +191,7 @@
     const extensions::Extension* extension) {
 #if BUILDFLAG(IS_CHROMEOS_ASH)
   base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
-  if (command_line->HasSwitch(chromeos::switches::kForceDevToolsAvailable))
+  if (command_line->HasSwitch(ash::switches::kForceDevToolsAvailable))
     return true;
 #endif
 
diff --git a/chrome/browser/devtools/devtools_browsertest.cc b/chrome/browser/devtools/devtools_browsertest.cc
index 2cb7b55..1750a58 100644
--- a/chrome/browser/devtools/devtools_browsertest.cc
+++ b/chrome/browser/devtools/devtools_browsertest.cc
@@ -2221,13 +2221,13 @@
  public:
   void SetUpCommandLine(base::CommandLine* command_line) override {
     extensions::ExtensionBrowserTest::SetUpCommandLine(command_line);
-    // Same as |chromeos::switches::kForceDevToolsAvailable|, but used as a
+    // Same as `ash::switches::kForceDevToolsAvailable`, but used as a
     // literal here so it's possible to verify that the switch does not apply on
     // non-ChromeOS platforms.
     const std::string kForceDevToolsAvailableBase = "force-devtools-available";
 #if BUILDFLAG(IS_CHROMEOS_ASH)
     ASSERT_EQ(kForceDevToolsAvailableBase,
-              chromeos::switches::kForceDevToolsAvailable);
+              ash::switches::kForceDevToolsAvailable);
 #endif
     command_line->AppendSwitch("--" + kForceDevToolsAvailableBase);
   }
diff --git a/chrome/browser/devtools/protocol/devtools_protocol_browsertest.cc b/chrome/browser/devtools/protocol/devtools_protocol_browsertest.cc
index c10f4e0..bbfafeb 100644
--- a/chrome/browser/devtools/protocol/devtools_protocol_browsertest.cc
+++ b/chrome/browser/devtools/protocol/devtools_protocol_browsertest.cc
@@ -209,17 +209,9 @@
   EXPECT_EQ(nullptr, DevToolsWindow::FindDevToolsWindow(agent_host_.get()));
 }
 
-// Flaky/failing on Linux/CrOS/Mac builds: crbug.com/1284536
-#if BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC)
-#define MAYBE_NoPendingUrlShownWhenAttachedToBrowserInitiatedFailedNavigation \
-  DISABLED_NoPendingUrlShownWhenAttachedToBrowserInitiatedFailedNavigation
-#else
-#define MAYBE_NoPendingUrlShownWhenAttachedToBrowserInitiatedFailedNavigation \
-  NoPendingUrlShownWhenAttachedToBrowserInitiatedFailedNavigation
-#endif
 IN_PROC_BROWSER_TEST_F(
     DevToolsProtocolTest,
-    MAYBE_NoPendingUrlShownWhenAttachedToBrowserInitiatedFailedNavigation) {
+    NoPendingUrlShownWhenAttachedToBrowserInitiatedFailedNavigation) {
   GURL url("invalid.scheme:for-sure");
   ui_test_utils::AllBrowserTabAddedWaiter tab_added_waiter;
 
@@ -228,6 +220,7 @@
           url, content::Referrer(), WindowOpenDisposition::NEW_FOREGROUND_TAB,
           ui::PAGE_TRANSITION_TYPED, false));
   tab_added_waiter.Wait();
+  ASSERT_TRUE(WaitForLoadStop(web_contents));
   content::NavigationController& navigation_controller =
       web_contents->GetController();
   content::NavigationEntry* pending_entry =
diff --git a/chrome/browser/diagnostics/diagnostics_controller.cc b/chrome/browser/diagnostics/diagnostics_controller.cc
index 2ceb163..01d07a47 100644
--- a/chrome/browser/diagnostics/diagnostics_controller.cc
+++ b/chrome/browser/diagnostics/diagnostics_controller.cc
@@ -76,7 +76,7 @@
 // Separate out recoveries that we execute automatically as a result of a
 // crash from user-run recoveries.
 #if BUILDFLAG(IS_CHROMEOS_ASH)  // Only collecting UMA stats on ChromeOS
-  if (command_line.HasSwitch(chromeos::switches::kLoginUser)) {
+  if (command_line.HasSwitch(ash::switches::kLoginUser)) {
     UMA_HISTOGRAM_ENUMERATION("Diagnostics.RecoveryRun",
                               diagnostics::RECOVERY_CRASH_RUN,
                               diagnostics::RECOVERY_RUN_METRICS_COUNT);
diff --git a/chrome/browser/download/notification/download_notification_browsertest.cc b/chrome/browser/download/notification/download_notification_browsertest.cc
index fda2441..7e45e22d 100644
--- a/chrome/browser/download/notification/download_notification_browsertest.cc
+++ b/chrome/browser/download/notification/download_notification_browsertest.cc
@@ -1187,14 +1187,14 @@
     DownloadNotificationTestBase::SetUpCommandLine(command_line);
 
     // Logs in to a dummy profile.
-    command_line->AppendSwitchASCII(chromeos::switches::kLoginUser,
+    command_line->AppendSwitchASCII(ash::switches::kLoginUser,
                                     kTestAccounts[DUMMY_ACCOUNT_INDEX].email);
-    command_line->AppendSwitchASCII(chromeos::switches::kLoginProfile,
+    command_line->AppendSwitchASCII(ash::switches::kLoginProfile,
                                     kTestAccounts[DUMMY_ACCOUNT_INDEX].hash);
     // Don't require policy for our sessions - this is required because
     // this test creates a secondary profile synchronously, so we need to
     // let the policy code know not to expect cached policy.
-    command_line->AppendSwitchASCII(chromeos::switches::kProfileRequiresPolicy,
+    command_line->AppendSwitchASCII(ash::switches::kProfileRequiresPolicy,
                                     "false");
   }
 
diff --git a/chrome/browser/drive/drive_notification_manager_factory_browsertest.cc b/chrome/browser/drive/drive_notification_manager_factory_browsertest.cc
index b8c73ab6..bb40ed4 100644
--- a/chrome/browser/drive/drive_notification_manager_factory_browsertest.cc
+++ b/chrome/browser/drive/drive_notification_manager_factory_browsertest.cc
@@ -23,8 +23,8 @@
     : public InProcessBrowserTest {
  protected:
   void SetUpCommandLine(base::CommandLine* command_line) override {
-    command_line->AppendSwitch(chromeos::switches::kLoginManager);
-    command_line->AppendSwitchASCII(chromeos::switches::kLoginProfile, "user");
+    command_line->AppendSwitch(ash::switches::kLoginManager);
+    command_line->AppendSwitchASCII(ash::switches::kLoginProfile, "user");
   }
 };
 
@@ -42,11 +42,11 @@
     : public InProcessBrowserTest {
  protected:
   void SetUpCommandLine(base::CommandLine* command_line) override {
-    command_line->AppendSwitch(chromeos::switches::kGuestSession);
+    command_line->AppendSwitch(ash::switches::kGuestSession);
     command_line->AppendSwitch(::switches::kIncognito);
-    command_line->AppendSwitchASCII(chromeos::switches::kLoginProfile, "user");
+    command_line->AppendSwitchASCII(ash::switches::kLoginProfile, "user");
     command_line->AppendSwitchASCII(
-        chromeos::switches::kLoginUser,
+        ash::switches::kLoginUser,
         user_manager::GuestAccountId().GetUserEmail());
   }
 };
diff --git a/chrome/browser/enterprise/connectors/analysis/content_analysis_delegate.cc b/chrome/browser/enterprise/connectors/analysis/content_analysis_delegate.cc
index 61c6576..f40c778 100644
--- a/chrome/browser/enterprise/connectors/analysis/content_analysis_delegate.cc
+++ b/chrome/browser/enterprise/connectors/analysis/content_analysis_delegate.cc
@@ -52,6 +52,9 @@
 
 namespace enterprise_connectors {
 
+const base::Feature kBypassJustificationEnabled{
+    "kBypassJustificationEnabled", base::FEATURE_DISABLED_BY_DEFAULT};
+
 namespace {
 
 // Global pointer of factory function (RepeatingCallback) used to create
@@ -242,6 +245,9 @@
 }
 
 bool ContentAnalysisDelegate::BypassRequiresJustification() const {
+  if (!base::FeatureList::IsEnabled(kBypassJustificationEnabled))
+    return false;
+
   return data_.settings.tags_requiring_justification.count(final_result_tag_) >
          0;
 }
diff --git a/chrome/browser/enterprise/connectors/analysis/content_analysis_delegate.h b/chrome/browser/enterprise/connectors/analysis/content_analysis_delegate.h
index b2a9d70..cbf3f75f 100644
--- a/chrome/browser/enterprise/connectors/analysis/content_analysis_delegate.h
+++ b/chrome/browser/enterprise/connectors/analysis/content_analysis_delegate.h
@@ -33,6 +33,8 @@
 
 namespace enterprise_connectors {
 
+extern const base::Feature kBypassJustificationEnabled;
+
 class ContentAnalysisDialog;
 
 // A class that performs deep scans of data (for example malicious or sensitive
diff --git a/chrome/browser/enterprise/connectors/analysis/content_analysis_dialog.cc b/chrome/browser/enterprise/connectors/analysis/content_analysis_dialog.cc
index 5178c975..09b8435 100644
--- a/chrome/browser/enterprise/connectors/analysis/content_analysis_dialog.cc
+++ b/chrome/browser/enterprise/connectors/analysis/content_analysis_dialog.cc
@@ -64,6 +64,8 @@
 constexpr int kSideIconBetweenChildSpacing = 16;
 constexpr int kPaddingBeforeBypassJustification = 16;
 
+constexpr size_t kMaxBypassJustificationLength = 200;
+
 // These time values are non-const in order to be overridden in test so they
 // complete faster.
 base::TimeDelta minimum_pending_dialog_time_ = base::Seconds(2);
@@ -270,7 +272,8 @@
 void ContentAnalysisDialog::ContentsChanged(
     views::Textfield* sender,
     const std::u16string& new_contents) {
-  if (new_contents.size() == 0)
+  if (new_contents.size() == 0 ||
+      new_contents.size() > kMaxBypassJustificationLength)
     DialogDelegate::SetButtonEnabled(ui::DIALOG_BUTTON_OK, false);
   else
     DialogDelegate::SetButtonEnabled(ui::DIALOG_BUTTON_OK, true);
diff --git a/chrome/browser/enterprise/connectors/analysis/content_analysis_dialog_browsertest.cc b/chrome/browser/enterprise/connectors/analysis/content_analysis_dialog_browsertest.cc
index 9467548b..3cce370 100644
--- a/chrome/browser/enterprise/connectors/analysis/content_analysis_dialog_browsertest.cc
+++ b/chrome/browser/enterprise/connectors/analysis/content_analysis_dialog_browsertest.cc
@@ -852,6 +852,26 @@
 }
 
 IN_PROC_BROWSER_TEST_F(ContentAnalysisDialogPlainTests,
+                       TestBypassJustificationTooLongDisablesBypassButton) {
+  enterprise_connectors::ContentAnalysisDialog::
+      SetMinimumPendingDialogTimeForTesting(base::Milliseconds(0));
+
+  std::unique_ptr<MockDelegate> delegate = std::make_unique<MockDelegate>();
+  delegate->SetBypassRequiresJustification(true);
+  ContentAnalysisDialog* dialog = CreateContentAnalysisDialog(
+      std::move(delegate), ContentAnalysisDelegateBase::FinalResult::SUCCESS);
+  dialog->ShowResult(ContentAnalysisDelegateBase::FinalResult::WARNING);
+
+  EXPECT_FALSE(dialog->IsDialogButtonEnabled(ui::DIALOG_BUTTON_OK));
+  dialog->GetBypassJustificationTextareaForTesting()->InsertOrReplaceText(
+      u"This is a very long string. In fact, it is over two hundred characters "
+      u"long because that is the maximum length of a bypass justification that "
+      u"can be entered by a user. When the justification is this long, the "
+      u"user will not be able to submit it.");
+  EXPECT_FALSE(dialog->IsDialogButtonEnabled(ui::DIALOG_BUTTON_OK));
+}
+
+IN_PROC_BROWSER_TEST_F(ContentAnalysisDialogPlainTests,
                        TestOpenInDefaultPendingState) {
   ContentAnalysisDialog* dialog =
       CreateContentAnalysisDialog(std::make_unique<MockDelegate>());
diff --git a/chrome/browser/extensions/active_tab_unittest.cc b/chrome/browser/extensions/active_tab_unittest.cc
index 8e6200f..f9eae1f 100644
--- a/chrome/browser/extensions/active_tab_unittest.cc
+++ b/chrome/browser/extensions/active_tab_unittest.cc
@@ -543,7 +543,7 @@
         switches::kDisableSync);
     // Necessary because no ProfileManager instance exists in this test.
     base::CommandLine::ForCurrentProcess()->AppendSwitch(
-        chromeos::switches::kIgnoreUserProfileMappingForTests);
+        ash::switches::kIgnoreUserProfileMappingForTests);
     // Necessary to skip cryptohome/profile sanity check in
     // ChromeUserManagerImpl for fake user login.
     base::CommandLine::ForCurrentProcess()->AppendSwitch(switches::kTestType);
diff --git a/chrome/browser/extensions/api/enterprise_platform_keys/enterprise_platform_keys_apitest_nss.cc b/chrome/browser/extensions/api/enterprise_platform_keys/enterprise_platform_keys_apitest_nss.cc
index fb34995..4de60d8 100644
--- a/chrome/browser/extensions/api/enterprise_platform_keys/enterprise_platform_keys_apitest_nss.cc
+++ b/chrome/browser/extensions/api/enterprise_platform_keys/enterprise_platform_keys_apitest_nss.cc
@@ -373,7 +373,7 @@
   void SetUpCommandLine(base::CommandLine* command_line) override {
     MixinBasedInProcessBrowserTest::SetUpCommandLine(command_line);
 
-    command_line->AppendSwitch(chromeos::switches::kLoginManager);
+    command_line->AppendSwitch(ash::switches::kLoginManager);
     command_line->AppendSwitchASCII(switches::kAllowlistedExtensionID,
                                     kExtensionId);
   }
diff --git a/chrome/browser/extensions/api/management/management_apitest.cc b/chrome/browser/extensions/api/management/management_apitest.cc
index b706d53..6855513 100644
--- a/chrome/browser/extensions/api/management/management_apitest.cc
+++ b/chrome/browser/extensions/api/management/management_apitest.cc
@@ -120,22 +120,8 @@
     extension_ids_[name] = extension->id();
   }
 
-  using ScopedUserGestureForTests =
-      ExtensionFunction::ScopedUserGestureForTests;
-
-  void MaybeCreateScopedUserGesture() {
-    // TODO(crbug.com/977629): Support for chrome.test.runWithUserGesture for
-    // service worker-based extensions is not implemented. For now, work around
-    // it by simulating a user gesture for the entire test.
-    if (GetParam() == ContextType::kServiceWorker)
-      scoped_user_gesture_ = std::make_unique<ScopedUserGestureForTests>();
-  }
-
   // Maps installed extension names to their IDs.
   std::map<std::string, std::string> extension_ids_;
-
- private:
-  std::unique_ptr<ScopedUserGestureForTests> scoped_user_gesture_;
 };
 
 INSTANTIATE_TEST_SUITE_P(PersistentBackground,
@@ -145,16 +131,6 @@
                          ExtensionManagementApiTest,
                          ::testing::Values(ContextType::kServiceWorker));
 
-// TODO(crbug.com/977629): Support for chrome.test.runWithUserGesture for
-// service worker-based extensions is not implemented. For now, don't run
-// those tests, since they mix subtests where user gestures are present and
-// missing.
-using ExtensionManagementApiTestWithUserGesture = ExtensionManagementApiTest;
-
-INSTANTIATE_TEST_SUITE_P(PersistentBackground,
-                         ExtensionManagementApiTestWithUserGesture,
-                         ::testing::Values(ContextType::kPersistentBackground));
-
 IN_PROC_BROWSER_TEST_P(ExtensionManagementApiTest, Basics) {
   LoadExtensions();
 
@@ -175,7 +151,7 @@
   ASSERT_TRUE(RunExtensionTest("management/no_permission"));
 }
 
-IN_PROC_BROWSER_TEST_P(ExtensionManagementApiTestWithUserGesture, Uninstall) {
+IN_PROC_BROWSER_TEST_P(ExtensionManagementApiTest, Uninstall) {
   LoadExtensions();
   // Confirmation dialog will be shown for uninstallations except for self.
   extensions::ScopedTestDialogAutoConfirm auto_confirm(
@@ -183,8 +159,7 @@
   ASSERT_TRUE(RunExtensionTest("management/uninstall"));
 }
 
-IN_PROC_BROWSER_TEST_P(ExtensionManagementApiTestWithUserGesture,
-                       CreateAppShortcut) {
+IN_PROC_BROWSER_TEST_P(ExtensionManagementApiTest, CreateAppShortcut) {
   LoadExtensions();
   base::FilePath basedir = test_data_dir_.AppendASCII("management");
   LoadNamedExtension(basedir, "packaged_app");
@@ -193,8 +168,7 @@
   ASSERT_TRUE(RunExtensionTest("management/create_app_shortcut"));
 }
 
-IN_PROC_BROWSER_TEST_P(ExtensionManagementApiTestWithUserGesture,
-                       GenerateAppForLink) {
+IN_PROC_BROWSER_TEST_P(ExtensionManagementApiTest, GenerateAppForLink) {
   web_app::test::WaitUntilReady(web_app::WebAppProvider::GetForTest(profile()));
   ASSERT_TRUE(RunExtensionTest("management/generate_app_for_link"));
 }
@@ -221,7 +195,6 @@
 IN_PROC_BROWSER_TEST_P(GenerateAppForLinkWithLacrosWebAppsApiTest,
                        GenerateAppForLink) {
   web_app::test::WaitUntilReady(web_app::WebAppProvider::GetForTest(profile()));
-  MaybeCreateScopedUserGesture();
   ASSERT_TRUE(RunExtensionTest("management/generate_app_for_link_lacros"));
 }
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
@@ -449,7 +422,6 @@
 // Tests actions on extensions when no management policy is in place.
 IN_PROC_BROWSER_TEST_P(ExtensionManagementApiTest, ManagementPolicyAllowed) {
   LoadExtensions();
-  MaybeCreateScopedUserGesture();
   extensions::ScopedTestDialogAutoConfirm auto_confirm(
       extensions::ScopedTestDialogAutoConfirm::ACCEPT);
   extensions::ExtensionRegistry* registry =
@@ -593,8 +565,7 @@
 #else
 #define MAYBE_LaunchType LaunchType
 #endif
-IN_PROC_BROWSER_TEST_P(ExtensionManagementApiTestWithUserGesture,
-                       MAYBE_LaunchType) {
+IN_PROC_BROWSER_TEST_P(ExtensionManagementApiTest, MAYBE_LaunchType) {
   LoadExtensions();
   base::FilePath basedir = test_data_dir_.AppendASCII("management");
   LoadNamedExtension(basedir, "packaged_app");
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 a400bc9..c1dad40 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
@@ -147,10 +147,10 @@
     // uses the ProfileHelper to obtain the userhash crbug/238623.
     cryptohome::AccountIdentifier login_user;
     login_user.set_account_id(user_manager::CanonicalizeUserID(
-        command_line->GetSwitchValueNative(chromeos::switches::kLoginUser)));
+        command_line->GetSwitchValueNative(ash::switches::kLoginUser)));
     const std::string sanitized_user =
         UserDataAuthClient::GetStubSanitizedUsername(login_user);
-    command_line->AppendSwitchASCII(chromeos::switches::kLoginProfile,
+    command_line->AppendSwitchASCII(ash::switches::kLoginProfile,
                                     sanitized_user);
   }
 
diff --git a/chrome/browser/extensions/api/runtime/runtime_apitest.cc b/chrome/browser/extensions/api/runtime/runtime_apitest.cc
index aef2311..255ffba 100644
--- a/chrome/browser/extensions/api/runtime/runtime_apitest.cc
+++ b/chrome/browser/extensions/api/runtime/runtime_apitest.cc
@@ -66,14 +66,6 @@
 }
 
 IN_PROC_BROWSER_TEST_P(RuntimeApiTest, ChromeRuntimeUninstallURL) {
-  // TODO(https://crbug.com/977629): Currently, chrome.test.runWithUserGesture()
-  // doesn't support Service Worker-based extensions, so this is a workaround.
-  using ScopedUserGestureForTests =
-      ExtensionFunction::ScopedUserGestureForTests;
-  std::unique_ptr<ScopedUserGestureForTests> scoped_user_gesture;
-  if (GetParam() == ContextType::kServiceWorker)
-    scoped_user_gesture = std::make_unique<ScopedUserGestureForTests>();
-
   // Auto-confirm the uninstall dialog.
   extensions::ScopedTestDialogAutoConfirm auto_confirm(
       extensions::ScopedTestDialogAutoConfirm::ACCEPT);
diff --git a/chrome/browser/extensions/api/sessions/sessions_apitest.cc b/chrome/browser/extensions/api/sessions/sessions_apitest.cc
index 735e05df..b5482e0 100644
--- a/chrome/browser/extensions/api/sessions/sessions_apitest.cc
+++ b/chrome/browser/extensions/api/sessions/sessions_apitest.cc
@@ -188,8 +188,7 @@
 
 void ExtensionSessionsTest::SetUpCommandLine(base::CommandLine* command_line) {
 #if BUILDFLAG(IS_CHROMEOS_ASH)
-  command_line->AppendSwitch(
-      chromeos::switches::kIgnoreUserProfileMappingForTests);
+  command_line->AppendSwitch(ash::switches::kIgnoreUserProfileMappingForTests);
 #endif
 }
 
diff --git a/chrome/browser/extensions/api/tabs/execute_script_apitest.cc b/chrome/browser/extensions/api/tabs/execute_script_apitest.cc
index 7ab3f45..df0b70aa 100644
--- a/chrome/browser/extensions/api/tabs/execute_script_apitest.cc
+++ b/chrome/browser/extensions/api/tabs/execute_script_apitest.cc
@@ -119,11 +119,6 @@
 }
 
 IN_PROC_BROWSER_TEST_P(ExecuteScriptApiTest, UserGesture) {
-  // TODO(https://crbug.com/977629): Gesture support for testing is not
-  // available for Service Worker-based extensions.
-  if (GetParam() == ContextType::kServiceWorker)
-    return;
-
   ASSERT_TRUE(RunExtensionTest("executescript/user_gesture")) << message_;
 }
 
diff --git a/chrome/browser/extensions/component_loader.cc b/chrome/browser/extensions/component_loader.cc
index 0545623..eb5684a2 100644
--- a/chrome/browser/extensions/component_loader.cc
+++ b/chrome/browser/extensions/component_loader.cc
@@ -127,7 +127,7 @@
 
 bool IsNormalSession() {
   return !base::CommandLine::ForCurrentProcess()->HasSwitch(
-             chromeos::switches::kGuestSession) &&
+             ash::switches::kGuestSession) &&
          user_manager::UserManager::IsInitialized() &&
          user_manager::UserManager::Get()->IsUserLoggedIn();
 }
@@ -540,7 +540,7 @@
     Add(IDR_ECHO_MANIFEST,
         base::FilePath(FILE_PATH_LITERAL("/usr/share/chromeos-assets/echo")));
 
-    if (!command_line->HasSwitch(chromeos::switches::kGuestSession)) {
+    if (!command_line->HasSwitch(ash::switches::kGuestSession)) {
       Add(IDR_WALLPAPERMANAGER_MANIFEST,
           base::FilePath(FILE_PATH_LITERAL("chromeos/wallpaper_manager")));
     }
diff --git a/chrome/browser/extensions/crx_installer_browsertest.cc b/chrome/browser/extensions/crx_installer_browsertest.cc
index 7a7a034..26f90b9 100644
--- a/chrome/browser/extensions/crx_installer_browsertest.cc
+++ b/chrome/browser/extensions/crx_installer_browsertest.cc
@@ -992,7 +992,7 @@
 IN_PROC_BROWSER_TEST_F(ExtensionCrxInstallerTest, InstallToSharedLocation) {
   base::ScopedAllowBlockingForTesting allow_io;
   base::CommandLine::ForCurrentProcess()->AppendSwitch(
-      chromeos::switches::kEnableExtensionAssetsSharing);
+      ash::switches::kEnableExtensionAssetsSharing);
   base::ScopedTempDir cache_dir;
   ASSERT_TRUE(cache_dir.CreateUniqueTempDir());
   ExtensionAssetsManagerChromeOS::SetSharedInstallDirForTesting(
diff --git a/chrome/browser/extensions/extension_assets_manager_chromeos.cc b/chrome/browser/extensions/extension_assets_manager_chromeos.cc
index c6a26bea..8f5cc2df 100644
--- a/chrome/browser/extensions/extension_assets_manager_chromeos.cc
+++ b/chrome/browser/extensions/extension_assets_manager_chromeos.cc
@@ -254,7 +254,7 @@
     const base::FilePath& unpacked_extension_root,
     bool updates_from_webstore_or_empty_update_url) {
   if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
-          chromeos::switches::kEnableExtensionAssetsSharing)) {
+          ash::switches::kEnableExtensionAssetsSharing)) {
     return false;
   }
 
diff --git a/chrome/browser/extensions/extension_browsertest.cc b/chrome/browser/extensions/extension_browsertest.cc
index f8d42a7..3cea637 100644
--- a/chrome/browser/extensions/extension_browsertest.cc
+++ b/chrome/browser/extensions/extension_browsertest.cc
@@ -392,9 +392,9 @@
     // This makes sure that we create the Default profile first, with no
     // ExtensionService and then the real profile with one, as we do when
     // running on chromeos.
-    command_line->AppendSwitchASCII(chromeos::switches::kLoginUser,
+    command_line->AppendSwitchASCII(ash::switches::kLoginUser,
                                     "testuser@gmail.com");
-    command_line->AppendSwitchASCII(chromeos::switches::kLoginProfile, "user");
+    command_line->AppendSwitchASCII(ash::switches::kLoginProfile, "user");
   }
 #endif
 }
diff --git a/chrome/browser/extensions/extension_protocols_unittest.cc b/chrome/browser/extensions/extension_protocols_unittest.cc
index b0fd527..09931665 100644
--- a/chrome/browser/extensions/extension_protocols_unittest.cc
+++ b/chrome/browser/extensions/extension_protocols_unittest.cc
@@ -54,7 +54,6 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
-#include "third_party/blink/public/common/loader/previews_state.h"
 #include "third_party/blink/public/common/loader/referrer_utils.h"
 #include "third_party/blink/public/common/privacy_budget/identifiability_metrics.h"
 #include "third_party/blink/public/common/privacy_budget/identifiable_surface.h"
diff --git a/chrome/browser/extensions/external_pref_loader.cc b/chrome/browser/extensions/external_pref_loader.cc
index 3bf51a9..0c2204ff 100644
--- a/chrome/browser/extensions/external_pref_loader.cc
+++ b/chrome/browser/extensions/external_pref_loader.cc
@@ -54,7 +54,7 @@
 // TODO(crbug.com/1023268) This is a temporary measure and should be replaced.
 bool SkipInstallForChromeOSTablet(const base::FilePath& file_path) {
 #if BUILDFLAG(IS_CHROMEOS_ASH)
-  if (!chromeos::switches::IsTabletFormFactor())
+  if (!ash::switches::IsTabletFormFactor())
     return false;
 
   constexpr char const* kIdsNotToBeInstalledOnTabletFormFactor[] = {
diff --git a/chrome/browser/extensions/process_manager_browsertest.cc b/chrome/browser/extensions/process_manager_browsertest.cc
index 8c14afb..9bfc3b8 100644
--- a/chrome/browser/extensions/process_manager_browsertest.cc
+++ b/chrome/browser/extensions/process_manager_browsertest.cc
@@ -317,8 +317,8 @@
   void SetUpCommandLine(base::CommandLine* command_line) override {
     ExtensionBrowserTest::SetUpCommandLine(command_line);
 #if BUILDFLAG(IS_CHROMEOS_ASH)
-    command_line->AppendSwitch(chromeos::switches::kLoginManager);
-    command_line->AppendSwitch(chromeos::switches::kForceLoginManagerInTests);
+    command_line->AppendSwitch(ash::switches::kLoginManager);
+    command_line->AppendSwitch(ash::switches::kForceLoginManagerInTests);
 #endif
   }
 };
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index 5403062..6c40b276f 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -1509,7 +1509,7 @@
   {
     "name": "enable-autofill-upi-vpa",
     "owners": [ "cfroussios" ],
-    "expiry_milestone": 98
+    "expiry_milestone": 108
   },
   {
     "name": "enable-automatic-snooze",
@@ -3075,6 +3075,11 @@
     "expiry_milestone": 102
   },
   {
+    "name": "extended-open-vpn-settings",
+    "owners": [ "gflegar@google.com" ],
+    "expiry_milestone": 110
+  },
+  {
     "name": "extension-content-verification",
     "owners": [ "//extensions/OWNERS" ],
     "expiry_milestone": 86
@@ -3102,6 +3107,11 @@
     "expiry_milestone": 101
   },
   {
+    "name": "feature-notification-guide",
+    "owners": [ "shaktisahu"],
+    "expiry_milestone": 110
+  },
+  {
     "name": "fedcm",
     "owners": [ "goto", "webid-core@google.com"],
     "expiry_milestone": 110
@@ -4000,6 +4010,11 @@
     "expiry_milestone": 105
   },
   {
+    "name": "nearby-sharing-wifilan",
+    "owners": [ "nohle@google.com", "chromeos-cross-device-eng@google.com" ],
+    "expiry_milestone": 105
+  },
+  {
     "name": "new-canvas-2d-api",
     "owners": [ "aaronhk", "fserb", "juanmihd", "yiyix" ],
     "expiry_milestone": 100
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index c424253..c16d08aa 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -1223,11 +1223,6 @@
 const char kEnableUseZoomForDsfChoiceEnabled[] = "Enabled";
 const char kEnableUseZoomForDsfChoiceDisabled[] = "Disabled";
 
-const char kEnableSubresourceRedirectName[] =
-    "Enable Subresource Redirect Compression";
-const char kEnableSubresourceRedirectDescription[] =
-    "Allow subresource compression for data savings";
-
 const char kEnableWebAuthenticationCableV2SupportName[] =
     "Web Authentication caBLE v2 QR codes";
 const char kEnableWebAuthenticationCableV2SupportDescription[] =
@@ -3087,6 +3082,10 @@
 const char kExploreSitesDescription[] =
     "Enables portal from new tab page to explore websites.";
 
+const char kFeatureNotificationGuideName[] = "Feature notification guide";
+const char kFeatureNotificationGuideDescription[] =
+    "Enables notifications about chrome features.";
+
 const char kFeedBackToTopName[] = "Back to top of the feeds";
 const char kFeedBackToTopDescription[] =
     "Enables showing a callout to help users return to the top of the feeds "
@@ -4612,6 +4611,11 @@
         "Enable multistep automation for Switch Access, which is a project for "
         "the 2021 accessibility sprint.";
 
+const char kExtendedOpenVpnSettingsName[] = "Enable extended OpenVPN settings";
+const char kExtendedOpenVpnSettingsDescription[] =
+    "Enable displaying additional configuration properties of already "
+    "configured OpenVPN networks.";
+
 const char kMagnifierContinuousMouseFollowingModeSettingName[] =
     "Enable ability to choose continuous mouse following mode in Magnifier "
     "settings";
@@ -4920,6 +4924,10 @@
 const char kNearbySharingSelfShareDescription[] =
     "Enables seamless sharing between a user's own devices.";
 
+const char kNearbySharingWifiLanName[] = "Nearby Sharing WifiLan";
+const char kNearbySharingWifiLanDescription[] =
+    "Enables WifiLan as a Nearby Share transfer medium.";
+
 const char kPcieBillboardNotificationName[] = "Pcie billboard notification";
 const char kPcieBillboardNotificationDescription[] =
     "Enable Pcie peripheral billboard notification.";
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 98454f5..e78c1ed 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -688,9 +688,6 @@
 extern const char kEnableRestrictedWebApisName[];
 extern const char kEnableRestrictedWebApisDescription[];
 
-extern const char kEnableSubresourceRedirectName[];
-extern const char kEnableSubresourceRedirectDescription[];
-
 extern const char kEnableUseZoomForDsfName[];
 extern const char kEnableUseZoomForDsfDescription[];
 extern const char kEnableUseZoomForDsfChoiceDefault[];
@@ -1753,6 +1750,9 @@
 extern const char kExploreSitesName[];
 extern const char kExploreSitesDescription[];
 
+extern const char kFeatureNotificationGuideName[];
+extern const char kFeatureNotificationGuideDescription[];
+
 extern const char kFeedBackToTopName[];
 extern const char kFeedBackToTopDescription[];
 
@@ -2642,6 +2642,9 @@
 extern const char
     kExperimentalAccessibilitySwitchAccessMultistepAutomationDescription[];
 
+extern const char kExtendedOpenVpnSettingsName[];
+extern const char kExtendedOpenVpnSettingsDescription[];
+
 extern const char kMagnifierContinuousMouseFollowingModeSettingName[];
 extern const char kMagnifierContinuousMouseFollowingModeSettingDescription[];
 
@@ -2825,6 +2828,9 @@
 extern const char kNearbySharingSelfShareName[];
 extern const char kNearbySharingSelfShareDescription[];
 
+extern const char kNearbySharingWifiLanName[];
+extern const char kNearbySharingWifiLanDescription[];
+
 extern const char kPcieBillboardNotificationName[];
 extern const char kPcieBillboardNotificationDescription[];
 
diff --git a/chrome/browser/flags/android/chrome_feature_list.cc b/chrome/browser/flags/android/chrome_feature_list.cc
index 13b4cf0..22d046c 100644
--- a/chrome/browser/flags/android/chrome_feature_list.cc
+++ b/chrome/browser/flags/android/chrome_feature_list.cc
@@ -14,6 +14,7 @@
 #include "base/metrics/field_trial_params.h"
 #include "chrome/browser/browser_features.h"
 #include "chrome/browser/commerce/commerce_feature_list.h"
+#include "chrome/browser/feature_guide/notifications/feature_notification_guide_service.h"
 #include "chrome/browser/flags/jni_headers/ChromeFeatureList_jni.h"
 #include "chrome/browser/notifications/chime/android/features.h"
 #include "chrome/browser/performance_hints/performance_hints_features.h"
@@ -135,6 +136,7 @@
     &feature_engagement::kIPHSnooze,
     &feature_engagement::kIPHTabSwitcherButtonFeature,
     &feature_engagement::kUseClientConfigIPH,
+    &feature_guide::features::kFeatureNotificationGuide,
     &feed::kFeedBackToTop,
     &feed::kFeedClearImageMemoryCache,
     &feed::kFeedImageMemoryCacheSizePercentage,
diff --git a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
index 766841d..4cdad66 100644
--- a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
+++ b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
@@ -332,6 +332,7 @@
     public static final String EXPERIMENTS_FOR_AGSA = "ExperimentsForAgsa";
     public static final String EXPLICIT_LANGUAGE_ASK = "ExplicitLanguageAsk";
     public static final String EXPLORE_SITES = "ExploreSites";
+    public static final String FEATURE_NOTIFICATION_GUIDE = "FeatureNotificationGuide";
     public static final String FEED_BACK_TO_TOP = "FeedBackToTop";
     public static final String FEED_CLEAR_IMAGE_MEMORY_CACHE = "FeedClearImageMemoryCache";
     public static final String FEED_IMAGE_MEMORY_CACHE_SIZE_PERCENTAGE =
diff --git a/chrome/browser/invalidation/profile_invalidation_provider_factory_browsertest.cc b/chrome/browser/invalidation/profile_invalidation_provider_factory_browsertest.cc
index 396f4d1..bc06a885 100644
--- a/chrome/browser/invalidation/profile_invalidation_provider_factory_browsertest.cc
+++ b/chrome/browser/invalidation/profile_invalidation_provider_factory_browsertest.cc
@@ -69,8 +69,8 @@
 
 void ProfileInvalidationProviderFactoryLoginScreenBrowserTest::SetUpCommandLine(
     base::CommandLine* command_line) {
-  command_line->AppendSwitch(chromeos::switches::kLoginManager);
-  command_line->AppendSwitchASCII(chromeos::switches::kLoginProfile, "user");
+  command_line->AppendSwitch(ash::switches::kLoginManager);
+  command_line->AppendSwitchASCII(ash::switches::kLoginProfile, "user");
 }
 
 // Verify that no InvalidationService is instantiated for the login profile on
@@ -105,12 +105,11 @@
 
 void ProfileInvalidationProviderFactoryGuestBrowserTest::SetUpCommandLine(
     base::CommandLine* command_line) {
-  command_line->AppendSwitch(chromeos::switches::kGuestSession);
+  command_line->AppendSwitch(ash::switches::kGuestSession);
   command_line->AppendSwitch(::switches::kIncognito);
-  command_line->AppendSwitchASCII(chromeos::switches::kLoginProfile, "user");
+  command_line->AppendSwitchASCII(ash::switches::kLoginProfile, "user");
   command_line->AppendSwitchASCII(
-      chromeos::switches::kLoginUser,
-      user_manager::GuestAccountId().GetUserEmail());
+      ash::switches::kLoginUser, user_manager::GuestAccountId().GetUserEmail());
 }
 
 // Verify that no InvalidationService is instantiated for the login profile or
diff --git a/chrome/browser/lifetime/browser_close_manager_browsertest.cc b/chrome/browser/lifetime/browser_close_manager_browsertest.cc
index 0b3aabe2..c91b35e 100644
--- a/chrome/browser/lifetime/browser_close_manager_browsertest.cc
+++ b/chrome/browser/lifetime/browser_close_manager_browsertest.cc
@@ -286,7 +286,7 @@
   void SetUpCommandLine(base::CommandLine* command_line) override {
 #if BUILDFLAG(IS_CHROMEOS_ASH)
     command_line->AppendSwitch(
-        chromeos::switches::kIgnoreUserProfileMappingForTests);
+        ash::switches::kIgnoreUserProfileMappingForTests);
 #endif
   }
 
@@ -1080,18 +1080,10 @@
     EXPECT_EQ(0, DownloadCoreService::NonMaliciousDownloadCountAllProfiles());
 }
 
-// Disabled due to flakiness on Linux, https://crbug.com/1287462
-#if BUILDFLAG(IS_LINUX)
-#define MAYBE_TestWithDownloadsFromDifferentProfiles \
-  DISABLED_TestWithDownloadsFromDifferentProfiles
-#else
-#define MAYBE_TestWithDownloadsFromDifferentProfiles \
-  TestWithDownloadsFromDifferentProfiles
-#endif
 // Test shutdown with a download in progress from one profile, where the only
 // open windows are for another profile.
 IN_PROC_BROWSER_TEST_F(BrowserCloseManagerBrowserTest,
-                       MAYBE_TestWithDownloadsFromDifferentProfiles) {
+                       TestWithDownloadsFromDifferentProfiles) {
   ProfileManager* profile_manager = g_browser_process->profile_manager();
   std::unique_ptr<Profile> other_profile;
   {
@@ -1128,7 +1120,7 @@
                 ->GetVisibleURL());
   EXPECT_EQ(GURL("about:blank"), other_profile_browser->tab_strip_model()
                                      ->GetActiveWebContents()
-                                     ->GetLastCommittedURL());
+                                     ->GetVisibleURL());
 
   TestBrowserCloseManager::AttemptClose(
       TestBrowserCloseManager::USER_CHOICE_USER_ALLOWS_CLOSE);
diff --git a/chrome/browser/media/protected_media_identifier_permission_context.cc b/chrome/browser/media/protected_media_identifier_permission_context.cc
index 9c4144d2..cbbc97f6 100644
--- a/chrome/browser/media/protected_media_identifier_permission_context.cc
+++ b/chrome/browser/media/protected_media_identifier_permission_context.cc
@@ -146,7 +146,7 @@
 #if BUILDFLAG(IS_CHROMEOS_ASH)
   base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
   if (command_line->HasSwitch(chromeos::switches::kSystemDevMode) &&
-      !command_line->HasSwitch(chromeos::switches::kAllowRAInDevMode)) {
+      !command_line->HasSwitch(ash::switches::kAllowRAInDevMode)) {
     DVLOG(1) << "Protected media identifier disabled in dev mode.";
     return false;
   }
diff --git a/chrome/browser/media/router/discovery/access_code/access_code_media_sink_util.cc b/chrome/browser/media/router/discovery/access_code/access_code_media_sink_util.cc
index a446e229d..30650ca 100644
--- a/chrome/browser/media/router/discovery/access_code/access_code_media_sink_util.cc
+++ b/chrome/browser/media/router/discovery/access_code/access_code_media_sink_util.cc
@@ -106,6 +106,7 @@
   }
   extra_data.capabilities =
       ConvertDeviceCapabilitiesToInt(discovery_device.device_capabilities());
+  extra_data.discovered_by_access_code = true;
 
   const std::string& processed_uuid =
       MediaSinkInternal::ProcessDeviceUUID(unique_id);
diff --git a/chrome/browser/media/router/discovery/access_code/access_code_media_sink_util_unittest.cc b/chrome/browser/media/router/discovery/access_code/access_code_media_sink_util_unittest.cc
index 8be73b5..fde4f53 100644
--- a/chrome/browser/media/router/discovery/access_code/access_code_media_sink_util_unittest.cc
+++ b/chrome/browser/media/router/discovery/access_code/access_code_media_sink_util_unittest.cc
@@ -3,11 +3,11 @@
 // found in the LICENSE file.
 
 #include "chrome/browser/media/router/discovery/access_code/access_code_media_sink_util.h"
-#include "chrome/browser/media/router/discovery/access_code/access_code_test_util.h"
 
 #include "base/strings/stringprintf.h"
 #include "base/test/mock_callback.h"
 #include "base/test/task_environment.h"
+#include "chrome/browser/media/router/discovery/access_code/access_code_test_util.h"
 #include "chrome/test/base/testing_browser_process.h"
 #include "content/public/test/browser_task_environment.h"
 #include "net/base/ip_address.h"
@@ -142,6 +142,7 @@
   int port_value = 0;
   EXPECT_EQ(true, base::StringToInt(kExpectedPort, &port_value));
   expected_extra_data.ip_endpoint = net::IPEndPoint(expected_ip, port_value);
+  expected_extra_data.discovered_by_access_code = true;
 
   media_router::MediaSink expected_sink(
       base::StringPrintf("cast:<%s>", kExpectedSinkId), kExpectedDisplayName,
diff --git a/chrome/browser/media/router/discovery/mdns/cast_media_sink_service_impl.cc b/chrome/browser/media/router/discovery/mdns/cast_media_sink_service_impl.cc
index 6b6c4da..1f1a1b8 100644
--- a/chrome/browser/media/router/discovery/mdns/cast_media_sink_service_impl.cc
+++ b/chrome/browser/media/router/discovery/mdns/cast_media_sink_service_impl.cc
@@ -399,8 +399,13 @@
   dial_sink_failure_count_.clear();
   if (!IsNetworkIdUnknownOrDisconnected(last_network_id)) {
     std::vector<MediaSinkInternal> current_sinks;
-    for (const auto& entry : GetSinks())
-      current_sinks.push_back(entry.second);
+    for (const auto& entry : GetSinks()) {
+      // AccessCode sinks should not be cached because of expiration -- this is
+      // handled elsewhere instead.
+      if (!entry.second.cast_data().discovered_by_access_code) {
+        current_sinks.push_back(entry.second);
+      }
+    }
 
     sink_cache_[last_network_id] = std::move(current_sinks);
   }
@@ -723,4 +728,19 @@
   return base::Contains(GetSinks(), sink_id);
 }
 
+void CastMediaSinkServiceImpl::RemoveSink(const MediaSinkInternal& sink) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  MediaSinkServiceBase::RemoveSink(sink);
+
+  // Need a PostTask() here because CloseSocket() will release the memory of
+  // |socket|. Need to make sure all tasks on |socket| finish before deleting
+  // the object.
+  task_runner_->PostNonNestableTask(
+      FROM_HERE,
+      base::BindOnce(
+          base::IgnoreResult(&cast_channel::CastSocketService::CloseSocket),
+          base::Unretained(cast_socket_service_),
+          sink.cast_data().cast_channel_id));
+}
+
 }  // namespace media_router
diff --git a/chrome/browser/media/router/discovery/mdns/cast_media_sink_service_impl.h b/chrome/browser/media/router/discovery/mdns/cast_media_sink_service_impl.h
index 65f895a6..933ffba 100644
--- a/chrome/browser/media/router/discovery/mdns/cast_media_sink_service_impl.h
+++ b/chrome/browser/media/router/discovery/mdns/cast_media_sink_service_impl.h
@@ -112,6 +112,14 @@
   // Check to see if the given cast sink exists the sinks_.
   bool HasSink(const MediaSink::Id& sink_id);
 
+  // Function that handles complete removal of a cast sink from the media
+  // router. Deals with removing cast socket and any instances of the cast sink
+  // recorded throughout the router.
+  void RemoveCastSinkFromRouter(const MediaSinkInternal& sink);
+
+  // MediaSinkServiceBase overrides.
+  void RemoveSink(const MediaSinkInternal& sink) override;
+
  private:
   friend class CastMediaSinkServiceImplTest;
   FRIEND_TEST_ALL_PREFIXES(CastMediaSinkServiceImplTest,
@@ -172,6 +180,9 @@
   FRIEND_TEST_ALL_PREFIXES(CastMediaSinkServiceImplTest,
                            OpenChannelNewIPSameSink);
   FRIEND_TEST_ALL_PREFIXES(CastMediaSinkServiceImplTest, TestHasSink);
+  FRIEND_TEST_ALL_PREFIXES(CastMediaSinkServiceImplTest, TestRemoveSink);
+  FRIEND_TEST_ALL_PREFIXES(CastMediaSinkServiceImplTest,
+                           TestAccessCodeSinkNotAddedToNetworkCache);
 
   // Holds parameters controlling Cast channel retry strategy.
   struct RetryParams {
@@ -237,7 +248,8 @@
       const MediaSinkInternal& sink);
 
   // Invoked when opening cast channel on IO thread completes.
-  // |cast_sink|: Cast sink created from mDNS service description or DIAL sink.
+  // |cast_sink|: Cast sink created from mDNS service description, DIAL sink, or
+  // access code sink.
   // |backoff_entry|: backoff entry passed to |OnChannelErrorMayRetry| callback
   // if open channel fails.
   // |start_time|: time at which corresponding |OpenChannel| was called.
@@ -253,7 +265,8 @@
   // opening channel in a delay specified by |backoff_entry| if current failure
   // count is less than max retry attempts. Or invoke |OnChannelError| if retry
   // is not allowed.
-  // |cast_sink|: Cast sink created from mDNS service description or DIAL sink.
+  // |cast_sink|: Cast sink created from mDNS service description, DIAL sink, or
+  // access code sink.
   // |backoff_entry|: backoff entry holds failure count and calculates back-off
   // for next retry.
   // |error_state|: error encountered when opending cast channel.
@@ -263,7 +276,8 @@
                               SinkSource sink_source);
 
   // Invoked when opening cast channel succeeds.
-  // |cast_sink|: Cast sink created from mDNS service description or DIAL sink.
+  // |cast_sink|: Cast sink created from mDNS service description, DIAL sink, or
+  // access code sink.
   // |socket|: raw pointer of newly created cast channel. Does not take
   // ownership of |socket|.
   void OnChannelOpenSucceeded(MediaSinkInternal cast_sink,
diff --git a/chrome/browser/media/router/discovery/mdns/cast_media_sink_service_impl_unittest.cc b/chrome/browser/media/router/discovery/mdns/cast_media_sink_service_impl_unittest.cc
index e344785..84b499b 100644
--- a/chrome/browser/media/router/discovery/mdns/cast_media_sink_service_impl_unittest.cc
+++ b/chrome/browser/media/router/discovery/mdns/cast_media_sink_service_impl_unittest.cc
@@ -1360,11 +1360,107 @@
   MediaSinkInternal cast_sink2 = CreateCastSink(2);
 
   media_sink_service_impl_.AddOrUpdateSink(cast_sink1);
-
   EXPECT_TRUE(media_sink_service_impl_.HasSink(cast_sink1.id()));
   EXPECT_FALSE(media_sink_service_impl_.HasSink(cast_sink2.id()));
 }
 
+TEST_P(CastMediaSinkServiceImplTest, TestRemoveSink) {
+  auto cast_sink = CreateCastSink(1);
+  net::IPEndPoint ip_endpoint = CreateIPEndPoint(1);
+  cast_channel::MockCastSocket socket;
+  socket.set_id(1);
+  socket.SetIPEndpoint(ip_endpoint);
+  socket.SetErrorState(cast_channel::ChannelError::NONE);
+
+  ExpectOpenSocket(&socket);
+  media_sink_service_impl_.OpenChannel(
+      cast_sink, nullptr, CastMediaSinkServiceImpl::SinkSource::kAccessCode);
+
+  // Simulate channel is successfully opened.
+  EXPECT_CALL(observer_, OnSinkAddedOrUpdated(cast_sink));
+  media_sink_service_impl_.OnChannelOpenSucceeded(
+      cast_sink, &socket, CastMediaSinkServiceImpl::SinkSource::kAccessCode);
+
+  EXPECT_EQ(1u, media_sink_service_impl_.GetSinks().size());
+
+  // Verify sink content
+  EXPECT_TRUE(mock_timer_->IsRunning());
+  EXPECT_CALL(mock_sink_discovered_cb_,
+              Run(std::vector<MediaSinkInternal>({cast_sink})));
+  mock_timer_->Fire();
+
+  ON_CALL(*mock_cast_socket_service_,
+          GetSocket(testing::Matcher<const net::IPEndPoint&>(_)))
+      .WillByDefault(testing::Return(&socket));
+
+  // Expect that the sink is removed from objects.
+  EXPECT_CALL(*mock_cast_socket_service_, CloseSocket(_));
+  EXPECT_CALL(observer_, OnSinkRemoved(cast_sink));
+  media_sink_service_impl_.RemoveSink(cast_sink);
+  mock_time_task_runner_->FastForwardUntilNoTasksRemain();
+  EXPECT_TRUE(media_sink_service_impl_.GetSinks().empty());
+}
+
+TEST_P(CastMediaSinkServiceImplTest, TestAccessCodeSinkNotAddedToNetworkCache) {
+  media_sink_service_impl_.Start();
+  content::RunAllTasksUntilIdle();
+  // We need to run the mock task runner for the network change callback, but
+  // the socket retries interfere with our normal expectations.  Instead we
+  // disable retries with this line.
+  media_sink_service_impl_.retry_params_.max_retry_attempts = 0;
+
+  MediaSinkInternal sink1 = CreateCastSink(1);
+  MediaSinkInternal access_sink = CreateCastSink(2);
+  access_sink.cast_data().discovered_by_access_code = true;
+  net::IPEndPoint ip_endpoint1 = CreateIPEndPoint(1);
+  net::IPEndPoint ip_endpoint2 = CreateIPEndPoint(2);
+  std::vector<MediaSinkInternal> sink_list1{sink1, access_sink};
+
+  // Resolution will succeed for both sinks.
+  cast_channel::MockCastSocket socket1;
+  cast_channel::MockCastSocket socket2;
+  socket1.SetIPEndpoint(ip_endpoint1);
+  socket1.set_id(1);
+  socket2.SetIPEndpoint(ip_endpoint2);
+  socket2.set_id(2);
+  ExpectOpenSocket(&socket1);
+  ExpectOpenSocket(&socket2);
+  media_sink_service_impl_.OpenChannels(
+      sink_list1, CastMediaSinkServiceImpl::SinkSource::kMdns);
+
+  // Connect to a new network with different sinks.
+  fake_network_info_ = fake_wifi_info_;
+  ChangeConnectionType(network::mojom::ConnectionType::CONNECTION_WIFI);
+  content::RunAllTasksUntilIdle();
+  media_sink_service_impl_.OnChannelOpenFailed(ip_endpoint1, sink1);
+  media_sink_service_impl_.OnChannelOpenFailed(ip_endpoint2, access_sink);
+
+  MediaSinkInternal sink3 = CreateCastSink(3);
+  net::IPEndPoint ip_endpoint3 = CreateIPEndPoint(3);
+  std::vector<MediaSinkInternal> sink_list2{sink3};
+
+  cast_channel::MockCastSocket socket3;
+  socket3.SetIPEndpoint(ip_endpoint3);
+  socket3.set_id(3);
+  ExpectOpenSocket(&socket3);
+
+  media_sink_service_impl_.OpenChannels(
+      sink_list2, CastMediaSinkServiceImpl::SinkSource::kMdns);
+
+  // Reconnecting to the previous ethernet network should restore the same sinks
+  // from the cache and attempt to resolve them. access_sink should not be
+  // present so no OpenSocket call should not be made.
+  EXPECT_CALL(*mock_cast_socket_service_, OpenSocket_(ip_endpoint1, _));
+  EXPECT_CALL(*mock_cast_socket_service_, OpenSocket_(ip_endpoint2, _))
+      .Times(0);
+
+  fake_network_info_ = fake_ethernet_info_;
+  ChangeConnectionType(network::mojom::ConnectionType::CONNECTION_ETHERNET);
+
+  content::RunAllTasksUntilIdle();
+  mock_time_task_runner_->FastForwardUntilNoTasksRemain();
+}
+
 INSTANTIATE_TEST_SUITE_P(DialMediaSinkServiceEnabled,
                          CastMediaSinkServiceImplTest,
                          testing::Bool());
diff --git a/chrome/browser/media/router/providers/cast/cast_app_discovery_service_unittest.cc b/chrome/browser/media/router/providers/cast/cast_app_discovery_service_unittest.cc
index cf2a2dc..1cbf562e 100644
--- a/chrome/browser/media/router/providers/cast/cast_app_discovery_service_unittest.cc
+++ b/chrome/browser/media/router/providers/cast/cast_app_discovery_service_unittest.cc
@@ -41,7 +41,7 @@
             *CastMediaSource::FromMediaSourceId("cast:AAAAAAAA?clientId=2")),
         source_b_1_(
             *CastMediaSource::FromMediaSourceId("cast:BBBBBBBB?clientId=1")) {
-    ON_CALL(socket_service_, GetSocket(_))
+    ON_CALL(socket_service_, GetSocket(testing::Matcher<int>(_)))
         .WillByDefault(testing::Return(&socket_));
     task_runner_->RunPendingTasks();
   }
diff --git a/chrome/browser/nearby_sharing/common/nearby_share_features.cc b/chrome/browser/nearby_sharing/common/nearby_share_features.cc
index a2d3ceaa..6318904 100644
--- a/chrome/browser/nearby_sharing/common/nearby_share_features.cc
+++ b/chrome/browser/nearby_sharing/common/nearby_share_features.cc
@@ -40,4 +40,8 @@
 const base::Feature kNearbySharingWebRtc{"NearbySharingWebRtc",
                                          base::FEATURE_ENABLED_BY_DEFAULT};
 
+// Enables use of WifiLan in Nearby Share.
+const base::Feature kNearbySharingWifiLan{"NearbySharingWifiLan",
+                                          base::FEATURE_DISABLED_BY_DEFAULT};
+
 }  // namespace features
diff --git a/chrome/browser/nearby_sharing/common/nearby_share_features.h b/chrome/browser/nearby_sharing/common/nearby_share_features.h
index 702a2c3..52807b5 100644
--- a/chrome/browser/nearby_sharing/common/nearby_share_features.h
+++ b/chrome/browser/nearby_sharing/common/nearby_share_features.h
@@ -16,6 +16,7 @@
 extern const base::Feature kNearbySharingOnePageOnboarding;
 extern const base::Feature kNearbySharingSelfShare;
 extern const base::Feature kNearbySharingWebRtc;
+extern const base::Feature kNearbySharingWifiLan;
 
 }  // namespace features
 
diff --git a/chrome/browser/nearby_sharing/constants.h b/chrome/browser/nearby_sharing/constants.h
index 2a7a9e6..73877665 100644
--- a/chrome/browser/nearby_sharing/constants.h
+++ b/chrome/browser/nearby_sharing/constants.h
@@ -38,9 +38,9 @@
 // Time between successive progress updates.
 constexpr base::TimeDelta kMinProgressUpdateFrequency = base::Milliseconds(100);
 
-// TODO(crbug.com/1129069): Set this to true when WiFi LAN is supported to
-// enable logic that checks for an internet connection for managing surfaces and
-// the utility process lifecycle.
-constexpr bool kIsWifiLanSupported = false;
+// Whether or not WifiLan is supported for advertising or discovery. Support as
+// a bandwidth upgrade medium is behind a feature flag.
+constexpr bool kIsWifiLanAdvertisingSupported = false;
+constexpr bool kIsWifiLanDiscoverySupported = false;
 
 #endif  // CHROME_BROWSER_NEARBY_SHARING_CONSTANTS_H_
diff --git a/chrome/browser/nearby_sharing/nearby_connections_manager_impl.cc b/chrome/browser/nearby_sharing/nearby_connections_manager_impl.cc
index 511bb68..8325908 100644
--- a/chrome/browser/nearby_sharing/nearby_connections_manager_impl.cc
+++ b/chrome/browser/nearby_sharing/nearby_connections_manager_impl.cc
@@ -28,10 +28,7 @@
 const location::nearby::connections::mojom::Strategy kStrategy =
     location::nearby::connections::mojom::Strategy::kP2pPointToPoint;
 
-bool ShouldEnableWebRtc(DataUsage data_usage, PowerLevel power_level) {
-  if (!base::FeatureList::IsEnabled(features::kNearbySharingWebRtc))
-    return false;
-
+bool ShouldUseInternet(DataUsage data_usage, PowerLevel power_level) {
   // We won't use internet if the user requested we don't.
   if (data_usage == DataUsage::kOffline)
     return false;
@@ -45,24 +42,47 @@
 
   // Verify that this network has an internet connection.
   if (connection_type == net::NetworkChangeNotifier::CONNECTION_NONE) {
-    NS_LOG(VERBOSE) << __func__
-                    << ": Do not use WebRTC; no internet connection.";
+    NS_LOG(VERBOSE) << __func__ << ": No internet connection.";
     return false;
   }
 
-  // If the user wants to limit WebRTC, then don't use it on metered networks.
+  // If the user wants to limit Wi-Fi, then don't use it on metered networks.
   if (data_usage == DataUsage::kWifiOnly &&
       net::NetworkChangeNotifier::GetConnectionCost() ==
           net::NetworkChangeNotifier::CONNECTION_COST_METERED) {
-    NS_LOG(VERBOSE) << __func__ << ": Do not use WebRTC with " << data_usage
+    NS_LOG(VERBOSE) << __func__ << ": Do not use internet with " << data_usage
                     << " and a metered connection.";
     return false;
   }
 
-  // We're online, the user hasn't disabled WebRTC, let's use it!
+  // We're online, the user hasn't disabled Wi-Fi, let's use it!
   return true;
 }
 
+bool ShouldEnableWebRtc(DataUsage data_usage, PowerLevel power_level) {
+  return base::FeatureList::IsEnabled(features::kNearbySharingWebRtc) &&
+         ShouldUseInternet(data_usage, power_level);
+}
+
+bool ShouldEnableWifiLan(DataUsage data_usage, PowerLevel power_level) {
+  if (!base::FeatureList::IsEnabled(features::kNearbySharingWifiLan))
+    return false;
+
+  // WifiLan only works if both devices are using the same router. We can't
+  // guarantee this, but at least check that we are using Wi-Fi or ethernet.
+  // TODO(https://crbug.com/1261238): Test if WifiLan can work if both devices
+  // are connected to the router without an internet connection. If so, return
+  // true if connection_type == net::NetworkChangeNotifier::CONNECTION_NONE.
+  net::NetworkChangeNotifier::ConnectionType connection_type =
+      net::NetworkChangeNotifier::GetConnectionType();
+  bool is_connection_wifi_or_ethernet =
+      connection_type == net::NetworkChangeNotifier::CONNECTION_WIFI ||
+      connection_type == net::NetworkChangeNotifier::CONNECTION_ETHERNET;
+
+  return ShouldUseInternet(data_usage, power_level) &&
+         is_connection_wifi_or_ethernet;
+}
+
 std::string MediumSelectionToString(
     const location::nearby::connections::mojom::MediumSelection& mediums) {
   std::stringstream ss;
@@ -120,7 +140,9 @@
       // level isn't a factor when deciding whether or not to allow WebRTC
       // upgrades from this advertisement.
       ShouldEnableWebRtc(data_usage, PowerLevel::kHighPower),
-      /*wifi_lan=*/is_high_power && kIsWifiLanSupported);
+      /*wifi_lan=*/
+      ShouldEnableWifiLan(data_usage, PowerLevel::kHighPower) &&
+          kIsWifiLanAdvertisingSupported);
   NS_LOG(VERBOSE) << __func__ << ": "
                   << "is_high_power=" << (is_high_power ? "yes" : "no")
                   << ", data_usage=" << data_usage << ", allowed_mediums="
@@ -184,7 +206,9 @@
       /*bluetooth=*/true,
       /*ble=*/true,
       /*webrtc=*/ShouldEnableWebRtc(data_usage, PowerLevel::kHighPower),
-      /*wifi_lan=*/kIsWifiLanSupported);
+      /*wifi_lan=*/
+      ShouldEnableWifiLan(data_usage, PowerLevel::kHighPower) &&
+          kIsWifiLanDiscoverySupported);
   NS_LOG(VERBOSE) << __func__ << ": "
                   << "data_usage=" << data_usage << ", allowed_mediums="
                   << MediumSelectionToString(*allowed_mediums);
@@ -238,7 +262,7 @@
   auto allowed_mediums = MediumSelection::New(
       /*bluetooth=*/true,
       /*ble=*/false, ShouldEnableWebRtc(data_usage, PowerLevel::kHighPower),
-      /*wifi_lan=*/kIsWifiLanSupported);
+      /*wifi_lan=*/ShouldEnableWifiLan(data_usage, PowerLevel::kHighPower));
   NS_LOG(VERBOSE) << __func__ << ": "
                   << "data_usage=" << data_usage << ", allowed_mediums="
                   << MediumSelectionToString(*allowed_mediums);
@@ -444,9 +468,11 @@
   if (!process_reference_)
     return;
 
-  // The only bandwidth upgrade at this point is WebRTC.
-  if (!base::FeatureList::IsEnabled(features::kNearbySharingWebRtc))
+  // The only bandwidth upgrade mediums at this point are WebRTC and WifiLan.
+  if (!base::FeatureList::IsEnabled(features::kNearbySharingWebRtc) &&
+      !base::FeatureList::IsEnabled(features::kNearbySharingWifiLan)) {
     return;
+  }
 
   requested_bwu_endpoint_ids_.emplace(endpoint_id);
   process_reference_->GetNearbyConnections()->InitiateBandwidthUpgrade(
diff --git a/chrome/browser/nearby_sharing/nearby_connections_manager_impl_unittest.cc b/chrome/browser/nearby_sharing/nearby_connections_manager_impl_unittest.cc
index b783a24e..cdabd076 100644
--- a/chrome/browser/nearby_sharing/nearby_connections_manager_impl_unittest.cc
+++ b/chrome/browser/nearby_sharing/nearby_connections_manager_impl_unittest.cc
@@ -399,6 +399,7 @@
   content::BrowserTaskEnvironment task_environment_{
       base::test::TaskEnvironment::TimeSource::MOCK_TIME};
   bool should_use_web_rtc_ = true;
+  bool should_use_wifilan_ = false;
   DataUsage default_data_usage_ = DataUsage::kWifiOnly;
   std::unique_ptr<net::test::MockNetworkChangeNotifier> network_notifier_ =
       net::test::MockNetworkChangeNotifier::Create();
@@ -512,8 +513,8 @@
 /******************************************************************************/
 // Begin: NearbyConnectionsManagerImplTestConnectionMediums
 /******************************************************************************/
-using ConnectionMediumsTestParam =
-    std::tuple<DataUsage, net::NetworkChangeNotifier::ConnectionType, bool>;
+using ConnectionMediumsTestParam = std::
+    tuple<DataUsage, net::NetworkChangeNotifier::ConnectionType, bool, bool>;
 class NearbyConnectionsManagerImplTestConnectionMediums
     : public NearbyConnectionsManagerImplTest,
       public testing::WithParamInterface<ConnectionMediumsTestParam> {};
@@ -525,25 +526,44 @@
   net::NetworkChangeNotifier::ConnectionType connection_type =
       std::get<1>(param);
   bool is_webrtc_enabled = std::get<2>(GetParam());
+  bool is_wifilan_enabled = std::get<3>(GetParam());
+
+  std::vector<base::Feature> enabled_features;
+  std::vector<base::Feature> disabled_features;
+  if (is_webrtc_enabled) {
+    enabled_features.push_back(features::kNearbySharingWebRtc);
+  } else {
+    disabled_features.push_back(features::kNearbySharingWebRtc);
+  }
+  if (is_wifilan_enabled) {
+    enabled_features.push_back(features::kNearbySharingWifiLan);
+  } else {
+    disabled_features.push_back(features::kNearbySharingWifiLan);
+  }
   scoped_feature_list_.Reset();
-  scoped_feature_list_.InitWithFeatureState(features::kNearbySharingWebRtc,
-                                            is_webrtc_enabled);
+  scoped_feature_list_.InitWithFeatures(enabled_features, disabled_features);
 
   network_notifier_->SetConnectionType(connection_type);
   network_notifier_->SetUseDefaultConnectionCostImplementation(true);
-  should_use_web_rtc_ =
-      is_webrtc_enabled && data_usage != DataUsage::kOffline &&
+  bool should_use_internet =
+      data_usage != DataUsage::kOffline &&
       connection_type != net::NetworkChangeNotifier::CONNECTION_NONE &&
       (data_usage != DataUsage::kWifiOnly ||
        (net::NetworkChangeNotifier::GetConnectionCost() !=
         net::NetworkChangeNotifier::CONNECTION_COST_METERED));
+  bool is_connection_wifi_or_ethernet =
+      connection_type == net::NetworkChangeNotifier::CONNECTION_WIFI ||
+      connection_type == net::NetworkChangeNotifier::CONNECTION_ETHERNET;
+  should_use_web_rtc_ = is_webrtc_enabled && should_use_internet;
+  should_use_wifilan_ = is_wifilan_enabled && should_use_internet &&
+                        is_connection_wifi_or_ethernet;
 
   // TODO(crbug.com/1129069): Update when WiFi LAN is supported.
   auto expected_mediums = MediumSelection::New(
       /*bluetooth=*/true,
       /*ble=*/false,
       /*web_rtc=*/should_use_web_rtc_,
-      /*wifi_lan=*/false);
+      /*wifi_lan=*/should_use_wifilan_);
 
   // StartDiscovery will succeed.
   mojo::Remote<EndpointDiscoveryListener> discovery_listener_remote;
@@ -585,6 +605,7 @@
         testing::Values(net::NetworkChangeNotifier::CONNECTION_NONE,
                         net::NetworkChangeNotifier::CONNECTION_WIFI,
                         net::NetworkChangeNotifier::CONNECTION_3G),
+        testing::Bool(),
         testing::Bool()));
 /******************************************************************************/
 // End: NearbyConnectionsManagerImplTestConnectionMediums
diff --git a/chrome/browser/nearby_sharing/nearby_sharing_service_impl.cc b/chrome/browser/nearby_sharing/nearby_sharing_service_impl.cc
index 03a753b..dbf32fb 100644
--- a/chrome/browser/nearby_sharing/nearby_sharing_service_impl.cc
+++ b/chrome/browser/nearby_sharing/nearby_sharing_service_impl.cc
@@ -1687,7 +1687,9 @@
           net::NetworkChangeNotifier::ConnectionType::CONNECTION_WIFI ||
       connection_type ==
           net::NetworkChangeNotifier::ConnectionType::CONNECTION_ETHERNET;
-  return IsBluetoothPowered() || (kIsWifiLanSupported && hasNetworkConnection);
+  return IsBluetoothPowered() ||
+         (kIsWifiLanAdvertisingSupported && kIsWifiLanDiscoverySupported &&
+          hasNetworkConnection);
 }
 
 void NearbySharingServiceImpl::InvalidateSurfaceState() {
diff --git a/chrome/browser/net/profile_network_context_service.cc b/chrome/browser/net/profile_network_context_service.cc
index ffe852e..04ac573 100644
--- a/chrome/browser/net/profile_network_context_service.cc
+++ b/chrome/browser/net/profile_network_context_service.cc
@@ -555,7 +555,7 @@
   // Note that while this applies to the whole sign-in profile / lock screen
   // profile, client certificates will only be selected for the StoragePartition
   // currently used in the sign-in frame (see SigninPartitionManager).
-  if (chromeos::switches::IsSigninFrameClientCertsEnabled() &&
+  if (ash::switches::IsSigninFrameClientCertsEnabled() &&
       (ash::ProfileHelper::IsSigninProfile(profile_) ||
        ash::ProfileHelper::IsLockScreenProfile(profile_))) {
     use_system_key_slot = true;
diff --git a/chrome/browser/offline_pages/background_loader_offliner.cc b/chrome/browser/offline_pages/background_loader_offliner.cc
index d8e7b83..08633d4 100644
--- a/chrome/browser/offline_pages/background_loader_offliner.cc
+++ b/chrome/browser/offline_pages/background_loader_offliner.cc
@@ -38,7 +38,6 @@
 #include "content/public/browser/web_contents_user_data.h"
 #include "net/cert/cert_status_flags.h"
 #include "net/http/http_response_headers.h"
-#include "third_party/blink/public/common/loader/previews_state.h"
 
 namespace offline_pages {
 
diff --git a/chrome/browser/offline_pages/offline_page_request_handler_unittest.cc b/chrome/browser/offline_pages/offline_page_request_handler_unittest.cc
index 7dec8374..b966744 100644
--- a/chrome/browser/offline_pages/offline_page_request_handler_unittest.cc
+++ b/chrome/browser/offline_pages/offline_page_request_handler_unittest.cc
@@ -52,7 +52,6 @@
 #include "services/network/public/cpp/resource_request.h"
 #include "services/network/public/mojom/url_response_head.mojom.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/blink/public/common/loader/previews_state.h"
 #include "url/gurl.h"
 
 #if BUILDFLAG(IS_ANDROID)
diff --git a/chrome/browser/offline_pages/offline_page_url_loader.cc b/chrome/browser/offline_pages/offline_page_url_loader.cc
index 048ccbf..3a12f0e 100644
--- a/chrome/browser/offline_pages/offline_page_url_loader.cc
+++ b/chrome/browser/offline_pages/offline_page_url_loader.cc
@@ -20,7 +20,6 @@
 #include "net/url_request/referrer_policy.h"
 #include "services/network/public/cpp/resource_request.h"
 #include "services/network/public/mojom/url_response_head.mojom.h"
-#include "third_party/blink/public/common/loader/previews_state.h"
 #include "third_party/blink/public/mojom/loader/resource_load_info.mojom-shared.h"
 
 namespace offline_pages {
diff --git a/chrome/browser/optimization_guide/prediction/prediction_manager_browsertest.cc b/chrome/browser/optimization_guide/prediction/prediction_manager_browsertest.cc
index 12d63b7..60bffe6 100644
--- a/chrome/browser/optimization_guide/prediction/prediction_manager_browsertest.cc
+++ b/chrome/browser/optimization_guide/prediction/prediction_manager_browsertest.cc
@@ -493,7 +493,7 @@
     PredictionManagerBrowserTest::SetUpCommandLine(command_line);
 #if BUILDFLAG(IS_CHROMEOS_ASH)
     command_line->AppendSwitch(
-        chromeos::switches::kIgnoreUserProfileMappingForTests);
+        ash::switches::kIgnoreUserProfileMappingForTests);
 #endif
   }
 
diff --git a/chrome/browser/password_check/android/BUILD.gn b/chrome/browser/password_check/android/BUILD.gn
index 9ed2539b..965e511 100644
--- a/chrome/browser/password_check/android/BUILD.gn
+++ b/chrome/browser/password_check/android/BUILD.gn
@@ -39,22 +39,28 @@
     "internal:public_ui_factory_java",
     "//base:base_java",
     "//chrome/browser/password_manager/android:java",
+    "//chrome/browser/tab:java",
     "//components/browser_ui/settings/android:java",
     "//components/browser_ui/widget/android:java",
+    "//components/ukm/android:java",
+    "//content/public/android:content_full_java",
     "//third_party/android_deps:material_design_java",
     "//third_party/androidx:androidx_annotation_annotation_java",
     "//third_party/androidx:androidx_fragment_fragment_java",
     "//third_party/androidx:androidx_preference_preference_java",
     "//third_party/androidx:androidx_recyclerview_recyclerview_java",
     "//third_party/androidx:androidx_vectordrawable_vectordrawable_java",
+    "//ui/android:ui_no_recycler_view_java",
   ]
   sources = [
+    "java/src/org/chromium/chrome/browser/password_check/PasswordChangeType.java",
     "java/src/org/chromium/chrome/browser/password_check/PasswordCheck.java",
     "java/src/org/chromium/chrome/browser/password_check/PasswordCheckEditFragmentView.java",
     "java/src/org/chromium/chrome/browser/password_check/PasswordCheckFragmentView.java",
     "java/src/org/chromium/chrome/browser/password_check/PasswordCheckMetricsRecorder.java",
     "java/src/org/chromium/chrome/browser/password_check/PasswordCheckReferrer.java",
     "java/src/org/chromium/chrome/browser/password_check/PasswordCheckResolutionAction.java",
+    "java/src/org/chromium/chrome/browser/password_check/PasswordCheckUkmRecorder.java",
     "java/src/org/chromium/chrome/browser/password_check/PasswordCheckUserAction.java",
   ]
   resources_package = "org.chromium.chrome.browser.password_check"
diff --git a/chrome/browser/password_check/android/internal/java/src/org/chromium/chrome/browser/password_check/helper/PasswordCheckChangePasswordHelper.java b/chrome/browser/password_check/android/internal/java/src/org/chromium/chrome/browser/password_check/helper/PasswordCheckChangePasswordHelper.java
index 22b7e32..b7ae5a6 100644
--- a/chrome/browser/password_check/android/internal/java/src/org/chromium/chrome/browser/password_check/helper/PasswordCheckChangePasswordHelper.java
+++ b/chrome/browser/password_check/android/internal/java/src/org/chromium/chrome/browser/password_check/helper/PasswordCheckChangePasswordHelper.java
@@ -14,8 +14,10 @@
 
 import org.chromium.base.IntentUtils;
 import org.chromium.chrome.browser.password_check.CompromisedCredential;
+import org.chromium.chrome.browser.password_check.PasswordChangeType;
 import org.chromium.chrome.browser.password_check.PasswordCheckComponentUi;
 import org.chromium.chrome.browser.password_check.PasswordCheckEditFragmentView;
+import org.chromium.chrome.browser.password_check.PasswordCheckUkmRecorder;
 import org.chromium.components.browser_ui.settings.SettingsLauncher;
 
 import java.io.UnsupportedEncodingException;
@@ -66,7 +68,8 @@
         // match to open it.
         IntentUtils.safeStartActivity(mContext,
                 credential.getAssociatedApp().isEmpty()
-                        ? buildIntent(credential.getPasswordChangeUrl())
+                        ? buildIntent(
+                                credential.getPasswordChangeUrl(), PasswordChangeType.MANUAL_CHANGE)
                         : getPackageLaunchIntent(credential.getAssociatedApp()));
     }
 
@@ -89,7 +92,7 @@
      */
     public void launchCctWithScript(CompromisedCredential credential) {
         String origin = credential.getAssociatedUrl().getOrigin().getSpec();
-        Intent intent = buildIntent(origin);
+        Intent intent = buildIntent(origin, PasswordChangeType.AUTOMATED_CHANGE);
         populateAutofillAssistantExtras(intent, origin, credential.getUsername());
         IntentUtils.safeStartActivity(mContext, intent);
     }
@@ -115,9 +118,10 @@
      * Builds an intent to launch a CCT.
      *
      * @param initialUrl Initial URL to launch a CCT.
+     * @param passwordChangeType Password change type.
      * @return {@link Intent} for CCT.
      */
-    private Intent buildIntent(String initialUrl) {
+    private Intent buildIntent(String initialUrl, @PasswordChangeType int passwordChangeType) {
         CustomTabsIntent customTabIntent =
                 new CustomTabsIntent.Builder().setShowTitle(true).build();
         customTabIntent.intent.setData(Uri.parse(initialUrl));
@@ -125,6 +129,9 @@
                 mContext, customTabIntent.intent);
         intent.setPackage(mContext.getPackageName());
         intent.putExtra(Browser.EXTRA_APPLICATION_ID, mContext.getPackageName());
+        intent.putExtra(PasswordCheckUkmRecorder.PASSWORD_CHECK_PACKAGE
+                        + PasswordCheckUkmRecorder.PASSWORD_CHANGE_TYPE,
+                passwordChangeType);
         mTrustedIntentHelper.addTrustedIntentExtras(intent);
         return intent;
     }
diff --git a/chrome/browser/password_check/android/java/src/org/chromium/chrome/browser/password_check/PasswordChangeType.java b/chrome/browser/password_check/android/java/src/org/chromium/chrome/browser/password_check/PasswordChangeType.java
new file mode 100644
index 0000000..eab01d2
--- /dev/null
+++ b/chrome/browser/password_check/android/java/src/org/chromium/chrome/browser/password_check/PasswordChangeType.java
@@ -0,0 +1,30 @@
+// 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.password_check;
+
+import androidx.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * These values are persisted to logs. Entries should not be renumbered and
+ * numeric values should never be reused. To be kept in sync with PasswordChangeType in
+ * enums.xml.
+ */
+@IntDef({PasswordChangeType.UNKNOWN, PasswordChangeType.MANUAL_CHANGE,
+        PasswordChangeType.AUTOMATED_CHANGE})
+@Retention(RetentionPolicy.SOURCE)
+public @interface PasswordChangeType {
+    int UNKNOWN = 0;
+    /**
+     * A user opened a site to change a password manually.
+     */
+    int MANUAL_CHANGE = 1;
+    /**
+     * A user started an automated password change flow.
+     */
+    int AUTOMATED_CHANGE = 2;
+}
\ No newline at end of file
diff --git a/chrome/browser/password_check/android/java/src/org/chromium/chrome/browser/password_check/PasswordCheckUkmRecorder.java b/chrome/browser/password_check/android/java/src/org/chromium/chrome/browser/password_check/PasswordCheckUkmRecorder.java
new file mode 100644
index 0000000..420a754
--- /dev/null
+++ b/chrome/browser/password_check/android/java/src/org/chromium/chrome/browser/password_check/PasswordCheckUkmRecorder.java
@@ -0,0 +1,77 @@
+// 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.password_check;
+
+import android.app.Activity;
+import android.content.Intent;
+
+import org.chromium.chrome.browser.tab.EmptyTabObserver;
+import org.chromium.chrome.browser.tab.Tab;
+import org.chromium.components.ukm.UkmRecorder;
+import org.chromium.content_public.browser.WebContents;
+import org.chromium.ui.base.WindowAndroid;
+
+/**
+ * Helper class for recording password check UKM metrics
+ */
+public class PasswordCheckUkmRecorder extends EmptyTabObserver {
+    public static final String PASSWORD_CHECK_PACKAGE =
+            "org.chromium.chrome.browser.password_check.";
+    public static final String PASSWORD_CHANGE_TYPE = "PASSWORD_CHANGE_TYPE";
+
+    /**
+     * Creates {@link PasswordCheckUkmRecorder} instance.
+     */
+    public static void createForTab(Tab tab) {
+        new PasswordCheckUkmRecorder(tab);
+    }
+
+    /**
+     * Constructs a new PasswordCheckUkmRecorder for a specific tab.
+     *
+     * @param tab Tab this PasswordCheckUkmRecorder is created for.
+     */
+    private PasswordCheckUkmRecorder(Tab tab) {
+        tab.addObserver(this);
+    }
+
+    private Intent getIntent(WebContents webContents) {
+        WindowAndroid window = webContents.getTopLevelNativeWindow();
+        if (window == null) return null;
+        Activity activity = window.getActivity().get();
+
+        return (activity != null) ? activity.getIntent() : null;
+    }
+
+    private void recordPasswordChange(
+            WebContents webContents, @PasswordChangeType int passwordChangeType) {
+        new UkmRecorder.Bridge().recordEventWithIntegerMetric(webContents,
+                "PasswordManager.PasswordChangeTriggered", "PasswordChangeType",
+                passwordChangeType);
+    }
+
+    @Override
+    public void onInitialized(Tab tab, String appId) {
+        // Record UKMs
+        if (tab.getWebContents() == null) {
+            return;
+        }
+        Intent intent = getIntent(tab.getWebContents());
+        if (intent == null || intent.getExtras() == null) {
+            return;
+        }
+
+        if (intent.hasExtra(PASSWORD_CHECK_PACKAGE + PASSWORD_CHANGE_TYPE)) {
+            recordPasswordChange(tab.getWebContents(),
+                    intent.getExtras().getInt(PASSWORD_CHECK_PACKAGE + PASSWORD_CHANGE_TYPE));
+            intent.removeExtra(PASSWORD_CHECK_PACKAGE + PASSWORD_CHANGE_TYPE);
+        }
+    }
+
+    @Override
+    public void onDestroyed(Tab tab) {
+        tab.removeObserver(this);
+    }
+}
diff --git a/chrome/browser/policy/cloud/component_cloud_policy_browsertest.cc b/chrome/browser/policy/cloud/component_cloud_policy_browsertest.cc
index 7e093eb5..df9f9d8 100644
--- a/chrome/browser/policy/cloud/component_cloud_policy_browsertest.cc
+++ b/chrome/browser/policy/cloud/component_cloud_policy_browsertest.cc
@@ -108,12 +108,12 @@
     // ExtensionBrowserTest sets the login users to a non-managed value;
     // replace it. This is the default username sent in policy blobs from the
     // testserver.
-    command_line->AppendSwitchASCII(::chromeos::switches::kLoginUser,
+    command_line->AppendSwitchASCII(ash::switches::kLoginUser,
                                     PolicyBuilder::kFakeUsername);
     // Let policy code know that policy is not required to be cached at startup
     // (it can be loaded asynchronously).
-    command_line->AppendSwitchASCII(
-        ::chromeos::switches::kProfileRequiresPolicy, "false");
+    command_line->AppendSwitchASCII(ash::switches::kProfileRequiresPolicy,
+                                    "false");
 #endif
   }
 
diff --git a/chrome/browser/policy/extension_policy_browsertest.cc b/chrome/browser/policy/extension_policy_browsertest.cc
index bab66c1..5b8b892 100644
--- a/chrome/browser/policy/extension_policy_browsertest.cc
+++ b/chrome/browser/policy/extension_policy_browsertest.cc
@@ -2410,7 +2410,7 @@
   void SetUpCommandLine(base::CommandLine* command_line) override {
 #if BUILDFLAG(IS_CHROMEOS_ASH)
     command_line->AppendSwitch(
-        chromeos::switches::kIgnoreUserProfileMappingForTests);
+        ash::switches::kIgnoreUserProfileMappingForTests);
 #endif
     PolicyTest::SetUpCommandLine(command_line);
   }
diff --git a/chrome/browser/policy/networking/device_network_configuration_updater_ash.cc b/chrome/browser/policy/networking/device_network_configuration_updater_ash.cc
index 9b1d18b6..9546961 100644
--- a/chrome/browser/policy/networking/device_network_configuration_updater_ash.cc
+++ b/chrome/browser/policy/networking/device_network_configuration_updater_ash.cc
@@ -92,7 +92,7 @@
   // allowed is the Device Policy. If there is no Device Policy, then
   // data roaming should be allowed if this is a Cellular First device.
   if (!chromeos::InstallAttributes::Get()->IsEnterpriseManaged() &&
-      chromeos::switches::IsCellularFirstDevice()) {
+      ash::switches::IsCellularFirstDevice()) {
     network_device_handler_->SetCellularPolicyAllowRoaming(
         /*policy_allow_roaming=*/true);
   } else {
diff --git a/chrome/browser/policy/policy_network_browsertest.cc b/chrome/browser/policy/policy_network_browsertest.cc
index b72495fb..03d753b6 100644
--- a/chrome/browser/policy/policy_network_browsertest.cc
+++ b/chrome/browser/policy/policy_network_browsertest.cc
@@ -340,7 +340,7 @@
   void SetUpCommandLine(base::CommandLine* command_line) override {
 #if BUILDFLAG(IS_CHROMEOS_ASH)
     command_line->AppendSwitch(
-        chromeos::switches::kIgnoreUserProfileMappingForTests);
+        ash::switches::kIgnoreUserProfileMappingForTests);
 #endif
     // Ensure that QUIC is enabled by default on browser startup.
     command_line->AppendSwitch(switches::kEnableQuic);
diff --git a/chrome/browser/policy/policy_prefs_browsertest.cc b/chrome/browser/policy/policy_prefs_browsertest.cc
index bcdb2f8..0c0a615 100644
--- a/chrome/browser/policy/policy_prefs_browsertest.cc
+++ b/chrome/browser/policy/policy_prefs_browsertest.cc
@@ -144,8 +144,8 @@
   void SetUpCommandLine(base::CommandLine* command_line) override {
     PolicyPrefsTest::SetUpCommandLine(command_line);
 
-    command_line->AppendSwitch(chromeos::switches::kLoginManager);
-    command_line->AppendSwitch(chromeos::switches::kForceLoginManagerInTests);
+    command_line->AppendSwitch(ash::switches::kLoginManager);
+    command_line->AppendSwitch(ash::switches::kForceLoginManagerInTests);
   }
 };
 
diff --git a/chrome/browser/predictors/loading_predictor.cc b/chrome/browser/predictors/loading_predictor.cc
index 3f29b8d..946aeaf 100644
--- a/chrome/browser/predictors/loading_predictor.cc
+++ b/chrome/browser/predictors/loading_predictor.cc
@@ -17,10 +17,10 @@
 #include "net/base/network_isolation_key.h"
 #include "url/origin.h"
 
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
 #include "base/android/radio_utils.h"
 #include "base/power_monitor/power_monitor.h"
-#endif  // defined(OS_ANDROID)
+#endif  // BUILDFLAG(IS_ANDROID)
 
 namespace features {
 
@@ -65,7 +65,7 @@
 }
 
 bool IsPreconnectExpensive() {
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
   // Preconnecting is expensive while on battery power and cellular data and
   // the radio signal is weak.
   if ((base::PowerMonitor::IsInitialized() &&
diff --git a/chrome/browser/predictors/predictors_features.cc b/chrome/browser/predictors/predictors_features.cc
index 15eef9a..744d501 100644
--- a/chrome/browser/predictors/predictors_features.cc
+++ b/chrome/browser/predictors/predictors_features.cc
@@ -35,11 +35,11 @@
 
 const base::FeatureState
     kLoadingPredictorUseOptimizationGuideDefaultFeatureState =
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
         base::FEATURE_ENABLED_BY_DEFAULT;
-#else   // !defined(OS_ANDROID)
+#else
         base::FEATURE_DISABLED_BY_DEFAULT;
-#endif  // defined(OS_ANDROID)
+#endif
 
 // Modifies loading predictor so that it can also use predictions coming from
 // the optimization guide.
@@ -48,11 +48,11 @@
     kLoadingPredictorUseOptimizationGuideDefaultFeatureState};
 
 const base::FeatureState kLoadingPredictorPrefetchDefaultFeatureState =
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
     base::FEATURE_ENABLED_BY_DEFAULT;
-#else   // !defined(OS_ANDROID)
+#else
     base::FEATURE_DISABLED_BY_DEFAULT;
-#endif  // defined(OS_ANDROID)
+#endif
 
 // Modifies loading predictor so that it does prefetches of subresources instead
 // of preconnects.
diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc
index 5c340b68..e30e8d33 100644
--- a/chrome/browser/prefs/browser_prefs.cc
+++ b/chrome/browser/prefs/browser_prefs.cc
@@ -333,6 +333,7 @@
 #include "chrome/browser/ash/login/users/multi_profile_user_controller.h"
 #include "chrome/browser/ash/net/network_throttling_observer.h"
 #include "chrome/browser/ash/plugin_vm/plugin_vm_pref_names.h"
+#include "chrome/browser/ash/policy/active_directory/active_directory_migration_manager.h"
 #include "chrome/browser/ash/policy/core/browser_policy_connector_ash.h"
 #include "chrome/browser/ash/policy/core/device_cloud_policy_manager_ash.h"
 #include "chrome/browser/ash/policy/core/dm_token_storage.h"
@@ -1107,6 +1108,7 @@
   extensions::login_api::RegisterLocalStatePrefs(registry);
   ::onc::RegisterPrefs(registry);
   policy::ActiveDirectoryDeviceStateUploader::RegisterLocalStatePrefs(registry);
+  policy::ActiveDirectoryMigrationManager::RegisterLocalStatePrefs(registry);
   policy::AdbSideloadingAllowanceModePolicyHandler::RegisterPrefs(registry);
   policy::AutoEnrollmentClientImpl::RegisterPrefs(registry);
   policy::BrowserPolicyConnectorAsh::RegisterPrefs(registry);
diff --git a/chrome/browser/prefs/chrome_command_line_pref_store.cc b/chrome/browser/prefs/chrome_command_line_pref_store.cc
index 7600c47..d82f30cc 100644
--- a/chrome/browser/prefs/chrome_command_line_pref_store.cc
+++ b/chrome/browser/prefs/chrome_command_line_pref_store.cc
@@ -81,12 +81,11 @@
         {safe_browsing::switches::kSbEnableEnhancedProtection,
          prefs::kSafeBrowsingEnhanced, true},
 #if BUILDFLAG(IS_CHROMEOS_ASH)
-        {chromeos::switches::kEnableTouchpadThreeFingerClick,
+        {ash::switches::kEnableTouchpadThreeFingerClick,
          prefs::kEnableTouchpadThreeFingerClick, true},
         {switches::kEnableUnifiedDesktop,
          prefs::kUnifiedDesktopEnabledByDefault, true},
-        {chromeos::switches::kEnableCastReceiver, prefs::kCastReceiverEnabled,
-         true},
+        {ash::switches::kEnableCastReceiver, prefs::kCastReceiverEnabled, true},
 #endif
         {switches::kEnableLocalSyncBackend,
          syncer::prefs::kEnableLocalSyncBackend, true},
diff --git a/chrome/browser/prefs/tracked/pref_hash_browsertest.cc b/chrome/browser/prefs/tracked/pref_hash_browsertest.cc
index d4c383f..b6550e0d 100644
--- a/chrome/browser/prefs/tracked/pref_hash_browsertest.cc
+++ b/chrome/browser/prefs/tracked/pref_hash_browsertest.cc
@@ -208,7 +208,7 @@
     extensions::ExtensionBrowserTest::SetUpCommandLine(command_line);
 #if BUILDFLAG(IS_CHROMEOS_ASH)
     command_line->AppendSwitch(
-        chromeos::switches::kIgnoreUserProfileMappingForTests);
+        ash::switches::kIgnoreUserProfileMappingForTests);
 #endif
   }
 
diff --git a/chrome/browser/profiles/incognito_profile_containment_browsertest.cc b/chrome/browser/profiles/incognito_profile_containment_browsertest.cc
index 60e6331..d29b596 100644
--- a/chrome/browser/profiles/incognito_profile_containment_browsertest.cc
+++ b/chrome/browser/profiles/incognito_profile_containment_browsertest.cc
@@ -248,8 +248,9 @@
     allow_list_.insert(test_folder.begin(), test_folder.end());
 #endif
 
-    allow_list_.insert(std::begin(kAllowListPrefixesForPlatform),
-                       std::end(kAllowListPrefixesForPlatform));
+    for (const char* platform_prefix : kAllowListPrefixesForPlatform) {
+      allow_list_.emplace(platform_prefix);
+    }
   }
 
   void SetUpOnMainThread() override {
diff --git a/chrome/browser/profiles/profile.cc b/chrome/browser/profiles/profile.cc
index 346cf8d1..a3fadab7 100644
--- a/chrome/browser/profiles/profile.cc
+++ b/chrome/browser/profiles/profile.cc
@@ -389,7 +389,7 @@
 #if BUILDFLAG(IS_CHROMEOS_ASH)
   static bool is_guest_session =
       base::CommandLine::ForCurrentProcess()->HasSwitch(
-          chromeos::switches::kGuestSession);
+          ash::switches::kGuestSession);
   return is_guest_session;
 #else
 #if BUILDFLAG(IS_CHROMEOS_LACROS)
diff --git a/chrome/browser/profiles/profile_browsertest.cc b/chrome/browser/profiles/profile_browsertest.cc
index 031ebbc4..02d8af13 100644
--- a/chrome/browser/profiles/profile_browsertest.cc
+++ b/chrome/browser/profiles/profile_browsertest.cc
@@ -269,7 +269,7 @@
   void SetUpCommandLine(base::CommandLine* command_line) override {
 #if BUILDFLAG(IS_CHROMEOS_ASH)
     command_line->AppendSwitch(
-        chromeos::switches::kIgnoreUserProfileMappingForTests);
+        ash::switches::kIgnoreUserProfileMappingForTests);
 #endif
   }
 
diff --git a/chrome/browser/profiles/profile_manager.cc b/chrome/browser/profiles/profile_manager.cc
index 9802db01..c48cc2e 100644
--- a/chrome/browser/profiles/profile_manager.cc
+++ b/chrome/browser/profiles/profile_manager.cc
@@ -713,6 +713,7 @@
   return profile;
 }
 
+#if BUILDFLAG(IS_CHROMEOS_ASH) || defined(OS_ANDROID)
 // static
 Profile* ProfileManager::CreateInitialProfile() {
   ProfileManager* const profile_manager = g_browser_process->profile_manager();
@@ -724,6 +725,7 @@
     return profile->GetPrimaryOTRProfile(/*create_if_needed=*/true);
   return profile;
 }
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH) || defined(OS_ANDROID)
 
 void ProfileManager::AddObserver(ProfileManagerObserver* observer) {
   observers_.AddObserver(observer);
diff --git a/chrome/browser/profiles/profile_manager.h b/chrome/browser/profiles/profile_manager.h
index cd4db5e1..7b5a8c7 100644
--- a/chrome/browser/profiles/profile_manager.h
+++ b/chrome/browser/profiles/profile_manager.h
@@ -119,11 +119,13 @@
   // Note that in case of a guest account this will return a 'suitable' profile.
   static Profile* GetActiveUserProfile();
 
+#if BUILDFLAG(IS_CHROMEOS_ASH) || defined(OS_ANDROID)
   // Load and return the initial profile for browser. On ChromeOS, this returns
   // either the sign-in profile or the active user profile depending on whether
   // browser is started normally or is restarted after crash. On other
   // platforms, this returns the default profile.
   static Profile* CreateInitialProfile();
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH) || defined(OS_ANDROID)
 
   void AddObserver(ProfileManagerObserver* observer);
   void RemoveObserver(ProfileManagerObserver* observer);
diff --git a/chrome/browser/profiles/profile_manager_browsertest.cc b/chrome/browser/profiles/profile_manager_browsertest.cc
index 3fcc4886..1936ab60 100644
--- a/chrome/browser/profiles/profile_manager_browsertest.cc
+++ b/chrome/browser/profiles/profile_manager_browsertest.cc
@@ -262,7 +262,7 @@
     InProcessBrowserTest::SetUpCommandLine(command_line);
 #if BUILDFLAG(IS_CHROMEOS_ASH)
     command_line->AppendSwitch(
-        chromeos::switches::kIgnoreUserProfileMappingForTests);
+        ash::switches::kIgnoreUserProfileMappingForTests);
 #endif
   }
 };
@@ -491,7 +491,7 @@
   void SetUpCommandLine(base::CommandLine* command_line) override {
     // Use a user hash other than the default chrome::kTestUserProfileDir
     // so that the prefix case is tested.
-    command_line->AppendSwitchASCII(chromeos::switches::kLoginProfile,
+    command_line->AppendSwitchASCII(ash::switches::kLoginProfile,
                                     "test-user-hash");
   }
 };
diff --git a/chrome/browser/profiles/profile_manager_unittest.cc b/chrome/browser/profiles/profile_manager_unittest.cc
index 6916c4c..a834768 100644
--- a/chrome/browser/profiles/profile_manager_unittest.cc
+++ b/chrome/browser/profiles/profile_manager_unittest.cc
@@ -873,7 +873,7 @@
   void SetUp() override {
 #if BUILDFLAG(IS_CHROMEOS_ASH)
     base::CommandLine* cl = base::CommandLine::ForCurrentProcess();
-    cl->AppendSwitch(chromeos::switches::kGuestSession);
+    cl->AppendSwitch(ash::switches::kGuestSession);
     cl->AppendSwitch(::switches::kIncognito);
 #endif
 
diff --git a/chrome/browser/resources/bookmarks/toolbar.html b/chrome/browser/resources/bookmarks/toolbar.html
index f5202d93..cbea79d 100644
--- a/chrome/browser/resources/bookmarks/toolbar.html
+++ b/chrome/browser/resources/bookmarks/toolbar.html
@@ -19,6 +19,10 @@
       --cr-icon-button-fill-color: currentColor;
       --cr-icon-button-focus-outline-color: white;
     }
+
+    :host-context([enable-branding-update]) cr-icon-button {
+      --cr-icon-button-focus-outline-color: var(--cr-focus-outline-color);
+    }
   }
 
   :host(:not([narrow_])) cr-toolbar-selection-overlay {
diff --git a/chrome/browser/resources/chromeos/emulator/audio_settings.js b/chrome/browser/resources/chromeos/emulator/audio_settings.js
index f11b9f6..2a8a40e 100644
--- a/chrome/browser/resources/chromeos/emulator/audio_settings.js
+++ b/chrome/browser/resources/chromeos/emulator/audio_settings.js
@@ -14,7 +14,7 @@
 import './icons.js';
 import './shared_styles.js';
 
-import {sendWithPromise} from 'chrome://resources/js/cr.m.js';
+import {addWebUIListener, sendWithPromise} from 'chrome://resources/js/cr.m.js';
 import {html, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 /** @enum {string} */ var AudioNodeType = {
@@ -139,8 +139,8 @@
   },
 
   ready() {
-    sendWithPromise('requestAudioNodes').then(
-        this.updateAudioNodes_.bind(this));
+    addWebUIListener('audioNodesUpdated', this.updateAudioNodes_.bind(this));
+    chrome.send('requestAudioNodes');
   },
 
   /**
diff --git a/chrome/browser/resources/downloads/toolbar.html b/chrome/browser/resources/downloads/toolbar.html
index 398b934..8544186 100644
--- a/chrome/browser/resources/downloads/toolbar.html
+++ b/chrome/browser/resources/downloads/toolbar.html
@@ -19,6 +19,10 @@
       --cr-icon-button-fill-color: currentColor;
       --cr-icon-button-focus-outline-color: white;
     }
+
+    :host-context([enable-branding-update]) cr-icon-button {
+      --cr-icon-button-focus-outline-color: var(--cr-focus-outline-color);
+    }
   }
 </style>
 <cr-toolbar id="toolbar" page-name="$i18n{title}" autofocus always-show-logo
diff --git a/chrome/browser/resources/extensions/BUILD.gn b/chrome/browser/resources/extensions/BUILD.gn
index 6e80a69..39116a4 100644
--- a/chrome/browser/resources/extensions/BUILD.gn
+++ b/chrome/browser/resources/extensions/BUILD.gn
@@ -88,6 +88,7 @@
 ts_library("build_ts") {
   root_dir = "$target_gen_dir/$preprocess_folder"
   out_dir = "$target_gen_dir/tsc"
+  composite = true
   tsconfig_base = "tsconfig_base.json"
   in_files = web_component_files + non_web_component_files
   definitions = [
diff --git a/chrome/browser/resources/extensions/item_mixin.ts b/chrome/browser/resources/extensions/item_mixin.ts
index c51c2d4..31795f3d 100644
--- a/chrome/browser/resources/extensions/item_mixin.ts
+++ b/chrome/browser/resources/extensions/item_mixin.ts
@@ -48,7 +48,7 @@
       return ItemMixin;
     });
 
-interface ItemMixinInterface {
+export interface ItemMixinInterface {
   appOrExtension(
       type: chrome.developerPrivate.ExtensionType, appLabel: string,
       extensionLabel: string): string;
diff --git a/chrome/browser/resources/settings/chromeos/internet_page/internet_detail_page.js b/chrome/browser/resources/settings/chromeos/internet_page/internet_detail_page.js
index 445676b..1c4f14c 100644
--- a/chrome/browser/resources/settings/chromeos/internet_page/internet_detail_page.js
+++ b/chrome/browser/resources/settings/chromeos/internet_page/internet_detail_page.js
@@ -286,6 +286,15 @@
       }
     },
 
+    /** @private {boolean} */
+    isExtendedOpenVpnSettingsEnabled_: {
+      type: Boolean,
+      value() {
+        return loadTimeData.valueExists('extendedOpenVpnSettingsEnabled') &&
+            loadTimeData.getBoolean('extendedOpenVpnSettingsEnabled');
+      }
+    },
+
     /**
      * When true, all inputs that allow state to be changed (e.g., toggles,
      * inputs) are disabled.
diff --git a/chrome/browser/resources/settings/privacy_page/privacy_review/privacy_review_page.ts b/chrome/browser/resources/settings/privacy_page/privacy_review/privacy_review_page.ts
index 02d9d3b5c..5b76312d 100644
--- a/chrome/browser/resources/settings/privacy_page/privacy_review/privacy_review_page.ts
+++ b/chrome/browser/resources/settings/privacy_page/privacy_review/privacy_review_page.ts
@@ -190,7 +190,6 @@
         PrivacyReviewStep.COMPLETION,
         {
           onForwardNavigation: () => {
-            Router.getInstance().navigateToPreviousRoute();
             this.metricsBrowserProxy_.recordPrivacyGuideNextNavigationHistogram(
                 PrivacyGuideInteractions.COMPLETION_NEXT_BUTTON);
             this.metricsBrowserProxy_.recordAction(
diff --git a/chrome/browser/rlz/chrome_rlz_tracker_delegate.cc b/chrome/browser/rlz/chrome_rlz_tracker_delegate.cc
index fcd1416..81e5d6d 100644
--- a/chrome/browser/rlz/chrome_rlz_tracker_delegate.cc
+++ b/chrome/browser/rlz/chrome_rlz_tracker_delegate.cc
@@ -57,14 +57,14 @@
   int rlz_ping_delay_seconds = 90;
 #if BUILDFLAG(IS_CHROMEOS_ASH)
   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
-          chromeos::switches::kRlzPingDelay)) {
+          ash::switches::kRlzPingDelay)) {
     // Use a switch for overwriting the default delay because it doesn't seem
     // possible to manually override the Preferences file on Chrome OS: the file
     // is already loaded into memory by the time you modify it and any changes
     // made get overwritten by Chrome.
     rlz_ping_delay_seconds =
         std::stoi(base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
-            chromeos::switches::kRlzPingDelay));
+            ash::switches::kRlzPingDelay));
   } else {
     rlz_ping_delay_seconds = 24 * 3600;
   }
diff --git a/chrome/browser/safe_browsing/incident_reporting/platform_state_store.cc b/chrome/browser/safe_browsing/incident_reporting/platform_state_store.cc
index 40a1b69b..9fea57e 100644
--- a/chrome/browser/safe_browsing/incident_reporting/platform_state_store.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/platform_state_store.cc
@@ -17,6 +17,7 @@
 #include "chrome/browser/safe_browsing/incident_reporting/platform_state_store.h"
 
 #include "base/values.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 
 #if defined(USE_PLATFORM_STATE_STORE)
 
@@ -127,14 +128,13 @@
 
 #endif  // USE_PLATFORM_STATE_STORE
 
-std::unique_ptr<base::DictionaryValue> Load(Profile* profile) {
+absl::optional<base::Value> Load(Profile* profile) {
 #if defined(USE_PLATFORM_STATE_STORE)
-  std::unique_ptr<base::DictionaryValue> value_dict(
-      new base::DictionaryValue());
+  base::Value value_dict(base::Value::Type::DICTIONARY);
   std::string data;
   PlatformStateStoreLoadResult result = ReadStoreData(profile, &data);
   if (result == PlatformStateStoreLoadResult::SUCCESS)
-    result = DeserializeIncidentsSent(data, value_dict.get());
+    result = DeserializeIncidentsSent(data, &value_dict);
   switch (result) {
     case PlatformStateStoreLoadResult::SUCCESS:
     case PlatformStateStoreLoadResult::CLEARED_DATA:
@@ -145,16 +145,15 @@
     case PlatformStateStoreLoadResult::OPEN_FAILED:
     case PlatformStateStoreLoadResult::READ_FAILED:
     case PlatformStateStoreLoadResult::PARSE_ERROR:
-      // Return null for all error cases.
-      value_dict.reset();
-      break;
+      // Return nullopt for all error cases.
+      return absl::nullopt;
     case PlatformStateStoreLoadResult::NUM_RESULTS:
       NOTREACHED();
       break;
   }
   return value_dict;
 #else
-  return nullptr;
+  return absl::nullopt;
 #endif
 }
 
@@ -177,9 +176,8 @@
   store_data.SerializeToString(data);
 }
 
-PlatformStateStoreLoadResult DeserializeIncidentsSent(
-    const std::string& data,
-    base::DictionaryValue* value_dict) {
+PlatformStateStoreLoadResult DeserializeIncidentsSent(const std::string& data,
+                                                      base::Value* value_dict) {
   StateStoreData store_data;
   if (data.empty()) {
     value_dict->DictClear();
diff --git a/chrome/browser/safe_browsing/incident_reporting/platform_state_store.h b/chrome/browser/safe_browsing/incident_reporting/platform_state_store.h
index f30b054..43d76322 100644
--- a/chrome/browser/safe_browsing/incident_reporting/platform_state_store.h
+++ b/chrome/browser/safe_browsing/incident_reporting/platform_state_store.h
@@ -14,6 +14,7 @@
 #include <string>
 
 #include "build/build_config.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 
 // Certain platforms provide their own storage of protobuf-serialized prune
 // state. On platforms where it is not supported, Load() and Store() are noops.
@@ -26,15 +27,16 @@
 
 namespace base {
 class DictionaryValue;
+class Value;
 }
 
 namespace safe_browsing {
 namespace platform_state_store {
 
-// Loads the platform-specific storage for |profile|. Returns null if there is
-// no such storage for the current platform or in case of error; otherwise, a
-// (possibly empty) dictionary.
-std::unique_ptr<base::DictionaryValue> Load(Profile* profile);
+// Loads the platform-specific storage for |profile|. Returns absl::nullopt if
+// there is no such storage for the current platform or in case of error;
+// otherwise, a (possibly empty) dictionary.
+absl::optional<base::Value> Load(Profile* profile);
 
 // Stores the state for |profile| in |incidents_sent| into platform-specific
 // storage if there is such for the current platform.
@@ -74,9 +76,8 @@
 
 // Deserializes |data| into |value_dict|. Returns SUCCESS if |data| is empty or
 // fully processed. Exposed for testing.
-PlatformStateStoreLoadResult DeserializeIncidentsSent(
-    const std::string& data,
-    base::DictionaryValue* value_dict);
+PlatformStateStoreLoadResult DeserializeIncidentsSent(const std::string& data,
+                                                      base::Value* value_dict);
 
 #endif  // USE_PLATFORM_STATE_STORE
 
diff --git a/chrome/browser/safe_browsing/incident_reporting/state_store.cc b/chrome/browser/safe_browsing/incident_reporting/state_store.cc
index 76cdb2b..2e33570 100644
--- a/chrome/browser/safe_browsing/incident_reporting/state_store.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/state_store.cc
@@ -39,7 +39,7 @@
                                              const std::string& key,
                                              IncidentDigest digest) {
   std::string type_string(base::NumberToString(static_cast<int>(type)));
-  base::DictionaryValue* incidents_sent = GetPrefDict();
+  base::Value* incidents_sent = GetPrefDict();
   base::Value* type_dict =
       incidents_sent->FindKeyOfType(type_string, base::Value::Type::DICTIONARY);
   if (!type_dict) {
@@ -63,8 +63,7 @@
   if (store_->incidents_sent_->GetDictionaryWithoutPathExpansion(
           type_string, &const_type_dict) &&
       const_type_dict->FindKey(key)) {
-    base::DictionaryValue* type_dict = nullptr;
-    GetPrefDict()->GetDictionaryWithoutPathExpansion(type_string, &type_dict);
+    base::Value* type_dict = GetPrefDict()->FindDictKey(type_string);
     type_dict->RemoveKey(key);
   }
 }
@@ -92,24 +91,24 @@
     GetPrefDict()->DictClear();
 }
 
-base::DictionaryValue* StateStore::Transaction::GetPrefDict() {
+base::Value* StateStore::Transaction::GetPrefDict() {
   if (!pref_update_) {
-    pref_update_ = std::make_unique<DictionaryPrefUpdateDeprecated>(
+    pref_update_ = std::make_unique<DictionaryPrefUpdate>(
         store_->profile_->GetPrefs(), prefs::kSafeBrowsingIncidentsSent);
     // Getting the dict will cause it to be created if it doesn't exist.
     // Unconditionally refresh the store's read-only view on the preference so
     // that it will always be correct.
-    store_->incidents_sent_ = pref_update_->Get();
+    store_->incidents_sent_ =
+        static_cast<base::DictionaryValue*>(pref_update_->Get());
   }
   return pref_update_->Get();
 }
 
-void StateStore::Transaction::ReplacePrefDict(
-    std::unique_ptr<base::DictionaryValue> pref_dict) {
-  GetPrefDict()->Swap(pref_dict.get());
+void StateStore::Transaction::ReplacePrefDict(base::Value pref_dict) {
+  DCHECK(pref_dict.is_dict());
+  *GetPrefDict() = std::move(pref_dict);
 }
 
-
 // StateStore ------------------------------------------------------------------
 
 StateStore::StateStore(Profile* profile)
@@ -128,13 +127,12 @@
 
   // Apply the platform data.
   Transaction transaction(this);
-  std::unique_ptr<base::DictionaryValue> value_dict(
-      platform_state_store::Load(profile_));
+  absl::optional<base::Value> value_dict(platform_state_store::Load(profile_));
   if (value_dict) {
     if (value_dict->DictEmpty())
       transaction.ClearAll();
     else if (!incidents_sent_ || *incidents_sent_ != *value_dict)
-      transaction.ReplacePrefDict(std::move(value_dict));
+      transaction.ReplacePrefDict(*std::move(value_dict));
   }
 
   if (incidents_sent_)
diff --git a/chrome/browser/safe_browsing/incident_reporting/state_store.h b/chrome/browser/safe_browsing/incident_reporting/state_store.h
index 239dd92..aba139e 100644
--- a/chrome/browser/safe_browsing/incident_reporting/state_store.h
+++ b/chrome/browser/safe_browsing/incident_reporting/state_store.h
@@ -16,6 +16,11 @@
 
 class Profile;
 
+namespace base {
+class DictionaryValue;
+class Value;
+}  // namespace base
+
 namespace safe_browsing {
 
 enum class IncidentType : int32_t;
@@ -71,17 +76,17 @@
     // obtaining this view will cause a serialize-and-write operation to be
     // scheduled when the transaction terminates. Use the store's
     // |incidents_sent_| member directly to simply query the preference.
-    base::DictionaryValue* GetPrefDict();
+    base::Value* GetPrefDict();
 
     // Replaces the contents of the underlying dictionary value.
-    void ReplacePrefDict(std::unique_ptr<base::DictionaryValue> pref_dict);
+    void ReplacePrefDict(base::Value pref_dict);
 
     // The store corresponding to this transaction.
     raw_ptr<StateStore> store_;
 
     // A ScopedUserPrefUpdate through which changes to the incidents_sent
     // preference are made.
-    std::unique_ptr<DictionaryPrefUpdateDeprecated> pref_update_;
+    std::unique_ptr<DictionaryPrefUpdate> pref_update_;
   };
 
   explicit StateStore(Profile* profile);
diff --git a/chrome/browser/safe_browsing/safe_browsing_service_browsertest.cc b/chrome/browser/safe_browsing/safe_browsing_service_browsertest.cc
index a45dddf..467a27c0 100644
--- a/chrome/browser/safe_browsing/safe_browsing_service_browsertest.cc
+++ b/chrome/browser/safe_browsing/safe_browsing_service_browsertest.cc
@@ -538,7 +538,7 @@
   void SetUpCommandLine(base::CommandLine* command_line) override {
 #if BUILDFLAG(IS_CHROMEOS_ASH)
     command_line->AppendSwitch(
-        chromeos::switches::kIgnoreUserProfileMappingForTests);
+        ash::switches::kIgnoreUserProfileMappingForTests);
 #endif
   }
 
diff --git a/chrome/browser/search/most_visited_iframe_source_unittest.cc b/chrome/browser/search/most_visited_iframe_source_unittest.cc
index 556dd27..0ed57c5 100644
--- a/chrome/browser/search/most_visited_iframe_source_unittest.cc
+++ b/chrome/browser/search/most_visited_iframe_source_unittest.cc
@@ -21,7 +21,6 @@
 #include "net/url_request/url_request_context.h"
 #include "net/url_request/url_request_test_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/blink/public/common/loader/previews_state.h"
 #include "url/gurl.h"
 
 const int kNonInstantRendererPID = 0;
diff --git a/chrome/browser/shell_integration.cc b/chrome/browser/shell_integration.cc
index aa6b876..6d31872 100644
--- a/chrome/browser/shell_integration.cc
+++ b/chrome/browser/shell_integration.cc
@@ -158,10 +158,10 @@
   }
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
-  base::FilePath profile = cmd_line.GetSwitchValuePath(
-      chromeos::switches::kLoginProfile);
+  base::FilePath profile =
+      cmd_line.GetSwitchValuePath(ash::switches::kLoginProfile);
   if (!profile.empty())
-    command_line->AppendSwitchPath(chromeos::switches::kLoginProfile, profile);
+    command_line->AppendSwitchPath(ash::switches::kLoginProfile, profile);
 #else
   if (!profile_path.empty())
     command_line->AppendSwitchPath(switches::kProfileDirectory,
diff --git a/chrome/browser/site_isolation/origin_agent_cluster_browsertest.cc b/chrome/browser/site_isolation/origin_agent_cluster_browsertest.cc
index 7ce6971..94b8bdd 100644
--- a/chrome/browser/site_isolation/origin_agent_cluster_browsertest.cc
+++ b/chrome/browser/site_isolation/origin_agent_cluster_browsertest.cc
@@ -344,7 +344,7 @@
   // Open two a.com tabs (with cross site http iframes). IsolateExtensions mode
   // should have no effect so far, since there are no frames straddling the
   // extension/web boundary.
-  AddTabAtIndex(1, start_url, ui::PAGE_TRANSITION_TYPED);
+  ASSERT_TRUE(AddTabAtIndex(1, start_url, ui::PAGE_TRANSITION_TYPED));
   content::WebContents* tab2 =
       browser()->tab_strip_model()->GetWebContentsAt(1);
   EXPECT_TRUE(NavigateIframeToURL(tab2, "test", origin_keyed_url));
diff --git a/chrome/browser/site_isolation/site_details_browsertest.cc b/chrome/browser/site_isolation/site_details_browsertest.cc
index 085a173d..5661b09 100644
--- a/chrome/browser/site_isolation/site_details_browsertest.cc
+++ b/chrome/browser/site_isolation/site_details_browsertest.cc
@@ -354,7 +354,7 @@
   // Open a second tab (different BrowsingInstance) with 4 sites (a through d).
   GURL abcd_url = embedded_test_server()->GetURL(
       "a.com", "/cross_site_iframe_factory.html?a(b(c(d())))");
-  AddTabAtIndex(1, abcd_url, ui::PAGE_TRANSITION_TYPED);
+  ASSERT_TRUE(AddTabAtIndex(1, abcd_url, ui::PAGE_TRANSITION_TYPED));
 
   details = new TestMemoryDetails();
   details->StartFetchAndWait();
@@ -377,7 +377,7 @@
                         ElementsAre(Bucket(12, 1), Bucket(68, 1))));
 
   // Open a third tab (different BrowsingInstance) with the same 4 sites.
-  AddTabAtIndex(2, abcd_url, ui::PAGE_TRANSITION_TYPED);
+  ASSERT_TRUE(AddTabAtIndex(2, abcd_url, ui::PAGE_TRANSITION_TYPED));
 
   details = new TestMemoryDetails();
   details->StartFetchAndWait();
@@ -467,7 +467,7 @@
   WebContents* tab1 = browser()->tab_strip_model()->GetWebContentsAt(0);
   GURL tab2_url = embedded_test_server()->GetURL(
       "a.com", "/cross_site_iframe_factory.html?a(d,e)");
-  AddTabAtIndex(1, tab2_url, ui::PAGE_TRANSITION_TYPED);
+  ASSERT_TRUE(AddTabAtIndex(1, tab2_url, ui::PAGE_TRANSITION_TYPED));
   WebContents* tab2 = browser()->tab_strip_model()->GetWebContentsAt(1);
 
   details = new TestMemoryDetails();
@@ -733,7 +733,7 @@
   // Open a tab, which will be in a different BrowsingInstance.
   GURL abcd_url = embedded_test_server()->GetURL(
       "a.com", "/cross_site_iframe_factory.html?a(b(c(d())))");
-  AddTabAtIndex(1, abcd_url, ui::PAGE_TRANSITION_TYPED);
+  ASSERT_TRUE(AddTabAtIndex(1, abcd_url, ui::PAGE_TRANSITION_TYPED));
 
   details = new TestMemoryDetails();
   details->StartFetchAndWait();
diff --git a/chrome/browser/ssl/ssl_browsertest.cc b/chrome/browser/ssl/ssl_browsertest.cc
index fc59fa0..4b16138 100644
--- a/chrome/browser/ssl/ssl_browsertest.cc
+++ b/chrome/browser/ssl/ssl_browsertest.cc
@@ -5839,7 +5839,7 @@
     SSLUITestNoCert::SetUpCommandLine(command_line);
     // Don't require policy for our sessions - this is required so the policy
     // code knows not to expect cached policy for the secondary profile.
-    command_line->AppendSwitchASCII(chromeos::switches::kProfileRequiresPolicy,
+    command_line->AppendSwitchASCII(ash::switches::kProfileRequiresPolicy,
                                     "false");
   }
 
diff --git a/chrome/browser/sync/chrome_sync_client.cc b/chrome/browser/sync/chrome_sync_client.cc
index 78b5a47..17b46945 100644
--- a/chrome/browser/sync/chrome_sync_client.cc
+++ b/chrome/browser/sync/chrome_sync_client.cc
@@ -442,7 +442,7 @@
   // Temporarily Disable AppListSyncableService for tablet form factor devices.
   // See crbug/1013732 for details.
   if (app_list::AppListSyncableServiceFactory::GetForProfile(profile_) &&
-      !chromeos::switches::IsTabletFormFactor()) {
+      !ash::switches::IsTabletFormFactor()) {
     if (chromeos::features::IsSyncSettingsCategorizationEnabled()) {
       // Runs in sync transport-mode and full-sync mode.
       controllers.push_back(
diff --git a/chrome/browser/sync/test/integration/sync_test.cc b/chrome/browser/sync/test/integration/sync_test.cc
index 2a624e6e..809ce1e7 100644
--- a/chrome/browser/sync/test/integration/sync_test.cc
+++ b/chrome/browser/sync/test/integration/sync_test.cc
@@ -373,8 +373,8 @@
   AddTestSwitches(cl);
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
-  cl->AppendSwitch(chromeos::switches::kIgnoreUserProfileMappingForTests);
-  cl->AppendSwitch(chromeos::switches::kDisableArcOptInVerification);
+  cl->AppendSwitch(ash::switches::kIgnoreUserProfileMappingForTests);
+  cl->AppendSwitch(ash::switches::kDisableArcOptInVerification);
   arc::SetArcAvailableCommandLineForTesting(cl);
 #endif
 }
diff --git a/chrome/browser/task_manager/providers/web_contents/tab_contents_tag_browsertest.cc b/chrome/browser/task_manager/providers/web_contents/tab_contents_tag_browsertest.cc
index dc498db..a51286c4 100644
--- a/chrome/browser/task_manager/providers/web_contents/tab_contents_tag_browsertest.cc
+++ b/chrome/browser/task_manager/providers/web_contents/tab_contents_tag_browsertest.cc
@@ -139,7 +139,7 @@
   void AddNewTestTabAt(int index, const char* test_page_file) {
     int tabs_count_before = tabs_count();
     GURL url = GetUrlOfFile(test_page_file);
-    AddTabAtIndex(index, url, ui::PAGE_TRANSITION_TYPED);
+    ASSERT_TRUE(AddTabAtIndex(index, url, ui::PAGE_TRANSITION_TYPED));
     EXPECT_EQ(++tabs_count_before, tabs_count());
   }
 
diff --git a/chrome/browser/task_manager/providers/worker_task_provider_browsertest.cc b/chrome/browser/task_manager/providers/worker_task_provider_browsertest.cc
index 99f2343..ea7850c 100644
--- a/chrome/browser/task_manager/providers/worker_task_provider_browsertest.cc
+++ b/chrome/browser/task_manager/providers/worker_task_provider_browsertest.cc
@@ -145,7 +145,7 @@
   void SetUpCommandLine(base::CommandLine* command_line) override {
 #if BUILDFLAG(IS_CHROMEOS_ASH)
     command_line->AppendSwitch(
-        chromeos::switches::kIgnoreUserProfileMappingForTests);
+        ash::switches::kIgnoreUserProfileMappingForTests);
 #endif
   }
 
diff --git a/chrome/browser/task_manager/task_manager_browsertest.cc b/chrome/browser/task_manager/task_manager_browsertest.cc
index 73df8396..f7c7c48 100644
--- a/chrome/browser/task_manager/task_manager_browsertest.cc
+++ b/chrome/browser/task_manager/task_manager_browsertest.cc
@@ -224,7 +224,7 @@
   ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(0, MatchTab("title1.html")));
 
   // Open a new tab and make sure the task manager notices it.
-  AddTabAtIndex(0, GetTestURL(), ui::PAGE_TRANSITION_TYPED);
+  ASSERT_TRUE(AddTabAtIndex(0, GetTestURL(), ui::PAGE_TRANSITION_TYPED));
 
   ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchTab("title1.html")));
   ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(2, MatchAnyTab()));
@@ -243,7 +243,7 @@
   ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(0, MatchTab("title1.html")));
 
   // Open a new tab and make sure the task manager notices it.
-  AddTabAtIndex(0, GetTestURL(), ui::PAGE_TRANSITION_TYPED);
+  ASSERT_TRUE(AddTabAtIndex(0, GetTestURL(), ui::PAGE_TRANSITION_TYPED));
 
   ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchTab("title1.html")));
   ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(2, MatchAnyTab()));
@@ -276,7 +276,7 @@
   GURL url3(embedded_test_server()->GetURL("a.com", "/iframe.html"));
 
   // Open a new tab and make sure the task manager notices it.
-  AddTabAtIndex(0, url1, ui::PAGE_TRANSITION_TYPED);
+  ASSERT_TRUE(AddTabAtIndex(0, url1, ui::PAGE_TRANSITION_TYPED));
   ASSERT_NO_FATAL_FAILURE(
       WaitForTaskManagerRows(1, MatchTab("Title Of Awesomeness")));
   ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAboutBlankTab()));
@@ -341,7 +341,7 @@
   // The fourth entry (page.html) is also of type extension and has both a
   // WebContents and an extension. The title should start with "Extension:".
   GURL url("chrome-extension://behllobkkfkfnphdnhnkndlbkcpglgmj/page.html");
-  AddTabAtIndex(0, url, ui::PAGE_TRANSITION_TYPED);
+  ASSERT_TRUE(AddTabAtIndex(0, url, ui::PAGE_TRANSITION_TYPED));
   ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchExtension("Foobar")));
   ASSERT_NO_FATAL_FAILURE(
       WaitForTaskManagerRows(1, MatchExtension("My extension 1")));
@@ -369,7 +369,7 @@
                                 .AppendASCII("behllobkkfkfnphdnhnkndlbkcpglgmj")
                                 .AppendASCII("1.0.0.0")));
   GURL url("chrome-extension://behllobkkfkfnphdnhnkndlbkcpglgmj/page.html");
-  AddTabAtIndex(0, url, ui::PAGE_TRANSITION_TYPED);
+  ASSERT_TRUE(AddTabAtIndex(0, url, ui::PAGE_TRANSITION_TYPED));
 
   ShowTaskManager();
   ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchExtension("Foobar")));
@@ -403,7 +403,7 @@
 
   // Open a new tab to the app's launch URL and make sure we notice that.
   GURL url(extension->GetResourceURL("main.html"));
-  AddTabAtIndex(0, url, ui::PAGE_TRANSITION_TYPED);
+  ASSERT_TRUE(AddTabAtIndex(0, url, ui::PAGE_TRANSITION_TYPED));
 
   // There should be 1 "App: " tab and the original new tab page.
   ASSERT_NO_FATAL_FAILURE(
@@ -439,7 +439,7 @@
 
   // Open a new tab to the app's launch URL and make sure we notice that.
   GURL url(extension->GetResourceURL("main.html"));
-  AddTabAtIndex(0, url, ui::PAGE_TRANSITION_TYPED);
+  ASSERT_TRUE(AddTabAtIndex(0, url, ui::PAGE_TRANSITION_TYPED));
 
   ShowTaskManager();
 
diff --git a/chrome/browser/ui/app_list/app_list_client_impl_browsertest.cc b/chrome/browser/ui/app_list/app_list_client_impl_browsertest.cc
index da42c16..2a75b02 100644
--- a/chrome/browser/ui/app_list/app_list_client_impl_browsertest.cc
+++ b/chrome/browser/ui/app_list/app_list_client_impl_browsertest.cc
@@ -576,10 +576,10 @@
 
 void AppListClientGuestModeBrowserTest::SetUpCommandLine(
     base::CommandLine* command_line) {
-  command_line->AppendSwitch(chromeos::switches::kGuestSession);
-  command_line->AppendSwitchASCII(chromeos::switches::kLoginUser,
+  command_line->AppendSwitch(ash::switches::kGuestSession);
+  command_line->AppendSwitchASCII(ash::switches::kLoginUser,
                                   user_manager::kGuestUserName);
-  command_line->AppendSwitchASCII(chromeos::switches::kLoginProfile,
+  command_line->AppendSwitchASCII(ash::switches::kLoginProfile,
                                   TestingProfile::kTestUserProfileDir);
   command_line->AppendSwitch(switches::kIncognito);
 }
diff --git a/chrome/browser/ui/app_list/app_list_syncable_service.cc b/chrome/browser/ui/app_list/app_list_syncable_service.cc
index 5589e69..b215b4e 100644
--- a/chrome/browser/ui/app_list/app_list_syncable_service.cc
+++ b/chrome/browser/ui/app_list/app_list_syncable_service.cc
@@ -531,7 +531,7 @@
 
   // Install default page brakes for tablet form factor devices here as
   // these devices do not have app list sync turned on.
-  if (chromeos::switches::IsTabletFormFactor() && profile_->IsNewProfile()) {
+  if (ash::switches::IsTabletFormFactor() && profile_->IsNewProfile()) {
     DCHECK(
         !SyncServiceFactory::GetForProfile(profile_)->GetActiveDataTypes().Has(
             syncer::APP_LIST));
diff --git a/chrome/browser/ui/app_list/arc/arc_app_list_prefs.cc b/chrome/browser/ui/app_list/arc/arc_app_list_prefs.cc
index 9ccafab..4b625c3 100644
--- a/chrome/browser/ui/app_list/arc/arc_app_list_prefs.cc
+++ b/chrome/browser/ui/app_list/arc/arc_app_list_prefs.cc
@@ -978,7 +978,7 @@
   // Match this requirement and don't show pre-installed apps for managed users
   // in app list.
   if (arc::policy_util::IsAccountManaged(profile_)) {
-    if (profile_->IsChild() || chromeos::switches::IsTabletFormFactor()) {
+    if (profile_->IsChild() || ash::switches::IsTabletFormFactor()) {
       // For child accounts, filter only optional apps.
       // For tablet form factor devices, filter only optional apps.
       default_apps_->set_filter_level(
diff --git a/chrome/browser/ui/app_list/arc/arc_app_unittest.cc b/chrome/browser/ui/app_list/arc/arc_app_unittest.cc
index f532bb3..90af152 100644
--- a/chrome/browser/ui/app_list/arc/arc_app_unittest.cc
+++ b/chrome/browser/ui/app_list/arc/arc_app_unittest.cc
@@ -1887,7 +1887,7 @@
 
   base::test::ScopedCommandLine command_line;
   command_line.GetProcessCommandLine()->AppendSwitch(
-      chromeos::switches::kArcGeneratePlayAutoInstall);
+      ash::switches::kArcGeneratePlayAutoInstall);
 
   const std::string app_id = ArcAppTest::GetAppId(fake_apps()[0]);
 
@@ -2358,7 +2358,7 @@
 
   base::test::ScopedCommandLine command_line;
   command_line.GetProcessCommandLine()->AppendSwitch(
-      chromeos::switches::kArcDisablePlayAutoInstall);
+      ash::switches::kArcDisablePlayAutoInstall);
 
   arc::ArcSessionManager* session_manager = arc::ArcSessionManager::Get();
   ASSERT_TRUE(session_manager);
@@ -3309,7 +3309,7 @@
 TEST_P(ArcAppModelBuilderTest, PackageSyncableServiceDisabled) {
   base::test::ScopedCommandLine command_line;
   command_line.GetProcessCommandLine()->AppendSwitch(
-      chromeos::switches::kArcDisableAppSync);
+      ash::switches::kArcDisableAppSync);
   EXPECT_FALSE(
       SyncServiceFactory::GetAsSyncServiceImplForProfile(profile_.get())
           ->GetRegisteredDataTypesForTest()
diff --git a/chrome/browser/ui/ash/assistant/assistant_browser_delegate_impl.cc b/chrome/browser/ui/ash/assistant/assistant_browser_delegate_impl.cc
index c6e66115..7fbaffa 100644
--- a/chrome/browser/ui/ash/assistant/assistant_browser_delegate_impl.cc
+++ b/chrome/browser/ui/ash/assistant/assistant_browser_delegate_impl.cc
@@ -191,7 +191,7 @@
   // Disable the handling for browser tests to prevent the Assistant being
   // enabled unexpectedly.
   base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
-  if (is_primary_user && !chromeos::switches::ShouldSkipOobePostLogin() &&
+  if (is_primary_user && !ash::switches::ShouldSkipOobePostLogin() &&
       !command_line->HasSwitch(switches::kBrowserTest)) {
     MaybeStartAssistantOptInFlow();
   }
diff --git a/chrome/browser/ui/ash/default_pinned_apps.cc b/chrome/browser/ui/ash/default_pinned_apps.cc
index 125bb2df..224f3b1 100644
--- a/chrome/browser/ui/ash/default_pinned_apps.cc
+++ b/chrome/browser/ui/ash/default_pinned_apps.cc
@@ -61,7 +61,7 @@
 }  // namespace
 
 base::span<StaticAppId> GetDefaultPinnedAppsForFormFactor() {
-  if (chromeos::switches::IsTabletFormFactor()) {
+  if (ash::switches::IsTabletFormFactor()) {
     return GetTabletFormFactorDefaultPinnedApps();
   }
 
diff --git a/chrome/browser/ui/ash/shelf/chrome_shelf_prefs.cc b/chrome/browser/ui/ash/shelf/chrome_shelf_prefs.cc
index 4070bae..72105c7 100644
--- a/chrome/browser/ui/ash/shelf/chrome_shelf_prefs.cc
+++ b/chrome/browser/ui/ash/shelf/chrome_shelf_prefs.cc
@@ -113,7 +113,7 @@
 // This is required because tablet form factor devices do not sync app
 // positions and pin preferences.
 const std::string GetShelfDefaultPinLayoutPref() {
-  if (chromeos::switches::IsTabletFormFactor())
+  if (ash::switches::IsTabletFormFactor())
     return prefs::kShelfDefaultPinLayoutRollsForTabletFormFactor;
 
   return prefs::kShelfDefaultPinLayoutRolls;
@@ -130,7 +130,7 @@
     return true;
 
   // Tablet form-factor devices do not have position sync.
-  if (chromeos::switches::IsTabletFormFactor())
+  if (ash::switches::IsTabletFormFactor())
     return true;
 
   const syncer::SyncUserSettings* settings = sync_service->GetUserSettings();
diff --git a/chrome/browser/ui/ash/shelf_browsertest.cc b/chrome/browser/ui/ash/shelf_browsertest.cc
index 31b0d202..d965eb5 100644
--- a/chrome/browser/ui/ash/shelf_browsertest.cc
+++ b/chrome/browser/ui/ash/shelf_browsertest.cc
@@ -19,11 +19,11 @@
 class ShelfGuestSessionBrowserTest : public InProcessBrowserTest {
  protected:
   void SetUpCommandLine(base::CommandLine* command_line) override {
-    command_line->AppendSwitch(chromeos::switches::kGuestSession);
+    command_line->AppendSwitch(ash::switches::kGuestSession);
     command_line->AppendSwitch(::switches::kIncognito);
-    command_line->AppendSwitchASCII(chromeos::switches::kLoginProfile, "hash");
+    command_line->AppendSwitchASCII(ash::switches::kLoginProfile, "hash");
     command_line->AppendSwitchASCII(
-        chromeos::switches::kLoginUser,
+        ash::switches::kLoginUser,
         user_manager::GuestAccountId().GetUserEmail());
   }
 };
diff --git a/chrome/browser/ui/ash/volume_controller_browsertest.cc b/chrome/browser/ui/ash/volume_controller_browsertest.cc
index 6dc8b26..592816c 100644
--- a/chrome/browser/ui/ash/volume_controller_browsertest.cc
+++ b/chrome/browser/ui/ash/volume_controller_browsertest.cc
@@ -244,7 +244,7 @@
 
   void SetUpCommandLine(base::CommandLine* command_line) override {
     VolumeControllerSoundsTest::SetUpCommandLine(command_line);
-    command_line->AppendSwitch(chromeos::switches::kDisableVolumeAdjustSound);
+    command_line->AppendSwitch(ash::switches::kDisableVolumeAdjustSound);
   }
 };
 
diff --git a/chrome/browser/ui/ash/wallpaper_controller_client_impl.cc b/chrome/browser/ui/ash/wallpaper_controller_client_impl.cc
index 6882e09..b22c307d 100644
--- a/chrome/browser/ui/ash/wallpaper_controller_client_impl.cc
+++ b/chrome/browser/ui/ash/wallpaper_controller_client_impl.cc
@@ -304,7 +304,7 @@
 
   // Guest wallpaper should be initialized when guest logs in.
   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
-          chromeos::switches::kGuestSession)) {
+          ash::switches::kGuestSession)) {
     return;
   }
 
diff --git a/chrome/browser/ui/browser_command_controller_browsertest.cc b/chrome/browser/ui/browser_command_controller_browsertest.cc
index 558a84b..d187d95 100644
--- a/chrome/browser/ui/browser_command_controller_browsertest.cc
+++ b/chrome/browser/ui/browser_command_controller_browsertest.cc
@@ -54,7 +54,7 @@
   void SetUpCommandLine(base::CommandLine* command_line) override {
 #if BUILDFLAG(IS_CHROMEOS_ASH)
     command_line->AppendSwitch(
-        chromeos::switches::kIgnoreUserProfileMappingForTests);
+        ash::switches::kIgnoreUserProfileMappingForTests);
 #endif
   }
 };
diff --git a/chrome/browser/ui/browser_navigator_browsertest_chromeos.cc b/chrome/browser/ui/browser_navigator_browsertest_chromeos.cc
index f71869a4..2bf5028 100644
--- a/chrome/browser/ui/browser_navigator_browsertest_chromeos.cc
+++ b/chrome/browser/ui/browser_navigator_browsertest_chromeos.cc
@@ -97,9 +97,8 @@
  protected:
   void SetUpCommandLine(base::CommandLine* command_line) override {
     base::CommandLine command_line_copy = *command_line;
-    command_line_copy.AppendSwitchASCII(chromeos::switches::kLoginProfile,
-                                        "user");
-    command_line_copy.AppendSwitch(chromeos::switches::kGuestSession);
+    command_line_copy.AppendSwitchASCII(ash::switches::kLoginProfile, "user");
+    command_line_copy.AppendSwitch(ash::switches::kGuestSession);
     ash::GetOffTheRecordCommandLine(GetGoogleURL(), command_line_copy,
                                     command_line);
   }
diff --git a/chrome/browser/ui/cocoa/applescript/scripting.sdef b/chrome/browser/ui/cocoa/applescript/scripting.sdef
index f16763b..3117c332 100644
--- a/chrome/browser/ui/cocoa/applescript/scripting.sdef
+++ b/chrome/browser/ui/cocoa/applescript/scripting.sdef
@@ -32,6 +32,11 @@
       <element description="The tabs contained within the window." type="tab">
         <cocoa key="tabs"/>
       </element>
+      <!-- "given name" is the user title as set by Window: Name Window... -->
+      <property name="given name" code="GNam" description="The given name of the window." type="text">
+        <cocoa key="givenName"/>
+      </property>
+      <!-- "name" is an alias for "title" -->
       <property name="name" code="pnam" description="The full title of the window." type="text" access="r">
         <cocoa key="title"/>
       </property>
diff --git a/chrome/browser/ui/cocoa/applescript/window_applescript.h b/chrome/browser/ui/cocoa/applescript/window_applescript.h
index e6b74ec73..d4a6b99 100644
--- a/chrome/browser/ui/cocoa/applescript/window_applescript.h
+++ b/chrome/browser/ui/cocoa/applescript/window_applescript.h
@@ -32,6 +32,10 @@
 - (NSNumber*)activeTabIndex;
 - (void)setActiveTabIndex:(NSNumber*)anActiveTabIndex;
 
+// Sets and get the given name of a window.
+- (NSString*)givenName;
+- (void)setGivenName:(NSString*)name;
+
 // Mode refers to whether a window is a normal window or an incognito window
 // it can be set only once while creating the window.
 - (NSString*)mode;
@@ -59,7 +63,8 @@
 // before calling directly.
 - (void)removeFromTabsAtIndex:(int)index;
 
-// Set the index of a window.
+// The index of the window, windows are ordered front to back.
+- (NSNumber*)orderedIndex;
 - (void)setOrderedIndex:(NSNumber*)anIndex;
 
 // Used to sort windows by index.
@@ -73,9 +78,6 @@
 // Used to close window.
 - (void)handlesCloseScriptCommand:(NSCloseCommand*)command;
 
-// The index of the window, windows are ordered front to back.
-- (NSNumber*)orderedIndex;
-
 @end
 
 #endif  // CHROME_BROWSER_UI_COCOA_APPLESCRIPT_WINDOW_APPLESCRIPT_H_
diff --git a/chrome/browser/ui/cocoa/applescript/window_applescript.mm b/chrome/browser/ui/cocoa/applescript/window_applescript.mm
index bee2a45..169ac04 100644
--- a/chrome/browser/ui/cocoa/applescript/window_applescript.mm
+++ b/chrome/browser/ui/cocoa/applescript/window_applescript.mm
@@ -9,6 +9,7 @@
 #import "base/mac/foundation_util.h"
 #import "base/mac/scoped_nsobject.h"
 #include "base/notreached.h"
+#include "base/strings/sys_string_conversions.h"
 #include "base/time/time.h"
 #import "chrome/browser/app_controller_mac.h"
 #import "chrome/browser/chrome_browser_application_mac.h"
@@ -137,6 +138,14 @@
     AppleScript::SetError(AppleScript::errInvalidTabIndex);
 }
 
+- (NSString*)givenName {
+  return base::SysUTF8ToNSString(_browser->user_title());
+}
+
+- (void)setGivenName:(NSString*)name {
+  _browser->SetWindowUserTitle(base::SysNSStringToUTF8(name));
+}
+
 - (NSString*)mode {
   Profile* profile = _browser->profile();
   if (profile->IsOffTheRecord())
@@ -255,7 +264,7 @@
 }
 
 - (void)setValue:(id)value forUndefinedKey:(NSString*)key {
-  [[self nativeHandle] setValue:(id)value forKey:key];
+  [[self nativeHandle] setValue:value forKey:key];
 }
 
 - (void)handlesCloseScriptCommand:(NSCloseCommand*)command {
diff --git a/chrome/browser/ui/search_engines/keyword_editor_controller.cc b/chrome/browser/ui/search_engines/keyword_editor_controller.cc
index a6aa317..20b273b5 100644
--- a/chrome/browser/ui/search_engines/keyword_editor_controller.cc
+++ b/chrome/browser/ui/search_engines/keyword_editor_controller.cc
@@ -68,8 +68,7 @@
 
 bool KeywordEditorController::CanRemove(const TemplateURL* url) const {
   return (url->type() == TemplateURL::NORMAL) &&
-         (url != url_model_->GetDefaultSearchProvider()) &&
-         (url->prepopulate_id() == 0);
+         (url != url_model_->GetDefaultSearchProvider());
 }
 
 bool KeywordEditorController::CanActivate(const TemplateURL* url) const {
diff --git a/chrome/browser/ui/search_engines/keyword_editor_controller.h b/chrome/browser/ui/search_engines/keyword_editor_controller.h
index 3092b58..582e7bf 100644
--- a/chrome/browser/ui/search_engines/keyword_editor_controller.h
+++ b/chrome/browser/ui/search_engines/keyword_editor_controller.h
@@ -44,9 +44,9 @@
   // Return true if the given |url| can be made the default.
   bool CanMakeDefault(const TemplateURL* url) const;
 
-  // Return true if the given |url| can be removed. A `url` can be removed if it
-  // is a normal entry (non-extension) and is not a prepopulated or default
-  // search engine.
+  // Return true if the given `url` can be removed. A `url` can be removed if it
+  // is a normal entry (non-extension) and is not the current default search
+  // engine.
   bool CanRemove(const TemplateURL* url) const;
 
   // Return true if the given `url` can be activated. A `url` can be activated
@@ -54,8 +54,8 @@
   bool CanActivate(const TemplateURL* url) const;
 
   // Return true if the given `url` can be deactivated. A `url` can be
-  // deactivated if it is currently active and is not a prepopulated or default
-  // search engine.
+  // deactivated if it is currently active and is not a prepopulated engine or
+  // the current default search engine.
   bool CanDeactivate(const TemplateURL* url) const;
 
   // Remove the TemplateURL at the specified index in the TableModel.
diff --git a/chrome/browser/ui/startup/startup_browser_creator.cc b/chrome/browser/ui/startup/startup_browser_creator.cc
index 7998041..34723e3 100644
--- a/chrome/browser/ui/startup/startup_browser_creator.cc
+++ b/chrome/browser/ui/startup/startup_browser_creator.cc
@@ -871,7 +871,7 @@
 #if BUILDFLAG(IS_CHROMEOS_ASH)
 
   // The browser will be launched after the user logs in.
-  if (command_line.HasSwitch(chromeos::switches::kLoginManager))
+  if (command_line.HasSwitch(ash::switches::kLoginManager))
     silent_launch = true;
 
   if (chrome::IsRunningInForcedAppMode()) {
diff --git a/chrome/browser/ui/views/location_bar/zoom_bubble_view_browsertest.cc b/chrome/browser/ui/views/location_bar/zoom_bubble_view_browsertest.cc
index c2d2563..5226ef0 100644
--- a/chrome/browser/ui/views/location_bar/zoom_bubble_view_browsertest.cc
+++ b/chrome/browser/ui/views/location_bar/zoom_bubble_view_browsertest.cc
@@ -254,7 +254,8 @@
 
 // Ensure a tab switch closes the bubble.
 IN_PROC_BROWSER_TEST_F(ZoomBubbleBrowserTest, TabSwitchCloses) {
-  AddTabAtIndex(0, GURL(url::kAboutBlankURL), ui::PAGE_TRANSITION_LINK);
+  ASSERT_TRUE(
+      AddTabAtIndex(0, GURL(url::kAboutBlankURL), ui::PAGE_TRANSITION_LINK));
   ShowInActiveTab(browser());
   chrome::SelectNextTab(browser());
   EXPECT_FALSE(ZoomBubbleView::GetZoomBubble());
@@ -263,7 +264,8 @@
 // Ensure the bubble is dismissed on tab closure and doesn't reference a
 // destroyed WebContents.
 IN_PROC_BROWSER_TEST_F(ZoomBubbleBrowserTest, DestroyedWebContents) {
-  AddTabAtIndex(0, GURL(url::kAboutBlankURL), ui::PAGE_TRANSITION_LINK);
+  ASSERT_TRUE(
+      AddTabAtIndex(0, GURL(url::kAboutBlankURL), ui::PAGE_TRANSITION_LINK));
   ShowInActiveTab(browser());
 
   ZoomBubbleView* bubble = ZoomBubbleView::GetZoomBubble();
diff --git a/chrome/browser/ui/views/payments/contact_info_editor_view_controller_browsertest.cc b/chrome/browser/ui/views/payments/contact_info_editor_view_controller_browsertest.cc
index bfa677e2..b4c3352 100644
--- a/chrome/browser/ui/views/payments/contact_info_editor_view_controller_browsertest.cc
+++ b/chrome/browser/ui/views/payments/contact_info_editor_view_controller_browsertest.cc
@@ -6,12 +6,14 @@
 
 #include "base/run_loop.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/test/scoped_feature_list.h"
 #include "build/build_config.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/ui/views/payments/payment_request_browsertest_base.h"
 #include "chrome/browser/ui/views/payments/payment_request_dialog_view_ids.h"
 #include "components/autofill/core/browser/autofill_test_utils.h"
 #include "components/autofill/core/browser/personal_data_manager.h"
+#include "content/public/common/content_features.h"
 #include "content/public/test/browser_test.h"
 
 namespace payments {
@@ -42,9 +44,14 @@
 class MAYBE_PaymentRequestContactInfoEditorTest
     : public PaymentRequestBrowserTestBase {
  protected:
-  MAYBE_PaymentRequestContactInfoEditorTest() {}
+  MAYBE_PaymentRequestContactInfoEditorTest() {
+    feature_list_.InitAndEnableFeature(::features::kPaymentRequestBasicCard);
+  }
 
   PersonalDataLoadedObserverMock personal_data_observer_;
+
+ private:
+  base::test::ScopedFeatureList feature_list_;
 };
 
 IN_PROC_BROWSER_TEST_F(MAYBE_PaymentRequestContactInfoEditorTest, HappyPath) {
@@ -420,4 +427,548 @@
                                        autofill::PHONE_HOME_WHOLE_NUMBER));
 }
 
+#if BUILDFLAG(IS_MAC)
+// Entire test suite is flaky on MacOS: https://crbug.com/1164438
+#define MAYBE_PaymentRequestContactInfoEditorBasicCardDisabledTest \
+  DISABLED_PaymentRequestContactInfoEditorBasicCardDisabledTest
+#else
+#define MAYBE_PaymentRequestContactInfoEditorBasicCardDisabledTest \
+  PaymentRequestContactInfoEditorBasicCardDisabledTest
+#endif
+
+// The tests in this class correspond to the tests of the same name in
+// PaymentRequestContactInfoEditorTest, with basic-card disabled.
+// Parameterized tests are not used because the test setup for both tests are
+// too different.
+class MAYBE_PaymentRequestContactInfoEditorBasicCardDisabledTest
+    : public PaymentRequestBrowserTestBase {
+ protected:
+  MAYBE_PaymentRequestContactInfoEditorBasicCardDisabledTest() {
+    feature_list_.InitAndDisableFeature(::features::kPaymentRequestBasicCard);
+  }
+
+  PersonalDataLoadedObserverMock personal_data_observer_;
+
+ private:
+  base::test::ScopedFeatureList feature_list_;
+};
+
+IN_PROC_BROWSER_TEST_F(
+    MAYBE_PaymentRequestContactInfoEditorBasicCardDisabledTest,
+    HappyPath) {
+  // Installs two apps so that the Payment Request UI will be shown.
+  std::string a_method_name;
+  InstallPaymentApp("a.com", "payment_request_success_responder.js",
+                    &a_method_name);
+  std::string b_method_name;
+  InstallPaymentApp("b.com", "payment_request_success_responder.js",
+                    &b_method_name);
+
+  NavigateTo("/payment_request_contact_details_test.html");
+
+  // Show a Payment Request.
+  InvokePaymentRequestUIWithJs(
+      content::JsReplace("buyWithMethods([{supportedMethods:$1}"
+                         ", {supportedMethods:$2}]);",
+                         a_method_name, b_method_name));
+
+  OpenContactInfoEditorScreen();
+
+  SetEditorTextfieldValue(kNameFull, autofill::NAME_FULL);
+  SetEditorTextfieldValue(kPhoneNumber, autofill::PHONE_HOME_WHOLE_NUMBER);
+  SetEditorTextfieldValue(kEmailAddress, autofill::EMAIL_ADDRESS);
+
+  autofill::PersonalDataManager* personal_data_manager = GetDataManager();
+  personal_data_manager->AddObserver(&personal_data_observer_);
+
+  // Wait until the web database has been updated and the notification sent.
+  base::RunLoop data_loop;
+  EXPECT_CALL(personal_data_observer_, OnPersonalDataChanged())
+      .WillOnce(QuitMessageLoop(&data_loop));
+  ClickOnDialogViewAndWait(DialogViewID::EDITOR_SAVE_BUTTON);
+  data_loop.Run();
+
+  personal_data_manager->RemoveObserver(&personal_data_observer_);
+  ASSERT_EQ(1UL, personal_data_manager->GetProfiles().size());
+  autofill::AutofillProfile* profile = personal_data_manager->GetProfiles()[0];
+  DCHECK(profile);
+
+  EXPECT_EQ(kNameFull,
+            profile->GetInfo(autofill::AutofillType(autofill::NAME_FULL),
+                             GetLocale()));
+  EXPECT_EQ(u"16515558946",
+            profile->GetInfo(
+                autofill::AutofillType(autofill::PHONE_HOME_WHOLE_NUMBER),
+                GetLocale()));
+  EXPECT_EQ(kEmailAddress,
+            profile->GetInfo(autofill::AutofillType(autofill::EMAIL_ADDRESS),
+                             GetLocale()));
+
+  PaymentRequest* request = GetPaymentRequests().front();
+  EXPECT_EQ(1U, request->state()->contact_profiles().size());
+  EXPECT_EQ(request->state()->contact_profiles().back(),
+            request->state()->selected_contact_profile());
+}
+
+IN_PROC_BROWSER_TEST_F(
+    MAYBE_PaymentRequestContactInfoEditorBasicCardDisabledTest,
+    EnterAcceleratorHappyPath) {
+  // Installs two apps so that the Payment Request UI will be shown.
+  std::string a_method_name;
+  InstallPaymentApp("a.com", "payment_request_success_responder.js",
+                    &a_method_name);
+  std::string b_method_name;
+  InstallPaymentApp("b.com", "payment_request_success_responder.js",
+                    &b_method_name);
+
+  NavigateTo("/payment_request_contact_details_test.html");
+
+  // Show a Payment Request.
+  InvokePaymentRequestUIWithJs(
+      content::JsReplace("buyWithMethods([{supportedMethods:$1}"
+                         ", {supportedMethods:$2}]);",
+                         a_method_name, b_method_name));
+
+  OpenContactInfoEditorScreen();
+
+  SetEditorTextfieldValue(kNameFull, autofill::NAME_FULL);
+  SetEditorTextfieldValue(kPhoneNumber, autofill::PHONE_HOME_WHOLE_NUMBER);
+  SetEditorTextfieldValue(kEmailAddress, autofill::EMAIL_ADDRESS);
+
+  autofill::PersonalDataManager* personal_data_manager = GetDataManager();
+  personal_data_manager->AddObserver(&personal_data_observer_);
+
+  // Wait until the web database has been updated and the notification sent.
+  base::RunLoop data_loop;
+  EXPECT_CALL(personal_data_observer_, OnPersonalDataChanged())
+      .WillOnce(QuitMessageLoop(&data_loop));
+  views::View* editor_sheet = dialog_view()->GetViewByID(
+      static_cast<int>(DialogViewID::CONTACT_INFO_EDITOR_SHEET));
+  editor_sheet->AcceleratorPressed(
+      ui::Accelerator(ui::VKEY_RETURN, ui::EF_NONE));
+  data_loop.Run();
+
+  personal_data_manager->RemoveObserver(&personal_data_observer_);
+  ASSERT_EQ(1UL, personal_data_manager->GetProfiles().size());
+  autofill::AutofillProfile* profile = personal_data_manager->GetProfiles()[0];
+  DCHECK(profile);
+
+  EXPECT_EQ(kNameFull,
+            profile->GetInfo(autofill::AutofillType(autofill::NAME_FULL),
+                             GetLocale()));
+  EXPECT_EQ(u"16515558946",
+            profile->GetInfo(
+                autofill::AutofillType(autofill::PHONE_HOME_WHOLE_NUMBER),
+                GetLocale()));
+  EXPECT_EQ(kEmailAddress,
+            profile->GetInfo(autofill::AutofillType(autofill::EMAIL_ADDRESS),
+                             GetLocale()));
+}
+
+IN_PROC_BROWSER_TEST_F(
+    MAYBE_PaymentRequestContactInfoEditorBasicCardDisabledTest,
+    Validation) {
+  // Installs two apps so that the Payment Request UI will be shown.
+  std::string a_method_name;
+  InstallPaymentApp("a.com", "payment_request_success_responder.js",
+                    &a_method_name);
+  std::string b_method_name;
+  InstallPaymentApp("b.com", "payment_request_success_responder.js",
+                    &b_method_name);
+
+  NavigateTo("/payment_request_contact_details_test.html");
+
+  // Show a Payment Request.
+  InvokePaymentRequestUIWithJs(
+      content::JsReplace("buyWithMethods([{supportedMethods:$1}"
+                         ", {supportedMethods:$2}]);",
+                         a_method_name, b_method_name));
+
+  OpenContactInfoEditorScreen();
+
+  // Insert invalid values into fields which have rules more complex than
+  // just emptiness, and an empty string into simple required fields.
+  SetEditorTextfieldValue(std::u16string(), autofill::NAME_FULL);
+  SetEditorTextfieldValue(kPhoneNumberInvalid,
+                          autofill::PHONE_HOME_WHOLE_NUMBER);
+  SetEditorTextfieldValue(kEmailAddressInvalid, autofill::EMAIL_ADDRESS);
+
+  EXPECT_TRUE(IsEditorTextfieldInvalid(autofill::NAME_FULL));
+  EXPECT_TRUE(IsEditorTextfieldInvalid(autofill::PHONE_HOME_WHOLE_NUMBER));
+  EXPECT_TRUE(IsEditorTextfieldInvalid(autofill::EMAIL_ADDRESS));
+
+  // Correct the problems.
+  SetEditorTextfieldValue(kNameFull, autofill::NAME_FULL);
+  SetEditorTextfieldValue(kPhoneNumber, autofill::PHONE_HOME_WHOLE_NUMBER);
+  SetEditorTextfieldValue(kEmailAddress, autofill::EMAIL_ADDRESS);
+
+  EXPECT_FALSE(IsEditorTextfieldInvalid(autofill::NAME_FULL));
+  EXPECT_FALSE(IsEditorTextfieldInvalid(autofill::PHONE_HOME_WHOLE_NUMBER));
+  EXPECT_FALSE(IsEditorTextfieldInvalid(autofill::EMAIL_ADDRESS));
+
+  autofill::PersonalDataManager* personal_data_manager = GetDataManager();
+  personal_data_manager->AddObserver(&personal_data_observer_);
+
+  // Wait until the web database has been updated and the notification sent.
+  base::RunLoop data_loop;
+  EXPECT_CALL(personal_data_observer_, OnPersonalDataChanged())
+      .WillOnce(QuitMessageLoop(&data_loop));
+  ClickOnDialogViewAndWait(DialogViewID::EDITOR_SAVE_BUTTON);
+  data_loop.Run();
+
+  personal_data_manager->RemoveObserver(&personal_data_observer_);
+  ASSERT_EQ(1UL, personal_data_manager->GetProfiles().size());
+  autofill::AutofillProfile* profile = personal_data_manager->GetProfiles()[0];
+  DCHECK(profile);
+
+  EXPECT_EQ(kNameFull,
+            profile->GetInfo(autofill::AutofillType(autofill::NAME_FULL),
+                             GetLocale()));
+  EXPECT_EQ(u"16515558946",
+            profile->GetInfo(
+                autofill::AutofillType(autofill::PHONE_HOME_WHOLE_NUMBER),
+                GetLocale()));
+  EXPECT_EQ(kEmailAddress,
+            profile->GetInfo(autofill::AutofillType(autofill::EMAIL_ADDRESS),
+                             GetLocale()));
+}
+
+IN_PROC_BROWSER_TEST_F(
+    MAYBE_PaymentRequestContactInfoEditorBasicCardDisabledTest,
+    ModifyExisting) {
+  // Installs two apps so that the Payment Request UI will be shown.
+  std::string a_method_name;
+  InstallPaymentApp("a.com", "payment_request_success_responder.js",
+                    &a_method_name);
+  std::string b_method_name;
+  InstallPaymentApp("b.com", "payment_request_success_responder.js",
+                    &b_method_name);
+
+  NavigateTo("/payment_request_contact_details_test.html");
+  autofill::PersonalDataManager* personal_data_manager = GetDataManager();
+  personal_data_manager->AddObserver(&personal_data_observer_);
+
+  autofill::AutofillProfile incomplete_profile;
+  incomplete_profile.SetInfo(autofill::NAME_FULL, kNameFull, GetLocale());
+  AddAutofillProfile(incomplete_profile);
+
+  // Show a Payment Request.
+  InvokePaymentRequestUIWithJs(
+      content::JsReplace("buyWithMethods([{supportedMethods:$1}"
+                         ", {supportedMethods:$2}]);",
+                         a_method_name, b_method_name));
+
+  OpenContactInfoScreen();
+
+  views::View* list_view = dialog_view()->GetViewByID(
+      static_cast<int>(DialogViewID::CONTACT_INFO_SHEET_LIST_VIEW));
+  DCHECK(list_view);
+  ClickOnDialogViewAndWait(list_view->children().front());
+
+  // Do not set name: This should have been populated when opening the screen.
+  EXPECT_EQ(kNameFull, GetEditorTextfieldValue(autofill::NAME_FULL));
+  SetEditorTextfieldValue(kPhoneNumber, autofill::PHONE_HOME_WHOLE_NUMBER);
+  SetEditorTextfieldValue(kEmailAddress, autofill::EMAIL_ADDRESS);
+
+  // Wait until the web database has been updated and the notification sent.
+  base::RunLoop save_data_loop;
+  EXPECT_CALL(personal_data_observer_, OnPersonalDataChanged())
+      .WillOnce(QuitMessageLoop(&save_data_loop));
+  ClickOnDialogViewAndWait(DialogViewID::EDITOR_SAVE_BUTTON);
+  save_data_loop.Run();
+
+  personal_data_manager->RemoveObserver(&personal_data_observer_);
+  ASSERT_EQ(1UL, personal_data_manager->GetProfiles().size());
+  autofill::AutofillProfile* profile = personal_data_manager->GetProfiles()[0];
+  DCHECK(profile);
+
+  EXPECT_EQ(kNameFull,
+            profile->GetInfo(autofill::AutofillType(autofill::NAME_FULL),
+                             GetLocale()));
+  EXPECT_EQ(u"16515558946",
+            profile->GetInfo(
+                autofill::AutofillType(autofill::PHONE_HOME_WHOLE_NUMBER),
+                GetLocale()));
+  EXPECT_EQ(kEmailAddress,
+            profile->GetInfo(autofill::AutofillType(autofill::EMAIL_ADDRESS),
+                             GetLocale()));
+}
+
+IN_PROC_BROWSER_TEST_F(
+    MAYBE_PaymentRequestContactInfoEditorBasicCardDisabledTest,
+    ModifyExistingSelectsIt) {
+  // Installs two apps so that the Payment Request UI will be shown.
+  std::string a_method_name;
+  InstallPaymentApp("a.com", "payment_request_success_responder.js",
+                    &a_method_name);
+  std::string b_method_name;
+  InstallPaymentApp("b.com", "payment_request_success_responder.js",
+                    &b_method_name);
+
+  NavigateTo("/payment_request_contact_details_test.html");
+  autofill::PersonalDataManager* personal_data_manager = GetDataManager();
+  personal_data_manager->AddObserver(&personal_data_observer_);
+
+  autofill::AutofillProfile incomplete_profile;
+  incomplete_profile.SetInfo(autofill::NAME_FULL, kNameFull, GetLocale());
+  AddAutofillProfile(incomplete_profile);
+
+  autofill::AutofillProfile other_incomplete_profile;
+  other_incomplete_profile.SetInfo(autofill::NAME_FULL, u"other", GetLocale());
+  AddAutofillProfile(other_incomplete_profile);
+
+  // Show a Payment Request.
+  InvokePaymentRequestUIWithJs(
+      content::JsReplace("buyWithMethods([{supportedMethods:$1}"
+                         ", {supportedMethods:$2}]);",
+                         a_method_name, b_method_name));
+
+  OpenContactInfoScreen();
+
+  PaymentRequest* request = GetPaymentRequests().front();
+
+  // No contact profiles are selected because both are incomplete.
+  EXPECT_EQ(nullptr, request->state()->selected_contact_profile());
+
+  views::View* list_view = dialog_view()->GetViewByID(
+      static_cast<int>(DialogViewID::CONTACT_INFO_SHEET_LIST_VIEW));
+  DCHECK(list_view);
+  ClickOnDialogViewAndWait(list_view->children()[1]);
+
+  SetEditorTextfieldValue(kPhoneNumber, autofill::PHONE_HOME_WHOLE_NUMBER);
+  SetEditorTextfieldValue(kEmailAddress, autofill::EMAIL_ADDRESS);
+
+  // Wait until the web database has been updated and the notification sent.
+  base::RunLoop save_data_loop;
+  EXPECT_CALL(personal_data_observer_, OnPersonalDataChanged())
+      .WillOnce(QuitMessageLoop(&save_data_loop));
+  ClickOnDialogViewAndWait(DialogViewID::EDITOR_SAVE_BUTTON);
+  save_data_loop.Run();
+
+  personal_data_manager->RemoveObserver(&personal_data_observer_);
+  autofill::AutofillProfile* profile =
+      request->state()->selected_contact_profile();
+  DCHECK(profile);
+
+  EXPECT_EQ(u"16515558946",
+            profile->GetInfo(
+                autofill::AutofillType(autofill::PHONE_HOME_WHOLE_NUMBER),
+                GetLocale()));
+  EXPECT_EQ(kEmailAddress,
+            profile->GetInfo(autofill::AutofillType(autofill::EMAIL_ADDRESS),
+                             GetLocale()));
+
+  // Expect the newly-completed profile to be selected.
+  EXPECT_EQ(2U, request->state()->contact_profiles().size());
+  EXPECT_EQ(request->state()->contact_profiles().back(), profile);
+}
+
+IN_PROC_BROWSER_TEST_F(
+    MAYBE_PaymentRequestContactInfoEditorBasicCardDisabledTest,
+    HappyPathInIncognito) {
+  // Installs two apps so that the Payment Request UI will be shown.
+  std::string a_method_name;
+  InstallPaymentApp("a.com", "payment_request_success_responder.js",
+                    &a_method_name);
+  std::string b_method_name;
+  InstallPaymentApp("b.com", "payment_request_success_responder.js",
+                    &b_method_name);
+
+  SetIncognito();
+  NavigateTo("/payment_request_contact_details_test.html");
+
+  // Show a Payment Request.
+  InvokePaymentRequestUIWithJs(
+      content::JsReplace("buyWithMethods([{supportedMethods:$1}"
+                         ", {supportedMethods:$2}]);",
+                         a_method_name, b_method_name));
+
+  OpenContactInfoEditorScreen();
+
+  SetEditorTextfieldValue(kNameFull, autofill::NAME_FULL);
+  SetEditorTextfieldValue(kPhoneNumber, autofill::PHONE_HOME_WHOLE_NUMBER);
+  SetEditorTextfieldValue(kEmailAddress, autofill::EMAIL_ADDRESS);
+
+  autofill::PersonalDataManager* personal_data_manager = GetDataManager();
+  personal_data_manager->AddObserver(&personal_data_observer_);
+
+  EXPECT_CALL(personal_data_observer_, OnPersonalDataChanged()).Times(0);
+  ClickOnDialogViewAndWait(DialogViewID::EDITOR_SAVE_BUTTON);
+
+  personal_data_manager->RemoveObserver(&personal_data_observer_);
+  // In incognito, the profile should be available in contact_profiles but it
+  // shouldn't be saved to the PersonalDataManager.
+  ASSERT_EQ(0UL, personal_data_manager->GetProfiles().size());
+  PaymentRequest* request = GetPaymentRequests().front();
+  EXPECT_EQ(1U, request->state()->contact_profiles().size());
+  EXPECT_EQ(request->state()->contact_profiles().back(),
+            request->state()->selected_contact_profile());
+
+  autofill::AutofillProfile* profile =
+      request->state()->contact_profiles().back();
+  DCHECK(profile);
+
+  EXPECT_EQ(kNameFull,
+            profile->GetInfo(autofill::AutofillType(autofill::NAME_FULL),
+                             GetLocale()));
+  EXPECT_EQ(u"16515558946",
+            profile->GetInfo(
+                autofill::AutofillType(autofill::PHONE_HOME_WHOLE_NUMBER),
+                GetLocale()));
+  EXPECT_EQ(kEmailAddress,
+            profile->GetInfo(autofill::AutofillType(autofill::EMAIL_ADDRESS),
+                             GetLocale()));
+}
+
+IN_PROC_BROWSER_TEST_F(
+    MAYBE_PaymentRequestContactInfoEditorBasicCardDisabledTest,
+    RetryWithPayerErrors) {
+  // Installs two apps so that the Payment Request UI will be shown.
+  std::string a_method_name;
+  InstallPaymentApp("a.com", "payment_request_success_responder.js",
+                    &a_method_name);
+  std::string b_method_name;
+  InstallPaymentApp("b.com", "payment_request_success_responder.js",
+                    &b_method_name);
+
+  NavigateTo("/payment_request_retry_with_payer_errors.html");
+
+  autofill::AutofillProfile contact = autofill::test::GetFullProfile();
+  AddAutofillProfile(contact);
+
+  // Show a Payment Request.
+  InvokePaymentRequestUIWithJs(
+      content::JsReplace("buyWithMethods([{supportedMethods:$1}"
+                         ", {supportedMethods:$2}]);",
+                         a_method_name, b_method_name));
+
+  // Click on pay.
+  EXPECT_TRUE(IsPayButtonEnabled());
+  ResetEventWaiterForSequence({DialogEvent::PROCESSING_SPINNER_SHOWN});
+  ClickOnDialogViewAndWait(DialogViewID::PAY_BUTTON, dialog_view());
+
+  ResetEventWaiterForSequence({DialogEvent::PROCESSING_SPINNER_HIDDEN,
+                               DialogEvent::SPEC_DONE_UPDATING,
+                               DialogEvent::PROCESSING_SPINNER_HIDDEN,
+                               DialogEvent::BACK_TO_PAYMENT_SHEET_NAVIGATION,
+                               DialogEvent::CONTACT_INFO_EDITOR_OPENED});
+  ASSERT_TRUE(content::ExecuteScript(GetActiveWebContents(),
+                                     "retry({"
+                                     "  payer: {"
+                                     "    email: 'EMAIL ERROR',"
+                                     "    name: 'NAME ERROR',"
+                                     "    phone: 'PHONE ERROR'"
+                                     "  }"
+                                     "});"));
+  WaitForObservedEvent();
+
+  EXPECT_EQ(u"EMAIL ERROR", GetErrorLabelForType(autofill::EMAIL_ADDRESS));
+  EXPECT_EQ(u"NAME ERROR", GetErrorLabelForType(autofill::NAME_FULL));
+  EXPECT_EQ(u"PHONE ERROR",
+            GetErrorLabelForType(autofill::PHONE_HOME_WHOLE_NUMBER));
+}
+
+IN_PROC_BROWSER_TEST_F(
+    MAYBE_PaymentRequestContactInfoEditorBasicCardDisabledTest,
+    RetryWithPayerErrors_HasSameValueButDifferentErrorsShown) {
+  // Installs two apps so that the Payment Request UI will be shown.
+  std::string a_method_name;
+  InstallPaymentApp("a.com", "payment_request_success_responder.js",
+                    &a_method_name);
+  std::string b_method_name;
+  InstallPaymentApp("b.com", "payment_request_success_responder.js",
+                    &b_method_name);
+
+  NavigateTo("/payment_request_retry_with_payer_errors.html");
+
+  autofill::AutofillProfile contact = autofill::test::GetFullProfile();
+  // Set the same value in both of email and name field.
+  contact.SetRawInfo(autofill::EMAIL_ADDRESS, u"johndoe@hades.com");
+  contact.SetRawInfo(autofill::NAME_FULL, u"johndoe@hades.com");
+  AddAutofillProfile(contact);
+
+  // Show a Payment Request.
+  InvokePaymentRequestUIWithJs(
+      content::JsReplace("buyWithMethods([{supportedMethods:$1}"
+                         ", {supportedMethods:$2}]);",
+                         a_method_name, b_method_name));
+
+  // Click on pay.
+  EXPECT_TRUE(IsPayButtonEnabled());
+  ResetEventWaiterForSequence({DialogEvent::PROCESSING_SPINNER_SHOWN});
+  ClickOnDialogViewAndWait(DialogViewID::PAY_BUTTON, dialog_view());
+
+  ResetEventWaiterForSequence({DialogEvent::PROCESSING_SPINNER_HIDDEN,
+                               DialogEvent::SPEC_DONE_UPDATING,
+                               DialogEvent::PROCESSING_SPINNER_HIDDEN,
+                               DialogEvent::BACK_TO_PAYMENT_SHEET_NAVIGATION,
+                               DialogEvent::CONTACT_INFO_EDITOR_OPENED});
+  ASSERT_TRUE(content::ExecuteScript(GetActiveWebContents(),
+                                     "retry({"
+                                     "  payer: {"
+                                     "    email: 'EMAIL ERROR',"
+                                     "    name: 'NAME ERROR',"
+                                     "    phone: 'PHONE ERROR'"
+                                     "  }"
+                                     "});"));
+  WaitForObservedEvent();
+
+  EXPECT_EQ(u"EMAIL ERROR", GetErrorLabelForType(autofill::EMAIL_ADDRESS));
+  EXPECT_EQ(u"NAME ERROR", GetErrorLabelForType(autofill::NAME_FULL));
+  EXPECT_EQ(u"PHONE ERROR",
+            GetErrorLabelForType(autofill::PHONE_HOME_WHOLE_NUMBER));
+}
+
+IN_PROC_BROWSER_TEST_F(
+    MAYBE_PaymentRequestContactInfoEditorBasicCardDisabledTest,
+    RetryWithPayerErrors_NoPaymentOptions) {
+  // Installs two apps so that the Payment Request UI will be shown.
+  std::string a_method_name;
+  InstallPaymentApp("a.com", "payment_request_success_responder.js",
+                    &a_method_name);
+  std::string b_method_name;
+  InstallPaymentApp("b.com", "payment_request_success_responder.js",
+                    &b_method_name);
+
+  NavigateTo("/payment_request_retry_with_no_payment_options.html");
+
+  autofill::AutofillProfile contact = autofill::test::GetFullProfile();
+  AddAutofillProfile(contact);
+
+  // Show a Payment Request.
+  InvokePaymentRequestUIWithJs(
+      content::JsReplace("buyWithMethods([{supportedMethods:$1}"
+                         ", {supportedMethods:$2}]);",
+                         a_method_name, b_method_name));
+
+  // Click on pay.
+  EXPECT_TRUE(IsPayButtonEnabled());
+  ResetEventWaiterForSequence({DialogEvent::PROCESSING_SPINNER_SHOWN});
+  ClickOnDialogViewAndWait(DialogViewID::PAY_BUTTON, dialog_view());
+
+  ResetEventWaiterForSequence({DialogEvent::PROCESSING_SPINNER_HIDDEN,
+                               DialogEvent::SPEC_DONE_UPDATING,
+                               DialogEvent::PROCESSING_SPINNER_HIDDEN,
+                               DialogEvent::BACK_TO_PAYMENT_SHEET_NAVIGATION});
+  ASSERT_TRUE(content::ExecuteScript(GetActiveWebContents(),
+                                     "retry({"
+                                     "  payer: {"
+                                     "    email: 'EMAIL ERROR',"
+                                     "    name: 'NAME ERROR',"
+                                     "    phone: 'PHONE ERROR'"
+                                     "  }"
+                                     "});"));
+  WaitForObservedEvent();
+
+  const int kErrorLabelOffset =
+      static_cast<int>(DialogViewID::ERROR_LABEL_OFFSET);
+  EXPECT_EQ(nullptr, dialog_view()->GetViewByID(kErrorLabelOffset +
+                                                autofill::EMAIL_ADDRESS));
+  EXPECT_EQ(nullptr, dialog_view()->GetViewByID(kErrorLabelOffset +
+                                                autofill::NAME_FULL));
+  EXPECT_EQ(nullptr,
+            dialog_view()->GetViewByID(kErrorLabelOffset +
+                                       autofill::PHONE_HOME_WHOLE_NUMBER));
+}
+
 }  // namespace payments
diff --git a/chrome/browser/ui/views/payments/payment_request_journey_logger_browsertest.cc b/chrome/browser/ui/views/payments/payment_request_journey_logger_browsertest.cc
index 05fee60..e9f2e0e 100644
--- a/chrome/browser/ui/views/payments/payment_request_journey_logger_browsertest.cc
+++ b/chrome/browser/ui/views/payments/payment_request_journey_logger_browsertest.cc
@@ -1554,6 +1554,62 @@
                                     0);
 }
 
+using PaymentRequestNotShownBasicCardDisabledTest = BasicCardDisabledTestBase;
+
+IN_PROC_BROWSER_TEST_F(PaymentRequestNotShownBasicCardDisabledTest,
+                       OnlyNotShownMetricsLogged) {
+  // Installs two apps so that canMakePayment is true.
+  std::string a_method_name;
+  InstallPaymentApp("a.com", "payment_request_success_responder.js",
+                    &a_method_name);
+  std::string b_method_name;
+  InstallPaymentApp("b.com", "payment_request_success_responder.js",
+                    &b_method_name);
+
+  NavigateTo("/payment_request_can_make_payment_metrics_test.html");
+  base::HistogramTester histogram_tester;
+
+  ResetEventWaiterForSequence({DialogEvent::CAN_MAKE_PAYMENT_CALLED,
+                               DialogEvent::CAN_MAKE_PAYMENT_RETURNED,
+                               DialogEvent::HAS_ENROLLED_INSTRUMENT_CALLED,
+                               DialogEvent::HAS_ENROLLED_INSTRUMENT_RETURNED});
+
+  // Initiate a Payment Request without showing it.
+  ASSERT_TRUE(content::ExecuteScript(
+      GetActiveWebContents(),
+      content::JsReplace("queryNoShowWithMethods([{supportedMethods:$1}"
+                         ", {supportedMethods:$2}]);",
+                         a_method_name, b_method_name)));
+
+  WaitForObservedEvent();
+
+  // Navigate away to abort the Payment Request and trigger the logs.
+  NavigateTo("/payment_request_email_test.html");
+
+  // Abort should be logged.
+  histogram_tester.ExpectBucketCount(
+      "PaymentRequest.CheckoutFunnel.Aborted",
+      JourneyLogger::ABORT_REASON_USER_NAVIGATION, 1);
+
+  // Some events should be logged.
+  std::vector<base::Bucket> buckets =
+      histogram_tester.GetAllSamples("PaymentRequest.Events");
+  ASSERT_EQ(1U, buckets.size());
+  EXPECT_EQ(JourneyLogger::EVENT_USER_ABORTED |
+                JourneyLogger::EVENT_HAD_INITIAL_FORM_OF_PAYMENT |
+                JourneyLogger::EVENT_HAD_NECESSARY_COMPLETE_SUGGESTIONS |
+                JourneyLogger::EVENT_CAN_MAKE_PAYMENT_TRUE |
+                JourneyLogger::EVENT_HAS_ENROLLED_INSTRUMENT_TRUE |
+                JourneyLogger::EVENT_REQUEST_METHOD_OTHER |
+                JourneyLogger::EVENT_AVAILABLE_METHOD_OTHER,
+            buckets[0].min);
+
+  // Make sure that the metrics that required the Payment Request to be shown
+  // are not logged.
+  histogram_tester.ExpectTotalCount("PaymentRequest.NumberOfSuggestionsShown",
+                                    0);
+}
+
 using PaymentRequestCompleteSuggestionsForEverythingTest =
     BasicCardEnabledTestBase;
 
diff --git a/chrome/browser/ui/views/tabs/tab_hover_card_controller.cc b/chrome/browser/ui/views/tabs/tab_hover_card_controller.cc
index e09fd37..57e9261 100644
--- a/chrome/browser/ui/views/tabs/tab_hover_card_controller.cc
+++ b/chrome/browser/ui/views/tabs/tab_hover_card_controller.cc
@@ -218,11 +218,11 @@
   // then when the fade timer elapses we won't incorrectly try to fade in on the
   // wrong tab.
   if (target_tab_ != tab) {
+    delayed_show_timer_.Stop();
     target_tab_observation_.Reset();
     if (tab)
       target_tab_observation_.Observe(tab);
     target_tab_ = tab;
-    delayed_show_timer_.Stop();
   }
 
   // If there's nothing to attach to then there's no point in creating a card.
@@ -320,6 +320,10 @@
         base::BindOnce(&TabHoverCardController::ShowHoverCard,
                        base::Unretained(this), true, tab));
   } else {
+    // Just in case, cancel the timer. This shouldn't cancel a delayed capture
+    // since delayed capture only happens when the hover card already exists,
+    // and this code is only invoked if there is no hover card yet.
+    delayed_show_timer_.Stop();
     DCHECK_EQ(target_tab_, tab);
     ShowHoverCard(is_initial, tab);
   }
@@ -373,6 +377,7 @@
 
 void TabHoverCardController::OnViewIsDeleting(views::View* observed_view) {
   if (hover_card_ == observed_view) {
+    delayed_show_timer_.Stop();
     hover_card_observation_.Reset();
     event_sniffer_.reset();
     slide_progressed_subscription_ = base::CallbackListSubscription();
diff --git a/chrome/browser/ui/views/tabs/tab_hover_card_controller.h b/chrome/browser/ui/views/tabs/tab_hover_card_controller.h
index e37154f..886844a 100644
--- a/chrome/browser/ui/views/tabs/tab_hover_card_controller.h
+++ b/chrome/browser/ui/views/tabs/tab_hover_card_controller.h
@@ -116,8 +116,6 @@
   // the mouse reenters within a given amount of time.
   base::TimeTicks last_mouse_exit_timestamp_;
 
-  base::OneShotTimer delayed_show_timer_;
-
   raw_ptr<Tab> target_tab_ = nullptr;
   const raw_ptr<TabStrip> tab_strip_;
   raw_ptr<TabHoverCardBubbleView> hover_card_ = nullptr;
@@ -144,6 +142,9 @@
   base::CallbackListSubscription fade_complete_subscription_;
   base::CallbackListSubscription slide_progressed_subscription_;
   base::CallbackListSubscription slide_complete_subscription_;
+
+  // Ensure that this timer is destroyed before anything else is cleaned up.
+  base::OneShotTimer delayed_show_timer_;
 };
 
 #endif  // CHROME_BROWSER_UI_VIEWS_TABS_TAB_HOVER_CARD_CONTROLLER_H_
diff --git a/chrome/browser/ui/views/tabs/tab_scrubber_chromeos_browsertest.cc b/chrome/browser/ui/views/tabs/tab_scrubber_chromeos_browsertest.cc
index bba2687..b387cfe 100644
--- a/chrome/browser/ui/views/tabs/tab_scrubber_chromeos_browsertest.cc
+++ b/chrome/browser/ui/views/tabs/tab_scrubber_chromeos_browsertest.cc
@@ -91,7 +91,7 @@
   TabScrubberChromeOSTest& operator=(const TabScrubberChromeOSTest&) = delete;
 
   void SetUpCommandLine(base::CommandLine* command_line) override {
-    command_line->AppendSwitch(chromeos::switches::kNaturalScrollDefault);
+    command_line->AppendSwitch(ash::switches::kNaturalScrollDefault);
   }
 
   void SetUpOnMainThread() override {
diff --git a/chrome/browser/ui/views/tabs/tab_strip.cc b/chrome/browser/ui/views/tabs/tab_strip.cc
index 3a18edf96..e17c26c 100644
--- a/chrome/browser/ui/views/tabs/tab_strip.cc
+++ b/chrome/browser/ui/views/tabs/tab_strip.cc
@@ -895,6 +895,7 @@
           controller_.get(),
           base::BindRepeating(&TabStrip::tabs_view_model,
                               base::Unretained(this)))),
+      hover_card_controller_(std::make_unique<TabHoverCardController>(this)),
       drag_context_(std::make_unique<TabDragContextImpl>(this)) {
   // TODO(pbos): This is probably incorrect, the background of individual tabs
   // depend on their selected state. This should probably be pushed down into
@@ -1850,11 +1851,8 @@
     update_type = HoverCardUpdateType::kAnimating;
   }
 
-  if (!hover_card_controller_) {
-    if (!tab)
-      return;
-    hover_card_controller_ = std::make_unique<TabHoverCardController>(this);
-  }
+  if (!hover_card_controller_)
+    return;
 
   hover_card_controller_->UpdateHoverCard(tab, update_type);
 }
diff --git a/chrome/browser/ui/views/user_education/feature_promo_snooze_interactive_uitest.cc b/chrome/browser/ui/views/user_education/feature_promo_snooze_interactive_uitest.cc
index bffd8f2..15a24d3 100644
--- a/chrome/browser/ui/views/user_education/feature_promo_snooze_interactive_uitest.cc
+++ b/chrome/browser/ui/views/user_education/feature_promo_snooze_interactive_uitest.cc
@@ -151,7 +151,8 @@
     // Opening 6 or more tabs is the triggering event for tab groups
     // IPH.
     for (int i = 0; i < 5; ++i)
-      AddTabAtIndex(0, GURL("about:blank"), ui::PAGE_TRANSITION_TYPED);
+      ASSERT_TRUE(
+          AddTabAtIndex(0, GURL("about:blank"), ui::PAGE_TRANSITION_TYPED));
 
     ASSERT_EQ(should_show,
               promo_controller_->BubbleIsShowing(
diff --git a/chrome/browser/ui/web_applications/web_app_guest_session_browsertest_chromeos.cc b/chrome/browser/ui/web_applications/web_app_guest_session_browsertest_chromeos.cc
index b724e6b8..1399ccd4 100644
--- a/chrome/browser/ui/web_applications/web_app_guest_session_browsertest_chromeos.cc
+++ b/chrome/browser/ui/web_applications/web_app_guest_session_browsertest_chromeos.cc
@@ -30,11 +30,11 @@
 class WebAppGuestSessionBrowserTest : public InProcessBrowserTest,
                                       public WithCrosapiParam {
   void SetUpCommandLine(base::CommandLine* command_line) override {
-    command_line->AppendSwitch(chromeos::switches::kGuestSession);
+    command_line->AppendSwitch(ash::switches::kGuestSession);
     command_line->AppendSwitch(::switches::kIncognito);
-    command_line->AppendSwitchASCII(chromeos::switches::kLoginProfile, "user");
+    command_line->AppendSwitchASCII(ash::switches::kLoginProfile, "user");
     command_line->AppendSwitchASCII(
-        chromeos::switches::kLoginUser,
+        ash::switches::kLoginUser,
         user_manager::GuestAccountId().GetUserEmail());
   }
 };
diff --git a/chrome/browser/ui/webui/chromeos/emulator/device_emulator_message_handler.cc b/chrome/browser/ui/webui/chromeos/emulator/device_emulator_message_handler.cc
index 70944bc..d383d37 100644
--- a/chrome/browser/ui/webui/chromeos/emulator/device_emulator_message_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/emulator/device_emulator_message_handler.cc
@@ -52,6 +52,7 @@
 const char kSetHasMouse[] = "setHasMouse";
 
 const char kPairedPropertyName[] = "Paired";
+const char kAudioNodesUpdated[] = "audioNodesUpdated";
 
 // Wattages to use as max power for power sources.
 const double kPowerLevelHigh = 50;
@@ -140,7 +141,12 @@
   }
 
   // chromeos::CrasAudioClient::Observer.
-  void NodesChanged() override { owner_->HandleRequestAudioNodes(nullptr); }
+  void NodesChanged() override {
+    if (!owner_->IsJavascriptAllowed()) {
+      return;
+    }
+    owner_->UpdateAudioNodes();
+  }
 
  private:
   DeviceEmulatorMessageHandler* owner_;
@@ -193,9 +199,9 @@
                      weak_ptr_factory_.GetWeakPtr()));
 }
 
-DeviceEmulatorMessageHandler::~DeviceEmulatorMessageHandler() {}
+DeviceEmulatorMessageHandler::~DeviceEmulatorMessageHandler() = default;
 
-void DeviceEmulatorMessageHandler::Init(const base::ListValue* args) {
+void DeviceEmulatorMessageHandler::Init(const base::Value::ConstListView args) {
   AllowJavascript();
 }
 
@@ -209,26 +215,26 @@
 }
 
 void DeviceEmulatorMessageHandler::RequestPowerInfo(
-    const base::ListValue* args) {
+    const base::Value::ConstListView args) {
   fake_power_manager_client_->RequestStatusUpdate();
 }
 
 void DeviceEmulatorMessageHandler::HandleRemoveBluetoothDevice(
-    const base::ListValue* args) {
-  CHECK(!args->GetList().empty());
-  std::string path = args->GetList()[0].GetString();
+    const base::Value::ConstListView args) {
+  CHECK(!args.empty());
+  std::string path = args[0].GetString();
   fake_bluetooth_device_client_->RemoveDevice(
       dbus::ObjectPath(bluez::FakeBluetoothAdapterClient::kAdapterPath),
       dbus::ObjectPath(path));
 }
 
 void DeviceEmulatorMessageHandler::HandleRequestBluetoothDiscover(
-    const base::ListValue* args) {
+    const base::Value::ConstListView args) {
   CreateBluetoothDeviceFromListValue(args);
 }
 
 void DeviceEmulatorMessageHandler::HandleRequestBluetoothInfo(
-    const base::ListValue* args) {
+    const base::Value::ConstListView args) {
   AllowJavascript();
   // Get a list containing paths of the devices which are connected to
   // the main adapter.
@@ -273,7 +279,7 @@
 }
 
 void DeviceEmulatorMessageHandler::HandleRequestBluetoothPair(
-    const base::ListValue* args) {
+    const base::Value::ConstListView args) {
   // Create the device if it does not already exist.
   std::string path = CreateBluetoothDeviceFromListValue(args);
   bluez::FakeBluetoothDeviceClient::Properties* props =
@@ -288,36 +294,16 @@
 }
 
 void DeviceEmulatorMessageHandler::HandleRequestAudioNodes(
-    const base::ListValue* args) {
+    const base::Value::ConstListView args) {
   AllowJavascript();
-  const base::Value& callback_id = args->GetList()[0];
-
-  // Get every active audio node and create a dictionary to
-  // send it to JavaScript.
-  base::ListValue audio_nodes;
-  for (const AudioNode& node :
-       chromeos::FakeCrasAudioClient::Get()->node_list()) {
-    std::unique_ptr<base::DictionaryValue> audio_node(
-        new base::DictionaryValue());
-
-    audio_node->SetBoolean("isInput", node.is_input);
-    audio_node->SetString("id", base::NumberToString(node.id));
-    audio_node->SetString("deviceName", node.device_name);
-    audio_node->SetString("type", node.type);
-    audio_node->SetString("name", node.name);
-    audio_node->SetBoolean("active", node.active);
-
-    audio_nodes.Append(std::move(audio_node));
-  }
-
-  ResolveJavascriptCallback(callback_id, audio_nodes);
+  UpdateAudioNodes();
 }
 
 void DeviceEmulatorMessageHandler::HandleInsertAudioNode(
-    const base::ListValue* args) {
+    const base::Value::ConstListView args) {
   AudioNode audio_node;
 
-  const base::Value& device_value = args->GetList()[0];
+  const base::Value& device_value = args[0];
   CHECK(device_value.is_dict());
   const base::DictionaryValue& device_dict =
       base::Value::AsDictionaryValue(device_value);
@@ -335,9 +321,9 @@
 }
 
 void DeviceEmulatorMessageHandler::HandleRemoveAudioNode(
-    const base::ListValue* args) {
-  CHECK(!args->GetList().empty());
-  std::string tmp_id = args->GetList()[0].GetString();
+    const base::Value::ConstListView args) {
+  CHECK(!args.empty());
+  std::string tmp_id = args[0].GetString();
   uint64_t id;
   CHECK(base::StringToUint64(tmp_id, &id));
 
@@ -345,30 +331,27 @@
 }
 
 void DeviceEmulatorMessageHandler::HandleSetHasTouchpad(
-    const base::ListValue* args) {
-  const auto& list = args->GetList();
-  CHECK(!list.empty());
-  const bool has_touchpad = list[0].GetBool();
+    const base::Value::ConstListView args) {
+  CHECK(!args.empty());
+  const bool has_touchpad = args[0].GetBool();
 
   system::InputDeviceSettings::Get()->GetFakeInterface()->set_touchpad_exists(
       has_touchpad);
 }
 
 void DeviceEmulatorMessageHandler::HandleSetHasMouse(
-    const base::ListValue* args) {
-  const auto& list = args->GetList();
-  CHECK(!list.empty());
-  const bool has_mouse = list[0].GetBool();
+    const base::Value::ConstListView args) {
+  CHECK(!args.empty());
+  const bool has_mouse = args[0].GetBool();
 
   system::InputDeviceSettings::Get()->GetFakeInterface()->set_mouse_exists(
       has_mouse);
 }
 
 void DeviceEmulatorMessageHandler::UpdateBatteryPercent(
-    const base::ListValue* args) {
-  const auto& list = args->GetList();
-  if (list.size() >= 1 && list[0].is_int()) {
-    int new_percent = list[0].GetInt();
+    const base::Value::ConstListView args) {
+  if (args.size() >= 1 && args[0].is_int()) {
+    int new_percent = args[0].GetInt();
     power_manager::PowerSupplyProperties props =
         *fake_power_manager_client_->GetLastStatus();
     props.set_battery_percent(new_percent);
@@ -377,10 +360,9 @@
 }
 
 void DeviceEmulatorMessageHandler::UpdateBatteryState(
-    const base::ListValue* args) {
-  const auto& list = args->GetList();
-  if (list.size() >= 1 && list[0].is_int()) {
-    int battery_state = list[0].GetInt();
+    const base::Value::ConstListView args) {
+  if (args.size() >= 1 && args[0].is_int()) {
+    int battery_state = args[0].GetInt();
     power_manager::PowerSupplyProperties props =
         *fake_power_manager_client_->GetLastStatus();
     props.set_battery_state(
@@ -391,10 +373,9 @@
 }
 
 void DeviceEmulatorMessageHandler::UpdateTimeToEmpty(
-    const base::ListValue* args) {
-  const auto& list = args->GetList();
-  if (list.size() >= 1 && list[0].is_int()) {
-    int new_time = list[0].GetInt();
+    const base::Value::ConstListView args) {
+  if (args.size() >= 1 && args[0].is_int()) {
+    int new_time = args[0].GetInt();
     power_manager::PowerSupplyProperties props =
         *fake_power_manager_client_->GetLastStatus();
     props.set_battery_time_to_empty_sec(new_time);
@@ -403,10 +384,9 @@
 }
 
 void DeviceEmulatorMessageHandler::UpdateTimeToFull(
-    const base::ListValue* args) {
-  const auto& list = args->GetList();
-  if (list.size() >= 1 && list[0].is_int()) {
-    int new_time = list[0].GetInt();
+    const base::Value::ConstListView args) {
+  if (args.size() >= 1 && args[0].is_int()) {
+    int new_time = args[0].GetInt();
     power_manager::PowerSupplyProperties props =
         *fake_power_manager_client_->GetLastStatus();
     props.set_battery_time_to_full_sec(new_time);
@@ -415,10 +395,9 @@
 }
 
 void DeviceEmulatorMessageHandler::UpdatePowerSources(
-    const base::ListValue* args) {
-  base::Value::ConstListView args_list = args->GetList();
-  CHECK(!args_list.empty() && args_list[0].is_list());
-  base::Value::ConstListView sources = args_list[0].GetList();
+    const base::Value::ConstListView args) {
+  CHECK(!args.empty() && args[0].is_list());
+  base::Value::ConstListView sources = args[0].GetList();
 
   power_manager::PowerSupplyProperties props =
       *fake_power_manager_client_->GetLastStatus();
@@ -474,83 +453,82 @@
 }
 
 void DeviceEmulatorMessageHandler::UpdatePowerSourceId(
-    const base::ListValue* args) {
-  base::Value::ConstListView args_list = args->GetList();
-  CHECK(!args_list.empty() && args_list[0].is_string());
-  std::string id = args_list[0].GetString();
+    const base::Value::ConstListView args) {
+  CHECK(!args.empty() && args[0].is_string());
+  std::string id = args[0].GetString();
   fake_power_manager_client_->SetPowerSource(id);
 }
 
 void DeviceEmulatorMessageHandler::RegisterMessages() {
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       kInitialize, base::BindRepeating(&DeviceEmulatorMessageHandler::Init,
                                        base::Unretained(this)));
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       kRequestPowerInfo,
       base::BindRepeating(&DeviceEmulatorMessageHandler::RequestPowerInfo,
                           base::Unretained(this)));
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       kUpdateBatteryPercent,
       base::BindRepeating(&DeviceEmulatorMessageHandler::UpdateBatteryPercent,
                           base::Unretained(this)));
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       kUpdateBatteryState,
       base::BindRepeating(&DeviceEmulatorMessageHandler::UpdateBatteryState,
                           base::Unretained(this)));
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       kUpdateTimeToEmpty,
       base::BindRepeating(&DeviceEmulatorMessageHandler::UpdateTimeToEmpty,
                           base::Unretained(this)));
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       kUpdateTimeToFull,
       base::BindRepeating(&DeviceEmulatorMessageHandler::UpdateTimeToFull,
                           base::Unretained(this)));
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       kUpdatePowerSources,
       base::BindRepeating(&DeviceEmulatorMessageHandler::UpdatePowerSources,
                           base::Unretained(this)));
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       kUpdatePowerSourceId,
       base::BindRepeating(&DeviceEmulatorMessageHandler::UpdatePowerSourceId,
                           base::Unretained(this)));
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       kRequestAudioNodes,
       base::BindRepeating(
           &DeviceEmulatorMessageHandler::HandleRequestAudioNodes,
           base::Unretained(this)));
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       kInsertAudioNode,
       base::BindRepeating(&DeviceEmulatorMessageHandler::HandleInsertAudioNode,
                           base::Unretained(this)));
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       kRemoveAudioNode,
       base::BindRepeating(&DeviceEmulatorMessageHandler::HandleRemoveAudioNode,
                           base::Unretained(this)));
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       kBluetoothDiscoverFunction,
       base::BindRepeating(
           &DeviceEmulatorMessageHandler::HandleRequestBluetoothDiscover,
           base::Unretained(this)));
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       kBluetoothPairFunction,
       base::BindRepeating(
           &DeviceEmulatorMessageHandler::HandleRequestBluetoothPair,
           base::Unretained(this)));
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       kRequestBluetoothInfo,
       base::BindRepeating(
           &DeviceEmulatorMessageHandler::HandleRequestBluetoothInfo,
           base::Unretained(this)));
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       kRemoveBluetoothDevice,
       base::BindRepeating(
           &DeviceEmulatorMessageHandler::HandleRemoveBluetoothDevice,
           base::Unretained(this)));
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       kSetHasTouchpad,
       base::BindRepeating(&DeviceEmulatorMessageHandler::HandleSetHasTouchpad,
                           base::Unretained(this)));
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       kSetHasMouse,
       base::BindRepeating(&DeviceEmulatorMessageHandler::HandleSetHasMouse,
                           base::Unretained(this)));
@@ -576,10 +554,10 @@
 }
 
 std::string DeviceEmulatorMessageHandler::CreateBluetoothDeviceFromListValue(
-    const base::ListValue* args) {
+    const base::Value::ConstListView args) {
   bluez::FakeBluetoothDeviceClient::IncomingDeviceProperties props;
 
-  const base::Value& device_value = args->GetList()[0];
+  const base::Value& device_value = args[0];
   CHECK(device_value.is_dict());
   const base::DictionaryValue& device_dict =
       base::Value::AsDictionaryValue(device_value);
@@ -679,4 +657,25 @@
   // TODO(crbug.com/1114828): support fake pointing sticks.
 }
 
+void DeviceEmulatorMessageHandler::UpdateAudioNodes() {
+  // Get every active audio node and create a dictionary to
+  // send it to JavaScript.
+  base::Value audio_nodes(base::Value::Type::LIST);
+  for (const AudioNode& node :
+       chromeos::FakeCrasAudioClient::Get()->node_list()) {
+    base::Value audio_node(base::Value::Type::DICTIONARY);
+
+    audio_node.SetBoolKey("isInput", node.is_input);
+    audio_node.SetStringKey("id", base::NumberToString(node.id));
+    audio_node.SetStringKey("deviceName", node.device_name);
+    audio_node.SetStringKey("type", node.type);
+    audio_node.SetStringKey("name", node.name);
+    audio_node.SetBoolKey("active", node.active);
+
+    audio_nodes.Append(std::move(audio_node));
+  }
+
+  FireWebUIListener(kAudioNodesUpdated, audio_nodes);
+}
+
 }  // namespace chromeos
diff --git a/chrome/browser/ui/webui/chromeos/emulator/device_emulator_message_handler.h b/chrome/browser/ui/webui/chromeos/emulator/device_emulator_message_handler.h
index 029a5f8..77c09e9 100644
--- a/chrome/browser/ui/webui/chromeos/emulator/device_emulator_message_handler.h
+++ b/chrome/browser/ui/webui/chromeos/emulator/device_emulator_message_handler.h
@@ -14,10 +14,6 @@
 #include "content/public/browser/web_ui_message_handler.h"
 #include "device/bluetooth/bluetooth_adapter.h"
 
-namespace base {
-class ListValue;
-}  // namespace base
-
 namespace dbus {
 class ObjectPath;
 }  // namespace dbus
@@ -44,56 +40,56 @@
   ~DeviceEmulatorMessageHandler() override;
 
   // Adds |this| as an observer to all necessary objects.
-  void Init(const base::ListValue* args);
+  void Init(const base::Value::ConstListView args);
 
   // Callback for the "removeBluetoothDevice" message. This is called by
   // the view to remove a bluetooth device from the FakeBluetoothDeviceClient's
   // observed list of devices.
-  void HandleRemoveBluetoothDevice(const base::ListValue* args);
+  void HandleRemoveBluetoothDevice(const base::Value::ConstListView args);
 
   // Callback for the "requestBluetoothDiscover" message. This asynchronously
   // requests for the system to discover a certain device. The device's data
   // should be passed into |args| as a dictionary. If the device does not
   // already exist, then it will be created and attached to the main adapter.
-  void HandleRequestBluetoothDiscover(const base::ListValue* args);
+  void HandleRequestBluetoothDiscover(const base::Value::ConstListView args);
 
   // Callback for the "requestBluetoothInfo" message. This asynchronously
   // requests for the devices which are already paired with the device.
-  void HandleRequestBluetoothInfo(const base::ListValue* args);
+  void HandleRequestBluetoothInfo(const base::Value::ConstListView args);
 
   // Callback for the "requestBluetoothPair" message. This asynchronously
   // requests for the system to pair a certain device. The device's data should
   // be passed into |args| as a dictionary. If the device does not already
   // exist, then it will be created and attached to the main adapter.
-  void HandleRequestBluetoothPair(const base::ListValue* args);
+  void HandleRequestBluetoothPair(const base::Value::ConstListView args);
 
   // Callback for the "requestAudioNodes" message. This asynchronously
   // requests the audio node that is current set to active. It is possible
   // that there can be multiple current active nodes.
-  void HandleRequestAudioNodes(const base::ListValue* args);
+  void HandleRequestAudioNodes(const base::Value::ConstListView args);
 
   // Create a node and add the node to the current AudioNodeList in the
   // FakeCrasAudioClient.
-  void HandleInsertAudioNode(const base::ListValue* args);
+  void HandleInsertAudioNode(const base::Value::ConstListView args);
 
   // Removes an AudioNode from the current list in the FakeCrasAudioClient
   // based on the node id.
-  void HandleRemoveAudioNode(const base::ListValue* args);
+  void HandleRemoveAudioNode(const base::Value::ConstListView args);
 
   // Connects or disconnects a fake touchpad.
-  void HandleSetHasTouchpad(const base::ListValue* args);
+  void HandleSetHasTouchpad(const base::Value::ConstListView args);
 
   // Connects or disconnects a fake mouse.
-  void HandleSetHasMouse(const base::ListValue* args);
+  void HandleSetHasMouse(const base::Value::ConstListView args);
 
   // Callbacks for JS update methods. All these methods work
   // asynchronously.
-  void UpdateBatteryPercent(const base::ListValue* args);
-  void UpdateBatteryState(const base::ListValue* args);
-  void UpdateTimeToEmpty(const base::ListValue* args);
-  void UpdateTimeToFull(const base::ListValue* args);
-  void UpdatePowerSources(const base::ListValue* args);
-  void UpdatePowerSourceId(const base::ListValue* args);
+  void UpdateBatteryPercent(const base::Value::ConstListView args);
+  void UpdateBatteryState(const base::Value::ConstListView args);
+  void UpdateTimeToEmpty(const base::Value::ConstListView args);
+  void UpdateTimeToFull(const base::Value::ConstListView args);
+  void UpdatePowerSources(const base::Value::ConstListView args);
+  void UpdatePowerSourceId(const base::Value::ConstListView args);
 
   // content::WebUIMessageHandler:
   void RegisterMessages() override;
@@ -103,7 +99,7 @@
   // Callback for the "requestPowerInfo" message. This asynchronously requests
   // for power settings such as battery percentage, external power, etc. to
   // update the view.
-  void RequestPowerInfo(const base::ListValue* args);
+  void RequestPowerInfo(const base::Value::ConstListView args);
 
  private:
   class BluetoothObserver;
@@ -117,7 +113,8 @@
   // should contain a dictionary so that each dictionary value can be mapped
   // to its respective property upon creating the device. Returns the device
   // path.
-  std::string CreateBluetoothDeviceFromListValue(const base::ListValue* args);
+  std::string CreateBluetoothDeviceFromListValue(
+      const base::Value::ConstListView args);
 
   // Builds a dictionary with each key representing a property of the device
   // with path |object_path|.
@@ -131,6 +128,8 @@
   void MouseExists(bool exists) override;
   void PointingStickExists(bool exists) override;
 
+  void UpdateAudioNodes();
+
   bluez::FakeBluetoothDeviceClient* fake_bluetooth_device_client_;
   std::unique_ptr<BluetoothObserver> bluetooth_observer_;
 
diff --git a/chrome/browser/ui/webui/chromeos/login/oobe_ui.cc b/chrome/browser/ui/webui/chromeos/login/oobe_ui.cc
index fab668d..8e952f6 100644
--- a/chrome/browser/ui/webui/chromeos/login/oobe_ui.cc
+++ b/chrome/browser/ui/webui/chromeos/login/oobe_ui.cc
@@ -240,8 +240,7 @@
 
 void AddDebuggerResources(content::WebUIDataSource* source) {
   base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
-  bool enable_debugger =
-      command_line->HasSwitch(::chromeos::switches::kShowOobeDevOverlay);
+  bool enable_debugger = command_line->HasSwitch(switches::kShowOobeDevOverlay);
   // Enable for ChromeOS-on-linux for developers and test images.
   if (enable_debugger && base::SysInfo::IsRunningOnChromeOS()) {
     LOG(WARNING) << "OOBE Debug overlay can only be used on test images";
@@ -256,8 +255,7 @@
 
 void AddTestAPIResources(content::WebUIDataSource* source) {
   base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
-  bool enable_test_api =
-      command_line->HasSwitch(::chromeos::switches::kEnableOobeTestAPI);
+  bool enable_test_api = command_line->HasSwitch(switches::kEnableOobeTestAPI);
   if (enable_test_api) {
     source->AddResourcePath(kTestAPIJSPath, IDR_OOBE_TEST_API_JS);
     source->AddResourcePath(kTestAPIJsMPath, IDR_OOBE_TEST_API_M_JS);
@@ -635,8 +633,7 @@
   web_ui->AddMessageHandler(std::make_unique<MetricsHandler>());
 
   base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
-  bool enable_debugger =
-      command_line->HasSwitch(::chromeos::switches::kShowOobeDevOverlay);
+  bool enable_debugger = command_line->HasSwitch(switches::kShowOobeDevOverlay);
   // TODO(crbug.com/1073095): Also enable for ChromeOS test images.
   // Enable for ChromeOS-on-linux for developers.
   bool test_mode = !base::SysInfo::IsRunningOnChromeOS();
@@ -646,8 +643,7 @@
         std::make_unique<DebugOverlayHandler>(js_calls_container_.get()));
   }
 
-  bool enable_test_api =
-      command_line->HasSwitch(::chromeos::switches::kEnableOobeTestAPI);
+  bool enable_test_api = command_line->HasSwitch(switches::kEnableOobeTestAPI);
   if (enable_test_api) {
     AddWebUIHandler(
         std::make_unique<OobeTestAPIHandler>(js_calls_container_.get()));
diff --git a/chrome/browser/ui/webui/flags/flags_ui.cc b/chrome/browser/ui/webui/flags/flags_ui.cc
index b7db9d61..26632b9 100644
--- a/chrome/browser/ui/webui/flags/flags_ui.cc
+++ b/chrome/browser/ui/webui/flags/flags_ui.cc
@@ -127,7 +127,7 @@
 
   // Show a warning info bar when kSafeMode switch is present.
   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
-          chromeos::switches::kSafeMode)) {
+          ash::switches::kSafeMode)) {
     CreateSimpleAlertInfoBar(
         infobars::ContentInfoBarManager::FromWebContents(
             flags_ui->web_ui()->GetWebContents()),
diff --git a/chrome/browser/ui/webui/help/help_utils_chromeos.cc b/chrome/browser/ui/webui/help/help_utils_chromeos.cc
index 81003fd8..baeb212 100644
--- a/chrome/browser/ui/webui/help/help_utils_chromeos.cc
+++ b/chrome/browser/ui/webui/help/help_utils_chromeos.cc
@@ -20,7 +20,7 @@
   // If this is a Cellular First device or the user actively checks for update,
   // the default is to allow updates over cellular.
   bool default_update_over_cellular_allowed =
-      interactive ? true : chromeos::switches::IsCellularFirstDevice();
+      interactive ? true : ash::switches::IsCellularFirstDevice();
 
   // Device Policy overrides the defaults.
   ash::CrosSettings* settings = ash::CrosSettings::Get();
diff --git a/chrome/browser/ui/webui/ntp/app_launcher_handler.cc b/chrome/browser/ui/webui/ntp/app_launcher_handler.cc
index 2ea57d9..a71d04b 100644
--- a/chrome/browser/ui/webui/ntp/app_launcher_handler.cc
+++ b/chrome/browser/ui/webui/ntp/app_launcher_handler.cc
@@ -277,10 +277,11 @@
       base::FeatureList::IsEnabled(features::kDesktopPWAsRunOnOsLogin) &&
           is_locally_installed);
 
+  // TODO(crbug.com/1280773): This should use the WebAppRegistrar to determine
+  // if the user can configure Run on OS Login.
   value->SetBoolean(
       "mayToggleRunOnOsLoginMode",
-      web_app_provider_->policy_manager().GetUrlRunOnOsLoginPolicy(
-          policy_installed_apps_[app_id]) ==
+      web_app_provider_->policy_manager().GetUrlRunOnOsLoginPolicy(app_id) ==
           web_app::RunOnOsLoginPolicy::kAllowed);
 
   std::string runOnOsLoginModeString =
@@ -695,10 +696,6 @@
     }
   }
 
-  policy_installed_apps_ =
-      web_app_provider_->registrar().GetExternallyInstalledApps(
-          web_app::ExternalInstallSource::kExternalPolicy);
-
   FillAppDictionary(&dictionary);
   web_ui()->CallJavascriptFunctionUnsafe("ntp.getAppsCallback", dictionary);
 
diff --git a/chrome/browser/ui/webui/ntp/app_launcher_handler.h b/chrome/browser/ui/webui/ntp/app_launcher_handler.h
index dda9ad9..0541a3b 100644
--- a/chrome/browser/ui/webui/ntp/app_launcher_handler.h
+++ b/chrome/browser/ui/webui/ntp/app_launcher_handler.h
@@ -261,9 +261,6 @@
   // The ids of apps to show on the NTP.
   std::set<std::string> visible_apps_;
 
-  // The ids of apps installed externally.
-  std::map<web_app::AppId, GURL> policy_installed_apps_;
-
   // The id of the extension we are prompting the user about (either enable or
   // uninstall).
   std::string extension_id_prompting_;
diff --git a/chrome/browser/ui/webui/settings/about_handler.cc b/chrome/browser/ui/webui/settings/about_handler.cc
index 4d3faf0..1b7a5b1 100644
--- a/chrome/browser/ui/webui/settings/about_handler.cc
+++ b/chrome/browser/ui/webui/settings/about_handler.cc
@@ -147,7 +147,7 @@
   base::FilePath region_path(kRegulatoryLabelsDirectory);
   const std::string model_subdir =
       base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
-          chromeos::switches::kRegulatoryLabelDir);
+          ash::switches::kRegulatoryLabelDir);
   if (!model_subdir.empty()) {
     region_path = region_path.AppendASCII(model_subdir);
   }
diff --git a/chrome/browser/ui/webui/settings/chromeos/device_keyboard_handler.cc b/chrome/browser/ui/webui/settings/chromeos/device_keyboard_handler.cc
index 8fed478..d6ba540 100644
--- a/chrome/browser/ui/webui/settings/chromeos/device_keyboard_handler.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/device_keyboard_handler.cc
@@ -139,7 +139,7 @@
   const bool has_caps_lock = keyboards_state.has_external_apple_keyboard ||
                              keyboards_state.has_external_generic_keyboard ||
                              !base::CommandLine::ForCurrentProcess()->HasSwitch(
-                                 chromeos::switches::kHasChromeOSKeyboard);
+                                 switches::kHasChromeOSKeyboard);
 
   base::Value keyboard_params(base::Value::Type::DICTIONARY);
   keyboard_params.SetKey("showCapsLock", base::Value(has_caps_lock));
diff --git a/chrome/browser/ui/webui/settings/chromeos/device_keyboard_handler_unittest.cc b/chrome/browser/ui/webui/settings/chromeos/device_keyboard_handler_unittest.cc
index 64b7e71..1a7b7f1 100644
--- a/chrome/browser/ui/webui/settings/chromeos/device_keyboard_handler_unittest.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/device_keyboard_handler_unittest.cc
@@ -169,7 +169,7 @@
 
 TEST_F(KeyboardHandlerTest, DefaultKeys) {
   base::CommandLine::ForCurrentProcess()->AppendSwitch(
-      chromeos::switches::kHasChromeOSKeyboard);
+      switches::kHasChromeOSKeyboard);
   handler_test_api_.Initialize();
   EXPECT_FALSE(HasLauncherKey());
   EXPECT_FALSE(HasCapsLock());
@@ -250,7 +250,7 @@
 
   // An internal keyboard shouldn't change the defaults.
   base::CommandLine::ForCurrentProcess()->AppendSwitch(
-      chromeos::switches::kHasChromeOSKeyboard);
+      switches::kHasChromeOSKeyboard);
   device_data_manager_test_api_.SetKeyboardDevices({internal_kbd});
   handler_test_api_.Initialize();
   EXPECT_TRUE(HasLauncherKey());
diff --git a/chrome/browser/ui/webui/settings/chromeos/privacy_section.cc b/chrome/browser/ui/webui/settings/chromeos/privacy_section.cc
index 1e437fd..d503fc04 100644
--- a/chrome/browser/ui/webui/settings/chromeos/privacy_section.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/privacy_section.cc
@@ -347,9 +347,8 @@
   ::settings::AddPersonalizationOptionsStrings(html_source);
   ::settings::AddSecureDnsStrings(html_source);
 
-  html_source->AddBoolean("isRevenBranding",
-                          chromeos::switches::IsRevenBranding());
-  if (chromeos::switches::IsRevenBranding()) {
+  html_source->AddBoolean("isRevenBranding", switches::IsRevenBranding());
+  if (switches::IsRevenBranding()) {
     html_source->AddString(
         "enableHWDataUsage",
         l10n_util::GetStringFUTF8(
diff --git a/chrome/browser/ui/webui/signin/inline_login_handler.cc b/chrome/browser/ui/webui/signin/inline_login_handler.cc
index d60e6e9..2f32e43 100644
--- a/chrome/browser/ui/webui/signin/inline_login_handler.cc
+++ b/chrome/browser/ui/webui/signin/inline_login_handler.cc
@@ -44,6 +44,13 @@
 
 InlineLoginHandler::CompleteLoginParams::CompleteLoginParams() = default;
 
+InlineLoginHandler::CompleteLoginParams::CompleteLoginParams(
+    const InlineLoginHandler::CompleteLoginParams&) = default;
+
+InlineLoginHandler::CompleteLoginParams&
+InlineLoginHandler::CompleteLoginParams::operator=(
+    const InlineLoginHandler::CompleteLoginParams&) = default;
+
 InlineLoginHandler::CompleteLoginParams::~CompleteLoginParams() = default;
 
 void InlineLoginHandler::RegisterMessages() {
@@ -195,6 +202,8 @@
 
   params.choose_what_to_sync =
       dict.FindBoolKey("chooseWhatToSync").value_or(false);
+  params.is_available_in_arc =
+      dict.FindBoolKey("isAvailableInArc").value_or(false);
 
   CompleteLogin(params);
 }
diff --git a/chrome/browser/ui/webui/signin/inline_login_handler.h b/chrome/browser/ui/webui/signin/inline_login_handler.h
index a13f3bc..de8f41a 100644
--- a/chrome/browser/ui/webui/signin/inline_login_handler.h
+++ b/chrome/browser/ui/webui/signin/inline_login_handler.h
@@ -48,6 +48,8 @@
   // Parameters passed to `CompleteLogin` method.
   struct CompleteLoginParams {
     CompleteLoginParams();
+    CompleteLoginParams(const CompleteLoginParams&);
+    CompleteLoginParams& operator=(const CompleteLoginParams&);
     ~CompleteLoginParams();
 
     std::string email;
@@ -58,6 +60,9 @@
     bool trusted_value = false;
     bool trusted_found = false;
     bool choose_what_to_sync = false;
+    // Whether the account should be available in ARC after addition. Used only
+    // on Chrome OS.
+    bool is_available_in_arc = false;
   };
 
   // Closes the dialog by calling the |inline.login.closeDialog| Javascript
diff --git a/chrome/browser/ui/webui/signin/inline_login_handler_chromeos.cc b/chrome/browser/ui/webui/signin/inline_login_handler_chromeos.cc
index 60a1feb82..32c2a2f01 100644
--- a/chrome/browser/ui/webui/signin/inline_login_handler_chromeos.cc
+++ b/chrome/browser/ui/webui/signin/inline_login_handler_chromeos.cc
@@ -229,12 +229,46 @@
   CHECK(!params.gaia_id.empty());
   CHECK(!params.email.empty());
 
-  // TODO(sinhak): Do not depend on Profile unnecessarily.
+  if (ash::AccountAppsAvailability::IsArcAccountRestrictionsEnabled()) {
+    ::GetAccountManagerFacade(Profile::FromWebUI(web_ui())->GetPath().value())
+        ->GetAccounts(base::BindOnce(
+            &InlineLoginHandlerChromeOS::OnGetAccountsToCompleteLogin,
+            weak_factory_.GetWeakPtr(), params));
+    return;
+  }
+
+  CreateSigninHelper(params, /*arc_helper=*/nullptr);
+}
+
+void InlineLoginHandlerChromeOS::HandleDialogClose(
+    const base::ListValue* args) {
+  close_dialog_closure_.Run();
+}
+
+void InlineLoginHandlerChromeOS::OnGetAccountsToCompleteLogin(
+    const CompleteLoginParams& params,
+    const std::vector<::account_manager::Account>& accounts) {
+  bool is_new_account = !base::Contains(
+      accounts, params.gaia_id,
+      [](const account_manager::Account& account) { return account.key.id(); });
+  bool is_available_in_arc = params.is_available_in_arc;
+  Profile* profile = Profile::FromWebUI(web_ui());
+  if (profile->IsChild())
+    is_available_in_arc = true;
+
+  std::unique_ptr<SigninHelper::ArcHelper> arc_helper =
+      std::make_unique<SigninHelper::ArcHelper>(
+          is_available_in_arc, /*is_account_addition=*/is_new_account,
+          ash::AccountAppsAvailabilityFactory::GetForProfile(
+              Profile::FromWebUI(web_ui())));
+  CreateSigninHelper(params, std::move(arc_helper));
+}
+
+void InlineLoginHandlerChromeOS::CreateSigninHelper(
+    const CompleteLoginParams& params,
+    std::unique_ptr<SigninHelper::ArcHelper> arc_helper) {
   Profile* profile = Profile::FromWebUI(web_ui());
 
-  // TODO(sinhak): Do not depend on Profile unnecessarily. When multiprofile on
-  // Chrome OS is released, get rid of |AccountManagerFactory| and get
-  // AccountManager directly from |g_browser_process|.
   auto* account_manager = g_browser_process->platform_part()
                               ->GetAccountManagerFactory()
                               ->GetAccountManager(profile->GetPath().value());
@@ -250,15 +284,6 @@
       identity_manager->GetPrimaryAccountInfo(signin::ConsentLevel::kSignin)
           .email;
 
-  std::unique_ptr<SigninHelper::ArcHelper> arc_helper;
-  if (ash::AccountAppsAvailability::IsArcAccountRestrictionsEnabled()) {
-    // TODO(crbug.com/1260909): Set `is_available_in_arc` to the value chosen by
-    // the user.
-    arc_helper = std::make_unique<SigninHelper::ArcHelper>(
-        /*is_available_in_arc=*/true,
-        ash::AccountAppsAvailabilityFactory::GetForProfile(profile));
-  }
-
   // Child user added a secondary account.
   if (profile->IsChild() &&
       !gaia::AreEmailsSame(primary_account_email, params.email)) {
@@ -282,11 +307,6 @@
                          params.gaia_id));
 }
 
-void InlineLoginHandlerChromeOS::HandleDialogClose(
-    const base::ListValue* args) {
-  close_dialog_closure_.Run();
-}
-
 void InlineLoginHandlerChromeOS::ShowIncognitoAndCloseDialog(
     const base::ListValue* args) {
   chrome::NewIncognitoWindow(Profile::FromWebUI(web_ui()));
diff --git a/chrome/browser/ui/webui/signin/inline_login_handler_chromeos.h b/chrome/browser/ui/webui/signin/inline_login_handler_chromeos.h
index c515283..ebd25b7 100644
--- a/chrome/browser/ui/webui/signin/inline_login_handler_chromeos.h
+++ b/chrome/browser/ui/webui/signin/inline_login_handler_chromeos.h
@@ -8,6 +8,7 @@
 #include <string>
 
 #include "chrome/browser/ui/webui/signin/inline_login_handler.h"
+#include "chrome/browser/ui/webui/signin/signin_helper_chromeos.h"
 #include "components/account_manager_core/account.h"
 #include "components/account_manager_core/chromeos/account_manager.h"
 #include "google_apis/gaia/gaia_auth_consumer.h"
@@ -37,6 +38,13 @@
   void HandleDialogClose(const base::ListValue* args) override;
 
  private:
+  // A callback for `GetAccounts` invoked from `CompleteLogin`.
+  void OnGetAccountsToCompleteLogin(
+      const CompleteLoginParams& params,
+      const std::vector<::account_manager::Account>& accounts);
+  // Creates a `SigninHelper` instance to complete login of the new account.
+  void CreateSigninHelper(const CompleteLoginParams& params,
+                          std::unique_ptr<SigninHelper::ArcHelper> arc_helper);
   void ShowIncognitoAndCloseDialog(const base::ListValue* args);
   void GetAccountsInSession(const base::ListValue* args);
   void OnGetAccounts(const std::string& callback_id,
diff --git a/chrome/browser/ui/webui/signin/inline_login_handler_chromeos_browsertest.cc b/chrome/browser/ui/webui/signin/inline_login_handler_chromeos_browsertest.cc
new file mode 100644
index 0000000..80057712
--- /dev/null
+++ b/chrome/browser/ui/webui/signin/inline_login_handler_chromeos_browsertest.cc
@@ -0,0 +1,465 @@
+// 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/ui/webui/signin/inline_login_handler_chromeos.h"
+
+#include "ash/constants/ash_features.h"
+#include "base/bind.h"
+#include "base/run_loop.h"
+#include "base/test/bind.h"
+#include "base/test/gmock_callback_support.h"
+#include "base/test/scoped_feature_list.h"
+#include "chrome/browser/ash/account_manager/account_apps_availability.h"
+#include "chrome/browser/ash/account_manager/account_apps_availability_factory.h"
+#include "chrome/browser/ash/login/users/fake_chrome_user_manager.h"
+#include "chrome/browser/ash/profiles/profile_helper.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/browser_process_platform_part.h"
+#include "chrome/browser/signin/identity_manager_factory.h"
+#include "chrome/browser/signin/identity_test_environment_profile_adaptor.h"
+#include "chrome/browser/signin/signin_promo.h"
+#include "chrome/browser/supervised_user/supervised_user_constants.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/webui/chromeos/edu_coexistence/edu_coexistence_login_handler_chromeos.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "chrome/test/base/mixin_based_in_process_browser_test.h"
+#include "chrome/test/base/testing_profile.h"
+#include "components/account_manager_core/account_manager_facade.h"
+#include "components/account_manager_core/chromeos/account_manager_facade_factory.h"
+#include "components/account_manager_core/mock_account_manager_facade.h"
+#include "components/signin/public/identity_manager/accounts_mutator.h"
+#include "components/signin/public/identity_manager/identity_manager.h"
+#include "components/signin/public/identity_manager/identity_test_utils.h"
+#include "components/signin/public/identity_manager/primary_account_mutator.h"
+#include "components/user_manager/scoped_user_manager.h"
+#include "components/user_manager/user_type.h"
+#include "content/public/browser/storage_partition.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/test/browser_test.h"
+#include "content/public/test/test_web_ui.h"
+#include "google_apis/gaia/fake_gaia.h"
+#include "google_apis/gaia/gaia_switches.h"
+#include "google_apis/gaia/gaia_urls.h"
+#include "services/network/public/mojom/cookie_manager.mojom.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+#include "chrome/browser/signin/identity_test_environment_profile_adaptor.h"
+
+namespace chromeos {
+
+namespace {
+
+constexpr char kEmailKey[] = "email";
+constexpr char kPasswordKey[] = "password";
+constexpr char kGaiaIdKey[] = "gaiaId";
+constexpr char kIsAvailableInArcKey[] = "isAvailableInArc";
+constexpr char kSecondaryAccount1Email[] = "secondary1@example.com";
+constexpr char kSecondaryAccountOAuthCode[] = "fake_oauth_code";
+constexpr char kSecondaryAccountRefreshToken[] = "fake_refresh_token";
+constexpr char kCompleteLoginMessage[] = "completeLogin";
+constexpr char kConsentLoggedCallback[] = "consent-logged-callback";
+constexpr char kToSVersion[] = "12345678";
+
+struct DeviceAccountInfo {
+  std::string id;
+  std::string email;
+
+  user_manager::UserType user_type;
+  account_manager::AccountType account_type;
+  std::string token;
+
+  friend std::ostream& operator<<(std::ostream& stream,
+                                  const DeviceAccountInfo& device_account_info);
+};
+
+std::ostream& operator<<(std::ostream& stream,
+                         const DeviceAccountInfo& device_account_info) {
+  return stream << "{email: " << device_account_info.email
+                << ", user_type: " << device_account_info.user_type << "}";
+}
+
+DeviceAccountInfo GetGaiaDeviceAccountInfo() {
+  return {signin::GetTestGaiaIdForEmail("primary@example.com") /*id*/,
+          "primary@example.com" /*email*/,
+          user_manager::USER_TYPE_REGULAR /*user_type*/,
+          account_manager::AccountType::kGaia /*account_type*/,
+          "device-account-token" /*token*/};
+}
+
+DeviceAccountInfo GetChildDeviceAccountInfo() {
+  return {supervised_users::kChildAccountSUID /*id*/,
+          "child@example.com" /*email*/,
+          user_manager::USER_TYPE_CHILD /*user_type*/,
+          account_manager::AccountType::kGaia /*account_type*/,
+          "device-account-token" /*token*/};
+}
+
+base::Value GetCompleteLoginArgs(const std::string& email) {
+  base::Value dict(base::Value::Type::DICTIONARY);
+  dict.SetKey(kEmailKey, base::Value(email));
+  dict.SetKey(kPasswordKey, base::Value("fake password"));
+  dict.SetKey(kGaiaIdKey, base::Value(signin::GetTestGaiaIdForEmail(email)));
+  dict.SetKey(kIsAvailableInArcKey, base::Value(true));
+  return dict;
+}
+
+MATCHER_P(AccountEmailEq, expected_email, "") {
+  return testing::ExplainMatchResult(
+      testing::Field(&account_manager::Account::raw_email,
+                     testing::StrEq(expected_email)),
+      arg, result_listener);
+}
+
+class TestInlineLoginHandlerChromeOS : public InlineLoginHandlerChromeOS {
+ public:
+  TestInlineLoginHandlerChromeOS(
+      const base::RepeatingClosure& close_dialog_closure,
+      content::WebUI* web_ui)
+      : InlineLoginHandlerChromeOS(close_dialog_closure) {
+    set_web_ui(web_ui);
+  }
+
+  TestInlineLoginHandlerChromeOS(const TestInlineLoginHandlerChromeOS&) =
+      delete;
+  TestInlineLoginHandlerChromeOS& operator=(
+      const TestInlineLoginHandlerChromeOS&) = delete;
+};
+
+class MockAccountAppsAvailabilityObserver
+    : public ::ash::AccountAppsAvailability::Observer {
+ public:
+  MockAccountAppsAvailabilityObserver() = default;
+  ~MockAccountAppsAvailabilityObserver() override = default;
+
+  MOCK_METHOD(void,
+              OnAccountAvailableInArc,
+              (const account_manager::Account&),
+              (override));
+  MOCK_METHOD(void,
+              OnAccountUnavailableInArc,
+              (const account_manager::Account&),
+              (override));
+};
+
+}  // namespace
+
+class InlineLoginHandlerChromeOSTest
+    : public InProcessBrowserTest,
+      public testing::WithParamInterface<DeviceAccountInfo> {
+ public:
+  InlineLoginHandlerChromeOSTest()
+      : embedded_test_server_(net::EmbeddedTestServer::TYPE_HTTPS) {
+    embedded_test_server_.RegisterRequestHandler(base::BindRepeating(
+        &FakeGaia::HandleRequest, base::Unretained(&fake_gaia_)));
+  }
+  ~InlineLoginHandlerChromeOSTest() override = default;
+
+  void SetUp() override {
+    ASSERT_TRUE(embedded_test_server_.InitializeAndListen());
+    InProcessBrowserTest::SetUp();
+  }
+
+  void SetUpCommandLine(base::CommandLine* command_line) override {
+    // Configure embedded test server.
+    const GURL& base_url = embedded_test_server_.base_url();
+    command_line->AppendSwitchASCII(switches::kGaiaUrl, base_url.spec());
+    command_line->AppendSwitchASCII(switches::kLsoUrl, base_url.spec());
+    command_line->AppendSwitchASCII(switches::kGoogleApisUrl, base_url.spec());
+    fake_gaia_.Initialize();
+  }
+
+  void SetUpInProcessBrowserTestFixture() override {
+    InProcessBrowserTest::SetUpInProcessBrowserTestFixture();
+
+    create_services_subscription_ =
+        BrowserContextDependencyManager::GetInstance()
+            ->RegisterCreateServicesCallbackForTesting(
+                base::BindRepeating(&InlineLoginHandlerChromeOSTest::
+                                        OnWillCreateBrowserContextServices,
+                                    base::Unretained(this)));
+  }
+
+  void SetUpOnMainThread() override {
+    embedded_test_server_.StartAcceptingConnections();
+
+    // Setup user.
+    auto user_manager = std::make_unique<FakeChromeUserManager>();
+    const user_manager::User* user;
+    if (GetDeviceAccountInfo().user_type ==
+        user_manager::UserType::USER_TYPE_CHILD) {
+      user = user_manager->AddChildUser(AccountId::FromUserEmailGaiaId(
+          GetDeviceAccountInfo().email, GetDeviceAccountInfo().id));
+      profile()->GetPrefs()->SetString(prefs::kSupervisedUserId,
+                                       GetDeviceAccountInfo().id);
+      // This is required for Child users, otherwise an account cannot be added.
+      edu_handler_ =
+          std::make_unique<EduCoexistenceLoginHandler>(base::DoNothing());
+      edu_handler_->set_web_ui_for_test(web_ui());
+      edu_handler_->RegisterMessages();
+    } else {
+      user = user_manager->AddUser(AccountId::FromUserEmailGaiaId(
+          GetDeviceAccountInfo().email, GetDeviceAccountInfo().id));
+    }
+    primary_account_id_ = user->GetAccountId();
+    user_manager->LoginUser(primary_account_id_);
+    user_manager_enabler_ = std::make_unique<user_manager::ScopedUserManager>(
+        std::move(user_manager));
+
+    ProfileHelper::Get()->SetUserToProfileMappingForTesting(user, profile());
+
+    identity_test_env_profile_adaptor_ =
+        std::make_unique<IdentityTestEnvironmentProfileAdaptor>(profile());
+    identity_test_env_profile_adaptor_->identity_test_env()
+        ->MakePrimaryAccountAvailable(GetDeviceAccountInfo().email,
+                                      signin::ConsentLevel::kSignin);
+
+    // Setup web ui with cookies.
+    web_ui_.set_web_contents(web_contents());
+    net::CookieOptions options;
+    options.set_same_site_cookie_context(
+        net::CookieOptions::SameSiteCookieContext::MakeInclusive());
+    auto url = GaiaUrls::GetInstance()->gaia_url();
+    auto cookie_obj = net::CanonicalCookie::Create(
+        url, std::string("oauth_code=") + kSecondaryAccountOAuthCode,
+        base::Time::Now(), absl::nullopt /* server_time */,
+        absl::nullopt /* cookie_partition_key */);
+    content::StoragePartition* partition =
+        signin::GetSigninPartition(web_contents()->GetBrowserContext());
+    base::RunLoop run_loop;
+    partition->GetCookieManagerForBrowserProcess()->SetCanonicalCookie(
+        *cookie_obj, url, options,
+        base::BindLambdaForTesting(
+            [&](net::CookieAccessResult status) { run_loop.Quit(); }));
+    run_loop.Run();
+
+    // Setup fake Gaia.
+    FakeGaia::MergeSessionParams params;
+    params.email = kSecondaryAccount1Email;
+    params.refresh_token = kSecondaryAccountRefreshToken;
+    params.auth_code = kSecondaryAccountOAuthCode;
+    fake_gaia_.UpdateMergeSessionParams(params);
+
+    // Setup handlers.
+    handler_ = std::make_unique<TestInlineLoginHandlerChromeOS>(
+        base::DoNothing(), &web_ui_);
+    handler_->RegisterMessages();
+    handler_->AllowJavascriptForTesting();
+    base::RunLoop().RunUntilIdle();
+  }
+
+  void OnWillCreateBrowserContextServices(content::BrowserContext* context) {
+    IdentityTestEnvironmentProfileAdaptor::
+        SetIdentityTestEnvironmentFactoriesOnBrowserContext(context);
+  }
+
+  void TearDownOnMainThread() override {
+    handler_.reset();
+    edu_handler_.reset();
+    identity_test_env_profile_adaptor_.reset();
+    base::RunLoop().RunUntilIdle();
+    GetFakeUserManager()->RemoveUserFromList(primary_account_id_);
+    user_manager_enabler_.reset();
+  }
+
+  void CompleteConsentLogForChildUser(const std::string& secondary_email) {
+    base::ListValue call_args;
+    call_args.Append(secondary_email);
+    call_args.Append(kToSVersion);
+
+    base::ListValue list_args;
+    list_args.Append(kConsentLoggedCallback);
+    list_args.Append(std::move(call_args));
+
+    web_ui()->HandleReceivedMessage("consentLogged", &list_args);
+  }
+
+  ash::FakeChromeUserManager* GetFakeUserManager() const {
+    return static_cast<ash::FakeChromeUserManager*>(
+        user_manager::UserManager::Get());
+  }
+
+  DeviceAccountInfo GetDeviceAccountInfo() const { return GetParam(); }
+
+  content::WebContents* web_contents() {
+    return browser()->tab_strip_model()->GetActiveWebContents();
+  }
+
+  Profile* profile() { return browser()->profile(); }
+
+  content::TestWebUI* web_ui() { return &web_ui_; }
+
+ private:
+  std::unique_ptr<InlineLoginHandlerChromeOS> handler_;
+  std::unique_ptr<EduCoexistenceLoginHandler> edu_handler_;
+  content::TestWebUI web_ui_;
+  net::EmbeddedTestServer embedded_test_server_;
+  FakeGaia fake_gaia_;
+  std::unique_ptr<user_manager::ScopedUserManager> user_manager_enabler_;
+  AccountId primary_account_id_;
+  std::unique_ptr<IdentityTestEnvironmentProfileAdaptor>
+      identity_test_env_profile_adaptor_;
+  base::CallbackListSubscription create_services_subscription_;
+};
+
+IN_PROC_BROWSER_TEST_P(InlineLoginHandlerChromeOSTest,
+                       NewAccountAdditionSuccess) {
+  account_manager::MockAccountManagerFacadeObserver observer;
+  ::GetAccountManagerFacade(profile()->GetPath().value())
+      ->AddObserver(&observer);
+
+  // Call "completeLogin".
+  base::ListValue args;
+  args.Append(GetCompleteLoginArgs(kSecondaryAccount1Email));
+  web_ui()->HandleReceivedMessage(kCompleteLoginMessage, &args);
+
+  if (GetDeviceAccountInfo().user_type ==
+      user_manager::UserType::USER_TYPE_CHILD) {
+    // Consent logging is required for secondary accounts.
+    CompleteConsentLogForChildUser(kSecondaryAccount1Email);
+  }
+
+  // Wait until account is added.
+  base::RunLoop run_loop;
+  EXPECT_CALL(observer,
+              OnAccountUpserted(AccountEmailEq(kSecondaryAccount1Email)))
+      .WillOnce(base::test::RunClosure(run_loop.QuitClosure()));
+  run_loop.Run();
+
+  ::GetAccountManagerFacade(profile()->GetPath().value())
+      ->RemoveObserver(&observer);
+}
+
+IN_PROC_BROWSER_TEST_P(InlineLoginHandlerChromeOSTest,
+                       PrimaryReauthenticationSuccess) {
+  account_manager::MockAccountManagerFacadeObserver observer;
+  ::GetAccountManagerFacade(profile()->GetPath().value())
+      ->AddObserver(&observer);
+
+  // Call "completeLogin".
+  base::ListValue args;
+  args.Append(GetCompleteLoginArgs(GetDeviceAccountInfo().email));
+  web_ui()->HandleReceivedMessage(kCompleteLoginMessage, &args);
+
+  // Wait until account is added.
+  base::RunLoop run_loop;
+  EXPECT_CALL(observer,
+              OnAccountUpserted(AccountEmailEq(GetDeviceAccountInfo().email)))
+      .WillOnce(base::test::RunClosure(run_loop.QuitClosure()));
+  run_loop.Run();
+
+  ::GetAccountManagerFacade(profile()->GetPath().value())
+      ->RemoveObserver(&observer);
+}
+
+INSTANTIATE_TEST_SUITE_P(InlineLoginHandlerChromeOSTestSuite,
+                         InlineLoginHandlerChromeOSTest,
+                         ::testing::Values(GetGaiaDeviceAccountInfo(),
+                                           GetChildDeviceAccountInfo()));
+
+class InlineLoginHandlerChromeOSTestWithArcRestrictions
+    : public InlineLoginHandlerChromeOSTest {
+ public:
+  InlineLoginHandlerChromeOSTestWithArcRestrictions() {
+    feature_list_.InitWithFeatures(
+        /*enabled_features=*/{chromeos::features::kArcAccountRestrictions,
+                              chromeos::features::kLacrosSupport},
+        /*disabled_features=*/{});
+  }
+
+  ~InlineLoginHandlerChromeOSTestWithArcRestrictions() override = default;
+
+  void SetUpOnMainThread() override {
+    InlineLoginHandlerChromeOSTest::SetUpOnMainThread();
+    // In-session account addition happens when `AccountAppsAvailability` is
+    // already initialized.
+    EXPECT_TRUE(ash::AccountAppsAvailabilityFactory::GetForProfile(profile())
+                    ->IsInitialized());
+  }
+
+ private:
+  base::test::ScopedFeatureList feature_list_;
+};
+
+IN_PROC_BROWSER_TEST_P(InlineLoginHandlerChromeOSTestWithArcRestrictions,
+                       NewAccountAdditionSuccess) {
+  account_manager::MockAccountManagerFacadeObserver observer;
+  ::GetAccountManagerFacade(profile()->GetPath().value())
+      ->AddObserver(&observer);
+
+  MockAccountAppsAvailabilityObserver apps_availability_observer;
+  ash::AccountAppsAvailabilityFactory::GetForProfile(profile())->AddObserver(
+      &apps_availability_observer);
+
+  // Call "completeLogin".
+  base::ListValue args;
+  args.Append(GetCompleteLoginArgs(kSecondaryAccount1Email));
+  web_ui()->HandleReceivedMessage(kCompleteLoginMessage, &args);
+
+  if (GetDeviceAccountInfo().user_type ==
+      user_manager::UserType::USER_TYPE_CHILD) {
+    // Consent logging is required for secondary accounts.
+    CompleteConsentLogForChildUser(kSecondaryAccount1Email);
+  }
+
+  // Wait until account is added.
+  base::RunLoop run_loop;
+  EXPECT_CALL(observer,
+              OnAccountUpserted(AccountEmailEq(kSecondaryAccount1Email)))
+      .WillOnce(base::test::RunClosure(run_loop.QuitClosure()));
+  run_loop.Run();
+
+  // Make sure that account was added to ARC.
+  base::RunLoop run_loop_1;
+  EXPECT_CALL(apps_availability_observer,
+              OnAccountAvailableInArc(AccountEmailEq(kSecondaryAccount1Email)))
+      .WillOnce(base::test::RunClosure(run_loop_1.QuitClosure()));
+  run_loop_1.Run();
+
+  ::GetAccountManagerFacade(profile()->GetPath().value())
+      ->RemoveObserver(&observer);
+  ash::AccountAppsAvailabilityFactory::GetForProfile(profile())->RemoveObserver(
+      &apps_availability_observer);
+}
+
+IN_PROC_BROWSER_TEST_P(InlineLoginHandlerChromeOSTestWithArcRestrictions,
+                       PrimaryReauthenticationSuccess) {
+  account_manager::MockAccountManagerFacadeObserver observer;
+  ::GetAccountManagerFacade(profile()->GetPath().value())
+      ->AddObserver(&observer);
+
+  MockAccountAppsAvailabilityObserver apps_availability_observer;
+  ash::AccountAppsAvailabilityFactory::GetForProfile(profile())->AddObserver(
+      &apps_availability_observer);
+
+  // Call "completeLogin".
+  base::ListValue args;
+  args.Append(GetCompleteLoginArgs(GetDeviceAccountInfo().email));
+  web_ui()->HandleReceivedMessage(kCompleteLoginMessage, &args);
+
+  // Wait until account is added.
+  base::RunLoop run_loop;
+  EXPECT_CALL(observer,
+              OnAccountUpserted(AccountEmailEq(GetDeviceAccountInfo().email)))
+      .WillOnce(base::test::RunClosure(run_loop.QuitClosure()));
+  run_loop.Run();
+
+  // Make sure that ARC availability didn't change for account.
+  EXPECT_CALL(apps_availability_observer, OnAccountAvailableInArc).Times(0);
+  EXPECT_CALL(apps_availability_observer, OnAccountUnavailableInArc).Times(0);
+
+  ::GetAccountManagerFacade(profile()->GetPath().value())
+      ->RemoveObserver(&observer);
+  ash::AccountAppsAvailabilityFactory::GetForProfile(profile())->RemoveObserver(
+      &apps_availability_observer);
+}
+
+INSTANTIATE_TEST_SUITE_P(InlineLoginHandlerChromeOSTestWithArcRestrictionsSuite,
+                         InlineLoginHandlerChromeOSTestWithArcRestrictions,
+                         ::testing::Values(GetGaiaDeviceAccountInfo(),
+                                           GetChildDeviceAccountInfo()));
+
+}  // namespace chromeos
diff --git a/chrome/browser/ui/webui/signin/signin_helper_chromeos.cc b/chrome/browser/ui/webui/signin/signin_helper_chromeos.cc
index 9d8a570..935fd57 100644
--- a/chrome/browser/ui/webui/signin/signin_helper_chromeos.cc
+++ b/chrome/browser/ui/webui/signin/signin_helper_chromeos.cc
@@ -15,16 +15,20 @@
 
 SigninHelper::ArcHelper::ArcHelper(
     bool is_available_in_arc,
+    bool is_account_addition,
     ash::AccountAppsAvailability* account_apps_availability)
     : is_available_in_arc_(is_available_in_arc),
+      is_account_addition_(is_account_addition),
       account_apps_availability_(account_apps_availability) {}
 
 SigninHelper::ArcHelper::~ArcHelper() = default;
 
 void SigninHelper::ArcHelper::OnAccountAdded(
     const account_manager::Account& account) {
-  // TODO(crbug.com/1260909): Call SetIsAccountAvailableInArc only for account
-  // addition.
+  // Don't change ARC availability after reauthentication.
+  if (!is_account_addition_)
+    return;
+
   account_apps_availability_->SetIsAccountAvailableInArc(account,
                                                          is_available_in_arc_);
 }
diff --git a/chrome/browser/ui/webui/signin/signin_helper_chromeos.h b/chrome/browser/ui/webui/signin/signin_helper_chromeos.h
index bb7fc159..53174ed 100644
--- a/chrome/browser/ui/webui/signin/signin_helper_chromeos.h
+++ b/chrome/browser/ui/webui/signin/signin_helper_chromeos.h
@@ -31,7 +31,9 @@
   // after account addition depending on the flags passed in the constructor.
   class ArcHelper {
    public:
+    // If `is_account_addition` is `false` - the account is reauthenticated.
     ArcHelper(bool is_available_in_arc,
+              bool is_account_addition,
               ash::AccountAppsAvailability* account_apps_availability);
     ArcHelper(const ArcHelper&) = delete;
     ArcHelper& operator=(const ArcHelper&) = delete;
@@ -43,6 +45,7 @@
 
    private:
     bool is_available_in_arc_ = false;
+    bool is_account_addition_ = false;
     // A non-owning pointer to AccountAppsAvailability which is a KeyedService
     // and should outlive this class.
     ash::AccountAppsAvailability* account_apps_availability_ = nullptr;
diff --git a/chrome/browser/ui/webui/signin/signin_helper_chromeos_browsertest.cc b/chrome/browser/ui/webui/signin/signin_helper_chromeos_browsertest.cc
index b58105d..4b1d452 100644
--- a/chrome/browser/ui/webui/signin/signin_helper_chromeos_browsertest.cc
+++ b/chrome/browser/ui/webui/signin/signin_helper_chromeos_browsertest.cc
@@ -95,10 +95,20 @@
         factory->GetAccountManagerMojoService(profile->GetPath().value());
     account_manager_->SetUrlLoaderFactoryForTests(shared_url_loader_factory());
     account_manager_->AddObserver(this);
+
+    // Setup the main account:
+    account_manager::AccountKey kPrimaryAccountKey{
+        "primary_account_gaia", account_manager::AccountType::kGaia};
+    account_manager()->UpsertAccount(kPrimaryAccountKey, "primary@gmai.com",
+                                     "access_token");
+    base::RunLoop().RunUntilIdle();
+    on_token_upserted_call_count_ = 0;
+    on_token_upserted_account_ = absl::nullopt;
   }
 
   void TearDownOnMainThread() override {
     account_manager_->RemoveObserver(this);
+    on_token_upserted_call_count_ = 0;
     on_token_upserted_account_ = absl::nullopt;
   }
 
@@ -216,6 +226,9 @@
     account_apps_availability_ =
         ash::AccountAppsAvailabilityFactory::GetForProfile(
             browser()->profile());
+    // In-session account addition happens when `AccountAppsAvailability` is
+    // already initialized.
+    EXPECT_TRUE(account_apps_availability()->IsInitialized());
     account_apps_availability()->AddObserver(this);
   }
 
@@ -239,6 +252,23 @@
         kFakeDeviceId);
   }
 
+  bool IsAccountAvailableInArc(account_manager::Account account) {
+    bool result = false;
+    base::RunLoop run_loop;
+    account_apps_availability()->GetAccountsAvailableInArc(
+        base::BindLambdaForTesting(
+            [&result, &account, &run_loop](
+                const base::flat_set<account_manager::Account>& accounts) {
+              for (const auto& a : accounts) {
+                if (a.raw_email == account.raw_email)
+                  result = true;
+              }
+              run_loop.Quit();
+            }));
+    run_loop.Run();
+    return result;
+  }
+
   ash::AccountAppsAvailability* account_apps_availability() {
     return account_apps_availability_;
   }
@@ -283,11 +313,14 @@
   base::test::ScopedFeatureList feature_list_;
 };
 
+// Account is available in ARC after account addition if `is_available_in_arc`
+// is set to `true`.
 IN_PROC_BROWSER_TEST_F(SigninHelperChromeOSTestWithArcAccountRestrictions,
                        AccountIsAvailableInArcAfterAddition) {
   std::unique_ptr<SigninHelper::ArcHelper> arc_helper =
       std::make_unique<SigninHelper::ArcHelper>(
-          /*is_available_in_arc=*/true, account_apps_availability());
+          /*is_available_in_arc=*/true, /*is_account_addition=*/true,
+          account_apps_availability());
   base::RunLoop close_dialog_closure_run_loop;
   auto* helper = CreateSigninHelper(
       std::move(arc_helper), close_dialog_closure_run_loop.QuitClosure());
@@ -304,17 +337,25 @@
   EXPECT_EQ(account.value().raw_email, kFakeEmail);
   // 1 account should be available in ARC.
   EXPECT_EQ(on_account_unavailable_in_arc_call_count(), 0);
-  EXPECT_EQ(on_account_available_in_arc_call_count(), 1);
+  // Note: after we receive one `OnAccountAvailableInArc` call - we may get
+  // another call after the refresh token is updated for account.
+  EXPECT_GT(on_account_available_in_arc_call_count(), 0);
   auto arc_account = on_account_available_in_arc_account();
   ASSERT_TRUE(arc_account.has_value());
   EXPECT_EQ(arc_account.value().raw_email, kFakeEmail);
+  // `AccountAppsAvailability::GetAccountsAvailableInArc` should return account
+  // list containing this account.
+  EXPECT_TRUE(IsAccountAvailableInArc(account.value()));
 }
 
+// Account is not available in ARC after account addition if
+// `is_available_in_arc` is set to `false`.
 IN_PROC_BROWSER_TEST_F(SigninHelperChromeOSTestWithArcAccountRestrictions,
                        AccountIsNotAvailableInArcAfterAddition) {
   std::unique_ptr<SigninHelper::ArcHelper> arc_helper =
       std::make_unique<SigninHelper::ArcHelper>(
-          /*is_available_in_arc=*/false, account_apps_availability());
+          /*is_available_in_arc=*/false, /*is_account_addition=*/true,
+          account_apps_availability());
   base::RunLoop close_dialog_closure_run_loop;
   auto* helper = CreateSigninHelper(
       std::move(arc_helper),
@@ -332,12 +373,54 @@
   auto account = on_token_upserted_account();
   ASSERT_TRUE(account.has_value());
   EXPECT_EQ(account.value().raw_email, kFakeEmail);
-  // 1 account should be added as "not available in ARC".
+
+  // The account didn't exist (and therefore wasn't available in ARC) before, so
+  // no `OnAccountUnavailableInArc` calls are expected.
   EXPECT_EQ(on_account_available_in_arc_call_count(), 0);
-  EXPECT_EQ(on_account_unavailable_in_arc_call_count(), 1);
-  auto arc_account = on_account_unavailable_in_arc_account();
-  ASSERT_TRUE(arc_account.has_value());
-  EXPECT_EQ(arc_account.value().raw_email, kFakeEmail);
+  EXPECT_EQ(on_account_unavailable_in_arc_call_count(), 0);
+  // `AccountAppsAvailability::GetAccountsAvailableInArc` should return account
+  // list not containing this account.
+  EXPECT_FALSE(IsAccountAvailableInArc(account.value()));
 }
 
+IN_PROC_BROWSER_TEST_F(SigninHelperChromeOSTestWithArcAccountRestrictions,
+                       ArcAvailabilityIsNotSetAfterReauthentication) {
+  account_manager::AccountKey kAccountKey{kFakeGaiaId,
+                                          account_manager::AccountType::kGaia};
+  account_manager()->UpsertAccount(kAccountKey, kFakeEmail, "access_token");
+  base::RunLoop().RunUntilIdle();
+  // 1 account should be added.
+  const int initial_upserted_calls = 1;
+  EXPECT_EQ(on_token_upserted_call_count(), initial_upserted_calls);
+
+  // Go through a reauthentication flow.
+  std::unique_ptr<SigninHelper::ArcHelper> arc_helper =
+      std::make_unique<SigninHelper::ArcHelper>(
+          /*is_available_in_arc=*/true, /*is_account_addition=*/false,
+          account_apps_availability());
+  base::RunLoop close_dialog_closure_run_loop;
+  auto* helper = CreateSigninHelper(
+      std::move(arc_helper),
+      base::BindLambdaForTesting([&close_dialog_closure_run_loop]() {
+        close_dialog_closure_run_loop.Quit();
+      }));
+  // Auth token fetch succeeds.
+  helper->OnClientOAuthSuccess(GetFakeOAuthResult());
+  // Make sure the close_dialog_closure was called.
+  close_dialog_closure_run_loop.Run();
+  // Wait until SigninHelper finishes and deletes itself.
+  base::RunLoop().RunUntilIdle();
+  // 1 account should be updated.
+  EXPECT_EQ(on_token_upserted_call_count(), initial_upserted_calls + 1);
+  auto account = on_token_upserted_account();
+  ASSERT_TRUE(account.has_value());
+  EXPECT_EQ(account.value().raw_email, kFakeEmail);
+  // 0 accounts should be added as "available in ARC".
+  EXPECT_EQ(on_account_available_in_arc_call_count(), 0);
+  EXPECT_EQ(on_account_unavailable_in_arc_call_count(), 0);
+}
+
+IN_PROC_BROWSER_TEST_F(SigninHelperChromeOSTestWithArcAccountRestrictions,
+                       AccountAvailabilityDoesntChangeAfterReauthentication) {}
+
 }  // namespace chromeos
diff --git a/chrome/browser/web_applications/extensions/web_app_policy_manager_unittest.cc b/chrome/browser/web_applications/extensions/web_app_policy_manager_unittest.cc
index 29cac44..979b9b6e 100644
--- a/chrome/browser/web_applications/extensions/web_app_policy_manager_unittest.cc
+++ b/chrome/browser/web_applications/extensions/web_app_policy_manager_unittest.cc
@@ -462,6 +462,12 @@
     install_result_code_ = result_code;
   }
 
+  RunOnOsLoginPolicy GetUrlRunOnOsLoginPolicy(
+      const std::string& unhashed_app_id) {
+    return policy_manager().GetUrlRunOnOsLoginPolicyByUnhashedAppId(
+        unhashed_app_id);
+  }
+
  private:
   InstallResultCode install_result_code_ =
       InstallResultCode::kSuccessNewInstall;
@@ -545,15 +551,13 @@
   SetWebAppSettingsDictPref(kWebAppSettingInvalidDefaultConfiguration);
   policy_manager().Start();
   AwaitPolicyManagerRefreshPolicySettings();
-  EXPECT_EQ(policy_manager().GetUrlRunOnOsLoginPolicy(GURL(kWindowedUrl)),
+  EXPECT_EQ(GetUrlRunOnOsLoginPolicy(kWindowedUrl),
             RunOnOsLoginPolicy::kRunWindowed);
-  EXPECT_EQ(policy_manager().GetUrlRunOnOsLoginPolicy(GURL(kTabbedUrl)),
+  EXPECT_EQ(GetUrlRunOnOsLoginPolicy(kTabbedUrl), RunOnOsLoginPolicy::kAllowed);
+  EXPECT_EQ(GetUrlRunOnOsLoginPolicy(kNoContainerUrl),
             RunOnOsLoginPolicy::kAllowed);
-  EXPECT_EQ(policy_manager().GetUrlRunOnOsLoginPolicy(GURL(kNoContainerUrl)),
+  EXPECT_EQ(GetUrlRunOnOsLoginPolicy("http://foo.example"),
             RunOnOsLoginPolicy::kAllowed);
-  EXPECT_EQ(
-      policy_manager().GetUrlRunOnOsLoginPolicy(GURL("http://foo.example")),
-      RunOnOsLoginPolicy::kAllowed);
 }
 
 TEST_P(WebAppPolicyManagerTest, WebAppSettingsNoDefaultConfiguration) {
@@ -578,15 +582,13 @@
   policy_manager().Start();
   AwaitPolicyManagerRefreshPolicySettings();
 
-  EXPECT_EQ(policy_manager().GetUrlRunOnOsLoginPolicy(GURL(kWindowedUrl)),
+  EXPECT_EQ(GetUrlRunOnOsLoginPolicy(kWindowedUrl),
             RunOnOsLoginPolicy::kRunWindowed);
-  EXPECT_EQ(policy_manager().GetUrlRunOnOsLoginPolicy(GURL(kTabbedUrl)),
-            RunOnOsLoginPolicy::kBlocked);
-  EXPECT_EQ(policy_manager().GetUrlRunOnOsLoginPolicy(GURL(kNoContainerUrl)),
+  EXPECT_EQ(GetUrlRunOnOsLoginPolicy(kTabbedUrl), RunOnOsLoginPolicy::kBlocked);
+  EXPECT_EQ(GetUrlRunOnOsLoginPolicy(kNoContainerUrl),
             RunOnOsLoginPolicy::kAllowed);
-  EXPECT_EQ(
-      policy_manager().GetUrlRunOnOsLoginPolicy(GURL("http://foo.example")),
-      RunOnOsLoginPolicy::kAllowed);
+  EXPECT_EQ(GetUrlRunOnOsLoginPolicy("http://foo.example"),
+            RunOnOsLoginPolicy::kAllowed);
 }
 
 TEST_P(WebAppPolicyManagerTest, WebAppSettingsWithDefaultConfiguration) {
@@ -596,15 +598,13 @@
   policy_manager().Start();
   AwaitPolicyManagerRefreshPolicySettings();
 
-  EXPECT_EQ(policy_manager().GetUrlRunOnOsLoginPolicy(GURL(kWindowedUrl)),
+  EXPECT_EQ(GetUrlRunOnOsLoginPolicy(kWindowedUrl),
             RunOnOsLoginPolicy::kRunWindowed);
-  EXPECT_EQ(policy_manager().GetUrlRunOnOsLoginPolicy(GURL(kTabbedUrl)),
-            RunOnOsLoginPolicy::kAllowed);
-  EXPECT_EQ(policy_manager().GetUrlRunOnOsLoginPolicy(GURL(kNoContainerUrl)),
+  EXPECT_EQ(GetUrlRunOnOsLoginPolicy(kTabbedUrl), RunOnOsLoginPolicy::kAllowed);
+  EXPECT_EQ(GetUrlRunOnOsLoginPolicy(kNoContainerUrl),
             RunOnOsLoginPolicy::kBlocked);
-  EXPECT_EQ(
-      policy_manager().GetUrlRunOnOsLoginPolicy(GURL("http://foo.example")),
-      RunOnOsLoginPolicy::kBlocked);
+  EXPECT_EQ(GetUrlRunOnOsLoginPolicy("http://foo.example"),
+            RunOnOsLoginPolicy::kBlocked);
 }
 
 TEST_P(WebAppPolicyManagerTest, TwoForceInstalledApps) {
@@ -1108,24 +1108,21 @@
   SetWebAppSettingsDictPref(kWebAppSettingInitialConfiguration);
   policy_manager().Start();
   AwaitPolicyManagerRefreshPolicySettings();
-  EXPECT_EQ(policy_manager().GetUrlRunOnOsLoginPolicy(GURL(kWindowedUrl)),
+  EXPECT_EQ(GetUrlRunOnOsLoginPolicy(kWindowedUrl),
             RunOnOsLoginPolicy::kBlocked);
-  EXPECT_EQ(policy_manager().GetUrlRunOnOsLoginPolicy(GURL(kTabbedUrl)),
-            RunOnOsLoginPolicy::kAllowed);
-  EXPECT_EQ(policy_manager().GetUrlRunOnOsLoginPolicy(GURL(kNoContainerUrl)),
+  EXPECT_EQ(GetUrlRunOnOsLoginPolicy(kTabbedUrl), RunOnOsLoginPolicy::kAllowed);
+  EXPECT_EQ(GetUrlRunOnOsLoginPolicy(kNoContainerUrl),
             RunOnOsLoginPolicy::kAllowed);
   EXPECT_EQ(1, mock_observer.GetOnPolicyChangedCalledCount());
 
   SetWebAppSettingsDictPref(kWebAppSettingWithDefaultConfiguration);
-  EXPECT_EQ(policy_manager().GetUrlRunOnOsLoginPolicy(GURL(kWindowedUrl)),
+  EXPECT_EQ(GetUrlRunOnOsLoginPolicy(kWindowedUrl),
             RunOnOsLoginPolicy::kRunWindowed);
-  EXPECT_EQ(policy_manager().GetUrlRunOnOsLoginPolicy(GURL(kTabbedUrl)),
-            RunOnOsLoginPolicy::kAllowed);
-  EXPECT_EQ(policy_manager().GetUrlRunOnOsLoginPolicy(GURL(kNoContainerUrl)),
+  EXPECT_EQ(GetUrlRunOnOsLoginPolicy(kTabbedUrl), RunOnOsLoginPolicy::kAllowed);
+  EXPECT_EQ(GetUrlRunOnOsLoginPolicy(kNoContainerUrl),
             RunOnOsLoginPolicy::kBlocked);
-  EXPECT_EQ(
-      policy_manager().GetUrlRunOnOsLoginPolicy(GURL("http://foo.example")),
-      RunOnOsLoginPolicy::kBlocked);
+  EXPECT_EQ(GetUrlRunOnOsLoginPolicy("http://foo.example"),
+            RunOnOsLoginPolicy::kBlocked);
   EXPECT_EQ(2, mock_observer.GetOnPolicyChangedCalledCount());
   policy_manager().RemoveObserver(&mock_observer);
 }
@@ -1152,11 +1149,10 @@
 
   EXPECT_EQ(install_requests, expected_install_options_list);
 
-  EXPECT_EQ(policy_manager().GetUrlRunOnOsLoginPolicy(GURL(kWindowedUrl)),
+  EXPECT_EQ(GetUrlRunOnOsLoginPolicy(kWindowedUrl),
             RunOnOsLoginPolicy::kAllowed);
-  EXPECT_EQ(policy_manager().GetUrlRunOnOsLoginPolicy(GURL(kTabbedUrl)),
-            RunOnOsLoginPolicy::kAllowed);
-  EXPECT_EQ(policy_manager().GetUrlRunOnOsLoginPolicy(GURL(kNoContainerUrl)),
+  EXPECT_EQ(GetUrlRunOnOsLoginPolicy(kTabbedUrl), RunOnOsLoginPolicy::kAllowed);
+  EXPECT_EQ(GetUrlRunOnOsLoginPolicy(kNoContainerUrl),
             RunOnOsLoginPolicy::kAllowed);
 
   // Now apply WebSettings policy
@@ -1164,15 +1160,13 @@
   policy_manager().AddObserver(&mock_observer);
   SetWebAppSettingsDictPref(kWebAppSettingWithDefaultConfiguration);
   EXPECT_EQ(1, mock_observer.GetOnPolicyChangedCalledCount());
-  EXPECT_EQ(policy_manager().GetUrlRunOnOsLoginPolicy(GURL(kWindowedUrl)),
+  EXPECT_EQ(GetUrlRunOnOsLoginPolicy(kWindowedUrl),
             RunOnOsLoginPolicy::kRunWindowed);
-  EXPECT_EQ(policy_manager().GetUrlRunOnOsLoginPolicy(GURL(kTabbedUrl)),
-            RunOnOsLoginPolicy::kAllowed);
-  EXPECT_EQ(policy_manager().GetUrlRunOnOsLoginPolicy(GURL(kNoContainerUrl)),
+  EXPECT_EQ(GetUrlRunOnOsLoginPolicy(kTabbedUrl), RunOnOsLoginPolicy::kAllowed);
+  EXPECT_EQ(GetUrlRunOnOsLoginPolicy(kNoContainerUrl),
             RunOnOsLoginPolicy::kBlocked);
-  EXPECT_EQ(
-      policy_manager().GetUrlRunOnOsLoginPolicy(GURL("http://foo.example")),
-      RunOnOsLoginPolicy::kBlocked);
+  EXPECT_EQ(GetUrlRunOnOsLoginPolicy("http://foo.example"),
+            RunOnOsLoginPolicy::kBlocked);
   policy_manager().RemoveObserver(&mock_observer);
 }
 
@@ -1186,15 +1180,13 @@
   policy_manager().Start();
   AwaitPolicyManagerAppsSynchronized();
   EXPECT_EQ(1, mock_observer.GetOnPolicyChangedCalledCount());
-  EXPECT_EQ(policy_manager().GetUrlRunOnOsLoginPolicy(GURL(kWindowedUrl)),
+  EXPECT_EQ(GetUrlRunOnOsLoginPolicy(kWindowedUrl),
             RunOnOsLoginPolicy::kRunWindowed);
-  EXPECT_EQ(policy_manager().GetUrlRunOnOsLoginPolicy(GURL(kTabbedUrl)),
-            RunOnOsLoginPolicy::kAllowed);
-  EXPECT_EQ(policy_manager().GetUrlRunOnOsLoginPolicy(GURL(kNoContainerUrl)),
+  EXPECT_EQ(GetUrlRunOnOsLoginPolicy(kTabbedUrl), RunOnOsLoginPolicy::kAllowed);
+  EXPECT_EQ(GetUrlRunOnOsLoginPolicy(kNoContainerUrl),
             RunOnOsLoginPolicy::kBlocked);
-  EXPECT_EQ(
-      policy_manager().GetUrlRunOnOsLoginPolicy(GURL("http://foo.example")),
-      RunOnOsLoginPolicy::kBlocked);
+  EXPECT_EQ(GetUrlRunOnOsLoginPolicy("http://foo.example"),
+            RunOnOsLoginPolicy::kBlocked);
 
   // Now add two sites, one that opens in a window and one that opens in a tab.
   base::Value list(base::Value::Type::LIST);
diff --git a/chrome/browser/web_applications/policy/web_app_policy_manager.cc b/chrome/browser/web_applications/policy/web_app_policy_manager.cc
index 77d974e..b1ce2491 100644
--- a/chrome/browser/web_applications/policy/web_app_policy_manager.cc
+++ b/chrome/browser/web_applications/policy/web_app_policy_manager.cc
@@ -126,9 +126,11 @@
   // No need to install a placeholder because there should be one already.
   install_options.wait_for_windows_closed = true;
   install_options.reinstall_placeholder = true;
-  install_options.run_on_os_login =
-      (GetUrlRunOnOsLoginPolicy(install_options.install_url) ==
-       RunOnOsLoginPolicy::kRunWindowed);
+
+  // TODO(crbug.com/1280773): Reevaluate if this is needed.
+  install_options.run_on_os_login = (GetUrlRunOnOsLoginPolicyByUnhashedAppId(
+                                         install_options.install_url.spec()) ==
+                                     RunOnOsLoginPolicy::kRunWindowed);
 
   // If the app is not a placeholder app, ExternallyManagedAppManager will
   // ignore the request.
@@ -230,8 +232,11 @@
     // apps but only if they are not being used.
     install_options.wait_for_windows_closed = true;
     install_options.reinstall_placeholder = true;
+
+    // TODO(crbug.com/1280773): Reevaluate if this is needed.
     install_options.run_on_os_login =
-        (GetUrlRunOnOsLoginPolicy(install_options.install_url) ==
+        (GetUrlRunOnOsLoginPolicyByUnhashedAppId(
+             install_options.install_url.spec()) ==
          RunOnOsLoginPolicy::kRunWindowed);
 
     absl::optional<AppId> app_id = externally_installed_app_prefs_.LookupAppId(
@@ -301,7 +306,7 @@
 
     WebAppPolicyManager::WebAppSetting by_url(*default_settings_);
     if (by_url.Parse(iter.second, false)) {
-      settings_by_url_[url] = by_url;
+      settings_by_url_[url.spec()] = by_url;
     } else {
       LOG(WARNING) << "Malformed web app settings for " << url;
     }
@@ -314,12 +319,10 @@
 }
 
 void WebAppPolicyManager::ApplyPolicySettings() {
-  std::map<AppId, GURL> policy_installed_apps =
-      app_registrar_->GetExternallyInstalledApps(
-          ExternalInstallSource::kExternalPolicy);
   for (const AppId& app_id : app_registrar_->GetAppIds()) {
-    RunOnOsLoginPolicy policy =
-        GetUrlRunOnOsLoginPolicy(policy_installed_apps[app_id]);
+    // TODO(crbug.com/1280773): Reevaluate if this code should belong here, or
+    // in WebAppRegistrar.
+    RunOnOsLoginPolicy policy = GetUrlRunOnOsLoginPolicy(app_id);
     if (policy == RunOnOsLoginPolicy::kBlocked) {
       sync_bridge_->SetAppRunOnOsLoginMode(app_id, RunOnOsLoginMode::kNotRun);
       OsHooksOptions os_hooks;
@@ -416,12 +419,16 @@
 }
 
 RunOnOsLoginPolicy WebAppPolicyManager::GetUrlRunOnOsLoginPolicy(
-    absl::optional<GURL> url) const {
-  if (url) {
-    auto it = settings_by_url_.find(url.value());
-    if (it != settings_by_url_.end())
-      return it->second.run_on_os_login_policy;
-  }
+    const AppId& app_id) const {
+  return GetUrlRunOnOsLoginPolicyByUnhashedAppId(
+      app_registrar_->GetComputedUnhashedAppId(app_id));
+}
+
+RunOnOsLoginPolicy WebAppPolicyManager::GetUrlRunOnOsLoginPolicyByUnhashedAppId(
+    const std::string& unhashed_app_id) const {
+  auto it = settings_by_url_.find(unhashed_app_id);
+  if (it != settings_by_url_.end())
+    return it->second.run_on_os_login_policy;
   return default_settings_->run_on_os_login_policy;
 }
 
diff --git a/chrome/browser/web_applications/policy/web_app_policy_manager.h b/chrome/browser/web_applications/policy/web_app_policy_manager.h
index 44913e6..f7c93ac8 100644
--- a/chrome/browser/web_applications/policy/web_app_policy_manager.h
+++ b/chrome/browser/web_applications/policy/web_app_policy_manager.h
@@ -84,7 +84,7 @@
   // Checks if UI mode of disabled web apps is hidden.
   bool IsDisabledAppsModeHidden() const;
 
-  RunOnOsLoginPolicy GetUrlRunOnOsLoginPolicy(absl::optional<GURL> url) const;
+  RunOnOsLoginPolicy GetUrlRunOnOsLoginPolicy(const AppId& app_id) const;
 
   void AddObserver(WebAppPolicyManagerObserver* observer);
   void RemoveObserver(WebAppPolicyManagerObserver* observer);
@@ -139,6 +139,8 @@
 
   void OverrideManifest(const GURL& custom_values_key,
                         blink::mojom::ManifestPtr& manifest) const;
+  RunOnOsLoginPolicy GetUrlRunOnOsLoginPolicyByUnhashedAppId(
+      const std::string& unhashed_app_id) const;
 
   // Parses install options from a Value, which represents one entry of the
   // kWepAppInstallForceList. If the value contains a custom_name or
@@ -179,7 +181,7 @@
   bool is_refreshing_ = false;
   bool needs_refresh_ = false;
 
-  base::flat_map<GURL, WebAppSetting> settings_by_url_;
+  base::flat_map<std::string, WebAppSetting> settings_by_url_;
   base::flat_map<GURL, CustomManifestValues> custom_manifest_values_by_url_;
   std::unique_ptr<WebAppSetting> default_settings_;
   base::ObserverList<WebAppPolicyManagerObserver, /*check_empty=*/true>
diff --git a/chrome/browser/web_applications/preinstalled_web_app_manager.cc b/chrome/browser/web_applications/preinstalled_web_app_manager.cc
index 6b0e21f..688bea5 100644
--- a/chrome/browser/web_applications/preinstalled_web_app_manager.cc
+++ b/chrome/browser/web_applications/preinstalled_web_app_manager.cc
@@ -195,7 +195,7 @@
 
   // Remove if device is tablet and app should be disabled.
   if (options.disable_if_tablet_form_factor &&
-      chromeos::switches::IsTabletFormFactor()) {
+      ash::switches::IsTabletFormFactor()) {
     return options.install_url.spec() + " disabled because device is tablet.";
   }
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
@@ -324,7 +324,7 @@
 std::string GetExtraConfigSubdirectory() {
 #if BUILDFLAG(IS_CHROMEOS_ASH)
   return base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
-      chromeos::switches::kExtraWebAppsDir);
+      ash::switches::kExtraWebAppsDir);
 #else
   return std::string();
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
diff --git a/chrome/browser/web_applications/preinstalled_web_app_manager_unittest.cc b/chrome/browser/web_applications/preinstalled_web_app_manager_unittest.cc
index 4d4eece4..c3e6539 100644
--- a/chrome/browser/web_applications/preinstalled_web_app_manager_unittest.cc
+++ b/chrome/browser/web_applications/preinstalled_web_app_manager_unittest.cc
@@ -522,7 +522,7 @@
   // directory apps are loaded from.
   base::test::ScopedCommandLine command_line;
   command_line.GetProcessCommandLine()->AppendSwitchASCII(
-      chromeos::switches::kExtraWebAppsDir, "model1");
+      ash::switches::kExtraWebAppsDir, "model1");
 
   const auto app_infos = LoadApps("extra_web_apps");
   EXPECT_EQ(1u, app_infos.size());
@@ -532,7 +532,7 @@
 TEST_F(PreinstalledWebAppManagerTest, ExtraWebAppsNoMatchingDirectory) {
   base::test::ScopedCommandLine command_line;
   command_line.GetProcessCommandLine()->AppendSwitchASCII(
-      chromeos::switches::kExtraWebAppsDir, "model3");
+      ash::switches::kExtraWebAppsDir, "model3");
 
   const auto app_infos = LoadApps("extra_web_apps");
   EXPECT_EQ(0u, app_infos.size());
diff --git a/chrome/browser/web_applications/preinstalled_web_app_utils_unittest.cc b/chrome/browser/web_applications/preinstalled_web_app_utils_unittest.cc
index dc3b3ca..ee512c4 100644
--- a/chrome/browser/web_applications/preinstalled_web_app_utils_unittest.cc
+++ b/chrome/browser/web_applications/preinstalled_web_app_utils_unittest.cc
@@ -100,7 +100,7 @@
   PreinstalledWebAppUtilsTabletTest() {
     if (GetParam()) {
       base::CommandLine::ForCurrentProcess()->AppendSwitch(
-          chromeos::switches::kEnableTabletFormFactor);
+          ash::switches::kEnableTabletFormFactor);
     }
   }
   ~PreinstalledWebAppUtilsTabletTest() override = default;
@@ -142,7 +142,7 @@
   PreinstalledWebAppUtilsArcTest() {
     if (GetParam()) {
       base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
-          chromeos::switches::kArcAvailability, "officially-supported");
+          ash::switches::kArcAvailability, "officially-supported");
     }
   }
   ~PreinstalledWebAppUtilsArcTest() override = default;
diff --git a/chrome/browser/web_applications/test/profile_test_helper.cc b/chrome/browser/web_applications/test/profile_test_helper.cc
index f4feb8f..1b8bc56b 100644
--- a/chrome/browser/web_applications/test/profile_test_helper.cc
+++ b/chrome/browser/web_applications/test/profile_test_helper.cc
@@ -42,12 +42,11 @@
 
 void ConfigureCommandLineForGuestMode(base::CommandLine* command_line) {
 #if BUILDFLAG(IS_CHROMEOS_ASH)
-  command_line->AppendSwitch(chromeos::switches::kGuestSession);
+  command_line->AppendSwitch(ash::switches::kGuestSession);
   command_line->AppendSwitch(::switches::kIncognito);
-  command_line->AppendSwitchASCII(chromeos::switches::kLoginProfile, "hash");
+  command_line->AppendSwitchASCII(ash::switches::kLoginProfile, "hash");
   command_line->AppendSwitchASCII(
-      chromeos::switches::kLoginUser,
-      user_manager::GuestAccountId().GetUserEmail());
+      ash::switches::kLoginUser, user_manager::GuestAccountId().GetUserEmail());
 #else
   NOTREACHED();
 #endif
diff --git a/chrome/browser/web_applications/web_app_registrar.cc b/chrome/browser/web_applications/web_app_registrar.cc
index aaa48d6..7b05072 100644
--- a/chrome/browser/web_applications/web_app_registrar.cc
+++ b/chrome/browser/web_applications/web_app_registrar.cc
@@ -386,6 +386,14 @@
   return GetAppDisplayMode(app_id);
 }
 
+std::string WebAppRegistrar::GetComputedUnhashedAppId(
+    const AppId& app_id) const {
+  auto* web_app = GetAppById(app_id);
+  return web_app ? GenerateAppIdUnhashed(web_app->manifest_id(),
+                                         web_app->start_url())
+                 : std::string();
+}
+
 bool WebAppRegistrar::IsTabbedWindowModeEnabled(const AppId& app_id) const {
   if (!base::FeatureList::IsEnabled(features::kDesktopPWAsTabStrip))
     return false;
diff --git a/chrome/browser/web_applications/web_app_registrar.h b/chrome/browser/web_applications/web_app_registrar.h
index 30493d4..80097c5 100644
--- a/chrome/browser/web_applications/web_app_registrar.h
+++ b/chrome/browser/web_applications/web_app_registrar.h
@@ -248,6 +248,10 @@
   // entries in the web app manifest.
   DisplayMode GetEffectiveDisplayModeFromManifest(const AppId& app_id) const;
 
+  // Computes and returns the unhashed app id from entries in the web app
+  // manifest.
+  std::string GetComputedUnhashedAppId(const AppId& app_id) const;
+
   // Returns whether the app should be opened in tabbed window mode.
   bool IsTabbedWindowModeEnabled(const AppId& app_id) const;
 
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index 0e94078a..32078baf 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-main-1642507068-a4726d7a8c9f733374bac29e892550c3b71ed890.profdata
+chrome-win32-main-1642517972-b0fbb5175050793ea8ac0b5679bdb492b56bf284.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index 948dbc7..d25dd4a 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-main-1642507068-6d17a8458597bfffcb2374d9e3ef4feaa7c2c3b8.profdata
+chrome-win64-main-1642517972-56e06703002ff94074041f7c00296ab1cf59ed71.profdata
diff --git a/chrome/common/logging_chrome.cc b/chrome/common/logging_chrome.cc
index 78840ec..4e35d60 100644
--- a/chrome/common/logging_chrome.cc
+++ b/chrome/common/logging_chrome.cc
@@ -245,11 +245,11 @@
   if (env->GetVar(env_vars::kSessionLogDir, &log_dir_str) &&
       !log_dir_str.empty()) {
     log_dir = base::FilePath(log_dir_str);
-  } else if (command_line.HasSwitch(chromeos::switches::kLoginProfile)) {
+  } else if (command_line.HasSwitch(ash::switches::kLoginProfile)) {
     base::PathService::Get(chrome::DIR_USER_DATA, &log_dir);
     base::FilePath profile_dir;
     std::string login_profile_value =
-        command_line.GetSwitchValueASCII(chromeos::switches::kLoginProfile);
+        command_line.GetSwitchValueASCII(ash::switches::kLoginProfile);
     if (login_profile_value == chrome::kLegacyProfileDir ||
         login_profile_value == chrome::kTestUserProfileDir) {
       profile_dir = base::FilePath(login_profile_value);
@@ -291,7 +291,7 @@
     // For BWSI (Incognito) logins, we want to put the logs in the user
     // profile directory that is created for the temporary session instead
     // of in the system log directory, for privacy reasons.
-    if (command_line.HasSwitch(chromeos::switches::kGuestSession))
+    if (command_line.HasSwitch(ash::switches::kGuestSession))
       log_path = GetSessionLogFile(command_line);
 
     // On ChromeOS we log to the symlink.  We force creation of a new
diff --git a/chrome/common/pref_names.cc b/chrome/common/pref_names.cc
index 3143e94..ef3e7f9c 100644
--- a/chrome/common/pref_names.cc
+++ b/chrome/common/pref_names.cc
@@ -2631,6 +2631,14 @@
 // to DM Server. Only used on Chromad devices. If this pref is true, the device
 // is ready for the remote migration to cloud management.
 const char kEnrollmentIdUploadedOnChromad[] = "chromad.enrollment_id_uploaded";
+
+// base::Time value indicating the last timestamp when the
+// ActiveDirectoryMigrationManager tried to trigger the migration. This device
+// migration from AD management into cloud management starts with a powerwash.
+// The goal of this pref is to avoid a loop of failed powerwash requests, by
+// adding a backoff time of 1 day between retries.
+const char kLastChromadMigrationAttemptTime[] =
+    "chromad.last_migration_attempt_time";
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
 // *************** SERVICE PREFS ***************
diff --git a/chrome/common/pref_names.h b/chrome/common/pref_names.h
index 25334eb..a50c00a5 100644
--- a/chrome/common/pref_names.h
+++ b/chrome/common/pref_names.h
@@ -905,6 +905,7 @@
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
 extern const char kEnrollmentIdUploadedOnChromad[];
+extern const char kLastChromadMigrationAttemptTime[];
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
 #if !defined(OS_ANDROID)
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 30f1307..6a85ac0 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -977,8 +977,7 @@
 
     if (use_v8_context_snapshot) {
       deps += [ "//tools/v8_context_snapshot:v8_context_snapshot_assets" ]
-    }
-    if (!use_v8_context_snapshot || include_both_v8_snapshots) {
+    } else {
       deps += [ "//v8:v8_external_startup_data_assets" ]
     }
   }
@@ -2221,6 +2220,7 @@
       deps += [
         "//chrome/browser/web_applications/app_service",
         "//chromeos/ui/frame",
+        "//components/account_manager_core:test_support",
       ]
       sources += [
         "../browser/download/notification/download_notification_browsertest.cc",
@@ -3586,6 +3586,7 @@
         "../browser/ui/webui/settings/chromeos/account_manager_handler_browsertest.cc",
         "../browser/ui/webui/settings/chromeos/device_power_handler_browsertest.cc",
         "../browser/ui/webui/signin/inline_login_dialog_chromeos_browsertest.cc",
+        "../browser/ui/webui/signin/inline_login_handler_chromeos_browsertest.cc",
         "../browser/ui/webui/signin/signin_helper_chromeos_browsertest.cc",
         "../browser/ui/window_sizer/window_sizer_chromeos_uitest.cc",
         "../browser/web_applications/app_service/link_capturing_migration_manager_browsertest.cc",
@@ -5874,8 +5875,7 @@
     ]
     if (use_v8_context_snapshot) {
       deps += [ "//tools/v8_context_snapshot:v8_context_snapshot_assets" ]
-    }
-    if (!use_v8_context_snapshot || include_both_v8_snapshots) {
+    } else {
       deps += [ "//v8:v8_external_startup_data_assets" ]
     }
     if (dfmify_dev_ui) {
diff --git a/chrome/test/base/chromeos/ash_browser_test_starter.cc b/chrome/test/base/chromeos/ash_browser_test_starter.cc
index cfcb8b9..ae6bbd1cd 100644
--- a/chrome/test/base/chromeos/ash_browser_test_starter.cc
+++ b/chrome/test/base/chromeos/ash_browser_test_starter.cc
@@ -27,7 +27,7 @@
 
 bool AshBrowserTestStarter::HasLacrosArgument() {
   return base::CommandLine::ForCurrentProcess()->HasSwitch(
-      chromeos::switches::kLacrosChromePath);
+      ash::switches::kLacrosChromePath);
 }
 
 bool AshBrowserTestStarter::PrepareEnvironmentForLacros() {
diff --git a/chrome/test/base/in_process_browser_test.cc b/chrome/test/base/in_process_browser_test.cc
index d296b91..114187c 100644
--- a/chrome/test/base/in_process_browser_test.cc
+++ b/chrome/test/base/in_process_browser_test.cc
@@ -381,14 +381,14 @@
   // (--login-user), or the guest session (--bwsi). This is essentially
   // the same as in `ChromeBrowserMainPartsAsh::PreEarlyInitialization`
   // but it will be done on device and only for tests.
-  if (!command_line->HasSwitch(chromeos::switches::kLoginManager) &&
-      !command_line->HasSwitch(chromeos::switches::kLoginUser) &&
-      !command_line->HasSwitch(chromeos::switches::kGuestSession)) {
+  if (!command_line->HasSwitch(ash::switches::kLoginManager) &&
+      !command_line->HasSwitch(ash::switches::kLoginUser) &&
+      !command_line->HasSwitch(ash::switches::kGuestSession)) {
     command_line->AppendSwitchASCII(
-        chromeos::switches::kLoginUser,
+        ash::switches::kLoginUser,
         cryptohome::Identification(user_manager::StubAccountId()).id());
-    if (!command_line->HasSwitch(chromeos::switches::kLoginProfile)) {
-      command_line->AppendSwitchASCII(chromeos::switches::kLoginProfile,
+    if (!command_line->HasSwitch(ash::switches::kLoginProfile)) {
+      command_line->AppendSwitchASCII(ash::switches::kLoginProfile,
                                       chrome::kTestUserProfileDir);
     }
   }
diff --git a/chrome/test/base/v8_unit_test.cc b/chrome/test/base/v8_unit_test.cc
index dc6807f..b4361129 100644
--- a/chrome/test/base/v8_unit_test.cc
+++ b/chrome/test/base/v8_unit_test.cc
@@ -289,7 +289,7 @@
           .ToLocalChecked();
 
   v8::TryCatch try_catch(isolate);
-  v8::ScriptOrigin origin(name);
+  v8::ScriptOrigin origin(isolate, name);
   v8::Local<v8::Script> script;
   // Ensure the script compiled without errors.
   if (!v8::Script::Compile(context, source, &origin).ToLocal(&script))
diff --git a/chrome/test/data/webui/BUILD.gn b/chrome/test/data/webui/BUILD.gn
index d6d2d8b..d616604 100644
--- a/chrome/test/data/webui/BUILD.gn
+++ b/chrome/test/data/webui/BUILD.gn
@@ -9,6 +9,7 @@
 import("//chrome/test/include_js_tests.gni")
 import("//chromeos/components/chromebox_for_meetings/buildflags/buildflags.gni")
 import("//components/signin/features.gni")
+import("//extensions/buildflags/buildflags.gni")
 import("//printing/buildflags/buildflags.gni")
 import("//third_party/closure_compiler/compile_js.gni")
 import("//tools/grit/grit_rule.gni")
@@ -416,7 +417,6 @@
       "bluetooth_internals/bluetooth_internals_browsertest.js",
       "media/media_engagement_browsertest.js",
       "media/media_history_webui_browsertest.js",
-      "read_later/side_panel/side_panel_browsertest.js",
     ]
 
     deps = [ "//chrome/browser/ui" ]
@@ -449,6 +449,7 @@
       "engagement/site_engagement_browsertest.js",
       "new_tab_page/new_tab_page_browsertest.js",
       "read_later/read_later_browsertest.js",
+      "read_later/side_panel/side_panel_browsertest.js",
       "tab_search/tab_search_browsertest.js",
       "usb_internals_browsertest.js",
     ]
@@ -631,6 +632,11 @@
     "$target_gen_dir/web_ui_test_mojo_resources.grdp",
   ]
 
+  if (enable_extensions) {
+    deps += [ "extensions:build_grdp" ]
+    grdp_files += [ "$target_gen_dir/extensions/resources.grdp" ]
+  }
+
   if (enable_print_preview) {
     deps += [ "print_preview:build_grdp" ]
     grdp_files += [ "$target_gen_dir/print_preview/resources.grdp" ]
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 2d76db5..fce7f33 100644
--- a/chrome/test/data/webui/chromeos/os_feedback_ui/BUILD.gn
+++ b/chrome/test/data/webui/chromeos/os_feedback_ui/BUILD.gn
@@ -14,6 +14,7 @@
   deps = [
     ":confirmation_page_test",
     ":os_feedback_unified_test",
+    ":search_page_test",
   ]
 }
 
@@ -25,5 +26,16 @@
   externs_list = [ "$externs_path/mocha-2.5.js" ]
 }
 
+js_library("search_page_test") {
+  deps = [
+    "../..:chai_assert",
+    "../..:test_util",
+    "//ash/webui/os_feedback_ui/resources:search_page",
+  ]
+  externs_list = [ "$externs_path/mocha-2.5.js" ]
+}
+
 js_library("os_feedback_unified_test") {
+  deps = []
+  externs_list = [ "$externs_path/mocha-2.5.js" ]
 }
diff --git a/chrome/test/data/webui/chromeos/os_feedback_ui/os_feedback_browsertest.js b/chrome/test/data/webui/chromeos/os_feedback_ui/os_feedback_browsertest.js
index 607b86d8..f4c6ce0 100644
--- a/chrome/test/data/webui/chromeos/os_feedback_ui/os_feedback_browsertest.js
+++ b/chrome/test/data/webui/chromeos/os_feedback_ui/os_feedback_browsertest.js
@@ -40,7 +40,8 @@
 // You must register all suites in unified test here as well for consistency,
 // although technically is not necessary.
 const debug_suites_list = [
-  'ConfirmationPageTest',
+  'confirmationPageTest',
+  'searchPageTest',
 ];
 
 TEST_F('OSFeedbackBrowserTest', 'All', function() {
diff --git a/chrome/test/data/webui/chromeos/os_feedback_ui/os_feedback_unified_test.js b/chrome/test/data/webui/chromeos/os_feedback_ui/os_feedback_unified_test.js
index e6fa15d9..2dbef69 100644
--- a/chrome/test/data/webui/chromeos/os_feedback_ui/os_feedback_unified_test.js
+++ b/chrome/test/data/webui/chromeos/os_feedback_ui/os_feedback_unified_test.js
@@ -5,6 +5,7 @@
 import 'chrome://resources/mojo/mojo/public/js/mojo_bindings_lite.js';
 
 import {confirmationPageTest} from './confirmation_page_test.js';
+import {searchPageTestSuite} from './search_page_test.js';
 
 window.test_suites_list = [];
 
@@ -13,4 +14,5 @@
   suite(suiteName, testFn);
 }
 
-runSuite('ConfirmationPageTest', confirmationPageTest);
+runSuite('confirmationPageTest', confirmationPageTest);
+runSuite('searchPageTest', searchPageTestSuite);
diff --git a/chrome/test/data/webui/chromeos/os_feedback_ui/search_page_test.js b/chrome/test/data/webui/chromeos/os_feedback_ui/search_page_test.js
new file mode 100644
index 0000000..c3ca5de
--- /dev/null
+++ b/chrome/test/data/webui/chromeos/os_feedback_ui/search_page_test.js
@@ -0,0 +1,45 @@
+// 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 {SearchPageElement} from 'chrome://os-feedback/search_page.js';
+
+import {assertEquals, assertFalse, assertTrue} from '../../chai_assert.js';
+import {flushTasks} from '../../test_util.js';
+
+export function searchPageTestSuite() {
+  /** @type {?SearchPageElement} */
+  let page = null;
+
+  setup(() => {
+    document.body.innerHTML = '';
+  });
+
+  teardown(() => {
+    page.remove();
+    page = null;
+  });
+
+  function initializePage() {
+    assertFalse(!!page);
+    page =
+        /** @type {!SearchPageElement} */ (
+            document.createElement('search-page'));
+    assertTrue(!!page);
+    document.body.appendChild(page);
+    return flushTasks();
+  }
+
+  test('SearchPageLoaded', () => {
+    return initializePage().then(() => {
+      // Verify the title is in the page
+      const title = page.shadowRoot.querySelector('#title');
+      assertTrue(!!title);
+      assertEquals('Send feedback', title.textContent);
+
+      // Verify the continue button is in the page
+      const btnContinue = page.shadowRoot.querySelector('#btnContinue');
+      assertTrue(!!btnContinue);
+    });
+  });
+}
\ No newline at end of file
diff --git a/chrome/test/data/webui/chromeos/personalization_app/BUILD.gn b/chrome/test/data/webui/chromeos/personalization_app/BUILD.gn
index 46fc303..a793c4ac 100644
--- a/chrome/test/data/webui/chromeos/personalization_app/BUILD.gn
+++ b/chrome/test/data/webui/chromeos/personalization_app/BUILD.gn
@@ -30,6 +30,7 @@
   ]
   in_files = [
     "ambient_subpage_element_test.ts",
+    "google_photos_albums_element_test.ts",
     "google_photos_collection_element_test.ts",
     "google_photos_photos_by_album_id_element_test.ts",
     "local_images_element_test.ts",
@@ -49,6 +50,7 @@
     "user_subpage_element_test.ts",
     "wallpaper_collections_element_test.ts",
     "wallpaper_fullscreen_element_test.ts",
+    "wallpaper_grid_item_element_test.ts",
     "wallpaper_images_element_test.ts",
     "wallpaper_observer_test.ts",
     "wallpaper_preview_element_test.ts",
diff --git a/chrome/test/data/webui/chromeos/personalization_app/google_photos_albums_element_test.ts b/chrome/test/data/webui/chromeos/personalization_app/google_photos_albums_element_test.ts
new file mode 100644
index 0000000..2ab2fd8
--- /dev/null
+++ b/chrome/test/data/webui/chromeos/personalization_app/google_photos_albums_element_test.ts
@@ -0,0 +1,101 @@
+// 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 {GooglePhotosAlbums} from 'chrome://personalization/trusted/wallpaper/google_photos_albums_element.js';
+
+import {getCountText} from 'chrome://personalization/common/utils.js';
+import {GooglePhotosAlbum} from 'chrome://personalization/trusted/personalization_app.mojom-webui.js';
+import {initializeGooglePhotosData} from 'chrome://personalization/trusted/wallpaper/wallpaper_controller.js';
+import {WallpaperGridItem} from 'chrome://personalization/trusted/wallpaper/wallpaper_grid_item_element.js';
+import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
+import {assertEquals} from 'chrome://webui-test/chai_assert.js';
+import {waitAfterNextRender} from 'chrome://webui-test/test_util.js';
+
+import {baseSetup, initElement, teardownElement} from './personalization_app_test_utils.js';
+import {TestPersonalizationStore} from './test_personalization_store.js';
+import {TestWallpaperProvider} from './test_wallpaper_interface_provider.js';
+
+export function GooglePhotosAlbumsTest() {
+  let googlePhotosAlbumsElement: GooglePhotosAlbums|null;
+  let personalizationStore: TestPersonalizationStore;
+  let wallpaperProvider: TestWallpaperProvider;
+
+  /**
+   * Returns all matches for |selector| in |googlePhotosAlbumsElement|'s shadow
+   * DOM.
+   */
+  function querySelectorAll(selector: string): Element[]|null {
+    const matches =
+        googlePhotosAlbumsElement!.shadowRoot!.querySelectorAll(selector);
+    return matches ? [...matches] : null;
+  }
+
+  setup(() => {
+    loadTimeData.overrideValues({'isGooglePhotosIntegrationEnabled': true});
+
+    const mocks = baseSetup();
+    personalizationStore = mocks.personalizationStore;
+    personalizationStore.setReducersEnabled(true);
+    wallpaperProvider = mocks.wallpaperProvider;
+  });
+
+  teardown(async () => {
+    await teardownElement(googlePhotosAlbumsElement);
+    googlePhotosAlbumsElement = null;
+  });
+
+  test('displays albums', async () => {
+    const albums: GooglePhotosAlbum[] = [
+      {
+        id: '9bd1d7a3-f995-4445-be47-53c5b58ce1cb',
+        title: 'Album 0',
+        photoCount: 0,
+        preview: {url: 'foo.com'}
+      },
+      {
+        id: '0ec40478-9712-42e1-b5bf-3e75870ca042',
+        title: 'Album 1',
+        photoCount: 1,
+        preview: {url: 'bar.com'}
+      },
+      {
+        id: '0a268a37-877a-4936-81d4-38cc84b0f596',
+        title: 'Album 2',
+        photoCount: 2,
+        preview: {url: 'baz.com'}
+      }
+    ];
+
+    // Set values returned by |wallpaperProvider|.
+    wallpaperProvider.setGooglePhotosAlbums(albums);
+    wallpaperProvider.setGooglePhotosCount(
+        albums.reduce((photosCount, album) => {
+          photosCount += album.photoCount;
+          return photosCount;
+        }, 0));
+
+    // Initialize |googlePhotosAlbumsElement|.
+    googlePhotosAlbumsElement =
+        initElement(GooglePhotosAlbums, {hidden: false});
+    await waitAfterNextRender(googlePhotosAlbumsElement);
+
+    // The |personalizationStore| should be empty, so no albums should be
+    // rendered initially.
+    const albumSelector = 'wallpaper-grid-item:not([hidden]).album';
+    assertEquals(querySelectorAll(albumSelector)!.length, 0);
+
+    // Initialize Google Photos data in the |personalizationStore|.
+    await initializeGooglePhotosData(wallpaperProvider, personalizationStore);
+    await waitAfterNextRender(googlePhotosAlbumsElement);
+
+    // Verify that the expected |albums| are rendered.
+    const albumEls = querySelectorAll(albumSelector) as WallpaperGridItem[];
+    assertEquals(albumEls.length, albums.length);
+    albumEls.forEach((albumEl, i) => {
+      assertEquals(albumEl.imageSrc, albums[i]!.preview.url);
+      assertEquals(albumEl.primaryText, albums[i]!.title);
+      assertEquals(albumEl.secondaryText, getCountText(albums[i]!.photoCount));
+    });
+  });
+}
diff --git a/chrome/test/data/webui/chromeos/personalization_app/google_photos_photos_by_album_id_element_test.ts b/chrome/test/data/webui/chromeos/personalization_app/google_photos_photos_by_album_id_element_test.ts
index 38d1c678..a051f6870 100644
--- a/chrome/test/data/webui/chromeos/personalization_app/google_photos_photos_by_album_id_element_test.ts
+++ b/chrome/test/data/webui/chromeos/personalization_app/google_photos_photos_by_album_id_element_test.ts
@@ -46,7 +46,7 @@
     await waitAfterNextRender(googlePhotosPhotosByAlbumIdElement);
 
     // Initially no album id selected. Photos should be absent.
-    const photoSelector = 'wallpaper-grid-item:not([hidden]) .photo';
+    const photoSelector = 'wallpaper-grid-item:not([hidden]).photo';
     assertEquals(querySelectorAll(photoSelector)!.length, 0);
 
     const {fetchGooglePhotosAlbum: fetchGooglePhotosAlbumPromise} =
diff --git a/chrome/test/data/webui/chromeos/personalization_app/personalization_app_component_test.ts b/chrome/test/data/webui/chromeos/personalization_app/personalization_app_component_test.ts
index f66db1d1..105ff07 100644
--- a/chrome/test/data/webui/chromeos/personalization_app/personalization_app_component_test.ts
+++ b/chrome/test/data/webui/chromeos/personalization_app/personalization_app_component_test.ts
@@ -6,6 +6,7 @@
 import 'chrome://webui-test/mojo_webui_test_support.js';
 
 import {AmbientSubpageTest} from './ambient_subpage_element_test.js';
+import {GooglePhotosAlbumsTest} from './google_photos_albums_element_test.js';
 import {GooglePhotosCollectionTest} from './google_photos_collection_element_test.js';
 import {GooglePhotosPhotosByAlbumIdTest} from './google_photos_photos_by_album_id_element_test.js';
 import {LocalImagesTest} from './local_images_element_test.js';
@@ -18,6 +19,7 @@
 import {UserSubpageTest} from './user_subpage_element_test.js';
 import {WallpaperCollectionsTest} from './wallpaper_collections_element_test.js';
 import {WallpaperFullscreenTest} from './wallpaper_fullscreen_element_test.js';
+import {WallpaperGridItemTest} from './wallpaper_grid_item_element_test.js';
 import {WallpaperImagesTest} from './wallpaper_images_element_test.js';
 import {WallpaperObserverTest} from './wallpaper_observer_test.js';
 import {WallpaperPreviewTest} from './wallpaper_preview_element_test.js';
@@ -29,6 +31,7 @@
 
 const testCases = [
   AmbientSubpageTest,
+  GooglePhotosAlbumsTest,
   GooglePhotosCollectionTest,
   GooglePhotosPhotosByAlbumIdTest,
   LocalImagesTest,
@@ -41,10 +44,11 @@
   UserSubpageTest,
   WallpaperCollectionsTest,
   WallpaperFullscreenTest,
+  WallpaperGridItemTest,
   WallpaperImagesTest,
+  WallpaperObserverTest,
   WallpaperPreviewTest,
   WallpaperSelectedTest,
-  WallpaperObserverTest,
 ];
 
 for (const testCase of testCases) {
diff --git a/chrome/test/data/webui/chromeos/personalization_app/personalization_app_controller_test.ts b/chrome/test/data/webui/chromeos/personalization_app/personalization_app_controller_test.ts
index 8447d77..16a3f2d 100644
--- a/chrome/test/data/webui/chromeos/personalization_app/personalization_app_controller_test.ts
+++ b/chrome/test/data/webui/chromeos/personalization_app/personalization_app_controller_test.ts
@@ -5,7 +5,7 @@
 import 'chrome://personalization/strings.m.js';
 import 'chrome://webui-test/mojo_webui_test_support.js';
 
-import {WallpaperCollection} from 'chrome://personalization/trusted/personalization_app.mojom-webui.js';
+import {GooglePhotosAlbum} from 'chrome://personalization/trusted/personalization_app.mojom-webui.js';
 import * as wallpaperAction from 'chrome://personalization/trusted/wallpaper/wallpaper_actions.js';
 import {fetchCollections, fetchGooglePhotosAlbum, fetchLocalData, getLocalImages, initializeBackdropData, initializeGooglePhotosData, selectWallpaper} from 'chrome://personalization/trusted/wallpaper/wallpaper_controller.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
@@ -190,7 +190,7 @@
   });
 
   test('sets Google Photos album in store', async () => {
-    const album = new WallpaperCollection();
+    const album = new GooglePhotosAlbum();
     album.id = '9bd1d7a3-f995-4445-be47-53c5b58ce1cb';
 
     // Attempts to `fetchGooglePhotosAlbum()` will fail unless the entire list
diff --git a/chrome/test/data/webui/chromeos/personalization_app/personalization_breadcrumb_element_test.ts b/chrome/test/data/webui/chromeos/personalization_app/personalization_breadcrumb_element_test.ts
index f74d4088..db2d729b 100644
--- a/chrome/test/data/webui/chromeos/personalization_app/personalization_breadcrumb_element_test.ts
+++ b/chrome/test/data/webui/chromeos/personalization_app/personalization_breadcrumb_element_test.ts
@@ -4,7 +4,7 @@
 
 /** @fileoverview Test suite for wallpaper-breadcrumb component.  */
 
-import {WallpaperCollection} from 'chrome://personalization/trusted/personalization_app.mojom-webui.js';
+import {GooglePhotosAlbum} from 'chrome://personalization/trusted/personalization_app.mojom-webui.js';
 import {PersonalizationBreadcrumb} from 'chrome://personalization/trusted/personalization_breadcrumb_element.js';
 import {Paths} from 'chrome://personalization/trusted/personalization_router_element.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
@@ -100,9 +100,9 @@
     // integration feature flag is enabled.
     loadTimeData.overrideValues({'googlePhotosLabel': 'Google Photos'});
 
-    const googlePhotosAlbum = new WallpaperCollection();
+    const googlePhotosAlbum = new GooglePhotosAlbum();
     googlePhotosAlbum.id = '9bd1d7a3-f995-4445-be47-53c5b58ce1cb';
-    googlePhotosAlbum.name = 'Album 0';
+    googlePhotosAlbum.title = 'Album 0';
 
     personalizationStore.data.wallpaper.googlePhotos.albums =
         [googlePhotosAlbum];
@@ -118,7 +118,7 @@
     assertTrue(!!breadcrumbContainer && !breadcrumbContainer.hidden);
     assertBreadcrumbs(breadcrumbContainer!, [
       breadcrumbElement.i18n('wallpaperLabel'),
-      breadcrumbElement.i18n('googlePhotosLabel'), googlePhotosAlbum.name
+      breadcrumbElement.i18n('googlePhotosLabel'), googlePhotosAlbum.title
     ]);
   });
 
diff --git a/chrome/test/data/webui/chromeos/personalization_app/test_wallpaper_interface_provider.ts b/chrome/test/data/webui/chromeos/personalization_app/test_wallpaper_interface_provider.ts
index 50a77b3..ed3cb6c 100644
--- a/chrome/test/data/webui/chromeos/personalization_app/test_wallpaper_interface_provider.ts
+++ b/chrome/test/data/webui/chromeos/personalization_app/test_wallpaper_interface_provider.ts
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {CurrentWallpaper, FetchGooglePhotosAlbumsResponse, OnlineImageType, WallpaperCollection, WallpaperImage, WallpaperLayout, WallpaperObserverInterface, WallpaperObserverRemote, WallpaperProviderInterface, WallpaperType} from 'chrome://personalization/trusted/personalization_app.mojom-webui.js';
+import {CurrentWallpaper, FetchGooglePhotosAlbumsResponse, GooglePhotosAlbum, OnlineImageType, WallpaperCollection, WallpaperImage, WallpaperLayout, WallpaperObserverInterface, WallpaperObserverRemote, WallpaperProviderInterface, WallpaperType} from 'chrome://personalization/trusted/personalization_app.mojom-webui.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
 import {FilePath} from 'chrome://resources/mojo/mojo/public/mojom/base/file_path.mojom-webui.js';
 import {assertTrue} from 'chrome://webui-test/chai_assert.js';
@@ -93,6 +93,8 @@
 
   private collections_: WallpaperCollection[]|null;
   private images_: WallpaperImage[]|null;
+  private googlePhotosAlbums_: GooglePhotosAlbum[]|undefined = [];
+  private googlePhotosCount_: number = 0;
   localImages: FilePath[]|null;
   localImageData: Record<string, string>;
   currentWallpaper: CurrentWallpaper;
@@ -134,17 +136,19 @@
     this.methodCalled('fetchGooglePhotosAlbums');
     const response = new FetchGooglePhotosAlbumsResponse();
     response.albums =
-        loadTimeData.getBoolean('isGooglePhotosIntegrationEnabled') ? [] :
-                                                                      undefined;
+        loadTimeData.getBoolean('isGooglePhotosIntegrationEnabled') ?
+        this.googlePhotosAlbums_ :
+        undefined;
     response.resumeToken = undefined;
     return Promise.resolve({response});
   }
 
   fetchGooglePhotosCount() {
     this.methodCalled('fetchGooglePhotosCount');
-    const count =
-        loadTimeData.getBoolean('isGooglePhotosIntegrationEnabled') ? 0 : -1;
-    return Promise.resolve({count: count});
+    const count = loadTimeData.getBoolean('isGooglePhotosIntegrationEnabled') ?
+        this.googlePhotosCount_ :
+        -1;
+    return Promise.resolve({count});
   }
 
   getLocalImages() {
@@ -215,6 +219,14 @@
     this.collections_ = null;
   }
 
+  setGooglePhotosAlbums(googlePhotosAlbums: GooglePhotosAlbum[]|undefined) {
+    this.googlePhotosAlbums_ = googlePhotosAlbums;
+  }
+
+  setGooglePhotosCount(googlePhotosCount: number) {
+    this.googlePhotosCount_ = googlePhotosCount;
+  }
+
   setImages(images: WallpaperImage[]) {
     this.images_ = images;
   }
diff --git a/chrome/test/data/webui/chromeos/personalization_app/wallpaper_grid_item_element_test.ts b/chrome/test/data/webui/chromeos/personalization_app/wallpaper_grid_item_element_test.ts
new file mode 100644
index 0000000..25a8875
--- /dev/null
+++ b/chrome/test/data/webui/chromeos/personalization_app/wallpaper_grid_item_element_test.ts
@@ -0,0 +1,81 @@
+// 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 {WallpaperGridItem} from 'chrome://personalization/trusted/wallpaper/wallpaper_grid_item_element.js';
+
+import {assertEquals, assertNotEquals} from 'chrome://webui-test/chai_assert.js';
+import {waitAfterNextRender} from 'chrome://webui-test/test_util.js';
+
+import {initElement, teardownElement} from './personalization_app_test_utils.js';
+
+export function WallpaperGridItemTest() {
+  let wallpaperGridItemElement: WallpaperGridItem|null;
+
+  /**
+   * Returns the match for |selector| in |wallpaperGridItemElement|'s shadow
+   * DOM.
+   */
+  function querySelector(selector: string): Element|null {
+    return wallpaperGridItemElement!.shadowRoot!.querySelector(selector);
+  }
+
+  teardown(async () => {
+    await teardownElement(wallpaperGridItemElement);
+    wallpaperGridItemElement = null;
+  });
+
+  test('displays empty state', async () => {
+    // Initialize |wallpaperGridItemElement|.
+    wallpaperGridItemElement = initElement(WallpaperGridItem);
+    await waitAfterNextRender(wallpaperGridItemElement);
+
+    // Verify state.
+    assertEquals(querySelector('img'), null);
+    assertEquals(querySelector('.text'), null);
+    assertEquals(querySelector('.primary-text'), null);
+    assertEquals(querySelector('.secondary-text'), null);
+  });
+
+  test('displays image', async () => {
+    const imageSrc = 'foo.com';
+
+    // Initialize |wallpaperGridItemElement|.
+    wallpaperGridItemElement = initElement(WallpaperGridItem, {imageSrc});
+    await waitAfterNextRender(wallpaperGridItemElement);
+
+    // Verify state.
+    assertEquals(querySelector('img')?.getAttribute('auto-src'), imageSrc);
+    assertEquals(querySelector('.text'), null);
+    assertEquals(querySelector('.primary-text'), null);
+    assertEquals(querySelector('.secondary-text'), null);
+  });
+
+  test('displays primary text', async () => {
+    const primaryText = 'foo';
+
+    // Initialize |wallpaperGridItemElement|.
+    wallpaperGridItemElement = initElement(WallpaperGridItem, {primaryText});
+    await waitAfterNextRender(wallpaperGridItemElement);
+
+    // Verify state.
+    assertEquals(querySelector('img'), null);
+    assertNotEquals(querySelector('.text'), null);
+    assertEquals(querySelector('.primary-text')?.innerHTML, primaryText);
+    assertEquals(querySelector('.secondary-text'), null);
+  });
+
+  test('displays secondary text', async () => {
+    const secondaryText = 'foo';
+
+    // Initialize |wallpaperGridItemElement|.
+    wallpaperGridItemElement = initElement(WallpaperGridItem, {secondaryText});
+    await waitAfterNextRender(wallpaperGridItemElement);
+
+    // Verify state.
+    assertEquals(querySelector('img'), null);
+    assertNotEquals(querySelector('.text'), null);
+    assertEquals(querySelector('.primary-text'), null);
+    assertEquals(querySelector('.secondary-text')?.innerHTML, secondaryText);
+  });
+}
diff --git a/chrome/test/data/webui/chromeos/shimless_rma/onboarding_landing_page_test.js b/chrome/test/data/webui/chromeos/shimless_rma/onboarding_landing_page_test.js
index a9f7fd3..bdb711a 100644
--- a/chrome/test/data/webui/chromeos/shimless_rma/onboarding_landing_page_test.js
+++ b/chrome/test/data/webui/chromeos/shimless_rma/onboarding_landing_page_test.js
@@ -140,8 +140,13 @@
     assertFalse(isVisible(error));
   });
 
-  test('OnBoardingPageValidationFailedWarningAndErrorVisible', async () => {
+  test('OnBoardingPageValidationFailedWarning', async () => {
     await initializeLandingPage();
+
+    // Link should not be created before the failure occurs.
+    assertFalse(
+        !!component.shadowRoot.querySelector('#unqualifiedComponentsLink'));
+
     service.triggerHardwareVerificationStatusObserver(false, 'FAILURE', 0);
     await flushTasks();
 
@@ -151,8 +156,25 @@
     const error = component.shadowRoot.querySelector('#errorMessage');
     assertTrue(busy.hidden);
     assertTrue(isVisible(verification));
-    assertEquals('shimless-icon:warning', verification.icon);
-    assertTrue(isVisible(error));
-    assertEquals('FAILURE', error.innerHTML);
+    assertTrue(
+        !!component.shadowRoot.querySelector('#unqualifiedComponentsLink'));
+  });
+
+  test('OnBoardingPageValidationFailedOpenDialog', async () => {
+    await initializeLandingPage();
+
+    const failedComponent = 'Keyboard';
+    service.triggerHardwareVerificationStatusObserver(
+        false, failedComponent, 0);
+    await flushTasks();
+
+    component.shadowRoot.querySelector('#unqualifiedComponentsLink').click();
+
+    assertTrue(
+        component.shadowRoot.querySelector('#unqualifiedComponentsDialog')
+            .open);
+    assertEquals(
+        failedComponent,
+        component.shadowRoot.querySelector('#dialogBody').textContent.trim());
   });
 }
diff --git a/chrome/test/data/webui/extensions/BUILD.gn b/chrome/test/data/webui/extensions/BUILD.gn
new file mode 100644
index 0000000..96cc330
--- /dev/null
+++ b/chrome/test/data/webui/extensions/BUILD.gn
@@ -0,0 +1,74 @@
+# 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("//extensions/buildflags/buildflags.gni")
+import("//tools/typescript/ts_library.gni")
+import("//ui/webui/resources/tools/generate_grd.gni")
+
+assert(enable_extensions, "enable extensions check failed")
+
+generate_grd("build_grdp") {
+  grd_prefix = "webui_extensions"
+  out_grd = "$target_gen_dir/resources.grdp"
+
+  deps = [ ":build_ts" ]
+  manifest_files = [ "$target_gen_dir/tsconfig.manifest" ]
+  resource_path_prefix = "extensions"
+}
+
+ts_library("build_ts") {
+  root_dir = "./"
+  out_dir = "$target_gen_dir/tsc"
+  tsconfig_base = "tsconfig_base.json"
+  path_mappings = [
+    "chrome://extensions/*|" +
+        rebase_path("$root_gen_dir/chrome/browser/resources/extensions/tsc/*",
+                    target_gen_dir),
+    "chrome://webui-test/*|" +
+        rebase_path("$root_gen_dir/chrome/test/data/webui/tsc/*",
+                    target_gen_dir),
+  ]
+  in_files = [
+    "activity_log_history_item_test.js",
+    "activity_log_history_test.js",
+    "activity_log_stream_item_test.js",
+    "activity_log_stream_test.js",
+    "activity_log_test.js",
+    "code_section_test.js",
+    "detail_view_test.js",
+    "error_console_test.js",
+    "error_page_test.js",
+    "extension_options_dialog_test.js",
+    "host_permissions_toggle_list_test.js",
+    "item_list_test.js",
+    "item_test.js",
+    "keyboard_shortcuts_test.js",
+    "kiosk_mode_manager_unit_test.js",
+    "kiosk_mode_test.js",
+    "load_error_test.js",
+    "manager_test.js",
+    "manager_test_with_activity_log_flag.js",
+    "manager_test_with_id_query_param.js",
+    "manager_unit_test.js",
+    "manager_unit_test_with_activity_log_flag.js",
+    "navigation_helper_test.js",
+    "options_dialog_test.js",
+    "pack_dialog_test.js",
+    "runtime_host_permissions_test.js",
+    "runtime_hosts_dialog_test.js",
+    "shortcut_input_test.js",
+    "sidebar_test.js",
+    "test_kiosk_browser_proxy.js",
+    "test_service.js",
+    "test_util.js",
+    "toggle_row_test.js",
+    "toolbar_test.js",
+  ]
+  deps = [ "//chrome/browser/resources/extensions:build_ts" ]
+  definitions = [
+    "//tools/typescript/definitions/developer_private.d.ts",
+    "//tools/typescript/definitions/activity_log_private.d.ts",
+  ]
+  extra_deps = [ "..:generate_definitions" ]
+}
diff --git a/chrome/test/data/webui/extensions/code_section_test.js b/chrome/test/data/webui/extensions/code_section_test.js
index c36343e..b889079 100644
--- a/chrome/test/data/webui/extensions/code_section_test.js
+++ b/chrome/test/data/webui/extensions/code_section_test.js
@@ -6,7 +6,7 @@
 import 'chrome://extensions/extensions.js';
 
 import {assert} from 'chrome://resources/js/assert.m.js';
-import {isChildVisible} from '../test_util.js';
+import {isChildVisible} from 'chrome://webui-test/test_util.js';
 
 window.extension_code_section_tests = {};
 extension_code_section_tests.suiteName = 'ExtensionCodeSectionTest';
diff --git a/chrome/test/data/webui/extensions/cr_extensions_browsertest.js b/chrome/test/data/webui/extensions/cr_extensions_browsertest.js
index 3a934b3..e44671b 100644
--- a/chrome/test/data/webui/extensions/cr_extensions_browsertest.js
+++ b/chrome/test/data/webui/extensions/cr_extensions_browsertest.js
@@ -59,7 +59,7 @@
 var CrExtensionsSidebarTest = class extends CrExtensionsBrowserTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://extensions/test_loader.html?module=extensions/sidebar_test.js';
+    return 'chrome://extensions/test_loader.html?module=extensions/sidebar_test.js&host=webui-test';
   }
 
   /** @override */
@@ -82,7 +82,7 @@
 var CrExtensionsToolbarTest = class extends CrExtensionsBrowserTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://extensions/test_loader.html?module=extensions/toolbar_test.js';
+    return 'chrome://extensions/test_loader.html?module=extensions/toolbar_test.js&host=webui-test';
   }
 
   /** @override */
@@ -126,7 +126,7 @@
 var CrExtensionsItemsTest = class extends CrExtensionsBrowserTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://extensions/test_loader.html?module=extensions/item_test.js';
+    return 'chrome://extensions/test_loader.html?module=extensions/item_test.js&host=webui-test';
   }
 
   /** @override */
@@ -183,7 +183,7 @@
 var CrExtensionsActivityLogTest = class extends CrExtensionsBrowserTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://extensions/test_loader.html?module=extensions/activity_log_test.js';
+    return 'chrome://extensions/test_loader.html?module=extensions/activity_log_test.js&host=webui-test';
   }
 };
 
@@ -197,7 +197,7 @@
 var CrExtensionsActivityLogHistoryTest = class extends CrExtensionsBrowserTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://extensions/test_loader.html?module=extensions/activity_log_history_test.js';
+    return 'chrome://extensions/test_loader.html?module=extensions/activity_log_history_test.js&host=webui-test';
   }
 };
 
@@ -212,7 +212,7 @@
     class extends CrExtensionsBrowserTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://extensions/test_loader.html?module=extensions/activity_log_history_item_test.js';
+    return 'chrome://extensions/test_loader.html?module=extensions/activity_log_history_item_test.js&host=webui-test';
   }
 };
 
@@ -226,7 +226,7 @@
 var CrExtensionsActivityLogStreamTest = class extends CrExtensionsBrowserTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://extensions/test_loader.html?module=extensions/activity_log_stream_test.js';
+    return 'chrome://extensions/test_loader.html?module=extensions/activity_log_stream_test.js&host=webui-test';
   }
 };
 
@@ -241,7 +241,7 @@
     class extends CrExtensionsBrowserTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://extensions/test_loader.html?module=extensions/activity_log_stream_item_test.js';
+    return 'chrome://extensions/test_loader.html?module=extensions/activity_log_stream_item_test.js&host=webui-test';
   }
 };
 
@@ -255,7 +255,7 @@
 var CrExtensionsDetailViewTest = class extends CrExtensionsBrowserTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://extensions/test_loader.html?module=extensions/detail_view_test.js';
+    return 'chrome://extensions/test_loader.html?module=extensions/detail_view_test.js&host=webui-test';
   }
 
   /** @override */
@@ -296,7 +296,7 @@
 var CrExtensionsItemListTest = class extends CrExtensionsBrowserTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://extensions/test_loader.html?module=extensions/item_list_test.js';
+    return 'chrome://extensions/test_loader.html?module=extensions/item_list_test.js&host=webui-test';
   }
 
   /** @override */
@@ -327,7 +327,7 @@
 var CrExtensionsLoadErrorTest = class extends CrExtensionsBrowserTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://extensions/test_loader.html?module=extensions/load_error_test.js';
+    return 'chrome://extensions/test_loader.html?module=extensions/load_error_test.js&host=webui-test';
   }
 
   /** @override */
@@ -354,7 +354,7 @@
 var CrExtensionsManagerUnitTest = class extends CrExtensionsBrowserTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://extensions/test_loader.html?module=extensions/manager_unit_test.js';
+    return 'chrome://extensions/test_loader.html?module=extensions/manager_unit_test.js&host=webui-test';
   }
 
   /** @override */
@@ -402,7 +402,7 @@
     class extends CrExtensionsManagerUnitTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://extensions/test_loader.html?module=extensions/manager_unit_test_with_activity_log_flag.js';
+    return 'chrome://extensions/test_loader.html?module=extensions/manager_unit_test_with_activity_log_flag.js&host=webui-test';
   }
 
   /** @override */
@@ -425,7 +425,7 @@
     class extends CrExtensionsManagerUnitTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://extensions/test_loader.html?module=extensions/kiosk_mode_manager_unit_test.js';
+    return 'chrome://extensions/test_loader.html?module=extensions/kiosk_mode_manager_unit_test.js&host=webui-test';
   }
 };
 
@@ -438,7 +438,7 @@
     class extends CrExtensionsBrowserTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://extensions/test_loader.html?module=extensions/manager_test.js';
+    return 'chrome://extensions/test_loader.html?module=extensions/manager_test.js&host=webui-test';
   }
 
   /** @override */
@@ -497,7 +497,7 @@
     class extends CrExtensionsBrowserTestWithInstalledExtension {
   /** @override */
   get browsePreload() {
-    return 'chrome://extensions/test_loader.html?module=extensions/manager_test_with_id_query_param.js';
+    return 'chrome://extensions/test_loader.html?module=extensions/manager_test_with_id_query_param.js&host=webui-test';
   }
 
   /** @override */
@@ -525,7 +525,7 @@
     class extends CrExtensionsManagerTestWithIdQueryParam {
   /** @override */
   get browsePreload() {
-    return 'chrome://extensions/test_loader.html?module=extensions/manager_test_with_activity_log_flag.js';
+    return 'chrome://extensions/test_loader.html?module=extensions/manager_test_with_activity_log_flag.js&host=webui-test';
   }
 
   /** @override */
@@ -549,7 +549,7 @@
 var CrExtensionsShortcutTest = class extends CrExtensionsBrowserTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://extensions/test_loader.html?module=extensions/keyboard_shortcuts_test.js';
+    return 'chrome://extensions/test_loader.html?module=extensions/keyboard_shortcuts_test.js&host=webui-test';
   }
 
   /** @override */
@@ -580,7 +580,7 @@
 var CrExtensionsPackDialogTest = class extends CrExtensionsBrowserTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://extensions/test_loader.html?module=extensions/pack_dialog_test.js';
+    return 'chrome://extensions/test_loader.html?module=extensions/pack_dialog_test.js&host=webui-test';
   }
 
   /** @override */
@@ -630,7 +630,7 @@
 
   /** @override */
   get browsePreload() {
-    return 'chrome://extensions/test_loader.html?module=extensions/options_dialog_test.js';
+    return 'chrome://extensions/test_loader.html?module=extensions/options_dialog_test.js&host=webui-test';
   }
 
   /** @override */
@@ -649,7 +649,7 @@
 var CrExtensionsErrorPageTest = class extends CrExtensionsBrowserTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://extensions/test_loader.html?module=extensions/error_page_test.js';
+    return 'chrome://extensions/test_loader.html?module=extensions/error_page_test.js&host=webui-test';
   }
 
   /** @override */
@@ -676,7 +676,7 @@
 var CrExtensionsCodeSectionTest = class extends CrExtensionsBrowserTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://extensions/test_loader.html?module=extensions/code_section_test.js';
+    return 'chrome://extensions/test_loader.html?module=extensions/code_section_test.js&host=webui-test';
   }
 
   /** @override */
@@ -699,7 +699,7 @@
 var CrExtensionsNavigationHelperTest = class extends CrExtensionsBrowserTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://extensions/test_loader.html?module=extensions/navigation_helper_test.js';
+    return 'chrome://extensions/test_loader.html?module=extensions/navigation_helper_test.js&host=webui-test';
   }
 
   /** @override */
@@ -732,7 +732,7 @@
 var CrExtensionsErrorConsoleTest = class extends CrExtensionsBrowserTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://extensions/test_loader.html?module=extensions/error_console_test.js';
+    return 'chrome://extensions/test_loader.html?module=extensions/error_console_test.js&host=webui-test';
   }
 
   /** @override */
@@ -762,7 +762,7 @@
 var CrExtensionsToggleRowTest = class extends CrExtensionsBrowserTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://extensions/test_loader.html?module=extensions/toggle_row_test.js';
+    return 'chrome://extensions/test_loader.html?module=extensions/toggle_row_test.js&host=webui-test';
   }
 };
 
@@ -778,7 +778,7 @@
 var CrExtensionsKioskModeTest = class extends CrExtensionsBrowserTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://extensions/test_loader.html?module=extensions/kiosk_mode_test.js';
+    return 'chrome://extensions/test_loader.html?module=extensions/kiosk_mode_test.js&host=webui-test';
   }
 
   /** @override */
@@ -819,7 +819,7 @@
 var CrExtensionsRuntimeHostsDialogTest = class extends CrExtensionsBrowserTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://extensions/test_loader.html?module=extensions/runtime_hosts_dialog_test.js';
+    return 'chrome://extensions/test_loader.html?module=extensions/runtime_hosts_dialog_test.js&host=webui-test';
   }
 };
 
@@ -834,7 +834,7 @@
     class extends CrExtensionsBrowserTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://extensions/test_loader.html?module=extensions/runtime_host_permissions_test.js';
+    return 'chrome://extensions/test_loader.html?module=extensions/runtime_host_permissions_test.js&host=webui-test';
   }
 };
 
@@ -849,7 +849,7 @@
     class extends CrExtensionsBrowserTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://extensions/test_loader.html?module=extensions/host_permissions_toggle_list_test.js';
+    return 'chrome://extensions/test_loader.html?module=extensions/host_permissions_toggle_list_test.js&host=webui-test';
   }
 };
 
diff --git a/chrome/test/data/webui/extensions/cr_extensions_interactive_ui_tests.js b/chrome/test/data/webui/extensions/cr_extensions_interactive_ui_tests.js
index 703a604..6f0768d 100644
--- a/chrome/test/data/webui/extensions/cr_extensions_interactive_ui_tests.js
+++ b/chrome/test/data/webui/extensions/cr_extensions_interactive_ui_tests.js
@@ -40,7 +40,7 @@
 var CrExtensionsOptionsPageTest = class extends CrExtensionsInteractiveUITest {
   /** @override */
   get browsePreload() {
-    return 'chrome://extensions/test_loader.html?module=extensions/extension_options_dialog_test.js';
+    return 'chrome://extensions/test_loader.html?module=extensions/extension_options_dialog_test.js&host=webui-test';
   }
 
   /** @override */
@@ -63,7 +63,7 @@
     class extends CrExtensionsInteractiveUITest {
   /** @override */
   get browsePreload() {
-    return 'chrome://extensions/test_loader.html?module=extensions/shortcut_input_test.js';
+    return 'chrome://extensions/test_loader.html?module=extensions/shortcut_input_test.js&host=webui-test';
   }
 
   /** @override */
diff --git a/chrome/test/data/webui/extensions/detail_view_test.js b/chrome/test/data/webui/extensions/detail_view_test.js
index 510beaed..3974fea 100644
--- a/chrome/test/data/webui/extensions/detail_view_test.js
+++ b/chrome/test/data/webui/extensions/detail_view_test.js
@@ -8,7 +8,7 @@
 import {assert} from 'chrome://resources/js/assert.m.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
-import {isChildVisible, isVisible} from '../test_util.js';
+import {isChildVisible, isVisible} from 'chrome://webui-test/test_util.js';
 
 import {createExtensionInfo, MockItemDelegate} from './test_util.js';
 
diff --git a/chrome/test/data/webui/extensions/error_console_test.js b/chrome/test/data/webui/extensions/error_console_test.js
index 449061fb..0802583 100644
--- a/chrome/test/data/webui/extensions/error_console_test.js
+++ b/chrome/test/data/webui/extensions/error_console_test.js
@@ -6,7 +6,7 @@
 
 import {keyDownOn} from 'chrome://resources/polymer/v3_0/iron-test-helpers/mock-interactions.js';
 
-import {eventToPromise, waitBeforeNextRender} from '../test_util.js';
+import {eventToPromise, waitBeforeNextRender} from 'chrome://webui-test/test_util.js';
 
 import {findMatches} from './test_util.js';
 
diff --git a/chrome/test/data/webui/extensions/error_page_test.js b/chrome/test/data/webui/extensions/error_page_test.js
index 1319c35..d055703 100644
--- a/chrome/test/data/webui/extensions/error_page_test.js
+++ b/chrome/test/data/webui/extensions/error_page_test.js
@@ -9,7 +9,7 @@
 import {PromiseResolver} from 'chrome://resources/js/promise_resolver.m.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
-import {isChildVisible} from '../test_util.js';
+import {isChildVisible} from 'chrome://webui-test/test_util.js';
 
 import {ClickMock, createExtensionInfo} from './test_util.js';
 
diff --git a/chrome/test/data/webui/extensions/extension_options_dialog_test.js b/chrome/test/data/webui/extensions/extension_options_dialog_test.js
index 4b9e6b8..32bedd70 100644
--- a/chrome/test/data/webui/extensions/extension_options_dialog_test.js
+++ b/chrome/test/data/webui/extensions/extension_options_dialog_test.js
@@ -11,7 +11,7 @@
 
 import {getDeepActiveElement} from 'chrome://resources/js/util.m.js';
 
-import {eventToPromise} from '../test_util.js';
+import {eventToPromise} from 'chrome://webui-test/test_util.js';
 
 suite('ExtensionOptionsDialogTest', () => {
   test('show options dialog', async () => {
diff --git a/chrome/test/data/webui/extensions/item_test.js b/chrome/test/data/webui/extensions/item_test.js
index 534ef01..206fa80 100644
--- a/chrome/test/data/webui/extensions/item_test.js
+++ b/chrome/test/data/webui/extensions/item_test.js
@@ -8,7 +8,7 @@
 import {assert} from 'chrome://resources/js/assert.m.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
-import {isChildVisible} from '../test_util.js';
+import {isChildVisible} from 'chrome://webui-test/test_util.js';
 
 import {TestService} from './test_service.js';
 import {createExtensionInfo, MockItemDelegate, testVisible} from './test_util.js';
diff --git a/chrome/test/data/webui/extensions/keyboard_shortcuts_test.js b/chrome/test/data/webui/extensions/keyboard_shortcuts_test.js
index 2779d8b..31ba0497 100644
--- a/chrome/test/data/webui/extensions/keyboard_shortcuts_test.js
+++ b/chrome/test/data/webui/extensions/keyboard_shortcuts_test.js
@@ -8,7 +8,7 @@
 import {assert} from 'chrome://resources/js/assert.m.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
-import {isChildVisible} from '../test_util.js';
+import {isChildVisible} from 'chrome://webui-test/test_util.js';
 
 import {TestService} from './test_service.js';
 import {createExtensionInfo} from './test_util.js';
diff --git a/chrome/test/data/webui/extensions/kiosk_mode_test.js b/chrome/test/data/webui/extensions/kiosk_mode_test.js
index 931df01..f7250365 100644
--- a/chrome/test/data/webui/extensions/kiosk_mode_test.js
+++ b/chrome/test/data/webui/extensions/kiosk_mode_test.js
@@ -9,7 +9,7 @@
 import {webUIListenerCallback} from 'chrome://resources/js/cr.m.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
-import {flushTasks} from '../test_util.js';
+import {flushTasks} from 'chrome://webui-test/test_util.js';
 
 import {TestKioskBrowserProxy} from './test_kiosk_browser_proxy.js';
 
diff --git a/chrome/test/data/webui/extensions/manager_test.js b/chrome/test/data/webui/extensions/manager_test.js
index 890627d..67825ee 100644
--- a/chrome/test/data/webui/extensions/manager_test.js
+++ b/chrome/test/data/webui/extensions/manager_test.js
@@ -6,7 +6,7 @@
 
 import {assert} from 'chrome://resources/js/assert.m.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-import {eventToPromise} from '../test_util.js';
+import {eventToPromise} from 'chrome://webui-test/test_util.js';
 
 window.extension_manager_tests = {};
 extension_manager_tests.suiteName = 'ExtensionManagerTest';
diff --git a/chrome/test/data/webui/extensions/manager_test_with_activity_log_flag.js b/chrome/test/data/webui/extensions/manager_test_with_activity_log_flag.js
index a9a5747..92e5e8a 100644
--- a/chrome/test/data/webui/extensions/manager_test_with_activity_log_flag.js
+++ b/chrome/test/data/webui/extensions/manager_test_with_activity_log_flag.js
@@ -6,7 +6,7 @@
 
 import {assert} from 'chrome://resources/js/assert.m.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-import {eventToPromise} from '../test_util.js';
+import {eventToPromise} from 'chrome://webui-test/test_util.js';
 
 
 window.extension_manager_tests = {};
diff --git a/chrome/test/data/webui/extensions/manager_test_with_id_query_param.js b/chrome/test/data/webui/extensions/manager_test_with_id_query_param.js
index 98966a1..194d4dd 100644
--- a/chrome/test/data/webui/extensions/manager_test_with_id_query_param.js
+++ b/chrome/test/data/webui/extensions/manager_test_with_id_query_param.js
@@ -6,7 +6,7 @@
 
 import {assert} from 'chrome://resources/js/assert.m.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-import {eventToPromise} from '../test_util.js';
+import {eventToPromise} from 'chrome://webui-test/test_util.js';
 
 window.extension_manager_tests = {};
 extension_manager_tests.suiteName = 'ExtensionManagerTest';
diff --git a/chrome/test/data/webui/extensions/navigation_helper_test.js b/chrome/test/data/webui/extensions/navigation_helper_test.js
index 6f6a3159..9cfcd1e 100644
--- a/chrome/test/data/webui/extensions/navigation_helper_test.js
+++ b/chrome/test/data/webui/extensions/navigation_helper_test.js
@@ -5,7 +5,7 @@
 import {Dialog, NavigationHelper, Page} from 'chrome://extensions/extensions.js';
 
 import {assert} from 'chrome://resources/js/assert.m.js';
-import {MockMethod} from '../mock_controller.js';
+import {MockMethod} from 'chrome://webui-test/mock_controller.js';
 
 window.extension_navigation_helper_tests = {};
 extension_navigation_helper_tests.suiteName = 'ExtensionNavigationHelperTest';
diff --git a/chrome/test/data/webui/extensions/options_dialog_test.js b/chrome/test/data/webui/extensions/options_dialog_test.js
index dc9d7a9..870a6b4 100644
--- a/chrome/test/data/webui/extensions/options_dialog_test.js
+++ b/chrome/test/data/webui/extensions/options_dialog_test.js
@@ -6,7 +6,7 @@
 import {OptionsDialogMaxHeight, OptionsDialogMinWidth, Service} from 'chrome://extensions/extensions.js';
 
 import {assert} from 'chrome://resources/js/assert.m.js';
-import {eventToPromise} from '../test_util.js';
+import {eventToPromise} from 'chrome://webui-test/test_util.js';
 
 window.extension_options_dialog_tests = {};
 extension_options_dialog_tests.suiteName = 'ExtensionOptionsDialogTests';
diff --git a/chrome/test/data/webui/extensions/pack_dialog_test.js b/chrome/test/data/webui/extensions/pack_dialog_test.js
index 9126db8..3a91211 100644
--- a/chrome/test/data/webui/extensions/pack_dialog_test.js
+++ b/chrome/test/data/webui/extensions/pack_dialog_test.js
@@ -10,7 +10,7 @@
 import {PromiseResolver} from 'chrome://resources/js/promise_resolver.m.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
-import {eventToPromise, flushTasks} from '../test_util.js';
+import {eventToPromise, flushTasks} from 'chrome://webui-test/test_util.js';
 
 import {isElementVisible} from './test_util.js';
 
diff --git a/chrome/test/data/webui/extensions/runtime_host_permissions_test.js b/chrome/test/data/webui/extensions/runtime_host_permissions_test.js
index 5109109..43721e5 100644
--- a/chrome/test/data/webui/extensions/runtime_host_permissions_test.js
+++ b/chrome/test/data/webui/extensions/runtime_host_permissions_test.js
@@ -7,7 +7,7 @@
 import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
-import {eventToPromise, isChildVisible} from '../test_util.js';
+import {eventToPromise, isChildVisible} from 'chrome://webui-test/test_util.js';
 
 import {TestService} from './test_service.js';
 import {MetricsPrivateMock} from './test_util.js';
diff --git a/chrome/test/data/webui/extensions/runtime_hosts_dialog_test.js b/chrome/test/data/webui/extensions/runtime_hosts_dialog_test.js
index 3392061..124c6b65 100644
--- a/chrome/test/data/webui/extensions/runtime_hosts_dialog_test.js
+++ b/chrome/test/data/webui/extensions/runtime_hosts_dialog_test.js
@@ -4,7 +4,7 @@
 
 import {getPatternFromSite} from 'chrome://extensions/extensions.js';
 
-import {eventToPromise} from '../test_util.js';
+import {eventToPromise} from 'chrome://webui-test/test_util.js';
 
 import {TestService} from './test_service.js';
 import {MetricsPrivateMock} from './test_util.js';
diff --git a/chrome/test/data/webui/extensions/sidebar_test.js b/chrome/test/data/webui/extensions/sidebar_test.js
index 587485b6..fcac55d5 100644
--- a/chrome/test/data/webui/extensions/sidebar_test.js
+++ b/chrome/test/data/webui/extensions/sidebar_test.js
@@ -7,7 +7,7 @@
 import {assert} from 'chrome://resources/js/assert.m.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
-import {eventToPromise} from '../test_util.js';
+import {eventToPromise} from 'chrome://webui-test/test_util.js';
 
 import {testVisible} from './test_util.js';
 
diff --git a/chrome/test/data/webui/extensions/test_service.js b/chrome/test/data/webui/extensions/test_service.js
index 7048db45..b6a81c08 100644
--- a/chrome/test/data/webui/extensions/test_service.js
+++ b/chrome/test/data/webui/extensions/test_service.js
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {FakeChromeEvent} from '../fake_chrome_event.js';
-import {TestBrowserProxy} from '../test_browser_proxy.js';
+import {FakeChromeEvent} from 'chrome://webui-test/fake_chrome_event.js';
+import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
 
 /** An extensions.Service implementation to be used in tests. */
 export class TestService extends TestBrowserProxy {
diff --git a/chrome/test/data/webui/extensions/test_util.js b/chrome/test/data/webui/extensions/test_util.js
index 19c00ba5..f5d11302 100644
--- a/chrome/test/data/webui/extensions/test_util.js
+++ b/chrome/test/data/webui/extensions/test_util.js
@@ -3,8 +3,8 @@
 // found in the LICENSE file.
 
 /** @fileoverview Common utilities for extension ui tests. */
-import {MockController, MockMethod} from '../mock_controller.js';
-import {isChildVisible} from '../test_util.js';
+import {MockController, MockMethod} from 'chrome://webui-test/mock_controller.js';
+import {isChildVisible} from 'chrome://webui-test/test_util.js';
 
 import {TestKioskBrowserProxy} from './test_kiosk_browser_proxy.js';
 
diff --git a/chrome/test/data/webui/extensions/toggle_row_test.js b/chrome/test/data/webui/extensions/toggle_row_test.js
index 5305063..5750ee2e 100644
--- a/chrome/test/data/webui/extensions/toggle_row_test.js
+++ b/chrome/test/data/webui/extensions/toggle_row_test.js
@@ -4,7 +4,7 @@
 
 import 'chrome://extensions/extensions.js';
 
-import {eventToPromise} from '../test_util.js';
+import {eventToPromise} from 'chrome://webui-test/test_util.js';
 
 suite('extensions-toggle-row', function() {
   let row;
diff --git a/chrome/test/data/webui/extensions/toolbar_test.js b/chrome/test/data/webui/extensions/toolbar_test.js
index cda75d4..26dd8c4 100644
--- a/chrome/test/data/webui/extensions/toolbar_test.js
+++ b/chrome/test/data/webui/extensions/toolbar_test.js
@@ -7,7 +7,7 @@
 import {isChromeOS, isMac} from 'chrome://resources/js/cr.m.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
-import {eventToPromise} from '../test_util.js';
+import {eventToPromise} from 'chrome://webui-test/test_util.js';
 
 import {TestService} from './test_service.js';
 import {createExtensionInfo, testVisible} from './test_util.js';
diff --git a/chrome/test/data/webui/extensions/tsconfig_base.json b/chrome/test/data/webui/extensions/tsconfig_base.json
new file mode 100644
index 0000000..eeddfb3
--- /dev/null
+++ b/chrome/test/data/webui/extensions/tsconfig_base.json
@@ -0,0 +1,9 @@
+{
+  "extends": "../../../../../tools/typescript/tsconfig_base.json",
+  "compilerOptions": {
+    "allowJs": true,
+    "typeRoots": [
+       "./../../../../../third_party/node/node_modules/@types"
+    ]
+  }
+}
diff --git a/chrome/test/data/webui/read_later/side_panel/bookmark_folder_test.ts b/chrome/test/data/webui/read_later/side_panel/bookmark_folder_test.ts
index 51c4adb..68c5650 100644
--- a/chrome/test/data/webui/read_later/side_panel/bookmark_folder_test.ts
+++ b/chrome/test/data/webui/read_later/side_panel/bookmark_folder_test.ts
@@ -2,10 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// ReadLaterUI is a Mojo WebUI controller and therefore needs mojo defined to
-// finish running its tests.
-import 'chrome://resources/mojo/mojo/public/js/mojo_bindings_lite.js';
-
+import 'chrome://webui-test/mojo_webui_test_support.js';
 import 'chrome://read-later.top-chrome/side_panel/bookmark_folder.js';
 
 import {BookmarkFolderElement, FOLDER_OPEN_CHANGED_EVENT, getBookmarkFromElement} from 'chrome://read-later.top-chrome/side_panel/bookmark_folder.js';
diff --git a/chrome/test/data/webui/read_later/side_panel/bookmarks_drag_manager_test.ts b/chrome/test/data/webui/read_later/side_panel/bookmarks_drag_manager_test.ts
index ab8a893..2dbd540 100644
--- a/chrome/test/data/webui/read_later/side_panel/bookmarks_drag_manager_test.ts
+++ b/chrome/test/data/webui/read_later/side_panel/bookmarks_drag_manager_test.ts
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import 'chrome://resources/mojo/mojo/public/js/mojo_bindings_lite.js';
+import 'chrome://webui-test/mojo_webui_test_support.js';
 
 import {BookmarksApiProxyImpl} from 'chrome://read-later.top-chrome/side_panel/bookmarks_api_proxy.js';
 import {BookmarksDragManager, DROP_POSITION_ATTR, DropPosition, overrideFolderOpenerTimeoutDelay} from 'chrome://read-later.top-chrome/side_panel/bookmarks_drag_manager.js';
diff --git a/chrome/test/data/webui/read_later/side_panel/bookmarks_list_test.ts b/chrome/test/data/webui/read_later/side_panel/bookmarks_list_test.ts
index 21ded94..4e0cfea 100644
--- a/chrome/test/data/webui/read_later/side_panel/bookmarks_list_test.ts
+++ b/chrome/test/data/webui/read_later/side_panel/bookmarks_list_test.ts
@@ -2,10 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// ReadLaterUI is a Mojo WebUI controller and therefore needs mojo defined to
-// finish running its tests.
-import 'chrome://resources/mojo/mojo/public/js/mojo_bindings_lite.js';
-
+import 'chrome://webui-test/mojo_webui_test_support.js';
 import 'chrome://read-later.top-chrome/side_panel/bookmarks_list.js';
 
 import {BookmarkFolderElement, FOLDER_OPEN_CHANGED_EVENT} from 'chrome://read-later.top-chrome/side_panel/bookmark_folder.js';
diff --git a/chrome/test/data/webui/read_later/side_panel/side_panel_app_test.ts b/chrome/test/data/webui/read_later/side_panel/side_panel_app_test.ts
index 41ea81c..8c498ba5 100644
--- a/chrome/test/data/webui/read_later/side_panel/side_panel_app_test.ts
+++ b/chrome/test/data/webui/read_later/side_panel/side_panel_app_test.ts
@@ -2,9 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// ReadLaterUI is a Mojo WebUI controller and therefore needs mojo defined to
-// finish running its tests.
-import 'chrome://resources/mojo/mojo/public/js/mojo_bindings_lite.js';
+import 'chrome://webui-test/mojo_webui_test_support.js';
 import 'chrome://read-later.top-chrome/side_panel/app.js';
 
 import {LOCAL_STORAGE_TAB_ID_KEY, SidePanelAppElement} from 'chrome://read-later.top-chrome/side_panel/app.js';
diff --git a/chromecast/android/lint-suppressions.xml b/chromecast/android/lint-suppressions.xml
index f46dd7a4..fa89c7f 100644
--- a/chromecast/android/lint-suppressions.xml
+++ b/chromecast/android/lint-suppressions.xml
@@ -58,6 +58,9 @@
     <!-- 1 resource used by android tv to generate MediaShell.apk file -->
     <ignore regexp="chromecast/internal/android/prebuilt/google-play-services-first-party"/>
   </issue>
+  <issue id="UnusedResources">
+    <ignore regexp="chromecast/browser/android/apk/res"/>
+  </issue>
   <issue id="UselessParent">
     <ignore regexp="chromecast/internal"/>
   </issue>
diff --git a/chromecast/browser/android/BUILD.gn b/chromecast/browser/android/BUILD.gn
index 4e5cef87..dcbc5d5d 100644
--- a/chromecast/browser/android/BUILD.gn
+++ b/chromecast/browser/android/BUILD.gn
@@ -48,6 +48,11 @@
               "apk/res/drawable-xhdpi/ic_settings_cast.png",
               "apk/res/drawable-xxhdpi/ic_settings_cast.png",
               "apk/res/drawable-xxxhdpi/ic_settings_cast.png",
+              "apk/res/drawable-hdpi/ic_notification_cast.png",
+              "apk/res/drawable-mdpi/ic_notification_cast.png",
+              "apk/res/drawable-xhdpi/ic_notification_cast.png",
+              "apk/res/drawable-xxhdpi/ic_notification_cast.png",
+              "apk/res/drawable-xxxhdpi/ic_notification_cast.png",
               "apk/res/layout/cast_web_contents_activity.xml",
             ]
 }
diff --git a/chromecast/browser/android/apk/res/drawable-hdpi/ic_notification_cast.png b/chromecast/browser/android/apk/res/drawable-hdpi/ic_notification_cast.png
new file mode 100644
index 0000000..5223f96
--- /dev/null
+++ b/chromecast/browser/android/apk/res/drawable-hdpi/ic_notification_cast.png
Binary files differ
diff --git a/chromecast/browser/android/apk/res/drawable-mdpi/ic_notification_cast.png b/chromecast/browser/android/apk/res/drawable-mdpi/ic_notification_cast.png
new file mode 100644
index 0000000..8dbf1fc
--- /dev/null
+++ b/chromecast/browser/android/apk/res/drawable-mdpi/ic_notification_cast.png
Binary files differ
diff --git a/chromecast/browser/android/apk/res/drawable-xhdpi/ic_notification_cast.png b/chromecast/browser/android/apk/res/drawable-xhdpi/ic_notification_cast.png
new file mode 100644
index 0000000..0513e5c0
--- /dev/null
+++ b/chromecast/browser/android/apk/res/drawable-xhdpi/ic_notification_cast.png
Binary files differ
diff --git a/chromecast/browser/android/apk/res/drawable-xxhdpi/ic_notification_cast.png b/chromecast/browser/android/apk/res/drawable-xxhdpi/ic_notification_cast.png
new file mode 100644
index 0000000..5bec37a0
--- /dev/null
+++ b/chromecast/browser/android/apk/res/drawable-xxhdpi/ic_notification_cast.png
Binary files differ
diff --git a/chromecast/browser/android/apk/res/drawable-xxxhdpi/ic_notification_cast.png b/chromecast/browser/android/apk/res/drawable-xxxhdpi/ic_notification_cast.png
new file mode 100644
index 0000000..f9a8320
--- /dev/null
+++ b/chromecast/browser/android/apk/res/drawable-xxxhdpi/ic_notification_cast.png
Binary files differ
diff --git a/chromeos/chromeos_strings.grd b/chromeos/chromeos_strings.grd
index 9f931c8..8e0e27e 100644
--- a/chromeos/chromeos_strings.grd
+++ b/chromeos/chromeos_strings.grd
@@ -2128,6 +2128,9 @@
       <message name="IDS_SHIMLESS_RMA_SKIP_BUTTON" desc="Default text for the button used to skip to the next step of RMA finalization.">
         Skip
       </message>
+      <message name="IDS_SHIMLESS_RMA_OK_BUTTON" desc="Default text for OK button used to acknowledge and close dialogs.">
+        OK
+      </message>
       <!-- Landing page -->
       <message name="IDS_SHIMLESS_RMA_LANDING_PAGE_TITLE" translateable="false" desc="Title for the landing page at the start of RMA.">
         Chromebook repair
@@ -2142,11 +2145,14 @@
         Components are installed correctly.
       </message>
       <message name="IDS_SHIMLESS_RMA_VALIDATED_COMPONENTS_FAIL" translateable="false" desc="Validation check found that device components are NOT approved for this device.">
-        Components could not be validated. If you are certain you are using qualified components updating Chrome OS may resolve this issue.
+        <ph name="LINK_BEGIN">&lt;a id="unqualifiedComponentsLink"&gt;</ph>Unqualified components<ph name="LINK_END">&lt;/a&gt;</ph> found. To make sure all qualified components are identified, update your ChromeOS to the latest version.
       </message>
       <message name="IDS_SHIMLESS_RMA_GET_STARTED_BUTTON_LABEL" desc="The label for the button to start the RMA process.">
         Get started
       </message>
+      <message name="IDS_SHIMLESS_RMA_UNQUALIFIED_COMPONENTS_TITLE" desc="The title for the dialog showing the list of components unqualfiied by Chrome OS.">
+        Unqualified components
+      </message>
       <!-- Network connect page -->
       <message name="IDS_SHIMLESS_RMA_CONNECT_PAGE_TITLE" desc="Title for the connect to wifi page.">
         Get connected
diff --git a/chromeos/chromeos_strings_grd/IDS_SHIMLESS_RMA_OK_BUTTON.png.sha1 b/chromeos/chromeos_strings_grd/IDS_SHIMLESS_RMA_OK_BUTTON.png.sha1
new file mode 100644
index 0000000..ed3719e
--- /dev/null
+++ b/chromeos/chromeos_strings_grd/IDS_SHIMLESS_RMA_OK_BUTTON.png.sha1
@@ -0,0 +1 @@
+424ffd5bc00347c9cb0c784fa81ab375790c0876
\ No newline at end of file
diff --git a/chromeos/chromeos_strings_grd/IDS_SHIMLESS_RMA_UNQUALIFIED_COMPONENTS_TITLE.png.sha1 b/chromeos/chromeos_strings_grd/IDS_SHIMLESS_RMA_UNQUALIFIED_COMPONENTS_TITLE.png.sha1
new file mode 100644
index 0000000..0f8d4d126
--- /dev/null
+++ b/chromeos/chromeos_strings_grd/IDS_SHIMLESS_RMA_UNQUALIFIED_COMPONENTS_TITLE.png.sha1
@@ -0,0 +1 @@
+167cf405a6b3eaca34e9f172048def3f09a43c29
\ No newline at end of file
diff --git a/chromeos/dbus/constants/dbus_switches.h b/chromeos/dbus/constants/dbus_switches.h
index 26ea914..e0240c0 100644
--- a/chromeos/dbus/constants/dbus_switches.h
+++ b/chromeos/dbus/constants/dbus_switches.h
@@ -34,4 +34,11 @@
 }  // namespace switches
 }  // namespace chromeos
 
+// TODO(https://crbug.com/1164001): remove after the migration is finished.
+namespace ash {
+namespace switches {
+using ::chromeos::switches::kFakeOobeConfiguration;
+}
+}  // namespace ash
+
 #endif  // CHROMEOS_DBUS_CONSTANTS_DBUS_SWITCHES_H_
diff --git a/chromeos/printing/printing_constants.h b/chromeos/printing/printing_constants.h
index 08ee41fa..e93a2a10 100644
--- a/chromeos/printing/printing_constants.h
+++ b/chromeos/printing/printing_constants.h
@@ -5,6 +5,8 @@
 #ifndef CHROMEOS_PRINTING_PRINTING_CONSTANTS_H_
 #define CHROMEOS_PRINTING_PRINTING_CONSTANTS_H_
 
+#include <stddef.h>
+
 namespace chromeos {
 
 // Maximum size of a PPD file that we will accept, currently 250k.  This number
@@ -13,13 +15,13 @@
 constexpr size_t kMaxPpdSizeBytes = 250 * 1024;
 
 // Printing protocol schemes.
-constexpr char kIppScheme[] = "ipp";
-constexpr char kIppsScheme[] = "ipps";
-constexpr char kUsbScheme[] = "usb";
-constexpr char kHttpScheme[] = "http";
-constexpr char kHttpsScheme[] = "https";
-constexpr char kSocketScheme[] = "socket";
-constexpr char kLpdScheme[] = "lpd";
+inline constexpr char kIppScheme[] = "ipp";
+inline constexpr char kIppsScheme[] = "ipps";
+inline constexpr char kUsbScheme[] = "usb";
+inline constexpr char kHttpScheme[] = "http";
+inline constexpr char kHttpsScheme[] = "https";
+inline constexpr char kSocketScheme[] = "socket";
+inline constexpr char kLpdScheme[] = "lpd";
 
 constexpr int kIppPort = 631;
 // IPPS commonly uses the HTTPS port despite the spec saying it should use the
diff --git a/chromeos/process_proxy/process_proxy.cc b/chromeos/process_proxy/process_proxy.cc
index 7d2e3a20..94bdbe3 100644
--- a/chromeos/process_proxy/process_proxy.cc
+++ b/chromeos/process_proxy/process_proxy.cc
@@ -234,8 +234,8 @@
   options.fds_to_remap.push_back(std::make_pair(slave_fd, STDERR_FILENO));
   // Do not set NO_NEW_PRIVS on processes if the system is in dev-mode. This
   // permits sudo in the crosh shell when in developer mode.
-  options.allow_new_privs = base::CommandLine::ForCurrentProcess()->
-      HasSwitch(chromeos::switches::kSystemInDevMode);
+  options.allow_new_privs = base::CommandLine::ForCurrentProcess()->HasSwitch(
+      chromeos::switches::kSystemInDevMode);
   options.ctrl_terminal_fd = slave_fd;
   // TODO(vapier): Ideally we'd just use the env settings from hterm itself.
   // We can't let the user inject any env var they want, but we should be able
diff --git a/chromeos/services/assistant/service.cc b/chromeos/services/assistant/service.cc
index 512f0dda..f0d2d5c 100644
--- a/chromeos/services/assistant/service.cc
+++ b/chromeos/services/assistant/service.cc
@@ -92,7 +92,7 @@
   // One example of using fake gaia login is in our automation tests, i.e.
   // Assistant Tast tests.
   return base::CommandLine::ForCurrentProcess()->HasSwitch(
-      chromeos::switches::kDisableGaiaServices);
+      switches::kDisableGaiaServices);
 }
 
 }  // namespace
diff --git a/chromeos/services/device_sync/cryptauth_device_manager_impl.cc b/chromeos/services/device_sync/cryptauth_device_manager_impl.cc
index 796997b..842e0ca 100644
--- a/chromeos/services/device_sync/cryptauth_device_manager_impl.cc
+++ b/chromeos/services/device_sync/cryptauth_device_manager_impl.cc
@@ -684,7 +684,7 @@
 void CryptAuthDeviceManagerImpl::OnGetMyDevicesSuccess(
     const cryptauth::GetMyDevicesResponse& response) {
   // Update the synced devices stored in the user's prefs.
-  std::unique_ptr<base::ListValue> devices_as_list(new base::ListValue());
+  base::Value devices_as_list(base::Value::Type::LIST);
 
   if (!response.devices().empty()) {
     PA_LOG(VERBOSE) << "Devices were successfully synced.";
@@ -702,16 +702,15 @@
     PA_LOG(INFO) << "Synced device '" << device_name
                  << "': " << device_dictionary;
 
-    devices_as_list->Append(std::move(device_dictionary));
+    devices_as_list.Append(std::move(device_dictionary));
   }
 
   bool unlock_keys_changed =
-      *devices_as_list !=
+      devices_as_list !=
       *pref_service_->GetList(prefs::kCryptAuthDeviceSyncUnlockKeys);
   {
-    ListPrefUpdateDeprecated update(pref_service_,
-                                    prefs::kCryptAuthDeviceSyncUnlockKeys);
-    update.Get()->Swap(devices_as_list.get());
+    ListPrefUpdate update(pref_service_, prefs::kCryptAuthDeviceSyncUnlockKeys);
+    *update.Get() = std::move(devices_as_list);
   }
   UpdateUnlockKeysFromPrefs();
 
diff --git a/chromeos/services/device_sync/cryptauth_device_manager_impl_unittest.cc b/chromeos/services/device_sync/cryptauth_device_manager_impl_unittest.cc
index 7f8b92f7..8f0956b 100644
--- a/chromeos/services/device_sync/cryptauth_device_manager_impl_unittest.cc
+++ b/chromeos/services/device_sync/cryptauth_device_manager_impl_unittest.cc
@@ -529,8 +529,7 @@
         prefs::kCryptAuthDeviceSyncReason,
         std::make_unique<base::Value>(cryptauth::INVOCATION_REASON_UNKNOWN));
 
-    std::unique_ptr<base::DictionaryValue> device_dictionary(
-        new base::DictionaryValue());
+    base::Value device_dictionary(base::Value::Type::DICTIONARY);
 
     std::string public_key_b64, device_name_b64, bluetooth_address_b64;
     base::Base64UrlEncode(kStoredPublicKey,
@@ -543,16 +542,18 @@
                           base::Base64UrlEncodePolicy::INCLUDE_PADDING,
                           &bluetooth_address_b64);
 
-    device_dictionary->SetString("public_key", public_key_b64);
-    device_dictionary->SetString("device_name", device_name_b64);
-    device_dictionary->SetString("bluetooth_address", bluetooth_address_b64);
-    device_dictionary->SetBoolean("unlockable", kStoredUnlockable);
-    device_dictionary->SetKey("beacon_seeds", base::ListValue());
-    device_dictionary->SetKey("software_features", base::DictionaryValue());
+    device_dictionary.SetStringKey("public_key", public_key_b64);
+    device_dictionary.SetStringKey("device_name", device_name_b64);
+    device_dictionary.SetStringKey("bluetooth_address", bluetooth_address_b64);
+    device_dictionary.SetBoolKey("unlockable", kStoredUnlockable);
+    device_dictionary.SetKey("beacon_seeds",
+                             base::Value(base::Value::Type::LIST));
+    device_dictionary.SetKey("software_features",
+                             base::Value(base::Value::Type::DICTIONARY));
 
     {
-      ListPrefUpdateDeprecated update(&pref_service_,
-                                      prefs::kCryptAuthDeviceSyncUnlockKeys);
+      ListPrefUpdate update(&pref_service_,
+                            prefs::kCryptAuthDeviceSyncUnlockKeys);
       update.Get()->Append(std::move(device_dictionary));
     }
 
@@ -723,23 +724,23 @@
 TEST_F(
     DeviceSyncCryptAuthDeviceManagerImplTest,
     InitWithExistingPrefs_MigrateDeprecateBooleansFromPrefsToSoftwareFeature) {
-  ListPrefUpdateDeprecated update_clear(&pref_service_,
-                                        prefs::kCryptAuthDeviceSyncUnlockKeys);
+  ListPrefUpdate update_clear(&pref_service_,
+                              prefs::kCryptAuthDeviceSyncUnlockKeys);
   update_clear.Get()->ClearList();
 
   // Simulate a deprecated device being persisted to prefs.
-  auto device_dictionary = std::make_unique<base::DictionaryValue>();
+  base::Value device_dictionary(base::Value::Type::DICTIONARY);
   std::string public_key_b64;
   base::Base64UrlEncode(kStoredPublicKey,
                         base::Base64UrlEncodePolicy::INCLUDE_PADDING,
                         &public_key_b64);
-  device_dictionary->SetString("public_key", public_key_b64);
-  device_dictionary->SetBoolean("unlock_key", true);
-  device_dictionary->SetBoolean("mobile_hotspot_supported", true);
-  device_dictionary->SetKey("software_features", base::DictionaryValue());
+  device_dictionary.SetStringKey("public_key", public_key_b64);
+  device_dictionary.SetBoolKey("unlock_key", true);
+  device_dictionary.SetBoolKey("mobile_hotspot_supported", true);
+  device_dictionary.SetKey("software_features",
+                           base::Value(base::Value::Type::DICTIONARY));
 
-  ListPrefUpdateDeprecated update(&pref_service_,
-                                  prefs::kCryptAuthDeviceSyncUnlockKeys);
+  ListPrefUpdate update(&pref_service_, prefs::kCryptAuthDeviceSyncUnlockKeys);
   update.Get()->Append(std::move(device_dictionary));
 
   device_manager_ = std::make_unique<TestCryptAuthDeviceManager>(
diff --git a/chromeos/system/statistics_provider.cc b/chromeos/system/statistics_provider.cc
index 9c22af5..867157c 100644
--- a/chromeos/system/statistics_provider.cc
+++ b/chromeos/system/statistics_provider.cc
@@ -386,7 +386,7 @@
 
   // Test region should override VPD values.
   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
-          chromeos::switches::kCrosRegion) &&
+          ash::switches::kCrosRegion) &&
       GetRegionalInformation(name, result)) {
     return true;
   }
@@ -619,9 +619,8 @@
     region_ = std::string();
 
   base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
-  if (command_line->HasSwitch(chromeos::switches::kCrosRegion)) {
-    region_ =
-        command_line->GetSwitchValueASCII(chromeos::switches::kCrosRegion);
+  if (command_line->HasSwitch(ash::switches::kCrosRegion)) {
+    region_ = command_line->GetSwitchValueASCII(ash::switches::kCrosRegion);
     machine_info_[kRegionKey] = region_;
     VLOG(1) << "CrOS region set to '" << region_ << "'";
   }
diff --git a/components/BUILD.gn b/components/BUILD.gn
index c70ebfc8..5734f9f7 100644
--- a/components/BUILD.gn
+++ b/components/BUILD.gn
@@ -437,8 +437,7 @@
     ]
     if (use_v8_context_snapshot) {
       deps += [ "//tools/v8_context_snapshot:v8_context_snapshot_assets" ]
-    }
-    if (!use_v8_context_snapshot || include_both_v8_snapshots) {
+    } else {
       deps += [ "//v8:v8_external_startup_data_assets" ]
     }
 
@@ -623,8 +622,7 @@
     ]
     if (use_v8_context_snapshot) {
       deps += [ "//tools/v8_context_snapshot:v8_context_snapshot_assets" ]
-    }
-    if (!use_v8_context_snapshot || include_both_v8_snapshots) {
+    } else {
       deps += [ "//v8:v8_external_startup_data_assets" ]
     }
   }
@@ -911,8 +909,7 @@
       deps += [ "//ui/android:ui_java" ]
       if (use_v8_context_snapshot) {
         deps += [ "//tools/v8_context_snapshot:v8_context_snapshot_assets" ]
-      }
-      if (!use_v8_context_snapshot || include_both_v8_snapshots) {
+      } else {
         deps += [ "//v8:v8_external_startup_data_assets" ]
       }
     }
diff --git a/components/account_manager_core/chromeos/account_manager.cc b/components/account_manager_core/chromeos/account_manager.cc
index c557d45..1abf946 100644
--- a/components/account_manager_core/chromeos/account_manager.cc
+++ b/components/account_manager_core/chromeos/account_manager.cc
@@ -472,19 +472,19 @@
 }
 
 void AccountManager::GetAccounts(AccountListCallback callback) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK_NE(init_state_, InitializationState::kNotStarted);
 
-  base::OnceClosure closure =
-      base::BindOnce(&AccountManager::GetAccountsInternal,
-                     weak_factory_.GetWeakPtr(), std::move(callback));
-  RunOnInitialization(std::move(closure));
-}
+  if (init_state_ != InitializationState::kInitialized) {
+    base::OnceClosure closure =
+        base::BindOnce(&AccountManager::GetAccounts, weak_factory_.GetWeakPtr(),
+                       std::move(callback));
+    RunOnInitialization(std::move(closure));
+    return;
+  }
 
-void AccountManager::GetAccountsInternal(AccountListCallback callback) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK_EQ(init_state_, InitializationState::kInitialized);
-
-  std::move(callback).Run(GetAccounts());
+  std::move(callback).Run(GetAccountsView());
 }
 
 void AccountManager::GetAccountEmail(
@@ -704,7 +704,9 @@
   return accounts_proto.SerializeAsString();
 }
 
-std::vector<::account_manager::Account> AccountManager::GetAccounts() {
+std::vector<::account_manager::Account> AccountManager::GetAccountsView() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
   std::vector<::account_manager::Account> accounts;
   accounts.reserve(accounts_.size());
 
diff --git a/components/account_manager_core/chromeos/account_manager.h b/components/account_manager_core/chromeos/account_manager.h
index 90e2973c..8ded5922 100644
--- a/components/account_manager_core/chromeos/account_manager.h
+++ b/components/account_manager_core/chromeos/account_manager.h
@@ -146,9 +146,9 @@
   // Returns |true| if |AccountManager| has been fully initialized.
   bool IsInitialized() const;
 
-  // Gets (async) a list of account keys known to |AccountManager|. Note that
-  // |callback| will be immediately called in the same thread if
-  // |AccountManager| has been fully initialized and hence it may not be safe to
+  // Gets (async) a list of account keys known to `AccountManager`. Note that
+  // `callback` will be immediately called in the same thread if
+  // `AccountManager` has been fully initialized and hence it may not be safe to
   // call this method directly in some class's constructor, with a callback on
   // the same class, since it may result in a method call on a partially
   // constructed object.
@@ -292,10 +292,6 @@
   // class is initialized.
   void RunOnInitialization(base::OnceClosure closure);
 
-  // Does the actual work of getting a list of accounts. Assumes that
-  // |AccountManager| initialization (|init_state_|) is complete.
-  void GetAccountsInternal(AccountListCallback callback);
-
   // Does the actual work of fetching the email for |account_key|. Assumes that
   // |AccountManager| initialization (|init_state_|) is complete.
   void GetAccountEmailInternal(
@@ -333,8 +329,8 @@
   // Gets a serialized representation of accounts.
   std::string GetSerializedAccounts();
 
-  // Gets the publicly viewable information stored in |accounts_|.
-  std::vector<::account_manager::Account> GetAccounts();
+  // Gets the publicly viewable information stored in `accounts_`.
+  std::vector<::account_manager::Account> GetAccountsView();
 
   // Notifies |Observer|s about a token update for |account|.
   void NotifyTokenObservers(const ::account_manager::Account& account);
diff --git a/components/autofill_assistant/browser/user_data_util.cc b/components/autofill_assistant/browser/user_data_util.cc
index e2146ae4..361301d 100644
--- a/components/autofill_assistant/browser/user_data_util.cc
+++ b/components/autofill_assistant/browser/user_data_util.cc
@@ -282,6 +282,29 @@
   return it != mapping.end() && !it->second.empty();
 }
 
+ClientStatus MoveAutofillValueRegexpToTextFilter(
+    const UserData* user_data,
+    SelectorProto::PropertyFilter* value) {
+  if (!value->has_autofill_value_regexp()) {
+    return OkClientStatus();
+  }
+  if (user_data == nullptr) {
+    return ClientStatus(PRECONDITION_FAILED);
+  }
+  const AutofillValueRegexp& autofill_value_regexp =
+      value->autofill_value_regexp();
+  TextFilter text_filter;
+  text_filter.set_case_sensitive(
+      autofill_value_regexp.value_expression_re2().case_sensitive());
+  std::string re2;
+  ClientStatus re2_status =
+      GetFormattedClientValue(autofill_value_regexp, *user_data, &re2);
+  text_filter.set_re2(re2);
+  // Assigning text_filter will clear autofill_value_regexp.
+  *value->mutable_text_filter() = text_filter;
+  return re2_status;
+}
+
 }  // namespace
 
 std::vector<std::string> GetContactValidationErrors(
@@ -782,5 +805,38 @@
   return bit_array;
 }
 
+ClientStatus ResolveSelectorUserData(SelectorProto* selector,
+                                     const UserData* user_data) {
+  for (auto& filter : *selector->mutable_filters()) {
+    switch (filter.filter_case()) {
+      case SelectorProto::Filter::kProperty: {
+        ClientStatus filter_status = MoveAutofillValueRegexpToTextFilter(
+            user_data, filter.mutable_property());
+        if (!filter_status.ok()) {
+          return filter_status;
+        }
+        break;
+      }
+      case SelectorProto::Filter::kInnerText:
+      case SelectorProto::Filter::kValue:
+      case SelectorProto::Filter::kPseudoElementContent:
+      case SelectorProto::Filter::kCssStyle:
+      case SelectorProto::Filter::kCssSelector:
+      case SelectorProto::Filter::kEnterFrame:
+      case SelectorProto::Filter::kPseudoType:
+      case SelectorProto::Filter::kBoundingBox:
+      case SelectorProto::Filter::kNthMatch:
+      case SelectorProto::Filter::kLabelled:
+      case SelectorProto::Filter::kMatchCssSelector:
+      case SelectorProto::Filter::kOnTop:
+      case SelectorProto::Filter::FILTER_NOT_SET:
+        break;
+        // Do not add default here. In case a new filter gets added (that may
+        // contain a RegexpFilter) we want this to fail at compilation here.
+    }
+  }
+  return OkClientStatus();
+}
+
 }  // namespace user_data
 }  // namespace autofill_assistant
diff --git a/components/autofill_assistant/browser/user_data_util.h b/components/autofill_assistant/browser/user_data_util.h
index 4bd3fc8..f24b440c 100644
--- a/components/autofill_assistant/browser/user_data_util.h
+++ b/components/autofill_assistant/browser/user_data_util.h
@@ -153,6 +153,11 @@
 // missing).
 int GetFieldBitArrayForCreditCard(const autofill::CreditCard* card);
 
+// Resolves |selector|'s references to user data with the actual values.
+// Modifies |selector| in place.
+ClientStatus ResolveSelectorUserData(SelectorProto* selector,
+                                     const UserData* user_data);
+
 }  // namespace user_data
 }  // namespace autofill_assistant
 
diff --git a/components/autofill_assistant/browser/user_data_util_unittest.cc b/components/autofill_assistant/browser/user_data_util_unittest.cc
index 2e91e067..3fa2c9f2 100644
--- a/components/autofill_assistant/browser/user_data_util_unittest.cc
+++ b/components/autofill_assistant/browser/user_data_util_unittest.cc
@@ -21,6 +21,7 @@
 #include "components/autofill_assistant/browser/client_status.h"
 #include "components/autofill_assistant/browser/mock_website_login_manager.h"
 #include "components/autofill_assistant/browser/service.pb.h"
+#include "components/autofill_assistant/browser/test_util.h"
 #include "components/autofill_assistant/browser/user_data.h"
 #include "components/autofill_assistant/browser/user_model.h"
 #include "components/autofill_assistant/browser/value_util.h"
@@ -1513,6 +1514,75 @@
       GetFieldBitArrayForCreditCard(&masked));
 }
 
+TEST_F(UserDataUtilTextValueTest, ResolveSelectorUserData) {
+  autofill::AutofillProfile contact(base::GenerateGUID(),
+                                    autofill::test::kEmptyOrigin);
+  autofill::test::SetProfileInfo(&contact, "Jo.h*n", /* middle name */ "",
+                                 "Doe", "", "", "", "", "", "", "", "", "");
+  user_model_.SetSelectedAutofillProfile(
+      "contact", std::make_unique<autofill::AutofillProfile>(contact),
+      &user_data_);
+
+  SelectorProto selector;
+  selector.add_filters()->set_css_selector("#test");
+
+  auto* filter = selector.add_filters();
+  auto* value = filter->mutable_property()->mutable_autofill_value_regexp();
+  value->mutable_profile()->set_identifier("contact");
+  auto* expression =
+      value->mutable_value_expression_re2()->mutable_value_expression();
+  expression->add_chunk()->set_text("My name is ");
+  expression->add_chunk()->set_key(
+      static_cast<int>(autofill::ServerFieldType::NAME_LAST));
+  expression->add_chunk()->set_text(", ");
+  expression->add_chunk()->set_key(
+      static_cast<int>(autofill::ServerFieldType::NAME_FIRST));
+  expression->add_chunk()->set_text(" ");
+  expression->add_chunk()->set_key(
+      static_cast<int>(autofill::ServerFieldType::NAME_LAST));
+
+  selector.add_filters()->mutable_enter_frame();
+  selector.add_filters()->mutable_nth_match()->set_index(0);
+  SelectorProto copy = selector;
+
+  ClientStatus status = ResolveSelectorUserData(&selector, &user_data_);
+
+  ASSERT_TRUE(status.ok());
+  ASSERT_EQ(selector.filters(1).property().text_filter().re2(),
+            "My name is Doe, Jo\\.h\\*n Doe");
+  ASSERT_EQ(selector.filters().size(), copy.filters().size());
+
+  // Other filters should remain unchanged
+  ASSERT_EQ(selector.filters(0), copy.filters(0));
+  ASSERT_EQ(selector.filters(2), copy.filters(2));
+  ASSERT_EQ(selector.filters(3), copy.filters(3));
+}
+
+TEST_F(UserDataUtilTextValueTest, ResolveSelectorUserDataError) {
+  autofill::AutofillProfile contact(base::GenerateGUID(),
+                                    autofill::test::kEmptyOrigin);
+  autofill::test::SetProfileInfo(&contact, "Jo.h*n", /* middle name */ "",
+                                 "Doe", "", "", "", "", "", "", "", "", "");
+  user_model_.SetSelectedAutofillProfile(
+      "contact", std::make_unique<autofill::AutofillProfile>(contact),
+      &user_data_);
+
+  SelectorProto selector;
+  selector.add_filters()->set_css_selector("#test");
+
+  auto* filter = selector.add_filters();
+  auto* value = filter->mutable_property()->mutable_autofill_value_regexp();
+  value->mutable_profile()->set_identifier("contact");
+  auto* expression =
+      value->mutable_value_expression_re2()->mutable_value_expression();
+  expression->add_chunk()->set_key(
+      static_cast<int>(autofill::ServerFieldType::NAME_MIDDLE));
+
+  ClientStatus status = ResolveSelectorUserData(&selector, &user_data_);
+
+  ASSERT_FALSE(status.ok());
+}
+
 }  // namespace
 }  // namespace user_data
 }  // namespace autofill_assistant
diff --git a/components/autofill_assistant/browser/web/element_finder.cc b/components/autofill_assistant/browser/web/element_finder.cc
index 89aa247..0c8ffe2 100644
--- a/components/autofill_assistant/browser/web/element_finder.cc
+++ b/components/autofill_assistant/browser/web/element_finder.cc
@@ -75,66 +75,6 @@
   }
   return false;
 }
-
-ClientStatus MoveAutofillValueRegexpToTextFilter(
-    const UserData* user_data,
-    SelectorProto::PropertyFilter* value) {
-  if (!value->has_autofill_value_regexp()) {
-    return OkClientStatus();
-  }
-  if (user_data == nullptr) {
-    return ClientStatus(PRECONDITION_FAILED);
-  }
-  const AutofillValueRegexp& autofill_value_regexp =
-      value->autofill_value_regexp();
-  TextFilter text_filter;
-  text_filter.set_case_sensitive(
-      autofill_value_regexp.value_expression_re2().case_sensitive());
-  std::string re2;
-  ClientStatus re2_status = user_data::GetFormattedClientValue(
-      autofill_value_regexp, *user_data, &re2);
-  text_filter.set_re2(re2);
-  // Assigning text_filter will clear autofill_value_regexp.
-  *value->mutable_text_filter() = text_filter;
-  return re2_status;
-}
-
-ClientStatus GetUserDataResolvedSelector(const Selector& selector,
-                                         const UserData* user_data,
-                                         SelectorProto* out_selector) {
-  SelectorProto copy = selector.proto;
-  for (auto& filter : *copy.mutable_filters()) {
-    switch (filter.filter_case()) {
-      case SelectorProto::Filter::kProperty: {
-        ClientStatus filter_status = MoveAutofillValueRegexpToTextFilter(
-            user_data, filter.mutable_property());
-        if (!filter_status.ok()) {
-          return filter_status;
-        }
-        break;
-      }
-      case SelectorProto::Filter::kInnerText:
-      case SelectorProto::Filter::kValue:
-      case SelectorProto::Filter::kPseudoElementContent:
-      case SelectorProto::Filter::kCssStyle:
-      case SelectorProto::Filter::kCssSelector:
-      case SelectorProto::Filter::kEnterFrame:
-      case SelectorProto::Filter::kPseudoType:
-      case SelectorProto::Filter::kBoundingBox:
-      case SelectorProto::Filter::kNthMatch:
-      case SelectorProto::Filter::kLabelled:
-      case SelectorProto::Filter::kMatchCssSelector:
-      case SelectorProto::Filter::kOnTop:
-      case SelectorProto::Filter::FILTER_NOT_SET:
-        break;
-        // Do not add default here. In case a new filter gets added (that may
-        // contain a RegexpFilter) we want this to fail at compilation here.
-    }
-  }
-  *out_selector = copy;
-  return OkClientStatus();
-}
-
 }  // namespace
 
 ElementFinder::Result::Result() = default;
@@ -170,8 +110,9 @@
     return;
   }
 
+  selector_proto_ = selector_.proto;
   ClientStatus resolve_status =
-      GetUserDataResolvedSelector(selector_, user_data_, &selector_proto_);
+      user_data::ResolveSelectorUserData(&selector_proto_, user_data_);
   if (!resolve_status.ok()) {
     SendErrorResult(resolve_status);
     return;
diff --git a/components/cast_channel/cast_message_handler_unittest.cc b/components/cast_channel/cast_message_handler_unittest.cc
index 982825f..9e440db 100644
--- a/components/cast_channel/cast_message_handler_unittest.cc
+++ b/components/cast_channel/cast_message_handler_unittest.cc
@@ -105,7 +105,7 @@
             kTestUserAgentString,
             "66.0.3331.0",
             "en-US") {
-    ON_CALL(cast_socket_service_, GetSocket(_))
+    ON_CALL(cast_socket_service_, GetSocket(testing::Matcher<int>(_)))
         .WillByDefault(testing::Return(&cast_socket_));
   }
 
diff --git a/components/cast_channel/cast_socket_service.cc b/components/cast_channel/cast_socket_service.cc
index d9322ff..0ac8f4f 100644
--- a/components/cast_channel/cast_socket_service.cc
+++ b/components/cast_channel/cast_socket_service.cc
@@ -71,6 +71,15 @@
   return socket;
 }
 
+void CastSocketService::CloseSocket(int channel_id) {
+  DCHECK(task_runner_->BelongsToCurrentThread());
+  auto* socket = GetSocket(channel_id);
+  if (socket) {
+    socket->Close(base::BindOnce([](int x) {}));
+    RemoveSocket(socket->id());
+  }
+}
+
 CastSocket* CastSocketServiceImpl::GetSocket(int channel_id) const {
   DCHECK(task_runner_->BelongsToCurrentThread());
   DCHECK(channel_id > 0);
diff --git a/components/cast_channel/cast_socket_service.h b/components/cast_channel/cast_socket_service.h
index 9243e51..1627c4d7 100644
--- a/components/cast_channel/cast_socket_service.h
+++ b/components/cast_channel/cast_socket_service.h
@@ -30,6 +30,10 @@
   // CastSocketRegistry. Returns nullptr if no such CastSocket exists.
   virtual std::unique_ptr<CastSocket> RemoveSocket(int channel_id) = 0;
 
+  // Attempts to close an open CastSocket connection corresponding to the given
+  // |ip_endpoint|. Does nothing if the socket_id doesn't exist.
+  virtual void CloseSocket(int channel_id);
+
   // Returns the socket corresponding to |channel_id| if one exists, or nullptr
   // otherwise.
   virtual CastSocket* GetSocket(int channel_id) const = 0;
diff --git a/components/cast_channel/cast_test_util.h b/components/cast_channel/cast_test_util.h
index bcf75d2..95fa59e4 100644
--- a/components/cast_channel/cast_test_util.h
+++ b/components/cast_channel/cast_test_util.h
@@ -99,6 +99,12 @@
                void(const net::IPEndPoint& ip_endpoint,
                     CastSocket::OnOpenCallback& open_cb));
   MOCK_CONST_METHOD1(GetSocket, CastSocket*(int channel_id));
+  MOCK_METHOD(CastSocket*,
+              GetSocket,
+              (const net::IPEndPoint& ip_endpoint),
+              (override, const));
+  MOCK_METHOD(std::unique_ptr<CastSocket>, RemoveSocket, (int channel_id), ());
+  MOCK_METHOD(void, CloseSocket, (int channel_id), (override));
 };
 
 class MockCastSocket : public CastSocket {
diff --git a/components/cdm/renderer/widevine_key_system_properties.cc b/components/cdm/renderer/widevine_key_system_properties.cc
index 061099d..4c522b0 100644
--- a/components/cdm/renderer/widevine_key_system_properties.cc
+++ b/components/cdm/renderer/widevine_key_system_properties.cc
@@ -5,7 +5,6 @@
 #include "components/cdm/renderer/widevine_key_system_properties.h"
 
 #include "base/command_line.h"
-#include "base/compiler_specific.h"
 #include "base/feature_list.h"
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
@@ -169,7 +168,7 @@
     return EmeConfigRule::NOT_SUPPORTED;
   }
 
-  bool hw_secure_codecs_required =
+  [[maybe_unused]] bool hw_secure_codecs_required =
       hw_secure_requirement && *hw_secure_requirement;
 
 #if BUILDFLAG(IS_CHROMEOS)
@@ -216,8 +215,6 @@
   // above.
   if (robustness >= Robustness::HW_SECURE_CRYPTO)
     return EmeConfigRule::HW_SECURE_CODECS_REQUIRED;
-
-  ALLOW_UNUSED_LOCAL(hw_secure_codecs_required);
 #endif  // BUILDFLAG(IS_CHROMEOS)
 
   return EmeConfigRule::SUPPORTED;
diff --git a/components/certificate_transparency/data/log_list.json b/components/certificate_transparency/data/log_list.json
index 84db802..d5253e8 100644
--- a/components/certificate_transparency/data/log_list.json
+++ b/components/certificate_transparency/data/log_list.json
@@ -1,6 +1,6 @@
 {
-  "version": "4.92",
-  "log_list_timestamp": "2022-01-17T01:35:38Z",
+  "version": "4.93",
+  "log_list_timestamp": "2022-01-18T01:34:12Z",
   "operators": [
     {
       "name": "Google",
diff --git a/components/content_settings/renderer/content_settings_agent_impl.cc b/components/content_settings/renderer/content_settings_agent_impl.cc
index f0720ef..e11724a 100644
--- a/components/content_settings/renderer/content_settings_agent_impl.cc
+++ b/components/content_settings/renderer/content_settings_agent_impl.cc
@@ -23,7 +23,6 @@
 #include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
 #include "third_party/blink/public/common/associated_interfaces/associated_interface_registry.h"
 #include "third_party/blink/public/common/browser_interface_broker_proxy.h"
-#include "third_party/blink/public/common/loader/previews_state.h"
 #include "third_party/blink/public/platform/url_conversion.h"
 #include "third_party/blink/public/platform/web_security_origin.h"
 #include "third_party/blink/public/platform/web_url.h"
diff --git a/components/discardable_memory/client/client_discardable_shared_memory_manager.cc b/components/discardable_memory/client/client_discardable_shared_memory_manager.cc
index 0d9cd77..cb84090 100644
--- a/components/discardable_memory/client/client_discardable_shared_memory_manager.cc
+++ b/components/discardable_memory/client/client_discardable_shared_memory_manager.cc
@@ -38,14 +38,15 @@
   // memory usage overhead. 4MB is measured as the ideal size according to the
   // usage statistics. For low-end devices, we care about lowering the memory
   // usage and 1MB is good for the most basic cases.
-  const size_t kDefaultAllocationSize = 4 * kOneMegabyteInBytes;
-  const size_t kDefaultLowEndDeviceAllocationSize = kOneMegabyteInBytes;
+  [[maybe_unused]] const size_t kDefaultAllocationSize =
+      4 * kOneMegabyteInBytes;
+  [[maybe_unused]] const size_t kDefaultLowEndDeviceAllocationSize =
+      kOneMegabyteInBytes;
 
 #if BUILDFLAG(IS_WIN) && defined(ARCH_CPU_32_BITS)
   // On Windows 32 bit, use a smaller chunk, as address space fragmentation may
   // make a 4MiB allocation impossible to fulfill in the browser process.
   // See crbug.com/983348 for details.
-  ALLOW_UNUSED_LOCAL(kDefaultAllocationSize);
   return kDefaultLowEndDeviceAllocationSize;
 #elif BUILDFLAG(IS_FUCHSIA)
   // Low end Fuchsia devices may be very constrained, so use smaller allocations
@@ -54,7 +55,6 @@
                                          : kDefaultAllocationSize;
 
 #else
-  ALLOW_UNUSED_LOCAL(kDefaultLowEndDeviceAllocationSize);
   return kDefaultAllocationSize;
 #endif
 }
diff --git a/components/dom_distiller/content/browser/distiller_javascript_service_impl.cc b/components/dom_distiller/content/browser/distiller_javascript_service_impl.cc
index 8da304a..ed213c6 100644
--- a/components/dom_distiller/content/browser/distiller_javascript_service_impl.cc
+++ b/components/dom_distiller/content/browser/distiller_javascript_service_impl.cc
@@ -3,47 +3,55 @@
 // found in the LICENSE file.
 
 #include "components/dom_distiller/content/browser/distiller_javascript_service_impl.h"
+#include "components/dom_distiller/core/dom_distiller_service.h"
 
 #include "mojo/public/cpp/bindings/self_owned_receiver.h"
 
 namespace dom_distiller {
 
 DistillerJavaScriptServiceImpl::DistillerJavaScriptServiceImpl(
-    DistillerUIHandle* distiller_ui_handle,
-    DistilledPagePrefs* distilled_page_prefs)
-    : distiller_ui_handle_(distiller_ui_handle),
-      distilled_page_prefs_(distilled_page_prefs) {}
+    base::WeakPtr<DomDistillerService> distiller_service_weak_ptr)
+    : distiller_service_weak_ptr_(distiller_service_weak_ptr) {}
 
 DistillerJavaScriptServiceImpl::~DistillerJavaScriptServiceImpl() = default;
 
 void DistillerJavaScriptServiceImpl::HandleDistillerOpenSettingsCall() {
-  if (!distiller_ui_handle_) {
+  if (distiller_service_weak_ptr_.WasInvalidated())
     return;
-  }
 
-  distiller_ui_handle_->OpenSettings();
+  distiller_service_weak_ptr_.get()->GetDistillerUIHandle()->OpenSettings();
 }
 
 void DistillerJavaScriptServiceImpl::HandleStoreThemePref(mojom::Theme theme) {
-  distilled_page_prefs_->SetTheme(theme);
+  if (distiller_service_weak_ptr_.WasInvalidated())
+    return;
+
+  distiller_service_weak_ptr_.get()->GetDistilledPagePrefs()->SetTheme(theme);
 }
 
 void DistillerJavaScriptServiceImpl::HandleStoreFontFamilyPref(
     mojom::FontFamily font_family) {
-  distilled_page_prefs_->SetFontFamily(font_family);
+  if (distiller_service_weak_ptr_.WasInvalidated())
+    return;
+
+  distiller_service_weak_ptr_.get()->GetDistilledPagePrefs()->SetFontFamily(
+      font_family);
 }
 
 void DistillerJavaScriptServiceImpl::HandleStoreFontScalingPref(
     float font_scale) {
-  distilled_page_prefs_->SetFontScaling(font_scale);
+  if (distiller_service_weak_ptr_.WasInvalidated())
+    return;
+
+  distiller_service_weak_ptr_.get()->GetDistilledPagePrefs()->SetFontScaling(
+      font_scale);
 }
 
 void CreateDistillerJavaScriptService(
-    DistillerUIHandle* distiller_ui_handle,
-    DistilledPagePrefs* distilled_page_prefs,
+    base::WeakPtr<DomDistillerService> distiller_service_weak_ptr,
     mojo::PendingReceiver<mojom::DistillerJavaScriptService> receiver) {
   mojo::MakeSelfOwnedReceiver(std::make_unique<DistillerJavaScriptServiceImpl>(
-                                  distiller_ui_handle, distilled_page_prefs),
+                                  distiller_service_weak_ptr),
                               std::move(receiver));
 }
 
diff --git a/components/dom_distiller/content/browser/distiller_javascript_service_impl.h b/components/dom_distiller/content/browser/distiller_javascript_service_impl.h
index 532f1c7..4954957 100644
--- a/components/dom_distiller/content/browser/distiller_javascript_service_impl.h
+++ b/components/dom_distiller/content/browser/distiller_javascript_service_impl.h
@@ -5,23 +5,21 @@
 #ifndef COMPONENTS_DOM_DISTILLER_CONTENT_BROWSER_DISTILLER_JAVASCRIPT_SERVICE_IMPL_H_
 #define COMPONENTS_DOM_DISTILLER_CONTENT_BROWSER_DISTILLER_JAVASCRIPT_SERVICE_IMPL_H_
 
-#include "base/memory/raw_ptr.h"
+#include "base/memory/weak_ptr.h"
 #include "components/dom_distiller/content/common/mojom/distiller_javascript_service.mojom.h"
-#include "components/dom_distiller/core/distilled_page_prefs.h"
-#include "components/dom_distiller/core/distiller_ui_handle.h"
 #include "components/dom_distiller/core/mojom/distilled_page_prefs.mojom.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 
 namespace dom_distiller {
 
-class DistilledPagePrefs;
+class DomDistillerService;
 
 // This is the receiving end of "distiller" JavaScript object calls.
 class DistillerJavaScriptServiceImpl
     : public mojom::DistillerJavaScriptService {
  public:
-  DistillerJavaScriptServiceImpl(DistillerUIHandle* distiller_ui_handle,
-                                 DistilledPagePrefs* distilled_page_prefs);
+  explicit DistillerJavaScriptServiceImpl(
+      base::WeakPtr<DomDistillerService> distiller_service_weak_ptr);
   ~DistillerJavaScriptServiceImpl() override;
 
   // Mojo mojom::DistillerJavaScriptService implementation.
@@ -39,14 +37,12 @@
       const DistillerJavaScriptServiceImpl&) = delete;
 
  private:
-  raw_ptr<DistillerUIHandle> distiller_ui_handle_;
-  raw_ptr<DistilledPagePrefs> distilled_page_prefs_;
+  base::WeakPtr<DomDistillerService> distiller_service_weak_ptr_;
 };
 
 // static
 void CreateDistillerJavaScriptService(
-    DistillerUIHandle* distiller_ui_handle,
-    DistilledPagePrefs* distilled_page_prefs,
+    base::WeakPtr<DomDistillerService> distiller_service_weak_ptr,
     mojo::PendingReceiver<mojom::DistillerJavaScriptService> receiver);
 
 }  // namespace dom_distiller
diff --git a/components/dom_distiller/core/dom_distiller_service.cc b/components/dom_distiller/core/dom_distiller_service.cc
index 54b3b0ec..dba0f17 100644
--- a/components/dom_distiller/core/dom_distiller_service.cc
+++ b/components/dom_distiller/core/dom_distiller_service.cc
@@ -41,7 +41,8 @@
       distiller_factory_(std::move(distiller_factory)),
       distiller_page_factory_(std::move(distiller_page_factory)),
       distilled_page_prefs_(std::move(distilled_page_prefs)),
-      distiller_ui_handle_(std::move(distiller_ui_handle)) {}
+      distiller_ui_handle_(std::move(distiller_ui_handle)),
+      weak_ptr_factory_(this) {}
 
 DomDistillerService::~DomDistillerService() {
   // There shouldn't be any tasks pending at this point.
@@ -132,4 +133,8 @@
   return distiller_ui_handle_.get();
 }
 
+base::WeakPtr<DomDistillerService> DomDistillerService::GetWeakPtr() {
+  return weak_ptr_factory_.GetWeakPtr();
+}
+
 }  // namespace dom_distiller
diff --git a/components/dom_distiller/core/dom_distiller_service.h b/components/dom_distiller/core/dom_distiller_service.h
index 0366e24f..e0efe9e 100644
--- a/components/dom_distiller/core/dom_distiller_service.h
+++ b/components/dom_distiller/core/dom_distiller_service.h
@@ -9,6 +9,7 @@
 #include <vector>
 
 #include "base/callback.h"
+#include "base/memory/weak_ptr.h"
 #include "components/dom_distiller/core/article_entry.h"
 #include "components/dom_distiller/core/distilled_page_prefs.h"
 #include "components/dom_distiller/core/distiller_page.h"
@@ -84,6 +85,7 @@
       std::unique_ptr<SourcePageHandle> handle) override;
   DistilledPagePrefs* GetDistilledPagePrefs() override;
   DistillerUIHandle* GetDistillerUIHandle() override;
+  base::WeakPtr<DomDistillerService> GetWeakPtr();
 
  private:
   void CancelTask(TaskTracker* task);
@@ -110,6 +112,8 @@
 
   typedef std::vector<std::unique_ptr<TaskTracker>> TaskList;
   TaskList tasks_;
+
+  base::WeakPtrFactory<DomDistillerService> weak_ptr_factory_;
 };
 
 }  // namespace dom_distiller
diff --git a/components/exo/wayland/protocol/aura-shell.xml b/components/exo/wayland/protocol/aura-shell.xml
index 9641ab1a..d179af5 100644
--- a/components/exo/wayland/protocol/aura-shell.xml
+++ b/components/exo/wayland/protocol/aura-shell.xml
@@ -24,7 +24,7 @@
     DEALINGS IN THE SOFTWARE.
   </copyright>
 
-  <interface name="zaura_shell" version="28">
+  <interface name="zaura_shell" version="29">
     <description summary="aura_shell">
       The global interface exposing aura shell capabilities is used to
       instantiate an interface extension for a wl_surface object.
@@ -148,7 +148,7 @@
     </request>
   </interface>
 
-  <interface name="zaura_surface" version="25">
+  <interface name="zaura_surface" version="29">
     <description summary="aura shell interface to a wl_surface">
       An additional interface to a wl_surface object, which allows the
       client to access aura shell specific functionality for surface.
@@ -499,6 +499,17 @@
   the window was locked with the trusted state or not.
       </description>
     </request>
+
+    <event name="start_throttle" since="29">
+      <description summary="start throttling on the surface">
+        Informs the client to start throttling on the surface.
+      </description>
+    </event>
+    <event name="end_throttle" since="29">
+      <description summary="end throttling on the surface">
+        Informs the client to end throttling on the surface.
+      </description>
+    </event>
   </interface>
 
   <interface name="zaura_output" version="6">
diff --git a/components/feed/core/v2/api_test/feed_api_reliability_logging_unittest.cc b/components/feed/core/v2/api_test/feed_api_reliability_logging_unittest.cc
index c280ca9..5f1f9e7 100644
--- a/components/feed/core/v2/api_test/feed_api_reliability_logging_unittest.cc
+++ b/components/feed/core/v2/api_test/feed_api_reliability_logging_unittest.cc
@@ -277,7 +277,7 @@
       "LogRequestFinished result=-7 id=1\n"
 
       "LogLaunchFinishedAfterStreamUpdate "
-      "result=NO_CARDS_RESPONSE_ERROR_NON_200\n"
+      "result=NO_CARDS_REQUEST_ERROR_OTHER\n"
 
       "LogAboveTheFoldRender result=FULL_FEED_ERROR\n",
       surface.reliability_logging_bridge.GetEventsString());
diff --git a/components/feed/core/v2/enums.cc b/components/feed/core/v2/enums.cc
index 7980cb9..552f659d 100644
--- a/components/feed/core/v2/enums.cc
+++ b/components/feed/core/v2/enums.cc
@@ -69,6 +69,12 @@
       return out << "kAlreadyHaveUnreadContent";
     case LoadStreamStatus::kNotAWebFeedSubscriber:
       return out << "kNotAWebFeedSubscriber";
+    case LoadStreamStatus::kAccountTokenFetchFailedWrongAccount:
+      return out << "kAccountTokenFetchFailedWrongAccount";
+    case LoadStreamStatus::kAccountTokenFetchTimedOut:
+      return out << "kAccountTokenFetchTimedOut";
+    case LoadStreamStatus::kNetworkFetchTimedOut:
+      return out << "kNetworkFetchTimedOut";
   }
 #else
   return out << (static_cast<int>(value));
@@ -106,6 +112,10 @@
     case LoadStreamStatus::kAbortWithPendingClearAll:
     case LoadStreamStatus::kAlreadyHaveUnreadContent:
     case LoadStreamStatus::kNotAWebFeedSubscriber:
+
+    case LoadStreamStatus::kAccountTokenFetchFailedWrongAccount:
+    case LoadStreamStatus::kAccountTokenFetchTimedOut:
+    case LoadStreamStatus::kNetworkFetchTimedOut:
       return false;
   }
 }
diff --git a/components/feed/core/v2/enums.h b/components/feed/core/v2/enums.h
index 0c99fe0f..c30d3de 100644
--- a/components/feed/core/v2/enums.h
+++ b/components/feed/core/v2/enums.h
@@ -81,7 +81,10 @@
   kAbortWithPendingClearAll = 24,
   kAlreadyHaveUnreadContent = 25,
   kNotAWebFeedSubscriber = 26,
-  kMaxValue = kNotAWebFeedSubscriber,
+  kAccountTokenFetchFailedWrongAccount = 27,
+  kAccountTokenFetchTimedOut = 28,
+  kNetworkFetchTimedOut = 29,
+  kMaxValue = kNetworkFetchTimedOut,
 };
 
 // Were we able to load fresh Feed data. This should be 'true' unless some kind
diff --git a/components/feed/core/v2/feed_network_impl.cc b/components/feed/core/v2/feed_network_impl.cc
index 2545103f..3578dd64 100644
--- a/components/feed/core/v2/feed_network_impl.cc
+++ b/components/feed/core/v2/feed_network_impl.cc
@@ -16,6 +16,8 @@
 #include "base/metrics/histogram_macros.h"
 #include "base/strings/strcat.h"
 #include "base/strings/string_util.h"
+#include "base/task/post_task.h"
+#include "base/threading/sequenced_task_runner_handle.h"
 #include "base/time/time.h"
 #include "components/feed/core/common/pref_names.h"
 #include "components/feed/core/proto/v2/wire/feed_query.pb.h"
@@ -181,6 +183,12 @@
   void Start(base::OnceCallback<void(RawResponse)> done_callback) {
     done_callback_ = std::move(done_callback);
 
+    if (delegate_->IsOffline()) {
+      std::move(done_callback_)
+          .Run(MakeFailureResponse(net::ERR_INTERNET_DISCONNECTED,
+                                   AccountTokenFetchStatus::kUnspecified));
+      return;
+    }
     if (account_info_.IsEmpty()) {
       StartLoader();
       return;
@@ -191,19 +199,44 @@
 
  private:
   void StartAccessTokenFetch() {
-    // It's safe to pass base::Unretained(this) since deleting the token fetcher
-    // will prevent the callback from being completed.
     token_fetcher_ = std::make_unique<signin::PrimaryAccountAccessTokenFetcher>(
         "feed", identity_manager_, GetAuthScopes(),
-        base::BindOnce(&NetworkFetch::AccessTokenFetchFinished,
-                       base::Unretained(this), base::TimeTicks::Now()),
+        base::BindOnce(&NetworkFetch::AccessTokenFetchFinished, GetWeakPtr(),
+                       base::TimeTicks::Now()),
         signin::PrimaryAccountAccessTokenFetcher::Mode::kWaitUntilAvailable);
+    base::SequencedTaskRunnerHandle::Get()->PostDelayedTask(
+        FROM_HERE,
+        base::BindOnce(&NetworkFetch::AccessTokenTimeout, GetWeakPtr()),
+        kAccessTokenFetchTimeout);
+  }
+
+  static RawResponse MakeFailureResponse(
+      int32_t status_code,
+      AccountTokenFetchStatus token_fetch_status) {
+    NetworkResponseInfo response_info;
+    RawResponse raw_response;
+    response_info.status_code = status_code;
+    response_info.account_token_fetch_status = token_fetch_status;
+    raw_response.response_info = std::move(response_info);
+    return raw_response;
+  }
+
+  void AccessTokenTimeout() {
+    if (access_token_fetch_complete_)
+      return;
+    access_token_fetch_complete_ = true;
+    std::move(done_callback_)
+        .Run(MakeFailureResponse(net::ERR_TIMED_OUT,
+                                 AccountTokenFetchStatus::kTimedOut));
   }
 
   void AccessTokenFetchFinished(base::TimeTicks token_start_ticks,
                                 GoogleServiceAuthError error,
                                 signin::AccessTokenInfo access_token_info) {
     DCHECK(!account_info_.IsEmpty());
+    if (access_token_fetch_complete_)
+      return;
+    access_token_fetch_complete_ = true;
     UMA_HISTOGRAM_ENUMERATION(
         "ContentSuggestions.Feed.Network.TokenFetchStatus", error.state(),
         GoogleServiceAuthError::NUM_STATES);
@@ -216,11 +249,10 @@
 
     // Abort if the signed-in user doesn't match.
     if (delegate_->GetAccountInfo() != account_info_) {
-      NetworkResponseInfo response_info;
-      RawResponse raw_response;
-      response_info.status_code = net::ERR_INVALID_ARGUMENT;
-      raw_response.response_info = std::move(response_info);
-      std::move(done_callback_).Run(std::move(raw_response));
+      std::move(done_callback_)
+          .Run(
+              MakeFailureResponse(net::ERR_INVALID_ARGUMENT,
+                                  AccountTokenFetchStatus::kUnexpectedAccount));
       return;
     }
 
@@ -410,10 +442,14 @@
     raw_response.response_bytes = std::move(response_body);
     std::move(done_callback_).Run(std::move(raw_response));
   }
+  base::WeakPtr<NetworkFetch> GetWeakPtr() {
+    return weak_ptr_factory_.GetWeakPtr();
+  }
 
   GURL url_;
   const std::string request_method_;
   std::string access_token_;
+  bool access_token_fetch_complete_ = false;
   const std::string request_body_;
   raw_ptr<FeedNetworkImpl::Delegate> delegate_;
   const raw_ptr<signin::IdentityManager> identity_manager_;
@@ -432,6 +468,7 @@
   // there is one.
   base::TimeTicks loader_only_start_ticks_;
   bool allow_bless_auth_ = false;
+  base::WeakPtrFactory<NetworkFetch> weak_ptr_factory_{this};
 };
 
 FeedNetworkImpl::FeedNetworkImpl(
diff --git a/components/feed/core/v2/feed_network_impl.h b/components/feed/core/v2/feed_network_impl.h
index 3a1920b..43d795c 100644
--- a/components/feed/core/v2/feed_network_impl.h
+++ b/components/feed/core/v2/feed_network_impl.h
@@ -28,6 +28,7 @@
 }  // namespace network
 
 namespace feed {
+constexpr base::TimeDelta kAccessTokenFetchTimeout = base::Seconds(10);
 
 class FeedNetworkImpl : public FeedNetwork {
  public:
@@ -41,6 +42,8 @@
     // Returns the AccountInfo for the signed in user if they are sync-enabled,
     // or empty otherwise.
     virtual AccountInfo GetAccountInfo() = 0;
+    // Returns whether the device is offline.
+    virtual bool IsOffline() = 0;
   };
 
   FeedNetworkImpl(Delegate* delegate,
diff --git a/components/feed/core/v2/feed_network_impl_unittest.cc b/components/feed/core/v2/feed_network_impl_unittest.cc
index 8f654d9..e4544d9 100644
--- a/components/feed/core/v2/feed_network_impl_unittest.cc
+++ b/components/feed/core/v2/feed_network_impl_unittest.cc
@@ -85,7 +85,9 @@
         identity_test_env_->identity_manager()->GetPrimaryAccountInfo(
             signin::ConsentLevel::kSync)};
   }
+  bool IsOffline() override { return is_offline_; }
 
+  bool is_offline_ = false;
   raw_ptr<signin::IdentityTestEnvironment> identity_test_env_;
 };
 
@@ -212,7 +214,7 @@
  protected:
   scoped_refptr<net::HttpResponseHeaders> response_headers_;
 
- private:
+ protected:
   signin::IdentityTestEnvironment identity_test_env_;
   TestDelegate delegate_{&identity_test_env_};
   variations::ScopedVariationsIdsProvider scoped_variations_ids_provider_{
@@ -336,6 +338,8 @@
   ASSERT_TRUE(receiver.GetResult());
   const QueryRequestResult& result = *receiver.GetResult();
   EXPECT_EQ(net::ERR_INVALID_ARGUMENT, result.response_info.status_code);
+  EXPECT_EQ(AccountTokenFetchStatus::kUnexpectedAccount,
+            result.response_info.account_token_fetch_status);
   EXPECT_FALSE(result.response_body);
   histogram().ExpectBucketCount(
       "ContentSuggestions.Feed.Network.ResponseStatus.FeedQuery",
@@ -368,6 +372,83 @@
       "ContentSuggestions.Feed.Network.Duration", base::Seconds(30), 1);
 }
 
+TEST_F(FeedNetworkTest, AccountTokenFetchTimeout) {
+  identity_test_env_.RemoveRefreshTokenForPrimaryAccount();
+  identity_test_env_.SetAutomaticIssueOfAccessTokens(false);
+
+  CallbackReceiver<QueryRequestResult> receiver;
+  feed_network()->SendQueryRequest(NetworkRequestType::kFeedQuery,
+                                   GetTestFeedRequest(), account_info(),
+                                   receiver.Bind());
+  task_environment_.FastForwardBy(kAccessTokenFetchTimeout - base::Seconds(1));
+  ASSERT_FALSE(receiver.GetResult());
+
+  task_environment_.FastForwardBy(base::Seconds(1));
+
+  ASSERT_TRUE(receiver.GetResult());
+  const QueryRequestResult& result = *receiver.GetResult();
+  EXPECT_EQ(AccountTokenFetchStatus::kTimedOut,
+            result.response_info.account_token_fetch_status);
+  EXPECT_EQ(net::ERR_TIMED_OUT, result.response_info.status_code);
+}
+
+TEST_F(FeedNetworkTest, AccountTokenRefreshCompleteAfterFetchTimeout) {
+  identity_test_env_.SetAutomaticIssueOfAccessTokens(false);
+
+  CallbackReceiver<QueryRequestResult> receiver;
+  feed_network()->SendQueryRequest(NetworkRequestType::kFeedQuery,
+                                   GetTestFeedRequest(), account_info(),
+                                   receiver.Bind());
+  // Time-out the token fetch and then complete it.
+  task_environment_.FastForwardBy(kAccessTokenFetchTimeout);
+  identity_test_env_.SetAutomaticIssueOfAccessTokens(true);
+  identity_test_env_.SetRefreshTokenForPrimaryAccount();
+
+  // Ensure the fetch failed.
+  const QueryRequestResult& result = receiver.RunAndGetResult();
+  EXPECT_EQ(AccountTokenFetchStatus::kTimedOut,
+            result.response_info.account_token_fetch_status);
+  EXPECT_EQ(net::ERR_TIMED_OUT, result.response_info.status_code);
+}
+
+TEST_F(FeedNetworkTest, AccountTokenRefreshCompleteBeforeFetchTimeout) {
+  identity_test_env_.SetAutomaticIssueOfAccessTokens(false);
+
+  CallbackReceiver<QueryRequestResult> receiver;
+  feed_network()->SendQueryRequest(NetworkRequestType::kFeedQuery,
+                                   GetTestFeedRequest(), account_info(),
+                                   receiver.Bind());
+  // Time-out the token fetch just after it completes.
+  identity_test_env_.SetAutomaticIssueOfAccessTokens(true);
+  identity_test_env_.SetRefreshTokenForPrimaryAccount();
+  task_environment_.FastForwardBy(kAccessTokenFetchTimeout);
+  RespondToQueryRequest(GetTestFeedResponse(), net::HTTP_OK);
+
+  // Ensure the fetch failed.
+  const QueryRequestResult& result = receiver.RunAndGetResult();
+  EXPECT_EQ(AccountTokenFetchStatus::kUnspecified,
+            result.response_info.account_token_fetch_status);
+  EXPECT_EQ(net::HTTP_OK, result.response_info.status_code);
+}
+
+TEST_F(FeedNetworkTest, FetchImmediatelyAbortsIfOffline) {
+  // Trying to fetch the token would timeout, but because the device is offline,
+  // the fetch quits immediately.
+  identity_test_env_.RemoveRefreshTokenForPrimaryAccount();
+  identity_test_env_.SetAutomaticIssueOfAccessTokens(false);
+  delegate_.is_offline_ = true;
+
+  CallbackReceiver<QueryRequestResult> receiver;
+  feed_network()->SendQueryRequest(NetworkRequestType::kFeedQuery,
+                                   GetTestFeedRequest(), account_info(),
+                                   receiver.Bind());
+  ASSERT_TRUE(receiver.GetResult());
+  const QueryRequestResult& result = *receiver.GetResult();
+  EXPECT_EQ(AccountTokenFetchStatus::kUnspecified,
+            result.response_info.account_token_fetch_status);
+  EXPECT_EQ(net::ERR_INTERNET_DISCONNECTED, result.response_info.status_code);
+}
+
 TEST_F(FeedNetworkTest, ParallelRequests) {
   CallbackReceiver<QueryRequestResult> receiver1, receiver2;
   feed_network()->SendQueryRequest(NetworkRequestType::kFeedQuery,
@@ -553,6 +634,8 @@
   const FeedNetwork::ApiResult<feedwire::UploadActionsResponse>& result =
       *receiver.GetResult();
   EXPECT_EQ(net::ERR_INVALID_ARGUMENT, result.response_info.status_code);
+  EXPECT_EQ(AccountTokenFetchStatus::kUnexpectedAccount,
+            result.response_info.account_token_fetch_status);
   EXPECT_FALSE(result.response_body);
   histogram().ExpectBucketCount(
       "ContentSuggestions.Feed.Network.ResponseStatus.UploadActions",
diff --git a/components/feed/core/v2/public/feed_service.cc b/components/feed/core/v2/public/feed_service.cc
index 0830a65cf9..815ff1d 100644
--- a/components/feed/core/v2/public/feed_service.cc
+++ b/components/feed/core/v2/public/feed_service.cc
@@ -110,6 +110,8 @@
         identity_manager_->GetPrimaryAccountInfo(signin::ConsentLevel::kSync));
   }
 
+  bool IsOffline() override { return net::NetworkChangeNotifier::IsOffline(); }
+
  private:
   raw_ptr<FeedService::Delegate> service_delegate_;
   raw_ptr<signin::IdentityManager> identity_manager_;
diff --git a/components/feed/core/v2/public/test/stub_feed_api.cc b/components/feed/core/v2/public/test/stub_feed_api.cc
index 00f0f362..eb9a8490 100644
--- a/components/feed/core/v2/public/test/stub_feed_api.cc
+++ b/components/feed/core/v2/public/test/stub_feed_api.cc
@@ -4,12 +4,10 @@
 
 #include "components/feed/core/v2/public/test/stub_feed_api.h"
 
-#include "base/compiler_specific.h"
-
 namespace feed {
 
 namespace {
-ALLOW_UNUSED_TYPE void EnsureStubFeedApiHasNoPureVirtualFunctions() {
+[[maybe_unused]] void EnsureStubFeedApiHasNoPureVirtualFunctions() {
   (void)StubFeedApi();
 }
 }  // namespace
diff --git a/components/feed/core/v2/public/types.h b/components/feed/core/v2/public/types.h
index 522a8d0..0574bd6 100644
--- a/components/feed/core/v2/public/types.h
+++ b/components/feed/core/v2/public/types.h
@@ -43,6 +43,17 @@
   kRefreshWebFeed,
 };
 
+enum class AccountTokenFetchStatus {
+  // Token fetch was not attempted, or status is unknown.
+  kUnspecified = 0,
+  // Successfully fetch the correct token.
+  kSuccess = 1,
+  // The primary account changed before fetching the token completed.
+  kUnexpectedAccount = 2,
+  // Timed out while fetching the token.
+  kTimedOut = 3,
+};
+
 // Information about the Chrome build and feature flags.
 struct ChromeInfo {
   version_info::Channel channel{};
@@ -82,6 +93,8 @@
   size_t encoded_size_bytes = 0;
   // If it was a signed-in request, this is the associated account info.
   AccountInfo account_info;
+  AccountTokenFetchStatus account_token_fetch_status =
+      AccountTokenFetchStatus::kUnspecified;
   base::TimeTicks fetch_time_ticks;
   base::TimeTicks loader_start_time_ticks;
 };
diff --git a/components/feed/core/v2/surface_updater.cc b/components/feed/core/v2/surface_updater.cc
index c0f35be..64fe159 100644
--- a/components/feed/core/v2/surface_updater.cc
+++ b/components/feed/core/v2/surface_updater.cc
@@ -171,6 +171,9 @@
     case LoadStreamStatus::kCannotLoadFromNetworkOffline:
     case LoadStreamStatus::kCannotLoadFromNetworkThrottled:
     case LoadStreamStatus::kNetworkFetchFailed:
+    case LoadStreamStatus::kAccountTokenFetchFailedWrongAccount:
+    case LoadStreamStatus::kAccountTokenFetchTimedOut:
+    case LoadStreamStatus::kNetworkFetchTimedOut:
       return feedui::ZeroStateSlice::CANT_REFRESH;
     case LoadStreamStatus::kNotAWebFeedSubscriber:
       return feedui::ZeroStateSlice::NO_WEB_FEED_SUBSCRIPTIONS;
diff --git a/components/feed/core/v2/tasks/load_more_task.cc b/components/feed/core/v2/tasks/load_more_task.cc
index 390dc4b08c..64759bd 100644
--- a/components/feed/core/v2/tasks/load_more_task.cc
+++ b/components/feed/core/v2/tasks/load_more_task.cc
@@ -15,12 +15,14 @@
 #include "components/feed/core/proto/v2/wire/client_info.pb.h"
 #include "components/feed/core/proto/v2/wire/feed_request.pb.h"
 #include "components/feed/core/proto/v2/wire/request.pb.h"
+#include "components/feed/core/v2/enums.h"
 #include "components/feed/core/v2/feed_network.h"
 #include "components/feed/core/v2/feed_stream.h"
 #include "components/feed/core/v2/feedstore_util.h"
 #include "components/feed/core/v2/proto_util.h"
 #include "components/feed/core/v2/protocol_translator.h"
 #include "components/feed/core/v2/stream_model.h"
+#include "components/feed/core/v2/tasks/load_stream_task.h"
 #include "components/feed/core/v2/tasks/upload_actions_task.h"
 #include "components/feed/core/v2/wire_response_translator.h"
 #include "components/feed/feed_feature_list.h"
@@ -122,8 +124,11 @@
   StreamModel* model = stream_.GetModel(stream_type_);
   DCHECK(model) << "Model was unloaded outside of a Task";
 
-  if (!response_body)
-    return Done(LoadStreamStatus::kNoResponseBody);
+  LoadStreamStatus network_status = LoadStreamTask::LaunchResultFromNetworkInfo(
+                                        response_info, response_body != nullptr)
+                                        .load_stream_status;
+  if (network_status != LoadStreamStatus::kNoStatus)
+    return Done(network_status);
 
   RefreshResponseData translated_response =
       stream_.GetWireResponseTranslator().TranslateWireResponse(
diff --git a/components/feed/core/v2/tasks/load_stream_task.cc b/components/feed/core/v2/tasks/load_stream_task.cc
index 5f2b263..ff088f85 100644
--- a/components/feed/core/v2/tasks/load_stream_task.cc
+++ b/components/feed/core/v2/tasks/load_stream_task.cc
@@ -26,10 +26,12 @@
 #include "components/feed/core/v2/metrics_reporter.h"
 #include "components/feed/core/v2/proto_util.h"
 #include "components/feed/core/v2/protocol_translator.h"
+#include "components/feed/core/v2/public/types.h"
 #include "components/feed/core/v2/stream_model.h"
 #include "components/feed/core/v2/tasks/upload_actions_task.h"
 #include "components/feed/core/v2/types.h"
 #include "components/feed/feed_feature_list.h"
+#include "net/base/net_errors.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace feed {
@@ -65,6 +67,47 @@
 Result::Result(Result&&) = default;
 Result& Result::operator=(Result&&) = default;
 
+// static
+LaunchResult LoadStreamTask::LaunchResultFromNetworkInfo(
+    const NetworkResponseInfo& response_info,
+    bool has_parsed_body) {
+  if (response_info.status_code == 200) {
+    if (has_parsed_body) {
+      // Success.
+      return {LoadStreamStatus::kNoStatus,
+              feedwire::DiscoverLaunchResult::CARDS_UNSPECIFIED};
+    }
+    if (response_info.response_body_bytes > 0) {
+      return {
+          LoadStreamStatus::kCannotParseNetworkResponseBody,
+          feedwire::DiscoverLaunchResult::NO_CARDS_RESPONSE_ERROR_ZERO_CARDS};
+    }
+    return {LoadStreamStatus::kNoResponseBody,
+            feedwire::DiscoverLaunchResult::NO_CARDS_RESPONSE_ERROR_ZERO_CARDS};
+  }
+
+  switch (response_info.account_token_fetch_status) {
+    case AccountTokenFetchStatus::kUnspecified:
+      if (response_info.status_code == net::ERR_TIMED_OUT) {
+        return {LoadStreamStatus::kNetworkFetchTimedOut,
+                feedwire::DiscoverLaunchResult::NO_CARDS_REQUEST_ERROR_OTHER};
+      }
+      break;
+    case AccountTokenFetchStatus::kSuccess:
+      break;
+    case AccountTokenFetchStatus::kUnexpectedAccount:
+      return {
+          LoadStreamStatus::kAccountTokenFetchFailedWrongAccount,
+          feedwire::DiscoverLaunchResult::NO_CARDS_FAILED_TO_GET_AUTH_TOKEN};
+    case AccountTokenFetchStatus::kTimedOut:
+      return {
+          LoadStreamStatus::kAccountTokenFetchTimedOut,
+          feedwire::DiscoverLaunchResult::NO_CARDS_FAILED_TO_GET_AUTH_TOKEN};
+  }
+  return {LoadStreamStatus::kNetworkFetchFailed,
+          feedwire::DiscoverLaunchResult::NO_CARDS_RESPONSE_ERROR_NON_200};
+}
+
 LoadStreamTask::LoadStreamTask(const Options& options,
                                FeedStream* stream,
                                base::OnceCallback<void(Result)> done_callback)
@@ -321,23 +364,10 @@
 
   network_response_info_ = response_info;
 
-  if (response_info.status_code != 200) {
-    return RequestFinished(
-        {LoadStreamStatus::kNetworkFetchFailed,
-         feedwire::DiscoverLaunchResult::NO_CARDS_RESPONSE_ERROR_NON_200});
-  }
-
-  if (!response_body) {
-    if (response_info.response_body_bytes > 0) {
-      return RequestFinished(
-          {LoadStreamStatus::kCannotParseNetworkResponseBody,
-           feedwire::DiscoverLaunchResult::NO_CARDS_RESPONSE_ERROR_ZERO_CARDS});
-    } else {
-      return RequestFinished(
-          {LoadStreamStatus::kNoResponseBody,
-           feedwire::DiscoverLaunchResult::NO_CARDS_RESPONSE_ERROR_ZERO_CARDS});
-    }
-  }
+  LaunchResult network_status =
+      LaunchResultFromNetworkInfo(response_info, response_body != nullptr);
+  if (network_status.load_stream_status != LoadStreamStatus::kNoStatus)
+    return RequestFinished(network_status);
 
   RefreshResponseData response_data =
       stream_.GetWireResponseTranslator().TranslateWireResponse(
diff --git a/components/feed/core/v2/tasks/load_stream_task.h b/components/feed/core/v2/tasks/load_stream_task.h
index 5d0abe5..6289b81 100644
--- a/components/feed/core/v2/tasks/load_stream_task.h
+++ b/components/feed/core/v2/tasks/load_stream_task.h
@@ -6,6 +6,7 @@
 #define COMPONENTS_FEED_CORE_V2_TASKS_LOAD_STREAM_TASK_H_
 
 #include <memory>
+#include <utility>
 
 #include "base/callback.h"
 #include "base/memory/weak_ptr.h"
@@ -35,6 +36,14 @@
 // This task has three modes, see |LoadType| in enums.h.
 class LoadStreamTask : public offline_pages::Task {
  public:
+  // Returns the `LaunchResult` that contains the terminal failure result if the
+  // parameters do not represent a successful Feed response. Returns a
+  // `load_stream_status` of `LoadStreamStatus::kNoStatus` if there was no
+  // failure.
+  static LaunchResult LaunchResultFromNetworkInfo(
+      const NetworkResponseInfo& network_response_info,
+      bool has_parsed_body);
+
   struct Options {
     // The stream type to load.
     StreamType stream_type;
diff --git a/components/gwp_asan/crash_handler/crash_handler.cc b/components/gwp_asan/crash_handler/crash_handler.cc
index a401b41..7dffbeae 100644
--- a/components/gwp_asan/crash_handler/crash_handler.cc
+++ b/components/gwp_asan/crash_handler/crash_handler.cc
@@ -8,7 +8,6 @@
 #include <memory>
 #include <string>
 
-#include "base/compiler_specific.h"
 #include "base/logging.h"
 #include "components/gwp_asan/crash_handler/crash.pb.h"
 #include "components/gwp_asan/crash_handler/crash_analyzer.h"
@@ -42,9 +41,8 @@
     uint32_t stream_type,
     const Crash& crash)
     : crashpad::MinidumpUserExtensionStreamDataSource(stream_type) {
-  bool result = crash.SerializeToString(&data_);
+  [[maybe_unused]] bool result = crash.SerializeToString(&data_);
   DCHECK(result);
-  ALLOW_UNUSED_LOCAL(result);
 }
 
 size_t BufferExtensionStreamDataSource::StreamDataSize() {
diff --git a/components/history/core/browser/sync/delete_directive_handler.h b/components/history/core/browser/sync/delete_directive_handler.h
index 51da3e16..98f8c98 100644
--- a/components/history/core/browser/sync/delete_directive_handler.h
+++ b/components/history/core/browser/sync/delete_directive_handler.h
@@ -20,6 +20,10 @@
 
 class GURL;
 
+namespace base {
+class Time;
+}
+
 namespace sync_pb {
 class HistoryDeleteDirectiveSpecifics;
 }
diff --git a/components/media_router/common/discovery/media_sink_internal.h b/components/media_router/common/discovery/media_sink_internal.h
index edbe0bb..d75f136 100644
--- a/components/media_router/common/discovery/media_sink_internal.h
+++ b/components/media_router/common/discovery/media_sink_internal.h
@@ -53,6 +53,9 @@
   // True if Cast channel is opened from DIAL sink.
   bool discovered_by_dial = false;
 
+  // True if Cast Device was discovered via access code.
+  bool discovered_by_access_code = false;
+
   CastSinkExtraData();
   CastSinkExtraData(const CastSinkExtraData& other);
   CastSinkExtraData(CastSinkExtraData&& other);
diff --git a/components/media_router/common/discovery/media_sink_service_base.h b/components/media_router/common/discovery/media_sink_service_base.h
index 5512197d..c4bc0ad 100644
--- a/components/media_router/common/discovery/media_sink_service_base.h
+++ b/components/media_router/common/discovery/media_sink_service_base.h
@@ -73,7 +73,7 @@
   // Notifies |observers_| that the sink has been added, updated, or removed.
   // Also invokes |StartTimer()|.
   void AddOrUpdateSink(const MediaSinkInternal& sink);
-  void RemoveSink(const MediaSinkInternal& sink);
+  virtual void RemoveSink(const MediaSinkInternal& sink);
   void RemoveSinkById(const MediaSink::Id& sink_id);
 
   const base::flat_map<MediaSink::Id, MediaSinkInternal>& GetSinks() const;
diff --git a/components/metrics/metrics_state_manager.cc b/components/metrics/metrics_state_manager.cc
index d3c9b92c..70713a6 100644
--- a/components/metrics/metrics_state_manager.cc
+++ b/components/metrics/metrics_state_manager.cc
@@ -17,7 +17,6 @@
 #include "base/callback_helpers.h"
 #include "base/check.h"
 #include "base/command_line.h"
-#include "base/compiler_specific.h"
 #include "base/debug/leak_annotations.h"
 #include "base/guid.h"
 #include "base/memory/raw_ptr.h"
@@ -254,7 +253,7 @@
   DCHECK(!load_client_info_.is_null());
   ResetMetricsIDsIfNecessary();
 
-  bool is_first_run = false;
+  [[maybe_unused]] bool is_first_run = false;
   int64_t install_date = local_state_->GetInt64(prefs::kInstallDate);
 
   // Set the install date if this is our first run.
@@ -272,9 +271,7 @@
     ForceClientIdCreation();
   }
 
-#if BUILDFLAG(IS_WIN)
-  ALLOW_UNUSED_LOCAL(is_first_run);
-#else
+#if !BUILDFLAG(IS_WIN)
   if (is_first_run) {
     // If this is a first run (no install date) and there's no client id, then
     // generate a provisional client id now. This id will be used for field
diff --git a/components/offline_pages/content/background_loader/background_loader_contents.cc b/components/offline_pages/content/background_loader/background_loader_contents.cc
index 24b6b04..2834e3e 100644
--- a/components/offline_pages/content/background_loader/background_loader_contents.cc
+++ b/components/offline_pages/content/background_loader/background_loader_contents.cc
@@ -126,13 +126,6 @@
   return false;  // No permissions granted.
 }
 
-void BackgroundLoaderContents::AdjustPreviewsStateForNavigation(
-    content::WebContents* web_contents,
-    blink::PreviewsState* previews_state) {
-  if (*previews_state == 0)
-    *previews_state = blink::PreviewsTypes::PREVIEWS_OFF;
-}
-
 bool BackgroundLoaderContents::ShouldAllowLazyLoad() {
   return false;
 }
diff --git a/components/offline_pages/content/background_loader/background_loader_contents.h b/components/offline_pages/content/background_loader/background_loader_contents.h
index 06014c6..c6bd5c19 100644
--- a/components/offline_pages/content/background_loader/background_loader_contents.h
+++ b/components/offline_pages/content/background_loader/background_loader_contents.h
@@ -88,9 +88,6 @@
   bool CheckMediaAccessPermission(content::RenderFrameHost* render_frame_host,
                                   const GURL& security_origin,
                                   blink::mojom::MediaStreamType type) override;
-  void AdjustPreviewsStateForNavigation(
-      content::WebContents* web_contents,
-      blink::PreviewsState* previews_state) override;
   bool ShouldAllowLazyLoad() override;
 
  private:
diff --git a/components/offline_pages/content/background_loader/background_loader_contents_unittest.cc b/components/offline_pages/content/background_loader/background_loader_contents_unittest.cc
index 9553401..15e346b9 100644
--- a/components/offline_pages/content/background_loader/background_loader_contents_unittest.cc
+++ b/components/offline_pages/content/background_loader/background_loader_contents_unittest.cc
@@ -185,16 +185,4 @@
       blink::mojom::MediaStreamType::GUM_TAB_VIDEO_CAPTURE /* type */));
 }
 
-TEST_F(BackgroundLoaderContentsTest, AdjustPreviewsState) {
-  blink::PreviewsState previews_state;
-
-  // If the state starts out as off or disabled, it should stay that way.
-  previews_state = blink::PreviewsTypes::PREVIEWS_OFF;
-  contents()->AdjustPreviewsStateForNavigation(nullptr, &previews_state);
-  EXPECT_EQ(previews_state, blink::PreviewsTypes::PREVIEWS_OFF);
-  previews_state = blink::PreviewsTypes::PREVIEWS_NO_TRANSFORM;
-  contents()->AdjustPreviewsStateForNavigation(nullptr, &previews_state);
-  EXPECT_EQ(previews_state, blink::PreviewsTypes::PREVIEWS_NO_TRANSFORM);
-}
-
 }  // namespace background_loader
diff --git a/components/page_load_metrics/renderer/metrics_render_frame_observer.cc b/components/page_load_metrics/renderer/metrics_render_frame_observer.cc
index 323a0f9..1e67661 100644
--- a/components/page_load_metrics/renderer/metrics_render_frame_observer.cc
+++ b/components/page_load_metrics/renderer/metrics_render_frame_observer.cc
@@ -173,8 +173,7 @@
     const GURL& response_url,
     int request_id,
     const network::mojom::URLResponseHead& response_head,
-    network::mojom::RequestDestination request_destination,
-    blink::PreviewsState previews_state) {
+    network::mojom::RequestDestination request_destination) {
   if (provisional_frame_resource_data_use_ &&
       blink::IsRequestDestinationFrame(request_destination)) {
     // TODO(rajendrant): This frame request might start before the provisional
diff --git a/components/page_load_metrics/renderer/metrics_render_frame_observer.h b/components/page_load_metrics/renderer/metrics_render_frame_observer.h
index c478cfd..069353f 100644
--- a/components/page_load_metrics/renderer/metrics_render_frame_observer.h
+++ b/components/page_load_metrics/renderer/metrics_render_frame_observer.h
@@ -67,11 +67,11 @@
                           uint32_t grid_ng_block_count) override;
   void DidObserveLazyLoadBehavior(
       blink::WebLocalFrameClient::LazyLoadBehavior lazy_load_behavior) override;
-  void DidStartResponse(const GURL& response_url,
-                        int request_id,
-                        const network::mojom::URLResponseHead& response_head,
-                        network::mojom::RequestDestination request_destination,
-                        blink::PreviewsState previews_state) override;
+  void DidStartResponse(
+      const GURL& response_url,
+      int request_id,
+      const network::mojom::URLResponseHead& response_head,
+      network::mojom::RequestDestination request_destination) override;
   void DidReceiveTransferSizeUpdate(int request_id,
                                     int received_data_length) override;
   void DidCompleteResponse(
diff --git a/components/page_load_metrics/renderer/page_resource_data_use.h b/components/page_load_metrics/renderer/page_resource_data_use.h
index 0146dee..9fed70f 100644
--- a/components/page_load_metrics/renderer/page_resource_data_use.h
+++ b/components/page_load_metrics/renderer/page_resource_data_use.h
@@ -7,7 +7,6 @@
 
 #include "components/page_load_metrics/common/page_load_metrics.mojom.h"
 #include "services/network/public/mojom/url_response_head.mojom-forward.h"
-#include "third_party/blink/public/common/loader/previews_state.h"
 #include "third_party/blink/public/mojom/loader/resource_load_info.mojom-shared.h"
 #include "url/origin.h"
 
diff --git a/components/page_load_metrics/renderer/page_timing_metrics_sender.h b/components/page_load_metrics/renderer/page_timing_metrics_sender.h
index 19168bd..d5766260 100644
--- a/components/page_load_metrics/renderer/page_timing_metrics_sender.h
+++ b/components/page_load_metrics/renderer/page_timing_metrics_sender.h
@@ -14,7 +14,6 @@
 #include "components/page_load_metrics/renderer/page_timing_metadata_recorder.h"
 #include "services/network/public/mojom/url_response_head.mojom-forward.h"
 #include "third_party/blink/public/common/loader/loading_behavior_flag.h"
-#include "third_party/blink/public/common/loader/previews_state.h"
 #include "third_party/blink/public/common/responsiveness_metrics/user_interaction_latency.h"
 #include "third_party/blink/public/common/use_counter/use_counter_feature_tracker.h"
 #include "third_party/blink/public/web/web_local_frame_client.h"
diff --git a/components/password_manager/core/browser/login_database_unittest.cc b/components/password_manager/core/browser/login_database_unittest.cc
index 7fb4f1f..ad28856 100644
--- a/components/password_manager/core/browser/login_database_unittest.cc
+++ b/components/password_manager/core/browser/login_database_unittest.cc
@@ -11,7 +11,6 @@
 #include <tuple>
 #include <utility>
 
-#include "base/compiler_specific.h"
 #include "base/containers/flat_map.h"
 #include "base/files/file_util.h"
 #include "base/files/scoped_temp_dir.h"
@@ -130,7 +129,7 @@
 }
 
 template <>
-ALLOW_UNUSED_TYPE std::string GetFirstColumn(sql::Statement& s) {
+[[maybe_unused]] std::string GetFirstColumn(sql::Statement& s) {
   return s.ColumnString(0);
 }
 
diff --git a/components/policy/resources/policy_templates.json b/components/policy/resources/policy_templates.json
index b40927f..39c9048 100644
--- a/components/policy/resources/policy_templates.json
+++ b/components/policy/resources/policy_templates.json
@@ -27776,9 +27776,10 @@
       'id': 824,
       'caption': 'Web App management settings',
       'tags': [],
-      'desc': '''Setting the policy controls web app management settings for <ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph>.
+      'desc': '''This policy allows an admin to specify settings for installed web apps.
 
-      This policy allows an admin to specify settings for web apps installed through <ph name="WEB_APP_INSTALL_FORCE_LIST_POLICY_NAME">WebAppInstallForceList</ph>. A default configuration can be set using the special ID <ph name="DEFAULT_SCOPE">"*"</ph>, which applies to all web apps without a custom configuration in this policy.
+      This policy maps a Web App ID to its specific setting. See <ph name="WEB_APP_ID_REFERENCE_URL">https://developer.chrome.com/blog/pwa-manifest-id/<ex>https://developer.chrome.com/blog/pwa-manifest-id/</ex></ph> for instructions on how to determine the id for an installed web app.
+      A default configuration can be set using the special ID <ph name="DEFAULT_SCOPE">"*"</ph>, which applies to all web apps without a custom configuration in this policy.
 
       The <ph name="RUN_ON_OS_LOGIN_FIELD">"run_on_os_login"</ph> field specifies if a web app can be run during OS login. If this field is set to <ph name="BLOCKED">"blocked"</ph>, the web app will not run during OS login and the user will not be able to enable this later. If this field is set to <ph name="RUN_WINDOWED">"run_windowed"</ph>, the web app will run during OS login and the user will not be able to disable this later. If this field is set to <ph name="ALLOWED">"allowed"</ph>, the user will be able to configure the web app to run at OS login. The default configuration only allows the <ph name="ALLOWED">"allowed"</ph> and <ph name="BLOCKED">"blocked"</ph> values.
       ''',
diff --git a/components/safe_browsing/content/browser/client_side_detection_service.cc b/components/safe_browsing/content/browser/client_side_detection_service.cc
index 8d80336..6bdc82d 100644
--- a/components/safe_browsing/content/browser/client_side_detection_service.cc
+++ b/components/safe_browsing/content/browser/client_side_detection_service.cc
@@ -106,6 +106,8 @@
 
 void ClientSideDetectionService::Shutdown() {
   url_loader_factory_.reset();
+  delegate_.reset();
+  enabled_ = false;
 }
 
 void ClientSideDetectionService::OnPrefsUpdated() {
diff --git a/components/signin/internal/identity_manager/primary_account_mutator_impl.cc b/components/signin/internal/identity_manager/primary_account_mutator_impl.cc
index f578b01c..083f841 100644
--- a/components/signin/internal/identity_manager/primary_account_mutator_impl.cc
+++ b/components/signin/internal/identity_manager/primary_account_mutator_impl.cc
@@ -37,9 +37,9 @@
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
   // |account_consistency_| is not used on CHROMEOS_ASH, however it is preferred
-  // to have it defined to avoid a lof of ifdefs in the header file.
-  signin::AccountConsistencyMethod unused = account_consistency_;
-  ALLOW_UNUSED_LOCAL(unused);
+  // to have it defined to avoid a lot of ifdefs in the header file.
+  [[maybe_unused]] signin::AccountConsistencyMethod unused =
+      account_consistency_;
 #endif
 }
 
diff --git a/components/signin/public/identity_manager/access_token_fetcher.h b/components/signin/public/identity_manager/access_token_fetcher.h
index 3770ba05..0595ef34 100644
--- a/components/signin/public/identity_manager/access_token_fetcher.h
+++ b/components/signin/public/identity_manager/access_token_fetcher.h
@@ -251,7 +251,7 @@
   raw_ptr<ProfileOAuth2TokenService> token_service_;
   // Suppress unused typedef warnings in some compiler builds when DCHECK is
   // disabled.
-  raw_ptr<PrimaryAccountManager> primary_account_manager_ ALLOW_UNUSED_TYPE;
+  [[maybe_unused]] raw_ptr<PrimaryAccountManager> primary_account_manager_;
   scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
   const ScopeSet scopes_;
   const Mode mode_;
diff --git a/components/site_engagement/content/site_engagement_score.h b/components/site_engagement/content/site_engagement_score.h
index a999e31..24307cb9 100644
--- a/components/site_engagement/content/site_engagement_score.h
+++ b/components/site_engagement/content/site_engagement_score.h
@@ -11,7 +11,6 @@
 #include <utility>
 
 #include "base/gtest_prod_util.h"
-#include "base/memory/raw_ptr.h"
 #include "base/time/time.h"
 #include "base/values.h"
 #include "components/site_engagement/core/mojom/site_engagement_details.mojom-forward.h"
@@ -215,7 +214,9 @@
 
   // The clock used to vend times. Enables time travelling in tests. Owned by
   // the SiteEngagementService.
-  raw_ptr<base::Clock> clock_;
+  // `clock_` is not a raw_ptr<...> for performance reasons (based on analysis
+  // of sampling profiler data).
+  base::Clock* clock_;
 
   // |raw_score_| is the score before any decay is applied.
   double raw_score_;
@@ -240,7 +241,9 @@
   GURL origin_;
 
   // The settings to write this score to when Commit() is called.
-  raw_ptr<HostContentSettingsMap> settings_map_;
+  // `settings_map_` is not a raw_ptr<...> for performance reasons (based on
+  // analysis of sampling profiler data).
+  HostContentSettingsMap* settings_map_;
 };
 
 }  // namespace site_engagement
diff --git a/components/sync_device_info/device_info_prefs.cc b/components/sync_device_info/device_info_prefs.cc
index 81c4940..b1b8f5a 100644
--- a/components/sync_device_info/device_info_prefs.cc
+++ b/components/sync_device_info/device_info_prefs.cc
@@ -74,8 +74,8 @@
 }
 
 void DeviceInfoPrefs::AddLocalCacheGuid(const std::string& cache_guid) {
-  ListPrefUpdateDeprecated update_cache_guids(
-      pref_service_, kDeviceInfoRecentGUIDsWithTimestamps);
+  ListPrefUpdate update_cache_guids(pref_service_,
+                                    kDeviceInfoRecentGUIDsWithTimestamps);
 
   for (auto it = update_cache_guids->GetList().begin();
        it != update_cache_guids->GetList().end(); it++) {
@@ -102,8 +102,8 @@
 }
 
 void DeviceInfoPrefs::GarbageCollectExpiredCacheGuids() {
-  ListPrefUpdateDeprecated update_cache_guids(
-      pref_service_, kDeviceInfoRecentGUIDsWithTimestamps);
+  ListPrefUpdate update_cache_guids(pref_service_,
+                                    kDeviceInfoRecentGUIDsWithTimestamps);
   update_cache_guids->EraseListValueIf([this](const auto& dict) {
     // Avoid crashes if the preference contains corrupt entries that are not
     // dictionaries, and meanwhile clean up these corrupt entries.
diff --git a/components/sync_device_info/device_info_prefs_unittest.cc b/components/sync_device_info/device_info_prefs_unittest.cc
index 3a38775f..7221ed1 100644
--- a/components/sync_device_info/device_info_prefs_unittest.cc
+++ b/components/sync_device_info/device_info_prefs_unittest.cc
@@ -56,8 +56,8 @@
 
   // Manipulate the preference directly to add a corrupt entry to the list,
   // which is a string instead of a dictionary.
-  ListPrefUpdateDeprecated cache_guids_update(
-      &pref_service_, kDeviceInfoRecentGUIDsWithTimestamps);
+  ListPrefUpdate cache_guids_update(&pref_service_,
+                                    kDeviceInfoRecentGUIDsWithTimestamps);
   cache_guids_update->Insert(cache_guids_update->GetList().begin(),
                              base::Value("corrupt_string_entry"));
 
diff --git a/components/test/data/payments/retry_with_no_payment_options.js b/components/test/data/payments/retry_with_no_payment_options.js
index 02f6fa7..38cff5a 100644
--- a/components/test/data/payments/retry_with_no_payment_options.js
+++ b/components/test/data/payments/retry_with_no_payment_options.js
@@ -5,16 +5,29 @@
  */
 
 var gPaymentResponse = null;
+var gValidationErrors = null;
 
 /**
  * Launches the PaymentRequest UI
  */
 function buy() { // eslint-disable-line no-unused-vars
+  buyWithMethods([{supportedMethods: 'basic-card'}]);
+}
+
+/**
+ * Launches the PaymentRequest UI
+ * @param {sequence<PaymentMethodData>} methodData An array of payment method
+ *        objects.
+ */
+function buyWithMethods(methodData) {
   var options = {};
-  getPaymentResponse(options)
-      .then(function(response) {
-        gPaymentResponse = response;
-      });
+  getPaymentResponseWithMethod(options, methodData).then(function(response) {
+    if (gValidationErrors != null) {
+      // retry() has been called before PaymentResponse promise resolved.
+      response.retry(gValidationErrors);
+    }
+    gPaymentResponse = response;
+  });
 }
 
 /**
@@ -24,6 +37,8 @@
  */
 function retry(validationErrors) { // eslint-disable-line no-unused-vars
   if (gPaymentResponse == null) {
+    // retry() has been called before PaymentResponse promise resolved.
+    gValidationErrors = validationErrors;
     return;
   }
 
diff --git a/components/test/data/payments/retry_with_payer_errors.js b/components/test/data/payments/retry_with_payer_errors.js
index a6a343a7..9471b3a 100644
--- a/components/test/data/payments/retry_with_payer_errors.js
+++ b/components/test/data/payments/retry_with_payer_errors.js
@@ -5,21 +5,34 @@
  */
 
 var gPaymentResponse = null;
+var gValidationErrors = null;
 
 /**
  * Launches the PaymentRequest UI
  */
 function buy() { // eslint-disable-line no-unused-vars
+  buyWithMethods([{supportedMethods: 'basic-card'}]);
+}
+
+/**
+ * Launches the PaymentRequest UI
+ * @param {sequence<PaymentMethodData>} methodData An array of payment method
+ *        objects.
+ */
+function buyWithMethods(methodData) {
   var options = {
     requestPayerEmail: true,
     requestPayerName: true,
     requestPayerPhone: true,
   };
-  getPaymentResponse(options)
-      .then(function(response) {
-        gPaymentResponse = response;
-        print(JSON.stringify(gPaymentResponse, undefined, 2));
-      });
+  getPaymentResponseWithMethod(options, methodData).then(function(response) {
+    if (gValidationErrors != null) {
+      // retry() has been called before PaymentResponse promise resolved.
+      response.retry(gValidationErrors);
+    }
+    gPaymentResponse = response;
+    print(JSON.stringify(gPaymentResponse, undefined, 2));
+  });
 }
 
 /**
@@ -29,6 +42,8 @@
  */
 function retry(validationErrors) { // eslint-disable-line no-unused-vars
   if (gPaymentResponse == null) {
+    // retry() has been called before PaymentResponse promise resolved.
+    gValidationErrors = validationErrors;
     return;
   }
 
diff --git a/components/update_client/ping_manager.cc b/components/update_client/ping_manager.cc
index ed215fc..170ac8e 100644
--- a/components/update_client/ping_manager.cc
+++ b/components/update_client/ping_manager.cc
@@ -118,8 +118,8 @@
               config_->GetProdId(), config_->GetBrowserVersion().GetString(),
               config_->GetLang(), config_->GetChannel(),
               config_->GetOSLongName(), config_->GetDownloadPreference(),
-              absl::nullopt, config_->ExtraRequestParams(), nullptr,
-              std::move(apps))),
+              config_->IsMachineExternallyManaged(),
+              config_->ExtraRequestParams(), nullptr, std::move(apps))),
       false, base::BindOnce(&PingSender::SendPingComplete, this));
 }
 
diff --git a/components/update_client/ping_manager_unittest.cc b/components/update_client/ping_manager_unittest.cc
index 8adb124f..07d321c 100644
--- a/components/update_client/ping_manager_unittest.cc
+++ b/components/update_client/ping_manager_unittest.cc
@@ -6,6 +6,7 @@
 
 #include <stdint.h>
 
+#include <initializer_list>
 #include <limits>
 #include <memory>
 #include <string>
@@ -465,6 +466,30 @@
       }
     interceptor->Reset();
   }
+
+  // Tests the presence of the `domain joined` in the ping request.
+  {
+    for (const auto is_managed : std::initializer_list<absl::optional<bool>>{
+             absl::nullopt, false, true}) {
+      config_->SetIsMachineExternallyManaged(is_managed);
+      EXPECT_TRUE(interceptor->ExpectRequest(std::make_unique<AnyMatch>()));
+      Component component(*update_context, "abc");
+      component.crx_component_ = CrxComponent();
+      component.previous_version_ = base::Version("1.0");
+      component.AppendEvent(component.MakeEventUpdateComplete());
+      ping_manager_->SendPing(component, *metadata_, MakePingCallback());
+
+      RunThreads();
+
+      ASSERT_EQ(interceptor->GetCount(), 1);
+      const auto root = base::JSONReader::Read(interceptor->GetRequestBody(0));
+      interceptor->Reset();
+
+      ASSERT_TRUE(root);
+      EXPECT_EQ(is_managed, root->FindBoolPath("request.domainjoined"));
+    }
+  }
+  config_->SetIsMachineExternallyManaged(absl::nullopt);
 }
 
 // Tests that sending the ping fails when the component requires encryption but
diff --git a/components/update_client/update_checker_unittest.cc b/components/update_client/update_checker_unittest.cc
index 3965c119..3638990 100644
--- a/components/update_client/update_checker_unittest.cc
+++ b/components/update_client/update_checker_unittest.cc
@@ -4,8 +4,10 @@
 
 #include "components/update_client/update_checker.h"
 
+#include <initializer_list>
 #include <map>
 #include <memory>
+#include <string>
 #include <tuple>
 #include <utility>
 #include <vector>
@@ -1190,13 +1192,14 @@
                        base::Unretained(this)));
     RunThreads();
 
-    ASSERT_GE(post_interceptor_->GetCount(), 1);
-    const auto root = base::JSONReader::Read(
-        post_interceptor_->GetRequestBody(post_interceptor_->GetCount() - 1));
-    ASSERT_TRUE(root);
+    ASSERT_EQ(post_interceptor_->GetCount(), 1);
+    const auto root =
+        base::JSONReader::Read(post_interceptor_->GetRequestBody(0));
+    post_interceptor_->Reset();
 
     // What is injected in the update checker by the configurator must
     // match what is sent in the update check.
+    ASSERT_TRUE(root);
     EXPECT_EQ(is_managed, root->FindBoolPath("request.domainjoined"));
   }
 }
diff --git a/components/user_manager/fake_user_manager.cc b/components/user_manager/fake_user_manager.cc
index eb7de9e6..f3ea6a38 100644
--- a/components/user_manager/fake_user_manager.cc
+++ b/components/user_manager/fake_user_manager.cc
@@ -381,7 +381,7 @@
 
 bool FakeUserManager::IsFirstExecAfterBoot() const {
   return base::CommandLine::ForCurrentProcess()->HasSwitch(
-      chromeos::switches::kFirstExecAfterBoot);
+      ash::switches::kFirstExecAfterBoot);
 }
 
 void FakeUserManager::AsyncRemoveCryptohome(const AccountId& account_id) const {
@@ -404,7 +404,7 @@
 bool FakeUserManager::HasBrowserRestarted() const {
   base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
   return base::SysInfo::IsRunningOnChromeOS() &&
-         command_line->HasSwitch(chromeos::switches::kLoginUser);
+         command_line->HasSwitch(ash::switches::kLoginUser);
 }
 
 const gfx::ImageSkia& FakeUserManager::GetResourceImagekiaNamed(int id) const {
diff --git a/components/viz/service/display/surface_aggregator_unittest.cc b/components/viz/service/display/surface_aggregator_unittest.cc
index cc336bb..ca7431f 100644
--- a/components/viz/service/display/surface_aggregator_unittest.cc
+++ b/components/viz/service/display/surface_aggregator_unittest.cc
@@ -9111,9 +9111,9 @@
         CompositorFrameTransitionDirective::Effect::kCoverLeft);
 
     root_sink_->SubmitCompositorFrame(local_surface_id, std::move(frame));
-    auto* surface = root_sink_->GetLastCreatedSurfaceForTesting();
-    ASSERT_TRUE(surface);
-    surface->GetSurfaceSavedFrameStorage()->CompleteForTesting();
+    root_sink_->GetSurfaceAnimationManagerForTesting()
+        ->GetSurfaceSavedFrameStorageForTesting()
+        ->CompleteForTesting();
   }
   AggregateFrame(surface_id);
 
diff --git a/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.cc b/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.cc
index 8fccf86..16dd9b7 100644
--- a/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.cc
+++ b/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.cc
@@ -1512,16 +1512,17 @@
     if (MakeCurrent(/*need_framebuffer=*/true)) {
       if (gl_surface_->IsSurfaceless()) {
 #if defined(USE_OZONE)
-        bool needs_background_image = ui::OzonePlatform::GetInstance()
-                                          ->GetPlatformRuntimeProperties()
-                                          .needs_background_image;
-        bool supports_non_backed_solid_color_images =
+        [[maybe_unused]] bool needs_background_image =
+            ui::OzonePlatform::GetInstance()
+                ->GetPlatformRuntimeProperties()
+                .needs_background_image;
+        [[maybe_unused]] bool supports_non_backed_solid_color_images =
             ui::OzonePlatform::GetInstance()
                 ->GetPlatformRuntimeProperties()
                 .supports_non_backed_solid_color_buffers;
 #else   // defined(USE_OZONE)
-        bool needs_background_image = false;
-        bool supports_non_backed_solid_color_images = false;
+        [[maybe_unused]] bool needs_background_image = false;
+        [[maybe_unused]] bool supports_non_backed_solid_color_images = false;
 #endif  // !defined(USE_OZONE)
 
 #if !BUILDFLAG(IS_WIN)
@@ -1535,8 +1536,6 @@
             supports_non_backed_solid_color_images);
 #else   // !BUILDFLAG(IS_WIN)
         NOTIMPLEMENTED();
-        ALLOW_UNUSED_LOCAL(needs_background_image);
-        ALLOW_UNUSED_LOCAL(supports_non_backed_solid_color_images);
 #endif  // BUILDFLAG(IS_WIN)
       } else {
         if (dependency_->NeedsSupportForExternalStencil()) {
@@ -1592,16 +1591,17 @@
 #endif
 
 #if defined(USE_OZONE)
-  bool needs_background_image = ui::OzonePlatform::GetInstance()
-                                    ->GetPlatformRuntimeProperties()
-                                    .needs_background_image;
-  bool supports_non_backed_solid_color_images =
+  [[maybe_unused]] bool needs_background_image =
+      ui::OzonePlatform::GetInstance()
+          ->GetPlatformRuntimeProperties()
+          .needs_background_image;
+  [[maybe_unused]] bool supports_non_backed_solid_color_images =
       ui::OzonePlatform::GetInstance()
           ->GetPlatformRuntimeProperties()
           .supports_non_backed_solid_color_buffers;
 #else   // defined(USE_OZONE)
-  bool needs_background_image = false;
-  bool supports_non_backed_solid_color_images = false;
+  [[maybe_unused]] bool needs_background_image = false;
+  [[maybe_unused]] bool supports_non_backed_solid_color_images = false;
 #endif  // !defined(USE_OZONE)
 
 #if !BUILDFLAG(IS_WIN)
@@ -1627,8 +1627,6 @@
     return true;
   }
 #endif  // !BUILDFLAG(IS_WIN)
-  ALLOW_UNUSED_LOCAL(needs_background_image);
-  ALLOW_UNUSED_LOCAL(supports_non_backed_solid_color_images);
 
   std::unique_ptr<SkiaOutputDeviceVulkan> output_device;
   if (!gpu_preferences_.disable_vulkan_surface) {
diff --git a/components/viz/service/frame_sinks/compositor_frame_sink_support.cc b/components/viz/service/frame_sinks/compositor_frame_sink_support.cc
index e4f9db7..3a15976 100644
--- a/components/viz/service/frame_sinks/compositor_frame_sink_support.cc
+++ b/components/viz/service/frame_sinks/compositor_frame_sink_support.cc
@@ -203,7 +203,7 @@
   if (!transition_directives.empty()) {
     bool started_animation =
         surface_animation_manager_.ProcessTransitionDirectives(
-            transition_directives, surface->GetSurfaceSavedFrameStorage());
+            transition_directives, surface);
 
     // If we started an animation, then we must need a begin frame for the code
     // below to work properly.
@@ -1190,6 +1190,11 @@
              last_evicted_local_surface_id_.parent_sequence_number();
 }
 
+SurfaceAnimationManager*
+CompositorFrameSinkSupport::GetSurfaceAnimationManagerForTesting() {
+  return &surface_animation_manager_;
+}
+
 void CompositorFrameSinkSupport::DestroySelf() {
   // SUBTLE: We explicitly copy `frame_sink_id_` because
   // DestroyCompositorFrameSink takes the FrameSinkId by reference and may
diff --git a/components/viz/service/frame_sinks/compositor_frame_sink_support.h b/components/viz/service/frame_sinks/compositor_frame_sink_support.h
index 46b8d85..3239dd3 100644
--- a/components/viz/service/frame_sinks/compositor_frame_sink_support.h
+++ b/components/viz/service/frame_sinks/compositor_frame_sink_support.h
@@ -235,6 +235,8 @@
 
   bool IsEvicted(const LocalSurfaceId& local_surface_id) const;
 
+  SurfaceAnimationManager* GetSurfaceAnimationManagerForTesting();
+
  private:
   friend class CompositorFrameSinkSupportTest;
   friend class DisplayTest;
diff --git a/components/viz/service/frame_sinks/external_begin_frame_source_android_unittest.cc b/components/viz/service/frame_sinks/external_begin_frame_source_android_unittest.cc
index 9057c80..ba2c5f4d 100644
--- a/components/viz/service/frame_sinks/external_begin_frame_source_android_unittest.cc
+++ b/components/viz/service/frame_sinks/external_begin_frame_source_android_unittest.cc
@@ -14,7 +14,13 @@
 class ExternalBeginFrameSourceAndroidTest : public ::testing::Test,
                                             public BeginFrameObserverBase {
  public:
-  ~ExternalBeginFrameSourceAndroidTest() override { thread_->Stop(); }
+  ~ExternalBeginFrameSourceAndroidTest() override {
+    thread_->task_runner()->PostTask(
+        FROM_HERE,
+        base::BindOnce(&ExternalBeginFrameSourceAndroidTest::TeardownOnThread,
+                       base::Unretained(this)));
+    thread_->Stop();
+  }
 
   void CreateThread() {
     thread_ = std::make_unique<base::android::JavaHandlerThread>("TestThread");
@@ -47,6 +53,8 @@
         /*requires_align_with_java=*/false);
   }
 
+  void TeardownOnThread() { begin_frame_source_.reset(); }
+
   void AddObserverOnThread(uint32_t frame_count) {
     pending_frames_ = frame_count;
     begin_frame_source_->AddObserver(this);
diff --git a/components/viz/service/frame_sinks/root_compositor_frame_sink_impl.cc b/components/viz/service/frame_sinks/root_compositor_frame_sink_impl.cc
index f9e6f39..5362650 100644
--- a/components/viz/service/frame_sinks/root_compositor_frame_sink_impl.cc
+++ b/components/viz/service/frame_sinks/root_compositor_frame_sink_impl.cc
@@ -8,7 +8,6 @@
 #include <utility>
 #include <vector>
 
-#include "base/compiler_specific.h"
 #include "base/containers/flat_set.h"
 #include "base/memory/ptr_util.h"
 #include "base/threading/platform_thread.h"
@@ -520,7 +519,6 @@
     display_client_->OnDisplayReceivedCALayerParams(ca_layer_params);
 #else
   NOTREACHED();
-  ALLOW_UNUSED_LOCAL(display_client_);
 #endif
 }
 
@@ -538,7 +536,6 @@
   }
 #else
   NOTREACHED();
-  ALLOW_UNUSED_LOCAL(display_client_);
 #endif
 }
 
diff --git a/components/viz/service/frame_sinks/root_compositor_frame_sink_impl.h b/components/viz/service/frame_sinks/root_compositor_frame_sink_impl.h
index 83cbb83..2bb4df6 100644
--- a/components/viz/service/frame_sinks/root_compositor_frame_sink_impl.h
+++ b/components/viz/service/frame_sinks/root_compositor_frame_sink_impl.h
@@ -156,7 +156,7 @@
   mojo::AssociatedReceiver<mojom::CompositorFrameSink>
       compositor_frame_sink_receiver_;
   // |display_client_| may be NullRemote on platforms that do not use it.
-  mojo::Remote<mojom::DisplayClient> display_client_;
+  [[maybe_unused]] mojo::Remote<mojom::DisplayClient> display_client_;
   mojo::AssociatedReceiver<mojom::DisplayPrivate> display_private_receiver_;
 
   std::unique_ptr<VSyncParameterListener> vsync_listener_;
diff --git a/components/viz/service/surfaces/surface.cc b/components/viz/service/surfaces/surface.cc
index 547e500..cd50c006 100644
--- a/components/viz/service/surfaces/surface.cc
+++ b/components/viz/service/surfaces/surface.cc
@@ -801,8 +801,4 @@
   return active_frame_data_->TakeDelegatedInkMetadata();
 }
 
-SurfaceSavedFrameStorage* Surface::GetSurfaceSavedFrameStorage() {
-  return &surface_saved_frame_storage_;
-}
-
 }  // namespace viz
diff --git a/components/viz/service/surfaces/surface.h b/components/viz/service/surfaces/surface.h
index cb83692..30c4619 100644
--- a/components/viz/service/surfaces/surface.h
+++ b/components/viz/service/surfaces/surface.h
@@ -28,7 +28,6 @@
 #include "components/viz/service/surfaces/pending_copy_output_request.h"
 #include "components/viz/service/surfaces/surface_client.h"
 #include "components/viz/service/surfaces/surface_dependency_deadline.h"
-#include "components/viz/service/surfaces/surface_saved_frame_storage.h"
 #include "components/viz/service/viz_service_export.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "ui/gfx/geometry/size.h"
@@ -272,8 +271,6 @@
 
   std::unique_ptr<gfx::DelegatedInkMetadata> TakeDelegatedInkMetadata();
 
-  SurfaceSavedFrameStorage* GetSurfaceSavedFrameStorage();
-
   base::WeakPtr<Surface> GetWeakPtr() { return weak_factory_.GetWeakPtr(); }
 
   // Always placed the given |copy_request| on the root render pass.
@@ -398,8 +395,6 @@
 
   const raw_ptr<SurfaceAllocationGroup> allocation_group_;
 
-  SurfaceSavedFrameStorage surface_saved_frame_storage_{this};
-
   bool has_damage_from_interpolated_frame_ = false;
 
   base::WeakPtrFactory<Surface> weak_factory_{this};
diff --git a/components/viz/service/surfaces/surface_saved_frame_storage.cc b/components/viz/service/surfaces/surface_saved_frame_storage.cc
index 612c9f0..ef603a3 100644
--- a/components/viz/service/surfaces/surface_saved_frame_storage.cc
+++ b/components/viz/service/surfaces/surface_saved_frame_storage.cc
@@ -23,15 +23,15 @@
 
 }  // namespace
 
-SurfaceSavedFrameStorage::SurfaceSavedFrameStorage(Surface* surface)
-    : surface_(surface) {}
-
+SurfaceSavedFrameStorage::SurfaceSavedFrameStorage() = default;
 SurfaceSavedFrameStorage::~SurfaceSavedFrameStorage() = default;
 
 void SurfaceSavedFrameStorage::ProcessSaveDirective(
     const CompositorFrameTransitionDirective& directive,
     SurfaceSavedFrame::TransitionDirectiveCompleteCallback
         directive_finished_callback) {
+  DCHECK(has_active_surface());
+
   // Create a new saved frame, destroying the old one if it existed.
   // TODO(vmpstr): This may need to change if the directive refers to a local
   // subframe (RP) of the compositor frame. However, as of now, the save
diff --git a/components/viz/service/surfaces/surface_saved_frame_storage.h b/components/viz/service/surfaces/surface_saved_frame_storage.h
index e635f09..75e2fc3e4 100644
--- a/components/viz/service/surfaces/surface_saved_frame_storage.h
+++ b/components/viz/service/surfaces/surface_saved_frame_storage.h
@@ -23,10 +23,7 @@
 // also responsible for expiring saved frames after a set timeout.
 class VIZ_SERVICE_EXPORT SurfaceSavedFrameStorage {
  public:
-  // Each Surface has its own storage, and the storage has a backpointer to the
-  // surface in order to append copy output requests to the active frame.
-  explicit SurfaceSavedFrameStorage(Surface* surface);
-
+  SurfaceSavedFrameStorage();
   ~SurfaceSavedFrameStorage();
 
   // Processes the save directive from a compositor frame. This interfaces with
@@ -46,11 +43,14 @@
   void ExpireForTesting();
   void CompleteForTesting();
 
+  bool has_active_surface() const { return !!surface_; }
+  void set_active_surface(Surface* surface) { surface_ = surface; }
+
  private:
   // This expires the saved frame, if any.
   void ExpireSavedFrame();
 
-  const raw_ptr<Surface> surface_;
+  raw_ptr<Surface> surface_ = nullptr;
 
   base::CancelableOnceClosure expiry_closure_;
 
diff --git a/components/viz/service/transitions/surface_animation_manager.cc b/components/viz/service/transitions/surface_animation_manager.cc
index 3f4727c..4c0b59d4 100644
--- a/components/viz/service/transitions/surface_animation_manager.cc
+++ b/components/viz/service/transitions/surface_animation_manager.cc
@@ -292,6 +292,22 @@
 
 }  // namespace
 
+class SurfaceAnimationManager::StorageWithSurface {
+ public:
+  StorageWithSurface(SurfaceSavedFrameStorage* storage, Surface* surface)
+      : storage_(storage) {
+    DCHECK(!storage_->has_active_surface());
+    storage_->set_active_surface(surface);
+  }
+
+  ~StorageWithSurface() { storage_->set_active_surface(nullptr); }
+
+  SurfaceSavedFrameStorage* operator->() { return storage_; }
+
+ private:
+  raw_ptr<SurfaceSavedFrameStorage> storage_;
+};
+
 SurfaceAnimationManager::SurfaceAnimationManager(
     SharedBitmapManager* shared_bitmap_manager)
     : animation_slowdown_factor_(
@@ -307,8 +323,9 @@
 
 bool SurfaceAnimationManager::ProcessTransitionDirectives(
     const std::vector<CompositorFrameTransitionDirective>& directives,
-    SurfaceSavedFrameStorage* storage) {
+    Surface* active_surface) {
   bool started_animation = false;
+  StorageWithSurface storage(&surface_saved_frame_storage_, active_surface);
   for (auto& directive : directives) {
     // Don't process directives with sequence ids smaller than or equal to the
     // last seen one. It is possible that we call this with the same frame
@@ -347,7 +364,7 @@
 
 bool SurfaceAnimationManager::ProcessSaveDirective(
     const CompositorFrameTransitionDirective& directive,
-    SurfaceSavedFrameStorage* storage) {
+    StorageWithSurface& storage) {
   // We need to be in the idle state in order to save.
   if (state_ != State::kIdle)
     return false;
@@ -357,7 +374,7 @@
 
 bool SurfaceAnimationManager::ProcessAnimateDirective(
     const CompositorFrameTransitionDirective& directive,
-    SurfaceSavedFrameStorage* storage) {
+    StorageWithSurface& storage) {
   // We can only begin an animate if we are currently idle.
   if (state_ != State::kIdle)
     return false;
@@ -395,7 +412,7 @@
 
 bool SurfaceAnimationManager::ProcessAnimateRendererDirective(
     const CompositorFrameTransitionDirective& directive,
-    SurfaceSavedFrameStorage* storage) {
+    StorageWithSurface& storage) {
   // We can only begin an animate if we are currently idle. The renderer sends
   // this in response to a notification of the capture completing successfully.
   if (state_ != State::kIdle)
@@ -418,7 +435,7 @@
 
 bool SurfaceAnimationManager::ProcessReleaseDirective(
     const CompositorFrameTransitionDirective& directive,
-    SurfaceSavedFrameStorage* storage) {
+    StorageWithSurface& storage) {
   if (state_ != State::kAnimatingRenderer)
     return false;
 
@@ -1260,6 +1277,11 @@
   surface->SetInterpolatedFrame(std::move(resolved_frame));
 }
 
+SurfaceSavedFrameStorage*
+SurfaceAnimationManager::GetSurfaceSavedFrameStorageForTesting() {
+  return &surface_saved_frame_storage_;
+}
+
 base::TimeDelta SurfaceAnimationManager::ApplySlowdownFactor(
     base::TimeDelta original) const {
   return original * animation_slowdown_factor_;
diff --git a/components/viz/service/transitions/surface_animation_manager.h b/components/viz/service/transitions/surface_animation_manager.h
index a67836a..bb7a1d6 100644
--- a/components/viz/service/transitions/surface_animation_manager.h
+++ b/components/viz/service/transitions/surface_animation_manager.h
@@ -18,6 +18,7 @@
 #include "components/viz/common/resources/resource_id.h"
 #include "components/viz/service/display/shared_bitmap_manager.h"
 #include "components/viz/service/surfaces/surface_saved_frame.h"
+#include "components/viz/service/surfaces/surface_saved_frame_storage.h"
 #include "components/viz/service/transitions/transferable_resource_tracker.h"
 #include "components/viz/service/viz_service_export.h"
 #include "ui/gfx/animation/keyframe/animation_curve.h"
@@ -28,7 +29,6 @@
 
 class Surface;
 class CompositorFrame;
-class SurfaceSavedFrameStorage;
 struct ReturnedResource;
 struct TransferableResource;
 
@@ -58,7 +58,7 @@
   // normally not do so in the middle of the animation.
   bool ProcessTransitionDirectives(
       const std::vector<CompositorFrameTransitionDirective>& directives,
-      SurfaceSavedFrameStorage* storage);
+      Surface* active_surface);
 
   // Returns true if this manager needs to observe begin frames to advance
   // animations.
@@ -82,11 +82,15 @@
   // necessary.
   void ReplaceSharedElementResources(Surface* surface);
 
+  SurfaceSavedFrameStorage* GetSurfaceSavedFrameStorageForTesting();
+
  private:
   friend class SurfaceAnimationManagerTest;
   FRIEND_TEST_ALL_PREFIXES(SurfaceAnimationManagerTest, CustomRootConfig);
   FRIEND_TEST_ALL_PREFIXES(SurfaceAnimationManagerTest, CustomSharedConfig);
 
+  class StorageWithSurface;
+
   struct RenderPassDrawData {
     RenderPassDrawData();
     RenderPassDrawData(RenderPassDrawData&&);
@@ -104,17 +108,17 @@
 
   // Helpers to process specific directives.
   bool ProcessSaveDirective(const CompositorFrameTransitionDirective& directive,
-                            SurfaceSavedFrameStorage* storage);
+                            StorageWithSurface& storage);
   // Returns true if the animation has started.
   bool ProcessAnimateDirective(
       const CompositorFrameTransitionDirective& directive,
-      SurfaceSavedFrameStorage* storage);
+      StorageWithSurface& storage);
   bool ProcessAnimateRendererDirective(
       const CompositorFrameTransitionDirective& directive,
-      SurfaceSavedFrameStorage* storage);
+      StorageWithSurface& storage);
   bool ProcessReleaseDirective(
       const CompositorFrameTransitionDirective& directive,
-      SurfaceSavedFrameStorage* storage);
+      StorageWithSurface& storage);
 
   // Finishes the animation and advance state to kLastFrame if it's time to do
   // so. This call is only valid if state is kAnimating.
@@ -281,6 +285,10 @@
     gfx::TransformOperations combined_transform_;
   };
 
+  // This is responsible for keeping track of the saved frame, accumulating copy
+  // output results.
+  SurfaceSavedFrameStorage surface_saved_frame_storage_;
+
   // This is the root animation state.
   RootAnimationState root_animation_;
 
diff --git a/components/viz/service/transitions/surface_animation_manager_unittest.cc b/components/viz/service/transitions/surface_animation_manager_unittest.cc
index 386e1b95..3d9a7c7 100644
--- a/components/viz/service/transitions/surface_animation_manager_unittest.cc
+++ b/components/viz/service/transitions/surface_animation_manager_unittest.cc
@@ -125,8 +125,7 @@
   }
 
   void TearDown() override {
-    if (storage())
-      storage()->ExpireForTesting();
+    storage()->ExpireForTesting();
     manager_.reset();
   }
 
@@ -135,15 +134,15 @@
     return current_time_;
   }
 
-  Surface* surface() const {
+  Surface* surface() {
     Surface* surface = surface_manager_->GetSurfaceForId(surface_id_);
     // Can't ASSERT in a non-void function, so just CHECK instead.
     CHECK(surface);
     return surface;
   }
 
-  SurfaceSavedFrameStorage* storage() const {
-    return surface()->GetSurfaceSavedFrameStorage();
+  SurfaceSavedFrameStorage* storage() {
+    return manager().GetSurfaceSavedFrameStorageForTesting();
   }
 
   void ValidateStartState(CompositorFrameTransitionDirective::Effect effect) {
@@ -280,7 +279,7 @@
 TEST_F(SurfaceAnimationManagerTest, DefaultState) {
   EXPECT_FALSE(manager().NeedsBeginFrame());
 
-  manager().ProcessTransitionDirectives({}, storage());
+  manager().ProcessTransitionDirectives({}, surface());
 
   EXPECT_FALSE(manager().NeedsBeginFrame());
 }
@@ -289,12 +288,12 @@
   EXPECT_FALSE(manager().NeedsBeginFrame());
 
   manager().ProcessTransitionDirectives(CreateSaveDirectiveAsVector(1),
-                                        storage());
+                                        surface());
 
   storage()->CompleteForTesting();
 
   manager().ProcessTransitionDirectives(CreateAnimateDirectiveAsVector(2),
-                                        storage());
+                                        surface());
 
   // Tick curves to set start time.
   manager().UpdateFrameTime(AdvanceTime(base::TimeDelta()));
@@ -320,7 +319,7 @@
   EXPECT_FALSE(manager().NeedsBeginFrame());
 
   manager().ProcessTransitionDirectives(CreateAnimateDirectiveAsVector(1),
-                                        storage());
+                                        surface());
   EXPECT_FALSE(manager().NeedsBeginFrame());
 }
 
@@ -328,14 +327,14 @@
   EXPECT_FALSE(manager().NeedsBeginFrame());
 
   manager().ProcessTransitionDirectives(CreateSaveDirectiveAsVector(1),
-                                        storage());
+                                        surface());
   EXPECT_FALSE(manager().NeedsBeginFrame());
 
   storage()->ExpireForTesting();
 
   AdvanceTime(base::Seconds(6));
   manager().ProcessTransitionDirectives(CreateAnimateDirectiveAsVector(2),
-                                        storage());
+                                        surface());
   EXPECT_FALSE(manager().NeedsBeginFrame());
 }
 
@@ -345,7 +344,7 @@
   uint32_t sequence_id = 1;
   for (int i = 0; i < 200; ++i) {
     manager().ProcessTransitionDirectives(
-        CreateSaveDirectiveAsVector(sequence_id), storage());
+        CreateSaveDirectiveAsVector(sequence_id), surface());
 
     EXPECT_FALSE(manager().NeedsBeginFrame());
 
@@ -356,7 +355,7 @@
   storage()->CompleteForTesting();
 
   manager().ProcessTransitionDirectives(
-      CreateAnimateDirectiveAsVector(sequence_id), storage());
+      CreateAnimateDirectiveAsVector(sequence_id), surface());
 
   // Tick curves to set start time.
   manager().UpdateFrameTime(AdvanceTime(base::TimeDelta()));
@@ -395,12 +394,12 @@
   uint32_t sequence_id = 1;
   for (auto effect : effects) {
     manager().ProcessTransitionDirectives(
-        CreateSaveDirectiveAsVector(sequence_id++, effect), storage());
+        CreateSaveDirectiveAsVector(sequence_id++, effect), surface());
 
     storage()->CompleteForTesting();
 
     manager().ProcessTransitionDirectives(
-        CreateAnimateDirectiveAsVector(sequence_id++), storage());
+        CreateAnimateDirectiveAsVector(sequence_id++), surface());
 
     // Tick curves to set start time.
     manager().UpdateFrameTime(AdvanceTime(base::TimeDelta()));
@@ -451,11 +450,11 @@
   support_->SubmitCompositorFrame(surface_id_.local_surface_id(),
                                   std::move(frame));
 
-  ASSERT_FALSE(manager().ProcessTransitionDirectives({save}, storage()));
+  ASSERT_FALSE(manager().ProcessTransitionDirectives({save}, surface()));
 
   storage()->CompleteForTesting();
 
-  ASSERT_TRUE(manager().ProcessTransitionDirectives({animate}, storage()));
+  ASSERT_TRUE(manager().ProcessTransitionDirectives({animate}, surface()));
 
   // We jump directly to the last frame but we should need a BeginFrame to tick
   // the last frame.
@@ -487,9 +486,9 @@
       /*root_config=*/root_config,
       /*shared_elements=*/{});
 
-  ASSERT_FALSE(manager().ProcessTransitionDirectives({save}, storage()));
+  ASSERT_FALSE(manager().ProcessTransitionDirectives({save}, surface()));
   storage()->CompleteForTesting();
-  ASSERT_TRUE(manager().ProcessTransitionDirectives({animate}, storage()));
+  ASSERT_TRUE(manager().ProcessTransitionDirectives({animate}, surface()));
 
   // Need the first frame which starts the animation.
   EXPECT_TRUE(manager().NeedsBeginFrame());
@@ -582,7 +581,7 @@
 
   support_->SubmitCompositorFrame(surface_id_.local_surface_id(),
                                   std::move(old_frame));
-  ASSERT_FALSE(manager().ProcessTransitionDirectives({save}, storage()));
+  ASSERT_FALSE(manager().ProcessTransitionDirectives({save}, surface()));
 
   // Dispatch copy request results. We're not using the test function here to
   // ensure valid result for shared elements.
@@ -612,7 +611,7 @@
       CreateSharedElements(new_frame.render_pass_list, {shared_config}));
   support_->SubmitCompositorFrame(surface_id_.local_surface_id(),
                                   std::move(new_frame));
-  ASSERT_TRUE(manager().ProcessTransitionDirectives({animate}, storage()));
+  ASSERT_TRUE(manager().ProcessTransitionDirectives({animate}, surface()));
 
   // Need the first frame which starts the animation.
   EXPECT_TRUE(manager().NeedsBeginFrame());
diff --git a/components/viz/test/data/oop_draw_image.png b/components/viz/test/data/oop_draw_image.png
new file mode 100644
index 0000000..df9bfaf
--- /dev/null
+++ b/components/viz/test/data/oop_draw_image.png
Binary files differ
diff --git a/components/viz/test/data/oop_draw_rect_playback_rect.png b/components/viz/test/data/oop_draw_rect_playback_rect.png
new file mode 100644
index 0000000..d00e0eb
--- /dev/null
+++ b/components/viz/test/data/oop_draw_rect_playback_rect.png
Binary files differ
diff --git a/components/viz/test/data/oop_draw_rect_query.png b/components/viz/test/data/oop_draw_rect_query.png
new file mode 100644
index 0000000..54557d8
--- /dev/null
+++ b/components/viz/test/data/oop_draw_rect_query.png
Binary files differ
diff --git a/components/viz/test/data/oop_draw_rect_scale_transform.png b/components/viz/test/data/oop_draw_rect_scale_transform.png
new file mode 100644
index 0000000..04f6d28a
--- /dev/null
+++ b/components/viz/test/data/oop_draw_rect_scale_transform.png
Binary files differ
diff --git a/components/viz/test/data/oop_path.png b/components/viz/test/data/oop_path.png
new file mode 100644
index 0000000..ad65eb8
--- /dev/null
+++ b/components/viz/test/data/oop_path.png
Binary files differ
diff --git a/components/viz/test/data/oop_record_shader_max_texture_size.png b/components/viz/test/data/oop_record_shader_max_texture_size.png
new file mode 100644
index 0000000..7676b239
--- /dev/null
+++ b/components/viz/test/data/oop_record_shader_max_texture_size.png
Binary files differ
diff --git a/components/viz/test/test_context_provider.h b/components/viz/test/test_context_provider.h
index 51846d23..21adbb4 100644
--- a/components/viz/test/test_context_provider.h
+++ b/components/viz/test/test_context_provider.h
@@ -213,7 +213,7 @@
   std::unique_ptr<skia_bindings::GrContextForGLES2Interface> gr_context_;
   std::unique_ptr<ContextCacheController> cache_controller_;
   std::unique_ptr<TestSharedImageInterface> shared_image_interface_;
-  const bool support_locking_ ALLOW_UNUSED_TYPE;
+  [[maybe_unused]] const bool support_locking_;
   bool bound_ = false;
 
   gpu::GpuFeatureInfo gpu_feature_info_;
diff --git a/content/app/content_main_runner_impl.cc b/content/app/content_main_runner_impl.cc
index 555562d..27f444b1 100644
--- a/content/app/content_main_runner_impl.cc
+++ b/content/app/content_main_runner_impl.cc
@@ -202,11 +202,7 @@
 #if defined(V8_USE_EXTERNAL_STARTUP_DATA)
 
 gin::V8SnapshotFileType GetSnapshotType(const base::CommandLine& command_line) {
-#if BUILDFLAG(IS_ANDROID) && defined(INCLUDE_BOTH_V8_SNAPSHOTS)
-  if (command_line.HasSwitch(switches::kUseContextSnapshotSwitch))
-    return gin::V8SnapshotFileType::kWithAdditionalContext;
-  return gin::V8SnapshotFileType::kDefault;
-#elif defined(USE_V8_CONTEXT_SNAPSHOT)
+#if defined(USE_V8_CONTEXT_SNAPSHOT)
   return gin::V8SnapshotFileType::kWithAdditionalContext;
 #else
   return gin::V8SnapshotFileType::kDefault;
@@ -215,11 +211,7 @@
 
 #if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_MAC)
 std::string GetSnapshotDataDescriptor(const base::CommandLine& command_line) {
-#if BUILDFLAG(IS_ANDROID) && defined(INCLUDE_BOTH_V8_SNAPSHOTS)
-  if (command_line.HasSwitch(switches::kUseContextSnapshotSwitch))
-    return std::string();
-  return kV8SnapshotDataDescriptor;
-#elif defined(USE_V8_CONTEXT_SNAPSHOT)
+#if defined(USE_V8_CONTEXT_SNAPSHOT)
 #if BUILDFLAG(IS_ANDROID)
   // On android, the renderer loads the context snapshot directly.
   return std::string();
diff --git a/content/browser/accessibility/dump_accessibility_tree_browsertest.cc b/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
index cf7c754..0b0acf8 100644
--- a/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
+++ b/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
@@ -742,6 +742,11 @@
   RunAriaTest(FILE_PATH_LITERAL("aria-directory.html"));
 }
 
+IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTest,
+                       AccessibilityAriaDirectoryChildren) {
+  RunAriaTest(FILE_PATH_LITERAL("aria-directory-children.html"));
+}
+
 IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTest, AccessibilityAriaDisabled) {
   RunAriaTest(FILE_PATH_LITERAL("aria-disabled.html"));
 }
@@ -994,9 +999,9 @@
                        AccessibilityAriaListBoxDisabled) {
   RunAriaTest(FILE_PATH_LITERAL("aria-listbox-disabled.html"));
 }
-// TODO(crbug.com/983802): Flaky.
+
 IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTest,
-                       DISABLED_AccessibilityAriaListBoxActiveDescendant) {
+                       AccessibilityAriaListBoxActiveDescendant) {
   RunAriaTest(FILE_PATH_LITERAL("aria-listbox-activedescendant.html"));
 }
 
diff --git a/content/browser/attribution_reporting/attribution_host_utils.cc b/content/browser/attribution_reporting/attribution_host_utils.cc
index 82efb85..4d53da3 100644
--- a/content/browser/attribution_reporting/attribution_host_utils.cc
+++ b/content/browser/attribution_reporting/attribution_host_utils.cc
@@ -62,9 +62,9 @@
   AttributionPolicy::AttributionMode mode =
       policy.GetAttributionMode(source_type);
   StorableSource storable_impression(
-      policy.SanitizeSourceEventId(impression.impression_data),
-      impression_origin, impression.conversion_destination, reporting_origin,
-      impression_time,
+      // Impression data doesn't need to be sanitized.
+      impression.impression_data, impression_origin,
+      impression.conversion_destination, reporting_origin, impression_time,
       policy.GetExpiryTimeForImpression(impression.expiry, impression_time,
                                         source_type),
       source_type, impression.priority, mode.logic(), mode.fake_trigger_data(),
diff --git a/content/browser/attribution_reporting/attribution_policy.cc b/content/browser/attribution_reporting/attribution_policy.cc
index 2ff7da6..af7b6c1 100644
--- a/content/browser/attribution_reporting/attribution_policy.cc
+++ b/content/browser/attribution_reporting/attribution_policy.cc
@@ -82,12 +82,6 @@
   return trigger_data < TriggerDataCardinality(source_type);
 }
 
-uint64_t AttributionPolicy::SanitizeSourceEventId(
-    uint64_t source_event_id) const {
-  // Impression data is allowed the full 64 bits.
-  return source_event_id;
-}
-
 base::Time AttributionPolicy::GetExpiryTimeForImpression(
     const absl::optional<base::TimeDelta>& declared_expiry,
     base::Time impression_time,
diff --git a/content/browser/attribution_reporting/attribution_policy.h b/content/browser/attribution_reporting/attribution_policy.h
index 76dcc28..96baa51 100644
--- a/content/browser/attribution_reporting/attribution_policy.h
+++ b/content/browser/attribution_reporting/attribution_policy.h
@@ -39,8 +39,6 @@
       uint64_t trigger_data,
       StorableSource::SourceType source_type) const;
 
-  [[nodiscard]] uint64_t SanitizeSourceEventId(uint64_t source_event_id) const;
-
   // Returns the expiry time for an impression that is clamped to a maximum
   // value of 30 days from |impression_time|.
   [[nodiscard]] base::Time GetExpiryTimeForImpression(
diff --git a/content/browser/attribution_reporting/attribution_policy_unittest.cc b/content/browser/attribution_reporting/attribution_policy_unittest.cc
index 646fca5..11f87a7 100644
--- a/content/browser/attribution_reporting/attribution_policy_unittest.cc
+++ b/content/browser/attribution_reporting/attribution_policy_unittest.cc
@@ -51,14 +51,6 @@
             policy->SanitizeTriggerData(3, StorableSource::SourceType::kEvent));
 }
 
-TEST(AttributionPolicyTest, SanitizeHighEntropySourceEventId_Unchanged) {
-  uint64_t source_event_id = 256LU;
-
-  // The policy should not alter the impression data, and return the base 10
-  // representation.
-  EXPECT_EQ(256LU, AttributionPolicy().SanitizeSourceEventId(source_event_id));
-}
-
 TEST(AttributionPolicyTest, LowEntropyTriggerData_Unchanged) {
   std::unique_ptr<AttributionPolicy> policy =
       std::make_unique<ConfigurableAttributionPolicy>(/*should_noise=*/false);
diff --git a/content/browser/attribution_reporting/rate_limit_table.cc b/content/browser/attribution_reporting/rate_limit_table.cc
index 2d6292b..6fc6dce 100644
--- a/content/browser/attribution_reporting/rate_limit_table.cc
+++ b/content/browser/attribution_reporting/rate_limit_table.cc
@@ -286,8 +286,8 @@
   std::vector<int64_t> rate_limit_ids_to_delete;
   {
     static constexpr char kScanCandidateData[] =
-        "SELECT rate_limit_id,impression_site,impression_origin,"
-        "conversion_destination,conversion_origin FROM rate_limits "
+        "SELECT rate_limit_id,impression_origin,conversion_origin "
+        "FROM rate_limits "
         DCHECK_SQL_INDEXED_BY("rate_limit_attribution_type_conversion_time_idx")
         "WHERE attribution_type = ? AND conversion_time BETWEEN ? AND ?";
     sql::Statement statement(
@@ -304,9 +304,7 @@
       while (statement.Step()) {
         int64_t rate_limit_id = statement.ColumnInt64(0);
         if (filter.Run(DeserializeOrigin(statement.ColumnString(1))) ||
-            filter.Run(DeserializeOrigin(statement.ColumnString(2))) ||
-            filter.Run(DeserializeOrigin(statement.ColumnString(3))) ||
-            filter.Run(DeserializeOrigin(statement.ColumnString(4)))) {
+            filter.Run(DeserializeOrigin(statement.ColumnString(2)))) {
           rate_limit_ids_to_delete.push_back(rate_limit_id);
         }
       }
diff --git a/content/browser/devtools/protocol/storage_handler.cc b/content/browser/devtools/protocol/storage_handler.cc
index d7aa668d..1df94fd 100644
--- a/content/browser/devtools/protocol/storage_handler.cc
+++ b/content/browser/devtools/protocol/storage_handler.cc
@@ -18,6 +18,7 @@
 #include "content/browser/devtools/protocol/network.h"
 #include "content/browser/devtools/protocol/network_handler.h"
 #include "content/browser/devtools/protocol/storage.h"
+#include "content/browser/interest_group/interest_group_manager.h"
 #include "content/browser/storage_partition_impl.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/browser_task_traits.h"
@@ -28,6 +29,7 @@
 #include "storage/browser/quota/quota_manager_proxy.h"
 #include "storage/browser/quota/quota_override_handle.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/blink/public/common/interest_group/interest_group.h"
 #include "third_party/blink/public/common/storage_key/storage_key.h"
 #include "third_party/blink/public/mojom/quota/quota_types.mojom.h"
 #include "url/gurl.h"
@@ -284,6 +286,7 @@
   cache_storage_observer_.reset();
   indexed_db_observer_.reset();
   quota_override_handle_.reset();
+  SetInterestGroupTracking(false);
   return Response::Success();
 }
 
@@ -361,12 +364,8 @@
       storage_types, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
   std::unordered_set<std::string> set(types.begin(), types.end());
   uint32_t remove_mask = 0;
-  if (set.count(Storage::StorageTypeEnum::Cookies)) {
+  if (set.count(Storage::StorageTypeEnum::Cookies))
     remove_mask |= StoragePartition::REMOVE_DATA_MASK_COOKIES;
-    // Interest groups should be cleared with cookies for its origin trial as
-    // they have the same privacy characteristics
-    remove_mask |= StoragePartition::REMOVE_DATA_MASK_INTEREST_GROUPS;
-  }
   if (set.count(Storage::StorageTypeEnum::File_systems))
     remove_mask |= StoragePartition::REMOVE_DATA_MASK_FILE_SYSTEMS;
   if (set.count(Storage::StorageTypeEnum::Indexeddb))
@@ -381,6 +380,8 @@
     remove_mask |= StoragePartition::REMOVE_DATA_MASK_SERVICE_WORKERS;
   if (set.count(Storage::StorageTypeEnum::Cache_storage))
     remove_mask |= StoragePartition::REMOVE_DATA_MASK_CACHE_STORAGE;
+  if (set.count(Storage::StorageTypeEnum::Interest_groups))
+    remove_mask |= StoragePartition::REMOVE_DATA_MASK_INTEREST_GROUPS;
   if (set.count(Storage::StorageTypeEnum::All))
     remove_mask |= StoragePartition::REMOVE_DATA_MASK_ALL;
 
@@ -640,5 +641,155 @@
       base::BindOnce(&SendClearTrustTokensStatus, std::move(callback)));
 }
 
+void StorageHandler::OnInterestGroupAccessed(
+    InterestGroupManager::InterestGroupObserverInterface::AccessType type,
+    const std::string& owner_origin,
+    const std::string& name) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  using AccessType =
+      InterestGroupManager::InterestGroupObserverInterface::AccessType;
+  std::string type_enum;
+  switch (type) {
+    case AccessType::kJoin:
+      type_enum = Storage::InterestGroupAccessTypeEnum::Join;
+      break;
+    case AccessType::kLeave:
+      type_enum = Storage::InterestGroupAccessTypeEnum::Leave;
+      break;
+    case AccessType::kUpdate:
+      type_enum = Storage::InterestGroupAccessTypeEnum::Update;
+      break;
+    case AccessType::kBid:
+      type_enum = Storage::InterestGroupAccessTypeEnum::Bid;
+      break;
+    case AccessType::kWin:
+      type_enum = Storage::InterestGroupAccessTypeEnum::Win;
+      break;
+  };
+  frontend_->InterestGroupAccessed(type_enum, owner_origin, name);
+}
+
+namespace {
+void SendGetInterestGroup(
+    std::unique_ptr<StorageHandler::GetInterestGroupDetailsCallback> callback,
+    absl::optional<StorageInterestGroup> storage_group) {
+  if (!storage_group) {
+    callback->sendFailure(Response::ServerError("Interest group not found"));
+    return;
+  }
+
+  const blink::InterestGroup& group = storage_group->interest_group;
+  auto trusted_bidding_signals_keys =
+      std::make_unique<protocol::Array<std::string>>();
+  if (group.trusted_bidding_signals_keys) {
+    for (const auto& key : group.trusted_bidding_signals_keys.value())
+      trusted_bidding_signals_keys->push_back(key);
+  }
+  auto ads =
+      std::make_unique<protocol::Array<protocol::Storage::InterestGroupAd>>();
+  if (group.ads) {
+    for (const auto& ad : *group.ads) {
+      auto protocol_ad = protocol::Storage::InterestGroupAd::Create()
+                             .SetRenderUrl(ad.render_url.spec())
+                             .Build();
+      if (ad.metadata)
+        protocol_ad->SetMetadata(*ad.metadata);
+      ads->push_back(std::move(protocol_ad));
+    }
+  }
+  auto ad_components =
+      std::make_unique<protocol::Array<protocol::Storage::InterestGroupAd>>();
+  if (group.ad_components) {
+    for (const auto& ad : *group.ad_components) {
+      auto protocol_ad = protocol::Storage::InterestGroupAd::Create()
+                             .SetRenderUrl(ad.render_url.spec())
+                             .Build();
+      if (ad.metadata)
+        protocol_ad->SetMetadata(*ad.metadata);
+      ad_components->push_back(std::move(protocol_ad));
+    }
+  }
+  auto protocol_group =
+      protocol::Storage::InterestGroupDetails::Create()
+          .SetOwnerOrigin(group.owner.Serialize())
+          .SetName(group.name)
+          .SetExpirationTime(group.expiry.ToJsTimeIgnoringNull())
+          .SetJoiningOrigin(storage_group->joining_origin.Serialize())
+          .SetTrustedBiddingSignalsKeys(std::move(trusted_bidding_signals_keys))
+          .SetAds(std::move(ads))
+          .SetAdComponents(std::move(ad_components))
+          .Build();
+  if (group.bidding_url)
+    protocol_group->SetBiddingUrl(group.bidding_url->spec());
+  if (group.bidding_wasm_helper_url)
+    protocol_group->SetBiddingWasmHelperUrl(
+        group.bidding_wasm_helper_url->spec());
+  if (group.update_url)
+    protocol_group->SetUpdateUrl(group.update_url->spec());
+  if (group.trusted_bidding_signals_url)
+    protocol_group->SetTrustedBiddingSignalsUrl(
+        group.trusted_bidding_signals_url->spec());
+  if (group.user_bidding_signals)
+    protocol_group->SetUserBiddingSignals(*group.user_bidding_signals);
+
+  callback->sendSuccess(std::move(protocol_group));
+}
+
+}  // namespace
+
+void StorageHandler::GetInterestGroupDetails(
+    const std::string& owner_origin_string,
+    const std::string& name,
+    std::unique_ptr<GetInterestGroupDetailsCallback> callback) {
+  if (!storage_partition_) {
+    callback->sendFailure(Response::InternalError());
+    return;
+  }
+
+  InterestGroupManager* manager =
+      static_cast<StoragePartitionImpl*>(storage_partition_)
+          ->GetInterestGroupManager();
+  if (!manager) {
+    callback->sendFailure(
+        Response::ServerError("Interest group storage is disabled"));
+    return;
+  }
+
+  GURL owner_origin_url(owner_origin_string);
+  if (!owner_origin_url.is_valid()) {
+    callback->sendFailure(Response::ServerError("Invalid Owner Origin"));
+    return;
+  }
+  url::Origin owner_origin = url::Origin::Create(GURL(owner_origin_string));
+  DCHECK(!owner_origin.opaque());
+
+  manager->GetInterestGroup(
+      owner_origin, name,
+      base::BindOnce(&SendGetInterestGroup, std::move(callback)));
+}
+
+Response StorageHandler::SetInterestGroupTracking(bool enable) {
+  if (!storage_partition_)
+    return Response::InternalError();
+
+  InterestGroupManager* manager =
+      static_cast<StoragePartitionImpl*>(storage_partition_)
+          ->GetInterestGroupManager();
+  if (!manager)
+    return Response::ServerError("Interest group storage is disabled.");
+
+  if (enable) {
+    // Only add if we are not already registered as an observer. We only
+    // observe the interest group manager, so if we're observing anything then
+    // we are already registered.
+    if (!IsInObserverList())
+      manager->AddInterestGroupObserver(this);
+  } else {
+    // Removal doesn't care if we are not registered.
+    manager->RemoveInterestGroupObserver(this);
+  }
+  return Response::Success();
+}
+
 }  // namespace protocol
 }  // namespace content
diff --git a/content/browser/devtools/protocol/storage_handler.h b/content/browser/devtools/protocol/storage_handler.h
index b078ce23..47d1dbb 100644
--- a/content/browser/devtools/protocol/storage_handler.h
+++ b/content/browser/devtools/protocol/storage_handler.h
@@ -11,6 +11,7 @@
 #include "base/memory/weak_ptr.h"
 #include "content/browser/devtools/protocol/devtools_domain_handler.h"
 #include "content/browser/devtools/protocol/storage.h"
+#include "content/browser/interest_group/interest_group_manager.h"
 
 namespace storage {
 class QuotaOverrideHandle;
@@ -21,8 +22,10 @@
 
 namespace protocol {
 
-class StorageHandler : public DevToolsDomainHandler,
-                       public Storage::Backend {
+class StorageHandler
+    : public DevToolsDomainHandler,
+      public Storage::Backend,
+      private content::InterestGroupManager::InterestGroupObserverInterface {
  public:
   StorageHandler();
 
@@ -79,15 +82,29 @@
       const std::string& issuerOrigin,
       std::unique_ptr<ClearTrustTokensCallback> callback) override;
 
+  void GetInterestGroupDetails(
+      const std::string& owner_origin_string,
+      const std::string& name,
+      std::unique_ptr<GetInterestGroupDetailsCallback> callback) override;
+
+  Response SetInterestGroupTracking(bool enable) override;
+
  private:
   // See definition for lifetime information.
   class CacheStorageObserver;
   class IndexedDBObserver;
+  class InterestGroupObserver;
 
   // Not thread safe.
   CacheStorageObserver* GetCacheStorageObserver();
   IndexedDBObserver* GetIndexedDBObserver();
 
+  // content::InterestGroupManager::InterestGroupObserverInterface
+  void OnInterestGroupAccessed(
+      InterestGroupManager::InterestGroupObserverInterface::AccessType type,
+      const std::string& owner_origin,
+      const std::string& name) override;
+
   void NotifyCacheStorageListChanged(const std::string& origin);
   void NotifyCacheStorageContentChanged(const std::string& origin,
                                         const std::string& name);
diff --git a/content/browser/devtools/protocol_config.json b/content/browser/devtools/protocol_config.json
index f47e589..74a4141 100644
--- a/content/browser/devtools/protocol_config.json
+++ b/content/browser/devtools/protocol_config.json
@@ -90,7 +90,7 @@
             },
             {
                 "domain": "Storage",
-                "async": ["getUsageAndQuota", "clearDataForOrigin", "getCookies", "setCookies", "clearCookies", "overrideQuotaForOrigin", "getTrustTokens", "clearTrustTokens"]
+                "async": ["getUsageAndQuota", "clearDataForOrigin", "getCookies", "setCookies", "clearCookies", "overrideQuotaForOrigin", "getTrustTokens", "clearTrustTokens", "getInterestGroupDetails"]
             },
             {
                 "domain": "SystemInfo",
diff --git a/content/browser/download/download_manager_impl.cc b/content/browser/download/download_manager_impl.cc
index 7dd62f86..2cab9dc 100644
--- a/content/browser/download/download_manager_impl.cc
+++ b/content/browser/download/download_manager_impl.cc
@@ -83,7 +83,6 @@
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 #include "services/network/public/cpp/url_util.h"
 #include "services/network/public/cpp/wrapper_shared_url_loader_factory.h"
-#include "third_party/blink/public/common/loader/previews_state.h"
 #include "third_party/blink/public/common/loader/referrer_utils.h"
 #include "third_party/blink/public/common/loader/throttling_url_loader.h"
 #include "third_party/blink/public/common/storage_key/storage_key.h"
diff --git a/content/browser/download/save_file_manager.cc b/content/browser/download/save_file_manager.cc
index 809d7b9..eea55ea 100644
--- a/content/browser/download/save_file_manager.cc
+++ b/content/browser/download/save_file_manager.cc
@@ -38,7 +38,6 @@
 #include "services/network/public/cpp/simple_url_loader_stream_consumer.h"
 #include "services/network/public/mojom/url_loader_factory.mojom.h"
 #include "services/network/public/mojom/url_response_head.mojom.h"
-#include "third_party/blink/public/common/loader/previews_state.h"
 #include "third_party/blink/public/common/storage_key/storage_key.h"
 #include "url/gurl.h"
 #include "url/origin.h"
diff --git a/content/browser/fenced_frame/fenced_frame_url_mapping.h b/content/browser/fenced_frame/fenced_frame_url_mapping.h
index f8578ac6..3ccf9c7b 100644
--- a/content/browser/fenced_frame/fenced_frame_url_mapping.h
+++ b/content/browser/fenced_frame/fenced_frame_url_mapping.h
@@ -10,6 +10,7 @@
 #include <string>
 #include <vector>
 
+#include "base/memory/raw_ptr.h"
 #include "content/common/content_export.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "url/gurl.h"
diff --git a/content/browser/interest_group/ad_auction_result_metrics.cc b/content/browser/interest_group/ad_auction_result_metrics.cc
index 694405b..36f6997 100644
--- a/content/browser/interest_group/ad_auction_result_metrics.cc
+++ b/content/browser/interest_group/ad_auction_result_metrics.cc
@@ -5,10 +5,13 @@
 #include "content/browser/interest_group/ad_auction_result_metrics.h"
 
 #include "base/check_op.h"
+#include "base/feature_list.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/metrics/sparse_histogram.h"
 #include "base/time/time.h"
 #include "content/public/browser/page_user_data.h"
+#include "content/public/common/content_features.h"
+#include "third_party/blink/public/common/features.h"
 
 namespace content {
 
@@ -18,29 +21,51 @@
     : PageUserData<AdAuctionResultMetrics>(page) {}
 
 AdAuctionResultMetrics::~AdAuctionResultMetrics() {
-  if (num_auctions_ <= 0)
+  if (num_completed_auctions_ <= 0)
     return;
+  // Check that every non-skipped auction should complete (but skip the check if
+  // clamping may have occurred).
+  if (num_requested_auctions_ <
+      std::numeric_limits<decltype(num_requested_auctions_)::type>::max()) {
+    DCHECK_EQ(num_auctions_not_run_due_to_auction_limit_.RawValue(),
+              (num_requested_auctions_ - num_completed_auctions_).RawValue());
+  }
   base::UmaHistogramCounts100("Ads.InterestGroup.Auction.NumAuctionsPerPage",
-                              num_auctions_);
+                              num_completed_auctions_);
   base::UmaHistogramPercentage(
       "Ads.InterestGroup.Auction.PercentAuctionsSuccessfulPerPage",
-      num_successful_auctions_ * 100 / num_auctions_);
+      num_successful_auctions_ * 100 / num_completed_auctions_);
   DCHECK_GE(first_auction_bits_, 0b10u);
   DCHECK_LE(first_auction_bits_, (1 << kNumFirstAuctionBits) - 1);
   base::SparseHistogram::FactoryGet(
       "Ads.InterestGroup.Auction.First6AuctionsBitsPerPage",
       base::HistogramBase::kUmaTargetedHistogramFlag)
       ->Add(first_auction_bits_);
+  base::UmaHistogramCounts100(
+      "Ads.InterestGroup.Auction.NumAuctionsSkippedDueToAuctionLimit",
+      num_auctions_not_run_due_to_auction_limit_);
+}
+
+bool AdAuctionResultMetrics::ShouldRunAuction() {
+  DCHECK(base::FeatureList::IsEnabled(blink::features::kFledge));
+  num_requested_auctions_++;
+  if (!base::FeatureList::IsEnabled(features::kFledgeLimitNumAuctions))
+    return true;
+  if (num_requested_auctions_ > features::kFledgeLimitNumAuctionsParam.Get()) {
+    num_auctions_not_run_due_to_auction_limit_++;
+    return false;
+  }
+  return true;
 }
 
 void AdAuctionResultMetrics::ReportAuctionResult(
     AdAuctionResultMetrics::AuctionResult result) {
-  num_auctions_++;
-  if (num_auctions_ < kNumFirstAuctionBits)
+  num_completed_auctions_++;
+  if (num_completed_auctions_ < kNumFirstAuctionBits)
     first_auction_bits_ <<= 1;
   if (result == AdAuctionResultMetrics::AuctionResult::kSucceeded) {
     num_successful_auctions_++;
-    if (num_auctions_ < kNumFirstAuctionBits)
+    if (num_completed_auctions_ < kNumFirstAuctionBits)
       first_auction_bits_ |= 0x1;
   }
   const base::TimeTicks now = base::TimeTicks::Now();
diff --git a/content/browser/interest_group/ad_auction_result_metrics.h b/content/browser/interest_group/ad_auction_result_metrics.h
index e2353bc40..eec2cc5 100644
--- a/content/browser/interest_group/ad_auction_result_metrics.h
+++ b/content/browser/interest_group/ad_auction_result_metrics.h
@@ -7,6 +7,7 @@
 
 #include <climits>
 
+#include "base/numerics/clamped_math.h"
 #include "base/time/time.h"
 #include "content/public/browser/page_user_data.h"
 
@@ -14,6 +15,8 @@
 
 class Page;
 
+// Reports UMA about success / failure auction patterns, and implements a
+// feature parameter to control the maximum number of auctions per-page.
 class AdAuctionResultMetrics
     : public content::PageUserData<AdAuctionResultMetrics> {
  public:
@@ -22,6 +25,22 @@
   explicit AdAuctionResultMetrics(content::Page& page);
   ~AdAuctionResultMetrics() override;
 
+  // To reduce the amount of information that may be exposed to the page from
+  // auction outcomes, the number of auctions per page may be limited by a
+  // feature parameter.
+  //
+  // Before starting an auction, this function should be consulted to check if
+  // this limit has already been encountered.
+  //
+  // This function should be called only once per auction attempt, since it
+  // modifies internal state.
+  bool ShouldRunAuction();
+
+  // After an auction as completed, this function should be called to report
+  // whether the auction succeeded or failed.
+  //
+  // This function should *not* be called for configuration failures where the
+  // auction result reveals no information about stored interest groups.
   void ReportAuctionResult(AuctionResult result);
 
  private:
@@ -31,15 +50,32 @@
   // high.
   static constexpr int kNumFirstAuctionBits = 7;
 
-  int num_auctions_ = 0;
-  int num_successful_auctions_ = 0;
+  // The number of calls to ShouldRunAuction(); used for enforcing auction
+  // limits.
+  base::ClampedNumeric<int> num_requested_auctions_ = 0;
+
+  // The number of auctions that ran to completion, successful and failed --
+  // this is also the number of ReportAuctionResult() calls.
+  base::ClampedNumeric<int> num_completed_auctions_ = 0;
+
+  // The number of auctions that ran to completion and succeeded.
+  base::ClampedNumeric<int> num_successful_auctions_ = 0;
+
+  // The number of auctions requested that didn't run because the page auction
+  // limit had been reached. Should equal num_requested_auctions_ -
+  // num_completed_auctions_, which is DCHECK'd on destruction.
+  base::ClampedNumeric<int> num_auctions_not_run_due_to_auction_limit_ = 0;
+
+  // Stores the bitfield of the first kNumFirstAuctionBits auction results.
   uint8_t first_auction_bits_ = 1u;
+
+  // Stores the time of the last completed auction.
   base::TimeTicks last_auction_time_ = base::TimeTicks::Min();
 
   static_assert(kNumFirstAuctionBits <=
                     sizeof(AdAuctionResultMetrics::first_auction_bits_) *
                         CHAR_BIT,
-                "Not enough bits in `last_auction_time_`.");
+                "Not enough bits in `first_auction_bits_`.");
 
   friend PageUserData;
   PAGE_USER_DATA_KEY_DECL();
diff --git a/content/browser/interest_group/ad_auction_service_impl.cc b/content/browser/interest_group/ad_auction_service_impl.cc
index 9dfd18d..f4b99ae 100644
--- a/content/browser/interest_group/ad_auction_service_impl.cc
+++ b/content/browser/interest_group/ad_auction_service_impl.cc
@@ -325,6 +325,13 @@
   auto browser_signals = auction_worklet::mojom::BrowserSignals::New(
       GetTopWindowOrigin(), config->seller);
 
+  auto* auction_result_metrics = AdAuctionResultMetrics::GetOrCreateForPage(
+      render_frame_host()->GetPage());
+  if (!auction_result_metrics->ShouldRunAuction()) {
+    std::move(callback).Run(absl::nullopt);
+    return;
+  }
+
   std::unique_ptr<AuctionRunner> auction = AuctionRunner::CreateAndStart(
       &auction_worklet_manager_, this, &GetInterestGroupManager(),
       std::move(config), std::move(filtered_buyers), std::move(browser_signals),
@@ -476,8 +483,8 @@
         base::StrCat({"Worklet error: ", error}));
   }
 
-  auto* auction_result_metrics = AdAuctionResultMetrics::GetOrCreateForPage(
-      render_frame_host()->GetPage());
+  auto* auction_result_metrics =
+      AdAuctionResultMetrics::GetForPage(render_frame_host()->GetPage());
 
   if (!render_url) {
     DCHECK(!bidder_report_url);
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 1878ee34..5228d3c 100644
--- a/content/browser/interest_group/ad_auction_service_impl_unittest.cc
+++ b/content/browser/interest_group/ad_auction_service_impl_unittest.cc
@@ -8,13 +8,16 @@
 #include <string>
 #include <vector>
 
+#include "base/barrier_closure.h"
 #include "base/bind.h"
 #include "base/containers/flat_map.h"
+#include "base/feature_list.h"
 #include "base/memory/raw_ptr.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/run_loop.h"
 #include "base/synchronization/lock.h"
 #include "base/test/bind.h"
+#include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/thread_annotations.h"
 #include "base/time/time.h"
@@ -26,10 +29,9 @@
 #include "content/browser/interest_group/interest_group_storage.h"
 #include "content/browser/renderer_host/render_frame_host_impl.h"
 #include "content/browser/storage_partition_impl.h"
-#include "content/public/browser/back_forward_cache.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/common/content_client.h"
-#include "content/public/test/back_forward_cache_util.h"
+#include "content/public/common/content_features.h"
 #include "content/public/test/navigation_simulator.h"
 #include "content/public/test/test_renderer_host.h"
 #include "content/public/test/test_utils.h"
@@ -342,7 +344,11 @@
   AdAuctionServiceImplTest()
       : RenderViewHostTestHarness(
             base::test::TaskEnvironment::TimeSource::MOCK_TIME) {
-    feature_list_.InitAndEnableFeature(blink::features::kInterestGroupStorage);
+    feature_list_.InitWithFeatures(
+        /*enabled_features=*/{blink::features::kInterestGroupStorage,
+                              blink::features::kAdInterestGroupAPI,
+                              blink::features::kFledge},
+        /*disabled_features=*/{});
     old_content_browser_client_ =
         SetBrowserClientForTesting(&content_browser_client_);
   }
@@ -372,21 +378,6 @@
     RenderViewHostTestHarness::TearDown();
   }
 
-  void NavigateAndWaitForPageDestroyed(const GURL& dest_url) {
-    // Wait until the main render frame is deleted, which happens asynchronously
-    // after navigation -- the page gets destroyed with the main render frame.
-    // On Android, this hangs, likely due to architectural differences, so don't
-    // perform this wait on Android.
-#if !BUILDFLAG(IS_ANDROID)
-    RenderFrameHostWrapper main_frame_wrapper(web_contents()->GetMainFrame());
-    ASSERT_FALSE(main_frame_wrapper.IsDestroyed());
-#endif  // !BUILDFLAG(IS_ANDROID)
-    NavigateAndCommit(dest_url);
-#if !BUILDFLAG(IS_ANDROID)
-    ASSERT_TRUE(main_frame_wrapper.WaitUntilRenderFrameDeleted());
-#endif  // !BUILDFLAG(IS_ANDROID)
-  }
-
   std::vector<StorageInterestGroup> GetInterestGroupsForOwner(
       const url::Origin& owner) {
     std::vector<StorageInterestGroup> interest_groups;
@@ -773,8 +764,8 @@
   blink::InterestGroup::Ad ad;
   ad.render_url = GURL("https://example.com/render");
   ad.metadata = "{\"ad\":\"metadata\",\"here\":[1,2,3]}";
-  interest_group.ads->push_back(std::move(ad));
-  JoinInterestGroupAndFlush(std::move(interest_group));
+  interest_group.ads->emplace_back(std::move(ad));
+  JoinInterestGroupAndFlush(interest_group);
   EXPECT_EQ(1, GetJoinCount(kOriginA, kInterestGroupName));
 
   UpdateInterestGroupNoFlush();
@@ -824,8 +815,8 @@
   blink::InterestGroup::Ad ad;
   ad.render_url = GURL("https://example.com/render");
   ad.metadata = "{\"ad\":\"metadata\",\"here\":[1,2,3]}";
-  interest_group.ads->push_back(std::move(ad));
-  JoinInterestGroupAndFlush(std::move(interest_group));
+  interest_group.ads->emplace_back(std::move(ad));
+  JoinInterestGroupAndFlush(interest_group);
   EXPECT_EQ(1, GetJoinCount(kOriginA, kInterestGroupName));
 
   UpdateInterestGroupNoFlush();
@@ -878,8 +869,8 @@
   blink::InterestGroup::Ad ad;
   ad.render_url = GURL("https://example.com/render");
   ad.metadata = "{\"ad\":\"metadata\",\"here\":[1,2,3]}";
-  interest_group.ads->push_back(std::move(ad));
-  JoinInterestGroupAndFlush(std::move(interest_group));
+  interest_group.ads->emplace_back(std::move(ad));
+  JoinInterestGroupAndFlush(interest_group);
   EXPECT_EQ(1, GetJoinCount(kOriginA, kInterestGroupName));
 
   // Lookup expiry from the database before updating.
@@ -928,8 +919,8 @@
   blink::InterestGroup::Ad ad;
   ad.render_url = GURL("https://example.com/render");
   ad.metadata = "{\"ad\":\"metadata\",\"here\":[1,2,3]}";
-  interest_group.ads->push_back(std::move(ad));
-  JoinInterestGroupAndFlush(std::move(interest_group));
+  interest_group.ads->emplace_back(std::move(ad));
+  JoinInterestGroupAndFlush(interest_group);
   EXPECT_EQ(1, GetJoinCount(kOriginA, kInterestGroupName));
 
   UpdateInterestGroupNoFlush();
@@ -985,8 +976,8 @@
   blink::InterestGroup::Ad ad;
   ad.render_url = GURL("https://example.com/render");
   ad.metadata = "{\"ad\":\"metadata\",\"here\":[1,2,3]}";
-  interest_group.ads->push_back(std::move(ad));
-  JoinInterestGroupAndFlush(std::move(interest_group));
+  interest_group.ads->emplace_back(std::move(ad));
+  JoinInterestGroupAndFlush(interest_group);
   EXPECT_EQ(1, GetJoinCount(kOriginA, kInterestGroupName));
 
   UpdateInterestGroupNoFlush();
@@ -1028,8 +1019,8 @@
   blink::InterestGroup::Ad ad;
   ad.render_url = GURL("https://example.com/render");
   ad.metadata = "{\"ad\":\"metadata\",\"here\":[1,2,3]}";
-  interest_group.ads->push_back(std::move(ad));
-  JoinInterestGroupAndFlush(std::move(interest_group));
+  interest_group.ads->emplace_back(std::move(ad));
+  JoinInterestGroupAndFlush(interest_group);
   EXPECT_EQ(1, GetJoinCount(kOriginA, kInterestGroupName));
 
   UpdateInterestGroupNoFlush();
@@ -1083,8 +1074,8 @@
   blink::InterestGroup::Ad ad;
   ad.render_url = GURL("https://example.com/render");
   ad.metadata = "{\"ad\":\"metadata\",\"here\":[1,2,3]}";
-  interest_group.ads->push_back(std::move(ad));
-  JoinInterestGroupAndFlush(std::move(interest_group));
+  interest_group.ads->emplace_back(std::move(ad));
+  JoinInterestGroupAndFlush(interest_group);
   EXPECT_EQ(1, GetJoinCount(kOriginA, kGroupName1));
 
   // Now, join the second interest group, also belonging to `kOriginA`.
@@ -1099,8 +1090,8 @@
   ad = blink::InterestGroup::Ad();
   ad.render_url = GURL("https://example.com/render");
   ad.metadata = "{\"ad\":\"metadata\",\"here\":[1,2,3]}";
-  interest_group_2.ads->push_back(std::move(ad));
-  JoinInterestGroupAndFlush(std::move(interest_group_2));
+  interest_group_2.ads->emplace_back(std::move(ad));
+  JoinInterestGroupAndFlush(interest_group_2);
   EXPECT_EQ(1, GetJoinCount(kOriginA, kGroupName2));
 
   // Now, run the update. Both interest groups should update.
@@ -1157,8 +1148,8 @@
   blink::InterestGroup::Ad ad;
   ad.render_url = GURL("https://example.com/render");
   ad.metadata = "{\"ad\":\"metadata\",\"here\":[1,2,3]}";
-  interest_group.ads->push_back(std::move(ad));
-  JoinInterestGroupAndFlush(std::move(interest_group));
+  interest_group.ads->emplace_back(std::move(ad));
+  JoinInterestGroupAndFlush(interest_group);
   EXPECT_EQ(1, GetJoinCount(kOriginA, kInterestGroupName));
 
   // Now, join the second interest group, belonging to `kOriginB`.
@@ -1175,8 +1166,8 @@
   ad = blink::InterestGroup::Ad();
   ad.render_url = GURL("https://example.com/render");
   ad.metadata = "{\"ad\":\"metadata\",\"here\":[1,2,3]}";
-  interest_group_b.ads->push_back(std::move(ad));
-  JoinInterestGroupAndFlush(std::move(interest_group_b));
+  interest_group_b.ads->emplace_back(std::move(ad));
+  JoinInterestGroupAndFlush(interest_group_b);
   EXPECT_EQ(1, GetJoinCount(kOriginB, kInterestGroupName));
 
   // Now, run the update. Only the `kOriginB` group should get updated.
@@ -1230,8 +1221,8 @@
   blink::InterestGroup::Ad ad;
   ad.render_url = GURL("https://example.com/render");
   ad.metadata = "{\"ad\":\"metadata\",\"here\":[1,2,3]}";
-  interest_group.ads->push_back(std::move(ad));
-  JoinInterestGroupAndFlush(std::move(interest_group));
+  interest_group.ads->emplace_back(std::move(ad));
+  JoinInterestGroupAndFlush(interest_group);
   EXPECT_EQ(1, GetJoinCount(kOriginA, kInterestGroupName));
 
   // Now, join the second interest group, belonging to `kOriginB`.
@@ -1248,8 +1239,8 @@
   ad = blink::InterestGroup::Ad();
   ad.render_url = GURL("https://example.com/render");
   ad.metadata = "{\"ad\":\"metadata\",\"here\":[1,2,3]}";
-  interest_group_b.ads->push_back(std::move(ad));
-  JoinInterestGroupAndFlush(std::move(interest_group_b));
+  interest_group_b.ads->emplace_back(std::move(ad));
+  JoinInterestGroupAndFlush(interest_group_b);
   EXPECT_EQ(1, GetJoinCount(kOriginB, kInterestGroupName));
 
   // Now, join the third interest group, belonging to `kOriginC`.
@@ -1266,8 +1257,8 @@
   ad = blink::InterestGroup::Ad();
   ad.render_url = GURL("https://example.com/render");
   ad.metadata = "{\"ad\":\"metadata\",\"here\":[1,2,3]}";
-  interest_group_c.ads->push_back(std::move(ad));
-  JoinInterestGroupAndFlush(std::move(interest_group_c));
+  interest_group_c.ads->emplace_back(std::move(ad));
+  JoinInterestGroupAndFlush(interest_group_c);
   EXPECT_EQ(1, GetJoinCount(kOriginC, kInterestGroupName));
 
   NavigateAndCommit(kUrlA);
@@ -1353,8 +1344,8 @@
   blink::InterestGroup::Ad ad;
   ad.render_url = GURL("https://example.com/render");
   ad.metadata = "{\"ad\":\"metadata\",\"here\":[1,2,3]}";
-  interest_group.ads->push_back(std::move(ad));
-  JoinInterestGroupAndFlush(std::move(interest_group));
+  interest_group.ads->emplace_back(std::move(ad));
+  JoinInterestGroupAndFlush(interest_group);
   EXPECT_EQ(1, GetJoinCount(kOriginA, kInterestGroupName));
 
   UpdateInterestGroupNoFlush();
@@ -1388,8 +1379,8 @@
   blink::InterestGroup::Ad ad;
   ad.render_url = GURL("https://example.com/render");
   ad.metadata = "{\"ad\":\"metadata\",\"here\":[1,2,3]}";
-  interest_group.ads->push_back(std::move(ad));
-  JoinInterestGroupAndFlush(std::move(interest_group));
+  interest_group.ads->emplace_back(std::move(ad));
+  JoinInterestGroupAndFlush(interest_group);
   EXPECT_EQ(1, GetJoinCount(kOriginA, kInterestGroupName));
 
   UpdateInterestGroupNoFlush();
@@ -1435,8 +1426,8 @@
   blink::InterestGroup::Ad ad;
   ad.render_url = GURL("https://example.com/render");
   ad.metadata = "{\"ad\":\"metadata\",\"here\":[1,2,3]}";
-  interest_group.ads->push_back(std::move(ad));
-  JoinInterestGroupAndFlush(std::move(interest_group));
+  interest_group.ads->emplace_back(std::move(ad));
+  JoinInterestGroupAndFlush(interest_group);
   EXPECT_EQ(1, GetJoinCount(kOriginA, kInterestGroupName));
 
   // Simulate the JSON service crashing instead of returning a result.
@@ -1474,8 +1465,8 @@
   blink::InterestGroup::Ad ad;
   ad.render_url = GURL("https://example.com/render");
   ad.metadata = "{\"ad\":\"metadata\",\"here\":[1,2,3]}";
-  interest_group.ads->push_back(std::move(ad));
-  JoinInterestGroupAndFlush(std::move(interest_group));
+  interest_group.ads->emplace_back(std::move(ad));
+  JoinInterestGroupAndFlush(interest_group);
   EXPECT_EQ(1, GetJoinCount(kOriginA, kInterestGroupName));
 
   UpdateInterestGroupNoFlush();
@@ -1508,8 +1499,8 @@
   blink::InterestGroup::Ad ad;
   ad.render_url = GURL("https://example.com/render");
   ad.metadata = "{\"ad\":\"metadata\",\"here\":[1,2,3]}";
-  interest_group.ads->push_back(std::move(ad));
-  JoinInterestGroupAndFlush(std::move(interest_group));
+  interest_group.ads->emplace_back(std::move(ad));
+  JoinInterestGroupAndFlush(interest_group);
   EXPECT_EQ(1, GetJoinCount(kOriginA, kInterestGroupName));
 
   UpdateInterestGroupNoFlush();
@@ -1560,8 +1551,8 @@
   blink::InterestGroup::Ad ad;
   ad.render_url = GURL("https://example.com/render");
   ad.metadata = "{\"ad\":\"metadata\",\"here\":[1,2,3]}";
-  interest_group.ads->push_back(std::move(ad));
-  JoinInterestGroupAndFlush(std::move(interest_group));
+  interest_group.ads->emplace_back(std::move(ad));
+  JoinInterestGroupAndFlush(interest_group);
   EXPECT_EQ(1, GetJoinCount(kOriginA, kInterestGroupName));
 
   // Start an interest group update and then advance time to ensure the interest
@@ -1640,8 +1631,8 @@
   blink::InterestGroup::Ad ad;
   ad.render_url = GURL("https://example.com/render");
   ad.metadata = "{\"ad\":\"metadata\",\"here\":[1,2,3]}";
-  interest_group.ads->push_back(std::move(ad));
-  JoinInterestGroupAndFlush(std::move(interest_group));
+  interest_group.ads->emplace_back(std::move(ad));
+  JoinInterestGroupAndFlush(interest_group);
   EXPECT_EQ(1, GetJoinCount(kOriginA, kInterestGroupName));
 
   // Start an interest group update and then advance time to ensure the interest
@@ -1705,8 +1696,8 @@
   blink::InterestGroup::Ad ad;
   ad.render_url = GURL("https://example.com/render");
   ad.metadata = "{\"ad\":\"metadata\",\"here\":[1,2,3]}";
-  interest_group.ads->push_back(std::move(ad));
-  JoinInterestGroupAndFlush(std::move(interest_group));
+  interest_group.ads->emplace_back(std::move(ad));
+  JoinInterestGroupAndFlush(interest_group);
   EXPECT_EQ(1, GetJoinCount(kOriginA, kInterestGroupName));
 
   UpdateInterestGroupNoFlush();
@@ -1746,8 +1737,8 @@
   blink::InterestGroup::Ad ad;
   ad.render_url = GURL("https://example.com/render");
   ad.metadata = "{\"ad\":\"metadata\",\"here\":[1,2,3]}";
-  interest_group.ads->push_back(std::move(ad));
-  JoinInterestGroupAndFlush(std::move(interest_group));
+  interest_group.ads->emplace_back(std::move(ad));
+  JoinInterestGroupAndFlush(interest_group);
   EXPECT_EQ(1, GetJoinCount(kOriginA, kInterestGroupName));
 
   // Register 2 bids and a win.
@@ -1809,8 +1800,8 @@
   blink::InterestGroup::Ad ad;
   ad.render_url = GURL("https://example.com/render");
   ad.metadata = "{\"ad\":\"metadata\",\"here\":[1,2,3]}";
-  interest_group.ads->push_back(std::move(ad));
-  JoinInterestGroupAndFlush(std::move(interest_group));
+  interest_group.ads->emplace_back(std::move(ad));
+  JoinInterestGroupAndFlush(interest_group);
   EXPECT_EQ(1, GetJoinCount(kOriginA, kInterestGroupName));
 
   UpdateInterestGroupNoFlush();
@@ -1908,8 +1899,8 @@
   blink::InterestGroup::Ad ad;
   ad.render_url = GURL("https://example.com/render");
   ad.metadata = "{\"ad\":\"metadata\",\"here\":[1,2,3]}";
-  interest_group.ads->push_back(std::move(ad));
-  JoinInterestGroupAndFlush(std::move(interest_group));
+  interest_group.ads->emplace_back(std::move(ad));
+  JoinInterestGroupAndFlush(interest_group);
   EXPECT_EQ(1, GetJoinCount(kOriginA, kInterestGroupName));
 
   UpdateInterestGroupNoFlush();
@@ -2009,8 +2000,8 @@
   blink::InterestGroup::Ad ad;
   ad.render_url = GURL("https://example.com/render");
   ad.metadata = "{\"ad\":\"metadata\",\"here\":[1,2,3]}";
-  interest_group.ads->push_back(std::move(ad));
-  JoinInterestGroupAndFlush(std::move(interest_group));
+  interest_group.ads->emplace_back(std::move(ad));
+  JoinInterestGroupAndFlush(interest_group);
   EXPECT_EQ(1, GetJoinCount(kOriginA, kInterestGroupName));
 
   UpdateInterestGroupNoFlush();
@@ -2110,8 +2101,8 @@
   blink::InterestGroup::Ad ad;
   ad.render_url = GURL("https://example.com/render");
   ad.metadata = "{\"ad\":\"metadata\",\"here\":[1,2,3]}";
-  interest_group.ads->push_back(std::move(ad));
-  JoinInterestGroupAndFlush(std::move(interest_group));
+  interest_group.ads->emplace_back(std::move(ad));
+  JoinInterestGroupAndFlush(interest_group);
   EXPECT_EQ(1, GetJoinCount(kOriginA, kInterestGroupName));
 
   UpdateInterestGroupNoFlush();
@@ -2171,8 +2162,8 @@
   blink::InterestGroup::Ad ad;
   ad.render_url = GURL("https://example.com/render");
   ad.metadata = "{\"ad\":\"metadata\",\"here\":[1,2,3]}";
-  interest_group.ads->push_back(std::move(ad));
-  JoinInterestGroupAndFlush(std::move(interest_group));
+  interest_group.ads->emplace_back(std::move(ad));
+  JoinInterestGroupAndFlush(interest_group);
   EXPECT_EQ(1, GetJoinCount(kOriginA, kInterestGroupName));
 
   EXPECT_EQ(network_responder_->UpdateCount(), 0u);
@@ -2204,22 +2195,25 @@
   browserSignals) {
   return {'ad': 'example', 'bid': 1, 'render': 'https://example.com/render'};
 }
-  )";
+)";
+
   constexpr char kDecisionScript[] = R"(
 function scoreAd(
   adMetadata, bid, auctionConfig, trustedScoringSignals, browserSignals) {
   return bid;
 }
-  )";
+)";
+
   network_responder_->RegisterScriptResponse(kBiddingUrlPath, kBiddingScript);
   network_responder_->RegisterScriptResponse(kDecisionUrlPath, kDecisionScript);
+
   blink::InterestGroup interest_group = CreateInterestGroup();
   interest_group.bidding_url = kUrlA.Resolve(kBiddingUrlPath);
   interest_group.ads.emplace();
   blink::InterestGroup::Ad ad;
   ad.render_url = GURL("https://example.com/render");
-  interest_group.ads->push_back(std::move(ad));
-  JoinInterestGroupAndFlush(std::move(interest_group));
+  interest_group.ads->emplace_back(std::move(ad));
+  JoinInterestGroupAndFlush(interest_group);
   EXPECT_EQ(1, GetJoinCount(kOriginA, kInterestGroupName));
 
   auto auction_config = blink::mojom::AuctionAdConfig::New();
@@ -2249,6 +2243,7 @@
 }
   )",
                                                         kOriginStringA);
+
   const std::string kDecisionScript =
       base::StringPrintf(R"(
 function scoreAd(
@@ -2263,8 +2258,9 @@
     'reportUrl': '%s/report_seller',
   };
 }
-  )",
+)",
                          kOriginStringA, kOriginStringA);
+
   network_responder_->RegisterScriptResponse(kBiddingUrlPath, kBiddingScript);
   network_responder_->RegisterScriptResponse(kDecisionUrlPath, kDecisionScript);
   network_responder_->RegisterReportResponse("/report_bidder", "");
@@ -2275,8 +2271,8 @@
   interest_group.ads.emplace();
   blink::InterestGroup::Ad ad;
   ad.render_url = GURL("https://example.com/render");
-  interest_group.ads->push_back(std::move(ad));
-  JoinInterestGroupAndFlush(std::move(interest_group));
+  interest_group.ads->emplace_back(std::move(ad));
+  JoinInterestGroupAndFlush(interest_group);
   EXPECT_EQ(1, GetJoinCount(kOriginA, kInterestGroupName));
 
   auto auction_config = blink::mojom::AuctionAdConfig::New();
@@ -2305,13 +2301,10 @@
 // not. Verify that the auction result UMA is recorded correctly.
 TEST_F(AdAuctionServiceImplTest,
        AddInterestGroupRunAuctionVerifyResultMetrics) {
-  // The test assumes that the main frame RFH will be replaced during
-  // navigation.
-  DisableBackForwardCacheForTesting(web_contents(),
-                                    BackForwardCache::TEST_ASSUMES_NO_CACHING);
   base::HistogramTester histogram_tester;
   constexpr char kDecisionFailAllUrlPath[] =
       "/interest_group/decision_logic_fail_all.js";
+
   constexpr char kBiddingScript[] = R"(
 function generateBid(
   interestGroup, auctionSignals, perBuyerSignals, trustedBiddingSignals,
@@ -2319,14 +2312,16 @@
   return {'ad': 'example', 'bid': 1, 'render': 'https://example.com/render'};
 }
 function reportWin() {}
-  )";
+)";
+
   constexpr char kDecisionScript[] = R"(
 function scoreAd(
   adMetadata, bid, auctionConfig, trustedScoringSignals, browserSignals) {
   return bid;
 }
 function reportResult() {}
-  )";
+)";
+
   constexpr char kDecisionScriptFailAll[] = R"(
 function scoreAd(
   adMetadata, bid, auctionConfig, trustedScoringSignals, browserSignals) {
@@ -2334,18 +2329,20 @@
 }
 function reportResult() {}
 )";
+
   network_responder_->RegisterScriptResponse(kBiddingUrlPath, kBiddingScript);
   network_responder_->RegisterScriptResponse(kDecisionUrlPath, kDecisionScript);
   network_responder_->RegisterScriptResponse(kDecisionFailAllUrlPath,
                                              kDecisionScriptFailAll);
+
   blink::InterestGroup interest_group = CreateInterestGroup();
   interest_group.expiry = base::Time::Now() + base::Days(10);
   interest_group.bidding_url = kUrlA.Resolve(kBiddingUrlPath);
   interest_group.ads.emplace();
   blink::InterestGroup::Ad ad;
   ad.render_url = GURL("https://example.com/render");
-  interest_group.ads->push_back(std::move(ad));
-  JoinInterestGroupAndFlush(std::move(interest_group));
+  interest_group.ads->emplace_back(std::move(ad));
+  JoinInterestGroupAndFlush(interest_group);
   EXPECT_EQ(1, GetJoinCount(kOriginA, kInterestGroupName));
 
   // Run 7 auctions, with delays:
@@ -2445,9 +2442,15 @@
           .GetAllSamples("Ads.InterestGroup.Auction.First6AuctionsBitsPerPage")
           .size(),
       0u);
+  EXPECT_EQ(
+      histogram_tester
+          .GetAllSamples(
+              "Ads.InterestGroup.Auction.NumAuctionsSkippedDueToAuctionLimit")
+          .size(),
+      0u);
 
-  // Navigate to populate remaining metrics.
-  ASSERT_NO_FATAL_FAILURE(NavigateAndWaitForPageDestroyed(kUrlB));
+  // DeleteContents() to force-populate remaining metrics.
+  DeleteContents();
 
   histogram_tester.ExpectUniqueSample(
       "Ads.InterestGroup.Auction.NumAuctionsPerPage", 7, 1);
@@ -2456,6 +2459,8 @@
       1);
   histogram_tester.ExpectUniqueSample(
       "Ads.InterestGroup.Auction.First6AuctionsBitsPerPage", 0b1101110, 1);
+  histogram_tester.ExpectUniqueSample(
+      "Ads.InterestGroup.Auction.NumAuctionsSkippedDueToAuctionLimit", 0, 1);
 }
 
 // Like AddInterestGroupRunAuctionVerifyResultMetrics, but with a smaller number
@@ -2463,13 +2468,10 @@
 // reported correctly in this scenario.
 TEST_F(AdAuctionServiceImplTest,
        AddInterestGroupRunAuctionVerifyResultMetricsFewAuctions) {
-  // The test assumes that the main frame RFH will be replaced during
-  // navigation.
-  DisableBackForwardCacheForTesting(web_contents(),
-                                    BackForwardCache::TEST_ASSUMES_NO_CACHING);
   base::HistogramTester histogram_tester;
   constexpr char kDecisionFailAllUrlPath[] =
       "/interest_group/decision_logic_fail_all.js";
+
   constexpr char kBiddingScript[] = R"(
 function generateBid(
   interestGroup, auctionSignals, perBuyerSignals, trustedBiddingSignals,
@@ -2477,14 +2479,16 @@
   return {'ad': 'example', 'bid': 1, 'render': 'https://example.com/render'};
 }
 function reportWin() {}
-  )";
+)";
+
   constexpr char kDecisionScript[] = R"(
 function scoreAd(
   adMetadata, bid, auctionConfig, trustedScoringSignals, browserSignals) {
   return bid;
 }
 function reportResult() {}
-  )";
+)";
+
   constexpr char kDecisionScriptFailAll[] = R"(
 function scoreAd(
   adMetadata, bid, auctionConfig, trustedScoringSignals, browserSignals) {
@@ -2492,18 +2496,20 @@
 }
 function reportResult() {}
 )";
+
   network_responder_->RegisterScriptResponse(kBiddingUrlPath, kBiddingScript);
   network_responder_->RegisterScriptResponse(kDecisionUrlPath, kDecisionScript);
   network_responder_->RegisterScriptResponse(kDecisionFailAllUrlPath,
                                              kDecisionScriptFailAll);
+
   blink::InterestGroup interest_group = CreateInterestGroup();
   interest_group.expiry = base::Time::Now() + base::Days(10);
   interest_group.bidding_url = kUrlA.Resolve(kBiddingUrlPath);
   interest_group.ads.emplace();
   blink::InterestGroup::Ad ad;
   ad.render_url = GURL("https://example.com/render");
-  interest_group.ads->push_back(std::move(ad));
-  JoinInterestGroupAndFlush(std::move(interest_group));
+  interest_group.ads->emplace_back(std::move(ad));
+  JoinInterestGroupAndFlush(interest_group);
   EXPECT_EQ(1, GetJoinCount(kOriginA, kInterestGroupName));
 
   // Run 2 auctions, with delays:
@@ -2561,9 +2567,15 @@
           .GetAllSamples("Ads.InterestGroup.Auction.First6AuctionsBitsPerPage")
           .size(),
       0u);
+  EXPECT_EQ(
+      histogram_tester
+          .GetAllSamples(
+              "Ads.InterestGroup.Auction.NumAuctionsSkippedDueToAuctionLimit")
+          .size(),
+      0u);
 
-  // Navigate to populate remaining metrics.
-  ASSERT_NO_FATAL_FAILURE(NavigateAndWaitForPageDestroyed(kUrlB));
+  // DeleteContents() to force-populate remaining metrics.
+  DeleteContents();
 
   histogram_tester.ExpectUniqueSample(
       "Ads.InterestGroup.Auction.NumAuctionsPerPage", 2, 1);
@@ -2572,22 +2584,20 @@
       1);
   histogram_tester.ExpectUniqueSample(
       "Ads.InterestGroup.Auction.First6AuctionsBitsPerPage", 0b110, 1);
+  histogram_tester.ExpectUniqueSample(
+      "Ads.InterestGroup.Auction.NumAuctionsSkippedDueToAuctionLimit", 0, 1);
 }
 
 // Like AddInterestGroupRunAuctionVerifyResultMetricsFewAuctions, but with no
 // auctions.
 TEST_F(AdAuctionServiceImplTest,
        AddInterestGroupRunAuctionVerifyResultMetricsNoAuctions) {
-  // The test assumes that the main frame RFH will be replaced during
-  // navigation.
-  DisableBackForwardCacheForTesting(web_contents(),
-                                    BackForwardCache::TEST_ASSUMES_NO_CACHING);
   base::HistogramTester histogram_tester;
 
   // Don't run any auctions.
 
   // Navigate to "populate" remaining metrics.
-  ASSERT_NO_FATAL_FAILURE(NavigateAndWaitForPageDestroyed(kUrlB));
+  DeleteContents();
 
   // Nothing gets reported since there were no auctions.
   EXPECT_EQ(histogram_tester
@@ -2610,6 +2620,345 @@
                     "Ads.InterestGroup.Auction.TimeSinceLastAuctionPerPage")
                 .size(),
             0u);
+  EXPECT_EQ(
+      histogram_tester
+          .GetAllSamples(
+              "Ads.InterestGroup.Auction.NumAuctionsSkippedDueToAuctionLimit")
+          .size(),
+      0u);
+}
+
+// The feature parameter that controls the interest group limit should default
+// to off. We both check the parameter is off, and we run a number of auctions
+// and make sure they all succeed.
+TEST_F(AdAuctionServiceImplTest, NoInterestLimitByDefault) {
+  EXPECT_FALSE(base::FeatureList::IsEnabled(features::kFledgeLimitNumAuctions));
+  base::HistogramTester histogram_tester;
+  constexpr char kDecisionFailAllUrlPath[] =
+      "/interest_group/decision_logic_fail_all.js";
+
+  constexpr char kBiddingScript[] = R"(
+function generateBid(
+  interestGroup, auctionSignals, perBuyerSignals, trustedBiddingSignals,
+  browserSignals) {
+  return {'ad': 'example', 'bid': 1, 'render': 'https://example.com/render'};
+}
+function reportWin() {}
+)";
+
+  constexpr char kDecisionScript[] = R"(
+function scoreAd(
+  adMetadata, bid, auctionConfig, trustedScoringSignals, browserSignals) {
+  return bid;
+}
+function reportResult() {}
+)";
+
+  constexpr char kDecisionScriptFailAll[] = R"(
+function scoreAd(
+  adMetadata, bid, auctionConfig, trustedScoringSignals, browserSignals) {
+  return 0;
+}
+function reportResult() {}
+)";
+
+  network_responder_->RegisterScriptResponse(kBiddingUrlPath, kBiddingScript);
+  network_responder_->RegisterScriptResponse(kDecisionUrlPath, kDecisionScript);
+  network_responder_->RegisterScriptResponse(kDecisionFailAllUrlPath,
+                                             kDecisionScriptFailAll);
+
+  blink::InterestGroup interest_group = CreateInterestGroup();
+  interest_group.expiry = base::Time::Now() + base::Days(10);
+  interest_group.bidding_url = kUrlA.Resolve(kBiddingUrlPath);
+  interest_group.ads.emplace();
+  blink::InterestGroup::Ad ad;
+  ad.render_url = GURL("https://example.com/render");
+  interest_group.ads->emplace_back(std::move(ad));
+  JoinInterestGroupAndFlush(interest_group);
+  EXPECT_EQ(1, GetJoinCount(kOriginA, kInterestGroupName));
+
+  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->shareable_auction_ad_config =
+      blink::mojom::ShareableAuctionAdConfig::New();
+  succeed_auction_config->shareable_auction_ad_config->interest_group_buyers =
+      blink::mojom::InterestGroupBuyers::NewBuyers({kOriginA});
+
+  for (int i = 0; i < kNumAuctions; i++) {
+    EXPECT_NE(RunAdAuctionAndFlush(succeed_auction_config->Clone()),
+              absl::nullopt);
+  }
+
+  // Some metrics only get reported until after navigation.
+  EXPECT_EQ(histogram_tester
+                .GetAllSamples("Ads.InterestGroup.Auction.NumAuctionsPerPage")
+                .size(),
+            0u);
+  EXPECT_EQ(
+      histogram_tester
+          .GetAllSamples(
+              "Ads.InterestGroup.Auction.PercentAuctionsSuccessfulPerPage")
+          .size(),
+      0u);
+  EXPECT_EQ(
+      histogram_tester
+          .GetAllSamples("Ads.InterestGroup.Auction.First6AuctionsBitsPerPage")
+          .size(),
+      0u);
+  EXPECT_EQ(
+      histogram_tester
+          .GetAllSamples(
+              "Ads.InterestGroup.Auction.NumAuctionsSkippedDueToAuctionLimit")
+          .size(),
+      0u);
+
+  // DeleteContents() to force-populate remaining metrics.
+  DeleteContents();
+
+  // Every auction succeeds, none are skipped.
+  histogram_tester.ExpectUniqueSample(
+      "Ads.InterestGroup.Auction.NumAuctionsPerPage", kNumAuctions, 1);
+  histogram_tester.ExpectUniqueSample(
+      "Ads.InterestGroup.Auction.PercentAuctionsSuccessfulPerPage", 100, 1);
+  histogram_tester.ExpectUniqueSample(
+      "Ads.InterestGroup.Auction.First6AuctionsBitsPerPage", 0b1111111, 1);
+  // However, we do record that the auction was skipped.
+  histogram_tester.ExpectUniqueSample(
+      "Ads.InterestGroup.Auction.NumAuctionsSkippedDueToAuctionLimit", 0, 1);
+}
+
+class AdAuctionServiceImplNumAuctionLimitTest
+    : public AdAuctionServiceImplTest {
+ public:
+  AdAuctionServiceImplNumAuctionLimitTest() {
+    // Only 2 auctions are allowed per-page.
+    feature_list_.InitAndEnableFeatureWithParameters(
+        features::kFledgeLimitNumAuctions, {{"max_auctions_per_page", "2"}});
+  }
+
+ protected:
+  base::test::ScopedFeatureList feature_list_;
+};
+
+// Like AddInterestGroupRunAuctionVerifyResultMetrics, but with enforcement
+// limiting the number of auctions.
+TEST_F(AdAuctionServiceImplNumAuctionLimitTest,
+       AddInterestGroupRunAuctionWithNumAuctionLimits) {
+  base::HistogramTester histogram_tester;
+  constexpr char kDecisionFailAllUrlPath[] =
+      "/interest_group/decision_logic_fail_all.js";
+
+  constexpr char kBiddingScript[] = R"(
+function generateBid(
+  interestGroup, auctionSignals, perBuyerSignals, trustedBiddingSignals,
+  browserSignals) {
+  return {'ad': 'example', 'bid': 1, 'render': 'https://example.com/render'};
+}
+function reportWin() {}
+)";
+
+  constexpr char kDecisionScript[] = R"(
+function scoreAd(
+  adMetadata, bid, auctionConfig, trustedScoringSignals, browserSignals) {
+  return bid;
+}
+function reportResult() {}
+)";
+
+  constexpr char kDecisionScriptFailAll[] = R"(
+function scoreAd(
+  adMetadata, bid, auctionConfig, trustedScoringSignals, browserSignals) {
+  return 0;
+}
+function reportResult() {}
+)";
+
+  network_responder_->RegisterScriptResponse(kBiddingUrlPath, kBiddingScript);
+  network_responder_->RegisterScriptResponse(kDecisionUrlPath, kDecisionScript);
+  network_responder_->RegisterScriptResponse(kDecisionFailAllUrlPath,
+                                             kDecisionScriptFailAll);
+
+  blink::InterestGroup interest_group = CreateInterestGroup();
+  interest_group.expiry = base::Time::Now() + base::Days(10);
+  interest_group.bidding_url = kUrlA.Resolve(kBiddingUrlPath);
+  interest_group.ads.emplace();
+  blink::InterestGroup::Ad ad;
+  ad.render_url = GURL("https://example.com/render");
+  interest_group.ads->emplace_back(std::move(ad));
+  JoinInterestGroupAndFlush(interest_group);
+  EXPECT_EQ(1, GetJoinCount(kOriginA, kInterestGroupName));
+
+  // Run 3 auctions, with delays:
+  //
+  // succeed, (1s), fail, (3s), succeed which in bits (with an extra leading 1)
+  // is 0b110 -- the last success isn't recorded since the auction limit is
+  // enforced.
+
+  // Expect*TimeSample() doesn't accept base::TimeDelta::Max(), but the max time
+  // 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->shareable_auction_ad_config =
+      blink::mojom::ShareableAuctionAdConfig::New();
+  succeed_auction_config->shareable_auction_ad_config->interest_group_buyers =
+      blink::mojom::InterestGroupBuyers::NewBuyers({kOriginA});
+
+  auto fail_auction_config = blink::mojom::AuctionAdConfig::New();
+  fail_auction_config->seller = kOriginA;
+  fail_auction_config->decision_logic_url =
+      kUrlA.Resolve(kDecisionFailAllUrlPath);
+  fail_auction_config->shareable_auction_ad_config =
+      blink::mojom::ShareableAuctionAdConfig::New();
+  fail_auction_config->shareable_auction_ad_config->interest_group_buyers =
+      blink::mojom::InterestGroupBuyers::NewBuyers({kOriginA});
+
+  // 1st auction
+  EXPECT_NE(RunAdAuctionAndFlush(succeed_auction_config->Clone()),
+            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);
+  histogram_tester.ExpectTimeBucketCount(
+      "Ads.InterestGroup.Auction.TimeSinceLastAuctionPerPage", base::Seconds(1),
+      1);
+
+  // 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);
+  // The time metrics shouldn't get updated.
+  histogram_tester.ExpectTimeBucketCount(
+      "Ads.InterestGroup.Auction.TimeSinceLastAuctionPerPage", base::Seconds(3),
+      0);
+
+  // Some metrics only get reported until after navigation.
+  EXPECT_EQ(histogram_tester
+                .GetAllSamples("Ads.InterestGroup.Auction.NumAuctionsPerPage")
+                .size(),
+            0u);
+  EXPECT_EQ(
+      histogram_tester
+          .GetAllSamples(
+              "Ads.InterestGroup.Auction.PercentAuctionsSuccessfulPerPage")
+          .size(),
+      0u);
+  EXPECT_EQ(
+      histogram_tester
+          .GetAllSamples("Ads.InterestGroup.Auction.First6AuctionsBitsPerPage")
+          .size(),
+      0u);
+  EXPECT_EQ(
+      histogram_tester
+          .GetAllSamples(
+              "Ads.InterestGroup.Auction.NumAuctionsSkippedDueToAuctionLimit")
+          .size(),
+      0u);
+
+  // DeleteContents() to force-populate remaining metrics.
+  DeleteContents();
+
+  // The last auction doesn't count towards these metrics since the auction
+  // limit is enforced -- this is because that auction doesn't contribute any
+  // knowledge about stored interest groups to the page.
+  histogram_tester.ExpectUniqueSample(
+      "Ads.InterestGroup.Auction.NumAuctionsPerPage", 2, 1);
+  histogram_tester.ExpectUniqueSample(
+      "Ads.InterestGroup.Auction.PercentAuctionsSuccessfulPerPage", 1 * 100 / 2,
+      1);
+  histogram_tester.ExpectUniqueSample(
+      "Ads.InterestGroup.Auction.First6AuctionsBitsPerPage", 0b110, 1);
+  // However, we do record that the auction was skipped.
+  histogram_tester.ExpectUniqueSample(
+      "Ads.InterestGroup.Auction.NumAuctionsSkippedDueToAuctionLimit", 1, 1);
+}
+
+TEST_F(AdAuctionServiceImplNumAuctionLimitTest,
+       AddInterestGroupRunAuctionStartManyAuctionsInParallel) {
+  base::HistogramTester histogram_tester;
+
+  constexpr char kBiddingScript[] = R"(
+function generateBid(
+  interestGroup, auctionSignals, perBuyerSignals, trustedBiddingSignals,
+  browserSignals) {
+  return {'ad': 'example', 'bid': 1, 'render': 'https://example.com/render'};
+}
+function reportWin() {}
+)";
+
+  constexpr char kDecisionScript[] = R"(
+function scoreAd(
+  adMetadata, bid, auctionConfig, trustedScoringSignals, browserSignals) {
+  return bid;
+}
+function reportResult() {}
+)";
+
+  network_responder_->RegisterScriptResponse(kBiddingUrlPath, kBiddingScript);
+  network_responder_->RegisterScriptResponse(kDecisionUrlPath, kDecisionScript);
+
+  blink::InterestGroup interest_group = CreateInterestGroup();
+  interest_group.expiry = base::Time::Now() + base::Days(10);
+  interest_group.bidding_url = kUrlA.Resolve(kBiddingUrlPath);
+  interest_group.ads.emplace();
+  blink::InterestGroup::Ad ad;
+  ad.render_url = GURL("https://example.com/render");
+  interest_group.ads->emplace_back(std::move(ad));
+  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->shareable_auction_ad_config =
+      blink::mojom::ShareableAuctionAdConfig::New();
+  succeed_auction_config->shareable_auction_ad_config->interest_group_buyers =
+      blink::mojom::InterestGroupBuyers::NewBuyers({kOriginA});
+
+  // Pick some large number, larger than the auction limit.
+  constexpr int kNumAuctions = 10;
+  base::RunLoop run_loop;
+  mojo::Remote<blink::mojom::AdAuctionService> interest_service;
+  AdAuctionServiceImpl::CreateMojoService(
+      main_rfh(), interest_service.BindNewPipeAndPassReceiver());
+  base::RepeatingClosure one_auction_complete =
+      base::BarrierClosure(kNumAuctions, run_loop.QuitClosure());
+
+  for (int i = 0; i < kNumAuctions; i++) {
+    interest_service->RunAdAuction(
+        succeed_auction_config->Clone(),
+        base::BindLambdaForTesting(
+            [&one_auction_complete](
+                const absl::optional<GURL>& ignored_result) {
+              one_auction_complete.Run();
+            }));
+  }
+  run_loop.Run();
+
+  // DeleteContents() to force-populate remaining metrics.
+  DeleteContents();
+
+  // Only the first 2 auctions should have succeeded -- the others should fail.
+  histogram_tester.ExpectUniqueSample(
+      "Ads.InterestGroup.Auction.NumAuctionsPerPage", 2, 1);
+  histogram_tester.ExpectUniqueSample(
+      "Ads.InterestGroup.Auction.PercentAuctionsSuccessfulPerPage", 2 * 100 / 2,
+      1);
+  histogram_tester.ExpectUniqueSample(
+      "Ads.InterestGroup.Auction.First6AuctionsBitsPerPage", 0b111, 1);
+  histogram_tester.ExpectUniqueSample(
+      "Ads.InterestGroup.Auction.NumAuctionsSkippedDueToAuctionLimit",
+      kNumAuctions - 2, 1);
 }
 
 class AdAuctionServiceImplRestrictedPermissionsPolicyTest
@@ -2638,7 +2987,7 @@
   blink::InterestGroup interest_group = CreateInterestGroup();
   interest_group.update_url = kUpdateUrlA;
   interest_group.bidding_url = kBiddingLogicUrlA;
-  JoinInterestGroupAndFlush(std::move(interest_group));
+  JoinInterestGroupAndFlush(interest_group);
   EXPECT_EQ(1, GetJoinCount(kOriginA, kInterestGroupName));
 
   UpdateInterestGroupNoFlush();
diff --git a/content/browser/interest_group/interest_group_browsertest.cc b/content/browser/interest_group/interest_group_browsertest.cc
index 91ca5936..dde56a2c 100644
--- a/content/browser/interest_group/interest_group_browsertest.cc
+++ b/content/browser/interest_group/interest_group_browsertest.cc
@@ -194,6 +194,22 @@
       GUARDED_BY(response_map_lock_);
 };
 
+class InterestGroupTestObserver
+    : public InterestGroupManager::InterestGroupObserverInterface {
+ public:
+  using Entry = std::tuple<
+      InterestGroupManager::InterestGroupObserverInterface::AccessType,
+      std::string,
+      std::string>;
+  void OnInterestGroupAccessed(
+      InterestGroupManager::InterestGroupObserverInterface::AccessType type,
+      const std::string& owner_origin,
+      const std::string& name) override {
+    accesses.emplace_back(Entry{type, owner_origin, name});
+  }
+  std::vector<Entry> accesses;
+};
+
 class InterestGroupBrowserTest : public ContentBrowserTest {
  public:
   InterestGroupBrowserTest() {
@@ -230,6 +246,8 @@
                                                ->GetBrowserContext()
                                                ->GetDefaultStoragePartition())
             ->GetInterestGroupManager();
+    observer_ = std::make_unique<InterestGroupTestObserver>();
+    manager_->AddInterestGroupObserver(observer_.get());
     content_browser_client_.SetAllowList(
         {url::Origin::Create(https_server_->GetURL("a.test", "/")),
          url::Origin::Create(https_server_->GetURL("b.test", "/")),
@@ -729,6 +747,11 @@
     return GURL(result.ExtractString());
   }
 
+  void ExpectAccessObserved(
+      const std::vector<InterestGroupTestObserver::Entry>& expected) {
+    EXPECT_EQ(expected, observer_->accesses);
+  }
+
   WebContentsImpl* web_contents() const {
     return static_cast<WebContentsImpl*>(shell()->web_contents());
   }
@@ -738,6 +761,7 @@
   base::test::ScopedFeatureList feature_list_;
   AllowlistedOriginContentBrowserClient content_browser_client_;
   raw_ptr<ContentBrowserClient> old_content_browser_client_;
+  std::unique_ptr<InterestGroupTestObserver> observer_;
   raw_ptr<InterestGroupManager> manager_;
   base::Lock requests_lock_;
   std::set<GURL> received_https_test_server_requests_
@@ -1230,6 +1254,12 @@
   received_groups = GetAllInterestGroups();
   EXPECT_THAT(received_groups,
               testing::UnorderedElementsAreArray(expected_groups));
+  ExpectAccessObserved(
+      {{InterestGroupTestObserver::kJoin, test_origin_a.Serialize(), "cars"},
+       {InterestGroupTestObserver::kJoin, test_origin_b.Serialize(), "trucks"},
+       {InterestGroupTestObserver::kJoin, test_origin_d.Serialize(), "candy"},
+       {InterestGroupTestObserver::kLeave, test_origin_b.Serialize(), "cars"},
+       {InterestGroupTestObserver::kLeave, test_origin_a.Serialize(), "cars"}});
 }
 
 IN_PROC_BROWSER_TEST_F(InterestGroupBrowserTest,
@@ -1241,6 +1271,7 @@
     decisionLogicUrl: 'https://test.com/decision_logic',
     interestGroupBuyers: '*',
   })"));
+  ExpectAccessObserved({});
 }
 
 IN_PROC_BROWSER_TEST_F(InterestGroupBrowserTest,
@@ -1265,6 +1296,7 @@
   }
   return 'done';
 })())"));
+  ExpectAccessObserved({});
 }
 
 IN_PROC_BROWSER_TEST_F(InterestGroupBrowserTest,
@@ -1292,6 +1324,7 @@
   }
   return 'done';
 })())"));
+  ExpectAccessObserved({});
 }
 
 IN_PROC_BROWSER_TEST_F(InterestGroupBrowserTest,
@@ -1322,6 +1355,7 @@
   return 'done';
 })())",
                                 origin_string.c_str())));
+  ExpectAccessObserved({});
 }
 
 IN_PROC_BROWSER_TEST_F(InterestGroupBrowserTest,
@@ -1383,6 +1417,7 @@
   return 'done';
 })())",
                                 origin_string.c_str())));
+  ExpectAccessObserved({});
 }
 
 IN_PROC_BROWSER_TEST_F(InterestGroupBrowserTest,
@@ -1413,6 +1448,7 @@
   return 'done';
 })())",
                                       origin_string.c_str())));
+  ExpectAccessObserved({});
 }
 
 IN_PROC_BROWSER_TEST_F(InterestGroupBrowserTest,
@@ -1443,6 +1479,7 @@
   return 'done';
 })())",
                                 origin_string.c_str())));
+  ExpectAccessObserved({});
 }
 
 IN_PROC_BROWSER_TEST_F(InterestGroupBrowserTest,
@@ -1473,6 +1510,7 @@
   return 'done';
 })())",
                                 origin_string.c_str())));
+  ExpectAccessObserved({});
 }
 
 IN_PROC_BROWSER_TEST_F(InterestGroupBrowserTest,
@@ -1507,6 +1545,7 @@
   return 'done';
 })())",
                                 origin_string.c_str())));
+  ExpectAccessObserved({});
 }
 
 IN_PROC_BROWSER_TEST_F(InterestGroupBrowserTest,
@@ -1531,6 +1570,7 @@
   }
   return 'done';
 })())"));
+  ExpectAccessObserved({});
 }
 
 IN_PROC_BROWSER_TEST_F(InterestGroupBrowserTest, RunAdAuctionInvalidSeller) {
@@ -1543,6 +1583,7 @@
       seller: 'https://invalid^&',
       decisionLogicUrl: 'https://test.com/decision_logic'
   })"));
+  ExpectAccessObserved({});
 }
 
 IN_PROC_BROWSER_TEST_F(InterestGroupBrowserTest, RunAdAuctionHttpSeller) {
@@ -1555,6 +1596,7 @@
       seller: 'http://test.com',
       decisionLogicUrl: 'https://test.com/decision_logic'
   })"));
+  ExpectAccessObserved({});
 }
 
 IN_PROC_BROWSER_TEST_F(InterestGroupBrowserTest,
@@ -1569,6 +1611,7 @@
       seller: 'https://test.com',
       decisionLogicUrl: 'https://invalid^&'
   })"));
+  ExpectAccessObserved({});
 }
 
 IN_PROC_BROWSER_TEST_F(InterestGroupBrowserTest,
@@ -1589,6 +1632,7 @@
       trustedScoringSignalsUrl: 'https://invalid^&'
   })",
                                   origin, url)));
+  ExpectAccessObserved({});
 }
 
 IN_PROC_BROWSER_TEST_F(InterestGroupBrowserTest,
@@ -1621,6 +1665,9 @@
                          test_origin,
                          https_server_->GetURL(
                              "b.test", "/interest_group/decision_logic.js"))));
+  ExpectAccessObserved({
+      {InterestGroupTestObserver::kJoin, test_origin.Serialize(), "cars"},
+  });
 }
 
 IN_PROC_BROWSER_TEST_F(InterestGroupBrowserTest,
@@ -1636,6 +1683,7 @@
       decisionLogicUrl: 'https://test.com',
       interestGroupBuyers: ['https://invalid^&'],
   })"));
+  ExpectAccessObserved({});
 }
 
 IN_PROC_BROWSER_TEST_F(InterestGroupBrowserTest,
@@ -1652,6 +1700,7 @@
       decisionLogicUrl: 'https://test.com',
       interestGroupBuyers: 'not star',
   })"));
+  ExpectAccessObserved({});
 }
 
 IN_PROC_BROWSER_TEST_F(InterestGroupBrowserTest,
@@ -1662,6 +1711,7 @@
       seller: 'https://test.com',
       decisionLogicUrl: 'https://test.com',
   })"));
+  ExpectAccessObserved({});
 }
 
 IN_PROC_BROWSER_TEST_F(InterestGroupBrowserTest,
@@ -1673,6 +1723,7 @@
       decisionLogicUrl: 'https://test.com',
       interestGroupBuyers: [],
   })"));
+  ExpectAccessObserved({});
 }
 
 IN_PROC_BROWSER_TEST_F(InterestGroupBrowserTest,
@@ -1688,6 +1739,7 @@
       decisionLogicUrl: 'https://test.com',
       auctionSignals: alert
   })"));
+  ExpectAccessObserved({});
 }
 
 IN_PROC_BROWSER_TEST_F(InterestGroupBrowserTest,
@@ -1703,6 +1755,7 @@
       decisionLogicUrl: 'https://test.com',
       sellerSignals: function() {}
   })"));
+  ExpectAccessObserved({});
 }
 
 IN_PROC_BROWSER_TEST_F(InterestGroupBrowserTest,
@@ -1718,6 +1771,7 @@
       decisionLogicUrl: 'https://test.com',
       perBuyerSignals: {'https://invalid^&': {a:1}}
   })"));
+  ExpectAccessObserved({});
 }
 
 IN_PROC_BROWSER_TEST_F(InterestGroupBrowserTest,
@@ -1751,6 +1805,9 @@
                          test_origin,
                          https_server_->GetURL(
                              "a.test", "/interest_group/decision_logic.js"))));
+  ExpectAccessObserved({
+      {InterestGroupTestObserver::kJoin, test_origin.Serialize(), "cars"},
+  });
 }
 
 IN_PROC_BROWSER_TEST_F(InterestGroupBrowserTest,
@@ -1766,6 +1823,7 @@
       decisionLogicUrl: 'https://test.com',
       perBuyerSignals: {'https://test.com': function() {}}
   })"));
+  ExpectAccessObserved({});
 }
 
 IN_PROC_BROWSER_TEST_F(InterestGroupBrowserTest,
@@ -1782,6 +1840,7 @@
                          url::Origin::Create(test_url),
                          https_server_->GetURL(
                              "a.test", "/interest_group/decision_logic.js"))));
+  ExpectAccessObserved({});
 }
 
 IN_PROC_BROWSER_TEST_F(InterestGroupBrowserTest,
@@ -1837,6 +1896,9 @@
   EXPECT_FALSE(base::Contains(
       received_https_test_server_requests_,
       https_server_->GetURL("/interest_group/decision_logic.js")));
+  ExpectAccessObserved({
+      {InterestGroupTestObserver::kJoin, test_origin_a.Serialize(), "cars"},
+  });
 }
 
 IN_PROC_BROWSER_TEST_F(InterestGroupBrowserTest,
@@ -1901,6 +1963,12 @@
       received_https_test_server_requests_,
       https_server_->GetURL(
           "/interest_group/bidding_logic_stop_bidding_after_win.js")));
+  ExpectAccessObserved({
+      {InterestGroupTestObserver::kJoin, disabled_origin.Serialize(), "candy"},
+      {InterestGroupTestObserver::kJoin, test_origin.Serialize(), "cars"},
+      {InterestGroupTestObserver::kBid, test_origin.Serialize(), "cars"},
+      {InterestGroupTestObserver::kWin, test_origin.Serialize(), "cars"},
+  });
 }
 
 IN_PROC_BROWSER_TEST_F(InterestGroupBrowserTest, RunAdAuctionWithWinner) {
@@ -1941,6 +2009,12 @@
       https_server_->GetURL("a.test", "/interest_group/decision_logic.js"));
   RunAuctionAndWaitForURLAndNavigateIframe(auction_config, ad_url);
 
+  ExpectAccessObserved({
+      {InterestGroupTestObserver::kJoin, test_origin.Serialize(), "cars"},
+      {InterestGroupTestObserver::kBid, test_origin.Serialize(), "cars"},
+      {InterestGroupTestObserver::kWin, test_origin.Serialize(), "cars"},
+  });
+
   // Check ResourceRequest structs of requests issued by the worklet process.
   const struct ExpectedRequest {
     GURL url;
@@ -2108,6 +2182,12 @@
                   https_server_->GetURL("a.test",
                                         "/interest_group/decision_logic.js"))));
 
+  ExpectAccessObserved({
+      {InterestGroupTestObserver::kJoin, test_origin.Serialize(), "cars"},
+      {InterestGroupTestObserver::kBid, test_origin.Serialize(), "cars"},
+      {InterestGroupTestObserver::kWin, test_origin.Serialize(), "cars"},
+  });
+
   // Check ResourceRequest structs of requests issued by the worklet process.
   const struct ExpectedRequest {
     GURL url;
@@ -2271,6 +2351,12 @@
                     kSeller, "/interest_group/trusted_scoring_signals.json"),
                 bidder_origin)));
 
+  ExpectAccessObserved({
+      {InterestGroupTestObserver::kJoin, bidder_origin.Serialize(), "cars"},
+      {InterestGroupTestObserver::kBid, bidder_origin.Serialize(), "cars"},
+      {InterestGroupTestObserver::kWin, bidder_origin.Serialize(), "cars"},
+  });
+
   // Reporting urls should be fetched after an auction succeeded.
   WaitForURL(https_server_->GetURL("/echoall?report_seller"));
   WaitForURL(https_server_->GetURL("/echoall?report_bidder"));
@@ -2322,6 +2408,12 @@
       https_server_->GetURL("a.test", "/interest_group/decision_logic.js"));
   RunAuctionAndWaitForURLAndNavigateIframe(auction_config, ad_url);
 
+  ExpectAccessObserved({
+      {InterestGroupTestObserver::kJoin, test_origin.Serialize(), "cars"},
+      {InterestGroupTestObserver::kBid, test_origin.Serialize(), "cars"},
+      {InterestGroupTestObserver::kWin, test_origin.Serialize(), "cars"},
+  });
+
   // Wait for the component to load.
   WaitForURL(component_url);
 }
@@ -2694,6 +2786,17 @@
             1);
   EXPECT_EQ(storage_interest_groups2.front().bidding_browser_signals->bid_count,
             3);
+  ExpectAccessObserved({
+      {InterestGroupTestObserver::kJoin, origin.Serialize(), "cars"},
+      {InterestGroupTestObserver::kJoin, origin2.Serialize(), "shoes"},
+      {InterestGroupTestObserver::kBid, origin2.Serialize(), "shoes"},
+      {InterestGroupTestObserver::kBid, origin.Serialize(), "cars"},
+      {InterestGroupTestObserver::kWin, origin.Serialize(), "cars"},
+      {InterestGroupTestObserver::kBid, origin2.Serialize(), "shoes"},
+      {InterestGroupTestObserver::kWin, origin2.Serialize(), "shoes"},
+      {InterestGroupTestObserver::kBid, origin2.Serialize(), "shoes"},
+      {InterestGroupTestObserver::kWin, origin2.Serialize(), "shoes"},
+  });
 }
 
 // Adding an interest group and then immediately running the ad acution, without
@@ -3550,6 +3653,10 @@
                        "/new_ad_render_url" &&
                    group.ads.value()[0].metadata == "{\"new_a\":\"b\"}";
           }));
+  ExpectAccessObserved({
+      {InterestGroupTestObserver::kJoin, test_origin.Serialize(), "cars"},
+      {InterestGroupTestObserver::kUpdate, test_origin.Serialize(), "cars"},
+  });
 }
 
 // Updates can proceed even if the page that started the update isn't running
@@ -4777,6 +4884,171 @@
   EXPECT_EQ(absl::nullopt, ConvertFencedFrameURNToURLInJS(invalid_urn));
 }
 
+class InterestGroupAuctionLimitBrowserTest : public InterestGroupBrowserTest {
+ public:
+  InterestGroupAuctionLimitBrowserTest() {
+    // Only 2 auctions are allowed per-page.
+    feature_list_.InitWithFeaturesAndParameters(
+        /*enabled_features=*/
+        {{features::kFledgeLimitNumAuctions, {{"max_auctions_per_page", "2"}}},
+         {blink::features::kAdInterestGroupAPIRestrictedPolicyByDefault, {}}},
+        /*disabled_features=*/{});
+    // TODO(crbug.com/1186444): When
+    // kAdInterestGroupAPIRestrictedPolicyByDefault is the default, we won't
+    // need to set it here.
+  }
+
+ protected:
+  base::test::ScopedFeatureList feature_list_;
+};
+
+// Perform an auction, navigate the top-level frame, then navigate it back.
+// Perform 2 more auctions. The second of those two should fail, because 2
+// auctions have already been performed on the page -- one before the top level
+// bfcached navigations, and one after.
+//
+// That is, the auction limit count is preserved due to bfcache.
+IN_PROC_BROWSER_TEST_F(InterestGroupAuctionLimitBrowserTest,
+                       NavigatingWithBfcachePreservesAuctionLimits) {
+  const GURL test_url = https_server_->GetURL("a.test", "/echo");
+  ASSERT_TRUE(NavigateToURL(shell(), test_url));
+  const url::Origin test_origin = url::Origin::Create(test_url);
+
+  EXPECT_TRUE(JoinInterestGroupAndWaitInJs(blink::InterestGroup(
+      /*expiry=*/base::Time(),
+      /*owner=*/test_origin,
+      /*name=*/"cars",
+      /*bidding_url=*/
+      https_server_->GetURL("a.test", "/interest_group/bidding_logic.js"),
+      /*bidding_wasm_helper_url=*/absl::nullopt,
+      /*update_url=*/absl::nullopt,
+      /*trusted_bidding_signals_url=*/absl::nullopt,
+      /*trusted_bidding_signals_keys=*/absl::nullopt,
+      /*user_bidding_signals=*/absl::nullopt,
+      /*ads=*/
+      {{{GURL("https://example.com/render"),
+         "{ad:'metadata', here : [1,2] }"}}},
+      /*ad_components=*/absl::nullopt)));
+
+  // 1st auction -- before navigations
+  EXPECT_EQ("https://example.com/render",
+            RunAuctionAndWaitForURL(JsReplace(
+                R"({
+    seller: $1,
+    decisionLogicUrl: $2,
+    interestGroupBuyers: [$1],
+                })",
+                test_origin,
+                https_server_->GetURL("a.test",
+                                      "/interest_group/decision_logic.js"))));
+
+  // Navigate, then navigate back. The auction limits shouldn't be reset since
+  // the original page goes into the bfcache.
+  const GURL test_url_b = https_server_->GetURL("b.test", "/echo");
+  ASSERT_TRUE(NavigateToURL(shell(), test_url_b));
+  TestNavigationObserver back_load_observer(shell()->web_contents());
+  shell()->web_contents()->GetController().GoBack();
+  back_load_observer.Wait();
+
+  // 2nd auction -- after navigations
+  EXPECT_EQ("https://example.com/render",
+            RunAuctionAndWaitForURL(JsReplace(
+                R"({
+    seller: $1,
+    decisionLogicUrl: $2,
+    interestGroupBuyers: [$1],
+                })",
+                test_origin,
+                https_server_->GetURL("a.test",
+                                      "/interest_group/decision_logic.js"))));
+
+  // 3rd auction -- after navigations; should fail due to hitting the auction
+  // limit.
+  EXPECT_EQ(nullptr, RunAuctionAndWait(JsReplace(
+                         R"({
+    seller: $1,
+    decisionLogicUrl: $2,
+    interestGroupBuyers: [$1],
+                })",
+                         test_origin,
+                         https_server_->GetURL(
+                             "a.test", "/interest_group/decision_logic.js"))));
+}
+
+// Create a page with a cross-origin iframe. Run an auction in the main frame,
+// then run 2 auctions in the cross-origin iframe. The last auction should fail
+// due to encontering the auction limit, since the limit is stored per-page (top
+// level frame), not per frame.
+IN_PROC_BROWSER_TEST_F(InterestGroupAuctionLimitBrowserTest,
+                       AuctionLimitSharedWithCrossOriginFrameOnPage) {
+  // Give the cross-origin iframe permission to run auctions.
+  const GURL test_url =
+      https_server_->GetURL("a.test",
+                            "/cross_site_iframe_factory.html?a.test(b.test{"
+                            "allow-run-ad-auction})");
+  ASSERT_TRUE(NavigateToURL(shell(), test_url));
+  const url::Origin test_origin = url::Origin::Create(test_url);
+  RenderFrameHost* const b_iframe =
+      ChildFrameAt(web_contents()->GetMainFrame(), 0);
+
+  EXPECT_TRUE(JoinInterestGroupAndWaitInJs(blink::InterestGroup(
+      /*expiry=*/base::Time(),
+      /*owner=*/test_origin,
+      /*name=*/"cars",
+      /*bidding_url=*/
+      https_server_->GetURL("a.test", "/interest_group/bidding_logic.js"),
+      /*bidding_wasm_helper_url=*/absl::nullopt,
+      /*update_url=*/absl::nullopt,
+      /*trusted_bidding_signals_url=*/absl::nullopt,
+      /*trusted_bidding_signals_keys=*/absl::nullopt,
+      /*user_bidding_signals=*/absl::nullopt,
+      /*ads=*/
+      {{{GURL("https://example.com/render"),
+         "{ad:'metadata', here : [1,2] }"}}},
+      /*ad_components=*/absl::nullopt)));
+
+  // 1st auction -- in main frame
+  EXPECT_EQ("https://example.com/render",
+            RunAuctionAndWaitForURL(JsReplace(
+                R"({
+    seller: $1,
+    decisionLogicUrl: $2,
+    interestGroupBuyers: [$1],
+                })",
+                test_origin,
+                https_server_->GetURL("a.test",
+                                      "/interest_group/decision_logic.js"))));
+
+  // 2nd auction -- in cross-origin iframe
+  EXPECT_EQ("https://example.com/render",
+            RunAuctionAndWaitForURL(
+                JsReplace(
+                    R"({
+    seller: $1,
+    decisionLogicUrl: $2,
+    interestGroupBuyers: [$1],
+                })",
+                    test_origin,
+                    https_server_->GetURL("a.test",
+                                          "/interest_group/decision_logic.js")),
+                b_iframe));
+
+  // 3rd auction -- in cross-origin iframe; should fail due to hitting the
+  // auction limit.
+  EXPECT_EQ(
+      nullptr,
+      RunAuctionAndWait(JsReplace(
+                            R"({
+    seller: $1,
+    decisionLogicUrl: $2,
+    interestGroupBuyers: [$1],
+                })",
+                            test_origin,
+                            https_server_->GetURL(
+                                "a.test", "/interest_group/decision_logic.js")),
+                        b_iframe));
+}
+
 }  // namespace
 
 }  // namespace content
diff --git a/content/browser/interest_group/interest_group_manager.cc b/content/browser/interest_group/interest_group_manager.cc
index ed55186..2f8e7f70 100644
--- a/content/browser/interest_group/interest_group_manager.cc
+++ b/content/browser/interest_group/interest_group_manager.cc
@@ -81,17 +81,23 @@
 
 void InterestGroupManager::JoinInterestGroup(blink::InterestGroup group,
                                              const GURL& joining_url) {
+  NotifyInterestGroupAccessed(InterestGroupObserverInterface::kJoin,
+                              group.owner.Serialize(), group.name);
   impl_.AsyncCall(&InterestGroupStorage::JoinInterestGroup)
       .WithArgs(std::move(group), std::move(joining_url));
 }
 
 void InterestGroupManager::LeaveInterestGroup(const ::url::Origin& owner,
                                               const std::string& name) {
+  NotifyInterestGroupAccessed(InterestGroupObserverInterface::kLeave,
+                              owner.Serialize(), name);
   impl_.AsyncCall(&InterestGroupStorage::LeaveInterestGroup)
       .WithArgs(owner, name);
 }
 
 void InterestGroupManager::UpdateInterestGroup(blink::InterestGroup group) {
+  NotifyInterestGroupAccessed(InterestGroupObserverInterface::kUpdate,
+                              group.owner.Serialize(), group.name);
   impl_.AsyncCall(&InterestGroupStorage::UpdateInterestGroup)
       .WithArgs(std::move(group));
 }
@@ -108,6 +114,8 @@
 
 void InterestGroupManager::RecordInterestGroupBid(const ::url::Origin& owner,
                                                   const std::string& name) {
+  NotifyInterestGroupAccessed(InterestGroupObserverInterface::kBid,
+                              owner.Serialize(), name);
   impl_.AsyncCall(&InterestGroupStorage::RecordInterestGroupBid)
       .WithArgs(owner, name);
 }
@@ -115,10 +123,21 @@
 void InterestGroupManager::RecordInterestGroupWin(const ::url::Origin& owner,
                                                   const std::string& name,
                                                   const std::string& ad_json) {
+  NotifyInterestGroupAccessed(InterestGroupObserverInterface::kWin,
+                              owner.Serialize(), name);
   impl_.AsyncCall(&InterestGroupStorage::RecordInterestGroupWin)
       .WithArgs(owner, name, std::move(ad_json));
 }
 
+void InterestGroupManager::GetInterestGroup(
+    const url::Origin& owner,
+    const std::string& name,
+    base::OnceCallback<void(absl::optional<StorageInterestGroup>)> callback) {
+  impl_.AsyncCall(&InterestGroupStorage::GetInterestGroup)
+      .WithArgs(owner, name)
+      .Then(std::move(callback));
+}
+
 void InterestGroupManager::GetAllInterestGroupOwners(
     base::OnceCallback<void(std::vector<url::Origin>)> callback) {
   impl_.AsyncCall(&InterestGroupStorage::GetAllInterestGroupOwners)
@@ -353,4 +372,13 @@
       .WithArgs(owner, name, net_disconnected);
 }
 
+void InterestGroupManager::NotifyInterestGroupAccessed(
+    InterestGroupObserverInterface::AccessType type,
+    const std::string& owner_origin,
+    const std::string& name) {
+  for (InterestGroupObserverInterface& observer : observers_) {
+    observer.OnInterestGroupAccessed(type, owner_origin, name);
+  }
+}
+
 }  // namespace content
diff --git a/content/browser/interest_group/interest_group_manager.h b/content/browser/interest_group/interest_group_manager.h
index 286e867..fb21541b 100644
--- a/content/browser/interest_group/interest_group_manager.h
+++ b/content/browser/interest_group/interest_group_manager.h
@@ -61,6 +61,15 @@
   InterestGroupManager(const InterestGroupManager& other) = delete;
   InterestGroupManager& operator=(const InterestGroupManager& other) = delete;
 
+  class CONTENT_EXPORT InterestGroupObserverInterface
+      : public base::CheckedObserver {
+   public:
+    enum AccessType { kJoin, kLeave, kUpdate, kBid, kWin };
+    virtual void OnInterestGroupAccessed(AccessType type,
+                                         const std::string& owner_origin,
+                                         const std::string& name) = 0;
+  };
+
   /******** Proxy function calls to InterestGroupsStorage **********/
 
   // Joins an interest group. If the interest group does not exist, a new one
@@ -89,6 +98,11 @@
   void RecordInterestGroupWin(const ::url::Origin& owner,
                               const std::string& name,
                               const std::string& ad_json);
+  // Gets a single interest group.
+  void GetInterestGroup(
+      const url::Origin& owner,
+      const std::string& name,
+      base::OnceCallback<void(absl::optional<StorageInterestGroup>)> callback);
   // Gets a list of all interest group owners. Each owner will only appear
   // once.
   void GetAllInterestGroupOwners(
@@ -122,6 +136,14 @@
     return *auction_process_manager_;
   }
 
+  void AddInterestGroupObserver(InterestGroupObserverInterface* observer) {
+    observers_.AddObserver(observer);
+  }
+
+  void RemoveInterestGroupObserver(InterestGroupObserverInterface* observer) {
+    observers_.RemoveObserver(observer);
+  }
+
   // Allows the AuctionProcessManager to be overridden in unit tests, both to
   // allow not creating a new process, and mocking out the Mojo service
   // interface.
@@ -149,6 +171,10 @@
   void ReportUpdateFetchFailed(const url::Origin& owner,
                                const std::string& name,
                                bool net_disconnected);
+  void NotifyInterestGroupAccessed(
+      InterestGroupObserverInterface::AccessType type,
+      const std::string& owner_origin,
+      const std::string& name);
 
   // Owns and manages access to the InterestGroupStorage living on a different
   // thread.
@@ -164,6 +190,8 @@
   // destroyed.
   UrlLoadersList url_loaders_;
 
+  base::ObserverList<InterestGroupObserverInterface> observers_;
+
   // TODO(crbug.com/1186444): Do we need to test InterestGroupManager
   // destruction during update? If so, how?
   base::WeakPtrFactory<InterestGroupManager> weak_factory_{this};
diff --git a/content/browser/interest_group/interest_group_storage.cc b/content/browser/interest_group/interest_group_storage.cc
index e29beb10..29be9303 100644
--- a/content/browser/interest_group/interest_group_storage.cc
+++ b/content/browser/interest_group/interest_group_storage.cc
@@ -542,11 +542,13 @@
 bool DoLoadInterestGroup(sql::Database& db,
                          const url::Origin& owner,
                          const std::string& name,
-                         blink::InterestGroup& group) {
+                         blink::InterestGroup& group,
+                         url::Origin* joining_origin) {
   // clang-format off
   sql::Statement load(
       db.GetCachedStatement(SQL_FROM_HERE,
         "SELECT expiration,"
+          "joining_origin,"
           "bidding_url,"
           "bidding_wasm_helper_url,"
           "update_url,"
@@ -572,16 +574,18 @@
   group.expiry = load.ColumnTime(0);
   group.owner = owner;
   group.name = name;
-  group.bidding_url = DeserializeURL(load.ColumnString(1));
-  group.bidding_wasm_helper_url = DeserializeURL(load.ColumnString(2));
-  group.update_url = DeserializeURL(load.ColumnString(3));
-  group.trusted_bidding_signals_url = DeserializeURL(load.ColumnString(4));
+  if (joining_origin)
+    *joining_origin = DeserializeOrigin(load.ColumnString(1));
+  group.bidding_url = DeserializeURL(load.ColumnString(2));
+  group.bidding_wasm_helper_url = DeserializeURL(load.ColumnString(3));
+  group.update_url = DeserializeURL(load.ColumnString(4));
+  group.trusted_bidding_signals_url = DeserializeURL(load.ColumnString(5));
   group.trusted_bidding_signals_keys =
-      DeserializeStringVector(load.ColumnString(5));
-  if (load.GetColumnType(6) != sql::ColumnType::kNull)
-    group.user_bidding_signals = load.ColumnString(6);
-  group.ads = DeserializeInterestGroupAdVector(load.ColumnString(7));
-  group.ad_components = DeserializeInterestGroupAdVector(load.ColumnString(8));
+      DeserializeStringVector(load.ColumnString(6));
+  if (load.GetColumnType(7) != sql::ColumnType::kNull)
+    group.user_bidding_signals = load.ColumnString(7);
+  group.ads = DeserializeInterestGroupAdVector(load.ColumnString(8));
+  group.ad_components = DeserializeInterestGroupAdVector(load.ColumnString(9));
 
   return true;
 }
@@ -639,8 +643,10 @@
   // verify the interest group is valid before writing it to the database.
 
   blink::InterestGroup stored_group;
-  if (!DoLoadInterestGroup(db, update.owner, update.name, stored_group))
+  if (!DoLoadInterestGroup(db, update.owner, update.name, stored_group,
+                           nullptr)) {
     return false;
+  }
 
   // (Optimization) Don't do anything for expired interest groups.
   if (stored_group.expiry < now)
@@ -1103,8 +1109,10 @@
     std::string name,
     base::Time now) {
   StorageInterestGroup db_interest_group;
-  if (!DoLoadInterestGroup(db, owner, name, db_interest_group.interest_group))
+  if (!DoLoadInterestGroup(db, owner, name, db_interest_group.interest_group,
+                           &db_interest_group.joining_origin)) {
     return absl::nullopt;
+  }
 
   if (!DoGetInterestGroupNameKAnonymity(db, owner,
                                         db_interest_group.interest_group.name,
@@ -1707,6 +1715,16 @@
   }
 }
 
+absl::optional<StorageInterestGroup> InterestGroupStorage::GetInterestGroup(
+    const url::Origin& owner,
+    const std::string& name) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  if (!EnsureDBInitialized())
+    return absl::nullopt;
+
+  return DoGetStoredInterestGroup(*db_, owner, name, base::Time::Now());
+}
+
 std::vector<url::Origin> InterestGroupStorage::GetAllInterestGroupOwners() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   if (!EnsureDBInitialized())
diff --git a/content/browser/interest_group/interest_group_storage.h b/content/browser/interest_group/interest_group_storage.h
index edc90466..4068b5a 100644
--- a/content/browser/interest_group/interest_group_storage.h
+++ b/content/browser/interest_group/interest_group_storage.h
@@ -92,6 +92,10 @@
   // Records the K-anonymity data for an ad.
   void UpdateAdKAnonymity(const StorageInterestGroup::KAnonymityData& data,
                           const absl::optional<base::Time>& update_sent_time);
+  // Gets a single interest group.
+  absl::optional<StorageInterestGroup> GetInterestGroup(
+      const url::Origin& owner,
+      const std::string& name);
   // Gets a list of all interest group owners. Each owner will only appear
   // once.
   std::vector<url::Origin> GetAllInterestGroupOwners();
diff --git a/content/browser/interest_group/storage_interest_group.h b/content/browser/interest_group/storage_interest_group.h
index 79182a3..2dd3299 100644
--- a/content/browser/interest_group/storage_interest_group.h
+++ b/content/browser/interest_group/storage_interest_group.h
@@ -14,6 +14,7 @@
 #include "mojo/public/cpp/bindings/struct_ptr.h"
 #include "third_party/blink/public/common/interest_group/interest_group.h"
 #include "url/gurl.h"
+#include "url/origin.h"
 
 namespace content {
 
@@ -52,6 +53,8 @@
   absl::optional<KAnonymityData> name_kanon;
   absl::optional<KAnonymityData> update_url_kanon;
   std::vector<KAnonymityData> ads_kanon;
+  // Top level page origin from when the interest group was joined.
+  url::Origin joining_origin;
 };
 
 // Stream operator so KAnonymityData can be used in assertion statements.
diff --git a/content/browser/loader/cached_navigation_url_loader.cc b/content/browser/loader/cached_navigation_url_loader.cc
index 60650b6f..06a0a7c 100644
--- a/content/browser/loader/cached_navigation_url_loader.cc
+++ b/content/browser/loader/cached_navigation_url_loader.cc
@@ -84,8 +84,7 @@
 void CachedNavigationURLLoader::FollowRedirect(
     const std::vector<std::string>& removed_headers,
     const net::HttpRequestHeaders& modified_headers,
-    const net::HttpRequestHeaders& modified_cors_exempt_headers,
-    blink::PreviewsState new_previews_state) {
+    const net::HttpRequestHeaders& modified_cors_exempt_headers) {
   NOTREACHED();
 }
 
diff --git a/content/browser/loader/cached_navigation_url_loader.h b/content/browser/loader/cached_navigation_url_loader.h
index c8ad533..ec3e4b3a 100644
--- a/content/browser/loader/cached_navigation_url_loader.h
+++ b/content/browser/loader/cached_navigation_url_loader.h
@@ -35,8 +35,7 @@
   void FollowRedirect(
       const std::vector<std::string>& removed_headers,
       const net::HttpRequestHeaders& modified_headers,
-      const net::HttpRequestHeaders& modified_cors_exempt_headers,
-      blink::PreviewsState new_previews_state) override;
+      const net::HttpRequestHeaders& modified_cors_exempt_headers) override;
   bool SetNavigationTimeout(base::TimeDelta timeout) override;
 
  private:
diff --git a/content/browser/loader/loader_browsertest.cc b/content/browser/loader/loader_browsertest.cc
index 84a3e4676..5464a1d 100644
--- a/content/browser/loader/loader_browsertest.cc
+++ b/content/browser/loader/loader_browsertest.cc
@@ -49,7 +49,6 @@
 #include "services/network/public/cpp/features.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "third_party/blink/public/common/chrome_debug_urls.h"
-#include "third_party/blink/public/common/loader/previews_state.h"
 #include "third_party/blink/public/common/loader/url_loader_throttle.h"
 #include "url/gurl.h"
 
diff --git a/content/browser/loader/navigation_url_loader.h b/content/browser/loader/navigation_url_loader.h
index 54333d7..0603850c 100644
--- a/content/browser/loader/navigation_url_loader.h
+++ b/content/browser/loader/navigation_url_loader.h
@@ -12,7 +12,6 @@
 #include "content/browser/loader/navigation_loader_interceptor.h"
 #include "content/common/content_export.h"
 #include "services/network/public/mojom/devtools_observer.mojom-forward.h"
-#include "third_party/blink/public/common/loader/previews_state.h"
 
 namespace net {
 class HttpRequestHeaders;
@@ -94,14 +93,11 @@
   virtual void Start() = 0;
 
   // Called in response to OnRequestRedirected to continue processing the
-  // request. `new_previews_state` will be updated for newly created URLLoaders,
-  // but the existing default URLLoader will not see `new_previews_state` unless
-  // the URLLoader happens to be reset.
+  // request.
   virtual void FollowRedirect(
       const std::vector<std::string>& removed_headers,
       const net::HttpRequestHeaders& modified_headers,
-      const net::HttpRequestHeaders& modified_cors_exempt_headers,
-      blink::PreviewsState new_previews_state) = 0;
+      const net::HttpRequestHeaders& modified_cors_exempt_headers) = 0;
 
   // Sets an overall request timeout for this navigation, which will cause the
   // navigation to fail if it expires before the navigation commits. This is
diff --git a/content/browser/loader/navigation_url_loader_impl.cc b/content/browser/loader/navigation_url_loader_impl.cc
index 475e7f0..c1235f9 100644
--- a/content/browser/loader/navigation_url_loader_impl.cc
+++ b/content/browser/loader/navigation_url_loader_impl.cc
@@ -285,7 +285,6 @@
   new_request->upgrade_if_insecure = request_info.upgrade_if_insecure;
   new_request->throttling_profile_id = request_info.devtools_frame_token;
   new_request->transition_type = request_info.common_params->transition;
-  new_request->previews_state = request_info.common_params->previews_state;
   new_request->devtools_request_id =
       request_info.devtools_navigation_token.ToString();
   new_request->obey_origin_policy = request_info.obey_origin_policy;
@@ -1376,8 +1375,7 @@
 void NavigationURLLoaderImpl::FollowRedirect(
     const std::vector<std::string>& removed_headers,
     const net::HttpRequestHeaders& modified_headers,
-    const net::HttpRequestHeaders& modified_cors_exempt_headers,
-    blink::PreviewsState new_previews_state) {
+    const net::HttpRequestHeaders& modified_cors_exempt_headers) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!redirect_info_.new_url.is_empty());
 
@@ -1411,7 +1409,6 @@
 
   resource_request_->referrer = GURL(redirect_info_.new_referrer);
   resource_request_->referrer_policy = redirect_info_.new_referrer_policy;
-  resource_request_->previews_state = new_previews_state;
   resource_request_->navigation_redirect_chain.push_back(
       redirect_info_.new_url);
   url_chain_.push_back(redirect_info_.new_url);
diff --git a/content/browser/loader/navigation_url_loader_impl.h b/content/browser/loader/navigation_url_loader_impl.h
index 3d75a59e..5babdc6 100644
--- a/content/browser/loader/navigation_url_loader_impl.h
+++ b/content/browser/loader/navigation_url_loader_impl.h
@@ -24,7 +24,6 @@
 #include "services/network/public/mojom/url_loader_factory.mojom.h"
 #include "services/network/public/mojom/url_response_head.mojom.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
-#include "third_party/blink/public/common/loader/previews_state.h"
 #include "third_party/blink/public/common/navigation/navigation_policy.h"
 
 namespace net {
@@ -211,8 +210,7 @@
   void FollowRedirect(
       const std::vector<std::string>& removed_headers,
       const net::HttpRequestHeaders& modified_headers,
-      const net::HttpRequestHeaders& modified_cors_exempt_headers,
-      blink::PreviewsState new_previews_state) override;
+      const net::HttpRequestHeaders& modified_cors_exempt_headers) override;
   bool SetNavigationTimeout(base::TimeDelta timeout) override;
 
   raw_ptr<NavigationURLLoaderDelegate> delegate_;
diff --git a/content/browser/loader/navigation_url_loader_impl_unittest.cc b/content/browser/loader/navigation_url_loader_impl_unittest.cc
index 01434ad..5624a678 100644
--- a/content/browser/loader/navigation_url_loader_impl_unittest.cc
+++ b/content/browser/loader/navigation_url_loader_impl_unittest.cc
@@ -266,7 +266,7 @@
         request_method, &delegate);
     loader->Start();
     delegate.WaitForRequestRedirected();
-    loader->FollowRedirect({}, {}, {}, blink::PreviewsTypes::PREVIEWS_OFF);
+    loader->FollowRedirect({}, {}, {});
 
     EXPECT_EQ(expected_redirect_method, delegate.redirect_info().new_method);
 
@@ -306,7 +306,8 @@
         true /*is_main_frame*/, upgrade_if_insecure);
     loader->Start();
     delegate.WaitForRequestRedirected();
-    loader->FollowRedirect({}, {}, {}, blink::PreviewsTypes::PREVIEWS_OFF);
+    loader->FollowRedirect({}, {}, {});
+
     if (expect_request_fail) {
       delegate.WaitForRequestFailed();
     } else {
@@ -474,8 +475,7 @@
   net::HttpRequestHeaders redirect_headers;
   redirect_headers.SetHeader("Header2", "");
   redirect_headers.SetHeader("Header3", "Value3");
-  loader->FollowRedirect({}, redirect_headers, {},
-                         blink::PreviewsTypes::PREVIEWS_OFF);
+  loader->FollowRedirect({}, redirect_headers, {});
   delegate.WaitForResponseStarted();
 
   // Redirected request should also have modified headers.
diff --git a/content/browser/navigation_browsertest.cc b/content/browser/navigation_browsertest.cc
index d541127..c85ddb6f 100644
--- a/content/browser/navigation_browsertest.cc
+++ b/content/browser/navigation_browsertest.cc
@@ -88,7 +88,6 @@
 #include "services/network/public/cpp/features.h"
 #include "services/network/public/cpp/web_sandbox_flags.h"
 #include "services/network/public/mojom/url_loader.mojom.h"
-#include "third_party/blink/public/common/loader/previews_state.h"
 #include "third_party/blink/public/common/loader/url_loader_throttle.h"
 #include "ui/base/page_transition_types.h"
 #include "url/gurl.h"
@@ -1559,113 +1558,6 @@
   EXPECT_FALSE(navigation.was_successful());
 }
 
-namespace {
-
-// Checks whether the given urls are requested, and that GetPreviewsState()
-// returns the appropriate value when the Previews are set.
-class PreviewsStateContentBrowserClient : public ContentBrowserClient {
- public:
-  explicit PreviewsStateContentBrowserClient(const GURL& main_frame_url)
-      : main_frame_url_(main_frame_url),
-        main_frame_url_seen_(false),
-        previews_state_(blink::PreviewsTypes::PREVIEWS_OFF),
-        determine_allowed_previews_called_(false),
-        determine_committed_previews_called_(false) {}
-
-  PreviewsStateContentBrowserClient(const PreviewsStateContentBrowserClient&) =
-      delete;
-  PreviewsStateContentBrowserClient& operator=(
-      const PreviewsStateContentBrowserClient&) = delete;
-
-  ~PreviewsStateContentBrowserClient() override {}
-
-  blink::PreviewsState DetermineAllowedPreviews(
-      blink::PreviewsState initial_state,
-      content::NavigationHandle* navigation_handle,
-      const GURL& current_navigation_url) override {
-    DCHECK_CURRENTLY_ON(BrowserThread::UI);
-
-    EXPECT_FALSE(determine_allowed_previews_called_);
-    determine_allowed_previews_called_ = true;
-    main_frame_url_seen_ = true;
-    EXPECT_EQ(main_frame_url_, current_navigation_url);
-    return previews_state_;
-  }
-
-  blink::PreviewsState DetermineCommittedPreviews(
-      blink::PreviewsState initial_state,
-      content::NavigationHandle* navigation_handle,
-      const net::HttpResponseHeaders* response_headers) override {
-    DCHECK_CURRENTLY_ON(BrowserThread::UI);
-    EXPECT_EQ(previews_state_, initial_state);
-    determine_committed_previews_called_ = true;
-    return initial_state;
-  }
-
-  void SetClient() {
-    DCHECK_CURRENTLY_ON(BrowserThread::UI);
-    content::SetBrowserClientForTesting(this);
-  }
-
-  void Reset(blink::PreviewsState previews_state) {
-    DCHECK_CURRENTLY_ON(BrowserThread::UI);
-    main_frame_url_seen_ = false;
-    previews_state_ = previews_state;
-    determine_allowed_previews_called_ = false;
-  }
-
-  void CheckResourcesRequested() {
-    DCHECK_CURRENTLY_ON(BrowserThread::UI);
-    EXPECT_TRUE(determine_allowed_previews_called_);
-    EXPECT_TRUE(determine_committed_previews_called_);
-    EXPECT_TRUE(main_frame_url_seen_);
-  }
-
- private:
-  const GURL main_frame_url_;
-
-  bool main_frame_url_seen_;
-  blink::PreviewsState previews_state_;
-  bool determine_allowed_previews_called_;
-  bool determine_committed_previews_called_;
-};
-
-}  // namespace
-
-class PreviewsStateBrowserTest : public ContentBrowserTest {
- public:
-  ~PreviewsStateBrowserTest() override {}
-
- protected:
-  void SetUpOnMainThread() override {
-    ContentBrowserTest::SetUpOnMainThread();
-
-    ASSERT_TRUE(embedded_test_server()->Start());
-
-    client_ = std::make_unique<PreviewsStateContentBrowserClient>(
-        embedded_test_server()->GetURL("/title1.html"));
-
-    client_->SetClient();
-  }
-
-  void Reset(blink::PreviewsState previews_state) {
-    client_->Reset(previews_state);
-  }
-
-  void CheckResourcesRequested() { client_->CheckResourcesRequested(); }
-
- private:
-  std::unique_ptr<PreviewsStateContentBrowserClient> client_;
-};
-
-// Test that navigating calls GetPreviewsState returning PREVIEWS_OFF.
-IN_PROC_BROWSER_TEST_F(PreviewsStateBrowserTest, ShouldEnablePreviewsOff) {
-  // Navigate with No Previews.
-  NavigateToURLBlockUntilNavigationsComplete(
-      shell(), embedded_test_server()->GetURL("/title1.html"), 1);
-  CheckResourcesRequested();
-}
-
 // Ensure the renderer process doesn't send too many IPC to the browser process
 // when history.pushState() and history.back() are called in a loop.
 // Failing to do so causes the browser to become unresponsive.
diff --git a/content/browser/renderer_host/dwrite_font_proxy_impl_win.cc b/content/browser/renderer_host/dwrite_font_proxy_impl_win.cc
index 335dffd..2a1e90c 100644
--- a/content/browser/renderer_host/dwrite_font_proxy_impl_win.cc
+++ b/content/browser/renderer_host/dwrite_font_proxy_impl_win.cc
@@ -137,6 +137,8 @@
   TRACE_EVENT0("dwrite,fonts", "FontProxyHost::OnFindFamily");
   UINT32 family_index = UINT32_MAX;
   if (collection_) {
+    SCOPED_UMA_HISTOGRAM_TIMER_MICROS(
+        "DirectWrite.Fonts.Content.FindFamilyTime");
     BOOL exists = FALSE;
     UINT32 index = UINT32_MAX;
     HRESULT hr = collection_->FindFamilyName(base::as_wcstr(family_name),
@@ -152,6 +154,8 @@
 void DWriteFontProxyImpl::GetFamilyCount(GetFamilyCountCallback callback) {
   InitializeDirectWrite();
   TRACE_EVENT0("dwrite,fonts", "FontProxyHost::OnGetFamilyCount");
+  SCOPED_UMA_HISTOGRAM_TIMER_MICROS(
+      "DirectWrite.Fonts.Content.GetFamilyCountTime");
   std::move(callback).Run(collection_ ? collection_->GetFontFamilyCount() : 0);
 }
 
@@ -164,6 +168,8 @@
   if (!collection_)
     return;
 
+  SCOPED_UMA_HISTOGRAM_TIMER_MICROS(
+      "DirectWrite.Fonts.Content.GetFamilyNamesTime");
   TRACE_EVENT0("dwrite,fonts", "FontProxyHost::DoGetFamilyNames");
 
   mswr::ComPtr<IDWriteFontFamily> family;
@@ -226,6 +232,8 @@
   if (!collection_)
     return;
 
+  SCOPED_UMA_HISTOGRAM_TIMER_MICROS(
+      "DirectWrite.Fonts.Content.GetFontFilesTime");
   mswr::ComPtr<IDWriteFontFamily> family;
   HRESULT hr = collection_->GetFontFamily(family_index, &family);
   if (FAILED(hr)) {
@@ -312,6 +320,8 @@
     }
   }
 
+  SCOPED_UMA_HISTOGRAM_TIMER_MICROS(
+      "DirectWrite.Fonts.Content.MapCharactersTime");
   mswr::ComPtr<IDWriteFont> mapped_font;
 
   mswr::ComPtr<IDWriteNumberSubstitution> number_substitution;
@@ -521,6 +531,8 @@
   if (!codepoint || !collection_ || !factory_)
     return;
 
+  SCOPED_UMA_HISTOGRAM_TIMER_MICROS(
+      "DirectWrite.Fonts.Content.FallbackFamilyAndStyleForCodepointTime");
   sk_sp<SkFontMgr> font_mgr(
       SkFontMgr_New_DirectWrite(factory_.Get(), collection_.Get()));
 
@@ -552,6 +564,7 @@
 void DWriteFontProxyImpl::InitializeDirectWrite() {
   if (direct_write_initialized_)
     return;
+  SCOPED_UMA_HISTOGRAM_TIMER_MICROS("DirectWrite.Fonts.Content.InitializeTime");
   direct_write_initialized_ = true;
 
   TRACE_EVENT0("dwrite,fonts", "DWriteFontProxyImpl::InitializeDirectWrite");
diff --git a/content/browser/renderer_host/navigation_controller_delegate.h b/content/browser/renderer_host/navigation_controller_delegate.h
index a156ad6..abaff1b 100644
--- a/content/browser/renderer_host/navigation_controller_delegate.h
+++ b/content/browser/renderer_host/navigation_controller_delegate.h
@@ -10,7 +10,6 @@
 #include "content/public/browser/invalidate_type.h"
 #include "content/public/browser/navigation_controller.h"
 #include "content/public/browser/navigation_details.h"
-#include "third_party/blink/public/common/loader/previews_state.h"
 
 namespace content {
 
diff --git a/content/browser/renderer_host/navigation_controller_impl.cc b/content/browser/renderer_host/navigation_controller_impl.cc
index 2265661..1c5b3dc 100644
--- a/content/browser/renderer_host/navigation_controller_impl.cc
+++ b/content/browser/renderer_host/navigation_controller_impl.cc
@@ -3498,17 +3498,6 @@
     return nullptr;
   }
 
-  // Determine if Previews should be used for the navigation.
-  blink::PreviewsState previews_state =
-      blink::PreviewsTypes::PREVIEWS_UNSPECIFIED;
-  if (!node->IsMainFrame()) {
-    // For subframes, use the state of the top-level frame.
-    previews_state = node->frame_tree()
-                         ->root()
-                         ->current_frame_host()
-                         ->last_navigation_previews_state();
-  }
-
   // This will be used to set the Navigation Timing API navigationStart
   // parameter for browser navigations in new tabs (intents, tabs opened through
   // "Open link in new tab"). If the navigation must wait on the current
@@ -3546,7 +3535,7 @@
                                       params.referrer.policy),
           params.transition_type, navigation_type, download_policy,
           should_replace_current_entry, params.base_url_for_data_url,
-          previews_state, navigation_start,
+          navigation_start,
           params.load_type == LOAD_TYPE_HTTP_POST ? "POST" : "GET",
           params.post_data, std::move(source_location),
           params.started_from_context_menu, has_user_gesture,
@@ -3684,17 +3673,6 @@
     return nullptr;
   }
 
-  // Determine if Previews should be used for the navigation.
-  blink::PreviewsState previews_state =
-      blink::PreviewsTypes::PREVIEWS_UNSPECIFIED;
-  if (!frame_tree_node->IsMainFrame()) {
-    // For subframes, use the state of the top-level frame.
-    previews_state = frame_tree_node->frame_tree()
-                         ->root()
-                         ->current_frame_host()
-                         ->last_navigation_previews_state();
-  }
-
   // This will be used to set the Navigation Timing API navigationStart
   // parameter for browser navigations in new tabs (intents, tabs opened through
   // "Open link in new tab"). If the navigation must wait on the current
@@ -3733,7 +3711,7 @@
       entry->ConstructCommonNavigationParams(
           *frame_entry, request_body, dest_url,
           blink::mojom::Referrer::New(dest_referrer.url, dest_referrer.policy),
-          navigation_type, previews_state, navigation_start,
+          navigation_type, navigation_start,
           base::TimeTicks() /* input_start */);
   common_params->is_history_navigation_in_new_child_frame =
       is_history_navigation_in_new_child_frame;
diff --git a/content/browser/renderer_host/navigation_controller_impl_unittest.cc b/content/browser/renderer_host/navigation_controller_impl_unittest.cc
index 550b88b..c3d8249 100644
--- a/content/browser/renderer_host/navigation_controller_impl_unittest.cc
+++ b/content/browser/renderer_host/navigation_controller_impl_unittest.cc
@@ -59,7 +59,6 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/common/frame/frame_policy.h"
-#include "third_party/blink/public/common/loader/previews_state.h"
 #include "third_party/blink/public/common/page_state/page_state.h"
 #include "third_party/blink/public/common/tokens/tokens.h"
 #include "third_party/blink/public/mojom/frame/frame_owner_properties.mojom.h"
@@ -260,13 +259,6 @@
     return navigation_request->common_params().url;
   }
 
-  blink::PreviewsState GetLastNavigationPreviewsState() {
-    NavigationRequest* navigation_request =
-        contents()->GetPrimaryFrameTree().root()->navigation_request();
-    CHECK(navigation_request);
-    return navigation_request->common_params().previews_state;
-  }
-
   TestRenderFrameHost* GetNavigatingRenderFrameHost() {
     return AreAllSitesIsolatedForTesting()
                ? contents()->GetSpeculativePrimaryMainFrame()
diff --git a/content/browser/renderer_host/navigation_entry_impl.cc b/content/browser/renderer_host/navigation_entry_impl.cc
index 8d48a6da..27dfef5 100644
--- a/content/browser/renderer_host/navigation_entry_impl.cc
+++ b/content/browser/renderer_host/navigation_entry_impl.cc
@@ -820,7 +820,6 @@
     const GURL& dest_url,
     blink::mojom::ReferrerPtr dest_referrer,
     blink::mojom::NavigationType navigation_type,
-    blink::PreviewsState previews_state,
     base::TimeTicks navigation_start,
     base::TimeTicks input_start) {
   blink::NavigationDownloadPolicy download_policy;
@@ -838,11 +837,10 @@
       // navigation that may use replacement create their CommonNavigationParams
       // via NavigationRequest, for example, instead of via NavigationEntry.
       false /* should_replace_entry */,
-      is_for_main_frame ? GetBaseURLForDataURL() : GURL(), previews_state,
-      navigation_start, frame_entry.method(),
-      post_body ? post_body : post_data_, network::mojom::SourceLocation::New(),
-      has_started_from_context_menu(), has_user_gesture(),
-      false /* has_text_fragment_token */,
+      is_for_main_frame ? GetBaseURLForDataURL() : GURL(), navigation_start,
+      frame_entry.method(), post_body ? post_body : post_data_,
+      network::mojom::SourceLocation::New(), has_started_from_context_menu(),
+      has_user_gesture(), false /* has_text_fragment_token */,
       network::mojom::CSPDisposition::CHECK, std::vector<int>(), std::string(),
       false /* is_history_navigation_in_new_child_frame */, input_start,
       network::mojom::RequestDestination::kEmpty);
diff --git a/content/browser/renderer_host/navigation_entry_impl.h b/content/browser/renderer_host/navigation_entry_impl.h
index 954fa49..93221181 100644
--- a/content/browser/renderer_host/navigation_entry_impl.h
+++ b/content/browser/renderer_host/navigation_entry_impl.h
@@ -29,7 +29,6 @@
 #include "content/public/browser/ssl_status.h"
 #include "net/base/isolation_info.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
-#include "third_party/blink/public/common/loader/previews_state.h"
 #include "third_party/blink/public/common/page_state/page_state.h"
 #include "third_party/blink/public/mojom/navigation/navigation_params.mojom-forward.h"
 #include "url/origin.h"
@@ -207,7 +206,6 @@
       const GURL& dest_url,
       blink::mojom::ReferrerPtr dest_referrer,
       blink::mojom::NavigationType navigation_type,
-      blink::PreviewsState previews_state,
       base::TimeTicks navigation_start,
       base::TimeTicks input_start);
   blink::mojom::CommitNavigationParamsPtr ConstructCommitNavigationParams(
diff --git a/content/browser/renderer_host/navigation_request.cc b/content/browser/renderer_host/navigation_request.cc
index 22024cc..8a84c35 100644
--- a/content/browser/renderer_host/navigation_request.cc
+++ b/content/browser/renderer_host/navigation_request.cc
@@ -1219,8 +1219,7 @@
           is_same_document ? blink::mojom::NavigationType::SAME_DOCUMENT
                            : blink::mojom::NavigationType::DIFFERENT_DOCUMENT,
           blink::NavigationDownloadPolicy(), should_replace_current_entry,
-          GURL() /* base_url_for_data_url*/,
-          blink::PreviewsTypes::PREVIEWS_UNSPECIFIED, base::TimeTicks::Now(),
+          GURL() /* base_url_for_data_url*/, base::TimeTicks::Now(),
           method /* method */, nullptr /* post_data */,
           network::mojom::SourceLocation::New(),
           false /* started_from_context_menu */, has_transient_activation,
@@ -2088,16 +2087,6 @@
     return;
   }
 
-  // If the navigation is served from the back-forward cache or is activating a
-  // prerendered page, we already know its preview type from the first time we
-  // navigated into the page, so we should only set |previews_state| when the
-  // navigation is not served from one of these.
-  if (!IsPageActivation()) {
-    common_params_->previews_state =
-        GetContentClient()->browser()->DetermineAllowedPreviews(
-            common_params_->previews_state, this, common_params_->url);
-  }
-
   base::UmaHistogramTimes(
       base::StrCat({"Navigation.BeginNavigationImpl.",
                     IsInMainFrame() ? "MainFrame" : "Subframe"}),
@@ -2655,13 +2644,6 @@
         site_instance->GetBrowserContext());
   }
 
-  // Re-evaluate the PreviewsState, but do not update the URLLoader. The
-  // URLLoader PreviewsState is considered immutable after the URLLoader is
-  // created.
-  common_params_->previews_state =
-      GetContentClient()->browser()->DetermineAllowedPreviews(
-          common_params_->previews_state, this, common_params_->url);
-
   // Check what the process of the SiteInstance is. It will be passed to the
   // NavigationHandle, and informed to expect a navigation to the redirected
   // URL.
@@ -3331,11 +3313,6 @@
   // This must be set before DetermineCommittedPreviews is called.
   proxy_server_ = response_head_->proxy_server;
 
-  // Update the previews state of the request.
-  common_params_->previews_state =
-      GetContentClient()->browser()->DetermineCommittedPreviews(
-          common_params_->previews_state, this, response_head_->headers.get());
-
   // Store the URLLoaderClient endpoints until checks have been processed.
   url_loader_client_endpoints_ = std::move(url_loader_client_endpoints);
 
@@ -3564,7 +3541,6 @@
   // anymore from now while the error page is being loaded.
   loader_.reset();
 
-  common_params_->previews_state = blink::PreviewsTypes::PREVIEWS_OFF;
   ssl_info_ = status.ssl_info;
 
   devtools_instrumentation::OnNavigationRequestFailed(*this, status);
@@ -4089,9 +4065,9 @@
 
   net::HttpRequestHeaders cors_exempt_headers;
   std::swap(cors_exempt_headers, cors_exempt_request_headers_);
-  loader_->FollowRedirect(
-      std::move(removed_headers), std::move(modified_headers),
-      std::move(cors_exempt_headers), common_params_->previews_state);
+  loader_->FollowRedirect(std::move(removed_headers),
+                          std::move(modified_headers),
+                          std::move(cors_exempt_headers));
 }
 
 void NavigationRequest::OnFailureChecksComplete(
diff --git a/content/browser/renderer_host/navigation_request.h b/content/browser/renderer_host/navigation_request.h
index 212ada6..6d3c43e 100644
--- a/content/browser/renderer_host/navigation_request.h
+++ b/content/browser/renderer_host/navigation_request.h
@@ -59,7 +59,6 @@
 #include "services/network/public/mojom/content_security_policy.mojom.h"
 #include "services/network/public/mojom/web_sandbox_flags.mojom-shared.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
-#include "third_party/blink/public/common/loader/previews_state.h"
 #include "third_party/blink/public/common/navigation/impression.h"
 #include "third_party/blink/public/common/tokens/tokens.h"
 #include "third_party/blink/public/mojom/loader/mixed_content.mojom-forward.h"
diff --git a/content/browser/renderer_host/navigator.h b/content/browser/renderer_host/navigator.h
index be80f0a..6e0a401 100644
--- a/content/browser/renderer_host/navigator.h
+++ b/content/browser/renderer_host/navigator.h
@@ -16,7 +16,6 @@
 #include "mojo/public/cpp/bindings/pending_associated_remote.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
-#include "third_party/blink/public/common/loader/previews_state.h"
 #include "third_party/blink/public/common/navigation/impression.h"
 #include "third_party/blink/public/mojom/frame/triggering_event_info.mojom-shared.h"
 #include "third_party/blink/public/mojom/navigation/navigation_params.mojom-forward.h"
diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc
index c1a0c5fb..95329d4 100644
--- a/content/browser/renderer_host/render_frame_host_impl.cc
+++ b/content/browser/renderer_host/render_frame_host_impl.cc
@@ -1326,8 +1326,6 @@
       last_committed_site_info_(site_instance_->GetBrowserContext()),
       routing_id_(routing_id),
       beforeunload_timeout_delay_(RenderViewHostImpl::kUnloadTimeout),
-      last_navigation_previews_state_(
-          blink::PreviewsTypes::PREVIEWS_UNSPECIFIED),
       frame_(std::move(frame_remote)),
       waiting_for_init_(renderer_initiated_creation_of_main_frame),
       frame_token_(frame_token),
@@ -8184,10 +8182,6 @@
       DCHECK(!prefetch_loader_factory);
     }
 
-    // If a network request was made, update the Previews state.
-    if (IsURLHandledByNetworkStack(common_params->url))
-      last_navigation_previews_state_ = common_params->previews_state;
-
     blink::mojom::PolicyContainerPtr policy_container =
         navigation_request->CreatePolicyContainerForBlink();
 
diff --git a/content/browser/renderer_host/render_frame_host_impl.h b/content/browser/renderer_host/render_frame_host_impl.h
index d4e00a00..a3f348e 100644
--- a/content/browser/renderer_host/render_frame_host_impl.h
+++ b/content/browser/renderer_host/render_frame_host_impl.h
@@ -95,7 +95,6 @@
 #include "services/network/public/mojom/url_loader_network_service_observer.mojom-forward.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/blink/public/common/frame/frame_owner_element_type.h"
-#include "third_party/blink/public/common/loader/previews_state.h"
 #include "third_party/blink/public/common/permissions_policy/permissions_policy.h"
 #include "third_party/blink/public/common/scheduler/web_scheduler_tracked_feature.h"
 #include "third_party/blink/public/common/storage_key/storage_key.h"
@@ -1291,13 +1290,6 @@
 
   void ClearFocusedElement();
 
-  // Returns the PreviewsState of the last successful navigation
-  // that made a network request. The PreviewsState is a bitmask of potentially
-  // several Previews optimizations.
-  blink::PreviewsState last_navigation_previews_state() const {
-    return last_navigation_previews_state_;
-  }
-
   bool has_focused_editable_element() const {
     return has_focused_editable_element_;
   }
@@ -3608,11 +3600,6 @@
   // Used for tracking the latest size of the RenderFrame.
   absl::optional<gfx::Size> frame_size_;
 
-  // The Previews state of the last navigation. This is used during history
-  // navigation of subframes to ensure that subframes navigate with the same
-  // Previews status as the top-level frame.
-  blink::PreviewsState last_navigation_previews_state_;
-
   // This boolean indicates whether the RenderFrame has committed *any*
   // navigation or not. Starts off false and is set to true for the lifetime of
   // the RenderFrame when the first CommitNavigation message is sent to the
diff --git a/content/browser/renderer_host/render_frame_host_manager_unittest.cc b/content/browser/renderer_host/render_frame_host_manager_unittest.cc
index b974554..007c8ce 100644
--- a/content/browser/renderer_host/render_frame_host_manager_unittest.cc
+++ b/content/browser/renderer_host/render_frame_host_manager_unittest.cc
@@ -66,7 +66,6 @@
 #include "net/http/http_response_headers.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/common/frame/frame_policy.h"
-#include "third_party/blink/public/common/loader/previews_state.h"
 #include "third_party/blink/public/common/tokens/tokens.h"
 #include "third_party/blink/public/mojom/favicon/favicon_url.mojom.h"
 #include "third_party/blink/public/mojom/frame/frame_owner_properties.mojom.h"
@@ -411,8 +410,7 @@
         entry->ConstructCommonNavigationParams(
             *frame_entry, request_body, frame_entry->url(),
             blink::mojom::Referrer::New(referrer.url, referrer.policy),
-            navigate_type, blink::PreviewsTypes::PREVIEWS_UNSPECIFIED,
-            base::TimeTicks::Now(), base::TimeTicks::Now());
+            navigate_type, base::TimeTicks::Now(), base::TimeTicks::Now());
     blink::mojom::CommitNavigationParamsPtr commit_params =
         entry->ConstructCommitNavigationParams(
             *frame_entry, common_params->url, frame_entry->committed_origin(),
@@ -3037,8 +3035,7 @@
           *frame_entry, nullptr, frame_entry->url(),
           blink::mojom::Referrer::New(referrer.url, referrer.policy),
           blink::mojom::NavigationType::DIFFERENT_DOCUMENT,
-          blink::PreviewsTypes::PREVIEWS_UNSPECIFIED, base::TimeTicks::Now(),
-          base::TimeTicks::Now());
+          base::TimeTicks::Now(), base::TimeTicks::Now());
   blink::mojom::CommitNavigationParamsPtr commit_params =
       entry.ConstructCommitNavigationParams(
           *frame_entry, common_params->url, frame_entry->committed_origin(),
diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc
index 258e2c07..fbbd1ef 100644
--- a/content/browser/renderer_host/render_process_host_impl.cc
+++ b/content/browser/renderer_host/render_process_host_impl.cc
@@ -1840,7 +1840,6 @@
     std::unique_ptr<SandboxedProcessLauncherDelegate> sandbox_delegate =
         std::make_unique<RendererSandboxedProcessLauncherDelegate>();
 #endif
-    auto snapshot_files = GetV8SnapshotFilesToPreload(*cmd_line);
     // Spawn the child process asynchronously to avoid blocking the UI thread.
     // As long as there's no renderer prefix, we can use the zygote process
     // at this stage.
@@ -1848,7 +1847,7 @@
         std::move(sandbox_delegate), std::move(cmd_line), GetID(), this,
         std::move(mojo_invitation_),
         base::BindRepeating(&RenderProcessHostImpl::OnMojoError, id_),
-        std::move(snapshot_files));
+        GetV8SnapshotFilesToPreload());
     channel_->Pause();
 
     // In single process mode, browser-side tracing and memory will cover the
diff --git a/content/browser/security_exploit_browsertest.cc b/content/browser/security_exploit_browsertest.cc
index d0cecf8d..331fbda 100644
--- a/content/browser/security_exploit_browsertest.cc
+++ b/content/browser/security_exploit_browsertest.cc
@@ -1474,7 +1474,6 @@
           blink::NavigationDownloadPolicy(),
           false /* should_replace_current_entry */,
           file_url, /* base_url_for_data_url */
-          blink::PreviewsTypes::PREVIEWS_UNSPECIFIED,
           base::TimeTicks::Now() /* navigation_start */, "GET",
           nullptr /* post_data */, network::mojom::SourceLocation::New(),
           false /* started_from_context_menu */, false /* has_user_gesture */,
diff --git a/content/browser/utility_process_host.cc b/content/browser/utility_process_host.cc
index 5f1847c..d596feb 100644
--- a/content/browser/utility_process_host.cc
+++ b/content/browser/utility_process_host.cc
@@ -328,9 +328,8 @@
         std::make_unique<UtilitySandboxedProcessLauncherDelegate>(
             sandbox_type_, env_, *cmd_line);
 
-    auto snapshot_files = GetV8SnapshotFilesToPreload(*cmd_line);
     process_->LaunchWithPreloadedFiles(std::move(delegate), std::move(cmd_line),
-                                       std::move(snapshot_files), true);
+                                       GetV8SnapshotFilesToPreload(), true);
   }
 
   return true;
diff --git a/content/browser/v8_snapshot_files.cc b/content/browser/v8_snapshot_files.cc
index 1bbc7c4..0ee3961 100644
--- a/content/browser/v8_snapshot_files.cc
+++ b/content/browser/v8_snapshot_files.cc
@@ -4,17 +4,12 @@
 
 #include "content/browser/v8_snapshot_files.h"
 
-#include "base/command_line.h"
-#include "base/feature_list.h"
 #include "build/build_config.h"
 #include "content/public/common/content_descriptor_keys.h"
-#include "content/public/common/content_features.h"
-#include "content/public/common/content_switches.h"
 
 namespace content {
 
-std::map<std::string, base::FilePath> GetV8SnapshotFilesToPreload(
-    base::CommandLine& process_command_line) {
+std::map<std::string, base::FilePath> GetV8SnapshotFilesToPreload() {
 #if defined(OS_LINUX) || defined(OS_CHROMEOS)
 #if defined(USE_V8_CONTEXT_SNAPSHOT)
   return {{kV8ContextSnapshotDataDescriptor,
@@ -24,14 +19,7 @@
            base::FilePath(FILE_PATH_LITERAL("snapshot_blob.bin"))}};
 #endif
 #elif defined(OS_ANDROID)
-#if defined(INCLUDE_BOTH_V8_SNAPSHOTS) || !defined(USE_V8_CONTEXT_SNAPSHOT)
-#if defined(INCLUDE_BOTH_V8_SNAPSHOTS)
-  if (base::FeatureList::IsEnabled(features::kUseContextSnapshot)) {
-    process_command_line.AppendSwitch(switches::kUseContextSnapshotSwitch);
-    // For USE_V8_CONTEXT_SNAPSHOT, the renderer reads the files directly.
-    return {};
-  }
-#endif
+#if !defined(USE_V8_CONTEXT_SNAPSHOT)
   return {{kV8Snapshot64DataDescriptor,
            base::FilePath(FILE_PATH_LITERAL("assets/snapshot_blob_64.bin"))},
           {kV8Snapshot32DataDescriptor,
diff --git a/content/browser/v8_snapshot_files.h b/content/browser/v8_snapshot_files.h
index a91f7c6..5b096c56 100644
--- a/content/browser/v8_snapshot_files.h
+++ b/content/browser/v8_snapshot_files.h
@@ -10,10 +10,6 @@
 
 #include "base/files/file_path.h"
 
-namespace base {
-class CommandLine;
-}
-
 namespace content {
 
 // Returns a mapping of V8 snapshot files to be preloaded for child processes
@@ -21,11 +17,8 @@
 // be empty or unused on some.
 //
 // This mapping can be passed to
-// `BrowserChildProcessHost::LaunchWithPreloadedFiles()`. `process_command_line`
-// is the command line that will be used in launching the process the files will
-// be supplied to.
-std::map<std::string, base::FilePath> GetV8SnapshotFilesToPreload(
-    base::CommandLine& process_command_line);
+// `BrowserChildProcessHost::LaunchWithPreloadedFiles()`.
+std::map<std::string, base::FilePath> GetV8SnapshotFilesToPreload();
 
 }  // namespace content
 
diff --git a/content/browser/worker_host/shared_worker_host.cc b/content/browser/worker_host/shared_worker_host.cc
index 357f00e..253fc21d 100644
--- a/content/browser/worker_host/shared_worker_host.cc
+++ b/content/browser/worker_host/shared_worker_host.cc
@@ -35,6 +35,7 @@
 #include "content/public/browser/service_worker_context.h"
 #include "content/public/browser/worker_type.h"
 #include "content/public/common/content_client.h"
+#include "content/public/common/content_features.h"
 #include "mojo/public/cpp/bindings/pending_associated_remote.h"
 #include "net/base/isolation_info.h"
 #include "net/cookies/site_for_cookies.h"
@@ -117,8 +118,7 @@
     scoped_refptr<SiteInstanceImpl> site_instance,
     std::vector<network::mojom::ContentSecurityPolicyPtr>
         content_security_policies,
-    const network::CrossOriginEmbedderPolicy&
-        creator_cross_origin_embedder_policy)
+    network::mojom::ClientSecurityStatePtr creator_client_security_state)
     : service_(service),
       token_(blink::SharedWorkerToken()),
       instance_(instance),
@@ -134,8 +134,7 @@
       ukm_source_id_(ukm::ConvertToSourceId(ukm::AssignNewSourceId(),
                                             ukm::SourceIdType::WORKER_ID)),
       reporting_source_(base::UnguessableToken::Create()),
-      creator_cross_origin_embedder_policy_(
-          creator_cross_origin_embedder_policy) {
+      creator_client_security_state_(std::move(creator_client_security_state)) {
   DCHECK(GetProcessHost());
   DCHECK(GetProcessHost()->IsInitializedAndNotDead());
 
@@ -208,23 +207,32 @@
   started_ = true;
   final_response_url_ = final_response_url;
 
+  worker_client_security_state_ = network::mojom::ClientSecurityState::New();
+  if (base::FeatureList::IsEnabled(features::kPrivateNetworkAccessForWorkers)) {
+    // TODO(https://crbug.com/1282639): Compute the client security state from
+    // the response if needed, just like we do for COEP.
+    worker_client_security_state_ = mojo::Clone(creator_client_security_state_);
+  }
+
+  worker_client_security_state_->cross_origin_embedder_policy =
+      network::CrossOriginEmbedderPolicy();
   if (base::FeatureList::IsEnabled(blink::features::kCOEPForSharedWorker)) {
     // https://html.spec.whatwg.org/C/#run-a-worker
-    worker_cross_origin_embedder_policy_ = network::CrossOriginEmbedderPolicy();
     if (final_response_url.SchemeIsBlob() ||
         final_response_url.SchemeIs(url::kAboutScheme) ||
         final_response_url.SchemeIs(url::kDataScheme)) {
       // > 13.6 If response's url's scheme is a local scheme, then set worker
       // global scope's embedder policy to owner's embedder policy.
-      worker_cross_origin_embedder_policy_ =
-          creator_cross_origin_embedder_policy_;
+      worker_client_security_state_->cross_origin_embedder_policy =
+          creator_client_security_state_->cross_origin_embedder_policy;
     } else if (main_script_load_params->response_head->parsed_headers) {
       // > 13.7 Otherwise, set worker global scope's embedder policy to the
       // result of obtaining an embedder policy from response.
-      worker_cross_origin_embedder_policy_ = CoepFromMainResponse(
-          final_response_url, main_script_load_params->response_head.get());
+      worker_client_security_state_->cross_origin_embedder_policy =
+          CoepFromMainResponse(final_response_url,
+                               main_script_load_params->response_head.get());
     }
-    switch (worker_cross_origin_embedder_policy_->value) {
+    switch (worker_client_security_state_->cross_origin_embedder_policy.value) {
       case network::mojom::CrossOriginEmbedderPolicyValue::kNone:
         OnFeatureUsed(blink::mojom::WebFeature::kCoepNoneSharedWorker);
         break;
@@ -242,11 +250,11 @@
     // Create a COEP reporter with worker's policy.
     coep_reporter_ = std::make_unique<CrossOriginEmbedderPolicyReporter>(
         storage_partition->GetWeakPtr(), final_response_url,
-        worker_cross_origin_embedder_policy_->reporting_endpoint,
-        worker_cross_origin_embedder_policy_->report_only_reporting_endpoint,
+        worker_client_security_state_->cross_origin_embedder_policy
+            .reporting_endpoint,
+        worker_client_security_state_->cross_origin_embedder_policy
+            .report_only_reporting_endpoint,
         GetReportingSource(), GetNetworkIsolationKey());
-  } else {
-    worker_cross_origin_embedder_policy_ = network::CrossOriginEmbedderPolicy();
   }
 
   auto options = blink::mojom::WorkerOptions::New(
@@ -372,16 +380,9 @@
   url::Origin origin = GetStorageKey().origin();
   mojo::PendingRemote<network::mojom::CrossOriginEmbedderPolicyReporter>
       coep_reporter;
-  network::mojom::ClientSecurityStatePtr client_security_state;
-  if (base::FeatureList::IsEnabled(blink::features::kCOEPForSharedWorker)) {
-    // TODO(crbug.com/1231019): make sure client_security_state is no longer
-    // nullptr anywhere.
-    client_security_state = network::mojom::ClientSecurityState::New();
-    client_security_state->cross_origin_embedder_policy =
-        cross_origin_embedder_policy();
-    if (coep_reporter_) {
-      coep_reporter_->Clone(coep_reporter.InitWithNewPipeAndPassReceiver());
-    }
+  if (base::FeatureList::IsEnabled(blink::features::kCOEPForSharedWorker) &&
+      coep_reporter_) {
+    coep_reporter_->Clone(coep_reporter.InitWithNewPipeAndPassReceiver());
   }
   network::mojom::URLLoaderFactoryParamsPtr factory_params =
       URLLoaderFactoryParamsHelper::CreateForWorker(
@@ -399,7 +400,7 @@
           std::move(coep_reporter),
           /*url_loader_network_observer=*/mojo::NullRemote(),
           /*devtools_observer=*/mojo::NullRemote(),
-          std::move(client_security_state),
+          mojo::Clone(worker_client_security_state_),
           /*debug_tag=*/
           "SharedWorkerHost::CreateNetworkFactoryForSubresource");
   return factory_params;
diff --git a/content/browser/worker_host/shared_worker_host.h b/content/browser/worker_host/shared_worker_host.h
index 45c2727d..6809efd0 100644
--- a/content/browser/worker_host/shared_worker_host.h
+++ b/content/browser/worker_host/shared_worker_host.h
@@ -68,13 +68,13 @@
 class CONTENT_EXPORT SharedWorkerHost : public blink::mojom::SharedWorkerHost,
                                         public SiteInstanceImpl::Observer {
  public:
-  SharedWorkerHost(SharedWorkerServiceImpl* service,
-                   const SharedWorkerInstance& instance,
-                   scoped_refptr<SiteInstanceImpl> site_instance,
-                   std::vector<network::mojom::ContentSecurityPolicyPtr>
-                       content_security_policies,
-                   const network::CrossOriginEmbedderPolicy&
-                       creator_cross_origin_embedder_policy);
+  SharedWorkerHost(
+      SharedWorkerServiceImpl* service,
+      const SharedWorkerInstance& instance,
+      scoped_refptr<SiteInstanceImpl> site_instance,
+      std::vector<network::mojom::ContentSecurityPolicyPtr>
+          content_security_policies,
+      network::mojom::ClientSecurityStatePtr creator_client_security_state);
 
   SharedWorkerHost(const SharedWorkerHost&) = delete;
   SharedWorkerHost& operator=(const SharedWorkerHost&) = delete;
@@ -159,7 +159,11 @@
 
   const network::CrossOriginEmbedderPolicy& cross_origin_embedder_policy()
       const {
-    return worker_cross_origin_embedder_policy_.value();
+    return worker_client_security_state_->cross_origin_embedder_policy;
+  }
+
+  const network::mojom::ClientSecurityStatePtr& client_security_state() const {
+    return worker_client_security_state_;
   }
 
   const std::vector<network::mojom::ContentSecurityPolicyPtr>&
@@ -306,15 +310,16 @@
 
   const base::UnguessableToken reporting_source_;
 
-  // The frame/worker's Cross-Origin-Embedder-Policy (COEP) that directly starts
-  // this worker.
-  const network::CrossOriginEmbedderPolicy
-      creator_cross_origin_embedder_policy_;
+  // The client security state of the creator execution context.
+  // Never nullptr. Copied at construction time.
+  // This is copied into `worker_client_security_state_` for workers loaded
+  // from URLs with local schemes, in which case the worker should inherit from
+  // its creator.
+  const network::mojom::ClientSecurityStatePtr creator_client_security_state_;
 
-  // The SharedWorker's Cross-Origin-Embedder-Policy (COEP). This is set when
-  // the script's response head is loaded.
-  absl::optional<network::CrossOriginEmbedderPolicy>
-      worker_cross_origin_embedder_policy_;
+  // The worker's own client security state, applied to subresource fetches.
+  // This is nullptr until it is computed in `DidStartScriptLoad()`.
+  network::mojom::ClientSecurityStatePtr worker_client_security_state_;
 
   std::unique_ptr<CrossOriginEmbedderPolicyReporter> coep_reporter_;
 
diff --git a/content/browser/worker_host/shared_worker_host_unittest.cc b/content/browser/worker_host/shared_worker_host_unittest.cc
index 07fe1eb..a22f3e6 100644
--- a/content/browser/worker_host/shared_worker_host_unittest.cc
+++ b/content/browser/worker_host/shared_worker_host_unittest.cc
@@ -22,6 +22,7 @@
 #include "content/browser/worker_host/shared_worker_connector_impl.h"
 #include "content/browser/worker_host/shared_worker_service_impl.h"
 #include "content/public/browser/shared_worker_instance.h"
+#include "content/public/common/content_features.h"
 #include "content/public/test/browser_task_environment.h"
 #include "content/public/test/mock_render_process_host.h"
 #include "content/public/test/test_browser_context.h"
@@ -34,6 +35,7 @@
 #include "services/network/public/cpp/cross_origin_embedder_policy.h"
 #include "services/network/public/cpp/features.h"
 #include "services/network/public/cpp/not_implemented_url_loader_factory.h"
+#include "services/network/test/client_security_state_builder.h"
 #include "testing/gmock/include/gmock/gmock-matchers.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/common/messaging/message_port_channel.h"
@@ -87,7 +89,7 @@
     auto host = std::make_unique<SharedWorkerHost>(
         &service_, instance, site_instance_,
         std::vector<network::mojom::ContentSecurityPolicyPtr>(),
-        network::CrossOriginEmbedderPolicy());
+        network::mojom::ClientSecurityState::New());
     auto weak_host = host->AsWeakPtr();
     service_.worker_hosts_.insert(std::move(host));
     return weak_host;
@@ -95,7 +97,8 @@
 
   void StartWorker(
       SharedWorkerHost* host,
-      mojo::PendingRemote<blink::mojom::SharedWorkerFactory> factory) {
+      mojo::PendingRemote<blink::mojom::SharedWorkerFactory> factory,
+      const GURL& final_response_url = GURL()) {
     auto main_script_load_params =
         blink::mojom::WorkerMainScriptLoadParams::New();
     main_script_load_params->response_head =
@@ -144,7 +147,7 @@
                     network::mojom::ReferrerPolicy::kDefault,
                     GURL() /* outgoing_referrer */,
                     blink::mojom::InsecureRequestsPolicy::kDoNotUpgrade),
-                GURL() /* final_response_url */);
+                final_response_url);
   }
 
   MessagePortChannel AddClient(
@@ -392,7 +395,7 @@
   auto host = std::make_unique<SharedWorkerHost>(
       &service_, instance, site_instance_,
       std::vector<network::mojom::ContentSecurityPolicyPtr>(),
-      network::CrossOriginEmbedderPolicy());
+      network::mojom::ClientSecurityState::New());
 
   // Start the worker.
   mojo::PendingRemote<blink::mojom::SharedWorkerFactory> factory;
@@ -407,4 +410,116 @@
   EXPECT_THAT(params->isolation_info.nonce(), testing::Optional(nonce));
 }
 
+// Enable PrivateNetworkAccessForWorkers only.
+class SharedWorkerHostTestWithPNAEnabled : public SharedWorkerHostTest {
+ public:
+  SharedWorkerHostTestWithPNAEnabled() {
+    feature_list.InitWithFeatures(
+        {/*enable_feature=*/features::kPrivateNetworkAccessForWorkers},
+        {/*disable_feature=*/blink::features::kCOEPForSharedWorker});
+  }
+  ~SharedWorkerHostTestWithPNAEnabled() override = default;
+
+ private:
+  base::test::ScopedFeatureList feature_list;
+};
+
+TEST_F(SharedWorkerHostTestWithPNAEnabled,
+       CreateNetworkFactoryParamsForSubresources) {
+  SharedWorkerInstance instance(
+      kWorkerUrl, blink::mojom::ScriptType::kClassic,
+      network::mojom::CredentialsMode::kSameOrigin, "name",
+      blink::StorageKey(url::Origin::Create(kWorkerUrl)),
+      network::mojom::IPAddressSpace::kPublic,
+      blink::mojom::SharedWorkerCreationContextType::kSecure);
+  network::CrossOriginEmbedderPolicy cross_origin_embedder_policy =
+      network::CrossOriginEmbedderPolicy();
+  cross_origin_embedder_policy.value =
+      network::mojom::CrossOriginEmbedderPolicyValue::kRequireCorp;
+  network::mojom::ClientSecurityStatePtr client_security_state =
+      network::ClientSecurityStateBuilder()
+          .WithPrivateNetworkRequestPolicy(
+              network::mojom::PrivateNetworkRequestPolicy::kPreflightBlock)
+          .WithIPAddressSpace(network::mojom::IPAddressSpace::kPublic)
+          .WithIsSecureContext(true)
+          .WithCrossOriginEmbedderPolicy(cross_origin_embedder_policy)
+          .Build();
+  auto host = std::make_unique<SharedWorkerHost>(
+      &service_, instance, site_instance_,
+      std::vector<network::mojom::ContentSecurityPolicyPtr>(),
+      std::move(client_security_state));
+
+  // Start the worker.
+  mojo::PendingRemote<blink::mojom::SharedWorkerFactory> factory;
+  MockSharedWorkerFactory factory_impl(
+      factory.InitWithNewPipeAndPassReceiver());
+  StartWorker(host.get(), std::move(factory), GURL("data://test.url"));
+
+  network::mojom::URLLoaderFactoryParamsPtr params =
+      host->CreateNetworkFactoryParamsForSubresources();
+  ASSERT_TRUE(params->client_security_state);
+  EXPECT_TRUE(params->client_security_state->is_web_secure_context);
+  EXPECT_EQ(params->client_security_state->ip_address_space,
+            network::mojom::IPAddressSpace::kPublic);
+  EXPECT_EQ(params->client_security_state->private_network_request_policy,
+            network::mojom::PrivateNetworkRequestPolicy::kPreflightBlock);
+  EXPECT_EQ(params->client_security_state->cross_origin_embedder_policy.value,
+            network::mojom::CrossOriginEmbedderPolicyValue::kNone);
+}
+
+// Enable COEPForSharedWorker and PrivateNetworkAccessForWorkers.
+class SharedWorkerHostTestWithCOEPandPNAEnabled
+    : public SharedWorkerHostTestWithCOEPEnabled {
+ public:
+  SharedWorkerHostTestWithCOEPandPNAEnabled() = default;
+  ~SharedWorkerHostTestWithCOEPandPNAEnabled() override = default;
+
+ private:
+  base::test::ScopedFeatureList feature_list{
+      /*enable_feature=*/features::kPrivateNetworkAccessForWorkers};
+};
+
+TEST_F(SharedWorkerHostTestWithCOEPandPNAEnabled,
+       CreateNetworkFactoryParamsForSubresources) {
+  SharedWorkerInstance instance(
+      kWorkerUrl, blink::mojom::ScriptType::kClassic,
+      network::mojom::CredentialsMode::kSameOrigin, "name",
+      blink::StorageKey(url::Origin::Create(kWorkerUrl)),
+      network::mojom::IPAddressSpace::kPublic,
+      blink::mojom::SharedWorkerCreationContextType::kSecure);
+  network::CrossOriginEmbedderPolicy cross_origin_embedder_policy =
+      network::CrossOriginEmbedderPolicy();
+  cross_origin_embedder_policy.value =
+      network::mojom::CrossOriginEmbedderPolicyValue::kRequireCorp;
+  network::mojom::ClientSecurityStatePtr client_security_state =
+      network::ClientSecurityStateBuilder()
+          .WithPrivateNetworkRequestPolicy(
+              network::mojom::PrivateNetworkRequestPolicy::kPreflightBlock)
+          .WithIPAddressSpace(network::mojom::IPAddressSpace::kPublic)
+          .WithIsSecureContext(true)
+          .WithCrossOriginEmbedderPolicy(cross_origin_embedder_policy)
+          .Build();
+  auto host = std::make_unique<SharedWorkerHost>(
+      &service_, instance, site_instance_,
+      std::vector<network::mojom::ContentSecurityPolicyPtr>(),
+      std::move(client_security_state));
+
+  // Start the worker.
+  mojo::PendingRemote<blink::mojom::SharedWorkerFactory> factory;
+  MockSharedWorkerFactory factory_impl(
+      factory.InitWithNewPipeAndPassReceiver());
+  StartWorker(host.get(), std::move(factory), GURL("data://test.url"));
+
+  network::mojom::URLLoaderFactoryParamsPtr params =
+      host->CreateNetworkFactoryParamsForSubresources();
+  ASSERT_TRUE(params->client_security_state);
+  EXPECT_TRUE(params->client_security_state->is_web_secure_context);
+  EXPECT_EQ(params->client_security_state->ip_address_space,
+            network::mojom::IPAddressSpace::kPublic);
+  EXPECT_EQ(params->client_security_state->private_network_request_policy,
+            network::mojom::PrivateNetworkRequestPolicy::kPreflightBlock);
+  EXPECT_EQ(params->client_security_state->cross_origin_embedder_policy.value,
+            network::mojom::CrossOriginEmbedderPolicyValue::kRequireCorp);
+}
+
 }  // namespace content
diff --git a/content/browser/worker_host/shared_worker_service_impl.cc b/content/browser/worker_host/shared_worker_service_impl.cc
index 9a8df40..0cac3d33 100644
--- a/content/browser/worker_host/shared_worker_service_impl.cc
+++ b/content/browser/worker_host/shared_worker_service_impl.cc
@@ -319,7 +319,7 @@
       worker_hosts_.insert(std::make_unique<SharedWorkerHost>(
           this, instance, std::move(site_instance),
           std::move(content_security_policies),
-          creator.cross_origin_embedder_policy()));
+          creator.BuildClientSecurityState()));
   DCHECK(insertion_result.second);
   SharedWorkerHost* host = insertion_result.first->get();
 
diff --git a/content/browser/worker_host/worker_browsertest.cc b/content/browser/worker_host/worker_browsertest.cc
index 85f80327..f50e7377 100644
--- a/content/browser/worker_host/worker_browsertest.cc
+++ b/content/browser/worker_host/worker_browsertest.cc
@@ -86,6 +86,7 @@
 // 0 => Base
 // 1 => kPlzDedicatedWorker enabled
 // 2 => kCOEPForSharedWorker enabled
+// 3 => kPrivateNetworkAccessForWorkers enabled
 class WorkerTest : public ContentBrowserTest,
                    public testing::WithParamInterface<int> {
  public:
@@ -106,6 +107,7 @@
             {
                 network::features::kCrossOriginEmbedderPolicyCredentialless,
                 blink::features::kPlzDedicatedWorker,
+                features::kPrivateNetworkAccessForWorkers,
             },
             {
                 blink::features::kCOEPForSharedWorker,
@@ -116,11 +118,22 @@
             {
                 network::features::kCrossOriginEmbedderPolicyCredentialless,
                 blink::features::kCOEPForSharedWorker,
+                features::kPrivateNetworkAccessForWorkers,
             },
             {
                 blink::features::kPlzDedicatedWorker,
             });
         break;
+      case 3:  // PrivateNetworkAccessForWorkers
+        feature_list_.InitWithFeatures(
+            {
+                network::features::kCrossOriginEmbedderPolicyCredentialless,
+                blink::features::kPlzDedicatedWorker,
+                blink::features::kCOEPForSharedWorker,
+                features::kPrivateNetworkAccessForWorkers,
+            },
+            {});
+        break;
       default:
         NOTREACHED();
         break;
@@ -325,7 +338,7 @@
   base::test::ScopedFeatureList feature_list_;
 };
 
-INSTANTIATE_TEST_SUITE_P(All, WorkerTest, testing::Range(0, 3));
+INSTANTIATE_TEST_SUITE_P(All, WorkerTest, testing::Range(0, 4));
 
 IN_PROC_BROWSER_TEST_P(WorkerTest, SingleWorker) {
   RunTest(GetTestURL("single_worker.html", std::string()));
@@ -345,7 +358,7 @@
 
 INSTANTIATE_TEST_SUITE_P(All,
                          WorkerTestWithAllowFileAccessFromFiles,
-                         testing::Range(0, 3));
+                         testing::Range(0, 4));
 
 IN_PROC_BROWSER_TEST_P(WorkerTestWithAllowFileAccessFromFiles,
                        SingleWorkerFromFile) {
@@ -926,7 +939,7 @@
 
 INSTANTIATE_TEST_SUITE_P(All,
                          WorkerFromAnonymousIframeNikBrowserTest,
-                         testing::Range(0, 3));
+                         testing::Range(0, 4));
 
 IN_PROC_BROWSER_TEST_P(WorkerFromAnonymousIframeNikBrowserTest,
                        SharedWorkerRequestIsDoneWithPartitionedNetworkState) {
diff --git a/content/public/android/java/src/org/chromium/content/browser/input/Range.java b/content/public/android/java/src/org/chromium/content/browser/input/Range.java
index c0bb79d..96a0cda 100644
--- a/content/public/android/java/src/org/chromium/content/browser/input/Range.java
+++ b/content/public/android/java/src/org/chromium/content/browser/input/Range.java
@@ -8,8 +8,6 @@
 
 /**
  * A simple class to set start and end in int type.
- * TODO(changwan): replace this with android.util.Range when the default SDK
- * version becomes 21 or higher.
  */
 public class Range {
     private int mStart; // guaranteed to be smaller than or equal to mEnd
diff --git a/content/public/browser/content_browser_client.cc b/content/public/browser/content_browser_client.cc
index 9fc5c7f..92da6ae 100644
--- a/content/public/browser/content_browser_client.cc
+++ b/content/public/browser/content_browser_client.cc
@@ -1053,20 +1053,6 @@
   return sandbox::policy::features::IsNetworkSandboxEnabled();
 }
 
-blink::PreviewsState ContentBrowserClient::DetermineAllowedPreviews(
-    blink::PreviewsState initial_state,
-    content::NavigationHandle* navigation_handle,
-    const GURL& current_navigation_url) {
-  return blink::PreviewsTypes::PREVIEWS_OFF;
-}
-
-blink::PreviewsState ContentBrowserClient::DetermineCommittedPreviews(
-    blink::PreviewsState initial_state,
-    content::NavigationHandle* navigation_handle,
-    const net::HttpResponseHeaders* response_headers) {
-  return blink::PreviewsTypes::PREVIEWS_OFF;
-}
-
 std::string ContentBrowserClient::GetProduct() {
   return std::string();
 }
diff --git a/content/public/browser/content_browser_client.h b/content/public/browser/content_browser_client.h
index 2b8a84d3..0fdf74e 100644
--- a/content/public/browser/content_browser_client.h
+++ b/content/public/browser/content_browser_client.h
@@ -58,7 +58,6 @@
 #include "services/network/public/mojom/websocket.mojom-forward.h"
 #include "storage/browser/file_system/file_system_context.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
-#include "third_party/blink/public/common/loader/previews_state.h"
 #include "third_party/blink/public/common/user_agent/user_agent_metadata.h"
 #include "third_party/blink/public/mojom/federated_learning/floc.mojom-forward.h"
 #include "third_party/blink/public/mojom/manifest/manifest.mojom-forward.h"
@@ -1865,26 +1864,6 @@
   // This is called on the UI thread.
   virtual bool ShouldSandboxNetworkService();
 
-  // Asks the embedder for the PreviewsState which says which previews should
-  // be enabled for the given navigation. The PreviewsState is a bitmask of
-  // potentially several Previews optimizations. |initial_state| is used to
-  // keep sub-frame navigation state consistent with main frame state.
-  // |current_navigation_url| is the URL that is currently being navigated to,
-  // and can differ from GetURL() in |navigation_handle| on redirects.
-  virtual blink::PreviewsState DetermineAllowedPreviews(
-      blink::PreviewsState initial_state,
-      content::NavigationHandle* navigation_handle,
-      const GURL& current_navigation_url);
-
-  // Asks the embedder for the preview state that should be committed to the
-  // renderer. |initial_state| was pre-determined by |DetermineAllowedPreviews|.
-  // |navigation_handle| is the corresponding navigation object.
-  // |response_headers| are the response headers related to this navigation.
-  virtual blink::PreviewsState DetermineCommittedPreviews(
-      blink::PreviewsState initial_state,
-      content::NavigationHandle* navigation_handle,
-      const net::HttpResponseHeaders* response_headers);
-
   // Browser-side API to log blink UseCounters for events that don't occur in
   // the renderer.
   virtual void LogWebFeatureForCurrentPage(
diff --git a/content/public/browser/web_contents_delegate.h b/content/public/browser/web_contents_delegate.h
index f328b36..58e9772 100644
--- a/content/public/browser/web_contents_delegate.h
+++ b/content/public/browser/web_contents_delegate.h
@@ -22,7 +22,6 @@
 #include "content/public/browser/serial_chooser.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/window_container_type.mojom-forward.h"
-#include "third_party/blink/public/common/loader/previews_state.h"
 #include "third_party/blink/public/common/mediastream/media_stream_request.h"
 #include "third_party/blink/public/common/page/drag_operation.h"
 #include "third_party/blink/public/mojom/choosers/color_chooser.mojom-forward.h"
@@ -647,11 +646,6 @@
   // top controls as a result of page gesture scrolling while in tablet mode.
   virtual void SetTopControlsGestureScrollInProgress(bool in_progress) {}
 
-  // Give WebContentsDelegates the opportunity to adjust the previews state.
-  virtual void AdjustPreviewsStateForNavigation(
-      WebContents* web_contents,
-      blink::PreviewsState* previews_state) {}
-
   // Requests to print an out-of-process subframe for the specified WebContents.
   // |rect| is the rectangular area where its content resides in its parent
   // frame. |document_cookie| is a unique id for a printed document associated
diff --git a/content/public/common/content_features.cc b/content/public/common/content_features.cc
index d159b6efb..6dd43128 100644
--- a/content/public/common/content_features.cc
+++ b/content/public/common/content_features.cc
@@ -377,6 +377,15 @@
 };
 #endif
 
+// If enabled, limits the number of FLEDGE auctions that can be run between page
+// load and unload -- any attempt to run more than this number of auctions will
+// fail (return null to JavaScript).
+const base::Feature kFledgeLimitNumAuctions{"LimitNumFledgeAuctions",
+                                            base::FEATURE_DISABLED_BY_DEFAULT};
+// The number of allowed auctions for each page load (load to unload).
+const base::FeatureParam<int> kFledgeLimitNumAuctionsParam{
+    &kFledgeLimitNumAuctions, "max_auctions_per_page", 8};
+
 // Enables scrollers inside Blink to store scroll offsets in fractional
 // floating-point numbers rather than truncating to integers.
 const base::Feature kFractionalScrollOffsets{"FractionalScrollOffsets",
@@ -945,12 +954,6 @@
 const base::Feature kUnrestrictedSharedArrayBuffer{
     "UnrestrictedSharedArrayBuffer", base::FEATURE_DISABLED_BY_DEFAULT};
 
-#if BUILDFLAG(IS_ANDROID) && defined(INCLUDE_BOTH_V8_SNAPSHOTS)
-// If enabled, blink's context snapshot is used rather than the v8 snapshot.
-const base::Feature kUseContextSnapshot{"UseContextSnapshot",
-                                        base::FEATURE_DISABLED_BY_DEFAULT};
-#endif
-
 // Allows user activation propagation to all frames having the same origin as
 // the activation notifier frame.  This is an intermediate measure before we
 // have an iframe attribute to declaratively allow user activation propagation
diff --git a/content/public/common/content_features.h b/content/public/common/content_features.h
index d787b2c..0559da1 100644
--- a/content/public/common/content_features.h
+++ b/content/public/common/content_features.h
@@ -95,6 +95,9 @@
 CONTENT_EXPORT extern const base::Feature
     kForwardMemoryPressureEventsToGpuProcess;
 #endif
+CONTENT_EXPORT extern const base::Feature kFledgeLimitNumAuctions;
+CONTENT_EXPORT extern const base::FeatureParam<int>
+    kFledgeLimitNumAuctionsParam;
 CONTENT_EXPORT extern const base::Feature kFractionalScrollOffsets;
 CONTENT_EXPORT extern const base::Feature kGreaseUACH;
 CONTENT_EXPORT extern const base::Feature kHistoryPreventSandboxedNavigation;
@@ -238,9 +241,6 @@
 CONTENT_EXPORT extern const base::Feature kTouchpadOverscrollHistoryNavigation;
 CONTENT_EXPORT extern const base::Feature kTrustedDOMTypes;
 CONTENT_EXPORT extern const base::Feature kUnrestrictedSharedArrayBuffer;
-#if BUILDFLAG(IS_ANDROID) && defined(INCLUDE_BOTH_V8_SNAPSHOTS)
-CONTENT_EXPORT extern const base::Feature kUseContextSnapshot;
-#endif
 CONTENT_EXPORT extern const base::Feature kUserActivationSameOriginVisibility;
 CONTENT_EXPORT extern const base::Feature kVerifyDidCommitParams;
 CONTENT_EXPORT extern const base::Feature kVideoPlaybackQuality;
diff --git a/content/public/common/content_switches.cc b/content/public/common/content_switches.cc
index bc80d8a..949c1a7 100644
--- a/content/public/common/content_switches.cc
+++ b/content/public/common/content_switches.cc
@@ -818,13 +818,6 @@
 // Texture target for CHROMIUM_image backed video frame textures.
 const char kVideoImageTextureTarget[] = "video-image-texture-target";
 
-#if BUILDFLAG(IS_ANDROID) && defined(INCLUDE_BOTH_V8_SNAPSHOTS)
-// Switch supplied to the renderer if the feature `kUseContextSnapshot` is
-// enabled. A switch is used as at the time the renderer needs this information
-// features have not yet been loaded.
-const char kUseContextSnapshotSwitch[] = "use-context-snapshot";
-#endif
-
 // Set when Chromium should use a mobile user agent.
 const char kUseMobileUserAgent[] = "use-mobile-user-agent";
 
diff --git a/content/public/common/content_switches.h b/content/public/common/content_switches.h
index ea7fe8c..3684a373 100644
--- a/content/public/common/content_switches.h
+++ b/content/public/common/content_switches.h
@@ -218,9 +218,6 @@
 CONTENT_EXPORT extern const char kUseFakeCodecForPeerConnection[];
 CONTENT_EXPORT extern const char kUseFakeUIForMediaStream[];
 CONTENT_EXPORT extern const char kVideoImageTextureTarget[];
-#if BUILDFLAG(IS_ANDROID) && defined(INCLUDE_BOTH_V8_SNAPSHOTS)
-CONTENT_EXPORT extern const char kUseContextSnapshotSwitch[];
-#endif
 CONTENT_EXPORT extern const char kUseMobileUserAgent[];
 CONTENT_EXPORT extern const char kUseMockCertVerifierForTesting[];
 extern const char kUtilityCmdPrefix[];
diff --git a/content/public/renderer/render_frame.h b/content/public/renderer/render_frame.h
index 7395d54a..faef767 100644
--- a/content/public/renderer/render_frame.h
+++ b/content/public/renderer/render_frame.h
@@ -17,7 +17,6 @@
 #include "ipc/ipc_sender.h"
 #include "ppapi/buildflags/buildflags.h"
 #include "services/service_manager/public/cpp/binder_registry.h"
-#include "third_party/blink/public/common/loader/previews_state.h"
 #include "third_party/blink/public/mojom/devtools/console_message.mojom.h"
 #include "third_party/blink/public/mojom/frame/triggering_event_info.mojom-shared.h"
 #include "third_party/blink/public/platform/task_type.h"
@@ -228,10 +227,6 @@
   virtual void AddMessageToConsole(blink::mojom::ConsoleMessageLevel level,
                                    const std::string& message) = 0;
 
-  // Returns the PreviewsState of this frame, a bitmask of potentially several
-  // Previews optimizations.
-  virtual blink::PreviewsState GetPreviewsState() = 0;
-
   // Whether or not this frame is currently pasting.
   virtual bool IsPasting() = 0;
 
diff --git a/content/public/renderer/render_frame_observer.h b/content/public/renderer/render_frame_observer.h
index 496b15e..1282c25 100644
--- a/content/public/renderer/render_frame_observer.h
+++ b/content/public/renderer/render_frame_observer.h
@@ -19,7 +19,6 @@
 #include "services/network/public/mojom/url_response_head.mojom-forward.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/blink/public/common/loader/loading_behavior_flag.h"
-#include "third_party/blink/public/common/loader/previews_state.h"
 #include "third_party/blink/public/common/responsiveness_metrics/user_interaction_latency.h"
 #include "third_party/blink/public/common/use_counter/use_counter_feature.h"
 #include "third_party/blink/public/mojom/loader/resource_load_info.mojom-shared.h"
@@ -237,14 +236,11 @@
   // Notification when the renderer a response started, completed or canceled.
   // Complete or Cancel is guaranteed to be called for a response that started.
   // |request_id| uniquely identifies the request within this render frame.
-  // |previews_state| is the PreviewsState if the request is a sub-resource. For
-  // Document resources, |previews_state| should be reported as PREVIEWS_OFF.
   virtual void DidStartResponse(
       const GURL& response_url,
       int request_id,
       const network::mojom::URLResponseHead& response_head,
-      network::mojom::RequestDestination request_destination,
-      blink::PreviewsState previews_state) {}
+      network::mojom::RequestDestination request_destination) {}
   virtual void DidCompleteResponse(
       int request_id,
       const network::URLLoaderCompletionStatus& status) {}
diff --git a/content/public/test/render_view_test.cc b/content/public/test/render_view_test.cc
index 9ffb17b..1484d5c8 100644
--- a/content/public/test/render_view_test.cc
+++ b/content/public/test/render_view_test.cc
@@ -48,7 +48,6 @@
 #include "third_party/blink/public/common/input/web_gesture_event.h"
 #include "third_party/blink/public/common/input/web_input_event.h"
 #include "third_party/blink/public/common/input/web_mouse_event.h"
-#include "third_party/blink/public/common/loader/previews_state.h"
 #include "third_party/blink/public/common/navigation/navigation_params.h"
 #include "third_party/blink/public/common/renderer_preferences/renderer_preferences.h"
 #include "third_party/blink/public/common/widget/visual_properties.h"
@@ -765,9 +764,8 @@
   auto common_params = blink::mojom::CommonNavigationParams::New(
       url, absl::nullopt, blink::mojom::Referrer::New(),
       ui::PAGE_TRANSITION_LINK, blink::mojom::NavigationType::RELOAD,
-      blink::NavigationDownloadPolicy(), false, GURL(),
-      blink::PreviewsTypes::PREVIEWS_UNSPECIFIED, base::TimeTicks::Now(), "GET",
-      nullptr, network::mojom::SourceLocation::New(),
+      blink::NavigationDownloadPolicy(), false, GURL(), base::TimeTicks::Now(),
+      "GET", nullptr, network::mojom::SourceLocation::New(),
       false /* started_from_context_menu */, false /* has_user_gesture */,
       false /* has_text_fragment_token */,
       network::mojom::CSPDisposition::CHECK, std::vector<int>(), std::string(),
@@ -902,9 +900,8 @@
       url, absl::nullopt, blink::mojom::Referrer::New(),
       ui::PAGE_TRANSITION_FORWARD_BACK,
       blink::mojom::NavigationType::HISTORY_DIFFERENT_DOCUMENT,
-      blink::NavigationDownloadPolicy(), false, GURL(),
-      blink::PreviewsTypes::PREVIEWS_UNSPECIFIED, base::TimeTicks::Now(), "GET",
-      nullptr, network::mojom::SourceLocation::New(),
+      blink::NavigationDownloadPolicy(), false, GURL(), base::TimeTicks::Now(),
+      "GET", nullptr, network::mojom::SourceLocation::New(),
       false /* started_from_context_menu */, false /* has_user_gesture */,
       false /* has_text_fragment_token */,
       network::mojom::CSPDisposition::CHECK, std::vector<int>(), std::string(),
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
index f23e6ab..8302cd97 100644
--- a/content/renderer/render_frame_impl.cc
+++ b/content/renderer/render_frame_impl.cc
@@ -311,10 +311,6 @@
     base::Seconds(5);
 constexpr base::TimeDelta kDelaySecondsForContentStateSync = base::Seconds(1);
 
-const blink::PreviewsState kDisabledPreviewsBits =
-    blink::PreviewsTypes::PREVIEWS_OFF |
-    blink::PreviewsTypes::PREVIEWS_NO_TRANSFORM;
-
 typedef std::map<int, RenderFrameImpl*> RoutingIDFrameMap;
 static base::LazyInstance<RoutingIDFrameMap>::DestructorAtExit
     g_routing_id_frame_map = LAZY_INSTANCE_INITIALIZER;
@@ -462,14 +458,6 @@
     }
   }
 
-  if (common_params.previews_state & kDisabledPreviewsBits) {
-    // Sanity check disabled vs. enabled bits here before passing on.
-    DCHECK(!(common_params.previews_state & ~kDisabledPreviewsBits))
-        << common_params.previews_state;
-  }
-  navigation_params->previews_state =
-      static_cast<blink::PreviewsState>(common_params.previews_state);
-
   // Set the request initiator origin, which is supplied by the browser
   // process. It is present in cases such as navigating a frame in a different
   // process, which is routed through RenderFrameProxy and the origin is
@@ -584,7 +572,6 @@
       std::move(referrer), url_request_extra_data->transition_type(),
       navigation_type, download_policy,
       info->frame_load_type == WebFrameLoadType::kReplaceCurrentItem, GURL(),
-      static_cast<blink::PreviewsState>(info->url_request.GetPreviewsState()),
       base::TimeTicks::Now(), info->url_request.HttpMethod().Latin1(),
       blink::GetRequestBodyForWebURLRequest(info->url_request),
       std::move(source_location), false /* started_from_context_menu */,
@@ -2497,12 +2484,6 @@
   AddMessageToConsoleImpl(level, message, false /* discard_duplicates */);
 }
 
-blink::PreviewsState RenderFrameImpl::GetPreviewsState() {
-  WebDocumentLoader* document_loader = frame_->GetDocumentLoader();
-  return document_loader ? document_loader->GetPreviewsState()
-                         : blink::PreviewsTypes::PREVIEWS_UNSPECIFIED;
-}
-
 bool RenderFrameImpl::IsPasting() {
   return GetLocalRootWebFrameWidget()->IsPasting();
 }
@@ -2532,14 +2513,13 @@
     int64_t request_id,
     const GURL& response_url,
     network::mojom::URLResponseHeadPtr response_head,
-    network::mojom::RequestDestination request_destination,
-    int32_t previews_state) {
+    network::mojom::RequestDestination request_destination) {
   if (!blink::IsRequestDestinationFrame(request_destination)) {
     GetFrameHost()->SubresourceResponseStarted(response_url,
                                                response_head->cert_status);
   }
   DidStartResponse(response_url, request_id, std::move(response_head),
-                   request_destination, previews_state);
+                   request_destination);
 }
 
 void RenderFrameImpl::NotifyResourceTransferSizeUpdated(
@@ -4388,11 +4368,10 @@
     const GURL& response_url,
     int request_id,
     network::mojom::URLResponseHeadPtr response_head,
-    network::mojom::RequestDestination request_destination,
-    blink::PreviewsState previews_state) {
+    network::mojom::RequestDestination request_destination) {
   for (auto& observer : observers_) {
     observer.DidStartResponse(response_url, request_id, *response_head,
-                              request_destination, previews_state);
+                              request_destination);
   }
 }
 
diff --git a/content/renderer/render_frame_impl.h b/content/renderer/render_frame_impl.h
index 89c39e1..760c40de 100644
--- a/content/renderer/render_frame_impl.h
+++ b/content/renderer/render_frame_impl.h
@@ -70,7 +70,6 @@
 #include "third_party/blink/public/common/browser_interface_broker_proxy.h"
 #include "third_party/blink/public/common/frame/frame_owner_element_type.h"
 #include "third_party/blink/public/common/loader/loading_behavior_flag.h"
-#include "third_party/blink/public/common/loader/previews_state.h"
 #include "third_party/blink/public/common/permissions_policy/document_policy.h"
 #include "third_party/blink/public/common/permissions_policy/permissions_policy.h"
 #include "third_party/blink/public/common/renderer_preferences/renderer_preferences.h"
@@ -371,7 +370,6 @@
                        const gfx::Range& range) override;
   void AddMessageToConsole(blink::mojom::ConsoleMessageLevel level,
                            const std::string& message) override;
-  blink::PreviewsState GetPreviewsState() override;
   bool IsPasting() override;
   bool IsBrowserSideNavigationPending() override;
   void LoadHTMLStringForTesting(const std::string& html,
@@ -408,8 +406,7 @@
       int64_t request_id,
       const GURL& response_url,
       network::mojom::URLResponseHeadPtr head,
-      network::mojom::RequestDestination request_destination,
-      int32_t previews_state) override;
+      network::mojom::RequestDestination request_destination) override;
   void NotifyResourceTransferSizeUpdated(int64_t request_id,
                                          int32_t transfer_size_diff) override;
   void NotifyResourceLoadCompleted(
@@ -732,8 +729,7 @@
   void DidStartResponse(const GURL& response_url,
                         int request_id,
                         network::mojom::URLResponseHeadPtr response_head,
-                        network::mojom::RequestDestination request_destination,
-                        blink::PreviewsState previews_state);
+                        network::mojom::RequestDestination request_destination);
   void DidCompleteResponse(int request_id,
                            const network::URLLoaderCompletionStatus& status);
   void DidCancelResponse(int request_id);
diff --git a/content/renderer/render_frame_impl_browsertest.cc b/content/renderer/render_frame_impl_browsertest.cc
index 0f5d9f2..ce63043 100644
--- a/content/renderer/render_frame_impl_browsertest.cc
+++ b/content/renderer/render_frame_impl_browsertest.cc
@@ -44,7 +44,6 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
-#include "third_party/blink/public/common/loader/previews_state.h"
 #include "third_party/blink/public/common/navigation/navigation_params.h"
 #include "third_party/blink/public/common/navigation/navigation_params_mojom_traits.h"
 #include "third_party/blink/public/common/tokens/tokens.h"
diff --git a/content/shell/android/BUILD.gn b/content/shell/android/BUILD.gn
index 431cb408..3f92028 100644
--- a/content/shell/android/BUILD.gn
+++ b/content/shell/android/BUILD.gn
@@ -175,8 +175,7 @@
   ]
   if (use_v8_context_snapshot) {
     deps += [ "//tools/v8_context_snapshot:v8_context_snapshot_assets" ]
-  }
-  if (!use_v8_context_snapshot || include_both_v8_snapshots) {
+  } else {
     deps += [ "//v8:v8_external_startup_data_assets" ]
   }
 }
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index e9fe911..59daf7b3 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -2744,8 +2744,7 @@
 
     if (use_v8_context_snapshot) {
       deps += [ "//tools/v8_context_snapshot:v8_context_snapshot_assets" ]
-    }
-    if (!use_v8_context_snapshot || include_both_v8_snapshots) {
+    } else {
       deps += [ "//v8:v8_external_startup_data_assets" ]
     }
 
diff --git a/content/test/data/accessibility/aria/aria-directory-children-expected-auralinux.txt b/content/test/data/accessibility/aria/aria-directory-children-expected-auralinux.txt
new file mode 100644
index 0000000..404b2b1
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-directory-children-expected-auralinux.txt
@@ -0,0 +1,21 @@
+[document web] tag:#document
+++[list] tag:div
+++++[list item] tag:div
+++++++[static] name='an item in the directory'
+++++[list item] tag:div
+++++++[static] name='an item in the directory'
+++[list] tag:ul
+++++[list item] tag:li
+++++++[static] name='an item in the directory, no explicit role'
+++++[list item] tag:li
+++++++[static] name='an item in the directory with role=listitem'
+++[list] tag:ol
+++++[list item] tag:li
+++++++[static] name='an item in the directory, no explicit role'
+++++[list item] tag:li
+++++++[static] name='an item in the directory with role=listitem'
+++[list] tag:menu
+++++[list item] tag:li
+++++++[static] name='an item in the directory, no explicit role'
+++++[list item] tag:li
+++++++[static] name='an item in the directory with role=listitem'
diff --git a/content/test/data/accessibility/aria/aria-directory-children-expected-blink.txt b/content/test/data/accessibility/aria/aria-directory-children-expected-blink.txt
new file mode 100644
index 0000000..9027f3e
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-directory-children-expected-blink.txt
@@ -0,0 +1,31 @@
+rootWebArea htmlTag='#document'
+++genericContainer ignored htmlTag='html'
+++++genericContainer ignored htmlTag='body'
+++++++directory htmlTag='div'
+++++++++listItem htmlTag='div'
+++++++++++staticText name='an item in the directory'
+++++++++++++inlineTextBox name='an item in the directory'
+++++++++listItem htmlTag='div'
+++++++++++staticText name='an item in the directory'
+++++++++++++inlineTextBox name='an item in the directory'
+++++++directory htmlTag='ul'
+++++++++listItem htmlTag='li'
+++++++++++staticText name='an item in the directory, no explicit role'
+++++++++++++inlineTextBox name='an item in the directory, no explicit role'
+++++++++listItem htmlTag='li'
+++++++++++staticText name='an item in the directory with role=listitem'
+++++++++++++inlineTextBox name='an item in the directory with role=listitem'
+++++++directory htmlTag='ol'
+++++++++listItem htmlTag='li'
+++++++++++staticText name='an item in the directory, no explicit role'
+++++++++++++inlineTextBox name='an item in the directory, no explicit role'
+++++++++listItem htmlTag='li'
+++++++++++staticText name='an item in the directory with role=listitem'
+++++++++++++inlineTextBox name='an item in the directory with role=listitem'
+++++++directory htmlTag='menu'
+++++++++listItem htmlTag='li'
+++++++++++staticText name='an item in the directory, no explicit role'
+++++++++++++inlineTextBox name='an item in the directory, no explicit role'
+++++++++listItem htmlTag='li'
+++++++++++staticText name='an item in the directory with role=listitem'
+++++++++++++inlineTextBox name='an item in the directory with role=listitem'
diff --git a/content/test/data/accessibility/aria/aria-directory-children.html b/content/test/data/accessibility/aria/aria-directory-children.html
new file mode 100644
index 0000000..c5a0f7d2
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-directory-children.html
@@ -0,0 +1,28 @@
+<!--
+@AURALINUX-ALLOW:tag*
+@BLINK-ALLOW:htmlTag*
+-->
+<!DOCTYPE html>
+<html>
+<body>
+<style>
+  li { list-style-type: none; }
+</style>
+<div role=directory>
+  <div role=listitem>an item in the directory</div>
+  <div role=listitem>an item in the directory</div>
+</div>
+<ul role=directory>
+  <li>an item in the directory, no explicit role</li>
+  <li role=listitem>an item in the directory with role=listitem</li>
+</ul>
+<ol role=directory>
+  <li>an item in the directory, no explicit role</li>
+  <li role=listitem>an item in the directory with role=listitem</li>
+</ol>
+<menu role=directory>
+  <li>an item in the directory, no explicit role</li>
+  <li role=listitem>an item in the directory with role=listitem</li>
+</menu>
+</body>
+</html>
diff --git a/content/test/data/accessibility/aria/aria-listbox-activedescendant-expected-blink.txt b/content/test/data/accessibility/aria/aria-listbox-activedescendant-expected-blink.txt
index 4edb2873..c64785af 100644
--- a/content/test/data/accessibility/aria/aria-listbox-activedescendant-expected-blink.txt
+++ b/content/test/data/accessibility/aria/aria-listbox-activedescendant-expected-blink.txt
@@ -1,6 +1,31 @@
 rootWebArea
 ++genericContainer ignored
-++++listBox activedescendantId=listBoxOption
-++++++listBoxOption name='1' selected=false
-++++++listBoxOption name='2' selected=false
-++++++listBoxOption name='3' selected=true
+++++genericContainer ignored
+++++++listBox activedescendantId=listBoxOption
+++++++++listBoxOption name='1' selected=false
+++++++++++staticText name='Item 1'
+++++++++++++inlineTextBox name='Item 1'
+++++++++listBoxOption name='2' selected=false
+++++++++++staticText name='Item 2'
+++++++++++++inlineTextBox name='Item 2'
+++++++++listBoxOption name='3' selected=false
+++++++++++staticText name='Item 3'
+++++++++++++inlineTextBox name='Item 3'
+++++++genericContainer
+=== Start Continuation ===
+rootWebArea
+++genericContainer ignored
+++++genericContainer ignored
+++++++listBox activedescendantId=listBoxOption
+++++++++listBoxOption name='1' selected=false
+++++++++++staticText name='Item 1'
+++++++++++++inlineTextBox name='Item 1'
+++++++++listBoxOption name='2' selected=false
+++++++++++staticText name='Item 2'
+++++++++++++inlineTextBox name='Item 2'
+++++++++listBoxOption name='3' selected=true
+++++++++++staticText name='Item 3'
+++++++++++++inlineTextBox name='Item 3'
+++++++genericContainer
+++++++++staticText name='focused'
+++++++++++inlineTextBox name='focused'
diff --git a/content/test/data/accessibility/aria/aria-listbox-activedescendant-expected-mac.txt b/content/test/data/accessibility/aria/aria-listbox-activedescendant-expected-mac.txt
index f141691..b32eb01a 100644
--- a/content/test/data/accessibility/aria/aria-listbox-activedescendant-expected-mac.txt
+++ b/content/test/data/accessibility/aria/aria-listbox-activedescendant-expected-mac.txt
@@ -1,5 +1,14 @@
-AXWebArea
-++AXList AXOrientation='AXVerticalOrientation' AXSelectedChildren=["AXStaticText 3"] AXVisibleChildren=["AXStaticText 1","AXStaticText 2","AXStaticText 3"]
-++++AXStaticText AXValue='1'
-++++AXStaticText AXValue='2'
-++++AXStaticText AXValue='3'
+AXWebArea AXSelected=0
+++AXList AXOrientation='AXVerticalOrientation' AXSelected=0 AXSelectedChildren=[] AXVisibleChildren=[:3, :4, :5]
+++++AXStaticText AXSelected=0 AXValue='1'
+++++AXStaticText AXSelected=0 AXValue='2'
+++++AXStaticText AXSelected=0 AXValue='3'
+++AXGroup AXSelected=0
+=== Start Continuation ===
+AXWebArea AXSelected=0
+++AXList AXOrientation='AXVerticalOrientation' AXSelected=0 AXSelectedChildren=[:5] AXVisibleChildren=[:3, :4, :5]
+++++AXStaticText AXSelected=0 AXValue='1'
+++++AXStaticText AXSelected=0 AXValue='2'
+++++AXStaticText AXSelected=1 AXValue='3'
+++AXGroup AXSelected=0
+++++AXStaticText AXSelected=0 AXValue='focused'
diff --git a/content/test/data/accessibility/aria/aria-listbox-activedescendant.html b/content/test/data/accessibility/aria/aria-listbox-activedescendant.html
index 00505b2..898ecfde0 100644
--- a/content/test/data/accessibility/aria/aria-listbox-activedescendant.html
+++ b/content/test/data/accessibility/aria/aria-listbox-activedescendant.html
@@ -1,6 +1,8 @@
 <!--
+@EXECUTE-AND-WAIT-FOR:focusChildren()
 @MAC-ALLOW:AXSubrole
 @MAC-ALLOW:AXOrientation
+@MAC-ALLOW:AXSelected
 @MAC-ALLOW:AXSelectedChildren
 @MAC-ALLOW:AXVisibleChildren
 -->
@@ -11,6 +13,14 @@
   <div id="2" aria-label="2" role="option">Item 2</div>
   <div id="3" aria-label="3" role="option">Item 3</div>
 </div>
-<script>document.querySelector('*[role="listbox"]').focus();</script>
+<div id="done"></div>
+<script>
+function focusChildren() {
+    document.querySelector('*[role="listbox"]').focus();
+    document.getElementById('done').innerText = 'focused';
+    return 'focused';
+}
+</script>
+
 </body>
 </html>
diff --git a/content/test/test_navigation_url_loader.cc b/content/test/test_navigation_url_loader.cc
index 4ccb572..7057af3e 100644
--- a/content/test/test_navigation_url_loader.cc
+++ b/content/test/test_navigation_url_loader.cc
@@ -39,8 +39,7 @@
 void TestNavigationURLLoader::FollowRedirect(
     const std::vector<std::string>& removed_headers,
     const net::HttpRequestHeaders& modified_headers,
-    const net::HttpRequestHeaders& modified_cors_exempt_headers,
-    blink::PreviewsState new_previews_state) {
+    const net::HttpRequestHeaders& modified_cors_exempt_headers) {
   DCHECK_EQ(loader_type_, NavigationURLLoader::LoaderType::kRegular);
   redirect_count_++;
 }
diff --git a/content/test/test_navigation_url_loader.h b/content/test/test_navigation_url_loader.h
index 69cb8561..33d26e1 100644
--- a/content/test/test_navigation_url_loader.h
+++ b/content/test/test_navigation_url_loader.h
@@ -36,8 +36,7 @@
   void FollowRedirect(
       const std::vector<std::string>& removed_headers,
       const net::HttpRequestHeaders& modified_headers,
-      const net::HttpRequestHeaders& modified_cors_exempt_headers,
-      blink::PreviewsState new_previews_state) override;
+      const net::HttpRequestHeaders& modified_cors_exempt_headers) override;
   bool SetNavigationTimeout(base::TimeDelta timeout) override;
 
   NavigationRequestInfo* request_info() const { return request_info_.get(); }
diff --git a/device/bluetooth/chromeos/bluetooth_utils.cc b/device/bluetooth/chromeos/bluetooth_utils.cc
index 5344309..cd400cf4 100644
--- a/device/bluetooth/chromeos/bluetooth_utils.cc
+++ b/device/bluetooth/chromeos/bluetooth_utils.cc
@@ -73,7 +73,7 @@
 BluetoothAdapter::DeviceList FilterUnknownDevices(
     const BluetoothAdapter::DeviceList& devices) {
 #if BUILDFLAG(IS_CHROMEOS_ASH)
-  if (chromeos::switches::IsUnfilteredBluetoothDevicesEnabled())
+  if (ash::switches::IsUnfilteredBluetoothDevicesEnabled())
     return devices;
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
@@ -168,7 +168,7 @@
     return false;
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
-  if (chromeos::switches::IsUnfilteredBluetoothDevicesEnabled())
+  if (ash::switches::IsUnfilteredBluetoothDevicesEnabled())
     return false;
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
diff --git a/docs/mac_build_instructions.md b/docs/mac_build_instructions.md
index 26ca10fb..a16f9766 100644
--- a/docs/mac_build_instructions.md
+++ b/docs/mac_build_instructions.md
@@ -36,6 +36,7 @@
     itself much, so if you're know what you're doing, you can likely get the
     build working with an older version of macOS as long as you get a new
     version of the macOS SDK on it.
+*   An APFS-formatted volume (this is the default format for macOS volumes).
 
 ## Install `depot_tools`
 
diff --git a/extensions/browser/lazy_context_task_queue.h b/extensions/browser/lazy_context_task_queue.h
index b26fcd8..0eaf1c8e 100644
--- a/extensions/browser/lazy_context_task_queue.h
+++ b/extensions/browser/lazy_context_task_queue.h
@@ -6,7 +6,6 @@
 #define EXTENSIONS_BROWSER_LAZY_CONTEXT_TASK_QUEUE_H_
 
 #include "base/callback.h"
-#include "base/memory/raw_ptr.h"
 #include "extensions/common/extension_id.h"
 #include "url/gurl.h"
 
@@ -32,16 +31,22 @@
   // consumers that add tasks to LazyContextTaskQueue.
   struct ContextInfo {
     const ExtensionId extension_id;
-    const raw_ptr<content::RenderProcessHost> render_process_host;
+    // `render_process_host` is not a raw_ptr<...> for performance reasons
+    // (based on analysis of sampling profiler data).
+    content::RenderProcessHost* const render_process_host;
     const int64_t service_worker_version_id;
     const int worker_thread_id;
     const GURL url;
     // TODO(dbertoni): This needs to be initialized for the Service Worker
     // version of the constructor.
-    const raw_ptr<content::BrowserContext> browser_context = nullptr;
+    // `browser_context` is not a raw_ptr<...> for performance reasons (based on
+    // analysis of sampling profiler data).
+    content::BrowserContext* const browser_context = nullptr;
     // This data member will have a nullptr value for Service Worker-related
     // tasks.
-    const raw_ptr<content::WebContents> web_contents = nullptr;
+    // `web_contents` is not a raw_ptr<...> for performance reasons (based on
+    // analysis of sampling profiler data).
+    content::WebContents* const web_contents = nullptr;
 
     explicit ContextInfo(ExtensionHost* host);
 
diff --git a/extensions/renderer/user_gestures_native_handler.cc b/extensions/renderer/user_gestures_native_handler.cc
index 407879a50..c3e4d3a 100644
--- a/extensions/renderer/user_gestures_native_handler.cc
+++ b/extensions/renderer/user_gestures_native_handler.cc
@@ -40,16 +40,22 @@
 
 void UserGesturesNativeHandler::RunWithUserActivationForTest(
     const v8::FunctionCallbackInfo<v8::Value>& args) {
-  // TODO(lazyboy): This won't work for Service Workers. Address this once we're
-  // certain that we need this for workers.
+  CHECK_EQ(args.Length(), 1);
+  CHECK(args[0]->IsFunction());
+
   if (context()->web_frame()) {
     context()->web_frame()->NotifyUserActivation(
         blink::mojom::UserActivationNotificationType::kTest);
+    context()->SafeCallFunction(v8::Local<v8::Function>::Cast(args[0]), 0,
+                                nullptr);
+  } else if (context()->IsForServiceWorker()) {
+    // Note |scoped_extension_interaction| requires a HandleScope.
+    const v8::HandleScope handle_scope(context()->isolate());
+    const std::unique_ptr<InteractionProvider::Scope> scoped_interaction =
+        ExtensionInteractionProvider::Scope::ForWorker(context()->v8_context());
+    context()->SafeCallFunction(v8::Local<v8::Function>::Cast(args[0]), 0,
+                                nullptr);
   }
-  CHECK_EQ(args.Length(), 1);
-  CHECK(args[0]->IsFunction());
-  context()->SafeCallFunction(v8::Local<v8::Function>::Cast(args[0]), 0,
-                              nullptr);
 }
 
 }  // namespace extensions
diff --git a/gpu/ipc/service/gpu_watchdog_thread_unittest.cc b/gpu/ipc/service/gpu_watchdog_thread_unittest.cc
index e12e09d6..d7d7581c7 100644
--- a/gpu/ipc/service/gpu_watchdog_thread_unittest.cc
+++ b/gpu/ipc/service/gpu_watchdog_thread_unittest.cc
@@ -17,19 +17,33 @@
 namespace gpu {
 
 namespace {
-constexpr auto kGpuWatchdogTimeoutForTesting = base::Milliseconds(1000);
+constexpr auto kGpuWatchdogTimeoutForTesting = base::Milliseconds(5);
 
 // This is the extra time the gpu main/test thread spends after
 // GpuWatchdogTimeout. Theoretically, any extra time such as 1 ms should be
-// enough to trigger the watchdog kill. However, more time is added to fix the
-// flakiness in CQ.
-base::TimeDelta ExtraGPUJobTimeMSForTesting(int milliseconds) {
-  return base::Milliseconds(milliseconds);
-}
+// enough to trigger the watchdog kill. However, it can cause test flakiness
+// when the time is too short.
+#if BUILDFLAG(IS_WIN)
+constexpr auto kExtraGPUJobTimeForTesting = base::Milliseconds(20);
+#else
+constexpr auto kExtraGPUJobTimeForTesting = base::Milliseconds(15);
+#endif
 
-// This task will run for duration_ms milliseconds.
-void SimpleTask(base::TimeDelta duration) {
-  base::PlatformThread::Sleep(duration);
+// On Windows, the gpu watchdog check if the main thread has used the full
+// thread time. We want to detect the case in which the main thread is swapped
+// out by the OS scheduler. The task on windows is simiulated by reading
+// TimeTicks instead of Sleep().
+void SimpleTask(base::TimeDelta duration, base::TimeDelta extra_time) {
+#if BUILDFLAG(IS_WIN)
+  auto start_timetick = base::TimeTicks::Now();
+  do {
+  } while ((base::TimeTicks::Now() - start_timetick) < duration);
+
+  base::PlatformThread::Sleep(extra_time);
+
+#else
+  base::PlatformThread::Sleep(duration + extra_time);
+#endif
 }
 }  // namespace
 
@@ -40,9 +54,12 @@
   void LongTaskWithReportProgress(base::TimeDelta duration,
                                   base::TimeDelta report_delta);
 
+#if BUILDFLAG(IS_ANDROID)
   void LongTaskFromBackgroundToForeground(
       base::TimeDelta duration,
+      base::TimeDelta extra_time,
       base::TimeDelta time_to_switch_to_foreground);
+#endif
 
   // Implements testing::Test
   void SetUp() override;
@@ -52,6 +69,7 @@
   base::test::SingleThreadTaskEnvironment task_environment_;
   base::RunLoop run_loop;
   std::unique_ptr<gpu::GpuWatchdogThread> watchdog_thread_;
+  base::TimeDelta full_thread_time_on_windows_ = base::TimeDelta();
 };
 
 class GpuWatchdogPowerTest : public GpuWatchdogTest {
@@ -59,6 +77,7 @@
   GpuWatchdogPowerTest() {}
 
   void LongTaskOnResume(base::TimeDelta duration,
+                        base::TimeDelta extra_time,
                         base::TimeDelta time_to_power_resume);
 
   // Implements testing::Test
@@ -81,6 +100,11 @@
       /*init_factor=*/kInitFactor,
       /*restart_factor=*/kRestartFactor,
       /*test_mode=*/true, /*thread_name=*/"GpuWatchdog");
+
+#if BUILDFLAG(IS_WIN)
+  full_thread_time_on_windows_ =
+      kGpuWatchdogTimeoutForTesting * kMaxCountOfMoreGpuThreadTimeAllowed;
+#endif
 }
 
 void GpuWatchdogPowerTest::SetUp() {
@@ -103,77 +127,93 @@
   base::TimeTicks end;
 
   do {
-    base::PlatformThread::Sleep(report_delta);
+    SimpleTask(report_delta, /*extra_time=*/base::TimeDelta());
     watchdog_thread_->ReportProgress();
     end = base::TimeTicks::Now();
   } while (end - start <= duration);
 }
 
+#if BUILDFLAG(IS_ANDROID)
 void GpuWatchdogTest::LongTaskFromBackgroundToForeground(
     base::TimeDelta duration,
+    base::TimeDelta extra_time,
     base::TimeDelta time_to_switch_to_foreground) {
   // Chrome is running in the background first.
   watchdog_thread_->OnBackgrounded();
-  base::PlatformThread::Sleep(time_to_switch_to_foreground);
+  SimpleTask(time_to_switch_to_foreground, /*extra_time=*/base::TimeDelta());
   // Now switch Chrome to the foreground after the specified time
   watchdog_thread_->OnForegrounded();
-  base::PlatformThread::Sleep(duration);
+  SimpleTask(duration, extra_time);
 }
+#endif
 
 void GpuWatchdogPowerTest::LongTaskOnResume(
     base::TimeDelta duration,
+    base::TimeDelta extra_time,
     base::TimeDelta time_to_power_resume) {
   // Stay in power suspension mode first.
   power_monitor_source_.GenerateSuspendEvent();
 
-  base::PlatformThread::Sleep(time_to_power_resume);
+  SimpleTask(time_to_power_resume, /*extra_time=*/base::TimeDelta());
 
   // Now wake up on power resume.
   power_monitor_source_.GenerateResumeEvent();
   // Continue the GPU task for the remaining time.
-  base::PlatformThread::Sleep(duration);
+  SimpleTask(duration, extra_time);
+}
+
+// Normal GPU Initialization and Running Task
+TEST_F(GpuWatchdogTest, GpuInitializationComplete) {
+  // Assume GPU initialization takes quarter of WatchdogTimeout time.
+  auto normal_task_time = kGpuWatchdogTimeoutForTesting / 4;
+
+  SimpleTask(normal_task_time, /*extra_time=*/base::TimeDelta());
+  watchdog_thread_->OnInitComplete();
+
+  bool result = watchdog_thread_->IsGpuHangDetectedForTesting();
+  EXPECT_FALSE(result);
 }
 
 // GPU Hang In Initialization
 TEST_F(GpuWatchdogTest, GpuInitializationHang) {
+  auto allowed_time = kGpuWatchdogTimeoutForTesting * (kInitFactor + 1) +
+                      full_thread_time_on_windows_;
+
   // GPU init takes longer than timeout.
-#if BUILDFLAG(IS_WIN)
-  SimpleTask(kGpuWatchdogTimeoutForTesting * kInitFactor +
-             kGpuWatchdogTimeoutForTesting *
-                 kMaxCountOfMoreGpuThreadTimeAllowed +
-             ExtraGPUJobTimeMSForTesting(3000));
-#else
-  SimpleTask(kGpuWatchdogTimeoutForTesting * kInitFactor +
-             ExtraGPUJobTimeMSForTesting(3000));
-#endif
+  SimpleTask(allowed_time, /*extra_time=*/kExtraGPUJobTimeForTesting);
 
   // Gpu hangs. OnInitComplete() is not called
-
   bool result = watchdog_thread_->IsGpuHangDetectedForTesting();
   EXPECT_TRUE(result);
+  // retry on failure.
 }
 
 // Normal GPU Initialization and Running Task
 TEST_F(GpuWatchdogTest, GpuInitializationAndRunningTasks) {
-  // Assume GPU initialization takes 300 milliseconds.
-  SimpleTask(base::Milliseconds(300));
+  // Assume GPU initialization takes quarter of WatchdogTimeout time.
+  auto normal_task_time = kGpuWatchdogTimeoutForTesting / 4;
+  SimpleTask(normal_task_time, /*extra_time=*/base::TimeDelta());
   watchdog_thread_->OnInitComplete();
 
   // Start running GPU tasks. Watchdog function WillProcessTask(),
   // DidProcessTask() and ReportProgress() are tested.
   task_environment_.GetMainThreadTaskRunner()->PostTask(
-      FROM_HERE, base::BindOnce(&SimpleTask, base::Milliseconds(500)));
+      FROM_HERE, base::BindOnce(&SimpleTask, normal_task_time,
+                                /*extra_time=*/base::TimeDelta()));
   task_environment_.GetMainThreadTaskRunner()->PostTask(
-      FROM_HERE, base::BindOnce(&SimpleTask, base::Milliseconds(500)));
+      FROM_HERE, base::BindOnce(&SimpleTask, normal_task_time,
+                                /*extra_time=*/base::TimeDelta()));
 
-  // This long task takes 3000 milliseconds to finish, longer than timeout.
-  // But it reports progress every 500 milliseconds
+  // This long task takes 6X timeout to finish, longer than timeout.
+  // But it reports progress every quarter of kGpuWatchdogTimeoutForTesting
+  // time, so this is an expected normal behavior.
+  auto normal_long_task_time = kGpuWatchdogTimeoutForTesting * 6;
   task_environment_.GetMainThreadTaskRunner()->PostTask(
       FROM_HERE,
-      base::BindOnce(&GpuWatchdogTest::LongTaskWithReportProgress,
-                     base::Unretained(this),
-                     kGpuWatchdogTimeoutForTesting + base::Milliseconds(2000),
-                     base::Milliseconds(500)));
+      base::BindOnce(
+          &GpuWatchdogTest::LongTaskWithReportProgress, base::Unretained(this),
+          normal_long_task_time,
+          /*report_progress_time*/ kGpuWatchdogTimeoutForTesting / 4));
 
   task_environment_.GetMainThreadTaskRunner()->PostTask(FROM_HERE,
                                                         run_loop.QuitClosure());
@@ -190,19 +230,12 @@
   watchdog_thread_->OnInitComplete();
 
   // Start running a GPU task.
-#if BUILDFLAG(IS_WIN)
+  auto allowed_time =
+      kGpuWatchdogTimeoutForTesting * 2 + full_thread_time_on_windows_;
+
   task_environment_.GetMainThreadTaskRunner()->PostTask(
       FROM_HERE,
-      base::BindOnce(&SimpleTask, kGpuWatchdogTimeoutForTesting * 2 +
-                                      kGpuWatchdogTimeoutForTesting *
-                                          kMaxCountOfMoreGpuThreadTimeAllowed +
-                                      ExtraGPUJobTimeMSForTesting(4000)));
-#else
-  task_environment_.GetMainThreadTaskRunner()->PostTask(
-      FROM_HERE,
-      base::BindOnce(&SimpleTask, kGpuWatchdogTimeoutForTesting * 2 +
-                                      ExtraGPUJobTimeMSForTesting(4000)));
-#endif
+      base::BindOnce(&SimpleTask, allowed_time, kExtraGPUJobTimeForTesting));
 
   task_environment_.GetMainThreadTaskRunner()->PostTask(FROM_HERE,
                                                         run_loop.QuitClosure());
@@ -213,22 +246,24 @@
   EXPECT_TRUE(result);
 }
 
+#if BUILDFLAG(IS_ANDROID)
 TEST_F(GpuWatchdogTest, ChromeInBackground) {
   // Chrome starts in the background.
   watchdog_thread_->OnBackgrounded();
 
-  // Gpu init (3000 ms) takes longer than timeout (2000 ms).
-  SimpleTask(kGpuWatchdogTimeoutForTesting * kInitFactor +
-             ExtraGPUJobTimeMSForTesting(1000));
+  // Gpu init takes longer than 6x kGpuWatchdogTimeoutForTesting. This is normal
+  // since Chrome is running in the background.
+  auto normal_long_task_time = kGpuWatchdogTimeoutForTesting * 6;
+  SimpleTask(normal_long_task_time, /*extra_time=*/base::TimeDelta());
 
   // Report GPU init complete.
   watchdog_thread_->OnInitComplete();
 
-  // Run a task that takes longer (3000 milliseconds) than timeout.
+  // Run a task that takes 6x kGpuWatchdogTimeoutForTesting longer.This is
+  // normal since Chrome is running in the background.
   task_environment_.GetMainThreadTaskRunner()->PostTask(
-      FROM_HERE,
-      base::BindOnce(&SimpleTask, kGpuWatchdogTimeoutForTesting * 2 +
-                                      ExtraGPUJobTimeMSForTesting(1000)));
+      FROM_HERE, base::BindOnce(&SimpleTask, normal_long_task_time,
+                                /*extra_time=*/base::TimeDelta()));
   task_environment_.GetMainThreadTaskRunner()->PostTask(FROM_HERE,
                                                         run_loop.QuitClosure());
   run_loop.Run();
@@ -242,29 +277,17 @@
   // Report GPU init complete.
   watchdog_thread_->OnInitComplete();
 
-  // A task stays in the background for 200 milliseconds, and then
-  // switches to the foreground and runs for 6000 milliseconds. This is longer
-  // than the first-time foreground watchdog timeout (2000 ms).
-#if BUILDFLAG(IS_WIN)
+  // A task stays in the background for kGpuWatchdogTimeoutForTesting/4, and
+  // then switches to the foreground and runs longer than the first-time
+  // foreground watchdog timeout allowed.
+  auto allowed_time = kGpuWatchdogTimeoutForTesting * (kRestartFactor + 1);
   task_environment_.GetMainThreadTaskRunner()->PostTask(
       FROM_HERE,
-      base::BindOnce(&GpuWatchdogTest::LongTaskFromBackgroundToForeground,
-                     base::Unretained(this),
-                     /*duration*/ kGpuWatchdogTimeoutForTesting * 2 +
-                         kGpuWatchdogTimeoutForTesting *
-                             kMaxCountOfMoreGpuThreadTimeAllowed +
-                         ExtraGPUJobTimeMSForTesting(4000),
-                     /*time_to_switch_to_foreground*/
-                     base::Milliseconds(200)));
-#else
-  task_environment_.GetMainThreadTaskRunner()->PostTask(
-      FROM_HERE,
-      base::BindOnce(&GpuWatchdogTest::LongTaskFromBackgroundToForeground,
-                     base::Unretained(this),
-                     /*duration*/ kGpuWatchdogTimeoutForTesting * 2 +
-                         ExtraGPUJobTimeMSForTesting(4000),
-                     /*time_to_switch_to_foreground*/ base::Milliseconds(200)));
-#endif
+      base::BindOnce(
+          &GpuWatchdogTest::LongTaskFromBackgroundToForeground,
+          base::Unretained(this), /*duration*/ allowed_time,
+          /*extra_time=*/kExtraGPUJobTimeForTesting,
+          /*time_to_switch_to_foreground*/ kGpuWatchdogTimeoutForTesting / 4));
 
   task_environment_.GetMainThreadTaskRunner()->PostTask(FROM_HERE,
                                                         run_loop.QuitClosure());
@@ -275,15 +298,18 @@
   bool result = watchdog_thread_->IsGpuHangDetectedForTesting();
   EXPECT_TRUE(result);
 }
+#endif
 
 TEST_F(GpuWatchdogTest, GpuInitializationPause) {
-  // Running for 100 ms in the beginning of GPU init.
-  SimpleTask(base::Milliseconds(100));
+  // Running for kGpuWatchdogTimeoutForTesting/4 in the beginning of GPU init.
+  SimpleTask(kGpuWatchdogTimeoutForTesting / 4,
+             /*extra_time=*/base::TimeDelta());
   watchdog_thread_->PauseWatchdog();
 
-  // The Gpu init continues for another (init timeout + 1000) ms after the pause
-  SimpleTask(kGpuWatchdogTimeoutForTesting * kInitFactor +
-             ExtraGPUJobTimeMSForTesting(1000));
+  // The Gpu init continues for another 6x kGpuWatchdogTimeoutForTesting after
+  // the pause. This is normal since watchdog is paused.
+  auto normal_long_task_time = kGpuWatchdogTimeoutForTesting * 6;
+  SimpleTask(normal_long_task_time, /*extra_time=*/base::TimeDelta());
 
   // No GPU hang is detected when the watchdog is paused.
   bool result = watchdog_thread_->IsGpuHangDetectedForTesting();
@@ -291,16 +317,12 @@
 
   // Continue the watchdog now.
   watchdog_thread_->ResumeWatchdog();
-  // The Gpu init continues for (init timeout + 4000) ms.
-#if BUILDFLAG(IS_WIN)
-  SimpleTask(kGpuWatchdogTimeoutForTesting * kInitFactor +
-             kGpuWatchdogTimeoutForTesting *
-                 kMaxCountOfMoreGpuThreadTimeAllowed +
-             ExtraGPUJobTimeMSForTesting(4000));
-#else
-  SimpleTask(kGpuWatchdogTimeoutForTesting * kInitFactor +
-             ExtraGPUJobTimeMSForTesting(4000));
-#endif
+
+  // The Gpu init continues for longer than allowed init time.
+  auto allowed_time = kGpuWatchdogTimeoutForTesting * (kInitFactor + 1) +
+                      full_thread_time_on_windows_;
+
+  SimpleTask(allowed_time, /*extra_time=*/kExtraGPUJobTimeForTesting);
 
   // A GPU hang should be detected.
   result = watchdog_thread_->IsGpuHangDetectedForTesting();
@@ -313,11 +335,11 @@
   // Enter power suspension mode.
   power_monitor_source_.GenerateSuspendEvent();
 
-  // Run a task that takes longer (5000 milliseconds) than timeout.
+  // Run a task that takes 6x kGpuWatchdogTimeoutForTesting.
+  auto normal_long_task_time = kGpuWatchdogTimeoutForTesting * 6;
   task_environment_.GetMainThreadTaskRunner()->PostTask(
-      FROM_HERE,
-      base::BindOnce(&SimpleTask, kGpuWatchdogTimeoutForTesting * 2 +
-                                      ExtraGPUJobTimeMSForTesting(3000)));
+      FROM_HERE, base::BindOnce(&SimpleTask, normal_long_task_time,
+                                /*extra_time=*/base::TimeDelta()));
   task_environment_.GetMainThreadTaskRunner()->PostTask(FROM_HERE,
                                                         run_loop.QuitClosure());
   run_loop.Run();
@@ -331,30 +353,18 @@
 TEST_F(GpuWatchdogPowerTest, GpuOnResumeHang) {
   // watchdog_thread_->OnInitComplete() is called in SetUp
 
-  // This task stays in the suspension mode for 200 milliseconds, and it
-  // wakes up on power resume and then runs for 6000 milliseconds. This is
-  // longer than the watchdog resume timeout (2000 ms).
-#if BUILDFLAG(IS_WIN)
+  // This task stays in the suspension mode for kGpuWatchdogTimeoutForTesting/4,
+  // and it wakes up on power resume and then runs a job that is longer than the
+  // watchdog resume restart timeout.
+  auto allowed_time = kGpuWatchdogTimeoutForTesting * (kRestartFactor + 1) +
+                      full_thread_time_on_windows_;
+
   task_environment_.GetMainThreadTaskRunner()->PostTask(
       FROM_HERE,
       base::BindOnce(
           &GpuWatchdogPowerTest::LongTaskOnResume, base::Unretained(this),
-          /*duration*/ kGpuWatchdogTimeoutForTesting * kRestartFactor +
-              kGpuWatchdogTimeoutForTesting *
-                  kMaxCountOfMoreGpuThreadTimeAllowed +
-              ExtraGPUJobTimeMSForTesting(4000),
-          /*time_to_power_resume*/
-          base::Milliseconds(200)));
-#else
-  task_environment_.GetMainThreadTaskRunner()->PostTask(
-      FROM_HERE,
-      base::BindOnce(
-          &GpuWatchdogPowerTest::LongTaskOnResume, base::Unretained(this),
-          /*duration*/ kGpuWatchdogTimeoutForTesting * kRestartFactor +
-              ExtraGPUJobTimeMSForTesting(4000),
-          /*time_to_power_resume*/
-          base::Milliseconds(200)));
-#endif
+          /*duration*/ allowed_time, /*extra_time=*/kExtraGPUJobTimeForTesting,
+          /*time_to_power_resume*/ kGpuWatchdogTimeoutForTesting / 4));
 
   task_environment_.GetMainThreadTaskRunner()->PostTask(FROM_HERE,
                                                         run_loop.QuitClosure());
diff --git a/headless/BUILD.gn b/headless/BUILD.gn
index 26b0a0e..5921e4c 100644
--- a/headless/BUILD.gn
+++ b/headless/BUILD.gn
@@ -557,8 +557,7 @@
     if (use_v8_context_snapshot) {
       data += [ "$root_out_dir/$v8_context_snapshot_filename" ]
       data_deps += [ "//tools/v8_context_snapshot" ]
-    }
-    if (!use_v8_context_snapshot || include_both_v8_snapshots) {
+    } else {
       data += [ "$root_out_dir/snapshot_blob.bin" ]
     }
   }
diff --git a/infra/config/generated/luci/cr-buildbucket.cfg b/infra/config/generated/luci/cr-buildbucket.cfg
index 04b673e..be51f97 100644
--- a/infra/config/generated/luci/cr-buildbucket.cfg
+++ b/infra/config/generated/luci/cr-buildbucket.cfg
@@ -23969,7 +23969,7 @@
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "luci.recipes.use_python3"
-        value: 25
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -24040,7 +24040,7 @@
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "luci.recipes.use_python3"
-        value: 25
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -24117,7 +24117,7 @@
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "luci.recipes.use_python3"
-        value: 25
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -24188,7 +24188,7 @@
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "luci.recipes.use_python3"
-        value: 25
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -29699,7 +29699,7 @@
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "luci.recipes.use_python3"
-        value: 25
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -30785,7 +30785,7 @@
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "luci.recipes.use_python3"
-        value: 25
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -30856,7 +30856,7 @@
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "luci.recipes.use_python3"
-        value: 25
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -32818,7 +32818,7 @@
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "luci.recipes.use_python3"
-        value: 25
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -32895,7 +32895,7 @@
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "luci.recipes.use_python3"
-        value: 25
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -32966,7 +32966,7 @@
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "luci.recipes.use_python3"
-        value: 25
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -33037,7 +33037,7 @@
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "luci.recipes.use_python3"
-        value: 25
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -33108,7 +33108,7 @@
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "luci.recipes.use_python3"
-        value: 25
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -33179,7 +33179,7 @@
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "luci.recipes.use_python3"
-        value: 25
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -36632,7 +36632,7 @@
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "luci.recipes.use_python3"
-        value: 25
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -36711,7 +36711,7 @@
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "luci.recipes.use_python3"
-        value: 25
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -37547,7 +37547,7 @@
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "luci.recipes.use_python3"
-        value: 25
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -37621,7 +37621,7 @@
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "luci.recipes.use_python3"
-        value: 25
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -37692,7 +37692,7 @@
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "luci.recipes.use_python3"
-        value: 25
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -40828,7 +40828,7 @@
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "luci.recipes.use_python3"
-        value: 25
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -40905,7 +40905,7 @@
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "luci.recipes.use_python3"
-        value: 25
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -40982,7 +40982,7 @@
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "luci.recipes.use_python3"
-        value: 25
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -42137,7 +42137,7 @@
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "luci.recipes.use_python3"
-        value: 25
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -42216,7 +42216,7 @@
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "luci.recipes.use_python3"
-        value: 25
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -42295,7 +42295,7 @@
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "luci.recipes.use_python3"
-        value: 25
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -42374,7 +42374,7 @@
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "luci.recipes.use_python3"
-        value: 25
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -42866,7 +42866,7 @@
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "luci.recipes.use_python3"
-        value: 25
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -42937,7 +42937,7 @@
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "luci.recipes.use_python3"
-        value: 25
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -43008,7 +43008,7 @@
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "luci.recipes.use_python3"
-        value: 25
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -43079,7 +43079,7 @@
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "luci.recipes.use_python3"
-        value: 25
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -44236,7 +44236,7 @@
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "luci.recipes.use_python3"
-        value: 25
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -44307,7 +44307,7 @@
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "luci.recipes.use_python3"
-        value: 25
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -48866,7 +48866,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 25
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -48953,7 +48953,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 25
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -57389,7 +57389,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 25
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -63716,7 +63716,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 25
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -65518,7 +65518,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 25
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -65698,7 +65698,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 25
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -70273,7 +70273,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 25
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -70357,7 +70357,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 25
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -73715,7 +73715,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 25
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -77371,7 +77371,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 25
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -77458,7 +77458,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 25
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -77545,7 +77545,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 25
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -78267,7 +78267,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 25
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -78351,7 +78351,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 25
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -78435,7 +78435,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 25
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -78519,7 +78519,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 25
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
diff --git a/infra/config/recipes.star b/infra/config/recipes.star
index 1ba90de..1b127f39 100644
--- a/infra/config/recipes.star
+++ b/infra/config/recipes.star
@@ -96,16 +96,12 @@
 
 build_recipe(
     name = "recipe:angle_chromium",
-    experiments = {
-        "luci.recipes.use_python3": 25,
-    },
+    use_python3 = True,
 )
 
 build_recipe(
     name = "recipe:angle_chromium_trybot",
-    experiments = {
-        "luci.recipes.use_python3": 25,
-    },
+    use_python3 = True,
 )
 
 build_recipe(
diff --git a/ios/chrome/browser/follow/BUILD.gn b/ios/chrome/browser/follow/BUILD.gn
new file mode 100644
index 0000000..d0d338d
--- /dev/null
+++ b/ios/chrome/browser/follow/BUILD.gn
@@ -0,0 +1,25 @@
+# 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("//ios/web/js_compile.gni")
+
+source_set("follow") {
+  sources = [
+    "rss_link_java_script_feature.h",
+    "rss_link_java_script_feature.mm",
+  ]
+  deps = [
+    ":rss_link_js",
+    "//base",
+    "//ios/web/public/js_messaging",
+  ]
+  configs += [ "//build/config/compiler:enable_arc" ]
+}
+
+js_compile_bundle("rss_link_js") {
+  visibility = [ ":follow" ]
+  closure_entry_point = "__crWeb.rssLink"
+
+  sources = [ "resources/rss_link.js" ]
+}
diff --git a/ios/chrome/browser/follow/OWNERS b/ios/chrome/browser/follow/OWNERS
new file mode 100644
index 0000000..43a61b3
--- /dev/null
+++ b/ios/chrome/browser/follow/OWNERS
@@ -0,0 +1,2 @@
+sczs@chromium.org
+tinazwang@chromium.org
diff --git a/ios/chrome/browser/follow/README.md b/ios/chrome/browser/follow/README.md
new file mode 100644
index 0000000..264fcb6
--- /dev/null
+++ b/ios/chrome/browser/follow/README.md
@@ -0,0 +1,2 @@
+This directory contains non ui code related to the follow operations
+for the following feed feature.
diff --git a/ios/chrome/browser/follow/resources/rss_link.js b/ios/chrome/browser/follow/resources/rss_link.js
new file mode 100644
index 0000000..5a07d49
--- /dev/null
+++ b/ios/chrome/browser/follow/resources/rss_link.js
@@ -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.
+
+goog.module('__crWeb.rssLink');
+
+/**
+ * @fileoverview Functions used to parse RSS links from a web page.
+ */
+(function () {
+
+  __gCrWeb['rssLink'] = {};
+
+  /* Gets RSS links. */
+  __gCrWeb.rssLink.getRSSLinks = function() {
+    const linkTags = document.head.getElementsByTagName('link');
+    const rssLinks = [];
+    for (const linkTag of linkTags) {
+        if (linkTag.rel === 'alternate' ||
+            linkTag.rel === 'service.feed') {
+            const type = linkTag.type;
+            if (type === 'application/rss+xml'||
+                type === 'application/rss+atom' ||
+                type === 'application/atom+xml') {
+                rssLinks.push(linkTag.href);
+            }
+        }
+    }
+    return rssLinks;
+  }
+})();
diff --git a/ios/chrome/browser/follow/rss_link_java_script_feature.h b/ios/chrome/browser/follow/rss_link_java_script_feature.h
new file mode 100644
index 0000000..7c8ba576
--- /dev/null
+++ b/ios/chrome/browser/follow/rss_link_java_script_feature.h
@@ -0,0 +1,43 @@
+// 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 IOS_CHROME_BROWSER_FOLLOW_RSS_LINK_JAVA_SCRIPT_FEATURE_H_
+#define IOS_CHROME_BROWSER_FOLLOW_RSS_LINK_JAVA_SCRIPT_FEATURE_H_
+
+#include <string>
+#include <vector>
+
+#import "base/memory/weak_ptr.h"
+#include "base/no_destructor.h"
+#import "ios/web/public/js_messaging/java_script_feature.h"
+
+/**
+ * Handles JS communication for the web channels feature.
+ */
+class RSSLinkJavaScriptFeature : public web::JavaScriptFeature {
+ public:
+  static RSSLinkJavaScriptFeature* GetInstance();
+
+  // Invokes JS-side handlers to get the RSS links.
+  virtual void GetRSSLinks(
+      web::WebFrame* frame,
+      base::OnceCallback<void(std::vector<std::string>*)> callback);
+
+ private:
+  friend class base::NoDestructor<RSSLinkJavaScriptFeature>;
+
+  RSSLinkJavaScriptFeature();
+  ~RSSLinkJavaScriptFeature() override;
+
+  void HandleResponse(
+      base::OnceCallback<void(std::vector<std::string>*)> callback,
+      const base::Value* response);
+
+  RSSLinkJavaScriptFeature(const RSSLinkJavaScriptFeature&) = delete;
+  RSSLinkJavaScriptFeature& operator=(const RSSLinkJavaScriptFeature&) = delete;
+
+  base::WeakPtrFactory<RSSLinkJavaScriptFeature> weak_ptr_factory_;
+};
+
+#endif  // IOS_CHROME_BROWSER_FOLLOW_RSS_LINK_JAVA_SCRIPT_FEATURE_H_
diff --git a/ios/chrome/browser/follow/rss_link_java_script_feature.mm b/ios/chrome/browser/follow/rss_link_java_script_feature.mm
new file mode 100644
index 0000000..bd958ea
--- /dev/null
+++ b/ios/chrome/browser/follow/rss_link_java_script_feature.mm
@@ -0,0 +1,76 @@
+// 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 "ios/chrome/browser/follow/rss_link_java_script_feature.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+#import "ios/chrome/browser/follow/rss_link_java_script_feature.h"
+
+#include <vector>
+
+#include "base/json/json_reader.h"
+#include "base/strings/sys_string_conversions.h"
+#include "base/values.h"
+#import "ios/web/public/js_messaging/web_frame.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+namespace {
+const char kRSSLinkScript[] = "rss_link_js";
+const char kGetRSSLinkFunction[] = "rssLink.getRSSLinks";
+// The timeout for any JavaScript call in this file.
+const double kJavaScriptExecutionTimeoutInMs = 500.0;
+
+}  // namespace
+
+// static
+RSSLinkJavaScriptFeature* RSSLinkJavaScriptFeature::GetInstance() {
+  static base::NoDestructor<RSSLinkJavaScriptFeature> instance;
+  return instance.get();
+}
+
+RSSLinkJavaScriptFeature::RSSLinkJavaScriptFeature()
+    : JavaScriptFeature(
+          ContentWorld::kAnyContentWorld,
+          {FeatureScript::CreateWithFilename(
+              kRSSLinkScript,
+              FeatureScript::InjectionTime::kDocumentStart,
+              FeatureScript::TargetFrames::kMainFrame,
+              FeatureScript::ReinjectionBehavior::kInjectOncePerWindow)}),
+      weak_ptr_factory_(this) {}
+
+RSSLinkJavaScriptFeature::~RSSLinkJavaScriptFeature() = default;
+
+void RSSLinkJavaScriptFeature::GetRSSLinks(
+    web::WebFrame* frame,
+    base::OnceCallback<void(std::vector<std::string>*)> callback) {
+  CallJavaScriptFunction(
+      frame, kGetRSSLinkFunction, /* parameters= */ {},
+      base::BindOnce(&RSSLinkJavaScriptFeature::HandleResponse,
+                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)),
+      base::Milliseconds(kJavaScriptExecutionTimeoutInMs));
+}
+
+void RSSLinkJavaScriptFeature::HandleResponse(
+    base::OnceCallback<void(std::vector<std::string>*)> callback,
+    const base::Value* response) {
+  if (!response)
+    return;
+
+  if (!response->is_list())
+    return;
+
+  std::vector<std::string> rss_links;
+  // Iterate through all the extracted links and copy
+  // the data from JSON.
+  for (const auto& link : response->GetList()) {
+    rss_links.push_back(*link.GetIfString());
+  }
+  std::move(callback).Run(&rss_links);
+}
diff --git a/ios/chrome/browser/ui/content_suggestions/BUILD.gn b/ios/chrome/browser/ui/content_suggestions/BUILD.gn
index b2cf9a1..4e482c28 100644
--- a/ios/chrome/browser/ui/content_suggestions/BUILD.gn
+++ b/ios/chrome/browser/ui/content_suggestions/BUILD.gn
@@ -66,7 +66,6 @@
     "//ios/chrome/browser/ui/content_suggestions:content_suggestions_ui",
     "//ios/chrome/browser/ui/content_suggestions:content_suggestions_ui_util",
     "//ios/chrome/browser/ui/content_suggestions/cells",
-    "//ios/chrome/browser/ui/content_suggestions/cells:cells_ui",
     "//ios/chrome/browser/ui/content_suggestions/identifier",
     "//ios/chrome/browser/ui/coordinators:chrome_coordinators",
     "//ios/chrome/browser/ui/default_promo:utils",
@@ -148,13 +147,11 @@
     "//ios/chrome/browser/ui/collection_view",
     "//ios/chrome/browser/ui/commands",
     "//ios/chrome/browser/ui/content_suggestions/cells",
-    "//ios/chrome/browser/ui/content_suggestions/cells:cells_ui",
     "//ios/chrome/browser/ui/content_suggestions/identifier",
     "//ios/chrome/browser/ui/elements",
     "//ios/chrome/browser/ui/list_model",
     "//ios/chrome/browser/ui/ntp",
     "//ios/chrome/browser/ui/ntp:logo",
-    "//ios/chrome/browser/ui/ntp_tile_views",
     "//ios/chrome/browser/ui/omnibox:omnibox_internal",
     "//ios/chrome/browser/ui/omnibox:omnibox_popup_shared",
     "//ios/chrome/browser/ui/start_surface:feature_flags",
@@ -194,10 +191,9 @@
     "//ios/chrome/app/strings",
     "//ios/chrome/browser/ui:feature_flags",
     "//ios/chrome/browser/ui/commands",
-    "//ios/chrome/browser/ui/content_suggestions/cells:cells_ui",
+    "//ios/chrome/browser/ui/content_suggestions/cells:constants",
     "//ios/chrome/browser/ui/location_bar:constants",
     "//ios/chrome/browser/ui/ntp",
-    "//ios/chrome/browser/ui/ntp_tile_views:constants",
     "//ios/chrome/browser/ui/start_surface:feature_flags",
     "//ios/chrome/browser/ui/toolbar/public",
     "//ios/chrome/browser/ui/util",
@@ -240,7 +236,6 @@
     "//ios/chrome/browser/ui/commands",
     "//ios/chrome/browser/ui/content_suggestions:content_suggestions_ui",
     "//ios/chrome/browser/ui/content_suggestions/cells",
-    "//ios/chrome/browser/ui/content_suggestions/cells:cells_ui",
     "//ios/chrome/browser/ui/content_suggestions/identifier",
     "//ios/chrome/browser/ui/ntp:logo",
     "//ios/chrome/browser/ui/start_surface:feature_flags",
diff --git a/ios/chrome/browser/ui/content_suggestions/cells/BUILD.gn b/ios/chrome/browser/ui/content_suggestions/cells/BUILD.gn
index 024f8cc5..28bf9f1 100644
--- a/ios/chrome/browser/ui/content_suggestions/cells/BUILD.gn
+++ b/ios/chrome/browser/ui/content_suggestions/cells/BUILD.gn
@@ -4,46 +4,32 @@
 
 source_set("cells") {
   sources = [
-    "content_suggestions_gesture_commands.h",
-    "content_suggestions_most_visited_action_item.h",
-    "content_suggestions_most_visited_action_item.mm",
-    "content_suggestions_most_visited_item.h",
-    "content_suggestions_most_visited_item.mm",
-    "content_suggestions_return_to_recent_tab_item.h",
-    "content_suggestions_return_to_recent_tab_item.mm",
-    "content_suggestions_return_to_recent_tab_view.h",
-    "content_suggestions_return_to_recent_tab_view.mm",
-  ]
-  deps = [
-    ":cells_ui",
-    "//base",
-    "//components/ntp_tiles",
-    "//ios/chrome/app/strings",
-    "//ios/chrome/browser/ui/collection_view",
-    "//ios/chrome/browser/ui/content_suggestions/identifier",
-    "//ios/chrome/browser/ui/ntp_tile_views:constants",
-    "//ios/chrome/browser/ui/table_view/cells:cells_constants",
-    "//ios/chrome/common/ui/colors",
-    "//ios/chrome/common/ui/favicon",
-    "//ios/chrome/common/ui/util",
-    "//ui/base",
-    "//url",
-  ]
-  configs += [ "//build/config/compiler:enable_arc" ]
-}
-
-source_set("cells_ui") {
-  sources = [
     "content_suggestions_header_item.h",
     "content_suggestions_header_item.mm",
     "content_suggestions_most_visited_action_cell.h",
     "content_suggestions_most_visited_action_cell.mm",
+    "content_suggestions_most_visited_action_item.h",
+    "content_suggestions_most_visited_action_item.mm",
     "content_suggestions_most_visited_cell.h",
     "content_suggestions_most_visited_cell.mm",
     "content_suggestions_most_visited_constants.h",
     "content_suggestions_most_visited_constants.mm",
+    "content_suggestions_most_visited_item.h",
+    "content_suggestions_most_visited_item.mm",
+    "content_suggestions_most_visited_tile_view.h",
+    "content_suggestions_most_visited_tile_view.mm",
+    "content_suggestions_return_to_recent_tab_item.h",
+    "content_suggestions_return_to_recent_tab_item.mm",
+    "content_suggestions_return_to_recent_tab_view.h",
+    "content_suggestions_return_to_recent_tab_view.mm",
+    "content_suggestions_shortcut_tile_view.h",
+    "content_suggestions_shortcut_tile_view.mm",
     "content_suggestions_text_item.h",
     "content_suggestions_text_item.mm",
+    "content_suggestions_tile_layout_util.h",
+    "content_suggestions_tile_layout_util.mm",
+    "content_suggestions_tile_view.h",
+    "content_suggestions_tile_view.mm",
     "content_suggestions_whats_new_item.h",
     "content_suggestions_whats_new_item.mm",
     "content_suggestions_whats_new_view.h",
@@ -51,38 +37,62 @@
     "suggested_content.h",
   ]
   deps = [
+    ":constants",
+    "resources:ntp_bookmarks_icon",
+    "resources:ntp_history_icon",
+    "resources:ntp_most_visited_tile",
+    "resources:ntp_readinglist_icon",
+    "resources:ntp_recent_icon",
     "//base",
+    "//components/ntp_tiles",
     "//ios/chrome/app/strings",
     "//ios/chrome/browser/ui:feature_flags",
     "//ios/chrome/browser/ui/collection_view",
     "//ios/chrome/browser/ui/colors",
     "//ios/chrome/browser/ui/content_suggestions:constants",
     "//ios/chrome/browser/ui/content_suggestions:content_suggestions_constant",
+    "//ios/chrome/browser/ui/content_suggestions:content_suggestions_ui_util",
     "//ios/chrome/browser/ui/content_suggestions/identifier",
     "//ios/chrome/browser/ui/material_components",
-    "//ios/chrome/browser/ui/ntp_tile_views",
-    "//ios/chrome/browser/ui/ntp_tile_views:constants",
+    "//ios/chrome/browser/ui/table_view/cells:cells_constants",
     "//ios/chrome/browser/ui/util",
     "//ios/chrome/common",
     "//ios/chrome/common/ui/colors",
     "//ios/chrome/common/ui/favicon",
     "//ios/chrome/common/ui/util",
+    "//ios/chrome/common/ui/util:dynamic_type_util",
+    "//ios/third_party/material_components_ios",
     "//ui/base",
+    "//url",
   ]
   public_deps = [ "//ios/third_party/material_components_ios" ]
   configs += [ "//build/config/compiler:enable_arc" ]
 }
 
+source_set("constants") {
+  configs += [ "//build/config/compiler:enable_arc" ]
+  sources = [
+    "content_suggestions_tile_constants.h",
+    "content_suggestions_tile_constants.mm",
+  ]
+
+  deps = [
+    "//ios/chrome/app/strings:ios_strings_grit",
+    "//ios/third_party/material_components_ios",
+    "//ui/base:base",
+  ]
+}
+
 source_set("unit_tests") {
   testonly = true
   sources = [
     "content_suggestions_header_item_unittest.mm",
     "content_suggestions_most_visited_item_unittest.mm",
+    "content_suggestions_tile_layout_util_unittest.mm",
     "content_suggestions_whats_new_item_unittest.mm",
   ]
   deps = [
     ":cells",
-    ":cells_ui",
     "//base",
     "//ios/chrome/app/strings",
     "//ios/chrome/browser/ui/collection_view",
diff --git a/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_most_visited_action_cell.mm b/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_most_visited_action_cell.mm
index e25a47a..febb062 100644
--- a/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_most_visited_action_cell.mm
+++ b/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_most_visited_action_cell.mm
@@ -5,8 +5,8 @@
 #import "ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_most_visited_action_cell.h"
 
 #import "ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_most_visited_constants.h"
-#import "ios/chrome/browser/ui/ntp_tile_views/ntp_shortcut_tile_view.h"
-#import "ios/chrome/browser/ui/ntp_tile_views/ntp_tile_layout_util.h"
+#import "ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_shortcut_tile_view.h"
+#import "ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_tile_layout_util.h"
 #include "ios/chrome/browser/ui/util/ui_util.h"
 #import "ios/chrome/browser/ui/util/uikit_ui_util.h"
 #import "ios/chrome/common/material_timing.h"
@@ -19,7 +19,7 @@
 
 @interface ContentSuggestionsMostVisitedActionCell ()
 
-@property(nonatomic, strong) NTPShortcutTileView* tileView;
+@property(nonatomic, strong) ContentSuggestionsShortcutTileView* tileView;
 
 @end
 
@@ -30,7 +30,8 @@
 - (instancetype)initWithFrame:(CGRect)frame {
   self = [super initWithFrame:frame];
   if (self) {
-    _tileView = [[NTPShortcutTileView alloc] initWithFrame:frame];
+    _tileView =
+        [[ContentSuggestionsShortcutTileView alloc] initWithFrame:frame];
     _tileView.translatesAutoresizingMaskIntoConstraints = NO;
     [self.contentView addSubview:_tileView];
     AddSameConstraints(self.contentView, _tileView);
diff --git a/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_most_visited_action_item.h b/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_most_visited_action_item.h
index 6a14b294e..c5b7b3b 100644
--- a/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_most_visited_action_item.h
+++ b/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_most_visited_action_item.h
@@ -8,8 +8,8 @@
 #import <UIKit/UIKit.h>
 
 #import "ios/chrome/browser/ui/collection_view/cells/collection_view_item.h"
+#import "ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_tile_constants.h"
 #import "ios/chrome/browser/ui/content_suggestions/cells/suggested_content.h"
-#import "ios/chrome/browser/ui/ntp_tile_views/ntp_tile_constants.h"
 
 // Item containing a most visited action button. These buttons belong to the
 // collection section as most visited items, but have static placement (the last
diff --git a/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_most_visited_cell.mm b/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_most_visited_cell.mm
index c29cc8c3..e5aa393a 100644
--- a/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_most_visited_cell.mm
+++ b/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_most_visited_cell.mm
@@ -4,10 +4,10 @@
 
 #import "ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_most_visited_cell.h"
 
-#import "ios/chrome/browser/ui/ntp_tile_views/ntp_most_visited_tile_view.h"
+#import "ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_most_visited_tile_view.h"
 
 #import "ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_most_visited_constants.h"
-#import "ios/chrome/browser/ui/ntp_tile_views/ntp_tile_layout_util.h"
+#import "ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_tile_layout_util.h"
 #include "ios/chrome/browser/ui/util/ui_util.h"
 #import "ios/chrome/common/material_timing.h"
 #import "ios/chrome/common/ui/favicon/favicon_view.h"
@@ -19,7 +19,8 @@
 
 @interface ContentSuggestionsMostVisitedCell ()
 
-@property(nonatomic, strong) NTPMostVisitedTileView* mostVisitedTile;
+@property(nonatomic, strong)
+    ContentSuggestionsMostVisitedTileView* mostVisitedTile;
 
 @end
 
@@ -30,7 +31,8 @@
 - (instancetype)initWithFrame:(CGRect)frame {
   self = [super initWithFrame:frame];
   if (self) {
-    _mostVisitedTile = [[NTPMostVisitedTileView alloc] initWithFrame:frame];
+    _mostVisitedTile =
+        [[ContentSuggestionsMostVisitedTileView alloc] initWithFrame:frame];
     [self.contentView addSubview:_mostVisitedTile];
     _mostVisitedTile.translatesAutoresizingMaskIntoConstraints = NO;
     AddSameConstraints(self.contentView, _mostVisitedTile);
diff --git a/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_most_visited_tile_view.h b/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_most_visited_tile_view.h
new file mode 100644
index 0000000..ca6d24d8
--- /dev/null
+++ b/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_most_visited_tile_view.h
@@ -0,0 +1,29 @@
+// 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.
+
+#ifndef IOS_CHROME_BROWSER_UI_CONTENT_SUGGESTIONS_CELLS_CONTENT_SUGGESTIONS_MOST_VISITED_TILE_VIEW_H_
+#define IOS_CHROME_BROWSER_UI_CONTENT_SUGGESTIONS_CELLS_CONTENT_SUGGESTIONS_MOST_VISITED_TILE_VIEW_H_
+
+#import "ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_tile_view.h"
+
+@class ContentSuggestionsMostVisitedItem;
+@class FaviconView;
+
+// NTP Tile representing a most visited website. Displays a favicon and a title.
+@interface ContentSuggestionsMostVisitedTileView : ContentSuggestionsTileView
+
+// Initializes and configures the view with |config|.
+- (instancetype)initWithConfiguration:
+    (ContentSuggestionsMostVisitedItem*)config;
+
+// FaviconView displaying the favicon.
+@property(nonatomic, strong, readonly) FaviconView* faviconView;
+
+// Configuration for this view.
+@property(nonatomic, strong, readonly)
+    ContentSuggestionsMostVisitedItem* config;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_UI_CONTENT_SUGGESTIONS_CELLS_CONTENT_SUGGESTIONS_MOST_VISITED_TILE_VIEW_H_
diff --git a/ios/chrome/browser/ui/ntp_tile_views/ntp_most_visited_tile_view.mm b/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_most_visited_tile_view.mm
similarity index 62%
rename from ios/chrome/browser/ui/ntp_tile_views/ntp_most_visited_tile_view.mm
rename to ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_most_visited_tile_view.mm
index 6dd64cd3..0bc8d86d 100644
--- a/ios/chrome/browser/ui/ntp_tile_views/ntp_most_visited_tile_view.mm
+++ b/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_most_visited_tile_view.mm
@@ -2,8 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ios/chrome/browser/ui/ntp_tile_views/ntp_most_visited_tile_view.h"
+#import "ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_most_visited_tile_view.h"
 
+#import "ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_most_visited_item.h"
 #import "ios/chrome/common/ui/favicon/favicon_view.h"
 #import "ios/chrome/common/ui/util/constraints_ui_util.h"
 
@@ -11,7 +12,7 @@
 #error "This file requires ARC support."
 #endif
 
-@implementation NTPMostVisitedTileView
+@implementation ContentSuggestionsMostVisitedTileView
 
 - (instancetype)initWithFrame:(CGRect)frame {
   self = [super initWithFrame:frame];
@@ -31,4 +32,16 @@
   return self;
 }
 
+- (instancetype)initWithConfiguration:
+    (ContentSuggestionsMostVisitedItem*)config {
+  self = [self initWithFrame:CGRectZero];
+  if (self) {
+    self.titleLabel.text = config.title;
+    self.accessibilityLabel = config.title;
+    [_faviconView configureWithAttributes:config.attributes];
+    _config = config;
+  }
+  return self;
+}
+
 @end
diff --git a/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_return_to_recent_tab_view.h b/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_return_to_recent_tab_view.h
index ffc6914..3c5fdab 100644
--- a/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_return_to_recent_tab_view.h
+++ b/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_return_to_recent_tab_view.h
@@ -7,9 +7,15 @@
 
 #import <UIKit/UIKit.h>
 
+@class ContentSuggestionsReturnToRecentTabItem;
+
 // View for the Return To Recent Tab tile.
 @interface ContentSuggestionsReturnToRecentTabView : UIView
 
+// Initializes and configures the view with |config|.
+- (instancetype)initWithConfiguration:
+    (ContentSuggestionsReturnToRecentTabItem*)config;
+
 // Favicon image.
 @property(nonatomic, strong) UIImageView* iconImageView;
 
diff --git a/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_return_to_recent_tab_view.mm b/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_return_to_recent_tab_view.mm
index a6d443d..0308a29 100644
--- a/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_return_to_recent_tab_view.mm
+++ b/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_return_to_recent_tab_view.mm
@@ -4,6 +4,7 @@
 
 #import "ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_return_to_recent_tab_view.h"
 
+#import "ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_return_to_recent_tab_item.h"
 #import "ios/chrome/browser/ui/table_view/cells/table_view_cells_constants.h"
 #import "ios/chrome/common/ui/colors/semantic_color_names.h"
 #include "ui/base/l10n/l10n_util.h"
@@ -92,6 +93,21 @@
   return self;
 }
 
+- (instancetype)initWithConfiguration:
+    (ContentSuggestionsReturnToRecentTabItem*)config {
+  self = [self initWithFrame:CGRectZero];
+  if (self) {
+    self.titleLabel.text = config.title;
+    self.subtitleLabel.text = config.subtitle;
+    self.accessibilityLabel = config.title;
+    self.iconImageView.image = config.icon;
+    if (!config.icon) {
+      self.iconImageView.hidden = YES;
+    }
+  }
+  return self;
+}
+
 - (void)traitCollectionDidChange:(UITraitCollection*)previousTraitCollection {
   [super traitCollectionDidChange:previousTraitCollection];
   if (self.traitCollection.userInterfaceStyle !=
diff --git a/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_shortcut_tile_view.h b/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_shortcut_tile_view.h
new file mode 100644
index 0000000..e5c291c
--- /dev/null
+++ b/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_shortcut_tile_view.h
@@ -0,0 +1,35 @@
+// 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.
+
+#ifndef IOS_CHROME_BROWSER_UI_CONTENT_SUGGESTIONS_CELLS_CONTENT_SUGGESTIONS_SHORTCUT_TILE_VIEW_H_
+#define IOS_CHROME_BROWSER_UI_CONTENT_SUGGESTIONS_CELLS_CONTENT_SUGGESTIONS_SHORTCUT_TILE_VIEW_H_
+
+#import "ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_tile_view.h"
+
+@class ContentSuggestionsMostVisitedActionItem;
+
+// A tile view displaying a collection shortcut. Accepts a simple icon and
+// optionally supports a badge, for example for reading list new item count.
+@interface ContentSuggestionsShortcutTileView : ContentSuggestionsTileView
+
+// Initializes and configures the view with |config|.
+- (instancetype)initWithConfiguration:
+    (ContentSuggestionsMostVisitedActionItem*)config;
+
+// View for action icon.
+@property(nonatomic, strong, readonly) UIImageView* iconView;
+
+// Container view for |countLabel|.
+@property(nonatomic, strong, readonly) UIView* countContainer;
+
+// Number shown in circle by top trailing side of cell.
+@property(nonatomic, strong, readonly) UILabel* countLabel;
+
+// Configuration for this view.
+@property(nonatomic, strong, readonly)
+    ContentSuggestionsMostVisitedActionItem* config;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_UI_CONTENT_SUGGESTIONS_CELLS_CONTENT_SUGGESTIONS_SHORTCUT_TILE_VIEW_H_
diff --git a/ios/chrome/browser/ui/ntp_tile_views/ntp_shortcut_tile_view.mm b/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_shortcut_tile_view.mm
similarity index 73%
rename from ios/chrome/browser/ui/ntp_tile_views/ntp_shortcut_tile_view.mm
rename to ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_shortcut_tile_view.mm
index 5c60b410..95ef1fe1 100644
--- a/ios/chrome/browser/ui/ntp_tile_views/ntp_shortcut_tile_view.mm
+++ b/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_shortcut_tile_view.mm
@@ -2,10 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ios/chrome/browser/ui/ntp_tile_views/ntp_shortcut_tile_view.h"
+#import "ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_shortcut_tile_view.h"
 
 #import <MaterialComponents/MaterialTypography.h>
 
+#import "ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_most_visited_action_item.h"
 #import "ios/chrome/common/ui/colors/semantic_color_names.h"
 #import "ios/chrome/common/ui/util/constraints_ui_util.h"
 
@@ -21,7 +22,7 @@
 
 }  // namespace
 
-@implementation NTPShortcutTileView
+@implementation ContentSuggestionsShortcutTileView
 @synthesize countLabel = _countLabel;
 
 - (instancetype)initWithFrame:(CGRect)frame {
@@ -42,6 +43,27 @@
   return self;
 }
 
+- (instancetype)initWithConfiguration:
+    (ContentSuggestionsMostVisitedActionItem*)config {
+  self = [self initWithFrame:CGRectZero];
+  if (self) {
+    self.accessibilityCustomActions = nil;
+    self.titleLabel.text = config.title;
+    self.accessibilityLabel = config.accessibilityLabel.length
+                                  ? config.accessibilityLabel
+                                  : config.title;
+    // The accessibilityUserInputLabel should just be the title, with nothing
+    // extra from the accessibilityLabel.
+    self.accessibilityUserInputLabels = @[ config.title ];
+    _iconView.image =
+        ImageForCollectionShortcutType(config.collectionShortcutType);
+      _countContainer.hidden = !config.count;
+      _countLabel.text = [@(config.count) stringValue];
+    _config = config;
+  }
+  return self;
+}
+
 - (UILabel*)countLabel {
   if (!_countLabel) {
     _countContainer = [[UIView alloc] init];
diff --git a/ios/chrome/browser/ui/ntp_tile_views/ntp_tile_constants.h b/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_tile_constants.h
similarity index 78%
rename from ios/chrome/browser/ui/ntp_tile_views/ntp_tile_constants.h
rename to ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_tile_constants.h
index 1a5e07b..7ba6a05 100644
--- a/ios/chrome/browser/ui/ntp_tile_views/ntp_tile_constants.h
+++ b/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_tile_constants.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 IOS_CHROME_BROWSER_UI_NTP_TILE_VIEWS_NTP_TILE_CONSTANTS_H_
-#define IOS_CHROME_BROWSER_UI_NTP_TILE_VIEWS_NTP_TILE_CONSTANTS_H_
+#ifndef IOS_CHROME_BROWSER_UI_CONTENT_SUGGESTIONS_CELLS_CONTENT_SUGGESTIONS_TILE_CONSTANTS_H_
+#define IOS_CHROME_BROWSER_UI_CONTENT_SUGGESTIONS_CELLS_CONTENT_SUGGESTIONS_TILE_CONSTANTS_H_
 
 #import <UIKit/UIKit.h>
 
@@ -26,4 +26,4 @@
 // for a reading list tile with no badge.
 NSString* AccessibilityLabelForReadingListCellWithCount(int count);
 
-#endif  // IOS_CHROME_BROWSER_UI_NTP_TILE_VIEWS_NTP_TILE_CONSTANTS_H_
+#endif  // IOS_CHROME_BROWSER_UI_CONTENT_SUGGESTIONS_CELLS_CONTENT_SUGGESTIONS_TILE_CONSTANTS_H_
diff --git a/ios/chrome/browser/ui/ntp_tile_views/ntp_tile_constants.mm b/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_tile_constants.mm
similarity index 95%
rename from ios/chrome/browser/ui/ntp_tile_views/ntp_tile_constants.mm
rename to ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_tile_constants.mm
index 4cb16c18..5a43f59 100644
--- a/ios/chrome/browser/ui/ntp_tile_views/ntp_tile_constants.mm
+++ b/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_tile_constants.mm
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ios/chrome/browser/ui/ntp_tile_views/ntp_tile_constants.h"
+#import "ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_tile_constants.h"
 
 #include "base/notreached.h"
 #include "base/strings/sys_string_conversions.h"
diff --git a/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_tile_layout_util.h b/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_tile_layout_util.h
new file mode 100644
index 0000000..0a27a4a
--- /dev/null
+++ b/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_tile_layout_util.h
@@ -0,0 +1,37 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_UI_CONTENT_SUGGESTIONS_CELLS_CONTENT_SUGGESTIONS_TILE_LAYOUT_UTIL_H_
+#define IOS_CHROME_BROWSER_UI_CONTENT_SUGGESTIONS_CELLS_CONTENT_SUGGESTIONS_TILE_LAYOUT_UTIL_H_
+
+#import <UIKit/UIKit.h>
+
+// Vertical spacing between rows of tiles.
+extern const int kContentSuggestionsTilesVerticalSpacing;
+// Vertical spacing between columns of tiles.
+extern const int kContentSuggestionsTilesHorizontalSpacingRegular;
+extern const int kContentSuggestionsTilesHorizontalSpacingCompact;
+
+// For font size < UIContentSizeCategoryExtraExtraExtraLarge.
+extern const CGSize kContentSuggestionsTileViewSizeSmall;
+// For font size == UIContentSizeCategoryExtraExtraExtraLarge.
+extern const CGSize kContentSuggestionsTileViewSizeMedium;
+// For font size == UIContentSizeCategoryAccessibilityMedium.
+extern const CGSize kContentSuggestionsTileViewSizeLarge;
+// For font size > UIContentSizeCategoryAccessibilityMedium.
+extern const CGSize kContentSuggestionsTileViewSizeExtraLarge;
+
+// Returns the vertical spacing between columns of tiles under
+// |trait_collection|.
+CGFloat ContentSuggestionsTilesHorizontalSpacing(UITraitCollection* trait_collection);
+
+// Returns the size of most visited cell based on |category|.
+CGSize MostVisitedCellSize(UIContentSizeCategory category);
+
+// Returns x-offset in order to have the tiles centered in a view with a
+// |width| under |environment|.
+CGFloat CenteredTilesMarginForWidth(UITraitCollection* trait_collection,
+                                    CGFloat width);
+
+#endif  // IOS_CHROME_BROWSER_UI_CONTENT_SUGGESTIONS_CELLS_CONTENT_SUGGESTIONS_TILE_LAYOUT_UTIL_H_
diff --git a/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_tile_layout_util.mm b/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_tile_layout_util.mm
new file mode 100644
index 0000000..18b8edc
--- /dev/null
+++ b/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_tile_layout_util.mm
@@ -0,0 +1,67 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_tile_layout_util.h"
+
+#include "base/notreached.h"
+#import "ios/chrome/browser/ui/util/uikit_ui_util.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+const int kContentSuggestionsTilesVerticalSpacing = 16;
+const int kContentSuggestionsTilesHorizontalSpacingRegular = 19;
+const int kContentSuggestionsTilesHorizontalSpacingCompact = 5;
+
+const CGSize kContentSuggestionsTileViewSizeSmall = {/*width=*/73, /*height=*/100};
+const CGSize kContentSuggestionsTileViewSizeMedium = {/*width=*/73, /*height=*/112};
+const CGSize kContentSuggestionsTileViewSizeLarge = {/*width=*/110, /*height=*/140};
+const CGSize kContentSuggestionsTileViewSizeExtraLarge = {/*width=*/146, /*height=*/150};
+
+namespace {
+// Display at most 4 tiles per row.
+const int kMaxNumberOfTilesPerRow = 4;
+}
+
+CGFloat ContentSuggestionsTilesHorizontalSpacing(UITraitCollection* trait_collection) {
+  return (trait_collection.horizontalSizeClass !=
+              UIUserInterfaceSizeClassCompact &&
+          trait_collection.verticalSizeClass != UIUserInterfaceSizeClassCompact)
+             ? kContentSuggestionsTilesHorizontalSpacingRegular
+             : kContentSuggestionsTilesHorizontalSpacingCompact;
+}
+
+CGSize MostVisitedCellSize(UIContentSizeCategory category) {
+  NSComparisonResult result = UIContentSizeCategoryCompareToCategory(
+      category, UIContentSizeCategoryAccessibilityMedium);
+  switch (result) {
+    case NSOrderedAscending:
+      return ([category
+                 isEqualToString:UIContentSizeCategoryExtraExtraExtraLarge])
+                 ? kContentSuggestionsTileViewSizeMedium
+                 : kContentSuggestionsTileViewSizeSmall;
+    case NSOrderedSame:
+      return kContentSuggestionsTileViewSizeLarge;
+    case NSOrderedDescending:
+      return kContentSuggestionsTileViewSizeExtraLarge;
+  }
+}
+
+CGFloat CenteredTilesMarginForWidth(UITraitCollection* trait_collection,
+                                    CGFloat width) {
+  CGFloat horizontalSpace = ContentSuggestionsTilesHorizontalSpacing(trait_collection);
+  CGSize cellSize =
+      MostVisitedCellSize(trait_collection.preferredContentSizeCategory);
+  for (int columns = kMaxNumberOfTilesPerRow; columns > 0; --columns) {
+    CGFloat whitespace =
+        width - (columns * cellSize.width) - ((columns - 1) * horizontalSpace);
+    CGFloat margin = AlignValueToPixel(whitespace / 2);
+    if (margin >= horizontalSpace) {
+      return margin;
+    }
+  }
+  NOTREACHED();
+  return 0;
+}
diff --git a/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_tile_layout_util_unittest.mm b/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_tile_layout_util_unittest.mm
new file mode 100644
index 0000000..c44d928
--- /dev/null
+++ b/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_tile_layout_util_unittest.mm
@@ -0,0 +1,126 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_tile_layout_util.h"
+
+#import <UIKit/UIKit.h>
+
+#include "testing/platform_test.h"
+#import "third_party/ocmock/OCMock/OCMock.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+using ContentSuggestionsTileLayoutUtilTest = PlatformTest;
+
+// Tests that MostVisitedCellSize returns correct size for all content size
+// categories.
+TEST_F(ContentSuggestionsTileLayoutUtilTest, MostVisitedCellSize) {
+  EXPECT_TRUE(
+      CGSizeEqualToSize(kContentSuggestionsTileViewSizeSmall,
+                        MostVisitedCellSize(UIContentSizeCategoryUnspecified)));
+  EXPECT_TRUE(
+      CGSizeEqualToSize(kContentSuggestionsTileViewSizeSmall,
+                        MostVisitedCellSize(UIContentSizeCategoryExtraSmall)));
+  EXPECT_TRUE(CGSizeEqualToSize(
+      kContentSuggestionsTileViewSizeSmall, MostVisitedCellSize(UIContentSizeCategorySmall)));
+  EXPECT_TRUE(CGSizeEqualToSize(
+      kContentSuggestionsTileViewSizeSmall, MostVisitedCellSize(UIContentSizeCategoryMedium)));
+  EXPECT_TRUE(CGSizeEqualToSize(
+      kContentSuggestionsTileViewSizeSmall, MostVisitedCellSize(UIContentSizeCategoryLarge)));
+  EXPECT_TRUE(
+      CGSizeEqualToSize(kContentSuggestionsTileViewSizeSmall,
+                        MostVisitedCellSize(UIContentSizeCategoryExtraLarge)));
+  EXPECT_TRUE(CGSizeEqualToSize(
+      kContentSuggestionsTileViewSizeSmall,
+      MostVisitedCellSize(UIContentSizeCategoryExtraExtraLarge)));
+  EXPECT_TRUE(CGSizeEqualToSize(
+      kContentSuggestionsTileViewSizeMedium,
+      MostVisitedCellSize(UIContentSizeCategoryExtraExtraExtraLarge)));
+  EXPECT_TRUE(CGSizeEqualToSize(
+      kContentSuggestionsTileViewSizeLarge,
+      MostVisitedCellSize(UIContentSizeCategoryAccessibilityMedium)));
+  EXPECT_TRUE(CGSizeEqualToSize(
+      kContentSuggestionsTileViewSizeExtraLarge,
+      MostVisitedCellSize(UIContentSizeCategoryAccessibilityLarge)));
+  EXPECT_TRUE(CGSizeEqualToSize(
+      kContentSuggestionsTileViewSizeExtraLarge,
+      MostVisitedCellSize(UIContentSizeCategoryAccessibilityExtraLarge)));
+  EXPECT_TRUE(CGSizeEqualToSize(
+      kContentSuggestionsTileViewSizeExtraLarge,
+      MostVisitedCellSize(UIContentSizeCategoryAccessibilityExtraExtraLarge)));
+  EXPECT_TRUE(CGSizeEqualToSize(
+      kContentSuggestionsTileViewSizeExtraLarge,
+      MostVisitedCellSize(
+          UIContentSizeCategoryAccessibilityExtraExtraExtraLarge)));
+}
+
+// Tests that CenteredTilesMarginForWidth works under various environment.
+TEST_F(ContentSuggestionsTileLayoutUtilTest, CenteredTilesMarginForWidth) {
+  // Set up Regular size class and Large font size.
+  UITraitCollection* trait_collection =
+      [UITraitCollection traitCollectionWithTraitsFromCollections:@[
+        [UITraitCollection traitCollectionWithHorizontalSizeClass:
+                               UIUserInterfaceSizeClassRegular],
+        [UITraitCollection traitCollectionWithPreferredContentSizeCategory:
+                               UIContentSizeCategoryLarge]
+      ]];
+
+  // Display 4 columns on very big screen.
+  EXPECT_EQ(200, CenteredTilesMarginForWidth(
+                     trait_collection,
+                     kContentSuggestionsTileViewSizeSmall.width * 4 +
+                         kContentSuggestionsTilesHorizontalSpacingRegular * 3 + 200 * 2));
+  // Display 4 columns on normal screen.
+  EXPECT_EQ(20, CenteredTilesMarginForWidth(
+                    trait_collection,
+                    kContentSuggestionsTileViewSizeSmall.width * 4 +
+                        kContentSuggestionsTilesHorizontalSpacingRegular * 3 + 20 * 2));
+  // Display 3 columns.
+  EXPECT_EQ(20, CenteredTilesMarginForWidth(
+                    trait_collection,
+                    kContentSuggestionsTileViewSizeSmall.width * 3 +
+                        kContentSuggestionsTilesHorizontalSpacingRegular * 2 + 20 * 2));
+  // Display 2 columns.
+  EXPECT_EQ(20, CenteredTilesMarginForWidth(
+                    trait_collection,
+                    kContentSuggestionsTileViewSizeSmall.width * 2 +
+                        kContentSuggestionsTilesHorizontalSpacingRegular * 1 + 20 * 2));
+  // Display 1 columns.
+  EXPECT_EQ(20, CenteredTilesMarginForWidth(
+                    trait_collection,
+                    kContentSuggestionsTileViewSizeSmall.width * 1 +
+                        kContentSuggestionsTilesHorizontalSpacingRegular * 0 + 20 * 2));
+
+  // Set up Compact size class and Accessibility Large font size.
+  trait_collection =
+      [UITraitCollection traitCollectionWithTraitsFromCollections:@[
+        [UITraitCollection traitCollectionWithHorizontalSizeClass:
+                               UIUserInterfaceSizeClassCompact],
+        [UITraitCollection traitCollectionWithPreferredContentSizeCategory:
+                               UIContentSizeCategoryAccessibilityLarge]
+      ]];
+
+  // Display 4 columns.
+  EXPECT_EQ(20, CenteredTilesMarginForWidth(
+                    trait_collection,
+                    kContentSuggestionsTileViewSizeExtraLarge.width * 4 +
+                        kContentSuggestionsTilesHorizontalSpacingCompact * 3 + 20 * 2));
+  // Display 3 columns.
+  EXPECT_EQ(20, CenteredTilesMarginForWidth(
+                    trait_collection,
+                    kContentSuggestionsTileViewSizeExtraLarge.width * 3 +
+                        kContentSuggestionsTilesHorizontalSpacingCompact * 2 + 20 * 2));
+  // Display 2 columns.
+  EXPECT_EQ(20, CenteredTilesMarginForWidth(
+                    trait_collection,
+                    kContentSuggestionsTileViewSizeExtraLarge.width * 2 +
+                        kContentSuggestionsTilesHorizontalSpacingCompact * 1 + 20 * 2));
+  // Display 1 columns.
+  EXPECT_EQ(20, CenteredTilesMarginForWidth(
+                    trait_collection,
+                    kContentSuggestionsTileViewSizeExtraLarge.width * 1 +
+                        kContentSuggestionsTilesHorizontalSpacingCompact * 0 + 20 * 2));
+}
diff --git a/ios/chrome/browser/ui/ntp_tile_views/ntp_tile_view.h b/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_tile_view.h
similarity index 62%
rename from ios/chrome/browser/ui/ntp_tile_views/ntp_tile_view.h
rename to ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_tile_view.h
index 98cc983..92062ba 100644
--- a/ios/chrome/browser/ui/ntp_tile_views/ntp_tile_view.h
+++ b/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_tile_view.h
@@ -2,15 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef IOS_CHROME_BROWSER_UI_NTP_TILE_VIEWS_NTP_TILE_VIEW_H_
-#define IOS_CHROME_BROWSER_UI_NTP_TILE_VIEWS_NTP_TILE_VIEW_H_
+#ifndef IOS_CHROME_BROWSER_UI_CONTENT_SUGGESTIONS_CELLS_CONTENT_SUGGESTIONS_TILE_VIEW_H_
+#define IOS_CHROME_BROWSER_UI_CONTENT_SUGGESTIONS_CELLS_CONTENT_SUGGESTIONS_TILE_VIEW_H_
 
 #import <UIKit/UIKit.h>
 
-// A generic NTP tile view. Provides a title label and an image container on a
+// A generic Content Suggestions tile view. Provides a title label and an image container on a
 // squircle-shaped background. Concrete subclasses of this are used to display
 // most visited tiles and shortcut tiles on NTP and other places.
-@interface NTPTileView : UIView <UIPointerInteractionDelegate>
+@interface ContentSuggestionsTileView : UIView <UIPointerInteractionDelegate>
 
 // Container for the image view. Used in subclasses.
 @property(nonatomic, strong, readonly, nonnull) UIView* imageContainerView;
@@ -24,4 +24,4 @@
 
 @end
 
-#endif  // IOS_CHROME_BROWSER_UI_NTP_TILE_VIEWS_NTP_TILE_VIEW_H_
+#endif  // IOS_CHROME_BROWSER_UI_CONTENT_SUGGESTIONS_CELLS_CONTENT_SUGGESTIONS_TILE_VIEW_H_
diff --git a/ios/chrome/browser/ui/ntp_tile_views/ntp_tile_view.mm b/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_tile_view.mm
similarity index 93%
rename from ios/chrome/browser/ui/ntp_tile_views/ntp_tile_view.mm
rename to ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_tile_view.mm
index a5cdab4..96f0f89 100644
--- a/ios/chrome/browser/ui/ntp_tile_views/ntp_tile_view.mm
+++ b/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_tile_view.mm
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ios/chrome/browser/ui/ntp_tile_views/ntp_tile_view.h"
+#import "ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_tile_view.h"
 
 #import "ios/chrome/browser/ui/util/dynamic_type_util.h"
 #import "ios/chrome/common/ui/colors/semantic_color_names.h"
@@ -22,14 +22,14 @@
 
 }  // namespace
 
-@interface NTPTileView ()
+@interface ContentSuggestionsTileView ()
 // Hold onto the created interaction for pointer support so it can be removed
 // when the view goes away.
 @property(nonatomic, strong)
     UIPointerInteraction* pointerInteraction API_AVAILABLE(ios(13.4));
 @end
 
-@implementation NTPTileView
+@implementation ContentSuggestionsTileView
 
 - (instancetype)initWithFrame:(CGRect)frame {
   self = [super initWithFrame:frame];
@@ -69,9 +69,9 @@
     UIView* containerView = backgroundView;
 
     ApplyVisualConstraintsWithMetrics(
-        @[ @"V:|[container]-(space)-[title]", @"H:|[title]|" ],
+        @[ @"V:|[container]-(space)-[title]|", @"H:|[title]|" ],
         @{@"container" : containerView, @"title" : _titleLabel},
-        @{ @"space" : @(kSpaceIconTitle) });
+        @{@"space" : @(kSpaceIconTitle)});
 
     _imageBackgroundView = backgroundView;
 
diff --git a/ios/chrome/browser/ui/ntp_tile_views/resources/BUILD.gn b/ios/chrome/browser/ui/content_suggestions/cells/resources/BUILD.gn
similarity index 100%
rename from ios/chrome/browser/ui/ntp_tile_views/resources/BUILD.gn
rename to ios/chrome/browser/ui/content_suggestions/cells/resources/BUILD.gn
diff --git a/ios/chrome/browser/ui/ntp_tile_views/resources/ntp_bookmarks_icon.imageset/Contents.json b/ios/chrome/browser/ui/content_suggestions/cells/resources/ntp_bookmarks_icon.imageset/Contents.json
similarity index 100%
rename from ios/chrome/browser/ui/ntp_tile_views/resources/ntp_bookmarks_icon.imageset/Contents.json
rename to ios/chrome/browser/ui/content_suggestions/cells/resources/ntp_bookmarks_icon.imageset/Contents.json
diff --git a/ios/chrome/browser/ui/ntp_tile_views/resources/ntp_bookmarks_icon.imageset/ntp_bookmarks_icon.png b/ios/chrome/browser/ui/content_suggestions/cells/resources/ntp_bookmarks_icon.imageset/ntp_bookmarks_icon.png
similarity index 100%
rename from ios/chrome/browser/ui/ntp_tile_views/resources/ntp_bookmarks_icon.imageset/ntp_bookmarks_icon.png
rename to ios/chrome/browser/ui/content_suggestions/cells/resources/ntp_bookmarks_icon.imageset/ntp_bookmarks_icon.png
Binary files differ
diff --git a/ios/chrome/browser/ui/ntp_tile_views/resources/ntp_bookmarks_icon.imageset/ntp_bookmarks_icon@2x.png b/ios/chrome/browser/ui/content_suggestions/cells/resources/ntp_bookmarks_icon.imageset/ntp_bookmarks_icon@2x.png
similarity index 100%
rename from ios/chrome/browser/ui/ntp_tile_views/resources/ntp_bookmarks_icon.imageset/ntp_bookmarks_icon@2x.png
rename to ios/chrome/browser/ui/content_suggestions/cells/resources/ntp_bookmarks_icon.imageset/ntp_bookmarks_icon@2x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/ntp_tile_views/resources/ntp_bookmarks_icon.imageset/ntp_bookmarks_icon@3x.png b/ios/chrome/browser/ui/content_suggestions/cells/resources/ntp_bookmarks_icon.imageset/ntp_bookmarks_icon@3x.png
similarity index 100%
rename from ios/chrome/browser/ui/ntp_tile_views/resources/ntp_bookmarks_icon.imageset/ntp_bookmarks_icon@3x.png
rename to ios/chrome/browser/ui/content_suggestions/cells/resources/ntp_bookmarks_icon.imageset/ntp_bookmarks_icon@3x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/ntp_tile_views/resources/ntp_history_icon.imageset/Contents.json b/ios/chrome/browser/ui/content_suggestions/cells/resources/ntp_history_icon.imageset/Contents.json
similarity index 100%
rename from ios/chrome/browser/ui/ntp_tile_views/resources/ntp_history_icon.imageset/Contents.json
rename to ios/chrome/browser/ui/content_suggestions/cells/resources/ntp_history_icon.imageset/Contents.json
diff --git a/ios/chrome/browser/ui/ntp_tile_views/resources/ntp_history_icon.imageset/ntp_history_icon.png b/ios/chrome/browser/ui/content_suggestions/cells/resources/ntp_history_icon.imageset/ntp_history_icon.png
similarity index 100%
rename from ios/chrome/browser/ui/ntp_tile_views/resources/ntp_history_icon.imageset/ntp_history_icon.png
rename to ios/chrome/browser/ui/content_suggestions/cells/resources/ntp_history_icon.imageset/ntp_history_icon.png
Binary files differ
diff --git a/ios/chrome/browser/ui/ntp_tile_views/resources/ntp_history_icon.imageset/ntp_history_icon@2x.png b/ios/chrome/browser/ui/content_suggestions/cells/resources/ntp_history_icon.imageset/ntp_history_icon@2x.png
similarity index 100%
rename from ios/chrome/browser/ui/ntp_tile_views/resources/ntp_history_icon.imageset/ntp_history_icon@2x.png
rename to ios/chrome/browser/ui/content_suggestions/cells/resources/ntp_history_icon.imageset/ntp_history_icon@2x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/ntp_tile_views/resources/ntp_history_icon.imageset/ntp_history_icon@3x.png b/ios/chrome/browser/ui/content_suggestions/cells/resources/ntp_history_icon.imageset/ntp_history_icon@3x.png
similarity index 100%
rename from ios/chrome/browser/ui/ntp_tile_views/resources/ntp_history_icon.imageset/ntp_history_icon@3x.png
rename to ios/chrome/browser/ui/content_suggestions/cells/resources/ntp_history_icon.imageset/ntp_history_icon@3x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/ntp_tile_views/resources/ntp_most_visited_tile.imageset/Contents.json b/ios/chrome/browser/ui/content_suggestions/cells/resources/ntp_most_visited_tile.imageset/Contents.json
similarity index 100%
rename from ios/chrome/browser/ui/ntp_tile_views/resources/ntp_most_visited_tile.imageset/Contents.json
rename to ios/chrome/browser/ui/content_suggestions/cells/resources/ntp_most_visited_tile.imageset/Contents.json
diff --git a/ios/chrome/browser/ui/ntp_tile_views/resources/ntp_most_visited_tile.imageset/ntp_most_visited_tile.png b/ios/chrome/browser/ui/content_suggestions/cells/resources/ntp_most_visited_tile.imageset/ntp_most_visited_tile.png
similarity index 100%
rename from ios/chrome/browser/ui/ntp_tile_views/resources/ntp_most_visited_tile.imageset/ntp_most_visited_tile.png
rename to ios/chrome/browser/ui/content_suggestions/cells/resources/ntp_most_visited_tile.imageset/ntp_most_visited_tile.png
Binary files differ
diff --git a/ios/chrome/browser/ui/ntp_tile_views/resources/ntp_most_visited_tile.imageset/ntp_most_visited_tile@2x.png b/ios/chrome/browser/ui/content_suggestions/cells/resources/ntp_most_visited_tile.imageset/ntp_most_visited_tile@2x.png
similarity index 100%
rename from ios/chrome/browser/ui/ntp_tile_views/resources/ntp_most_visited_tile.imageset/ntp_most_visited_tile@2x.png
rename to ios/chrome/browser/ui/content_suggestions/cells/resources/ntp_most_visited_tile.imageset/ntp_most_visited_tile@2x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/ntp_tile_views/resources/ntp_most_visited_tile.imageset/ntp_most_visited_tile@3x.png b/ios/chrome/browser/ui/content_suggestions/cells/resources/ntp_most_visited_tile.imageset/ntp_most_visited_tile@3x.png
similarity index 100%
rename from ios/chrome/browser/ui/ntp_tile_views/resources/ntp_most_visited_tile.imageset/ntp_most_visited_tile@3x.png
rename to ios/chrome/browser/ui/content_suggestions/cells/resources/ntp_most_visited_tile.imageset/ntp_most_visited_tile@3x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/ntp_tile_views/resources/ntp_readinglist_icon.imageset/Contents.json b/ios/chrome/browser/ui/content_suggestions/cells/resources/ntp_readinglist_icon.imageset/Contents.json
similarity index 100%
rename from ios/chrome/browser/ui/ntp_tile_views/resources/ntp_readinglist_icon.imageset/Contents.json
rename to ios/chrome/browser/ui/content_suggestions/cells/resources/ntp_readinglist_icon.imageset/Contents.json
diff --git a/ios/chrome/browser/ui/ntp_tile_views/resources/ntp_readinglist_icon.imageset/ntp_readinglist_icon.png b/ios/chrome/browser/ui/content_suggestions/cells/resources/ntp_readinglist_icon.imageset/ntp_readinglist_icon.png
similarity index 100%
rename from ios/chrome/browser/ui/ntp_tile_views/resources/ntp_readinglist_icon.imageset/ntp_readinglist_icon.png
rename to ios/chrome/browser/ui/content_suggestions/cells/resources/ntp_readinglist_icon.imageset/ntp_readinglist_icon.png
Binary files differ
diff --git a/ios/chrome/browser/ui/ntp_tile_views/resources/ntp_readinglist_icon.imageset/ntp_readinglist_icon@2x.png b/ios/chrome/browser/ui/content_suggestions/cells/resources/ntp_readinglist_icon.imageset/ntp_readinglist_icon@2x.png
similarity index 100%
rename from ios/chrome/browser/ui/ntp_tile_views/resources/ntp_readinglist_icon.imageset/ntp_readinglist_icon@2x.png
rename to ios/chrome/browser/ui/content_suggestions/cells/resources/ntp_readinglist_icon.imageset/ntp_readinglist_icon@2x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/ntp_tile_views/resources/ntp_readinglist_icon.imageset/ntp_readinglist_icon@3x.png b/ios/chrome/browser/ui/content_suggestions/cells/resources/ntp_readinglist_icon.imageset/ntp_readinglist_icon@3x.png
similarity index 100%
rename from ios/chrome/browser/ui/ntp_tile_views/resources/ntp_readinglist_icon.imageset/ntp_readinglist_icon@3x.png
rename to ios/chrome/browser/ui/content_suggestions/cells/resources/ntp_readinglist_icon.imageset/ntp_readinglist_icon@3x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/ntp_tile_views/resources/ntp_recent_icon.imageset/Contents.json b/ios/chrome/browser/ui/content_suggestions/cells/resources/ntp_recent_icon.imageset/Contents.json
similarity index 100%
rename from ios/chrome/browser/ui/ntp_tile_views/resources/ntp_recent_icon.imageset/Contents.json
rename to ios/chrome/browser/ui/content_suggestions/cells/resources/ntp_recent_icon.imageset/Contents.json
diff --git a/ios/chrome/browser/ui/ntp_tile_views/resources/ntp_recent_icon.imageset/ntp_recent_icon.png b/ios/chrome/browser/ui/content_suggestions/cells/resources/ntp_recent_icon.imageset/ntp_recent_icon.png
similarity index 100%
rename from ios/chrome/browser/ui/ntp_tile_views/resources/ntp_recent_icon.imageset/ntp_recent_icon.png
rename to ios/chrome/browser/ui/content_suggestions/cells/resources/ntp_recent_icon.imageset/ntp_recent_icon.png
Binary files differ
diff --git a/ios/chrome/browser/ui/ntp_tile_views/resources/ntp_recent_icon.imageset/ntp_recent_icon@2x.png b/ios/chrome/browser/ui/content_suggestions/cells/resources/ntp_recent_icon.imageset/ntp_recent_icon@2x.png
similarity index 100%
rename from ios/chrome/browser/ui/ntp_tile_views/resources/ntp_recent_icon.imageset/ntp_recent_icon@2x.png
rename to ios/chrome/browser/ui/content_suggestions/cells/resources/ntp_recent_icon.imageset/ntp_recent_icon@2x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/ntp_tile_views/resources/ntp_recent_icon.imageset/ntp_recent_icon@3x.png b/ios/chrome/browser/ui/content_suggestions/cells/resources/ntp_recent_icon.imageset/ntp_recent_icon@3x.png
similarity index 100%
rename from ios/chrome/browser/ui/ntp_tile_views/resources/ntp_recent_icon.imageset/ntp_recent_icon@3x.png
rename to ios/chrome/browser/ui/content_suggestions/cells/resources/ntp_recent_icon.imageset/ntp_recent_icon@3x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_utils.mm b/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_utils.mm
index 92be2ca..5f80aa6 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_utils.mm
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_utils.mm
@@ -6,7 +6,6 @@
 
 #include "base/i18n/rtl.h"
 #include "components/strings/grit/components_strings.h"
-#import "ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_most_visited_cell.h"
 #import "ios/chrome/browser/ui/content_suggestions/ntp_home_constant.h"
 #import "ios/chrome/browser/ui/location_bar/location_bar_constants.h"
 #import "ios/chrome/browser/ui/ntp/new_tab_page_header_constants.h"
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller.mm b/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller.mm
index 658234ca..79eeb77e 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller.mm
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller.mm
@@ -15,6 +15,7 @@
 #import "ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_most_visited_cell.h"
 #import "ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_most_visited_item.h"
 #import "ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_text_item.h"
+#import "ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_tile_layout_util.h"
 #import "ios/chrome/browser/ui/content_suggestions/cells/suggested_content.h"
 #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_utils.h"
 #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_commands.h"
@@ -28,7 +29,6 @@
 #import "ios/chrome/browser/ui/content_suggestions/ntp_home_constant.h"
 #import "ios/chrome/browser/ui/list_model/list_item+Controller.h"
 #import "ios/chrome/browser/ui/ntp/new_tab_page_header_constants.h"
-#import "ios/chrome/browser/ui/ntp_tile_views/ntp_tile_layout_util.h"
 #import "ios/chrome/browser/ui/start_surface/start_surface_features.h"
 #import "ios/chrome/browser/ui/toolbar/public/toolbar_utils.h"
 #import "ios/chrome/browser/ui/ui_feature_flags.h"
@@ -259,7 +259,7 @@
                                             collectionViewLayout
     minimumLineSpacingForSectionAtIndex:(NSInteger)section {
   if ([self isMostVisitedSection:section]) {
-    return kNtpTilesVerticalSpacing;
+    return kContentSuggestionsTilesVerticalSpacing;
   }
   return [super collectionView:collectionView
                                    layout:collectionViewLayout
diff --git a/ios/chrome/browser/ui/ntp/feed_metrics_recorder.h b/ios/chrome/browser/ui/ntp/feed_metrics_recorder.h
index c2cb532..74438c6 100644
--- a/ios/chrome/browser/ui/ntp/feed_metrics_recorder.h
+++ b/ios/chrome/browser/ui/ntp/feed_metrics_recorder.h
@@ -27,10 +27,6 @@
 // Record metrics for when the user has scrolled |scrollDistance| in the Feed.
 - (void)recordFeedScrolled:(int)scrollDistance;
 
-// Record metrics for when the user has reached the bottom of their current
-// feed.
-- (void)recordInfiniteFeedTriggered;
-
 // Record metrics for when the user changes the device orientation with the feed
 // visible.
 - (void)recordDeviceOrientationChanged:(UIDeviceOrientation)orientation;
diff --git a/ios/chrome/browser/ui/ntp/feed_metrics_recorder.mm b/ios/chrome/browser/ui/ntp/feed_metrics_recorder.mm
index 7dd4c16..dba0945 100644
--- a/ios/chrome/browser/ui/ntp/feed_metrics_recorder.mm
+++ b/ios/chrome/browser/ui/ntp/feed_metrics_recorder.mm
@@ -49,10 +49,6 @@
 };
 
 namespace {
-// Histogram name for the infinite feed trigger.
-const char kDiscoverFeedInfiniteFeedTriggered[] =
-    "ContentSuggestions.Feed.LoadStreamStatus.LoadMore";
-
 // User action names for the device orientation having changed.
 const char kDiscoverFeedHistogramDeviceOrientationChangedToPortrait[] =
     "ContentSuggestions.Feed.DeviceOrientationChanged.Portrait";
@@ -115,10 +111,6 @@
 const char kDiscoverFeedUserActionManageInterestsTapped[] =
     "ContentSuggestions.Feed.HeaderAction.ManageInterests";
 
-// User action name for infinite feed triggering.
-const char kDiscoverFeedUserActionInfiniteFeedTriggered[] =
-    "ContentSuggestions.Feed.InfiniteFeedTriggered";
-
 // User action name for engaging with feed.
 const char kDiscoverFeedUserActionEngaged[] = "ContentSuggestions.Feed.Engaged";
 
@@ -223,13 +215,6 @@
   }
 }
 
-- (void)recordInfiniteFeedTriggered {
-  UMA_HISTOGRAM_ENUMERATION(kDiscoverFeedInfiniteFeedTriggered,
-                            FeedLoadStreamStatus::kLoadedFromNetwork);
-  base::RecordAction(
-      base::UserMetricsAction(kDiscoverFeedUserActionInfiniteFeedTriggered));
-}
-
 - (void)recordDeviceOrientationChanged:(UIDeviceOrientation)orientation {
   if (orientation == UIDeviceOrientationPortrait) {
     base::RecordAction(base::UserMetricsAction(
diff --git a/ios/chrome/browser/ui/ntp_tile_views/BUILD.gn b/ios/chrome/browser/ui/ntp_tile_views/BUILD.gn
deleted file mode 100644
index 3b0f2d30..0000000
--- a/ios/chrome/browser/ui/ntp_tile_views/BUILD.gn
+++ /dev/null
@@ -1,59 +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.
-
-source_set("ntp_tile_views") {
-  configs += [ "//build/config/compiler:enable_arc" ]
-  sources = [
-    "ntp_most_visited_tile_view.h",
-    "ntp_most_visited_tile_view.mm",
-    "ntp_shortcut_tile_view.h",
-    "ntp_shortcut_tile_view.mm",
-    "ntp_tile_layout_util.h",
-    "ntp_tile_layout_util.mm",
-    "ntp_tile_view.h",
-    "ntp_tile_view.mm",
-  ]
-
-  deps = [
-    "resources:ntp_bookmarks_icon",
-    "resources:ntp_history_icon",
-    "resources:ntp_most_visited_tile",
-    "resources:ntp_readinglist_icon",
-    "resources:ntp_recent_icon",
-    "//base",
-    "//ios/chrome/browser/ui:feature_flags",
-    "//ios/chrome/browser/ui/util",
-    "//ios/chrome/common/ui/colors",
-    "//ios/chrome/common/ui/favicon",
-    "//ios/chrome/common/ui/util",
-    "//ios/chrome/common/ui/util:dynamic_type_util",
-    "//ios/third_party/material_components_ios",
-  ]
-}
-
-source_set("unit_tests") {
-  testonly = true
-  sources = [ "ntp_tile_layout_util_unittest.mm" ]
-  deps = [
-    ":ntp_tile_views",
-    "//base",
-    "//testing/gtest",
-    "//third_party/ocmock",
-  ]
-  configs += [ "//build/config/compiler:enable_arc" ]
-}
-
-source_set("constants") {
-  configs += [ "//build/config/compiler:enable_arc" ]
-  sources = [
-    "ntp_tile_constants.h",
-    "ntp_tile_constants.mm",
-  ]
-
-  deps = [
-    "//ios/chrome/app/strings:ios_strings_grit",
-    "//ios/third_party/material_components_ios",
-    "//ui/base:base",
-  ]
-}
diff --git a/ios/chrome/browser/ui/ntp_tile_views/OWNERS b/ios/chrome/browser/ui/ntp_tile_views/OWNERS
deleted file mode 100644
index 2d35f0a5..0000000
--- a/ios/chrome/browser/ui/ntp_tile_views/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-gambard@chromium.org
diff --git a/ios/chrome/browser/ui/ntp_tile_views/ntp_most_visited_tile_view.h b/ios/chrome/browser/ui/ntp_tile_views/ntp_most_visited_tile_view.h
deleted file mode 100644
index be1d1af..0000000
--- a/ios/chrome/browser/ui/ntp_tile_views/ntp_most_visited_tile_view.h
+++ /dev/null
@@ -1,20 +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.
-
-#ifndef IOS_CHROME_BROWSER_UI_NTP_TILE_VIEWS_NTP_MOST_VISITED_TILE_VIEW_H_
-#define IOS_CHROME_BROWSER_UI_NTP_TILE_VIEWS_NTP_MOST_VISITED_TILE_VIEW_H_
-
-#import "ios/chrome/browser/ui/ntp_tile_views/ntp_tile_view.h"
-
-@class FaviconView;
-
-// NTP Tile representing a most visited website. Displays a favicon and a title.
-@interface NTPMostVisitedTileView : NTPTileView
-
-// FaviconView displaying the favicon.
-@property(nonatomic, strong, readonly, nonnull) FaviconView* faviconView;
-
-@end
-
-#endif  // IOS_CHROME_BROWSER_UI_NTP_TILE_VIEWS_NTP_MOST_VISITED_TILE_VIEW_H_
diff --git a/ios/chrome/browser/ui/ntp_tile_views/ntp_shortcut_tile_view.h b/ios/chrome/browser/ui/ntp_tile_views/ntp_shortcut_tile_view.h
deleted file mode 100644
index 9910491..0000000
--- a/ios/chrome/browser/ui/ntp_tile_views/ntp_shortcut_tile_view.h
+++ /dev/null
@@ -1,25 +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.
-
-#ifndef IOS_CHROME_BROWSER_UI_NTP_TILE_VIEWS_NTP_SHORTCUT_TILE_VIEW_H_
-#define IOS_CHROME_BROWSER_UI_NTP_TILE_VIEWS_NTP_SHORTCUT_TILE_VIEW_H_
-
-#import "ios/chrome/browser/ui/ntp_tile_views/ntp_tile_view.h"
-
-// A tile view displaying a collection shortcut. Accepts a simple icon and
-// optionally supports a badge, for example for reading list new item count.
-@interface NTPShortcutTileView : NTPTileView
-
-// View for action icon.
-@property(nonatomic, strong, readonly, nonnull) UIImageView* iconView;
-
-// Container view for |countLabel|.
-@property(nonatomic, strong, readonly, nonnull) UIView* countContainer;
-
-// Number shown in circle by top trailing side of cell.
-@property(nonatomic, strong, readonly, nonnull) UILabel* countLabel;
-
-@end
-
-#endif  // IOS_CHROME_BROWSER_UI_NTP_TILE_VIEWS_NTP_SHORTCUT_TILE_VIEW_H_
diff --git a/ios/chrome/browser/ui/ntp_tile_views/ntp_tile_layout_util.h b/ios/chrome/browser/ui/ntp_tile_views/ntp_tile_layout_util.h
deleted file mode 100644
index ff376b22..0000000
--- a/ios/chrome/browser/ui/ntp_tile_views/ntp_tile_layout_util.h
+++ /dev/null
@@ -1,37 +0,0 @@
-// 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.
-
-#ifndef IOS_CHROME_BROWSER_UI_NTP_TILE_VIEWS_NTP_TILE_LAYOUT_UTIL_H_
-#define IOS_CHROME_BROWSER_UI_NTP_TILE_VIEWS_NTP_TILE_LAYOUT_UTIL_H_
-
-#import <UIKit/UIKit.h>
-
-// Vertical spacing between rows of tiles.
-extern const int kNtpTilesVerticalSpacing;
-// Vertical spacing between columns of tiles.
-extern const int kNtpTilesHorizontalSpacingRegular;
-extern const int kNtpTilesHorizontalSpacingCompact;
-
-// For font size < UIContentSizeCategoryExtraExtraExtraLarge.
-extern const CGSize kNtpTileViewSizeSmall;
-// For font size == UIContentSizeCategoryExtraExtraExtraLarge.
-extern const CGSize kNtpTileViewSizeMedium;
-// For font size == UIContentSizeCategoryAccessibilityMedium.
-extern const CGSize kNtpTileViewSizeLarge;
-// For font size > UIContentSizeCategoryAccessibilityMedium.
-extern const CGSize kNtpTileViewSizeExtraLarge;
-
-// Returns the vertical spacing between columns of tiles under
-// |trait_collection|.
-CGFloat NtpTilesHorizontalSpacing(UITraitCollection* trait_collection);
-
-// Returns the size of most visited cell based on |category|.
-CGSize MostVisitedCellSize(UIContentSizeCategory category);
-
-// Returns x-offset in order to have the tiles centered in a view with a
-// |width| under |environment|.
-CGFloat CenteredTilesMarginForWidth(UITraitCollection* trait_collection,
-                                    CGFloat width);
-
-#endif  // IOS_CHROME_BROWSER_UI_NTP_TILE_VIEWS_NTP_TILE_LAYOUT_UTIL_H_
diff --git a/ios/chrome/browser/ui/ntp_tile_views/ntp_tile_layout_util.mm b/ios/chrome/browser/ui/ntp_tile_views/ntp_tile_layout_util.mm
deleted file mode 100644
index d801418..0000000
--- a/ios/chrome/browser/ui/ntp_tile_views/ntp_tile_layout_util.mm
+++ /dev/null
@@ -1,67 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "ios/chrome/browser/ui/ntp_tile_views/ntp_tile_layout_util.h"
-
-#include "base/notreached.h"
-#import "ios/chrome/browser/ui/util/uikit_ui_util.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-const int kNtpTilesVerticalSpacing = 16;
-const int kNtpTilesHorizontalSpacingRegular = 19;
-const int kNtpTilesHorizontalSpacingCompact = 5;
-
-const CGSize kNtpTileViewSizeSmall = {/*width=*/73, /*height=*/100};
-const CGSize kNtpTileViewSizeMedium = {/*width=*/73, /*height=*/112};
-const CGSize kNtpTileViewSizeLarge = {/*width=*/110, /*height=*/140};
-const CGSize kNtpTileViewSizeExtraLarge = {/*width=*/146, /*height=*/150};
-
-namespace {
-// Display at most 4 tiles per row.
-const int kMaxNumberOfTilesPerRow = 4;
-}
-
-CGFloat NtpTilesHorizontalSpacing(UITraitCollection* trait_collection) {
-  return (trait_collection.horizontalSizeClass !=
-              UIUserInterfaceSizeClassCompact &&
-          trait_collection.verticalSizeClass != UIUserInterfaceSizeClassCompact)
-             ? kNtpTilesHorizontalSpacingRegular
-             : kNtpTilesHorizontalSpacingCompact;
-}
-
-CGSize MostVisitedCellSize(UIContentSizeCategory category) {
-  NSComparisonResult result = UIContentSizeCategoryCompareToCategory(
-      category, UIContentSizeCategoryAccessibilityMedium);
-  switch (result) {
-    case NSOrderedAscending:
-      return ([category
-                 isEqualToString:UIContentSizeCategoryExtraExtraExtraLarge])
-                 ? kNtpTileViewSizeMedium
-                 : kNtpTileViewSizeSmall;
-    case NSOrderedSame:
-      return kNtpTileViewSizeLarge;
-    case NSOrderedDescending:
-      return kNtpTileViewSizeExtraLarge;
-  }
-}
-
-CGFloat CenteredTilesMarginForWidth(UITraitCollection* trait_collection,
-                                    CGFloat width) {
-  CGFloat horizontalSpace = NtpTilesHorizontalSpacing(trait_collection);
-  CGSize cellSize =
-      MostVisitedCellSize(trait_collection.preferredContentSizeCategory);
-  for (int columns = kMaxNumberOfTilesPerRow; columns > 0; --columns) {
-    CGFloat whitespace =
-        width - (columns * cellSize.width) - ((columns - 1) * horizontalSpace);
-    CGFloat margin = AlignValueToPixel(whitespace / 2);
-    if (margin >= horizontalSpace) {
-      return margin;
-    }
-  }
-  NOTREACHED();
-  return 0;
-}
diff --git a/ios/chrome/browser/ui/ntp_tile_views/ntp_tile_layout_util_unittest.mm b/ios/chrome/browser/ui/ntp_tile_views/ntp_tile_layout_util_unittest.mm
deleted file mode 100644
index b0bd4980..0000000
--- a/ios/chrome/browser/ui/ntp_tile_views/ntp_tile_layout_util_unittest.mm
+++ /dev/null
@@ -1,126 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "ios/chrome/browser/ui/ntp_tile_views/ntp_tile_layout_util.h"
-
-#import <UIKit/UIKit.h>
-
-#include "testing/platform_test.h"
-#import "third_party/ocmock/OCMock/OCMock.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-using NtpTileLayoutUtilTest = PlatformTest;
-
-// Tests that MostVisitedCellSize returns correct size for all content size
-// categories.
-TEST_F(NtpTileLayoutUtilTest, MostVisitedCellSize) {
-  EXPECT_TRUE(
-      CGSizeEqualToSize(kNtpTileViewSizeSmall,
-                        MostVisitedCellSize(UIContentSizeCategoryUnspecified)));
-  EXPECT_TRUE(
-      CGSizeEqualToSize(kNtpTileViewSizeSmall,
-                        MostVisitedCellSize(UIContentSizeCategoryExtraSmall)));
-  EXPECT_TRUE(CGSizeEqualToSize(
-      kNtpTileViewSizeSmall, MostVisitedCellSize(UIContentSizeCategorySmall)));
-  EXPECT_TRUE(CGSizeEqualToSize(
-      kNtpTileViewSizeSmall, MostVisitedCellSize(UIContentSizeCategoryMedium)));
-  EXPECT_TRUE(CGSizeEqualToSize(
-      kNtpTileViewSizeSmall, MostVisitedCellSize(UIContentSizeCategoryLarge)));
-  EXPECT_TRUE(
-      CGSizeEqualToSize(kNtpTileViewSizeSmall,
-                        MostVisitedCellSize(UIContentSizeCategoryExtraLarge)));
-  EXPECT_TRUE(CGSizeEqualToSize(
-      kNtpTileViewSizeSmall,
-      MostVisitedCellSize(UIContentSizeCategoryExtraExtraLarge)));
-  EXPECT_TRUE(CGSizeEqualToSize(
-      kNtpTileViewSizeMedium,
-      MostVisitedCellSize(UIContentSizeCategoryExtraExtraExtraLarge)));
-  EXPECT_TRUE(CGSizeEqualToSize(
-      kNtpTileViewSizeLarge,
-      MostVisitedCellSize(UIContentSizeCategoryAccessibilityMedium)));
-  EXPECT_TRUE(CGSizeEqualToSize(
-      kNtpTileViewSizeExtraLarge,
-      MostVisitedCellSize(UIContentSizeCategoryAccessibilityLarge)));
-  EXPECT_TRUE(CGSizeEqualToSize(
-      kNtpTileViewSizeExtraLarge,
-      MostVisitedCellSize(UIContentSizeCategoryAccessibilityExtraLarge)));
-  EXPECT_TRUE(CGSizeEqualToSize(
-      kNtpTileViewSizeExtraLarge,
-      MostVisitedCellSize(UIContentSizeCategoryAccessibilityExtraExtraLarge)));
-  EXPECT_TRUE(CGSizeEqualToSize(
-      kNtpTileViewSizeExtraLarge,
-      MostVisitedCellSize(
-          UIContentSizeCategoryAccessibilityExtraExtraExtraLarge)));
-}
-
-// Tests that CenteredTilesMarginForWidth works under various environment.
-TEST_F(NtpTileLayoutUtilTest, CenteredTilesMarginForWidth) {
-  // Set up Regular size class and Large font size.
-  UITraitCollection* trait_collection =
-      [UITraitCollection traitCollectionWithTraitsFromCollections:@[
-        [UITraitCollection traitCollectionWithHorizontalSizeClass:
-                               UIUserInterfaceSizeClassRegular],
-        [UITraitCollection traitCollectionWithPreferredContentSizeCategory:
-                               UIContentSizeCategoryLarge]
-      ]];
-
-  // Display 4 columns on very big screen.
-  EXPECT_EQ(200, CenteredTilesMarginForWidth(
-                     trait_collection,
-                     kNtpTileViewSizeSmall.width * 4 +
-                         kNtpTilesHorizontalSpacingRegular * 3 + 200 * 2));
-  // Display 4 columns on normal screen.
-  EXPECT_EQ(20, CenteredTilesMarginForWidth(
-                    trait_collection,
-                    kNtpTileViewSizeSmall.width * 4 +
-                        kNtpTilesHorizontalSpacingRegular * 3 + 20 * 2));
-  // Display 3 columns.
-  EXPECT_EQ(20, CenteredTilesMarginForWidth(
-                    trait_collection,
-                    kNtpTileViewSizeSmall.width * 3 +
-                        kNtpTilesHorizontalSpacingRegular * 2 + 20 * 2));
-  // Display 2 columns.
-  EXPECT_EQ(20, CenteredTilesMarginForWidth(
-                    trait_collection,
-                    kNtpTileViewSizeSmall.width * 2 +
-                        kNtpTilesHorizontalSpacingRegular * 1 + 20 * 2));
-  // Display 1 columns.
-  EXPECT_EQ(20, CenteredTilesMarginForWidth(
-                    trait_collection,
-                    kNtpTileViewSizeSmall.width * 1 +
-                        kNtpTilesHorizontalSpacingRegular * 0 + 20 * 2));
-
-  // Set up Compact size class and Accessibility Large font size.
-  trait_collection =
-      [UITraitCollection traitCollectionWithTraitsFromCollections:@[
-        [UITraitCollection traitCollectionWithHorizontalSizeClass:
-                               UIUserInterfaceSizeClassCompact],
-        [UITraitCollection traitCollectionWithPreferredContentSizeCategory:
-                               UIContentSizeCategoryAccessibilityLarge]
-      ]];
-
-  // Display 4 columns.
-  EXPECT_EQ(20, CenteredTilesMarginForWidth(
-                    trait_collection,
-                    kNtpTileViewSizeExtraLarge.width * 4 +
-                        kNtpTilesHorizontalSpacingCompact * 3 + 20 * 2));
-  // Display 3 columns.
-  EXPECT_EQ(20, CenteredTilesMarginForWidth(
-                    trait_collection,
-                    kNtpTileViewSizeExtraLarge.width * 3 +
-                        kNtpTilesHorizontalSpacingCompact * 2 + 20 * 2));
-  // Display 2 columns.
-  EXPECT_EQ(20, CenteredTilesMarginForWidth(
-                    trait_collection,
-                    kNtpTileViewSizeExtraLarge.width * 2 +
-                        kNtpTilesHorizontalSpacingCompact * 1 + 20 * 2));
-  // Display 1 columns.
-  EXPECT_EQ(20, CenteredTilesMarginForWidth(
-                    trait_collection,
-                    kNtpTileViewSizeExtraLarge.width * 1 +
-                        kNtpTilesHorizontalSpacingCompact * 0 + 20 * 2));
-}
diff --git a/ios/chrome/browser/ui/omnibox/popup/BUILD.gn b/ios/chrome/browser/ui/omnibox/popup/BUILD.gn
index 349ccaa..2239193 100644
--- a/ios/chrome/browser/ui/omnibox/popup/BUILD.gn
+++ b/ios/chrome/browser/ui/omnibox/popup/BUILD.gn
@@ -90,8 +90,8 @@
     "//ios/chrome/browser/ui:feature_flags",
     "//ios/chrome/browser/ui/colors",
     "//ios/chrome/browser/ui/commands",
+    "//ios/chrome/browser/ui/content_suggestions/cells",
     "//ios/chrome/browser/ui/elements",
-    "//ios/chrome/browser/ui/ntp_tile_views",
     "//ios/chrome/browser/ui/omnibox:omnibox_popup_shared",
     "//ios/chrome/browser/ui/omnibox:omnibox_suggestion_icon_util",
     "//ios/chrome/browser/ui/toolbar/buttons",
diff --git a/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_view_controller.mm b/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_view_controller.mm
index e7a7111..f97933c 100644
--- a/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_view_controller.mm
+++ b/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_view_controller.mm
@@ -9,8 +9,8 @@
 #include "base/mac/foundation_util.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/time/time.h"
+#import "ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_tile_layout_util.h"
 #include "ios/chrome/browser/ui/elements/self_sizing_table_view.h"
-#import "ios/chrome/browser/ui/ntp_tile_views/ntp_tile_layout_util.h"
 #include "ios/chrome/browser/ui/omnibox/omnibox_constants.h"
 #import "ios/chrome/browser/ui/omnibox/popup/omnibox_popup_accessibility_identifier_constants.h"
 #import "ios/chrome/browser/ui/omnibox/popup/omnibox_popup_row_cell.h"
diff --git a/ios/chrome/browser/ui/popup_menu/BUILD.gn b/ios/chrome/browser/ui/popup_menu/BUILD.gn
index a1850d7..f88393ae 100644
--- a/ios/chrome/browser/ui/popup_menu/BUILD.gn
+++ b/ios/chrome/browser/ui/popup_menu/BUILD.gn
@@ -76,13 +76,13 @@
     "//ios/chrome/browser/ui/browser_container:ui",
     "//ios/chrome/browser/ui/bubble",
     "//ios/chrome/browser/ui/commands",
+    "//ios/chrome/browser/ui/content_suggestions/cells:constants",
     "//ios/chrome/browser/ui/coordinators:chrome_coordinators",
     "//ios/chrome/browser/ui/default_promo:utils",
     "//ios/chrome/browser/ui/follow:enums",
     "//ios/chrome/browser/ui/follow:utils",
     "//ios/chrome/browser/ui/list_model",
     "//ios/chrome/browser/ui/ntp:feature_flags",
-    "//ios/chrome/browser/ui/ntp_tile_views:constants",
     "//ios/chrome/browser/ui/popup_menu/cells",
     "//ios/chrome/browser/ui/popup_menu/overflow_menu",
     "//ios/chrome/browser/ui/popup_menu/overflow_menu:feature_flags",
diff --git a/ios/chrome/browser/ui/popup_menu/popup_menu_mediator.mm b/ios/chrome/browser/ui/popup_menu/popup_menu_mediator.mm
index 94c7299..54738a3 100644
--- a/ios/chrome/browser/ui/popup_menu/popup_menu_mediator.mm
+++ b/ios/chrome/browser/ui/popup_menu/popup_menu_mediator.mm
@@ -42,8 +42,8 @@
 #include "ios/chrome/browser/ui/bookmarks/bookmark_model_bridge_observer.h"
 #import "ios/chrome/browser/ui/commands/browser_commands.h"
 #import "ios/chrome/browser/ui/commands/reading_list_add_command.h"
+#import "ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_tile_constants.h"
 #import "ios/chrome/browser/ui/list_model/list_model.h"
-#import "ios/chrome/browser/ui/ntp_tile_views/ntp_tile_constants.h"
 #import "ios/chrome/browser/ui/popup_menu/cells/popup_menu_navigation_item.h"
 #import "ios/chrome/browser/ui/popup_menu/cells/popup_menu_text_item.h"
 #import "ios/chrome/browser/ui/popup_menu/cells/popup_menu_tools_item.h"
diff --git a/ios/chrome/browser/ui/recent_tabs/recent_tabs_table_view_controller.mm b/ios/chrome/browser/ui/recent_tabs/recent_tabs_table_view_controller.mm
index 2925cdc5..b1d7da4a 100644
--- a/ios/chrome/browser/ui/recent_tabs/recent_tabs_table_view_controller.mm
+++ b/ios/chrome/browser/ui/recent_tabs/recent_tabs_table_view_controller.mm
@@ -1363,23 +1363,27 @@
     self.signinPromoViewMediator = nil;
     return;
   }
-  // Update the TableViewSigninPromoItem configurator. It will be used by the
-  // item to configure the cell once |self.tableView| requests a cell on
-  // cellForRowAtIndexPath.
-  NSIndexPath* indexPath =
-      [self.tableViewModel indexPathForItemType:ItemTypeOtherDevicesSigninPromo
-                              sectionIdentifier:SectionIdentifierOtherDevices];
-  TableViewItem* item = [self.tableViewModel itemAtIndexPath:indexPath];
-  TableViewSigninPromoItem* signInItem =
-      base::mac::ObjCCastStrict<TableViewSigninPromoItem>(item);
-  signInItem.configurator = configurator;
-  // If section is collapsed no tableView update is needed.
-  if ([self.tableViewModel sectionIsCollapsed:SectionIdentifierOtherDevices]) {
-    return;
+  if ([self.tableViewModel hasItemForItemType:ItemTypeOtherDevicesSigninPromo
+                            sectionIdentifier:SectionIdentifierOtherDevices]) {
+    // Update the TableViewSigninPromoItem configurator. It will be used by the
+    // item to configure the cell once |self.tableView| requests a cell on
+    // cellForRowAtIndexPath.
+    NSIndexPath* indexPath = [self.tableViewModel
+        indexPathForItemType:ItemTypeOtherDevicesSigninPromo
+           sectionIdentifier:SectionIdentifierOtherDevices];
+    TableViewItem* item = [self.tableViewModel itemAtIndexPath:indexPath];
+    TableViewSigninPromoItem* signInItem =
+        base::mac::ObjCCastStrict<TableViewSigninPromoItem>(item);
+    signInItem.configurator = configurator;
+    // If section is collapsed no tableView update is needed.
+    if ([self.tableViewModel
+            sectionIsCollapsed:SectionIdentifierOtherDevices]) {
+      return;
+    }
+    // After setting the new configurator to the item, reload the item's Cell.
+    [self reloadCellsForItems:@[ signInItem ]
+             withRowAnimation:UITableViewRowAnimationNone];
   }
-  // After setting the new configurator to the item, reload the item's Cell.
-  [self reloadCellsForItems:@[ signInItem ]
-           withRowAnimation:UITableViewRowAnimationNone];
 }
 
 - (void)signinDidFinish {
diff --git a/ios/chrome/browser/ui/settings/BUILD.gn b/ios/chrome/browser/ui/settings/BUILD.gn
index 88292da1..70dc196 100644
--- a/ios/chrome/browser/ui/settings/BUILD.gn
+++ b/ios/chrome/browser/ui/settings/BUILD.gn
@@ -150,7 +150,6 @@
     "//ios/chrome/browser/ui/commands",
     "//ios/chrome/browser/ui/content_suggestions:feature_flags",
     "//ios/chrome/browser/ui/content_suggestions/cells",
-    "//ios/chrome/browser/ui/content_suggestions/cells:cells_ui",
     "//ios/chrome/browser/ui/coordinators:chrome_coordinators",
     "//ios/chrome/browser/ui/elements:elements_internal",
     "//ios/chrome/browser/ui/icons",
diff --git a/ios/chrome/browser/ui/text_fragments/text_fragments_egtest.mm b/ios/chrome/browser/ui/text_fragments/text_fragments_egtest.mm
index 12cc9495..f48873503f 100644
--- a/ios/chrome/browser/ui/text_fragments/text_fragments_egtest.mm
+++ b/ios/chrome/browser/ui/text_fragments/text_fragments_egtest.mm
@@ -6,6 +6,7 @@
 #import "components/shared_highlighting/core/common/shared_highlighting_features.h"
 #import "ios/chrome/grit/ios_strings.h"
 #import "ios/chrome/test/earl_grey/chrome_earl_grey.h"
+#import "ios/chrome/test/earl_grey/chrome_matchers.h"
 #import "ios/chrome/test/earl_grey/chrome_test_case.h"
 #import "ios/testing/earl_grey/earl_grey_test.h"
 #import "ios/web/public/test/element_selector.h"
@@ -42,6 +43,21 @@
   return std::move(http_response);
 }
 
+auto GetMenuTitleMatcher() {
+  return grey_text(l10n_util::GetNSString(IDS_IOS_SHARED_HIGHLIGHT_MENU_TITLE));
+}
+
+void DismissMenu() {
+  if ([ChromeEarlGrey isIPadIdiom]) {
+    // Tap the tools menu to dismiss the popover.
+    [[EarlGrey selectElementWithMatcher:chrome_test_util::ToolsMenuButton()]
+        performAction:grey_tap()];
+  } else {
+    [[EarlGrey selectElementWithMatcher:chrome_test_util::CancelButton()]
+        performAction:grey_tap()];
+  }
+}
+
 }  // namespace
 
 // Test class verifying behavior of interactions with text fragments in web
@@ -73,11 +89,52 @@
   [ChromeEarlGrey loadURL:self.testServer->GetURL(kURLWithFragment)];
   [ChromeEarlGrey waitForWebStateContainingText:kTestPageTextSample];
 
+  // The <mark> generated by the script doesn't have an id we can target for a
+  // tap, but it is coterminous with its parent element, #target.
   [ChromeEarlGrey tapWebStateElementWithID:@"target"];
 
-  [ChromeEarlGrey waitForSufficientlyVisibleElementWithMatcher:
-                      grey_text(l10n_util::GetNSString(
-                          IDS_IOS_SHARED_HIGHLIGHT_MENU_TITLE))];
+  [ChromeEarlGrey
+      waitForSufficientlyVisibleElementWithMatcher:GetMenuTitleMatcher()];
+}
+
+- (void)testRemove {
+  [ChromeEarlGrey loadURL:self.testServer->GetURL(kURLWithFragment)];
+  [ChromeEarlGrey waitForWebStateContainingText:kTestPageTextSample];
+
+  ElementSelector* selector = [ElementSelector selectorWithCSSSelector:"mark"];
+
+  [ChromeEarlGrey waitForWebStateContainingElement:selector];
+
+  [ChromeEarlGrey tapWebStateElementWithID:@"target"];
+
+  [ChromeEarlGrey
+      waitForSufficientlyVisibleElementWithMatcher:GetMenuTitleMatcher()];
+
+  [[EarlGrey selectElementWithMatcher:grey_text(l10n_util::GetNSString(
+                                          IDS_IOS_SHARED_HIGHLIGHT_REMOVE))]
+      performAction:grey_tap()];
+
+  [ChromeEarlGrey waitForWebStateNotContainingElement:selector];
+}
+
+- (void)testCancel {
+  [ChromeEarlGrey loadURL:self.testServer->GetURL(kURLWithFragment)];
+  [ChromeEarlGrey waitForWebStateContainingText:kTestPageTextSample];
+
+  ElementSelector* selector = [ElementSelector selectorWithCSSSelector:"mark"];
+
+  [ChromeEarlGrey waitForWebStateContainingElement:selector];
+
+  [ChromeEarlGrey tapWebStateElementWithID:@"target"];
+
+  [ChromeEarlGrey
+      waitForSufficientlyVisibleElementWithMatcher:GetMenuTitleMatcher()];
+
+  DismissMenu();
+
+  [ChromeEarlGrey waitForUIElementToDisappearWithMatcher:GetMenuTitleMatcher()];
+
+  [ChromeEarlGrey waitForWebStateContainingElement:selector];
 }
 
 @end
diff --git a/ios/chrome/browser/web/BUILD.gn b/ios/chrome/browser/web/BUILD.gn
index 1b13c96..a447f44 100644
--- a/ios/chrome/browser/web/BUILD.gn
+++ b/ios/chrome/browser/web/BUILD.gn
@@ -230,6 +230,7 @@
     "//ios/chrome/browser:browser_impl",
     "//ios/chrome/browser/browser_state",
     "//ios/chrome/browser/content_settings",
+    "//ios/chrome/browser/follow",
     "//ios/chrome/browser/link_to_text",
     "//ios/chrome/browser/ntp",
     "//ios/chrome/browser/passwords",
diff --git a/ios/chrome/browser/web/chrome_web_client.mm b/ios/chrome/browser/web/chrome_web_client.mm
index 213d50d..8a8927a 100644
--- a/ios/chrome/browser/web/chrome_web_client.mm
+++ b/ios/chrome/browser/web/chrome_web_client.mm
@@ -30,6 +30,7 @@
 #include "ios/chrome/browser/chrome_switches.h"
 #include "ios/chrome/browser/chrome_url_constants.h"
 #include "ios/chrome/browser/content_settings/host_content_settings_map_factory.h"
+#import "ios/chrome/browser/follow/rss_link_java_script_feature.h"
 #include "ios/chrome/browser/ios_chrome_main_parts.h"
 #import "ios/chrome/browser/link_to_text/link_to_text_java_script_feature.h"
 #include "ios/chrome/browser/ntp/browser_policy_new_tab_page_rewriter.h"
@@ -314,6 +315,7 @@
       SearchEngineTabHelperFactory::GetInstance());
   features.push_back(SearchEngineJavaScriptFeature::GetInstance());
   features.push_back(WebPerformanceMetricsJavaScriptFeature::GetInstance());
+  features.push_back(RSSLinkJavaScriptFeature::GetInstance());
   return features;
 }
 
diff --git a/ios/chrome/test/BUILD.gn b/ios/chrome/test/BUILD.gn
index 2a78b326f..3580e425 100644
--- a/ios/chrome/test/BUILD.gn
+++ b/ios/chrome/test/BUILD.gn
@@ -300,7 +300,6 @@
     "//ios/chrome/browser/ui/main_content:unit_tests",
     "//ios/chrome/browser/ui/menu:unit_tests",
     "//ios/chrome/browser/ui/ntp:unit_tests",
-    "//ios/chrome/browser/ui/ntp_tile_views:unit_tests",
     "//ios/chrome/browser/ui/omnibox:unit_tests",
     "//ios/chrome/browser/ui/omnibox/keyboard_assist:unit_tests",
     "//ios/chrome/browser/ui/omnibox/popup:unit_tests",
diff --git a/ios/chrome/test/earl_grey/chrome_earl_grey.h b/ios/chrome/test/earl_grey/chrome_earl_grey.h
index 04b482f..a88e064 100644
--- a/ios/chrome/test/earl_grey/chrome_earl_grey.h
+++ b/ios/chrome/test/earl_grey/chrome_earl_grey.h
@@ -494,6 +494,11 @@
 // If the condition is not met within a timeout a GREYAssert is induced.
 - (void)waitForWebStateContainingElement:(ElementSelector*)selector;
 
+// Waits for the current web state to NOT contain an element matching
+// |selector|. If the condition is not met within a timeout a GREYAssert is
+// induced.
+- (void)waitForWebStateNotContainingElement:(ElementSelector*)selector;
+
 // Attempts to submit form with |formID| in the current WebState.
 // Induces a GREYAssert if the operation fails.
 - (void)submitWebStateFormWithID:(const std::string&)formID;
diff --git a/ios/chrome/test/earl_grey/chrome_earl_grey.mm b/ios/chrome/test/earl_grey/chrome_earl_grey.mm
index 68869b90..f96a65f 100644
--- a/ios/chrome/test/earl_grey/chrome_earl_grey.mm
+++ b/ios/chrome/test/earl_grey/chrome_earl_grey.mm
@@ -520,6 +520,11 @@
       [ChromeEarlGreyAppInterface waitForWebStateContainingElement:selector]);
 }
 
+- (void)waitForWebStateNotContainingElement:(ElementSelector*)selector {
+  EG_TEST_HELPER_ASSERT_NO_ERROR([ChromeEarlGreyAppInterface
+      waitForWebStateNotContainingElement:selector]);
+}
+
 - (void)waitForMainTabCount:(NSUInteger)count {
   __block NSUInteger actualCount = [ChromeEarlGreyAppInterface mainTabCount];
   NSString* conditionName = [NSString
diff --git a/ios/chrome/test/earl_grey/chrome_earl_grey_app_interface.h b/ios/chrome/test/earl_grey/chrome_earl_grey_app_interface.h
index 486f003..cfe0f11d 100644
--- a/ios/chrome/test/earl_grey/chrome_earl_grey_app_interface.h
+++ b/ios/chrome/test/earl_grey/chrome_earl_grey_app_interface.h
@@ -249,6 +249,10 @@
 // otherwise nil.
 + (NSError*)waitForWebStateContainingElement:(ElementSelector*)selector;
 
+// Waits for the current web state to no longer contain an element matching
+// |selector|. On failure, returns an NSError, otherwise nil.
++ (NSError*)waitForWebStateNotContainingElement:(ElementSelector*)selector;
+
 // Waits for the current web state's frames to contain |text|.
 // If not succeed returns an NSError indicating  why the operation failed,
 // otherwise nil.
diff --git a/ios/chrome/test/earl_grey/chrome_earl_grey_app_interface.mm b/ios/chrome/test/earl_grey/chrome_earl_grey_app_interface.mm
index fdc4e51..787a7bbc 100644
--- a/ios/chrome/test/earl_grey/chrome_earl_grey_app_interface.mm
+++ b/ios/chrome/test/earl_grey/chrome_earl_grey_app_interface.mm
@@ -538,6 +538,20 @@
   return nil;
 }
 
++ (NSError*)waitForWebStateNotContainingElement:(ElementSelector*)selector {
+  bool success = WaitUntilConditionOrTimeout(kWaitForPageLoadTimeout, ^bool {
+    return !web::test::IsWebViewContainingElement(
+        chrome_test_util::GetCurrentWebState(), selector);
+  });
+  if (!success) {
+    NSString* NSErrorDescription = [NSString
+        stringWithFormat:@"Failed waiting for web state without element %@",
+                         selector.selectorDescription];
+    return testing::NSErrorWithLocalizedDescription(NSErrorDescription);
+  }
+  return nil;
+}
+
 + (NSError*)waitForWebStateContainingTextInIFrame:(NSString*)text {
   std::string stringText = base::SysNSStringToUTF8(text);
   bool success = WaitUntilConditionOrTimeout(kWaitForPageLoadTimeout, ^bool {
diff --git a/ios/public/provider/chrome/browser/discover_feed/discover_feed_provider.h b/ios/public/provider/chrome/browser/discover_feed/discover_feed_provider.h
index 67b36ed..b97d308 100644
--- a/ios/public/provider/chrome/browser/discover_feed/discover_feed_provider.h
+++ b/ios/public/provider/chrome/browser/discover_feed/discover_feed_provider.h
@@ -63,12 +63,6 @@
   // Methods to register or remove observers.
   virtual void AddObserver(Observer* observer);
   virtual void RemoveObserver(Observer* observer);
-  // Loads and appends the next set of articles in the feed.
-  virtual void LoadMoreFeedArticles();
-  // Called by the embedder whenever the Feed has been shown.
-  // TODO(crbug.com/1126940): The Feed should have a callback for this, remove
-  // when its available.
-  virtual void FeedWasShown();
 };
 
 #endif  // IOS_PUBLIC_PROVIDER_CHROME_BROWSER_DISCOVER_FEED_DISCOVER_FEED_PROVIDER_H_
diff --git a/ios/public/provider/chrome/browser/discover_feed/discover_feed_provider.mm b/ios/public/provider/chrome/browser/discover_feed/discover_feed_provider.mm
index fbdf4297..f216edd 100644
--- a/ios/public/provider/chrome/browser/discover_feed/discover_feed_provider.mm
+++ b/ios/public/provider/chrome/browser/discover_feed/discover_feed_provider.mm
@@ -40,5 +40,3 @@
 
 void DiscoverFeedProvider::AddObserver(Observer* observer) {}
 void DiscoverFeedProvider::RemoveObserver(Observer* observer) {}
-void DiscoverFeedProvider::LoadMoreFeedArticles() {}
-void DiscoverFeedProvider::FeedWasShown() {}
diff --git a/ios/web/download/BUILD.gn b/ios/web/download/BUILD.gn
index 0f380d4..0e1834a4 100644
--- a/ios/web/download/BUILD.gn
+++ b/ios/web/download/BUILD.gn
@@ -23,6 +23,8 @@
     "download_native_task_bridge.mm",
     "download_native_task_impl.h",
     "download_native_task_impl.mm",
+    "download_result.h",
+    "download_result.mm",
     "download_session_task_impl.h",
     "download_session_task_impl.mm",
     "download_task_impl.h",
diff --git a/ios/web/download/download_native_task_bridge.h b/ios/web/download/download_native_task_bridge.h
index e651542..4be063c8 100644
--- a/ios/web/download/download_native_task_bridge.h
+++ b/ios/web/download/download_native_task_bridge.h
@@ -7,6 +7,8 @@
 
 #import <WebKit/WebKit.h>
 
+#include "ios/web/download/download_result.h"
+
 @class DownloadNativeTaskBridge;
 
 @protocol DownloadNativeTaskBridgeDelegate <NSObject>
@@ -36,7 +38,7 @@
 // Starts download and sets |progressionHandler| and |completionHandler|
 - (void)startDownload:(NSURL*)url
     progressionHandler:(void (^)())progressionHander
-     completionHandler:(void (^)(int error_code))completionHandler;
+     completionHandler:(web::DownloadCompletionHandler)completionHandler;
 
 @property(nonatomic, readonly) WKDownload* download API_AVAILABLE(ios(15));
 @property(nonatomic, readonly) NSURLResponse* response;
diff --git a/ios/web/download/download_native_task_bridge.mm b/ios/web/download/download_native_task_bridge.mm
index 6593165..34cbac6 100644
--- a/ios/web/download/download_native_task_bridge.mm
+++ b/ios/web/download/download_native_task_bridge.mm
@@ -5,6 +5,8 @@
 #import "ios/web/download/download_native_task_bridge.h"
 
 #import "base/check.h"
+#include "ios/web/download/download_result.h"
+#include "net/base/net_errors.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
@@ -14,7 +16,7 @@
   void (^_startDownloadBlock)(NSURL*);
   id<DownloadNativeTaskBridgeDelegate> _delegate;
   void (^_progressionHandler)();
-  void (^_completionHandler)(int error_code);
+  web::DownloadCompletionHandler _completionHandler;
   BOOL _observingDownloadProgress;
 }
 
@@ -68,7 +70,7 @@
 
 - (void)startDownload:(NSURL*)url
     progressionHandler:(void (^)())progressionHandler
-     completionHandler:(void (^)(int error_code))completionHandler {
+     completionHandler:(web::DownloadCompletionHandler)completionHandler {
   _progressionHandler = progressionHandler;
   _completionHandler = completionHandler;
 
@@ -103,15 +105,19 @@
 - (void)download:(WKDownload*)download
     didFailWithError:(NSError*)error
           resumeData:(NSData*)resumeData API_AVAILABLE(ios(15)) {
-  if (_completionHandler)
-    (_completionHandler)(error.code);
+  if (_completionHandler) {
+    web::DownloadResult download_result(net::ERR_FAILED, resumeData != nil);
+    (_completionHandler)(download_result);
+  }
 
   [self stopObservingDownloadProgress];
 }
 
 - (void)downloadDidFinish:(WKDownload*)download API_AVAILABLE(ios(15)) {
-  if (_completionHandler)
-    (_completionHandler)(0);
+  if (_completionHandler) {
+    web::DownloadResult download_result(net::OK);
+    (_completionHandler)(download_result);
+  }
 
   [self stopObservingDownloadProgress];
 }
diff --git a/ios/web/download/download_native_task_impl.mm b/ios/web/download/download_native_task_impl.mm
index 4d9fce24..0271244 100644
--- a/ios/web/download/download_native_task_impl.mm
+++ b/ios/web/download/download_native_task_impl.mm
@@ -77,10 +77,10 @@
           if (task)
             task->OnDownloadUpdated();
         }
-        completionHandler:^(int error_code) {
+        completionHandler:^(DownloadResult download_result) {
           DownloadNativeTaskImpl* task = weak_this.get();
           if (task)
-            task->OnDownloadFinished(error_code);
+            task->OnDownloadFinished(download_result);
         }];
   }
 }
diff --git a/ios/web/download/download_result.h b/ios/web/download/download_result.h
new file mode 100644
index 0000000..8b733da0
--- /dev/null
+++ b/ios/web/download/download_result.h
@@ -0,0 +1,42 @@
+// 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 IOS_WEB_DOWNLOAD_DOWNLOAD_RESULT_H_
+#define IOS_WEB_DOWNLOAD_DOWNLOAD_RESULT_H_
+
+namespace web {
+
+class DownloadResult {
+ public:
+  DownloadResult();
+
+  // Constructs a new DownloadResult object. |error_code| value of net::OK
+  // indicates success while other values indicate a failure. |can_retry|
+  // indicates if a download can be retried or not and depends on if a
+  // WKDownload can return resumed data.
+  explicit DownloadResult(int error_code, bool can_retry = true);
+
+  ~DownloadResult();
+
+  // Returns whether the download can be retried. Only meaningful
+  // if |is_successful()| is false.
+  bool can_retry() const;
+
+  // Returns the underlying error code. Only meaningful if
+  // |is_successful()| is false.
+  bool is_successful() const;
+
+  // Returns error code in object
+  int error_code() const;
+
+ private:
+  int error_code_ = 0;
+  bool can_retry_ = true;
+};
+
+using DownloadCompletionHandler = void (^)(DownloadResult download_result);
+
+}  // namespace web
+
+#endif  // IOS_WEB_DOWNLOAD_DOWNLOAD_RESULT_H_
diff --git a/ios/web/download/download_result.mm b/ios/web/download/download_result.mm
new file mode 100644
index 0000000..3cf9bf6
--- /dev/null
+++ b/ios/web/download/download_result.mm
@@ -0,0 +1,34 @@
+// 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 "ios/web/download/download_result.h"
+
+#include "net/base/net_errors.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+namespace web {
+
+DownloadResult::DownloadResult() = default;
+
+DownloadResult::DownloadResult(int error_code, bool can_retry)
+    : error_code_(error_code), can_retry_(can_retry) {}
+
+DownloadResult::~DownloadResult() = default;
+
+bool DownloadResult::can_retry() const {
+  return can_retry_;
+}
+
+int DownloadResult::error_code() const {
+  return error_code_;
+}
+
+bool DownloadResult::is_successful() const {
+  return error_code_ == net::OK;
+}
+
+}  // namespace web
diff --git a/ios/web/download/download_session_task_impl.h b/ios/web/download/download_session_task_impl.h
index 3b3672dc..2d5edf99 100644
--- a/ios/web/download/download_session_task_impl.h
+++ b/ios/web/download/download_session_task_impl.h
@@ -13,6 +13,7 @@
 }
 
 namespace web {
+
 // Implementation of DownloadTaskImpl that uses NSURLRequest to perform the
 // download
 class DownloadSessionTaskImpl final : public DownloadTaskImpl {
@@ -41,9 +42,11 @@
   void Start(const base::FilePath& path, Destination destination_hint) final;
   void Cancel() final;
   void ShutDown() final;
-  void OnDownloadFinished(int error_code) final;
 
  private:
+  // Called once net::URLFetcherResponseWriter completes the download
+  void OnWriterDownloadFinished(int error_code);
+
   // Called once the net::URLFetcherResponseWriter created in
   // Start() has been initialised. The download can be started
   // unless the initialisation has failed (as reported by the
diff --git a/ios/web/download/download_session_task_impl.mm b/ios/web/download/download_session_task_impl.mm
index 3873727b..87b1a1bb 100644
--- a/ios/web/download/download_session_task_impl.mm
+++ b/ios/web/download/download_session_task_impl.mm
@@ -10,6 +10,7 @@
 #include "base/task/post_task.h"
 #include "base/task/thread_pool.h"
 #import "ios/net/cookies/system_cookie_util.h"
+#include "ios/web/download/download_result.h"
 #include "ios/web/public/browser_state.h"
 #import "ios/web/public/download/download_task_observer.h"
 #include "ios/web/public/thread/web_task_traits.h"
@@ -252,7 +253,7 @@
   DownloadTaskImpl::ShutDown();
 }
 
-void DownloadSessionTaskImpl::OnDownloadFinished(int error_code) {
+void DownloadSessionTaskImpl::OnWriterDownloadFinished(int error_code) {
   // If downloads manager's flag is enabled, keeps the downloaded file. The
   // writer deletes it if it owns it, that's why it shouldn't owns it anymore
   // when the current download is finished.
@@ -261,7 +262,7 @@
   if (writer_->AsFileWriter())
     writer_->AsFileWriter()->DisownFile();
   session_task_ = nil;
-  DownloadTaskImpl::OnDownloadFinished(error_code);
+  DownloadTaskImpl::OnDownloadFinished(DownloadResult(error_code));
 }
 
 void DownloadSessionTaskImpl::OnWriterInitialized(
@@ -271,7 +272,7 @@
   writer_ = std::move(writer);
 
   if (writer_initialization_status != net::OK) {
-    OnDownloadFinished(writer_initialization_status);
+    OnWriterDownloadFinished(writer_initialization_status);
   } else if (original_url_.SchemeIs(url::kDataScheme)) {
     StartDataUrlParsing();
   } else {
@@ -292,9 +293,8 @@
         if (!weak_this.get()) {
           return;
         }
-
-        error_code_ =
-            GetNetErrorCodeFromNSError(error, task.currentRequest.URL);
+        download_result_ = DownloadResult(
+            GetNetErrorCodeFromNSError(error, task.currentRequest.URL));
         percent_complete_ = GetTaskPercentComplete(task);
         received_bytes_ = task.countOfBytesReceived;
         if (total_bytes_ == -1 || task.countOfBytesExpectedToReceive) {
@@ -319,11 +319,11 @@
 
         // Download has finished, so finalize the writer and signal completion.
         auto callback =
-            base::BindOnce(&DownloadSessionTaskImpl::OnDownloadFinished,
+            base::BindOnce(&DownloadSessionTaskImpl::OnWriterDownloadFinished,
                            weak_factory_.GetWeakPtr());
-        if (writer_->Finish(error_code_, std::move(callback)) !=
-            net::ERR_IO_PENDING) {
-          OnDownloadFinished(error_code_);
+        if (writer_->Finish(download_result_.error_code(),
+                            std::move(callback)) != net::ERR_IO_PENDING) {
+          OnWriterDownloadFinished(download_result_.error_code());
         }
       }
       dataBlock:^(scoped_refptr<net::IOBufferWithSize> buffer,
@@ -399,7 +399,7 @@
   std::string charset;
   std::string data;
   if (!net::DataURL::Parse(original_url_, &mime_type_, &charset, &data)) {
-    OnDownloadFinished(net::ERR_INVALID_URL);
+    OnWriterDownloadFinished(net::ERR_INVALID_URL);
     return;
   }
   auto callback = base::BindOnce(&DownloadSessionTaskImpl::OnDataUrlWritten,
@@ -416,10 +416,11 @@
   percent_complete_ = 100;
   total_bytes_ = bytes_written;
   received_bytes_ = total_bytes_;
-  auto callback = base::BindOnce(&DownloadSessionTaskImpl::OnDownloadFinished,
-                                 weak_factory_.GetWeakPtr());
+  auto callback =
+      base::BindOnce(&DownloadSessionTaskImpl::OnWriterDownloadFinished,
+                     weak_factory_.GetWeakPtr());
   if (writer_->Finish(net::OK, std::move(callback)) != net::ERR_IO_PENDING) {
-    OnDownloadFinished(net::OK);
+    OnWriterDownloadFinished(net::OK);
   }
 }
 }  // namespace web
diff --git a/ios/web/download/download_task_impl.h b/ios/web/download/download_task_impl.h
index bb0d16d..f0d8bfc4 100644
--- a/ios/web/download/download_task_impl.h
+++ b/ios/web/download/download_task_impl.h
@@ -11,6 +11,7 @@
 #include "base/files/file_path.h"
 #include "base/memory/weak_ptr.h"
 #include "base/observer_list.h"
+#include "ios/web/download/download_result.h"
 #import "ios/web/public/download/download_task.h"
 #include "url/gurl.h"
 
@@ -18,6 +19,7 @@
 
 namespace web {
 
+class DownloadResult;
 class DownloadTaskObserver;
 class WebState;
 
@@ -83,7 +85,7 @@
 
  protected:
   // Called when download was completed and the data writing was finished.
-  virtual void OnDownloadFinished(int error_code);
+  virtual void OnDownloadFinished(DownloadResult download_result);
 
   // Called when download task was updated.
   void OnDownloadUpdated();
@@ -95,7 +97,6 @@
   State state_ = State::kNotStarted;
   GURL original_url_;
   NSString* http_method_ = nil;
-  int error_code_ = 0;
   int http_code_ = -1;
   int64_t total_bytes_ = -1;
   int64_t received_bytes_ = 0;
@@ -105,6 +106,7 @@
   std::string mime_type_;
   NSString* identifier_ = nil;
   bool has_performed_background_download_ = false;
+  DownloadResult download_result_;
   WebState* web_state_ = nullptr;
   Delegate* delegate_ = nullptr;
 
diff --git a/ios/web/download/download_task_impl.mm b/ios/web/download/download_task_impl.mm
index baadea6..977710e 100644
--- a/ios/web/download/download_task_impl.mm
+++ b/ios/web/download/download_task_impl.mm
@@ -7,6 +7,7 @@
 #import <WebKit/WebKit.h>
 
 #include "base/strings/sys_string_conversions.h"
+#include "ios/web/download/download_result.h"
 #import "ios/web/public/download/download_task_observer.h"
 #include "ios/web/public/thread/web_thread.h"
 #import "ios/web/public/web_state.h"
@@ -115,7 +116,7 @@
 
 int DownloadTaskImpl::GetErrorCode() const {
   DCHECK_CURRENTLY_ON(web::WebThread::UI);
-  return error_code_;
+  return download_result_.error_code();
 }
 
 int DownloadTaskImpl::GetHttpCode() const {
@@ -181,8 +182,8 @@
     observer.OnDownloadUpdated(this);
 }
 
-void DownloadTaskImpl::OnDownloadFinished(int error_code) {
-  error_code_ = error_code;
+void DownloadTaskImpl::OnDownloadFinished(DownloadResult download_result) {
+  download_result_ = download_result;
   state_ = State::kComplete;
   OnDownloadUpdated();
 }
diff --git a/ios/web/test/fakes/fake_native_task_bridge.mm b/ios/web/test/fakes/fake_native_task_bridge.mm
index 66a6c80..5a73f10 100644
--- a/ios/web/test/fakes/fake_native_task_bridge.mm
+++ b/ios/web/test/fakes/fake_native_task_bridge.mm
@@ -33,7 +33,7 @@
 
 - (void)startDownload:(NSURL*)url
     progressionHandler:(void (^)())progressionHandler
-     completionHandler:(void (^)(int error_code))completionHandler {
+     completionHandler:(web::DownloadCompletionHandler)completionHandler {
   [super startDownload:url
       progressionHandler:progressionHandler
        completionHandler:completionHandler];
diff --git a/media/cdm/cdm_context_ref_impl.cc b/media/cdm/cdm_context_ref_impl.cc
index 7dee07e..bd78438 100644
--- a/media/cdm/cdm_context_ref_impl.cc
+++ b/media/cdm/cdm_context_ref_impl.cc
@@ -4,6 +4,8 @@
 
 #include "media/cdm/cdm_context_ref_impl.h"
 
+#include <ostream>
+
 #include "base/check_op.h"
 #include "media/base/content_decryption_module.h"
 
diff --git a/media/formats/hls/attribute_list_fuzzer.cc b/media/formats/hls/attribute_list_fuzzer.cc
index 24d6fc4..5b58448 100644
--- a/media/formats/hls/attribute_list_fuzzer.cc
+++ b/media/formats/hls/attribute_list_fuzzer.cc
@@ -27,20 +27,9 @@
   AttributeMap map(attributes);
   AttributeListIterator iter(SourceString::CreateForTesting(str));
 
-  while (true) {
-    auto result = map.Fill(&iter);
-
-    if (result.has_error()) {
-      auto code = std::move(result).error().code();
-      CHECK(code == ParseStatusCode::kReachedEOF ||
-            code == ParseStatusCode::kMalformedAttributeList ||
-            code == ParseStatusCode::kAttributeListHasDuplicateNames);
-
-      if (code != ParseStatusCode::kAttributeListHasDuplicateNames) {
-        break;
-      }
-    }
-  }
-
+  auto error = map.FillUntilError(&iter);
+  CHECK(error == ParseStatusCode::kReachedEOF ||
+        error == ParseStatusCode::kMalformedAttributeList ||
+        error == ParseStatusCode::kAttributeListHasDuplicateNames);
   return 0;
 }
diff --git a/media/gpu/vaapi/vaapi_wrapper.cc b/media/gpu/vaapi/vaapi_wrapper.cc
index 0d11af9..b923b5b 100644
--- a/media/gpu/vaapi/vaapi_wrapper.cc
+++ b/media/gpu/vaapi/vaapi_wrapper.cc
@@ -24,6 +24,7 @@
 #include "base/callback_helpers.h"
 #include "base/containers/contains.h"
 #include "base/containers/cxx20_erase.h"
+#include "base/containers/fixed_flat_set.h"
 #include "base/cpu.h"
 #include "base/cxx17_backports.h"
 #include "base/environment.h"
@@ -249,8 +250,14 @@
 }
 
 bool UseGlobalVaapiLock(media::VAImplementation implementation_type) {
-  // Only iHD is known to be thread safe at the moment, see crbug.com/1123429.
-  return implementation_type != media::VAImplementation::kIntelIHD ||
+  // Only iHD and Mesa Gallium are known to be thread safe at the moment.
+  // * Mesa Gallium: b/144877595
+  // * iHD: crbug.com/1123429.
+  constexpr auto kNoVaapiLockImplementations =
+      base::MakeFixedFlatSet<media::VAImplementation>(
+          {media::VAImplementation::kMesaGallium,
+           media::VAImplementation::kIntelIHD});
+  return !kNoVaapiLockImplementations.contains(implementation_type) ||
          base::FeatureList::IsEnabled(media::kGlobalVaapiLock);
 }
 
diff --git a/mojo/core/message_pipe_dispatcher.h b/mojo/core/message_pipe_dispatcher.h
index ddf71a9..1b49022 100644
--- a/mojo/core/message_pipe_dispatcher.h
+++ b/mojo/core/message_pipe_dispatcher.h
@@ -10,7 +10,6 @@
 #include <memory>
 #include <queue>
 
-#include "base/memory/raw_ptr.h"
 #include "mojo/core/atomic_flag.h"
 #include "mojo/core/dispatcher.h"
 #include "mojo/core/ports/port_ref.h"
@@ -92,7 +91,9 @@
   void OnPortStatusChanged();
 
   // These are safe to access from any thread without locking.
-  const raw_ptr<NodeController> node_controller_;
+  // `node_controller_` is not a raw_ptr<...> for performance reasons (based on
+  // analysis of sampling profiler data).
+  NodeController* const node_controller_;
   const ports::PortRef port_;
   const uint64_t pipe_id_;
   const int endpoint_;
diff --git a/mojo/public/cpp/base/thread_priority_mojom_traits.h b/mojo/public/cpp/base/thread_priority_mojom_traits.h
index d75d05a..dd2193c 100644
--- a/mojo/public/cpp/base/thread_priority_mojom_traits.h
+++ b/mojo/public/cpp/base/thread_priority_mojom_traits.h
@@ -8,6 +8,10 @@
 #include "base/component_export.h"
 #include "mojo/public/mojom/base/thread_priority.mojom-shared.h"
 
+namespace base {
+enum class ThreadPriority;
+}
+
 namespace mojo {
 
 template <>
diff --git a/mojo/public/cpp/bindings/array_data_view.h b/mojo/public/cpp/bindings/array_data_view.h
index 6803ac3..4c40940 100644
--- a/mojo/public/cpp/bindings/array_data_view.h
+++ b/mojo/public/cpp/bindings/array_data_view.h
@@ -7,7 +7,6 @@
 
 #include <type_traits>
 
-#include "base/memory/raw_ptr.h"
 #include "mojo/public/cpp/bindings/lib/array_internal.h"
 #include "mojo/public/cpp/bindings/lib/bindings_internal.h"
 #include "mojo/public/cpp/bindings/lib/serialization_forward.h"
@@ -37,8 +36,12 @@
   const T* data() const { return data_->storage(); }
 
  protected:
-  raw_ptr<Data_> data_;
-  raw_ptr<Message> message_;
+  // `data_` is not a raw_ptr<...> for performance reasons (based on analysis of
+  // sampling profiler data).
+  Data_* data_;
+  // `message_` is not a raw_ptr<...> for performance reasons (based on analysis
+  // of sampling profiler data).
+  Message* message_;
 };
 
 template <typename T>
@@ -55,8 +58,12 @@
   bool operator[](size_t index) const { return data_->at(index); }
 
  protected:
-  raw_ptr<Data_> data_;
-  raw_ptr<Message> message_;
+  // `data_` is not a raw_ptr<...> for performance reasons (based on analysis of
+  // sampling profiler data).
+  Data_* data_;
+  // `message_` is not a raw_ptr<...> for performance reasons (based on analysis
+  // of sampling profiler data).
+  Message* message_;
 };
 
 template <typename T>
@@ -83,8 +90,12 @@
   }
 
  protected:
-  raw_ptr<Data_> data_;
-  raw_ptr<Message> message_;
+  // `data_` is not a raw_ptr<...> for performance reasons (based on analysis of
+  // sampling profiler data).
+  Data_* data_;
+  // `message_` is not a raw_ptr<...> for performance reasons (based on analysis
+  // of sampling profiler data).
+  Message* message_;
 };
 
 template <typename T>
@@ -111,8 +122,12 @@
   }
 
  protected:
-  raw_ptr<Data_> data_;
-  raw_ptr<Message> message_;
+  // `data_` is not a raw_ptr<...> for performance reasons (based on analysis of
+  // sampling profiler data).
+  Data_* data_;
+  // `message_` is not a raw_ptr<...> for performance reasons (based on analysis
+  // of sampling profiler data).
+  Message* message_;
 };
 
 template <typename T>
@@ -134,8 +149,12 @@
   }
 
  protected:
-  raw_ptr<Data_> data_;
-  raw_ptr<Message> message_;
+  // `data_` is not a raw_ptr<...> for performance reasons (based on analysis of
+  // sampling profiler data).
+  Data_* data_;
+  // `message_` is not a raw_ptr<...> for performance reasons (based on analysis
+  // of sampling profiler data).
+  Message* message_;
 };
 
 template <typename T>
@@ -162,8 +181,12 @@
   }
 
  protected:
-  raw_ptr<Data_> data_;
-  raw_ptr<Message> message_;
+  // `data_` is not a raw_ptr<...> for performance reasons (based on analysis of
+  // sampling profiler data).
+  Data_* data_;
+  // `message_` is not a raw_ptr<...> for performance reasons (based on analysis
+  // of sampling profiler data).
+  Message* message_;
 };
 
 template <typename T>
@@ -187,8 +210,12 @@
   }
 
  protected:
-  raw_ptr<Data_> data_;
-  raw_ptr<Message> message_;
+  // `data_` is not a raw_ptr<...> for performance reasons (based on analysis of
+  // sampling profiler data).
+  Data_* data_;
+  // `message_` is not a raw_ptr<...> for performance reasons (based on analysis
+  // of sampling profiler data).
+  Message* message_;
 };
 
 }  // namespace internal
diff --git a/mojo/public/cpp/bindings/connector.h b/mojo/public/cpp/bindings/connector.h
index a4d6702..cbf43e47 100644
--- a/mojo/public/cpp/bindings/connector.h
+++ b/mojo/public/cpp/bindings/connector.h
@@ -12,7 +12,6 @@
 #include "base/callback.h"
 #include "base/compiler_specific.h"
 #include "base/component_export.h"
-#include "base/memory/raw_ptr.h"
 #include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
 #include "base/sequence_checker.h"
@@ -296,7 +295,9 @@
   base::OnceClosure connection_error_handler_;
 
   ScopedMessagePipeHandle message_pipe_;
-  raw_ptr<MessageReceiver> incoming_receiver_ = nullptr;
+  // `incoming_receiver_` is not a raw_ptr<...> for performance reasons (based
+  // on analysis of sampling profiler data).
+  MessageReceiver* incoming_receiver_ = nullptr;
 
   scoped_refptr<base::SequencedTaskRunner> task_runner_;
   std::unique_ptr<SimpleWatcher> handle_watcher_;
@@ -341,7 +342,9 @@
 
   // A cached pointer to the RunLoopNestingObserver for the thread on which this
   // Connector was created.
-  raw_ptr<RunLoopNestingObserver> nesting_observer_ = nullptr;
+  // `nesting_observer_` is not a raw_ptr<...> for performance reasons (based on
+  // analysis of sampling profiler data).
+  RunLoopNestingObserver* nesting_observer_ = nullptr;
 
   // |true| iff the Connector is currently dispatching a message. Used to detect
   // nested dispatch operations.
diff --git a/mojo/public/cpp/bindings/interface_endpoint_client.h b/mojo/public/cpp/bindings/interface_endpoint_client.h
index 4d4e3cfa..5434c21 100644
--- a/mojo/public/cpp/bindings/interface_endpoint_client.h
+++ b/mojo/public/cpp/bindings/interface_endpoint_client.h
@@ -293,10 +293,13 @@
 
   ScopedInterfaceEndpointHandle handle_;
   std::unique_ptr<AssociatedGroup> associated_group_;
-  raw_ptr<InterfaceEndpointController> controller_ = nullptr;
+  // `controller_` is not a raw_ptr<...> for performance reasons (based on
+  // analysis of sampling profiler data).
+  InterfaceEndpointController* controller_ = nullptr;
 
-  const raw_ptr<MessageReceiverWithResponderStatus> incoming_receiver_ =
-      nullptr;
+  // `incoming_receiver_` is not a raw_ptr<...> for performance reasons (based
+  // on analysis of sampling profiler data).
+  MessageReceiverWithResponderStatus* const incoming_receiver_ = nullptr;
   HandleIncomingMessageThunk thunk_{this};
   MessageDispatcher dispatcher_;
 
diff --git a/mojo/public/cpp/bindings/lib/may_auto_lock.h b/mojo/public/cpp/bindings/lib/may_auto_lock.h
index 7f15adf..3264c8d 100644
--- a/mojo/public/cpp/bindings/lib/may_auto_lock.h
+++ b/mojo/public/cpp/bindings/lib/may_auto_lock.h
@@ -6,7 +6,6 @@
 #define MOJO_PUBLIC_CPP_BINDINGS_LIB_MAY_AUTO_LOCK_H_
 
 #include "base/component_export.h"
-#include "base/memory/raw_ptr.h"
 #include "base/synchronization/lock.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
@@ -34,7 +33,9 @@
   }
 
  private:
-  raw_ptr<base::Lock> lock_;
+  // `lock_` is not a raw_ptr<...> for performance reasons: on-stack pointer +
+  // based on analysis of sampling profiler data.
+  base::Lock* lock_;
 };
 
 // Similar to base::AutoUnlock, except that it does nothing if |lock| passed
@@ -58,7 +59,9 @@
   }
 
  private:
-  raw_ptr<base::Lock> lock_;
+  // `lock_` is not a raw_ptr<...> for performance reasons: on-stack pointer +
+  // based on analysis of sampling profiler data.
+  base::Lock* lock_;
 };
 
 }  // namespace internal
diff --git a/mojo/public/cpp/bindings/lib/multiplex_router.h b/mojo/public/cpp/bindings/lib/multiplex_router.h
index 0a5e78c0..1ac3f1e 100644
--- a/mojo/public/cpp/bindings/lib/multiplex_router.h
+++ b/mojo/public/cpp/bindings/lib/multiplex_router.h
@@ -14,7 +14,6 @@
 #include "base/component_export.h"
 #include "base/containers/circular_deque.h"
 #include "base/containers/small_map.h"
-#include "base/memory/raw_ptr.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/sequence_checker.h"
 #include "base/synchronization/lock.h"
@@ -305,7 +304,9 @@
   scoped_refptr<base::SequencedTaskRunner> task_runner_;
 
   // Owned by |dispatcher_| below.
-  raw_ptr<MessageHeaderValidator> header_validator_ = nullptr;
+  // `header_validator_` is not a raw_ptr<...> for performance reasons (based on
+  // analysis of sampling profiler data).
+  MessageHeaderValidator* header_validator_ = nullptr;
 
   MessageDispatcher dispatcher_;
   Connector connector_;
diff --git a/net/base/prioritized_dispatcher.cc b/net/base/prioritized_dispatcher.cc
index 0adfda6..38ca35f 100644
--- a/net/base/prioritized_dispatcher.cc
+++ b/net/base/prioritized_dispatcher.cc
@@ -4,6 +4,8 @@
 
 #include "net/base/prioritized_dispatcher.h"
 
+#include <ostream>
+
 #include "base/check_op.h"
 
 namespace net {
diff --git a/remoting/host/it2me/it2me_confirmation_dialog.h b/remoting/host/it2me/it2me_confirmation_dialog.h
index 9437ad56..941986c 100644
--- a/remoting/host/it2me/it2me_confirmation_dialog.h
+++ b/remoting/host/it2me/it2me_confirmation_dialog.h
@@ -9,7 +9,6 @@
 #include <string>
 
 #include "base/callback_forward.h"
-#include "base/compiler_specific.h"
 
 namespace remoting {
 
@@ -51,7 +50,7 @@
 
  private:
   // This field is only used on ChromeOS.
-  ALLOW_UNUSED_TYPE It2MeConfirmationDialog::DialogStyle dialog_style_ =
+  [[maybe_unused]] It2MeConfirmationDialog::DialogStyle dialog_style_ =
       It2MeConfirmationDialog::DialogStyle::kConsumer;
 };
 
diff --git a/remoting/host/security_key/security_key_socket.h b/remoting/host/security_key/security_key_socket.h
index 7679251..8e313d6 100644
--- a/remoting/host/security_key/security_key_socket.h
+++ b/remoting/host/security_key/security_key_socket.h
@@ -17,6 +17,7 @@
 
 namespace base {
 class OneShotTimer;
+class TimeDelta;
 }  // namespace base
 
 namespace net {
diff --git a/services/image_annotation/annotator.cc b/services/image_annotation/annotator.cc
index 41751f78..3feb3a1 100644
--- a/services/image_annotation/annotator.cc
+++ b/services/image_annotation/annotator.cc
@@ -464,9 +464,8 @@
 Annotator::~Annotator() {
   // Report any clients still connected at service shutdown.
   for (const auto& request_info_kv : request_infos_) {
-    for (const auto& unused : request_info_kv.second) {
+    for ([[maybe_unused]] const auto& unused : request_info_kv.second) {
       ReportClientResult(ClientResult::kShutdown);
-      ANALYZER_ALLOW_UNUSED(unused);
     }
   }
 }
diff --git a/services/network/BUILD.gn b/services/network/BUILD.gn
index 53250cf..282d1967 100644
--- a/services/network/BUILD.gn
+++ b/services/network/BUILD.gn
@@ -477,6 +477,8 @@
   sources = [
     "mojo_socket_test_util.cc",
     "mojo_socket_test_util.h",
+    "test/client_security_state_builder.cc",
+    "test/client_security_state_builder.h",
     "test/fake_test_cert_verifier_params_factory.cc",
     "test/fake_test_cert_verifier_params_factory.h",
     "test/mock_devtools_observer.cc",
diff --git a/services/network/cors/cors_url_loader_unittest.cc b/services/network/cors/cors_url_loader_unittest.cc
index ef08f19..dbee310 100644
--- a/services/network/cors/cors_url_loader_unittest.cc
+++ b/services/network/cors/cors_url_loader_unittest.cc
@@ -45,6 +45,7 @@
 #include "services/network/public/mojom/url_request.mojom-forward.h"
 #include "services/network/resource_scheduler/resource_scheduler.h"
 #include "services/network/resource_scheduler/resource_scheduler_client.h"
+#include "services/network/test/client_security_state_builder.h"
 #include "services/network/test/fake_test_cert_verifier_params_factory.h"
 #include "services/network/test/mock_devtools_observer.h"
 #include "services/network/test/test_url_loader_client.h"
@@ -3433,33 +3434,6 @@
   EXPECT_EQ(GetRequest().method, "OPTIONS");
 }
 
-class ClientSecurityStateBuilder {
- public:
-  ClientSecurityStateBuilder() = default;
-  ~ClientSecurityStateBuilder() = default;
-
-  ClientSecurityStateBuilder& WithPrivateNetworkRequestPolicy(
-      mojom::PrivateNetworkRequestPolicy policy) {
-    state_.private_network_request_policy = policy;
-    return *this;
-  }
-
-  ClientSecurityStateBuilder& WithIPAddressSpace(mojom::IPAddressSpace space) {
-    state_.ip_address_space = space;
-    return *this;
-  }
-
-  ClientSecurityStateBuilder& WithIsSecureContext(bool is_secure_context) {
-    state_.is_web_secure_context = is_secure_context;
-    return *this;
-  }
-
-  mojom::ClientSecurityStatePtr Build() const { return state_.Clone(); }
-
- private:
-  mojom::ClientSecurityState state_;
-};
-
 class RequestTrustedParamsBuilder {
  public:
   RequestTrustedParamsBuilder() = default;
diff --git a/services/network/test/client_security_state_builder.cc b/services/network/test/client_security_state_builder.cc
new file mode 100644
index 0000000..4341124
--- /dev/null
+++ b/services/network/test/client_security_state_builder.cc
@@ -0,0 +1,43 @@
+// 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 "services/network/test/client_security_state_builder.h"
+
+#include "services/network/public/cpp/cross_origin_embedder_policy.h"
+#include "services/network/public/mojom/ip_address_space.mojom.h"
+
+namespace network {
+
+ClientSecurityStateBuilder&
+ClientSecurityStateBuilder::WithPrivateNetworkRequestPolicy(
+    network::mojom::PrivateNetworkRequestPolicy policy) {
+  state_.private_network_request_policy = policy;
+  return *this;
+}
+
+ClientSecurityStateBuilder& ClientSecurityStateBuilder::WithIPAddressSpace(
+    network::mojom::IPAddressSpace space) {
+  state_.ip_address_space = space;
+  return *this;
+}
+
+ClientSecurityStateBuilder& ClientSecurityStateBuilder::WithIsSecureContext(
+    bool is_secure_context) {
+  state_.is_web_secure_context = is_secure_context;
+  return *this;
+}
+
+ClientSecurityStateBuilder&
+ClientSecurityStateBuilder::WithCrossOriginEmbedderPolicy(
+    network::CrossOriginEmbedderPolicy policy) {
+  state_.cross_origin_embedder_policy = policy;
+  return *this;
+}
+
+network::mojom::ClientSecurityStatePtr ClientSecurityStateBuilder::Build()
+    const {
+  return state_.Clone();
+}
+
+}  // namespace network
\ No newline at end of file
diff --git a/services/network/test/client_security_state_builder.h b/services/network/test/client_security_state_builder.h
new file mode 100644
index 0000000..0f99752
--- /dev/null
+++ b/services/network/test/client_security_state_builder.h
@@ -0,0 +1,43 @@
+// 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 SERVICES_NETWORK_TEST_CLIENT_SECURITY_STATE_BUILDER_H_
+#define SERVICES_NETWORK_TEST_CLIENT_SECURITY_STATE_BUILDER_H_
+
+#include "services/network/public/mojom/client_security_state.mojom-forward.h"
+#include "services/network/public/mojom/client_security_state.mojom.h"
+
+namespace network {
+
+namespace mojom {
+enum class PrivateNetworkRequestPolicy;
+enum class IPAddressSpace;
+}  // namespace mojom
+
+struct CrossOriginEmbedderPolicy;
+
+class ClientSecurityStateBuilder {
+ public:
+  ClientSecurityStateBuilder() = default;
+  ~ClientSecurityStateBuilder() = default;
+
+  ClientSecurityStateBuilder& WithPrivateNetworkRequestPolicy(
+      network::mojom::PrivateNetworkRequestPolicy policy);
+
+  ClientSecurityStateBuilder& WithIPAddressSpace(
+      network::mojom::IPAddressSpace space);
+
+  ClientSecurityStateBuilder& WithIsSecureContext(bool is_secure_context);
+
+  ClientSecurityStateBuilder& WithCrossOriginEmbedderPolicy(
+      network::CrossOriginEmbedderPolicy policy);
+
+  network::mojom::ClientSecurityStatePtr Build() const;
+
+ private:
+  network::mojom::ClientSecurityState state_;
+};
+}  // namespace network
+
+#endif  // SERVICES_NETWORK_TEST_TEST_CLIENT_SECURITY_STATE_BUILDER_H_
\ No newline at end of file
diff --git a/services/proxy_resolver/BUILD.gn b/services/proxy_resolver/BUILD.gn
index f3a6d01..cf61166 100644
--- a/services/proxy_resolver/BUILD.gn
+++ b/services/proxy_resolver/BUILD.gn
@@ -79,8 +79,7 @@
   if (is_android) {
     if (use_v8_context_snapshot) {
       deps += [ "//tools/v8_context_snapshot:v8_context_snapshot_assets" ]
-    }
-    if (!use_v8_context_snapshot || include_both_v8_snapshots) {
+    } else {
       deps += [ "//v8:v8_external_startup_data_assets" ]
     }
   }
diff --git a/services/tracing/public/cpp/stack_sampling/loader_lock_sampler_win.cc b/services/tracing/public/cpp/stack_sampling/loader_lock_sampler_win.cc
index 1787895..eff30a5 100644
--- a/services/tracing/public/cpp/stack_sampling/loader_lock_sampler_win.cc
+++ b/services/tracing/public/cpp/stack_sampling/loader_lock_sampler_win.cc
@@ -4,6 +4,8 @@
 
 #include "services/tracing/public/cpp/stack_sampling/loader_lock_sampler_win.h"
 
+#include <ostream>
+
 #include "base/check.h"
 #include "base/native_library.h"
 #include "base/win/windows_types.h"
diff --git a/testing/buildbot/chromium.rust.json b/testing/buildbot/chromium.rust.json
index 220120c..7ffc002 100644
--- a/testing/buildbot/chromium.rust.json
+++ b/testing/buildbot/chromium.rust.json
@@ -13,6 +13,11 @@
         },
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "device_os": "MMB29Q"
+            }
+          ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "base_unittests",
@@ -26,6 +31,11 @@
         "name": "test_cpp_including_rust_unittests",
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "device_os": "MMB29Q"
+            }
+          ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "test_cpp_including_rust_unittests",
@@ -39,6 +49,11 @@
         "name": "test_serde_jsonrc",
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "device_os": "MMB29Q"
+            }
+          ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "test_serde_jsonrc",
diff --git a/testing/buildbot/waterfalls.pyl b/testing/buildbot/waterfalls.pyl
index e802b34..90cdd170 100644
--- a/testing/buildbot/waterfalls.pyl
+++ b/testing/buildbot/waterfalls.pyl
@@ -5394,6 +5394,7 @@
         'additional_compile_targets': [
           'rust_build_tests',
         ],
+        'mixins': [ 'marshmallow' ],
         'test_suites': {
           'gtest_tests': 'rust_gtests',
           # Currently `build_rust_unit_tests` is false on Android (because
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 48e42cf7..73f9719 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -6105,7 +6105,9 @@
     "PartitionAllocLargeThreadCacheSizeAndroid": [
         {
             "platforms": [
-                "android"
+                "android",
+                "android_weblayer",
+                "android_webview"
             ],
             "experiments": [
                 {
diff --git a/third_party/blink/common/features.cc b/third_party/blink/common/features.cc
index 48521af..b1b5a37 100644
--- a/third_party/blink/common/features.cc
+++ b/third_party/blink/common/features.cc
@@ -574,16 +574,6 @@
 #endif
 };
 
-// Enables redirecting subresources in the page to better compressed and
-// optimized versions to provide data savings.
-const base::Feature kSubresourceRedirect{"SubresourceRedirect",
-                                         base::FEATURE_DISABLED_BY_DEFAULT};
-
-// Enables redirecting src videos in the page to better compressed and optimized
-// versions to provide data savings.
-const base::Feature kSubresourceRedirectSrcVideo{
-    "SubresourceRedirectSrcVideo", base::FEATURE_DISABLED_BY_DEFAULT};
-
 // When enabled, enforces new interoperable semantics for 3D transforms.
 // See crbug.com/1008483.
 const base::Feature kBackfaceVisibilityInterop{
diff --git a/third_party/blink/public/common/BUILD.gn b/third_party/blink/public/common/BUILD.gn
index cd03ac01..8a9134a 100644
--- a/third_party/blink/public/common/BUILD.gn
+++ b/third_party/blink/public/common/BUILD.gn
@@ -156,7 +156,6 @@
     "loader/mime_sniffing_throttle.h",
     "loader/mime_sniffing_url_loader.h",
     "loader/network_utils.h",
-    "loader/previews_state.h",
     "loader/record_load_histograms.h",
     "loader/referrer_utils.h",
     "loader/resource_type_util.h",
diff --git a/third_party/blink/public/common/features.h b/third_party/blink/public/common/features.h
index 4999fde..2791799 100644
--- a/third_party/blink/public/common/features.h
+++ b/third_party/blink/public/common/features.h
@@ -184,10 +184,6 @@
 
 BLINK_COMMON_EXPORT extern const base::Feature kBackfaceVisibilityInterop;
 
-BLINK_COMMON_EXPORT extern const base::Feature kSubresourceRedirect;
-
-BLINK_COMMON_EXPORT extern const base::Feature kSubresourceRedirectSrcVideo;
-
 BLINK_COMMON_EXPORT extern const base::Feature kSetLowPriorityForBeacon;
 
 BLINK_COMMON_EXPORT extern const base::Feature kCacheStorageCodeCacheHintHeader;
diff --git a/third_party/blink/public/common/loader/previews_state.h b/third_party/blink/public/common/loader/previews_state.h
deleted file mode 100644
index 189cb83..0000000
--- a/third_party/blink/public/common/loader/previews_state.h
+++ /dev/null
@@ -1,87 +0,0 @@
-// 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.
-
-#ifndef THIRD_PARTY_BLINK_PUBLIC_COMMON_LOADER_PREVIEWS_STATE_H_
-#define THIRD_PARTY_BLINK_PUBLIC_COMMON_LOADER_PREVIEWS_STATE_H_
-
-namespace blink {
-
-typedef int PreviewsState;
-
-// The Previews types which determines whether to request a Preview version of
-// the resource. Previews are optimizations that change the format and
-// content of web pages to improve data savings and / or performance. This enum
-// determines which Previews types to request.
-// Deprecated values should be commented out and not reused since this bitmask
-// is persisted on disk.
-// TODO(nhiroki): Remove snake-case enum values (e.g., PREVIEWS_UNSPECIFIED) in
-// favor of camel-case enum values (e.g., kPreviewsUnspecified).
-enum PreviewsTypes {
-  PREVIEWS_UNSPECIFIED = 0,  // Let the browser process decide whether or
-                             // not to request Preview types.
-  kPreviewsUnspecified = PREVIEWS_UNSPECIFIED,
-
-  // DEPRECATED: SERVER_LOFI_ON = 1 << 0, Request a Lo-Fi version of the
-  // resource from the server. This preview type has been deprecated and should
-  // no longer be used.
-  // DEPRECATED: CLIENT_LOFI_ON = 1 << 1, Request a Lo-Fi version of the
-  // resource from the client. This preview type has been deprecated and should
-  // no longer be used.
-  // DEPRECATED: CLIENT_LOFI_AUTO_RELOAD = 1 << 2,  // Request the original
-  // version of the resource after a decoding error occurred when attempting to
-  // use Client Lo-Fi.
-  // kClientLoFiAutoReload = CLIENT_LOFI_AUTO_RELOAD,
-
-  // DEPRECATED: SERVER_LITE_PAGE_ON = 1 << 3,  // Request a Lite Page version
-  // of the resource from the server.
-  // kServiceLitePageOn = SERVER_LITE_PAGE_ON,
-
-  PREVIEWS_NO_TRANSFORM = 1 << 4,  // Explicitly forbid Previews
-                                   // transformations.
-  kPreviewsNoTransform = PREVIEWS_NO_TRANSFORM,
-
-  PREVIEWS_OFF = 1 << 5,  // Request a normal (non-Preview) version of
-                          // the resource. Server transformations may
-                          // still happen if the page is heavy.
-  kPreviewsOff = PREVIEWS_OFF,
-
-  // DEPRECATED: NOSCRIPT_ON = 1 << 6,  // Request that script be disabled for
-  // page load. kNoScriptOn = NOSCRIPT_ON,
-
-  // DEPRECATED: RESOURCE_LOADING_HINTS_ON =
-  //    1 << 7,  // Request that resource loading hints be used during pageload.
-  // kResourceLoadingHintsOn = RESOURCE_LOADING_HINTS_ON,
-
-  // DEPRECATED: OFFLINE_PAGE_ON =
-  //    1 << 8,  // Request that an offline page be used if one is stored.
-  // kOfflinePageOn = OFFLINE_PAGE_ON,
-
-  // DEPRECATED: LITE_PAGE_REDIRECT_ON = 1 << 9,  // Allow the browser to
-  // redirect the resource to a Lite Page server. Support for this functionality
-  // has been removed.
-
-  // DEPRECATED DEFER_ALL_SCRIPT_ON = 1 << 10,  // Request that script execution
-  // be deferred until parsing completes. kDeferAllScriptOn =
-  // DEFER_ALL_SCRIPT_ON,
-
-  SUBRESOURCE_REDIRECT_ON =
-      1 << 11,  // Allow the subresources in the page to be redirected to
-                // serve better optimized resources. Set on subresources.
-  kSubresourceRedirectOn = SUBRESOURCE_REDIRECT_ON,
-
-  // Indicates this is a src video request. Multiple range requests are
-  // triggered for playing a src video element. This bit is set only for the
-  // first request for the resource (FRFR), subsequent range requests will not
-  // have this bit. Metrics recording and redirecting to its compressed version
-  // is enough to be done for the first range request, and subsequent range
-  // requests need not have this bit.
-  kSrcVideoRedirectOn = 1 << 12,
-
-  PREVIEWS_STATE_LAST = kSrcVideoRedirectOn,
-  kPreviewsStateLast = PREVIEWS_STATE_LAST
-};
-
-}  // namespace blink
-
-#endif  // THIRD_PARTY_BLINK_PUBLIC_COMMON_LOADER_PREVIEWS_STATE_H_
diff --git a/third_party/blink/public/devtools_protocol/browser_protocol.pdl b/third_party/blink/public/devtools_protocol/browser_protocol.pdl
index ecd637d..082a771 100644
--- a/third_party/blink/public/devtools_protocol/browser_protocol.pdl
+++ b/third_party/blink/public/devtools_protocol/browser_protocol.pdl
@@ -8626,6 +8626,7 @@
       websql
       service_workers
       cache_storage
+      interest_groups
       all
       other
 
@@ -8644,6 +8645,37 @@
       string issuerOrigin
       number count
 
+  # Enum of interest group access types.
+  type InterestGroupAccessType extends string
+    enum
+      join
+      leave
+      update
+      bid
+      win
+
+  # Ad advertising element inside an interest group.
+  type InterestGroupAd extends object
+    properties
+      string renderUrl
+      optional string metadata
+
+  # The full details of an interest group.
+  type InterestGroupDetails extends object
+    properties
+      string ownerOrigin
+      string name
+      number expirationTime
+      string joiningOrigin
+      optional string biddingUrl
+      optional string biddingWasmHelperUrl
+      optional string updateUrl
+      optional string trustedBiddingSignalsUrl
+      array of string trustedBiddingSignalsKeys
+      optional string userBiddingSignals
+      array of InterestGroupAd ads
+      array of InterestGroupAd adComponents
+
   # Clears storage for origin.
   command clearDataForOrigin
     parameters
@@ -8743,6 +8775,19 @@
       # True if any tokens were deleted, false otherwise.
       boolean didDeleteTokens
 
+  # Gets details for a named interest group.
+  experimental command getInterestGroupDetails
+    parameters
+      string ownerOrigin
+      string name
+    returns
+      InterestGroupDetails details
+
+  # Enables/Disables issuing of interestGroupAccessed events.
+  experimental command setInterestGroupTracking
+    parameters
+      boolean enable
+
   # A cache's contents have been modified.
   event cacheStorageContentUpdated
     parameters
@@ -8773,6 +8818,13 @@
       # Origin to update.
       string origin
 
+  # One of the interest groups was accessed by the associated page.
+  event interestGroupAccessed
+    parameters
+      InterestGroupAccessType type
+      string ownerOrigin
+      string name
+
 # The SystemInfo domain defines methods and events for querying low-level system information.
 experimental domain SystemInfo
 
diff --git a/third_party/blink/public/mojom/loader/resource_load_info_notifier.mojom b/third_party/blink/public/mojom/loader/resource_load_info_notifier.mojom
index 303b346c..b76fb04c 100644
--- a/third_party/blink/public/mojom/loader/resource_load_info_notifier.mojom
+++ b/third_party/blink/public/mojom/loader/resource_load_info_notifier.mojom
@@ -22,8 +22,7 @@
   NotifyResourceResponseReceived(int64 request_id,
                                  url.mojom.Url response_url,
                                  network.mojom.URLResponseHead head,
-                                 network.mojom.RequestDestination request_destination,
-                                 int32 previews_state);
+                                 network.mojom.RequestDestination request_destination);
 
   // Called to notify the transfer size is updated.
   NotifyResourceTransferSizeUpdated(int64 request_id, int32 transfer_size_diff);
diff --git a/third_party/blink/public/mojom/navigation/navigation_params.mojom b/third_party/blink/public/mojom/navigation/navigation_params.mojom
index c6f67d1..09b5610 100644
--- a/third_party/blink/public/mojom/navigation/navigation_params.mojom
+++ b/third_party/blink/public/mojom/navigation/navigation_params.mojom
@@ -170,11 +170,6 @@
   // Is only used with data: URLs.
   url.mojom.Url base_url_for_data_url;
 
-  // Bitmask that has whether or not to request a Preview version of the
-  // document for various preview types or let the browser decide.
-  // Defined in third_party/blink/public/common/loader/previews_state.h.
-  int32 previews_state = 0;  // 0 == PREVIEWS_UNSPECIFIED
-
   // The navigationStart time exposed through the Navigation Timing API to JS.
   mojo_base.mojom.TimeTicks navigation_start;
 
diff --git a/third_party/blink/public/platform/resource_load_info_notifier_wrapper.h b/third_party/blink/public/platform/resource_load_info_notifier_wrapper.h
index 809723b..42fae52 100644
--- a/third_party/blink/public/platform/resource_load_info_notifier_wrapper.h
+++ b/third_party/blink/public/platform/resource_load_info_notifier_wrapper.h
@@ -10,7 +10,6 @@
 #include "net/base/request_priority.h"
 #include "services/network/public/mojom/fetch_api.mojom-shared.h"
 #include "services/network/public/mojom/url_response_head.mojom-forward.h"
-#include "third_party/blink/public/common/loader/previews_state.h"
 #include "third_party/blink/public/mojom/loader/resource_load_info.mojom.h"
 #include "third_party/blink/public/platform/web_common.h"
 
@@ -53,8 +52,7 @@
       const net::RedirectInfo& redirect_info,
       network::mojom::URLResponseHeadPtr redirect_response);
   void NotifyResourceResponseReceived(
-      network::mojom::URLResponseHeadPtr response_head,
-      PreviewsState previews_state);
+      network::mojom::URLResponseHeadPtr response_head);
   void NotifyResourceTransferSizeUpdated(int32_t transfer_size_diff);
   void NotifyResourceLoadCompleted(
       const network::URLLoaderCompletionStatus& status);
diff --git a/third_party/blink/public/platform/weak_wrapper_resource_load_info_notifier.h b/third_party/blink/public/platform/weak_wrapper_resource_load_info_notifier.h
index 2b1b6d56..0542ad9 100644
--- a/third_party/blink/public/platform/weak_wrapper_resource_load_info_notifier.h
+++ b/third_party/blink/public/platform/weak_wrapper_resource_load_info_notifier.h
@@ -9,7 +9,6 @@
 #include "base/threading/thread_checker.h"
 #include "build/build_config.h"
 #include "services/network/public/mojom/url_response_head.mojom-forward.h"
-#include "third_party/blink/public/common/loader/previews_state.h"
 #include "third_party/blink/public/mojom/loader/resource_load_info.mojom-forward.h"
 #include "third_party/blink/public/mojom/loader/resource_load_info_notifier.mojom.h"
 #include "third_party/blink/public/platform/web_common.h"
@@ -34,8 +33,7 @@
       int64_t request_id,
       const GURL& final_url,
       network::mojom::URLResponseHeadPtr response_head,
-      network::mojom::RequestDestination request_destination,
-      int32_t previews_state) override;
+      network::mojom::RequestDestination request_destination) override;
   void NotifyResourceTransferSizeUpdated(int64_t request_id,
                                          int32_t transfer_size_diff) override;
   void NotifyResourceLoadCompleted(
diff --git a/third_party/blink/public/platform/web_resource_request_sender.h b/third_party/blink/public/platform/web_resource_request_sender.h
index fa252560..04959db6 100644
--- a/third_party/blink/public/platform/web_resource_request_sender.h
+++ b/third_party/blink/public/platform/web_resource_request_sender.h
@@ -24,7 +24,6 @@
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 #include "services/network/public/mojom/fetch_api.mojom-forward.h"
 #include "services/network/public/mojom/url_response_head.mojom-forward.h"
-#include "third_party/blink/public/common/loader/previews_state.h"
 #include "third_party/blink/public/common/loader/url_loader_throttle.h"
 #include "third_party/blink/public/mojom/blob/blob_registry.mojom-forward.h"
 #include "third_party/blink/public/mojom/loader/resource_load_info.mojom-shared.h"
@@ -200,7 +199,6 @@
     // it's not completed. Used both to distinguish completion from
     // cancellation, and to log histograms.
     int net_error = net::ERR_IO_PENDING;
-    PreviewsState previews_state = PreviewsTypes::PREVIEWS_UNSPECIFIED;
 
     std::unique_ptr<ThrottlingURLLoader> url_loader;
     std::unique_ptr<MojoURLLoaderClient> url_loader_client;
diff --git a/third_party/blink/public/platform/web_url_request.h b/third_party/blink/public/platform/web_url_request.h
index bfba1a93..69debb3 100644
--- a/third_party/blink/public/platform/web_url_request.h
+++ b/third_party/blink/public/platform/web_url_request.h
@@ -36,7 +36,6 @@
 #include "base/time/time.h"
 #include "base/unguessable_token.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
-#include "third_party/blink/public/common/loader/previews_state.h"
 #include "third_party/blink/public/platform/web_common.h"
 #include "ui/base/page_transition_types.h"
 
@@ -229,12 +228,6 @@
   BLINK_PLATFORM_EXPORT WebString GetFetchIntegrity() const;
   BLINK_PLATFORM_EXPORT void SetFetchIntegrity(const WebString&);
 
-  // The PreviewsState which determines whether to request a Preview version of
-  // the resource. The PreviewsState is a bitmask of potentially several
-  // Previews optimizations.
-  BLINK_PLATFORM_EXPORT PreviewsState GetPreviewsState() const;
-  BLINK_PLATFORM_EXPORT void SetPreviewsState(PreviewsState);
-
   // Extra data associated with the underlying resource request. Resource
   // requests can be copied. If non-null, each copy of a resource requests
   // holds a pointer to the extra data, and the extra data pointer will be
diff --git a/third_party/blink/public/web/web_document_loader.h b/third_party/blink/public/web/web_document_loader.h
index 23162a37..32a7dd0 100644
--- a/third_party/blink/public/web/web_document_loader.h
+++ b/third_party/blink/public/web/web_document_loader.h
@@ -34,7 +34,6 @@
 #include <memory>
 
 #include "services/network/public/mojom/ip_address_space.mojom-shared.h"
-#include "third_party/blink/public/common/loader/previews_state.h"
 #include "third_party/blink/public/mojom/loader/code_cache.mojom.h"
 #include "third_party/blink/public/platform/web_archive_info.h"
 #include "third_party/blink/public/platform/web_common.h"
@@ -127,9 +126,6 @@
   // can return true even if archive loading ended up failing.
   virtual bool HasBeenLoadedAsWebArchive() const = 0;
 
-  // Returns the previews state for the document.
-  virtual PreviewsState GetPreviewsState() const = 0;
-
   // Returns archive info for the archive.
   virtual WebArchiveInfo GetArchiveInfo() const = 0;
 
diff --git a/third_party/blink/public/web/web_navigation_params.h b/third_party/blink/public/web/web_navigation_params.h
index 74d13e8e2..d33c638d 100644
--- a/third_party/blink/public/web/web_navigation_params.h
+++ b/third_party/blink/public/web/web_navigation_params.h
@@ -353,8 +353,6 @@
   bool is_browser_initiated = false;
   // Whether the document should be able to access local file:// resources.
   bool grant_load_local_resources = false;
-  // The previews state which should be used for this navigation.
-  PreviewsState previews_state = PreviewsTypes::kPreviewsUnspecified;
   // The service worker network provider to be used in the new
   // document.
   std::unique_ptr<blink::WebServiceWorkerNetworkProvider>
diff --git a/third_party/blink/renderer/core/css/css_image_value.cc b/third_party/blink/renderer/core/css/css_image_value.cc
index be4f2d35..394308c 100644
--- a/third_party/blink/renderer/core/css/css_image_value.cc
+++ b/third_party/blink/renderer/core/css/css_image_value.cc
@@ -114,15 +114,6 @@
     params.SetLazyImageDeferred();
   }
 
-  if (base::FeatureList::IsEnabled(blink::features::kSubresourceRedirect) &&
-      params.Url().ProtocolIsInHTTPFamily() &&
-      GetNetworkStateNotifier().SaveDataEnabled()) {
-    auto& subresource_request = params.MutableResourceRequest();
-    subresource_request.SetPreviewsState(
-        subresource_request.GetPreviewsState() |
-        PreviewsTypes::kSubresourceRedirectOn);
-  }
-
   if (origin_clean_ != OriginClean::kTrue)
     params.SetFromOriginDirtyStyleSheet(true);
 
diff --git a/third_party/blink/renderer/core/css/css_properties.json5 b/third_party/blink/renderer/core/css/css_properties.json5
index 276b363..233d533 100644
--- a/third_party/blink/renderer/core/css/css_properties.json5
+++ b/third_party/blink/renderer/core/css/css_properties.json5
@@ -4954,7 +4954,7 @@
     {
       name: "-webkit-print-color-adjust",
       property_methods: ["CSSValueFromComputedStyleInternal"],
-      independent: false,  # Actually true, but setting it to false saves a precious bit in ComputedStyleBase.
+      independent: false,  // Actually true, but setting it to false saves a precious bit in ComputedStyleBase.
       inherited: true,
       field_template: "keyword",
       keywords: ["economy", "exact"],
diff --git a/third_party/blink/renderer/core/css/element_rule_collector.h b/third_party/blink/renderer/core/css/element_rule_collector.h
index 8daf7ae..42a7784 100644
--- a/third_party/blink/renderer/core/css/element_rule_collector.h
+++ b/third_party/blink/renderer/core/css/element_rule_collector.h
@@ -38,6 +38,8 @@
 namespace blink {
 
 class CSSStyleSheet;
+class Element;
+class HTMLSlotElement;
 class PartNames;
 class RuleData;
 class SelectorFilter;
@@ -149,6 +151,30 @@
 
   void AddMatchedRulesToTracker(StyleRuleUsageTracker*) const;
 
+  // Temporarily swap the StyleRecalcContext with one which points to the
+  // closest query container for matching ::slotted rules for a given slot.
+  class SlottedRulesScope {
+   public:
+    SlottedRulesScope(ElementRuleCollector& collector, HTMLSlotElement& slot)
+        : context_(&collector.style_recalc_context_,
+                   collector.style_recalc_context_.ForSlottedRules(slot)) {}
+
+   private:
+    base::AutoReset<StyleRecalcContext> context_;
+  };
+
+  // Temporarily swap the StyleRecalcContext with one which points to the
+  // closest query container for matching ::part rules for a given host.
+  class PartRulesScope {
+   public:
+    PartRulesScope(ElementRuleCollector& collector, Element& host)
+        : context_(&collector.style_recalc_context_,
+                   collector.style_recalc_context_.ForPartRules(host)) {}
+
+   private:
+    base::AutoReset<StyleRecalcContext> context_;
+  };
+
  private:
   struct PartRequest {
     PartNames& part_names;
diff --git a/third_party/blink/renderer/core/css/resolver/style_resolver.cc b/third_party/blink/renderer/core/css/resolver/style_resolver.cc
index 5ecd132..4065045f 100644
--- a/third_party/blink/renderer/core/css/resolver/style_resolver.cc
+++ b/third_party/blink/renderer/core/css/resolver/style_resolver.cc
@@ -512,17 +512,20 @@
   if (!slot)
     return;
 
-  HeapVector<Member<ScopedStyleResolver>> resolvers;
+  HeapVector<std::pair<Member<HTMLSlotElement>, Member<ScopedStyleResolver>>>
+      resolvers;
   for (; slot; slot = slot->AssignedSlot()) {
     if (ScopedStyleResolver* resolver =
-            slot->GetTreeScope().GetScopedStyleResolver())
-      resolvers.push_back(resolver);
+            slot->GetTreeScope().GetScopedStyleResolver()) {
+      resolvers.push_back(std::make_pair(slot, resolver));
+    }
   }
   for (auto it = resolvers.rbegin(); it != resolvers.rend(); ++it) {
+    ElementRuleCollector::SlottedRulesScope scope(collector, *(*it).first);
     collector.ClearMatchedRules();
-    (*it)->CollectMatchingSlottedRules(collector);
+    (*it).second->CollectMatchingSlottedRules(collector);
     collector.SortAndTransferMatchedRules();
-    collector.FinishAddingAuthorRulesForTreeScope((*it)->GetTreeScope());
+    collector.FinishAddingAuthorRulesForTreeScope((*it).first->GetTreeScope());
   }
 }
 
@@ -620,6 +623,7 @@
   while (current_names.size()) {
     TreeScope& tree_scope = host->GetTreeScope();
     if (ScopedStyleResolver* resolver = tree_scope.GetScopedStyleResolver()) {
+      ElementRuleCollector::PartRulesScope scope(collector, *host);
       collector.ClearMatchedRules();
       resolver->CollectMatchingPartPseudoRules(collector, current_names,
                                                for_shadow_pseudo);
diff --git a/third_party/blink/renderer/core/css/style_recalc_context.cc b/third_party/blink/renderer/core/css/style_recalc_context.cc
index 732f97b..a64d2e53 100644
--- a/third_party/blink/renderer/core/css/style_recalc_context.cc
+++ b/third_party/blink/renderer/core/css/style_recalc_context.cc
@@ -12,8 +12,9 @@
 
 namespace {
 
-Element* ClosestInclusiveAncestorContainer(Element& element) {
-  for (auto* container = &element; container;
+Element* ClosestInclusiveAncestorContainer(Element& element,
+                                           Element* stay_within = nullptr) {
+  for (auto* container = &element; container && container != stay_within;
        container = container->ParentOrShadowHostElement()) {
     if (container->GetContainerQueryEvaluator())
       return container;
@@ -64,4 +65,33 @@
   return StyleRecalcContext{FromInclusiveAncestors(*host)};
 }
 
+StyleRecalcContext StyleRecalcContext::ForSlottedRules(
+    HTMLSlotElement& slot) const {
+  if (!RuntimeEnabledFeatures::CSSContainerQueriesEnabled())
+    return *this;
+
+  // The current container is the shadow-including inclusive ancestors of the
+  // host. When matching ::slotted rules, the closest container may be found in
+  // the shadow-including inclusive ancestry of the slot. If we reach the host,
+  // the current container is still the closest one.
+  if (Element* shadow_container =
+          ClosestInclusiveAncestorContainer(slot, slot.OwnerShadowHost())) {
+    return StyleRecalcContext{shadow_container};
+  }
+  return *this;
+}
+
+StyleRecalcContext StyleRecalcContext::ForPartRules(Element& host) const {
+  if (!container || !RuntimeEnabledFeatures::CSSContainerQueriesEnabled())
+    return *this;
+
+  // The closest container for matching ::part rules is the originating host.
+  // There is no need to walk past the current container.
+  if (Element* host_container =
+          ClosestInclusiveAncestorContainer(host, container)) {
+    return StyleRecalcContext{host_container};
+  }
+  return *this;
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/css/style_recalc_context.h b/third_party/blink/renderer/core/css/style_recalc_context.h
index fd60092..823e72b 100644
--- a/third_party/blink/renderer/core/css/style_recalc_context.h
+++ b/third_party/blink/renderer/core/css/style_recalc_context.h
@@ -37,6 +37,14 @@
   // context with the container adjusted as necessary.
   StyleRecalcContext ForSlotChildren(const HTMLSlotElement& slot) const;
 
+  // Called to update the context when matching ::slotted rules for shadow host
+  // children. ::slotted rules may query containers inside the slot's shadow
+  // tree as well.
+  StyleRecalcContext ForSlottedRules(HTMLSlotElement& slot) const;
+
+  // Called to update the context when matching ::part rules for shadow hosts.
+  StyleRecalcContext ForPartRules(Element& host) const;
+
   // Set to the nearest container (for container queries), if any.
   // This is used to evaluate container queries in ElementRuleCollector.
   Element* container = nullptr;
diff --git a/third_party/blink/renderer/core/exported/web_document_loader_impl.cc b/third_party/blink/renderer/core/exported/web_document_loader_impl.cc
index 06fda95..5c6a514 100644
--- a/third_party/blink/renderer/core/exported/web_document_loader_impl.cc
+++ b/third_party/blink/renderer/core/exported/web_document_loader_impl.cc
@@ -159,10 +159,6 @@
   return archive_;
 }
 
-PreviewsState WebDocumentLoaderImpl::GetPreviewsState() const {
-  return DocumentLoader::GetPreviewsState();
-}
-
 WebArchiveInfo WebDocumentLoaderImpl::GetArchiveInfo() const {
   if (archive_ &&
       archive_->LoadResult() == mojom::blink::MHTMLLoadResult::kSuccess) {
diff --git a/third_party/blink/renderer/core/exported/web_document_loader_impl.h b/third_party/blink/renderer/core/exported/web_document_loader_impl.h
index 7c19fa0..cbf439d3 100644
--- a/third_party/blink/renderer/core/exported/web_document_loader_impl.h
+++ b/third_party/blink/renderer/core/exported/web_document_loader_impl.h
@@ -82,7 +82,6 @@
   void BlockParser() override;
   void ResumeParser() override;
   bool HasBeenLoadedAsWebArchive() const override;
-  PreviewsState GetPreviewsState() const override;
   WebArchiveInfo GetArchiveInfo() const override;
   bool LastNavigationHadTransientUserActivation() const override;
   void SetCodeCacheHost(
diff --git a/third_party/blink/renderer/core/exported/web_navigation_params.cc b/third_party/blink/renderer/core/exported/web_navigation_params.cc
index 8b04ab3..64c0813 100644
--- a/third_party/blink/renderer/core/exported/web_navigation_params.cc
+++ b/third_party/blink/renderer/core/exported/web_navigation_params.cc
@@ -34,7 +34,6 @@
   result->http_body = info.url_request.HttpBody();
   result->http_content_type =
       info.url_request.HttpHeaderField(http_names::kContentType);
-  result->previews_state = info.url_request.GetPreviewsState();
   result->requestor_origin = info.url_request.RequestorOrigin();
   result->frame_load_type = info.frame_load_type;
   result->is_client_redirect = info.is_client_redirect;
diff --git a/third_party/blink/renderer/core/html/build.gni b/third_party/blink/renderer/core/html/build.gni
index ddd1ba2f..c38bdbd9 100644
--- a/third_party/blink/renderer/core/html/build.gni
+++ b/third_party/blink/renderer/core/html/build.gni
@@ -629,8 +629,6 @@
   "parser/pump_session.h",
   "parser/resource_preloader.cc",
   "parser/resource_preloader.h",
-  "parser/subresource_redirect_origins_preloader.h",
-  "parser/subresource_redirect_origins_preloader.cc",
   "parser/text_document_parser.cc",
   "parser/text_document_parser.h",
   "parser/text_resource_decoder.cc",
diff --git a/third_party/blink/renderer/core/html/parser/html_document_parser.cc b/third_party/blink/renderer/core/html/parser/html_document_parser.cc
index c42a3fd..6414108 100644
--- a/third_party/blink/renderer/core/html/parser/html_document_parser.cc
+++ b/third_party/blink/renderer/core/html/parser/html_document_parser.cc
@@ -50,7 +50,6 @@
 #include "third_party/blink/renderer/core/html/parser/html_resource_preloader.h"
 #include "third_party/blink/renderer/core/html/parser/html_tree_builder.h"
 #include "third_party/blink/renderer/core/html/parser/pump_session.h"
-#include "third_party/blink/renderer/core/html/parser/subresource_redirect_origins_preloader.h"
 #include "third_party/blink/renderer/core/html_names.h"
 #include "third_party/blink/renderer/core/inspector/inspector_trace_events.h"
 #include "third_party/blink/renderer/core/loader/document_loader.h"
@@ -1832,10 +1831,6 @@
                                           GetPreloadHistogramSuffix()}),
                             timer.Elapsed());
   }
-  if (auto* subresource_redirect_origins_preloader =
-          SubresourceRedirectOriginsPreloader::From(*GetDocument())) {
-    subresource_redirect_origins_preloader->PreloadOriginsNow();
-  }
 }
 
 std::string HTMLDocumentParser::GetPreloadHistogramSuffix() {
diff --git a/third_party/blink/renderer/core/html/parser/html_preload_scanner.cc b/third_party/blink/renderer/core/html/parser/html_preload_scanner.cc
index 56f87b05..b192d58c 100644
--- a/third_party/blink/renderer/core/html/parser/html_preload_scanner.cc
+++ b/third_party/blink/renderer/core/html/parser/html_preload_scanner.cc
@@ -53,7 +53,6 @@
 #include "third_party/blink/renderer/core/html/parser/html_parser_idioms.h"
 #include "third_party/blink/renderer/core/html/parser/html_srcset_parser.h"
 #include "third_party/blink/renderer/core/html/parser/html_tokenizer.h"
-#include "third_party/blink/renderer/core/html/parser/subresource_redirect_origins_preloader.h"
 #include "third_party/blink/renderer/core/html_names.h"
 #include "third_party/blink/renderer/core/input_type_names.h"
 #include "third_party/blink/renderer/core/loader/document_loader.h"
@@ -403,11 +402,6 @@
     }
     request->SetRenderBlockingBehavior(render_blocking_behavior);
 
-    if (type == ResourceType::kImage &&
-        document_parameters.subresource_redirect_origins_preloader) {
-      document_parameters.subresource_redirect_origins_preloader
-          ->AddImagePreloadRequest(predicted_base_url, url_to_load_);
-    }
     if (type == ResourceType::kImage && Match(tag_impl_, html_names::kImgTag) &&
         IsLazyLoadImageDeferable(document_parameters)) {
       return nullptr;
@@ -1287,8 +1281,6 @@
   }
   probe::GetDisabledImageTypes(document->GetExecutionContext(),
                                &disabled_image_types);
-  subresource_redirect_origins_preloader =
-      SubresourceRedirectOriginsPreloader::From(*document);
   local_dom_window = document->domWindow();
 }
 
diff --git a/third_party/blink/renderer/core/html/parser/preload_request.cc b/third_party/blink/renderer/core/html/parser/preload_request.cc
index 9e5e2f5..fa1075ae 100644
--- a/third_party/blink/renderer/core/html/parser/preload_request.cc
+++ b/third_party/blink/renderer/core/html/parser/preload_request.cc
@@ -105,13 +105,6 @@
 
   resource_request.SetFetchImportanceMode(importance_);
 
-  if (resource_type_ == ResourceType::kImage && url.ProtocolIsInHTTPFamily() &&
-      base::FeatureList::IsEnabled(blink::features::kSubresourceRedirect) &&
-      blink::GetNetworkStateNotifier().SaveDataEnabled()) {
-    resource_request.SetPreviewsState(resource_request.GetPreviewsState() |
-                                      PreviewsTypes::kSubresourceRedirectOn);
-  }
-
   ResourceLoaderOptions options(document->domWindow()->GetCurrentWorld());
   options.initiator_info = initiator_info;
   FetchParameters params(std::move(resource_request), options);
diff --git a/third_party/blink/renderer/core/html/parser/subresource_redirect_origins_preloader.cc b/third_party/blink/renderer/core/html/parser/subresource_redirect_origins_preloader.cc
deleted file mode 100644
index f3bedc2..0000000
--- a/third_party/blink/renderer/core/html/parser/subresource_redirect_origins_preloader.cc
+++ /dev/null
@@ -1,72 +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.
-
-#include "third_party/blink/renderer/core/html/parser/subresource_redirect_origins_preloader.h"
-#include "third_party/blink/public/common/features.h"
-#include "third_party/blink/renderer/core/frame/frame.h"
-#include "third_party/blink/renderer/core/frame/local_frame.h"
-#include "third_party/blink/renderer/core/frame/local_frame_client.h"
-#include "third_party/blink/renderer/platform/network/network_state_notifier.h"
-#include "third_party/blink/renderer/platform/weborigin/kurl.h"
-
-namespace blink {
-
-// static
-const char SubresourceRedirectOriginsPreloader::kSupplementName[] =
-    "SubresourceRedirectOriginsPreloader";
-
-SubresourceRedirectOriginsPreloader* SubresourceRedirectOriginsPreloader::From(
-    Document& document) {
-  if (!base::FeatureList::IsEnabled(blink::features::kSubresourceRedirect) ||
-      !GetNetworkStateNotifier().SaveDataEnabled()) {
-    return nullptr;
-  }
-
-  SubresourceRedirectOriginsPreloader* preloader =
-      Supplement<Document>::From<SubresourceRedirectOriginsPreloader>(document);
-  if (!preloader) {
-    preloader =
-        MakeGarbageCollected<SubresourceRedirectOriginsPreloader>(document);
-    ProvideTo(document, preloader);
-  }
-  return preloader;
-}
-
-SubresourceRedirectOriginsPreloader::SubresourceRedirectOriginsPreloader(
-    Document& document)
-    : Supplement<Document>(document) {}
-
-void SubresourceRedirectOriginsPreloader::Trace(Visitor* visitor) const {
-  Supplement<Document>::Trace(visitor);
-}
-
-void SubresourceRedirectOriginsPreloader::AddImagePreloadRequest(
-    const KURL& base_url,
-    const String& resource_url) {
-  KURL url = base_url.IsEmpty() ? GetSupplementable()->CompleteURL(resource_url)
-                                : GetSupplementable()->CompleteURLWithOverride(
-                                      resource_url, base_url);
-
-  if (!url.IsValid())
-    return;
-
-  if (!url.ProtocolIsInHTTPFamily())
-    return;
-
-  auto origin = SecurityOrigin::Create(url);
-  if (origin->IsOpaque())
-    return;
-
-  origins_.insert(origin);
-}
-
-void SubresourceRedirectOriginsPreloader::PreloadOriginsNow() {
-  GetSupplementable()
-      ->GetFrame()
-      ->Client()
-      ->PreloadSubresourceOptimizationsForOrigins(origins_);
-  origins_.clear();
-}
-
-}  // namespace blink
diff --git a/third_party/blink/renderer/core/html/parser/subresource_redirect_origins_preloader.h b/third_party/blink/renderer/core/html/parser/subresource_redirect_origins_preloader.h
deleted file mode 100644
index c338898..0000000
--- a/third_party/blink/renderer/core/html/parser/subresource_redirect_origins_preloader.h
+++ /dev/null
@@ -1,57 +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.
-
-#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_HTML_PARSER_SUBRESOURCE_REDIRECT_ORIGINS_PRELOADER_H_
-#define THIRD_PARTY_BLINK_RENDERER_CORE_HTML_PARSER_SUBRESOURCE_REDIRECT_ORIGINS_PRELOADER_H_
-
-#include "third_party/blink/renderer/core/dom/document.h"
-#include "third_party/blink/renderer/platform/heap/handle.h"
-#include "third_party/blink/renderer/platform/supplementable.h"
-#include "third_party/blink/renderer/platform/weborigin/security_origin.h"
-#include "third_party/blink/renderer/platform/weborigin/security_origin_hash.h"
-#include "third_party/blink/renderer/platform/wtf/hash_set.h"
-#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
-
-namespace blink {
-
-// Helper class for preloading subresource redirect optimizations for images.
-// Tied with the lifetime of a Document.
-class SubresourceRedirectOriginsPreloader final
-    : public GarbageCollected<SubresourceRedirectOriginsPreloader>,
-      public Supplement<Document> {
- public:
-  static const char kSupplementName[];
-
-  explicit SubresourceRedirectOriginsPreloader(Document&);
-  SubresourceRedirectOriginsPreloader(
-      const SubresourceRedirectOriginsPreloader&) = delete;
-  SubresourceRedirectOriginsPreloader& operator=(
-      const SubresourceRedirectOriginsPreloader&) = delete;
-  virtual ~SubresourceRedirectOriginsPreloader() = default;
-
-  static SubresourceRedirectOriginsPreloader* From(Document&);
-
-  // Adds the origin computed from |base_url| and |resource_url| to origins that
-  // require preloading optimizations. |resource_url| is the URL of the
-  // resource, and origin is computed from it along with |base_url|. When
-  // |resource_url| is absolute, |base_url| will not be used. When
-  // |resource_url| is relative, |base_url| determines the base origin. For
-  // relative |resource_url| when |base_url| is empty, the document's base URL
-  // will be used.
-  void AddImagePreloadRequest(const KURL& base_url, const String& resource_url);
-
-  // Triggers preloading the optimizations.
-  void PreloadOriginsNow();
-
-  void Trace(Visitor*) const override;
-
- private:
-  // The origins for which subresource redirect optimizations should be
-  // preloaded.
-  WTF::HashSet<scoped_refptr<const SecurityOrigin>, SecurityOriginHash>
-      origins_;
-};
-}  // namespace blink
-
-#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_HTML_PARSER_SUBRESOURCE_REDIRECT_ORIGINS_PRELOADER_H_
diff --git a/third_party/blink/renderer/core/layout/DEPS b/third_party/blink/renderer/core/layout/DEPS
index 0159bb9..4b3822e1 100644
--- a/third_party/blink/renderer/core/layout/DEPS
+++ b/third_party/blink/renderer/core/layout/DEPS
@@ -3,6 +3,6 @@
     "+ui/native_theme/native_theme.h",
   ],
   "layout_document_transition_content*": [
-    "+cc/layers/shared_element_layer.h",
+    "+cc/layers/document_transition_content_layer.h",
   ],
 }
diff --git a/third_party/blink/renderer/core/layout/layout_document_transition_content.cc b/third_party/blink/renderer/core/layout/layout_document_transition_content.cc
index b60a9d3d..39ea75b6 100644
--- a/third_party/blink/renderer/core/layout/layout_document_transition_content.cc
+++ b/third_party/blink/renderer/core/layout/layout_document_transition_content.cc
@@ -12,7 +12,8 @@
 LayoutDocumentTransitionContent::LayoutDocumentTransitionContent(
     DocumentTransitionContentElement* element)
     : LayoutReplaced(element),
-      layer_(cc::SharedElementLayer::Create(element->resource_id())) {
+      layer_(
+          cc::DocumentTransitionContentLayer::Create(element->resource_id())) {
   SetIntrinsicSize(element->intrinsic_size());
 }
 
diff --git a/third_party/blink/renderer/core/layout/layout_document_transition_content.h b/third_party/blink/renderer/core/layout/layout_document_transition_content.h
index d4c42d9..bf5e202 100644
--- a/third_party/blink/renderer/core/layout/layout_document_transition_content.h
+++ b/third_party/blink/renderer/core/layout/layout_document_transition_content.h
@@ -6,7 +6,7 @@
 #define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_LAYOUT_DOCUMENT_TRANSITION_CONTENT_H_
 
 #include "base/memory/scoped_refptr.h"
-#include "cc/layers/shared_element_layer.h"
+#include "cc/layers/document_transition_content_layer.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/document_transition/document_transition_content_element.h"
 #include "third_party/blink/renderer/core/layout/layout_replaced.h"
@@ -37,7 +37,7 @@
   }
   CompositingReasons AdditionalCompositingReasons() const override;
 
-  scoped_refptr<cc::SharedElementLayer> layer_;
+  scoped_refptr<cc::DocumentTransitionContentLayer> layer_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/loader/document_loader.cc b/third_party/blink/renderer/core/loader/document_loader.cc
index 6863435..9a770c9 100644
--- a/third_party/blink/renderer/core/loader/document_loader.cc
+++ b/third_party/blink/renderer/core/loader/document_loader.cc
@@ -241,7 +241,6 @@
   AtomicString referrer;
   scoped_refptr<EncodedFormData> http_body;
   AtomicString http_content_type;
-  PreviewsState previews_state;
   absl::optional<WebOriginPolicy> origin_policy;
   scoped_refptr<const SecurityOrigin> requestor_origin;
   KURL unreachable_url;
@@ -430,14 +429,6 @@
   has_text_fragment_token_ = TextFragmentAnchor::GenerateNewToken(*this) ||
                              params_->has_text_fragment_token;
 
-  if (frame_->IsMainFrame()) {
-    previews_state_ = params_->previews_state;
-  } else {
-    // Subframes inherit previews state from the main frame.
-    if (auto* parent = DynamicTo<LocalFrame>(frame_->Tree().Parent()))
-      previews_state_ = parent->Loader().GetDocumentLoader()->previews_state_;
-  }
-
   document_policy_ = CreateDocumentPolicy();
 
   WebNavigationTimings& timings = params_->navigation_timings;
@@ -535,7 +526,6 @@
   params->is_cross_site_cross_browsing_context_group =
       is_cross_site_cross_browsing_context_group_;
   params->has_text_fragment_token = has_text_fragment_token_;
-  params->previews_state = previews_state_;
   // Origin trials must still work on the cloned document.
   params->initiator_origin_trial_features =
       CopyInitiatorOriginTrials(initiator_origin_trial_features_);
diff --git a/third_party/blink/renderer/core/loader/document_loader.h b/third_party/blink/renderer/core/loader/document_loader.h
index a89de04..16c02bf4 100644
--- a/third_party/blink/renderer/core/loader/document_loader.h
+++ b/third_party/blink/renderer/core/loader/document_loader.h
@@ -244,8 +244,6 @@
 
   DocumentLoadTiming& GetTiming() { return document_load_timing_; }
 
-  PreviewsState GetPreviewsState() const { return previews_state_; }
-
   struct InitialScrollState {
     DISALLOW_NEW();
     InitialScrollState()
@@ -505,7 +503,6 @@
   AtomicString referrer_;
   scoped_refptr<EncodedFormData> http_body_;
   AtomicString http_content_type_;
-  PreviewsState previews_state_;
   absl::optional<WebOriginPolicy> origin_policy_;
   const scoped_refptr<const SecurityOrigin> requestor_origin_;
   const KURL unreachable_url_;
diff --git a/third_party/blink/renderer/core/loader/frame_fetch_context.cc b/third_party/blink/renderer/core/loader/frame_fetch_context.cc
index 05f9979d..8628f3a 100644
--- a/third_party/blink/renderer/core/loader/frame_fetch_context.cc
+++ b/third_party/blink/renderer/core/loader/frame_fetch_context.cc
@@ -288,12 +288,6 @@
   return document_loader_->GetSubresourceFilter();
 }
 
-PreviewsState FrameFetchContext::previews_state() const {
-  if (GetResourceFetcherProperties().IsDetached())
-    return PreviewsTypes::kPreviewsUnspecified;
-  return document_loader_->GetPreviewsState();
-}
-
 LocalFrame* FrameFetchContext::GetFrame() const {
   return document_->GetFrame();
 }
@@ -377,13 +371,6 @@
   if (document_loader_->ForceFetchCacheMode())
     request.SetCacheMode(*document_loader_->ForceFetchCacheMode());
 
-  if (request.GetPreviewsState() == PreviewsTypes::kPreviewsUnspecified) {
-    PreviewsState request_previews_state = document_loader_->GetPreviewsState();
-    if (request_previews_state == PreviewsTypes::kPreviewsUnspecified)
-      request_previews_state = PreviewsTypes::kPreviewsOff;
-    request.SetPreviewsState(request_previews_state);
-  }
-
   GetLocalFrameClient()->DispatchWillSendRequest(request);
   FrameScheduler* frame_scheduler = GetFrame()->GetFrameScheduler();
   if (!for_redirect && frame_scheduler) {
diff --git a/third_party/blink/renderer/core/loader/frame_fetch_context.h b/third_party/blink/renderer/core/loader/frame_fetch_context.h
index 29d45f66..69fd9e9 100644
--- a/third_party/blink/renderer/core/loader/frame_fetch_context.h
+++ b/third_party/blink/renderer/core/loader/frame_fetch_context.h
@@ -164,7 +164,6 @@
   net::SiteForCookies GetSiteForCookies() const override;
   scoped_refptr<const SecurityOrigin> GetTopFrameOrigin() const override;
   SubresourceFilter* GetSubresourceFilter() const override;
-  PreviewsState previews_state() const override;
   bool AllowScriptFromSource(const KURL&) const override;
   bool ShouldBlockRequestByInspector(const KURL&) const override;
   void DispatchDidBlockRequest(const ResourceRequest&,
diff --git a/third_party/blink/renderer/core/loader/frame_fetch_context_test.cc b/third_party/blink/renderer/core/loader/frame_fetch_context_test.cc
index 6bf06b9..5042d865 100644
--- a/third_party/blink/renderer/core/loader/frame_fetch_context_test.cc
+++ b/third_party/blink/renderer/core/loader/frame_fetch_context_test.cc
@@ -90,7 +90,6 @@
                void(const ResourceRequest&, const ResourceResponse&));
   MOCK_METHOD0(UserAgent, String());
   MOCK_METHOD0(MayUseClientLoFiForImageRequests, bool());
-  MOCK_CONST_METHOD0(GetPreviewsStateForFrame, PreviewsState());
 };
 
 class FixedPolicySubresourceFilter : public WebDocumentSubresourceFilter {
diff --git a/third_party/blink/renderer/core/loader/image_loader.cc b/third_party/blink/renderer/core/loader/image_loader.cc
index 0c3446d..e277162 100644
--- a/third_party/blink/renderer/core/loader/image_loader.cc
+++ b/third_party/blink/renderer/core/loader/image_loader.cc
@@ -472,7 +472,6 @@
     ResourceRequest resource_request(url);
     if (update_behavior == kUpdateForcedReload) {
       resource_request.SetCacheMode(mojom::FetchCacheMode::kBypassCache);
-      resource_request.SetPreviewsState(PreviewsTypes::kPreviewsNoTransform);
     }
 
     resource_request.SetReferrerPolicy(referrer_policy);
diff --git a/third_party/blink/renderer/modules/accessibility/ax_layout_object.cc b/third_party/blink/renderer/modules/accessibility/ax_layout_object.cc
index 730f3ea..4a1d342 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_layout_object.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_layout_object.cc
@@ -189,7 +189,7 @@
       IsA<HTMLOListElement>(*parent)) {
     AtomicString role = AccessibleNode::GetPropertyOrARIAAttribute(
         parent, AOMStringProperty::kRole);
-    if (!role.IsEmpty() && role != "list")
+    if (!role.IsEmpty() && role != "list" && role != "directory")
       return true;
   }
   return false;
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object.cc b/third_party/blink/renderer/modules/accessibility/ax_object.cc
index afff75f2..8d7d0f92 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_object.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_object.cc
@@ -3215,8 +3215,13 @@
   if (!elem)
     return false;
 
-  // NOT focusable: inert elements.
-  if (elem->IsInert())
+  // NOT focusable: inert elements. Note we can't just call IsInert() here
+  // because UpdateCachedAttributeValuesIfNeeded() can end up calling
+  // CanSetFocusAttribute() again, which will then try to return
+  // cached_can_set_focus_attribute_, but we haven't set it yet.
+  bool are_cached_attributes_up_to_date =
+      AXObjectCache().ModificationCount() == last_modification_count_;
+  if (are_cached_attributes_up_to_date ? cached_is_inert_ : ComputeIsInert())
     return false;
 
   // NOT focusable: disabled form controls.
@@ -6325,7 +6330,7 @@
     }
     if (cached_values_only ? cached_is_hidden_via_style : IsHiddenViaStyle())
       string_builder = string_builder + " isHiddenViaCSS";
-    if (GetNode() && GetNode()->IsInert())
+    if (cached_values_only ? cached_is_inert_ : IsInert())
       string_builder = string_builder + " isInert";
     if (IsMissingParent())
       string_builder = string_builder + " isMissingParent";
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object_test.cc b/third_party/blink/renderer/modules/accessibility/ax_object_test.cc
index 9adfde96..27a0511 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_object_test.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_object_test.cc
@@ -1443,5 +1443,51 @@
   ASSERT_TRUE(ax_span_text->IsInert());
 }
 
+TEST_F(AccessibilityTest, CanSetFocusInCanvasFallbackContent) {
+  ScopedInertAttributeForTest enabled_scope(true);
+  NonThrowableExceptionState exception_state;
+  SetBodyInnerHTML(R"HTML(
+    <canvas>
+      <section style="display: none">
+        <div tabindex="-1" id="div"></div>
+        <span tabindex="-1" id="span"></div>
+        <a tabindex="-1" id="a"></a>
+      </section>
+      <section style="display: none" inert>
+        <div tabindex="-1" id="div-inert"></div>
+        <span tabindex="-1" id="span-inert"></div>
+        <a tabindex="-1" id="a-inert"></a>
+      </section>
+    </div>
+  )HTML");
+
+  // Elements being used as relevant canvas fallback content can be focusable,
+  // even in a display:none subtree.
+  AXObject* div = GetAXObjectByElementId("div");
+  ASSERT_NE(div, nullptr);
+  ASSERT_TRUE(div->CanSetFocusAttribute());
+
+  AXObject* span = GetAXObjectByElementId("span");
+  ASSERT_NE(span, nullptr);
+  ASSERT_TRUE(span->CanSetFocusAttribute());
+
+  AXObject* a = GetAXObjectByElementId("a");
+  ASSERT_NE(a, nullptr);
+  ASSERT_TRUE(a->CanSetFocusAttribute());
+
+  // But they are not focusable if expressly inert.
+  AXObject* div_inert = GetAXObjectByElementId("div-inert");
+  ASSERT_NE(div_inert, nullptr);
+  ASSERT_FALSE(div_inert->CanSetFocusAttribute());
+
+  AXObject* span_inert = GetAXObjectByElementId("span-inert");
+  ASSERT_NE(span_inert, nullptr);
+  ASSERT_FALSE(span_inert->CanSetFocusAttribute());
+
+  AXObject* a_inert = GetAXObjectByElementId("a-inert");
+  ASSERT_NE(a_inert, nullptr);
+  ASSERT_FALSE(a_inert->CanSetFocusAttribute());
+}
+
 }  // namespace test
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_pattern.cc b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_pattern.cc
index c06e40d..11a5f3f9 100644
--- a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_pattern.cc
+++ b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_pattern.cc
@@ -56,7 +56,7 @@
 CanvasPattern::CanvasPattern(scoped_refptr<Image> image,
                              Pattern::RepeatMode repeat,
                              bool origin_clean)
-    : pattern_(Pattern::CreateImagePattern(std::move(image), repeat)),
+    : pattern_(Pattern::CreateImagePattern(image, repeat)),
       origin_clean_(origin_clean) {
   if (identifiability_study_helper_.ShouldUpdateBuilder()) {
     identifiability_study_helper_.UpdateBuilder(
diff --git a/third_party/blink/renderer/modules/webaudio/audio_context.cc b/third_party/blink/renderer/modules/webaudio/audio_context.cc
index 7c14d66c..a83953c 100644
--- a/third_party/blink/renderer/modules/webaudio/audio_context.cc
+++ b/third_party/blink/renderer/modules/webaudio/audio_context.cc
@@ -96,7 +96,7 @@
 
   if (document.IsDetached()) {
     exception_state.ThrowDOMException(
-        DOMExceptionCode::kNotSupportedError,
+        DOMExceptionCode::kInvalidStateError,
         "Cannot create AudioContext on a detached document.");
     return nullptr;
   }
diff --git a/third_party/blink/renderer/modules/webrtc/webrtc_audio_device_impl.h b/third_party/blink/renderer/modules/webrtc/webrtc_audio_device_impl.h
index 26d8784a..460c556 100644
--- a/third_party/blink/renderer/modules/webrtc/webrtc_audio_device_impl.h
+++ b/third_party/blink/renderer/modules/webrtc/webrtc_audio_device_impl.h
@@ -13,6 +13,7 @@
 
 #include "base/memory/scoped_refptr.h"
 #include "base/threading/thread_checker.h"
+#include "base/time/time.h"
 #include "base/unguessable_token.h"
 #include "third_party/blink/renderer/modules/modules_export.h"
 #include "third_party/blink/renderer/modules/webrtc/webrtc_audio_device_not_impl.h"
diff --git a/third_party/blink/renderer/platform/exported/resource_load_info_notifier_wrapper.cc b/third_party/blink/renderer/platform/exported/resource_load_info_notifier_wrapper.cc
index abc5e63..7ab5af8 100644
--- a/third_party/blink/renderer/platform/exported/resource_load_info_notifier_wrapper.cc
+++ b/third_party/blink/renderer/platform/exported/resource_load_info_notifier_wrapper.cc
@@ -6,6 +6,7 @@
 
 #include "base/bind.h"
 #include "base/metrics/histogram_macros.h"
+#include "build/build_config.h"
 #include "net/base/ip_endpoint.h"
 #include "net/url_request/redirect_info.h"
 #include "services/network/public/cpp/url_loader_completion_status.h"
@@ -85,8 +86,7 @@
 }
 
 void ResourceLoadInfoNotifierWrapper::NotifyResourceResponseReceived(
-    network::mojom::URLResponseHeadPtr response_head,
-    PreviewsState previews_state) {
+    network::mojom::URLResponseHeadPtr response_head) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   if (response_head->network_accessed) {
     if (resource_load_info_->request_destination ==
@@ -114,8 +114,7 @@
     if (weak_wrapper_resource_load_info_notifier_) {
       weak_wrapper_resource_load_info_notifier_->NotifyResourceResponseReceived(
           resource_load_info_->request_id, resource_load_info_->final_url,
-          std::move(response_head), resource_load_info_->request_destination,
-          previews_state);
+          std::move(response_head), resource_load_info_->request_destination);
     }
     return;
   }
@@ -131,8 +130,7 @@
           &mojom::ResourceLoadInfoNotifier::NotifyResourceResponseReceived,
           weak_wrapper_resource_load_info_notifier_,
           resource_load_info_->request_id, resource_load_info_->final_url,
-          std::move(response_head), resource_load_info_->request_destination,
-          previews_state));
+          std::move(response_head), resource_load_info_->request_destination));
 }
 
 void ResourceLoadInfoNotifierWrapper::NotifyResourceTransferSizeUpdated(
diff --git a/third_party/blink/renderer/platform/exported/weak_wrapper_resource_load_info_notifier.cc b/third_party/blink/renderer/platform/exported/weak_wrapper_resource_load_info_notifier.cc
index 74e1ee17..3bf1917 100644
--- a/third_party/blink/renderer/platform/exported/weak_wrapper_resource_load_info_notifier.cc
+++ b/third_party/blink/renderer/platform/exported/weak_wrapper_resource_load_info_notifier.cc
@@ -4,6 +4,7 @@
 
 #include "third_party/blink/public/platform/weak_wrapper_resource_load_info_notifier.h"
 
+#include "build/build_config.h"
 #include "services/network/public/mojom/url_response_head.mojom.h"
 #include "third_party/blink/public/mojom/loader/resource_load_info.mojom.h"
 
@@ -28,12 +29,10 @@
     int64_t request_id,
     const GURL& final_url,
     network::mojom::URLResponseHeadPtr response_head,
-    network::mojom::RequestDestination request_destination,
-    int32_t previews_state) {
+    network::mojom::RequestDestination request_destination) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   resource_load_info_notifier_->NotifyResourceResponseReceived(
-      request_id, final_url, std::move(response_head), request_destination,
-      previews_state);
+      request_id, final_url, std::move(response_head), request_destination);
 }
 
 void WeakWrapperResourceLoadInfoNotifier::NotifyResourceTransferSizeUpdated(
diff --git a/third_party/blink/renderer/platform/exported/web_url_request.cc b/third_party/blink/renderer/platform/exported/web_url_request.cc
index b7b8e5e..fe5804ab 100644
--- a/third_party/blink/renderer/platform/exported/web_url_request.cc
+++ b/third_party/blink/renderer/platform/exported/web_url_request.cc
@@ -355,14 +355,6 @@
   return resource_request_->SetFetchIntegrity(integrity);
 }
 
-PreviewsState WebURLRequest::GetPreviewsState() const {
-  return resource_request_->GetPreviewsState();
-}
-
-void WebURLRequest::SetPreviewsState(PreviewsState previews_state) {
-  return resource_request_->SetPreviewsState(previews_state);
-}
-
 const scoped_refptr<WebURLRequestExtraData>&
 WebURLRequest::GetURLRequestExtraData() const {
   return resource_request_->GetURLRequestExtraData();
diff --git a/third_party/blink/renderer/platform/graphics/static_bitmap_image_to_video_frame_copier.cc b/third_party/blink/renderer/platform/graphics/static_bitmap_image_to_video_frame_copier.cc
index 59e3e62..8cc935f 100644
--- a/third_party/blink/renderer/platform/graphics/static_bitmap_image_to_video_frame_copier.cc
+++ b/third_party/blink/renderer/platform/graphics/static_bitmap_image_to_video_frame_copier.cc
@@ -4,6 +4,7 @@
 
 #include "third_party/blink/renderer/platform/graphics/static_bitmap_image_to_video_frame_copier.h"
 
+#include "base/callback_helpers.h"
 #include "base/trace_event/trace_event.h"
 #include "build/build_config.h"
 #include "components/viz/common/resources/resource_format_utils.h"
@@ -84,6 +85,7 @@
 
   // Try async reading if image is texture backed.
   if (image->CurrentFrameKnownToBeOpaque() || can_discard_alpha_) {
+    auto split_callback = base::SplitOnceCallback(std::move(callback));
     if (accelerated_frame_pool_enabled_) {
       if (!accelerated_frame_pool_) {
         accelerated_frame_pool_ =
@@ -110,7 +112,7 @@
           };
       auto blit_done_callback =
           WTF::Bind(blit_done_lambda, weak_ptr_factory_.GetWeakPtr(),
-                    context_provider, image, std::move(callback));
+                    context_provider, image, std::move(split_callback.first));
 
       // TODO(https://crbug.com/1224279): This assumes that all
       // StaticBitmapImages are 8-bit sRGB. Expose the color space and pixel
@@ -130,7 +132,7 @@
       return;
     }
     ReadYUVPixelsAsync(image, context_provider->ContextProvider(),
-                       std::move(callback));
+                       std::move(split_callback.second));
   } else {
     ReadARGBPixelsAsync(image, context_provider->ContextProvider(),
                         std::move(callback));
diff --git a/third_party/blink/renderer/platform/loader/fetch/fetch_context.h b/third_party/blink/renderer/platform/loader/fetch/fetch_context.h
index 2050c78..293fb09 100644
--- a/third_party/blink/renderer/platform/loader/fetch/fetch_context.h
+++ b/third_party/blink/renderer/platform/loader/fetch/fetch_context.h
@@ -177,10 +177,6 @@
     return false;
   }
 
-  virtual PreviewsState previews_state() const {
-    return PreviewsTypes::kPreviewsUnspecified;
-  }
-
   // Returns a receiver corresponding to a request with |request_id|.
   // Null if the request has not been intercepted by a service worker.
   virtual mojo::PendingReceiver<mojom::blink::WorkerTimingContainer>
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource.cc b/third_party/blink/renderer/platform/loader/fetch/resource.cc
index 4ded5ed3..79a4f39 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/resource.cc
@@ -920,10 +920,6 @@
   resource_request_.SetCacheMode(mojom::FetchCacheMode::kBypassCache);
 }
 
-void Resource::SetPreviewsState(PreviewsState previews_state) {
-  resource_request_.SetPreviewsState(previews_state);
-}
-
 void Resource::ClearRangeRequestHeader() {
   resource_request_.ClearHttpHeaderField("range");
 }
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource.h b/third_party/blink/renderer/platform/loader/fetch/resource.h
index 30d6405..087cc58 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource.h
+++ b/third_party/blink/renderer/platform/loader/fetch/resource.h
@@ -484,7 +484,6 @@
   }
 
   void SetCachePolicyBypassingCache();
-  void SetPreviewsState(PreviewsState);
   void ClearRangeRequestHeader();
 
   SharedBuffer* Data() const { return data_.get(); }
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_request.cc b/third_party/blink/renderer/platform/loader/fetch/resource_request.cc
index 3c1fe17..d09c54aa 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_request.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_request.cc
@@ -102,7 +102,6 @@
       initial_priority_(ResourceLoadPriority::kUnresolved),
       priority_(ResourceLoadPriority::kUnresolved),
       intra_priority_value_(0),
-      previews_state_(PreviewsTypes::kPreviewsUnspecified),
       request_context_(mojom::blink::RequestContextType::UNSPECIFIED),
       destination_(network::mojom::RequestDestination::kEmpty),
       mode_(network::mojom::RequestMode::kNoCors),
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_request.h b/third_party/blink/renderer/platform/loader/fetch/resource_request.h
index eecbe6f..bd83ffb 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_request.h
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_request.h
@@ -342,11 +342,6 @@
     fetch_integrity_ = integrity;
   }
 
-  PreviewsState GetPreviewsState() const { return previews_state_; }
-  void SetPreviewsState(PreviewsState previews_state) {
-    previews_state_ = previews_state;
-  }
-
   bool CacheControlContainsNoCache() const;
   bool CacheControlContainsNoStore() const;
   bool HasCacheValidatorFields() const;
@@ -580,7 +575,6 @@
   ResourceLoadPriority initial_priority_;
   ResourceLoadPriority priority_;
   int intra_priority_value_;
-  PreviewsState previews_state_;
   scoped_refptr<WebURLRequestExtraData> url_request_extra_data_;
   mojom::blink::RequestContextType request_context_;
   network::mojom::RequestDestination destination_;
diff --git a/third_party/blink/renderer/platform/loader/fetch/url_loader/navigation_body_loader.cc b/third_party/blink/renderer/platform/loader/fetch/url_loader/navigation_body_loader.cc
index 9f15f58..504377f 100644
--- a/third_party/blink/renderer/platform/loader/fetch/url_loader/navigation_body_loader.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/url_loader/navigation_body_loader.cc
@@ -134,7 +134,7 @@
 
   base::Time response_head_response_time = response_head_->response_time;
   resource_load_info_notifier_wrapper_->NotifyResourceResponseReceived(
-      std::move(response_head_), PreviewsTypes::PREVIEWS_OFF);
+      std::move(response_head_));
 
   if (code_cache_host) {
     if (code_cache_data_) {
diff --git a/third_party/blink/renderer/platform/loader/fetch/url_loader/request_conversion.cc b/third_party/blink/renderer/platform/loader/fetch/url_loader/request_conversion.cc
index dfade0bf..eddee88 100644
--- a/third_party/blink/renderer/platform/loader/fetch/url_loader/request_conversion.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/url_loader/request_conversion.cc
@@ -351,9 +351,6 @@
   dest->has_user_gesture = src.HasUserGesture();
   dest->enable_load_timing = true;
   dest->enable_upload_progress = src.ReportUploadProgress();
-  // TODO(ryansturm): Remove dest->previews_state once it is no
-  // longer used in a network delegate. https://crbug.com/842233
-  dest->previews_state = static_cast<int>(src.GetPreviewsState());
   dest->throttling_profile_id = src.GetDevToolsToken();
   dest->trust_token_params = ConvertTrustTokenParams(src.TrustTokenParams());
 
diff --git a/third_party/blink/renderer/platform/loader/fetch/url_loader/web_resource_request_sender.cc b/third_party/blink/renderer/platform/loader/fetch/url_loader/web_resource_request_sender.cc
index 3fb67038..48456dc 100644
--- a/third_party/blink/renderer/platform/loader/fetch/url_loader/web_resource_request_sender.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/url_loader/web_resource_request_sender.cc
@@ -270,8 +270,6 @@
           request_id, request->url, request->method, request->referrer,
           request_info_->request_destination, request->priority);
 
-  request_info_->previews_state = request->previews_state;
-
   auto client = std::make_unique<MojoURLLoaderClient>(
       this, loading_task_runner, url_loader_factory->BypassRedirectChecks(),
       request->url, back_forward_cache_loader_helper);
@@ -455,8 +453,7 @@
     return;
 
   request_info_->resource_load_info_notifier_wrapper
-      ->NotifyResourceResponseReceived(std::move(response_head),
-                                       request_info_->previews_state);
+      ->NotifyResourceResponseReceived(std::move(response_head));
 }
 
 void WebResourceRequestSender::OnReceivedCachedMetadata(
diff --git a/third_party/blink/renderer/platform/loader/fetch/url_loader/web_url_loader.cc b/third_party/blink/renderer/platform/loader/fetch/url_loader/web_url_loader.cc
index 65b80704..7682111 100644
--- a/third_party/blink/renderer/platform/loader/fetch/url_loader/web_url_loader.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/url_loader/web_url_loader.cc
@@ -56,7 +56,6 @@
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/blink/public/common/features.h"
 #include "third_party/blink/public/common/loader/mime_sniffing_throttle.h"
-#include "third_party/blink/public/common/loader/previews_state.h"
 #include "third_party/blink/public/common/loader/referrer_utils.h"
 #include "third_party/blink/public/common/loader/resource_type_util.h"
 #include "third_party/blink/public/common/mime_util/mime_util.h"
diff --git a/third_party/blink/renderer/platform/loader/fetch/url_loader/worker_main_script_loader.cc b/third_party/blink/renderer/platform/loader/fetch/url_loader/worker_main_script_loader.cc
index 600fda84..8bdf508 100644
--- a/third_party/blink/renderer/platform/loader/fetch/url_loader/worker_main_script_loader.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/url_loader/worker_main_script_loader.cc
@@ -71,7 +71,7 @@
       response_head->ssl_info.has_value(), request_id_);
   resource_response_ = response.ToResourceResponse();
   resource_load_info_notifier_wrapper_->NotifyResourceResponseReceived(
-      std::move(response_head), PreviewsTypes::kPreviewsUnspecified);
+      std::move(response_head));
 
   ResourceRequest resource_request(initial_request_);
   resource_load_observer_->DidReceiveResponse(
diff --git a/third_party/blink/renderer/platform/loader/fetch/url_loader/worker_main_script_loader_unittest.cc b/third_party/blink/renderer/platform/loader/fetch/url_loader/worker_main_script_loader_unittest.cc
index 1afb0b56..58cfbb1 100644
--- a/third_party/blink/renderer/platform/loader/fetch/url_loader/worker_main_script_loader_unittest.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/url_loader/worker_main_script_loader_unittest.cc
@@ -122,8 +122,7 @@
         int64_t request_id,
         const GURL& final_url,
         network::mojom::URLResponseHeadPtr head,
-        network::mojom::RequestDestination request_destination,
-        int32_t previews_state) override {}
+        network::mojom::RequestDestination request_destination) override {}
     void NotifyResourceTransferSizeUpdated(
         int64_t request_id,
         int32_t transfer_size_diff) override {}
diff --git a/third_party/blink/renderer/platform/media/resource_multi_buffer_data_provider.cc b/third_party/blink/renderer/platform/media/resource_multi_buffer_data_provider.cc
index a6b4dcc..f6723f0 100644
--- a/third_party/blink/renderer/platform/media/resource_multi_buffer_data_provider.cc
+++ b/third_party/blink/renderer/platform/media/resource_multi_buffer_data_provider.cc
@@ -18,7 +18,6 @@
 #include "net/http/http_byte_range.h"
 #include "net/http/http_request_headers.h"
 #include "services/network/public/cpp/cors/cors.h"
-#include "third_party/blink/public/common/loader/previews_state.h"
 #include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom.h"
 #include "third_party/blink/public/platform/media/resource_fetch_context.h"
 #include "third_party/blink/public/platform/media/url_index.h"
@@ -88,18 +87,6 @@
       WebString::FromUTF8(
           net::HttpByteRange::RightUnbounded(byte_pos()).GetHeaderValue()));
 
-  if (url_data_->length() == kPositionNotSpecified &&
-      url_data_->CachedSize() == 0 && url_data_->BytesReadFromCache() == 0 &&
-      WebNetworkStateNotifier::SaveDataEnabled() &&
-      (url_data_->url().SchemeIs(url::kHttpScheme) ||
-       url_data_->url().SchemeIs(url::kHttpsScheme))) {
-    // This lets the data reduction proxy know that we don't have any previously
-    // cached data for this resource. We can only send it if this is the first
-    // request for this resource.
-    request.SetPreviewsState(request.GetPreviewsState() |
-                             PreviewsTypes::kSrcVideoRedirectOn);
-  }
-
   // We would like to send an if-match header with the request to
   // tell the remote server that we really can't handle files other
   // than the one we already started playing. Unfortunately, doing
diff --git a/third_party/blink/renderer/platform/media/resource_multi_buffer_data_provider_unittest.cc b/third_party/blink/renderer/platform/media/resource_multi_buffer_data_provider_unittest.cc
index fb410e3d..fa36c71 100644
--- a/third_party/blink/renderer/platform/media/resource_multi_buffer_data_provider_unittest.cc
+++ b/third_party/blink/renderer/platform/media/resource_multi_buffer_data_provider_unittest.cc
@@ -40,7 +40,6 @@
 using ::testing::Truly;
 
 const char kHttpUrl[] = "http://test";
-const char kHttpsUrl[] = "https://test";
 const char kHttpRedirect[] = "http://test/ing";
 const char kEtag[] = "\"arglebargle glopy-glyf?\"";
 
@@ -50,18 +49,8 @@
 
 enum NetworkState { kNone, kLoaded, kLoading };
 
-static bool want_frfr = false;
-
-// Predicate that checks the Accept-Encoding request header and FRFR previews
-// state.
-static bool CorrectAcceptEncodingAndPreviewsState(
-    const WebURLRequest& request) {
-  bool has_frfr =
-      request.GetPreviewsState() & PreviewsTypes::kSrcVideoRedirectOn;
-  if (has_frfr != want_frfr) {
-    return false;
-  }
-
+// Predicate that checks the Accept-Encoding request header.
+static bool CorrectAcceptEncoding(const WebURLRequest& request) {
   std::string value = request
                           .HttpHeaderField(WebString::FromUTF8(
                               net::HttpRequestHeaders::kAcceptEncoding))
@@ -87,7 +76,6 @@
       const ResourceMultiBufferDataProviderTest&) = delete;
 
   void Initialize(const char* url, int first_position) {
-    want_frfr = false;
     gurl_ = GURL(url);
     url_data_ = url_index_.GetByUrl(gurl_, UrlData::CORS_UNSPECIFIED,
                                     UrlIndex::kNormal);
@@ -214,8 +202,7 @@
       const WebAssociatedURLLoaderOptions& options) {
     auto url_loader = std::make_unique<NiceMock<MockWebAssociatedURLLoader>>();
     EXPECT_CALL(*url_loader.get(),
-                LoadAsynchronously(Truly(CorrectAcceptEncodingAndPreviewsState),
-                                   loader_));
+                LoadAsynchronously(Truly(CorrectAcceptEncoding), loader_));
     return url_loader;
   }
 
@@ -335,50 +322,4 @@
   StopWhenLoad();
 }
 
-TEST_F(ResourceMultiBufferDataProviderTest, TestSaveDataFRFRPreviewsState) {
-  struct TestCase {
-    std::string label;
-    bool enable_save_data;
-    std::string url;
-    bool want_frfr_previews_enabled;
-  };
-  const TestCase kTestCases[]{
-      {
-          "SaveData on, HTTP URL: FRFR previews state should exist.",
-          true,
-          kHttpUrl,
-          true,
-      },
-      {
-          "SaveData on, HTTPS URL: FRFR previews state should exist.",
-          true,
-          kHttpsUrl,
-          true,
-      },
-      {
-          "SaveData off, HTTP URL: FRFR previews state should not exist.",
-          false,
-          kHttpUrl,
-          false,
-      },
-      {
-          "SaveData off, HTTPS URL: FRFR previews state should not exist.",
-          false,
-          kHttpsUrl,
-          false,
-      },
-  };
-  for (const TestCase& test_case : kTestCases) {
-    SCOPED_TRACE(test_case.label);
-    WebNetworkStateNotifier::SetSaveDataEnabled(test_case.enable_save_data);
-
-    Initialize(test_case.url.c_str(), 0);
-    want_frfr = test_case.want_frfr_previews_enabled;
-
-    Start();
-    FullResponse(1024);
-    StopWhenLoad();
-  }
-}
-
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/webrtc/webrtc_source.h b/third_party/blink/renderer/platform/webrtc/webrtc_source.h
index 376d865..5efe1825 100644
--- a/third_party/blink/renderer/platform/webrtc/webrtc_source.h
+++ b/third_party/blink/renderer/platform/webrtc/webrtc_source.h
@@ -9,6 +9,7 @@
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
 
 namespace base {
+class TimeDelta;
 class UnguessableToken;
 }
 
diff --git a/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py b/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py
index 442efef9..d090b7ac 100755
--- a/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py
+++ b/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py
@@ -458,7 +458,7 @@
 
             # Document transitions
             'cc::DocumentTransitionRequest',
-            'cc::SharedElementLayer',
+            'cc::DocumentTransitionContentLayer',
             'viz::SharedElementResourceId',
 
             # base/types/strong_alias.h
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index f48e7f5..cb8b5618 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -197,34 +197,12 @@
 # These tests were disabled to allow enabling WebAssembly Reference Types.
 crbug.com/v8/7581 external/wpt/wasm/jsapi/global/type.tentative.any.html [ Failure Pass ]
 crbug.com/v8/7581 external/wpt/wasm/jsapi/global/type.tentative.any.worker.html [ Failure Pass ]
-crbug.com/v8/7581 external/wpt/wasm/jsapi/table/constructor-reftypes.tentative.any.html [ Failure Pass ]
-crbug.com/v8/7581 external/wpt/wasm/jsapi/table/constructor-reftypes.tentative.any.worker.html [ Failure Pass ]
-crbug.com/v8/7581 external/wpt/wasm/jsapi/table/set-reftypes.tentative.any.html [ Failure Pass ]
-crbug.com/v8/7581 external/wpt/wasm/jsapi/table/set-reftypes.tentative.any.worker.html [ Failure Pass ]
 
 # These tests block fixes done in V8. The tests run on the V8 bots as well.
 crbug.com/v8/12227 external/wpt/wasm/jsapi/exception/toString.tentative.any.html [ Failure Pass ]
 crbug.com/v8/12227 external/wpt/wasm/jsapi/exception/toString.tentative.any.worker.html [ Failure Pass ]
-crbug.com/v8/12227 external/wpt/wasm/jsapi/memory/constructor-types.tentative.any.html [ Failure Pass ]
-crbug.com/v8/12227 external/wpt/wasm/jsapi/memory/constructor-types.tentative.any.worker.html [ Failure Pass ]
-crbug.com/v8/12227 external/wpt/wasm/jsapi/memory/type.tentative.any.html [ Failure Pass ]
-crbug.com/v8/12227 external/wpt/wasm/jsapi/memory/type.tentative.any.worker.html [ Failure Pass ]
-crbug.com/v8/12227 external/wpt/wasm/jsapi/memory/types.tentative.any.html [ Failure Pass ]
-crbug.com/v8/12227 external/wpt/wasm/jsapi/memory/types.tentative.any.worker.html [ Failure Pass ]
-crbug.com/v8/12227 external/wpt/wasm/jsapi/prototypes.any.html [ Failure Pass ]
-crbug.com/v8/12227 external/wpt/wasm/jsapi/prototypes.any.worker.html [ Failure Pass ]
 crbug.com/v8/12227 external/wpt/wasm/jsapi/table/type.tentative.any.html [ Failure Pass ]
 crbug.com/v8/12227 external/wpt/wasm/jsapi/table/type.tentative.any.worker.html [ Failure Pass ]
-crbug.com/v8/12227 external/wpt/wasm/jsapi/table/constructor-types.tentative.any.html [ Failure Pass ]
-crbug.com/v8/12227 external/wpt/wasm/jsapi/table/constructor-types.tentative.any.worker.html [ Failure Pass ]
-crbug.com/v8/12227 external/wpt/wasm/jsapi/table/grow-reftypes.tentative.any.html [ Failure Pass ]
-crbug.com/v8/12227 external/wpt/wasm/jsapi/table/grow-reftypes.tentative.any.worker.html [ Failure Pass ]
-crbug.com/v8/12227 external/wpt/wasm/jsapi/table/get-set.any.html [ Failure Pass ]
-crbug.com/v8/12227 external/wpt/wasm/jsapi/table/get-set.any.worker.html [ Failure Pass ]
-crbug.com/v8/12227 external/wpt/wasm/jsapi/table/constructor.any.html [ Failure Pass ]
-crbug.com/v8/12227 external/wpt/wasm/jsapi/table/constructor.any.worker.html [ Failure Pass ]
-crbug.com/v8/12227 external/wpt/wasm/jsapi/table/grow.any.html [ Failure Pass ]
-crbug.com/v8/12227 external/wpt/wasm/jsapi/table/grow.any.worker.html [ Failure Pass ]
 
 # Sheriff on 2020-09-03
 crbug.com/1124352 media/picture-in-picture/clear-after-request.html [ Crash Pass ]
@@ -4007,7 +3985,6 @@
 crbug.com/626703 external/wpt/fetch/connection-pool/network-partition-key.html [ Failure Skip Timeout ]
 crbug.com/626703 external/wpt/input-events/input-events-get-target-ranges-backspace.tentative.html [ Failure Skip Timeout ]
 crbug.com/626703 external/wpt/input-events/input-events-get-target-ranges-forwarddelete.tentative.html [ Failure Skip Timeout ]
-crbug.com/626703 external/wpt/wasm/jsapi/functions/incumbent.html [ Crash ]
 crbug.com/626703 [ Mac ] external/wpt/dom/events/scrolling/scrollend-event-for-user-scroll.html [ Failure Timeout ]
 crbug.com/626703 [ Win ] external/wpt/dom/events/scrolling/scrollend-event-for-user-scroll.html [ Failure Timeout ]
 crbug.com/626703 [ Mac10.15 ] external/wpt/webaudio/the-audio-api/the-mediastreamaudiosourcenode-interface/mediastreamaudiosourcenode-routing.html [ Timeout ]
@@ -4683,8 +4660,6 @@
 crbug.com/874302 external/wpt/wasm/serialization/module/broadcastchannel-success-and-failure.html [ Timeout ]
 crbug.com/874302 external/wpt/wasm/serialization/module/broadcastchannel-success.html [ Timeout ]
 
-crbug.com/877286 external/wpt/wasm/serialization/module/no-transferring.html [ Failure ]
-
 crbug.com/831509 external/wpt/service-workers/service-worker/skip-waiting-installed.https.html [ Failure Pass ]
 
 # Fullscreen tests are failed because of consuming user activation on fullscreen
@@ -5849,6 +5824,13 @@
 # Sheriff 2020-03-08
 crbug.com/1059645 external/wpt/pointerevents/pointerevent_coalesced_events_attributes.html [ Failure Pass ]
 
+# Temporarily disable to land DevTools changes
+crbug.com/1205856 http/tests/devtools/application-panel/resources-panel-iframe-idb.js [ Failure Pass ]
+crbug.com/1205856 http/tests/devtools/application-panel/resources-panel-on-navigation.js [ Failure Pass ]
+crbug.com/1205856 http/tests/devtools/application-panel/resources-panel-resource-preview.js [ Failure Pass ]
+crbug.com/1205856 http/tests/devtools/application-panel/resources-panel-selection-on-reload.js [ Failure Pass ]
+crbug.com/1205856 http/tests/devtools/application-panel/resources-panel-websql.js [ Failure Pass ]
+
 # Failing on Fuchsia due to dependency on FuchsiaMediaResourceProvider, which is not implemented in content_shell.
 crbug.com/1061226 [ Fuchsia ] fast/mediastream/MediaStream-onactive-oninactive.html [ Skip ]
 crbug.com/1061226 [ Fuchsia ] external/wpt/html/cross-origin-embedder-policy/credentialless/video.tentative.https.window.html [ Skip ]
@@ -5905,10 +5887,6 @@
 crbug.com/1084256 [ Mac ] http/tests/misc/insert-iframe-into-xml-document-before-xsl-transform.html [ Failure Pass ]
 crbug.com/1084276 [ Mac ] http/tests/security/offscreencanvas-placeholder-read-blocked-no-crossorigin.html [ Crash Failure Pass ]
 
-# Disabled to prepare fixing the tests in V8
-crbug.com/v8/10556 external/wpt/wasm/jsapi/instance/constructor-caching.any.html [ Failure Pass ]
-crbug.com/v8/10556 external/wpt/wasm/jsapi/instance/constructor-caching.any.worker.html [ Failure Pass ]
-
 # Temporarily disabled to land the new wasm exception handling JS API
 crbug.com/v8/11992 external/wpt/wasm/jsapi/exception/* [ Skip ]
 crbug.com/v8/11992 external/wpt/wasm/jsapi/tag/* [ Skip ]
@@ -5986,10 +5964,6 @@
 crbug.com/1093445 http/tests/loading/pdf-commit-load-callbacks.html [ Failure Pass ]
 crbug.com/1093497 http/tests/history/client-redirect-after-push-state.html [ Failure Pass ]
 
-# Tests blocking WPT importer
-crbug.com/1098844 external/wpt/wasm/jsapi/functions/entry.html [ Crash Pass Timeout ]
-crbug.com/1098844 external/wpt/wasm/jsapi/functions/entry-different-function-realm.html [ Crash Pass Timeout ]
-
 #Sheriff 2020-06-25
 crbug.com/1010170 media/video-played-reset.html [ Failure Pass ]
 
@@ -7620,10 +7594,6 @@
 crbug.com/1286944 [ Mac ] virtual/off-main-thread-css-paint/external/wpt/css/css-paint-api/hidpi/canvas-transform.https.html [ Failure Pass ]
 crbug.com/1286944 virtual/off-main-thread-css-paint/external/wpt/css/css-paint-api/parse-input-arguments-011.https.html [ Failure Pass ]
 
-# Temporarily disable failing tests to unblock WebRTC roll.
-crbug.com/webrtc/13540 virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-setRemoteDescription-rollback.html [ Failure Pass ]
-crbug.com/webrtc/13540 virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpTransceiver.https.html [ Failure Pass ]
-
 # Sheriff 2022-01-17
 crbug.com/1287928 [ Mac ] external/wpt/webrtc/RTCPeerConnection-perfect-negotiation-stress-glare.https.html [ Failure Pass ]
 crbug.com/1233938 http/tests/notifications/click-shared-worker.html [ Pass Timeout ]
@@ -7636,3 +7606,21 @@
 
 # Sheriff 2022-01-18
 crbug.com/1287067 virtual/fenced-frame-mparch/wpt_internal/fenced_frame/embedder-require-corp.https.html [ Failure Pass ]
+crbug.com/1288264 [ Win ] http/tests/fetch/serviceworker-proxied/thorough/redirect-loop-base-https-other-https.html [ Skip Pass Timeout ]
+crbug.com/1288264 [ Win ] http/tests/fetch/window/thorough/cors-preflight2-base-https-other-https.html [ Skip Pass Timeout ]
+crbug.com/1288264 [ Win ] http/tests/fetch/serviceworker/thorough/nocors-base-https-other-https.html [ Skip Pass Timeout ]
+crbug.com/1288264 [ Win ] http/tests/security/aboutBlank/xss-DENIED-navigate-opener-document-write.html [ Pass Timeout ]
+crbug.com/1227911 [ Linux Release ] virtual/no-auto-wpt-origin-isolation/external/wpt/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-no-child1-no-subdomain-child2-yes-subdomainport.sub.https.html [ Failure Pass ]
+crbug.com/1227911 [ Mac10.14 ] virtual/no-auto-wpt-origin-isolation/external/wpt/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-no-child1-no-subdomain-child2-yes-subdomainport.sub.https.html [ Failure Pass ]
+crbug.com/1227911 [ Linux Release ] virtual/no-auto-wpt-origin-isolation/external/wpt/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-no-child1-yes-subdomain-child2-no-subdomain.sub.https.html [ Failure Pass ]
+crbug.com/1227911 [ Mac10.14 ] virtual/no-auto-wpt-origin-isolation/external/wpt/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-no-child1-yes-subdomain-child2-no-subdomain.sub.https.html [ Failure Pass ]
+crbug.com/1227911 [ Linux Release ] virtual/no-auto-wpt-origin-isolation/external/wpt/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-no-subdomain-child2-no-subdomain2.sub.https.html [ Failure Pass ]
+crbug.com/1227911 [ Mac10.14 ] virtual/no-auto-wpt-origin-isolation/external/wpt/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-no-subdomain-child2-no-subdomain2.sub.https.html [ Failure Pass ]
+crbug.com/1227911 [ Linux Release ] virtual/no-auto-wpt-origin-isolation/external/wpt/html/browsers/origin/origin-keyed-agent-clusters/iframe-navigation/parent-no-1-subdomain-yes-2-subdomain2-no.sub.https.html [ Failure Pass ]
+crbug.com/1227911 [ Mac10.14 ] virtual/no-auto-wpt-origin-isolation/external/wpt/html/browsers/origin/origin-keyed-agent-clusters/iframe-navigation/parent-no-1-subdomain-yes-2-subdomain2-no.sub.https.html [ Failure Pass ]
+crbug.com/1227911 [ Win Release ] virtual/not-site-per-process/external/wpt/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-no-child1-no-subdomain-child2-yes-subdomain.sub.https.html [ Failure Pass ]
+crbug.com/1227911 [ Linux Release ] virtual/not-site-per-process/external/wpt/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-no-child1-no-subdomain-child2-yes-subdomain.sub.https.html [ Failure Pass ]
+crbug.com/1227911 [ Mac10.14 ] virtual/not-site-per-process/external/wpt/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-no-child1-no-subdomain-child2-yes-subdomain.sub.https.html [ Failure Pass ]
+crbug.com/1227911 [ Win Release ] virtual/not-site-per-process/external/wpt/html/browsers/origin/origin-keyed-agent-clusters/iframe-navigation/parent-no-1-no-subdomain-2-yes-subdomain.sub.https.html [ Failure Pass ]
+crbug.com/1227911 [ Linux Release ] virtual/not-site-per-process/external/wpt/html/browsers/origin/origin-keyed-agent-clusters/iframe-navigation/parent-no-1-no-subdomain-2-yes-subdomain.sub.https.html [ Failure Pass ]
+crbug.com/1227911 [ Mac10.14 ] virtual/not-site-per-process/external/wpt/html/browsers/origin/origin-keyed-agent-clusters/iframe-navigation/parent-no-1-no-subdomain-2-yes-subdomain.sub.https.html [ Failure Pass ]
diff --git a/third_party/blink/web_tests/VirtualTestSuites b/third_party/blink/web_tests/VirtualTestSuites
index 13f662ea..fc896cba 100644
--- a/third_party/blink/web_tests/VirtualTestSuites
+++ b/third_party/blink/web_tests/VirtualTestSuites
@@ -992,7 +992,8 @@
   {
     "prefix": "fledge",
     "bases": [
-      "http/tests/inspector-protocol/target/auto-attach-auction-worklet.js"
+      "http/tests/inspector-protocol/target/auto-attach-auction-worklet.js",
+      "http/tests/inspector-protocol/storage/interest-groups.js"
     ],
     "args": [
       "--enable-features=InterestGroupStorage,AdInterestGroupAPI,Fledge,FencedFrames:implementation_type/shadow_dom"
diff --git a/third_party/blink/web_tests/external/wpt/IndexedDB/database-names-by-origin.html b/third_party/blink/web_tests/external/wpt/IndexedDB/database-names-by-origin.html
new file mode 100644
index 0000000..2224d5b
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/IndexedDB/database-names-by-origin.html
@@ -0,0 +1,144 @@
+<!doctype html>
+<meta charset="utf8">
+<meta name="timeout" content="long">
+<title>IndexedDB: origins have isolated namespaces</title>
+<link rel="author" href="pwnall@chromium.org" title="Victor Costan">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../common/get-host-info.sub.js"></script>
+<script src="support-promises.js"></script>
+
+<body>
+<script>
+'use strict';
+
+// Returns a Promise that resolves with the helper's response.
+function waitForCrossOriginHelperResponse(origin, request) {
+  return new Promise((resolve, reject) => {
+    self.addEventListener('message', event => {
+      if (event.origin !== origin) {
+        reject(new Error(`Unexpected message from ${event.origin}`));
+        return;
+      }
+
+      if (event.data.action === request.action) {
+        resolve(event.data.response);
+      } else {
+        reject(new Error(`Unexpected message ${JSON.stringify(event.data)}`));
+      }
+    }, { once: true });
+  });
+}
+
+// Returns a Promise that resolves with the helper's response.
+async function crossOriginIframeHelper(testCase, origin, request) {
+  const iframe = document.createElement('iframe');
+  iframe.src = origin + '/IndexedDB/resources/cross-origin-helper-frame.html';
+  document.body.appendChild(iframe);
+  testCase.add_cleanup(() => {
+    try {
+      document.body.removeChild(iframe);
+    } catch (e) {
+      // removeChild() throws if the iframe was already removed, which happens
+      // if this method runs to completion.
+    }
+  });
+
+  await new Promise((resolve, reject) => {
+    iframe.onload = resolve;
+    iframe.onerror = reject;
+  });
+
+  iframe.contentWindow.postMessage(request, origin);
+  const response = await waitForCrossOriginHelperResponse(origin, request);
+  document.body.removeChild(iframe);
+  return response;
+};
+
+// Returns a Promise that resolves with the helper's response.
+async function crossOriginWindowHelper(testCase, origin, request) {
+  const helperWindow = window.open(
+      origin + '/IndexedDB/resources/cross-origin-helper-frame.html',
+      '_blank');
+  testCase.add_cleanup(() => { helperWindow.close(); });
+
+  await new Promise((resolve, reject) => {
+    self.addEventListener('message', event => {
+      if (event.origin !== origin) {
+        reject(new Error(`Unexpected message from ${event.origin}`));
+        return;
+      }
+
+      if (event.data.action === null && event.data.response === 'ready') {
+        resolve(event.data.response);
+      } else {
+        reject(new Error(`Unexpected message ${JSON.stringify(event.data)}`));
+      }
+    }, { once: true });
+  });
+
+  helperWindow.postMessage(request, origin);
+  const response = await waitForCrossOriginHelperResponse(origin, request);
+  helperWindow.close();
+  return response;
+};
+
+// Returns a Promise that resolves with the helper's response.
+function crossOriginHelper(testCase, mode, origin, request) {
+  switch (mode) {
+    case 'iframe':
+      return crossOriginIframeHelper(testCase, origin, request);
+    case 'window':
+      return crossOriginWindowHelper(testCase, origin, request);
+    default:
+      throw new Error(`Unsupported cross-origin helper mode ${mode}`);
+  }
+}
+
+const sameOrigin = get_host_info().ORIGIN;
+const otherOrigin = get_host_info().REMOTE_ORIGIN;
+
+// The disclosure that inspired this test demonstrated leaked open database
+// connections across windows.
+for (const databaseKind of ['open', 'closed']) {
+  for (const mode of ['iframe', 'window']) {
+    promise_test(async testCase => {
+      const dbName = databaseName(testCase);
+
+      assert_true(
+          await crossOriginHelper(
+              testCase, mode, sameOrigin,
+              {action: 'delete-database', name: dbName}),
+          'Same-origin setup error');
+      assert_true(
+          await crossOriginHelper(
+              testCase, mode, otherOrigin,
+              { action: 'delete-database', name: dbName }),
+          'Cross-origin setup error');
+
+      const db = await createNamedDatabase(testCase, dbName, database => {
+        database.createObjectStore('store');
+      });
+
+      if (databaseKind === 'closed')
+        await db.close();
+
+      const sameOriginDbNames = await crossOriginHelper(
+          testCase, mode, sameOrigin, { action: 'get-database-names' });
+      assert_in_array(
+          sameOriginDbNames, dbName,
+          `Database creation should reflect in same-origin ${mode}`);
+
+      const otherOriginDbNames = await crossOriginHelper(
+          testCase, mode, otherOrigin, { action: 'get-database-names' });
+      assert_true(
+          otherOriginDbNames.indexOf(dbName) === -1,
+          `Database creation should not impact cross-origin ${mode} list`);
+
+      if (databaseKind !== 'closed')
+        await db.close();
+    }, `${databaseKind} database names don't leak to cross-origin ${mode}`);
+  }
+}
+</script>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/IndexedDB/resources/cross-origin-helper-frame.html b/third_party/blink/web_tests/external/wpt/IndexedDB/resources/cross-origin-helper-frame.html
new file mode 100644
index 0000000..997c5a2
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/IndexedDB/resources/cross-origin-helper-frame.html
@@ -0,0 +1,37 @@
+<!doctype html>
+<meta charset="utf8">
+<title>Performs IndexedDB tasks in response to postMessage</title>
+<script>
+'use strict';
+
+self.addEventListener('message', async event => {
+  const action = event.data.action;
+  let response = null;
+  switch(action) {
+    case 'get-database-names': {
+      const dbInfos = await self.indexedDB.databases();
+      response = dbInfos.map(dbInfo => dbInfo.name);
+      break;
+    }
+
+    case 'delete-database': {
+      const dbName = event.data.name;
+      await new Promise((resolve, reject) => {
+        const request = indexedDB.deleteDatabase(dbName);
+        request.onsuccess = resolve;
+        request.onerror = reject;
+      });
+      response = true;
+      break;
+    }
+  }
+  event.source.postMessage({ action, response }, event.origin);
+  window.close();
+});
+
+// Make up for the fact that the opener of a cross-origin window has no way of
+// knowing when the window finishes loading.
+if (window.opener !== null) {
+  window.opener.postMessage({ action: null, response: 'ready' }, '*');
+}
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-contain/container-queries/container-for-shadow-dom.tentative-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-contain/container-queries/container-for-shadow-dom.tentative-expected.txt
deleted file mode 100644
index 94708db..0000000
--- a/third_party/blink/web_tests/external/wpt/css/css-contain/container-queries/container-for-shadow-dom.tentative-expected.txt
+++ /dev/null
@@ -1,12 +0,0 @@
-This is a testharness.js-based test.
-PASS Match container in outer tree
-PASS Match container in same tree, not walking flat tree ancestors
-FAIL Match container in ::slotted selector's originating element tree assert_equals: expected "rgb(0, 128, 0)" but got "rgb(0, 0, 0)"
-PASS Match container in outer tree for :host
-FAIL Match container in ::part selector's originating element tree assert_equals: expected "rgb(0, 128, 0)" but got "rgb(0, 0, 0)"
-FAIL Match container for ::before in ::slotted selector's originating element tree assert_equals: expected "rgb(0, 128, 0)" but got "rgb(0, 0, 0)"
-PASS Match container in outer tree for :host::before
-FAIL Match container for ::before in ::part selector's originating element tree assert_equals: expected "rgb(0, 128, 0)" but got "rgb(0, 0, 0)"
-FAIL Match container for ::part selector's originating element tree for exportparts assert_equals: expected "rgb(0, 128, 0)" but got "rgb(0, 0, 0)"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/css/css-contain/container-queries/container-for-shadow-dom.tentative.html b/third_party/blink/web_tests/external/wpt/css/css-contain/container-queries/container-for-shadow-dom.tentative.html
index 24d52da..66e9ea0 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-contain/container-queries/container-for-shadow-dom.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-contain/container-queries/container-for-shadow-dom.tentative.html
@@ -15,7 +15,8 @@
   #inclusive-ancestor-slotted-before,
   #inclusive-ancestor-host-before,
   #inclusive-ancestor-part-before,
-  #inclusive-ancestor-inner-part {
+  #inclusive-ancestor-inner-part,
+  #inclusive-ancestor-slot-fallback {
     width: 400px;
     container-type: inline-size;
   }
@@ -210,6 +211,23 @@
   </div>
 </div>
 
+<div id="inclusive-ancestor-slot-fallback">
+  <div><template shadowroot="open">
+    <style>
+      div {
+        width: 200px;
+        container-type: inline-size;
+      }
+      @container size(width = 200px) {
+        #t10 { color: green; }
+      }
+    </style>
+    <div>
+      <slot><span id="t10"></span></slot>
+    </div>
+  </template></div>
+</div>
+
 <script>
   setup(() => assert_implements_container_queries());
 
@@ -261,4 +279,9 @@
     const t9 = innerhost.shadowRoot.querySelector("#t9");
     assert_equals(getComputedStyle(t9).color, green);
   }, "Match container for ::part selector's originating element tree for exportparts");
+
+  test(() => {
+    const t10 = document.querySelector("#inclusive-ancestor-slot-fallback > div").shadowRoot.querySelector("#t10");
+    assert_equals(getComputedStyle(t10).color, green);
+  }, "Match container for slot light tree child fallback");
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/fetch/private-network-access/shared-worker-fetch.https.window-expected.txt b/third_party/blink/web_tests/external/wpt/fetch/private-network-access/shared-worker-fetch.https.window-expected.txt
index 31ae173..0b54e1f 100644
--- a/third_party/blink/web_tests/external/wpt/fetch/private-network-access/shared-worker-fetch.https.window-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/fetch/private-network-access/shared-worker-fetch.https.window-expected.txt
@@ -1,12 +1,12 @@
 This is a testharness.js-based test.
 PASS local to local: success.
-FAIL private to local: failure. assert_equals: fetch error expected (string) "TypeError" but got (undefined) undefined
-FAIL private to local: success. assert_equals: fetch error expected (undefined) undefined but got (string) "TypeError"
+PASS private to local: failure.
+PASS private to local: success.
 PASS private to private: success.
-FAIL public to local: failed preflight. assert_equals: fetch error expected (string) "TypeError" but got (undefined) undefined
-FAIL public to local: success. assert_equals: fetch error expected (undefined) undefined but got (string) "TypeError"
-FAIL public to private: failed preflight. assert_equals: fetch error expected (string) "TypeError" but got (undefined) undefined
-FAIL public to private: success. assert_equals: fetch error expected (undefined) undefined but got (string) "TypeError"
+PASS public to local: failed preflight.
+PASS public to local: success.
+PASS public to private: failed preflight.
+PASS public to private: success.
 PASS public to public: success.
 FAIL treat-as-public to local: failure. assert_equals: fetch error expected (string) "TypeError" but got (undefined) undefined
 PASS treat-as-public to private: failure.
diff --git a/third_party/blink/web_tests/external/wpt/wasm/jsapi/prototypes.any-expected.txt b/third_party/blink/web_tests/external/wpt/wasm/jsapi/prototypes.any-expected.txt
deleted file mode 100644
index 4df4260..0000000
--- a/third_party/blink/web_tests/external/wpt/wasm/jsapi/prototypes.any-expected.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-This is a testharness.js-based test.
-FAIL _Module assert_true: _Module instanceof _Module expected true got false
-FAIL _Instance assert_true: _Instance instanceof _Instance expected true got false
-FAIL _Memory assert_true: _Memory instanceof _Memory expected true got false
-FAIL _Table assert_true: _Table instanceof _Table expected true got false
-FAIL _Global assert_true: _Global instanceof _Global expected true got false
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/wasm/jsapi/prototypes.any.worker-expected.txt b/third_party/blink/web_tests/external/wpt/wasm/jsapi/prototypes.any.worker-expected.txt
deleted file mode 100644
index 4df4260..0000000
--- a/third_party/blink/web_tests/external/wpt/wasm/jsapi/prototypes.any.worker-expected.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-This is a testharness.js-based test.
-FAIL _Module assert_true: _Module instanceof _Module expected true got false
-FAIL _Instance assert_true: _Instance instanceof _Instance expected true got false
-FAIL _Memory assert_true: _Memory instanceof _Memory expected true got false
-FAIL _Table assert_true: _Table instanceof _Table expected true got false
-FAIL _Global assert_true: _Global instanceof _Global expected true got false
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/wasm/jsapi/table/constructor.any-expected.txt b/third_party/blink/web_tests/external/wpt/wasm/jsapi/table/constructor.any-expected.txt
deleted file mode 100644
index 49f7733..0000000
--- a/third_party/blink/web_tests/external/wpt/wasm/jsapi/table/constructor.any-expected.txt
+++ /dev/null
@@ -1,34 +0,0 @@
-This is a testharness.js-based test.
-PASS name
-PASS length
-PASS No arguments
-PASS Calling
-PASS Empty descriptor
-PASS Invalid descriptor argument
-PASS Undefined initial value in descriptor
-PASS Undefined element value in descriptor
-PASS Out-of-range initial value in descriptor: NaN
-PASS Out-of-range maximum value in descriptor: NaN
-PASS Out-of-range initial value in descriptor: Infinity
-PASS Out-of-range maximum value in descriptor: Infinity
-PASS Out-of-range initial value in descriptor: -Infinity
-PASS Out-of-range maximum value in descriptor: -Infinity
-PASS Out-of-range initial value in descriptor: -1
-PASS Out-of-range maximum value in descriptor: -1
-PASS Out-of-range initial value in descriptor: 4294967296
-PASS Out-of-range maximum value in descriptor: 4294967296
-PASS Out-of-range initial value in descriptor: 68719476736
-PASS Out-of-range maximum value in descriptor: 68719476736
-PASS Initial value exceeds maximum
-PASS Basic (zero)
-PASS Basic (non-zero)
-PASS Stray argument
-PASS Proxy descriptor
-PASS Type conversion for descriptor.element
-PASS Order of evaluation for descriptor
-FAIL initialize externref table with default value WebAssembly.Table(): Descriptor property 'element' must be a WebAssembly reference type
-PASS initialize table with a wrong element value
-PASS initialize anyfunc table with default value
-PASS initialize anyfunc table with a bad default value
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/wasm/jsapi/table/constructor.any.worker-expected.txt b/third_party/blink/web_tests/external/wpt/wasm/jsapi/table/constructor.any.worker-expected.txt
deleted file mode 100644
index 49f7733..0000000
--- a/third_party/blink/web_tests/external/wpt/wasm/jsapi/table/constructor.any.worker-expected.txt
+++ /dev/null
@@ -1,34 +0,0 @@
-This is a testharness.js-based test.
-PASS name
-PASS length
-PASS No arguments
-PASS Calling
-PASS Empty descriptor
-PASS Invalid descriptor argument
-PASS Undefined initial value in descriptor
-PASS Undefined element value in descriptor
-PASS Out-of-range initial value in descriptor: NaN
-PASS Out-of-range maximum value in descriptor: NaN
-PASS Out-of-range initial value in descriptor: Infinity
-PASS Out-of-range maximum value in descriptor: Infinity
-PASS Out-of-range initial value in descriptor: -Infinity
-PASS Out-of-range maximum value in descriptor: -Infinity
-PASS Out-of-range initial value in descriptor: -1
-PASS Out-of-range maximum value in descriptor: -1
-PASS Out-of-range initial value in descriptor: 4294967296
-PASS Out-of-range maximum value in descriptor: 4294967296
-PASS Out-of-range initial value in descriptor: 68719476736
-PASS Out-of-range maximum value in descriptor: 68719476736
-PASS Initial value exceeds maximum
-PASS Basic (zero)
-PASS Basic (non-zero)
-PASS Stray argument
-PASS Proxy descriptor
-PASS Type conversion for descriptor.element
-PASS Order of evaluation for descriptor
-FAIL initialize externref table with default value WebAssembly.Table(): Descriptor property 'element' must be a WebAssembly reference type
-PASS initialize table with a wrong element value
-PASS initialize anyfunc table with default value
-PASS initialize anyfunc table with a bad default value
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/wasm/jsapi/table/get-set.any-expected.txt b/third_party/blink/web_tests/external/wpt/wasm/jsapi/table/get-set.any-expected.txt
deleted file mode 100644
index cad5a83..0000000
--- a/third_party/blink/web_tests/external/wpt/wasm/jsapi/table/get-set.any-expected.txt
+++ /dev/null
@@ -1,35 +0,0 @@
-This is a testharness.js-based test.
-PASS Missing arguments: get
-PASS Branding: get
-PASS Missing arguments: set
-PASS Branding: set
-PASS Basic
-PASS Growing
-PASS Setting out-of-bounds
-PASS Setting non-function
-PASS Setting non-wasm function
-PASS Setting non-wasm arrow function
-PASS Getting out-of-range argument: undefined
-PASS Setting out-of-range argument: undefined
-PASS Getting out-of-range argument: NaN
-PASS Setting out-of-range argument: NaN
-PASS Getting out-of-range argument: Infinity
-PASS Setting out-of-range argument: Infinity
-PASS Getting out-of-range argument: -Infinity
-PASS Setting out-of-range argument: -Infinity
-PASS Getting out-of-range argument: -1
-PASS Setting out-of-range argument: -1
-PASS Getting out-of-range argument: 4294967296
-PASS Setting out-of-range argument: 4294967296
-PASS Getting out-of-range argument: 68719476736
-PASS Setting out-of-range argument: 68719476736
-PASS Getting out-of-range argument: "0x100000000"
-PASS Setting out-of-range argument: "0x100000000"
-PASS Getting out-of-range argument: object "[object Object]"
-PASS Setting out-of-range argument: object "[object Object]"
-PASS Order of argument conversion
-PASS Stray argument
-FAIL Arguments for anyfunc table set WebAssembly.Table.set(): Argument 1 must be null or a WebAssembly function of type compatible to 'this'
-FAIL Arguments for externref table set WebAssembly.Table(): Descriptor property 'element' must be a WebAssembly reference type
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/wasm/jsapi/table/get-set.any.worker-expected.txt b/third_party/blink/web_tests/external/wpt/wasm/jsapi/table/get-set.any.worker-expected.txt
deleted file mode 100644
index cad5a83..0000000
--- a/third_party/blink/web_tests/external/wpt/wasm/jsapi/table/get-set.any.worker-expected.txt
+++ /dev/null
@@ -1,35 +0,0 @@
-This is a testharness.js-based test.
-PASS Missing arguments: get
-PASS Branding: get
-PASS Missing arguments: set
-PASS Branding: set
-PASS Basic
-PASS Growing
-PASS Setting out-of-bounds
-PASS Setting non-function
-PASS Setting non-wasm function
-PASS Setting non-wasm arrow function
-PASS Getting out-of-range argument: undefined
-PASS Setting out-of-range argument: undefined
-PASS Getting out-of-range argument: NaN
-PASS Setting out-of-range argument: NaN
-PASS Getting out-of-range argument: Infinity
-PASS Setting out-of-range argument: Infinity
-PASS Getting out-of-range argument: -Infinity
-PASS Setting out-of-range argument: -Infinity
-PASS Getting out-of-range argument: -1
-PASS Setting out-of-range argument: -1
-PASS Getting out-of-range argument: 4294967296
-PASS Setting out-of-range argument: 4294967296
-PASS Getting out-of-range argument: 68719476736
-PASS Setting out-of-range argument: 68719476736
-PASS Getting out-of-range argument: "0x100000000"
-PASS Setting out-of-range argument: "0x100000000"
-PASS Getting out-of-range argument: object "[object Object]"
-PASS Setting out-of-range argument: object "[object Object]"
-PASS Order of argument conversion
-PASS Stray argument
-FAIL Arguments for anyfunc table set WebAssembly.Table.set(): Argument 1 must be null or a WebAssembly function of type compatible to 'this'
-FAIL Arguments for externref table set WebAssembly.Table(): Descriptor property 'element' must be a WebAssembly reference type
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/wasm/jsapi/table/grow.any-expected.txt b/third_party/blink/web_tests/external/wpt/wasm/jsapi/table/grow.any-expected.txt
deleted file mode 100644
index 16927db..0000000
--- a/third_party/blink/web_tests/external/wpt/wasm/jsapi/table/grow.any-expected.txt
+++ /dev/null
@@ -1,21 +0,0 @@
-This is a testharness.js-based test.
-PASS Missing arguments
-PASS Branding
-PASS Basic
-PASS Reached maximum
-PASS Exceeded maximum
-PASS Out-of-range argument: undefined
-PASS Out-of-range argument: NaN
-PASS Out-of-range argument: Infinity
-PASS Out-of-range argument: -Infinity
-PASS Out-of-range argument: -1
-PASS Out-of-range argument: 4294967296
-PASS Out-of-range argument: 68719476736
-PASS Out-of-range argument: "0x100000000"
-PASS Out-of-range argument: object "[object Object]"
-PASS Stray argument
-FAIL Grow with exported-function argument assert_equals: expected (function) function "function 0() { [native code] }" but got (object) null
-FAIL Grow with non-function argument assert_throws_js: function "() => table.grow(2, {})" did not throw
-FAIL Grow with JS-function argument assert_throws_js: function "() => table.grow(2, () => true)" did not throw
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/wasm/jsapi/table/grow.any.worker-expected.txt b/third_party/blink/web_tests/external/wpt/wasm/jsapi/table/grow.any.worker-expected.txt
deleted file mode 100644
index 16927db..0000000
--- a/third_party/blink/web_tests/external/wpt/wasm/jsapi/table/grow.any.worker-expected.txt
+++ /dev/null
@@ -1,21 +0,0 @@
-This is a testharness.js-based test.
-PASS Missing arguments
-PASS Branding
-PASS Basic
-PASS Reached maximum
-PASS Exceeded maximum
-PASS Out-of-range argument: undefined
-PASS Out-of-range argument: NaN
-PASS Out-of-range argument: Infinity
-PASS Out-of-range argument: -Infinity
-PASS Out-of-range argument: -1
-PASS Out-of-range argument: 4294967296
-PASS Out-of-range argument: 68719476736
-PASS Out-of-range argument: "0x100000000"
-PASS Out-of-range argument: object "[object Object]"
-PASS Stray argument
-FAIL Grow with exported-function argument assert_equals: expected (function) function "function 0() { [native code] }" but got (object) null
-FAIL Grow with non-function argument assert_throws_js: function "() => table.grow(2, {})" did not throw
-FAIL Grow with JS-function argument assert_throws_js: function "() => table.grow(2, () => true)" did not throw
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-audiocontext-interface/audiocontext-not-fully-active-expected.txt b/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-audiocontext-interface/audiocontext-not-fully-active-expected.txt
deleted file mode 100644
index 86d99c2..0000000
--- a/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-audiocontext-interface/audiocontext-not-fully-active-expected.txt
+++ /dev/null
@@ -1,13 +0,0 @@
-This is a testharness.js-based test.
-FAIL removed frame assert_throws_dom: function "() => new AudioContext()" threw object "NotSupportedError: Failed to construct 'AudioContext': Cannot create AudioContext on a detached document." that is not a DOMException InvalidStateError: property "code" is equal to 9, expected 11
-FAIL navigated frame assert_throws_dom: function "() => new AudioContext()" threw object "NotSupportedError: Failed to construct 'AudioContext': Cannot create AudioContext on a detached document." that is not a DOMException InvalidStateError: property "code" is equal to 9, expected 11
-FAIL frame in removed frame assert_throws_dom: function "() => new AudioContext()" threw object "NotSupportedError: Failed to construct 'AudioContext': Cannot create AudioContext on a detached document." that is not a DOMException InvalidStateError: property "code" is equal to 9, expected 11
-FAIL frame in navigated frame assert_throws_dom: function "() => new AudioContext()" threw object "NotSupportedError: Failed to construct 'AudioContext': Cannot create AudioContext on a detached document." that is not a DOMException InvalidStateError: property "code" is equal to 9, expected 11
-FAIL frame in removed remote-site frame assert_throws_dom: function "() => new AudioContext()" threw object "NotSupportedError: Failed to construct 'AudioContext': Cannot create AudioContext on a detached document." that is not a DOMException InvalidStateError: property "code" is equal to 9, expected 11
-FAIL frame in navigated remote-site frame assert_throws_dom: function "() => new AudioContext()" threw object "NotSupportedError: Failed to construct 'AudioContext': Cannot create AudioContext on a detached document." that is not a DOMException InvalidStateError: property "code" is equal to 9, expected 11
-FAIL removed frame in remote-site frame assert_throws_dom: function "() => new AudioContext()" threw object "NotSupportedError: Failed to construct 'AudioContext': Cannot create AudioContext on a detached document." that is not a DOMException InvalidStateError: property "code" is equal to 9, expected 11
-FAIL navigated frame in remote-site frame assert_throws_dom: function "() => new AudioContext()" threw object "NotSupportedError: Failed to construct 'AudioContext': Cannot create AudioContext on a detached document." that is not a DOMException InvalidStateError: property "code" is equal to 9, expected 11
-FAIL frame in removed remote-site frame in remote-site frame assert_throws_dom: function "() => new AudioContext()" threw object "NotSupportedError: Failed to construct 'AudioContext': Cannot create AudioContext on a detached document." that is not a DOMException InvalidStateError: property "code" is equal to 9, expected 11
-FAIL frame in navigated remote-site frame in remote-site frame assert_throws_dom: function "() => new AudioContext()" threw object "NotSupportedError: Failed to construct 'AudioContext': Cannot create AudioContext on a detached document." that is not a DOMException InvalidStateError: property "code" is equal to 9, expected 11
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/storage/interest-groups-expected.txt b/third_party/blink/web_tests/http/tests/inspector-protocol/storage/interest-groups-expected.txt
new file mode 100644
index 0000000..aa1deabb
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/storage/interest-groups-expected.txt
@@ -0,0 +1,4 @@
+Tests that interest groups are read and cleared.
+Events logged: 0
+Start Tracking
+
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/storage/interest-groups.js b/third_party/blink/web_tests/http/tests/inspector-protocol/storage/interest-groups.js
new file mode 100644
index 0000000..cc7793b4
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/storage/interest-groups.js
@@ -0,0 +1,49 @@
+(async function(testRunner) {
+  const {dp, page} = await testRunner.startBlank(
+      `Tests that interest groups are read and cleared.`);
+  const baseOrigin = 'https://a.test:8443/';
+  const base = baseOrigin + 'inspector-protocol/resources/';
+
+  async function runAdAuction (page) {
+    const auctionJs = `
+    navigator.runAdAuction({
+      decisionLogicUrl: "${base}fledge_decision_logic.js.php",
+      seller: "${baseOrigin}",
+      interestGroupBuyers: ["${baseOrigin}"]})`;
+
+    const pageSession = await page.createSession();
+    return pageSession.evaluateAsync(auctionJs);
+  }
+
+  await dp.Page.enable();
+
+  const events = [];
+  dp.Storage.onInterestGroupAccessed((messageObject)=>{events.push(messageObject.params)});
+  await dp.Storage.setInterestGroupTracking({enable: false});
+
+  // These navigations should not trigger any events, since tracking is
+  // disabled.
+  await dp.Page.navigate({url: base + 'fledge_join.html'});
+
+  await runAdAuction(page);
+
+  testRunner.log(`Events logged: ${events.length}`);
+
+  await dp.Storage.setInterestGroupTracking({enable: true});
+  testRunner.log("Start Tracking");
+
+  await dp.Page.navigate({url: base + 'fledge_join.html'});
+
+  await runAdAuction(page);
+
+  for (event of events) {
+    testRunner.log(
+      JSON.stringify(event, ['ownerOrigin', 'name', 'type'], 2));
+    data = await dp.Storage.getInterestGroupDetails(
+      {ownerOrigin: event.ownerOrigin, name: event.name});
+    const details = data.result.details;
+    details.expirationTime = 0;
+    testRunner.log(details);
+  }
+  testRunner.completeTest();
+  })
\ No newline at end of file
diff --git a/third_party/blink/web_tests/virtual/fledge/http/tests/inspector-protocol/storage/interest-groups-expected.txt b/third_party/blink/web_tests/virtual/fledge/http/tests/inspector-protocol/storage/interest-groups-expected.txt
new file mode 100644
index 0000000..5e1bb21
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/fledge/http/tests/inspector-protocol/storage/interest-groups-expected.txt
@@ -0,0 +1,114 @@
+Tests that interest groups are read and cleared.
+Events logged: 0
+Start Tracking
+{
+  "ownerOrigin": "https://a.test:8443",
+  "name": "0",
+  "type": "join"
+}
+{
+    adComponents : [
+    ]
+    ads : [
+        [0] : {
+            metadata : {"ad":"metadata","here":[1,2,3]}
+            renderUrl : https://example.com/render0
+        }
+    ]
+    biddingUrl : https://a.test:8443/inspector-protocol/resources/fledge_bidding_logic.js.php
+    expirationTime : 0
+    joiningOrigin : https://a.test:8443
+    name : 0
+    ownerOrigin : https://a.test:8443
+    trustedBiddingSignalsKeys : [
+    ]
+}
+{
+  "ownerOrigin": "https://a.test:8443",
+  "name": "1",
+  "type": "join"
+}
+{
+    adComponents : [
+    ]
+    ads : [
+        [0] : {
+            metadata : {"ad":"metadata","here":[1,2,3]}
+            renderUrl : https://example.com/render1
+        }
+    ]
+    biddingUrl : https://a.test:8443/inspector-protocol/resources/fledge_bidding_logic.js.php
+    expirationTime : 0
+    joiningOrigin : https://a.test:8443
+    name : 1
+    ownerOrigin : https://a.test:8443
+    trustedBiddingSignalsKeys : [
+    ]
+}
+{
+  "ownerOrigin": "https://a.test:8443",
+  "name": "1",
+  "type": "bid"
+}
+{
+    adComponents : [
+    ]
+    ads : [
+        [0] : {
+            metadata : {"ad":"metadata","here":[1,2,3]}
+            renderUrl : https://example.com/render1
+        }
+    ]
+    biddingUrl : https://a.test:8443/inspector-protocol/resources/fledge_bidding_logic.js.php
+    expirationTime : 0
+    joiningOrigin : https://a.test:8443
+    name : 1
+    ownerOrigin : https://a.test:8443
+    trustedBiddingSignalsKeys : [
+    ]
+}
+{
+  "ownerOrigin": "https://a.test:8443",
+  "name": "0",
+  "type": "bid"
+}
+{
+    adComponents : [
+    ]
+    ads : [
+        [0] : {
+            metadata : {"ad":"metadata","here":[1,2,3]}
+            renderUrl : https://example.com/render0
+        }
+    ]
+    biddingUrl : https://a.test:8443/inspector-protocol/resources/fledge_bidding_logic.js.php
+    expirationTime : 0
+    joiningOrigin : https://a.test:8443
+    name : 0
+    ownerOrigin : https://a.test:8443
+    trustedBiddingSignalsKeys : [
+    ]
+}
+{
+  "ownerOrigin": "https://a.test:8443",
+  "name": "1",
+  "type": "win"
+}
+{
+    adComponents : [
+    ]
+    ads : [
+        [0] : {
+            metadata : {"ad":"metadata","here":[1,2,3]}
+            renderUrl : https://example.com/render1
+        }
+    ]
+    biddingUrl : https://a.test:8443/inspector-protocol/resources/fledge_bidding_logic.js.php
+    expirationTime : 0
+    joiningOrigin : https://a.test:8443
+    name : 1
+    ownerOrigin : https://a.test:8443
+    trustedBiddingSignalsKeys : [
+    ]
+}
+
diff --git a/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-setRemoteDescription-rollback-expected.txt b/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-setRemoteDescription-rollback-expected.txt
index 244f3a1f..85db245 100644
--- a/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-setRemoteDescription-rollback-expected.txt
+++ b/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-setRemoteDescription-rollback-expected.txt
@@ -1,7 +1,7 @@
 This is a testharness.js-based test.
-FAIL setRemoteDescription(rollback) in have-remote-offer state should revert to stable state promise_test: Unhandled rejection with value: object "OperationError: Failed to execute 'setRemoteDescription' on 'RTCPeerConnection': Rollback not supported in Plan B"
-FAIL setRemoteDescription(rollback) from stable state should reject with InvalidStateError promise_rejects_dom: function "function() { throw e }" threw object "OperationError: Failed to execute 'setRemoteDescription' on 'RTCPeerConnection': Rollback not supported in Plan B" that is not a DOMException InvalidStateError: property "code" is equal to 0, expected 11
-FAIL setRemoteDescription(rollback) should ignore invalid sdp content and succeed promise_test: Unhandled rejection with value: object "OperationError: Failed to execute 'setRemoteDescription' on 'RTCPeerConnection': Rollback not supported in Plan B"
+FAIL setRemoteDescription(rollback) in have-remote-offer state should revert to stable state promise_test: Unhandled rejection with value: object "OperationError: Failed to execute 'setRemoteDescription' on 'RTCPeerConnection': Failed to set remote rollback sdp: Rollback not supported in Plan B"
+FAIL setRemoteDescription(rollback) from stable state should reject with InvalidStateError promise_rejects_dom: function "function() { throw e }" threw object "OperationError: Failed to execute 'setRemoteDescription' on 'RTCPeerConnection': Failed to set remote rollback sdp: Rollback not supported in Plan B" that is not a DOMException InvalidStateError: property "code" is equal to 0, expected 11
+FAIL setRemoteDescription(rollback) should ignore invalid sdp content and succeed promise_test: Unhandled rejection with value: object "OperationError: Failed to execute 'setRemoteDescription' on 'RTCPeerConnection': Failed to set remote rollback sdp: Rollback not supported in Plan B"
 FAIL local offer created before setRemoteDescription(remote offer) then rollback should still be usable promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
 FAIL local offer created before setRemoteDescription(remote offer) with different transceiver level assignments then rollback should still be usable promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
 FAIL rollback of a remote offer should remove a transceiver assert_equals: expected 1 but got 0
@@ -12,11 +12,11 @@
 FAIL explicit rollback of local offer should remove transceivers and transport promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of undefined (reading 'sender')"
 FAIL when using addTransceiver, implicit rollback of a local offer should visit stable state, but not fire negotiationneeded until we settle in stable promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
 FAIL when using addTrack, implicit rollback of a local offer should visit stable state, but not fire negotiationneeded promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'setRemoteDescription' on 'RTCPeerConnection': Failed to set remote offer sdp: Called in wrong state: have-local-offer"
-FAIL rollback of a remote offer to negotiated stable state should enable applying of a local offer promise_test: Unhandled rejection with value: object "OperationError: Failed to execute 'setRemoteDescription' on 'RTCPeerConnection': Rollback not supported in Plan B"
+FAIL rollback of a remote offer to negotiated stable state should enable applying of a local offer promise_test: Unhandled rejection with value: object "OperationError: Failed to execute 'setRemoteDescription' on 'RTCPeerConnection': Failed to set remote rollback sdp: Rollback not supported in Plan B"
 FAIL rollback of a local offer to negotiated stable state should enable applying of a remote offer assert_equals: expected 2 but got 0
 FAIL rollback a local offer with audio direction change to negotiated stable state and then add video receiver promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of undefined (reading 'direction')"
 FAIL two transceivers with same mids promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL onremovetrack fires during remote rollback promise_test: Unhandled rejection with value: object "OperationError: Failed to execute 'setRemoteDescription' on 'RTCPeerConnection': Rollback not supported in Plan B"
+FAIL onremovetrack fires during remote rollback promise_test: Unhandled rejection with value: object "OperationError: Failed to execute 'setRemoteDescription' on 'RTCPeerConnection': Failed to set remote rollback sdp: Rollback not supported in Plan B"
 FAIL rollback of a remote offer with stream changes promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of undefined (reading 'sender')"
 FAIL removeTrack() with a sender being rolled back does not crash or throw promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
 FAIL Implicit rollback with only a datachannel works promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
diff --git a/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpTransceiver.https-expected.txt b/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpTransceiver.https-expected.txt
index 7347ead..58e77a7e 100644
--- a/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpTransceiver.https-expected.txt
+++ b/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpTransceiver.https-expected.txt
@@ -33,7 +33,7 @@
 FAIL checkStopAfterClose assert_equals: Stopping a transceiver on a closed PC should throw. throws InvalidStateError expected "InvalidStateError" but got "TypeError"
 FAIL checkLocalRollback assert_equals: expected "[{currentDirection:null,direction:\"sendrecv\",receiver:{track:{kind:\"audio\"}},sender:{track:{}}}]" but got "[]"
 FAIL checkRollbackAndSetRemoteOfferWithDifferentType promise_test: Unhandled rejection with value: object "OperationError: Failed to execute 'setLocalDescription' on 'RTCPeerConnection': Rollback not supported in Plan B"
-FAIL checkRemoteRollback promise_test: Unhandled rejection with value: object "OperationError: Failed to execute 'setRemoteDescription' on 'RTCPeerConnection': Rollback not supported in Plan B"
+FAIL checkRemoteRollback promise_test: Unhandled rejection with value: object "OperationError: Failed to execute 'setRemoteDescription' on 'RTCPeerConnection': Failed to set remote rollback sdp: Rollback not supported in Plan B"
 FAIL checkMsectionReuse promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of undefined (reading 'mid')"
 FAIL checkStopAfterCreateOfferWithReusedMsection promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of undefined (reading 'stop')"
 FAIL checkAddIceCandidateToStoppedTransceiver promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of undefined (reading 'stop')"
diff --git a/third_party/blink/web_tests/wpt_internal/bluetooth/characteristic/notifications/concurrent-starts.https.html b/third_party/blink/web_tests/wpt_internal/bluetooth/characteristic/notifications/concurrent-starts.https.html
index fe08ec98..1196e23 100644
--- a/third_party/blink/web_tests/wpt_internal/bluetooth/characteristic/notifications/concurrent-starts.https.html
+++ b/third_party/blink/web_tests/wpt_internal/bluetooth/characteristic/notifications/concurrent-starts.https.html
@@ -8,6 +8,8 @@
 <script>
 'use strict';
 bluetooth_test(() => {
+  // Note, this test fails when converted to use the dynamic mocking system.
+  // See https://crbug.com/736226 for more details.
   return setBluetoothFakeAdapter('HeartRateAdapter')
     .then(() => requestDeviceWithTrustedClick({
       filters: [{services: ['heart_rate']}]}))
diff --git a/tools/clang/rewrite_raw_ptr_fields/manual-fields-to-ignore.txt b/tools/clang/rewrite_raw_ptr_fields/manual-fields-to-ignore.txt
index 350416a..269080d 100644
--- a/tools/clang/rewrite_raw_ptr_fields/manual-fields-to-ignore.txt
+++ b/tools/clang/rewrite_raw_ptr_fields/manual-fields-to-ignore.txt
@@ -342,6 +342,25 @@
 tracing::(anonymous namespace)::LazyLegacyEventInitializer::legacy_event_
 tracing::(anonymous namespace)::LazyLegacyEventInitializer::track_event_
 url::StdStringCanonOutput::str_
+base::Pickle::header_
+base::internal::TimerBase::task_destruction_detector_
+cc::TilingSetRasterQueueAll::tiling_set_
+site_engagement::SiteEngagementScore::clock_
+site_engagement::SiteEngagementScore::settings_map_
+extensions::LazyContextTaskQueue::ContextInfo::render_process_host
+extensions::LazyContextTaskQueue::ContextInfo::browser_context
+extensions::LazyContextTaskQueue::ContextInfo::web_contents
+mojo::core::MessagePipeDispatcher::node_controller_
+mojo::internal::ArrayDataViewImpl::data_
+mojo::internal::ArrayDataViewImpl::message_
+mojo::Connector::incoming_receiver_
+mojo::Connector::nesting_observer_
+mojo::InterfaceEndpointClient::controller_
+mojo::InterfaceEndpointClient::incoming_receiver_
+mojo::internal::MayAutoLock::lock_
+mojo::internal::MayAutoUnlock::lock_
+mojo::internal::MultiplexRouter::header_validator_
+url::CanonOutputT::buffer_
 
 # Populated manually - type is unsupported by raw_ptr to avoid being used in
 # performance sensitive base::Unretained
diff --git a/tools/metrics/actions/actions.xml b/tools/metrics/actions/actions.xml
index 0d4fbfc..7a48b19 100644
--- a/tools/metrics/actions/actions.xml
+++ b/tools/metrics/actions/actions.xml
@@ -6215,6 +6215,7 @@
 </action>
 
 <action name="ContentSuggestions.Feed.InfiniteFeedTriggered">
+  <obsolete>Deprecated with refactored NTP in August 2021.</obsolete>
   <owner>adamta@google.org</owner>
   <owner>sczs@chromium.org</owner>
   <owner>feed@chromium.org</owner>
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index abb5321..a47eb13 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -36993,6 +36993,11 @@
   <int value="24" label="Failed - cannot load stream with pending clear all"/>
   <int value="25" label="Failed - already has unread content available"/>
   <int value="26" label="Failed - user does not follow any web feeds"/>
+  <int value="27"
+      label="Failed - account token fetch failed because it was for the wrong
+             account"/>
+  <int value="28" label="Failed - account token fetch timed out"/>
+  <int value="29" label="Failed - network fetch timed out"/>
 </enum>
 
 <enum name="FeedNoticeAcknowledgementPath">
@@ -53601,6 +53606,7 @@
   <int value="1558582" label="ResamplingInputEvents:enabled"/>
   <int value="1618593" label="AutofillUseConsistentPopupSettingsIcons:enabled"/>
   <int value="2070488" label="EnablePasswordsAccountStorageSavingUi:disabled"/>
+  <int value="3001424" label="FeatureNotificationGuide:enabled"/>
   <int value="3428212" label="OmniboxAggregateShortcuts:disabled"/>
   <int value="3826348" label="DarkenWebsitesCheckboxInThemesSetting:disabled"/>
   <int value="4277967" label="SideSearchStatePerTab:disabled"/>
@@ -53633,6 +53639,7 @@
   <int value="26945819" label="EnhancedDeskAnimations:disabled"/>
   <int value="27507364" label="apps-keep-chrome-alive"/>
   <int value="29212695" label="OfflineIndicator:enabled"/>
+  <int value="31693434" label="ExtendedOpenVpnSettings:enabled"/>
   <int value="31848187" label="ViewsTaskManager:disabled"/>
   <int value="32057053" label="EnterpriseReportingInBrowser:disabled"/>
   <int value="32242305" label="NtpRecipeTasksModule:enabled"/>
@@ -54195,6 +54202,7 @@
       label="AutofillEnableStickyManualFallbackForCards:enabled"/>
   <int value="439267320" label="ScreenTime:disabled"/>
   <int value="439525862" label="GlobalMediaControlsForCast:disabled"/>
+  <int value="442561299" label="FeatureNotificationGuide:disabled"/>
   <int value="444411390" label="enable-incognito-shortcut-on-desktop"/>
   <int value="444754854" label="LegacyTLSWarnings:disabled"/>
   <int value="446316019" label="enable-threaded-compositing"/>
@@ -54808,6 +54816,7 @@
   <int value="892899792" label="MaterialDesignIncognitoNTP:disabled"/>
   <int value="894434593" label="TabRestoreSubMenus:enabled"/>
   <int value="898311758" label="ReaderMode:disabled"/>
+  <int value="899347105" label="NearbySharingWifiLan:enabled"/>
   <int value="900614020" label="ContentSuggestionsShowSummary:disabled"/>
   <int value="902209599" label="ShelfHotseat:enabled"/>
   <int value="902839593" label="WebContentsForceDark:disabled"/>
@@ -54960,6 +54969,7 @@
   <int value="1019857902"
       label="disable-hide-inactive-stacked-tab-close-buttons"/>
   <int value="1020127284" label="HardwareSecureDecryption:enabled"/>
+  <int value="1021266345" label="NearbySharingWifiLan:disabled"/>
   <int value="1021573543" label="QuickSettingsPWA:disabled"/>
   <int value="1021848000" label="SharedHighlightingUseBlocklist:disabled"/>
   <int value="1022424308" label="SignedExchangeSubresourcePrefetch:enabled"/>
@@ -55466,6 +55476,7 @@
   <int value="1379571437" label="ExoPointerLock:disabled"/>
   <int value="1379944457" label="EnableMessagesWebPush:disabled"/>
   <int value="1380367827" label="AllowDisableTouchpadHapticFeedback:enabled"/>
+  <int value="1381267110" label="ExtendedOpenVpnSettings:disabled"/>
   <int value="1381746642" label="enable-automatic-password-saving"/>
   <int value="1381817717" label="EduCoexistence:enabled"/>
   <int value="1382107019" label="LevelDBPerformRewrite:disabled"/>
@@ -67986,6 +67997,12 @@
   <int value="5" label="Weak signature algorithm"/>
 </enum>
 
+<enum name="PasswordChangeType">
+  <int value="0" label="Unknown"/>
+  <int value="1" label="Opened a site to change password manually"/>
+  <int value="2" label="Started an automated password change flow"/>
+</enum>
+
 <enum name="PasswordCheckInteraction">
   <int value="0" label="Check started automatically"/>
   <int value="1" label="Check started manually"/>
diff --git a/tools/metrics/histograms/metadata/android/histograms.xml b/tools/metrics/histograms/metadata/android/histograms.xml
index 654e4d5..3dcc20d 100644
--- a/tools/metrics/histograms/metadata/android/histograms.xml
+++ b/tools/metrics/histograms/metadata/android/histograms.xml
@@ -106,11 +106,6 @@
   <variant name=".VrServicesUpgrade"/>
 </variants>
 
-<variants name="ProviderPermissionType">
-  <variant name=".READ_HISTORY_BOOKMARKS" summary=""/>
-  <variant name=".WRITE_HISTORY_BOOKMARKS" summary=""/>
-</variants>
-
 <variants name="ThumbnailProvider_ClientType">
   <variant name=".DownloadHome" summary="Download home"/>
   <variant name=".NTPSnippets" summary="NTP snippets"/>
@@ -296,21 +291,6 @@
   </summary>
 </histogram>
 
-<histogram name="Android.AutofillAssistant.OnBoarding{Intent}"
-    enum="AutofillAssistantOnBoarding" expires_after="2022-06-08">
-  <obsolete>
-    Removed in November 2021.
-  </obsolete>
-  <owner>lsuder@chromium.org</owner>
-  <owner>mcarlen@chromium.org</owner>
-  <summary>
-    Reports the basic user interactions that can happen in regard to onboarding
-    during the {Intent} flow. It is recorded on Autofill Assistant startup and
-    during the on boarding.
-  </summary>
-  <token key="Intent" variants="AutofillAssistantIntents"/>
-</histogram>
-
 <histogram name="Android.AutofillAssistant.PaymentRequest.AutofillChanged"
     enum="AutofillAssistantPaymentRequestAutofillInfoChanged"
     expires_after="2022-06-08">
@@ -394,22 +374,6 @@
   </summary>
 </histogram>
 
-<histogram name="Android.BackgroundTaskScheduler.MigrationToProto"
-    enum="BackgroundTaskId" expires_after="2020-12-01">
-  <obsolete>
-    Removed in October 2020.
-  </obsolete>
-  <owner>ioanastefan@chromium.org</owner>
-  <owner>nator@chromium.org</owner>
-  <owner>knollr@chromium.org</owner>
-  <summary>
-    Records the stored tasks migrated to the Protocol Buffer data format. The
-    metric is recorded when the migration to the new format is being done. The
-    migration is done at a restart / OS upgrade, if tasks stored in the old
-    format are found.
-  </summary>
-</histogram>
-
 <histogram name="Android.BackgroundTaskScheduler.TaskCanceled"
     enum="BackgroundTaskId" expires_after="2022-07-03">
   <owner>fgorski@chromium.org</owner>
@@ -526,21 +490,6 @@
   </summary>
 </histogram>
 
-<histogram name="Android.ChildProcessLauncher.OnServiceConnectedTimedOutResult"
-    enum="OnServiceConnectedTimedOutResult" expires_after="2022-01-02">
-  <obsolete>
-    Deprecated as of 01/2022
-  </obsolete>
-  <owner>boliu@chromium.org</owner>
-  <owner>src/base/android/OWNERS</owner>
-  <summary>
-    When ChildProcessConnection fallback is enabled (Android 10+ for sandboxed
-    services), this is recorded after the timeout (10s) of every
-    ChildProcessConnection launch. Record whether fallback operation is
-    performed.
-  </summary>
-</histogram>
-
 <histogram name="Android.ChildProcessStartTimeV2{type}" units="ms"
     expires_after="2022-10-01">
   <owner>cduvall@chromium.org</owner>
@@ -561,25 +510,6 @@
   </token>
 </histogram>
 
-<histogram name="Android.ChildProcessStartTime{type}" units="ms"
-    expires_after="2021-09-18">
-  <obsolete>
-    Removed in December 2020, use Android.ChildProcessStartTimeV2.* instead
-    which allows timings up to 3 min.
-  </obsolete>
-  <owner>cduvall@chromium.org</owner>
-  <owner>boliu@chromium.org</owner>
-  <summary>
-    Measures time from {type} child process starts to right before main. Only
-    recorded on Android N+.
-  </summary>
-  <token key="type">
-    <variant name=".All" summary="all"/>
-    <variant name=".Isolated" summary="isolated"/>
-    <variant name=".NotIsolated" summary="non-isolated"/>
-  </token>
-</histogram>
-
 <histogram name="Android.ChromeActivity.Type" enum="ChromeActivityType"
     expires_after="never">
 <!-- expires-never: Used to classify UMA reports by the dashboards. -->
@@ -596,41 +526,6 @@
   </summary>
 </histogram>
 
-<histogram
-    name="Android.ChromeBrowserProvider.CallerHasPermission{ProviderPermissionType}"
-    enum="ClientAppId" expires_after="2021-06-28">
-  <obsolete>
-    ChromeBrowserProvider removed in 03/2021.
-  </obsolete>
-  <owner>yfriedman@chromium.org</owner>
-  <summary>
-    Android: Count of requests to Chrome browser which are granted to other apps
-    to read/write user's bookmarks and history. Only records cases when the app
-    has permission according to Android's permission manager.
-    {ProviderPermissionType}
-  </summary>
-  <token key="ProviderPermissionType" variants="ProviderPermissionType">
-    <variant name=""/>
-  </token>
-</histogram>
-
-<histogram
-    name="Android.ChromeBrowserProvider.SignaturePassed{ProviderPermissionType}"
-    enum="ClientAppId" expires_after="2021-06-28">
-  <obsolete>
-    ChromeBrowserProvider removed in 03/2021.
-  </obsolete>
-  <owner>yfriedman@chromium.org</owner>
-  <summary>
-    Android: Count of requests to Chrome browser which are granted to other apps
-    to read/write user's bookmarks and history. Only records cases where the app
-    is a system app or signed by Google. {ProviderPermissionType}
-  </summary>
-  <token key="ProviderPermissionType" variants="ProviderPermissionType">
-    <variant name=""/>
-  </token>
-</histogram>
-
 <histogram name="Android.ChromeStartupDelegate.FailureReason"
     enum="ChromeStartupDelegateFailureType" expires_after="2022-06-19">
   <owner>gangwu@chromium.org</owner>
@@ -651,65 +546,6 @@
   </summary>
 </histogram>
 
-<histogram name="Android.ChromeStartupDelegate.{Result}.Time" units="ms"
-    expires_after="2021-07-01">
-  <obsolete>
-    Deprecated as of 07/2021
-  </obsolete>
-  <owner>gangwu@chromium.org</owner>
-  <owner>twellington@chromium.org</owner>
-  <summary>
-    Records the time taken to fetch a result for ChromeStartupDelegate. Recorded
-    on application startup.
-  </summary>
-  <token key="Result">
-    <variant name="All"/>
-    <variant name="Allow"/>
-    <variant name="Disallow"/>
-    <variant name="Failure"/>
-  </token>
-</histogram>
-
-<histogram name="Android.CompressedResources.ExtractionBlockingTime" units="ms"
-    expires_after="M82">
-  <obsolete>
-    Removed in M82.
-  </obsolete>
-  <owner>estevenson@chromium.org</owner>
-  <owner>agrieve@chromium.org</owner>
-  <summary>
-    The amount of time Chrome spends blocked on resource extraction. Generally,
-    extraction is completed in the background but in certain cases a main-thread
-    getResources() call may block on resource extraction.
-  </summary>
-</histogram>
-
-<histogram name="Android.CompressedResources.ExtractionStatus"
-    enum="AndroidResourceExtractionStatus" expires_after="2021-01-24">
-  <obsolete>
-    Removed in M82.
-  </obsolete>
-  <owner>estevenson@chromium.org</owner>
-  <owner>agrieve@chromium.org</owner>
-  <summary>
-    Describes the result of Android resource extraction, and is recorded once
-    per browser start as part of deferred startup (not at time of extraction).
-  </summary>
-</histogram>
-
-<histogram name="Android.CompressedResources.ExtractionTime" units="ms"
-    expires_after="2021-01-24">
-  <obsolete>
-    Removed in M82.
-  </obsolete>
-  <owner>estevenson@chromium.org</owner>
-  <owner>agrieve@chromium.org</owner>
-  <summary>
-    The amount of time spent on resource extraction, whether or not extraction
-    was performed in the background or foreground.
-  </summary>
-</histogram>
-
 <histogram name="Android.ContactsPicker.AddressHasDerivedField"
     enum="AddressHasDerivedField" expires_after="2020-09-01">
   <owner>finnur@chromium.org</owner>
@@ -794,22 +630,6 @@
   </token>
 </histogram>
 
-<histogram name="Android.DarkTheme.DarkSearchRequested" enum="Boolean"
-    expires_after="2021-10-25">
-  <obsolete>
-    Removed April 2021; experiment is done and the feature code was removed.
-  </obsolete>
-  <owner>wylieb@chromium.org</owner>
-  <owner>twellington@chromium.org</owner>
-  <owner>clank-app-team@google.com</owner>
-  <summary>
-    Records if Chrome altered a google homepage/search request to include the
-    dark theme. Recorded only when navigating to google or google search.
-    Histogram total count will be the total number of requests to the google
-    hompage or google search.
-  </summary>
-</histogram>
-
 <histogram name="Android.DarkTheme.EnabledReason" enum="DarkThemeEnabledReason"
     expires_after="2022-06-19">
   <owner>twellington@chromium.org</owner>
@@ -851,110 +671,6 @@
   </summary>
 </histogram>
 
-<histogram name="Android.DefaultBrowserPromo.DialogShown"
-    enum="AndroidDefaultBrowserPromoType" expires_after="2021-05-30">
-  <obsolete>
-    Removed Dec 2020; experiment is done and the data isn't useful.
-  </obsolete>
-  <owner>lazzzis@google.com</owner>
-  <owner>twellington@chromium.org</owner>
-  <owner>pavely@chromium.org</owner>
-  <summary>
-    Records the current default browser state when the primer dialog prompting
-    users to change their default browser is shown.
-  </summary>
-</histogram>
-
-<histogram
-    name="Android.DefaultBrowserPromo.IntentReceivedFromDisambiguationSheet"
-    enum="AndroidDefaultBrowserPromoOutcomeType" expires_after="2021-03-26">
-  <obsolete>
-    Removed Dec 2020; experiment is done and the data isn't useful.
-  </obsolete>
-  <owner>lazzzis@google.com</owner>
-  <owner>twellington@chromium.org</owner>
-  <owner>pavely@chromium.org</owner>
-  <summary>
-    Records the current default browser state if the activity receives an intent
-    through the disambiguation sheet that is displayed in response to the
-    default browser promo.
-  </summary>
-</histogram>
-
-<histogram name="Android.DefaultBrowserPromo.Outcome.NoDefault"
-    enum="AndroidDefaultBrowserPromoOutcomeType" expires_after="2021-08-01">
-  <obsolete>
-    Removed Mar 2021; experiment is done and the data isn't useful.
-  </obsolete>
-  <owner>lazzzis@google.com</owner>
-  <owner>twellington@chromium.org</owner>
-  <owner>pavely@chromium.org</owner>
-  <summary>
-    The final default browser state (outcome) after the user has been prompted
-    to set Chrome as the default when no browser is set as the default
-    initially. Recorded when the system UI prompting the user to change their
-    default is dismissed.
-  </summary>
-</histogram>
-
-<histogram name="Android.DefaultBrowserPromo.Outcome.OtherDefault"
-    enum="AndroidDefaultBrowserPromoOutcomeType" expires_after="2021-08-01">
-  <obsolete>
-    Removed Mar 2021; experiment is done and the data isn't useful.
-  </obsolete>
-  <owner>lazzzis@google.com</owner>
-  <owner>twellington@chromium.org</owner>
-  <owner>pavely@chromium.org</owner>
-  <summary>
-    The final default browser state (outcome) after the user has been prompted
-    to set Chrome as the default when another browser is set as the default
-    initially. Recorded when the system UI prompting the user to change their
-    default is dismissed.
-  </summary>
-</histogram>
-
-<histogram name="Android.DefaultBrowserPromo.RoleManagerShown"
-    enum="AndroidDefaultBrowserPromoType" expires_after="2021-08-01">
-  <obsolete>
-    Removed Mar 2021; experiment is done and the data isn't useful.
-  </obsolete>
-  <owner>lazzzis@google.com</owner>
-  <owner>twellington@chromium.org</owner>
-  <owner>pavely@chromium.org</owner>
-  <summary>
-    Records the default browser state when the Android Q+ role manager dialog is
-    shown.
-  </summary>
-</histogram>
-
-<histogram name="Android.DefaultBrowserPromo.UIDismissalReason.NoDefault"
-    enum="AndroidDefaultBrowserPromoUserActionType" expires_after="2021-06-06">
-  <obsolete>
-    Removed Dec 2020; experiment is done and the data isn't useful.
-  </obsolete>
-  <owner>lazzzis@google.com</owner>
-  <owner>twellington@chromium.org</owner>
-  <owner>pavely@chromium.org</owner>
-  <summary>
-    Records which button click led to the default browser promo dialog being
-    dismissed for dialogs shown when there is currently no default browser set.
-  </summary>
-</histogram>
-
-<histogram name="Android.DefaultBrowserPromo.UIDismissalReason.OtherDefault"
-    enum="AndroidDefaultBrowserPromoUserActionType" expires_after="2021-05-30">
-  <obsolete>
-    Removed Dec 2020; experiment is done and the data isn't useful.
-  </obsolete>
-  <owner>lazzzis@google.com</owner>
-  <owner>twellington@chromium.org</owner>
-  <owner>pavely@chromium.org</owner>
-  <summary>
-    Records which button click led to the default browser promo dialog being
-    dismissed for dialogs shown when another browser is set as the default.
-  </summary>
-</histogram>
-
 <histogram name="Android.DeviceSize.LargestDisplaySize" units="dp"
     expires_after="2022-06-12">
   <owner>twellington@chromium.org</owner>
@@ -1193,25 +909,6 @@
   </summary>
 </histogram>
 
-<histogram name="Android.DownloadManager.ServiceStopped.DownloadNotification"
-    enum="DownloadNotificationServiceStopped" expires_after="M85">
-  <obsolete>
-    Deprecated as of 10/2020
-  </obsolete>
-  <owner>xingliu@chromium.org</owner>
-  <owner>clank-downloads@google.com</owner>
-  <summary>
-    Records instances of DownloadNotificationService stops. The number of
-    expected stops (stopped) and unexpected stops (task removed, low memory,
-    etc) can be compared to get a sense of how often the service crashes to see
-    how it handles restarts. For example, the number of times the service is
-    stopped because of low memory can be compared to the number of times the
-    service has been destroyed to see how frequently it occurs. Or, the number
-    of start sticky's will indicate how often the service is able to restart due
-    to an unexpected stop.
-  </summary>
-</histogram>
-
 <histogram name="Android.DownloadManager.Share.Count" units="units"
     expires_after="M86">
   <owner>twellington@chromium.org</owner>
@@ -1249,13 +946,7 @@
     Records the required stretch of the smaller dimension before displaying an
     image on download home. Measured in percentage. {AndroidDownloadTypes}
   </summary>
-  <token key="AndroidDownloadTypes" variants="AndroidDownloadTypes">
-    <variant name="">
-      <obsolete>
-        Base histogram. Use suffixes of this histogram instead.
-      </obsolete>
-    </variant>
-  </token>
+  <token key="AndroidDownloadTypes" variants="AndroidDownloadTypes"/>
 </histogram>
 
 <histogram name="Android.DownloadPage.OpenSource"
@@ -1360,19 +1051,6 @@
   </summary>
 </histogram>
 
-<histogram name="Android.FontLookup.Blink.DLFontsLatencyFailure" units="ms"
-    expires_after="2022-06-30">
-  <obsolete>
-    Removed in M98. No longer tracked.
-  </obsolete>
-  <owner>drott@chromium.org</owner>
-  <owner>layout-dev@chromium.org</owner>
-  <summary>
-    This metric was never logged. See
-    Android.FontLookup.Blink.DLFontsLatencyFailure2 instead.
-  </summary>
-</histogram>
-
 <histogram name="Android.FontLookup.Blink.DLFontsLatencyFailure2"
     units="microseconds" expires_after="2022-06-30">
   <owner>drott@chromium.org</owner>
@@ -1387,19 +1065,6 @@
   </summary>
 </histogram>
 
-<histogram name="Android.FontLookup.Blink.DLFontsLatencySuccess" units="ms"
-    expires_after="2022-06-30">
-  <obsolete>
-    Removed in M98. No longer tracked.
-  </obsolete>
-  <owner>drott@chromium.org</owner>
-  <owner>layout-dev@chromium.org</owner>
-  <summary>
-    This is obsolete in favor of Android.FontLookup.Blink.DLFontsLatencySuccess.
-    This was incorrectly logging both successes and failures.
-  </summary>
-</histogram>
-
 <histogram name="Android.FontLookup.Blink.DLFontsLatencySuccess2"
     units="microseconds" expires_after="2022-06-30">
   <owner>drott@chromium.org</owner>
@@ -1696,19 +1361,6 @@
   </summary>
 </histogram>
 
-<histogram name="Android.Intent.OverrideUrlLoadingIntentAction"
-    enum="StandardActions" expires_after="2021-03-21">
-  <obsolete>
-    Removed in M91. No longer tracked.
-  </obsolete>
-  <owner>mthiesse@chromium.org</owner>
-  <owner>yfriedman@chromium.org</owner>
-  <summary>
-    When a site navigates to a URL that can be parsed as an Intent, record the
-    action of that Intent.
-  </summary>
-</histogram>
-
 <histogram name="Android.Intent.WebIntentToOtherBrowser" enum="Boolean"
     expires_after="M98">
   <owner>mthiesse@chromium.org</owner>
@@ -1859,34 +1511,6 @@
   </summary>
 </histogram>
 
-<histogram name="Android.Language.UiIsSystemLanguage" enum="BooleanMatched"
-    expires_after="2021-12-01">
-  <obsolete>
-    Replaced by LanguageUsage.UI.Android.* Feb 2021.
-  </obsolete>
-  <owner>perrier@chromium.org</owner>
-  <owner>chrome-language@google.com</owner>
-  <summary>
-    Reports whether the Chrome UI language is the same as the Android system
-    lanaguage. Recorded once per top-level ChromeActivty start.
-  </summary>
-</histogram>
-
-<histogram name="Android.Language.WrongLanguageAfterResume" enum="Boolean"
-    expires_after="2021-12-01">
-  <obsolete>
-    Replaced by LanguageUsage.UI.Android.* Feb 2021.
-  </obsolete>
-  <owner>perrier@chromium.org</owner>
-  <owner>chrome-language@google.com</owner>
-  <summary>
-    Reports whether Chrome was started in a language other than the system
-    language but we support the system language. That can happen if the user
-    changes the system language and the required language split cannot be
-    installed in time.
-  </summary>
-</histogram>
-
 <histogram
     name="Android.MainActivity.ExplicitMainViewIntentDispatched.OnCreate"
     enum="BooleanDispatched" expires_after="2021-01-24">
@@ -1996,11 +1620,6 @@
     MemoryAndroid.NotificationForeground. {AndroidProcessType}
   </summary>
   <token key="AndroidProcessType">
-    <variant name="">
-      <obsolete>
-        Base histogram. Use suffixes of this histogram instead.
-      </obsolete>
-    </variant>
     <variant name=".Browser" summary="Browser process"/>
     <variant name=".ChildService"
         summary="Child service process (renderer, GPU, etc.)"/>
@@ -2303,11 +1922,6 @@
     {OfflineItemsSource}
   </summary>
   <token key="OfflineItemsSource">
-    <variant name="">
-      <obsolete>
-        Base histogram. Use suffixes of this histogram instead.
-      </obsolete>
-    </variant>
     <variant name=".Downloads" summary="Downloads"/>
     <variant name=".OfflinePages" summary="Offline pages"/>
   </token>
@@ -2927,18 +2541,6 @@
   </summary>
 </histogram>
 
-<histogram name="Android.SeccompStatus.Syscall" enum="AndroidSeccompStatus"
-    expires_after="M99">
-  <obsolete>
-    Recording code deleted on 2015-04-22.
-  </obsolete>
-  <owner>rsesek@chromium.org</owner>
-  <summary>
-    Reports the level of kernel support for the seccomp-bpf sandbox using the
-    seccomp system call.
-  </summary>
-</histogram>
-
 <histogram name="Android.SelectFileDialogContentSelected"
     units="SelectFileDialogContent" expires_after="2022-06-12">
   <owner>finnur@chromium.org</owner>
@@ -2973,20 +2575,6 @@
   </summary>
 </histogram>
 
-<histogram name="Android.ShouldDestroyIncognitoProfileOnStartup"
-    units="Boolean" expires_after="2021-10-10">
-  <obsolete>
-    Removed as of 05/2021.
-  </obsolete>
-  <owner>rhalavati@chromium.org</owner>
-  <owner>yfriedman@chromium.org</owner>
-  <summary>
-    Records the number of times the call to the function
-    |IncognitoUtils.shouldDestroyIncognitoProfileOnStartup| returns true or
-    false.
-  </summary>
-</histogram>
-
 <histogram name="Android.StartupTabPreloader.ActivityStartToLoadDecision"
     units="ms" expires_after="2022-09-01">
   <owner>blundell@chromium.org</owner>
@@ -3138,36 +2726,6 @@
   </summary>
 </histogram>
 
-<histogram
-    name="Android.StrictMode.TabPersistentStore{AndroidTabPersistentStoreTime}"
-    units="ms" expires_after="2021-10-04">
-  <obsolete>
-    No longer used. Removed as of 09/2021.
-  </obsolete>
-  <owner>wnwen@chromium.org</owner>
-  <owner>yfriedman@chromium.org</owner>
-  <summary>
-    Measures time spent during StrictMode-whitelisted code in
-    TabPersistentStore. {AndroidTabPersistentStoreTime}
-  </summary>
-  <token key="AndroidTabPersistentStoreTime">
-    <variant name=""/>
-    <variant name=".CleanupAllEncryptedTime" summary=""/>
-    <variant name=".LoadStateInternalPrefetchTime" summary=""/>
-    <variant name=".LoadStateInternalTime" summary=""/>
-    <variant name=".LoadStateTime" summary=""/>
-    <variant name=".MergeStateInternalFetchTime" summary=""/>
-    <variant name=".MergeStateInternalTime" summary=""/>
-    <variant name=".ReadMergedStateTime" summary=""/>
-    <variant name=".ReadSavedStateTime" summary=""/>
-    <variant name=".RestoreTabPrefetchTime" summary=""/>
-    <variant name=".RestoreTabTime" summary=""/>
-    <variant name=".SaveListTime" summary=""/>
-    <variant name=".SaveStateTime" summary=""/>
-    <variant name=".SaveTabsTime" summary=""/>
-  </token>
-</histogram>
-
 <histogram name="Android.StrictMode.ThumbnailCacheDir" units="ms"
     expires_after="M85">
   <owner>wnwen@chromium.org</owner>
@@ -3199,19 +2757,6 @@
   </summary>
 </histogram>
 
-<histogram name="Android.Survey.DownloadRequested" enum="BooleanRequested"
-    expires_after="2021-06-01">
-  <obsolete>
-    Deprecated as of 05/2021. Replaced with Android.Survey.DownloadRequested2
-  </obsolete>
-  <owner>twellington@chromium.org</owner>
-  <owner>clank-app-team@google.com</owner>
-  <summary>
-    Recorded when a survey download is requested. If the client already has
-    successfully downloaded a survey, 'not requested' is recorded.
-  </summary>
-</histogram>
-
 <histogram name="Android.Survey.DownloadRequested2" enum="BooleanRequested"
     expires_after="2022-06-01">
   <owner>twellington@chromium.org</owner>
@@ -3224,17 +2769,6 @@
   </summary>
 </histogram>
 
-<histogram name="Android.Survey.DownloadResponseCode"
-    enum="SurveyDownloadResponseCodes" expires_after="2021-06-01">
-  <obsolete>
-    Deprecated as of 05/2021 with the old library. Replaced by
-    Android.Survey.DownloadResponseCode2.
-  </obsolete>
-  <owner>twellington@chromium.org</owner>
-  <owner>clank-app-team@google.com</owner>
-  <summary>The response code of the completed survey download request.</summary>
-</histogram>
-
 <histogram name="Android.Survey.DownloadResponseCode2"
     enum="SurveyDownloadResponseCodes2" expires_after="2022-06-01">
   <owner>skym@chromium.org</owner>
@@ -3393,13 +2927,7 @@
     added to the cache. {ThumbnailProvider_ClientType}
   </summary>
   <token key="ThumbnailProvider_ClientType"
-      variants="ThumbnailProvider_ClientType">
-    <variant name="">
-      <obsolete>
-        Base histogram. Use suffixes of this histogram instead.
-      </obsolete>
-    </variant>
-  </token>
+      variants="ThumbnailProvider_ClientType"/>
 </histogram>
 
 <histogram
@@ -3415,13 +2943,7 @@
     in-memory cache. {ThumbnailProvider_ClientType}
   </summary>
   <token key="ThumbnailProvider_ClientType"
-      variants="ThumbnailProvider_ClientType">
-    <variant name="">
-      <obsolete>
-        Base histogram. Use suffixes of this histogram instead.
-      </obsolete>
-    </variant>
-  </token>
+      variants="ThumbnailProvider_ClientType"/>
 </histogram>
 
 <histogram name="Android.View.onDraw.30Seconds" units="count"
@@ -3437,53 +2959,6 @@
   </summary>
 </histogram>
 
-<histogram name="Android.WebView.ActualUiThread"
-    enum="AndroidWebViewActualUiThread" expires_after="2021-03-31">
-  <obsolete>
-    Removed Feb 2021; they virtually always use the main thread.
-  </obsolete>
-  <owner>torne@chromium.org</owner>
-  <owner>src/android_webview/OWNERS</owner>
-  <summary>
-    Recorded once per startup for apps targeting releases before JB MR2 if they
-    create their first WebView off the main thread. This records which thread
-    they actually ended up using as their UI thread.
-  </summary>
-</histogram>
-
-<histogram name="Android.WebView.AndroidProtocolHandler.ResourceGetIdentifier"
-    enum="BooleanSuccess" expires_after="2021-01-30">
-  <obsolete>
-    Removed Jan 2021 because it doesn't cover the cases, replaced with
-    ResourceGetIdentifier2.
-  </obsolete>
-  <owner>hazems@chromium.org</owner>
-  <owner>src/android_webview/OWNERS</owner>
-  <summary>
-    Track if looking up Android app resource id using
-    android.content.res.Resources#getIdentifier succeeds in
-    AndroidProtocolHandler or if it falls back to reflection look up.
-  </summary>
-</histogram>
-
-<histogram name="Android.WebView.AndroidProtocolHandler.ResourceGetIdentifier2"
-    enum="AndroidWebViewAndroidProtocolHandlerResources"
-    expires_after="2021-05-30">
-  <obsolete>
-    Removed May 2021 because it's no longer needed, we found that
-    AndroidProtocolHandler still needs to fail back to reflection to find
-    resource ids in 1.7% of cases.
-  </obsolete>
-  <owner>hazems@chromium.org</owner>
-  <owner>src/android_webview/OWNERS</owner>
-  <summary>
-    Track if looking up Android app resource id using
-    android.content.res.Resources#getIdentifier succeeds in
-    AndroidProtocolHandler in all cases or if should still fall back to
-    reflection look up.
-  </summary>
-</histogram>
-
 <histogram name="Android.WebView.AndroidX.ApiCall" enum="AndroidXWebkitApiCall"
     expires_after="2022-12-20">
   <owner>ntfschr@chromium.org</owner>
@@ -3514,72 +2989,6 @@
   </summary>
 </histogram>
 
-<histogram name="Android.WebView.AttachedToWindowIn30s" enum="Boolean"
-    expires_after="2021-06-09">
-  <obsolete>
-    Removed in June 2021.
-  </obsolete>
-  <owner>michaelbai@chromium.org</owner>
-  <owner>boliu@chromium.org</owner>
-  <summary>
-    Records if WebView was attached to a Android window in 30s since WebView was
-    created. Nothing will be recorded if WebView has already been destroyed.
-  </summary>
-</histogram>
-
-<histogram name="Android.WebView.AttachedToWindowIn3m" enum="Boolean"
-    expires_after="2021-06-09">
-  <obsolete>
-    Removed in June 2021.
-  </obsolete>
-  <owner>michaelbai@chromium.org</owner>
-  <owner>boliu@chromium.org</owner>
-  <summary>
-    Records if WebView was attached to a Android window in 3m since WebView was
-    created. Nothing will be recorded if WebView has already been destroyed.
-  </summary>
-</histogram>
-
-<histogram name="Android.WebView.AttachedToWindowIn5s" enum="Boolean"
-    expires_after="2021-06-09">
-  <obsolete>
-    Removed in June 2021.
-  </obsolete>
-  <owner>michaelbai@chromium.org</owner>
-  <owner>boliu@chromium.org</owner>
-  <summary>
-    Records if WebView was attached to a Android window in 5s since WebView was
-    created. Nothing will be recorded if WebView has already been destroyed.
-  </summary>
-</histogram>
-
-<histogram name="Android.WebView.AttachedToWindowTime" units="ms"
-    expires_after="2021-06-09">
-  <obsolete>
-    Removed in June 2021.
-  </obsolete>
-  <owner>michaelbai@chromium.org</owner>
-  <owner>boliu@chromium.org</owner>
-  <summary>
-    Records how long it took for WebView to be attached to a Android window
-    since WebView was created. If WebView is never attached to a window, nothing
-    will be recorded.
-  </summary>
-</histogram>
-
-<histogram name="Android.WebView.AwDebugCall" enum="AwDebugCall"
-    expires_after="2022-01-31">
-  <obsolete>
-    Removed in January 2022.
-  </obsolete>
-  <owner>oksamyt@chromium.org</owner>
-  <owner>src/android_webview/OWNERS</owner>
-  <summary>
-    Counts the number of calls to AwDebug methods, recorded each time a method
-    is called.
-  </summary>
-</histogram>
-
 <histogram name="Android.WebView.Callback.Counts" enum="WebViewCallbackType"
     expires_after="2022-07-11">
   <owner>ntfschr@chromium.org</owner>
@@ -3587,16 +2996,6 @@
   <summary>Records the invocation count of WebView callbacks.</summary>
 </histogram>
 
-<histogram name="Android.WebView.ClearProxyOverride" units="units"
-    expires_after="2021-08-01">
-  <obsolete>
-    Removed in July 2021.
-  </obsolete>
-  <owner>ntfschr@chromium.org</owner>
-  <owner>src/android_webview/OWNERS</owner>
-  <summary>Records ClearProxyOverride calls.</summary>
-</histogram>
-
 <histogram name="Android.WebView.ComponentUpdater.CPSDirectorySize" units="KB"
     expires_after="2022-07-01">
   <owner>nator@chromium.org</owner>
@@ -3688,69 +3087,6 @@
   </summary>
 </histogram>
 
-<histogram name="Android.WebView.ConnectedToVariationService" enum="Boolean"
-    expires_after="M93">
-  <obsolete>
-    Removed Jun 2021 for cleanup (no longer needed): https://crbug.com/1217220
-  </obsolete>
-  <owner>hazems@chromium.org</owner>
-  <owner>webview-team@google.com</owner>
-  <summary>
-    Reports if an embedded WebView attempts to connect to the VariationService
-    during WebView startup or not. This should help to get an estimate of how
-    often &quot;:webview_service&quot; nonembedded process is launched during
-    WebView startup. There are other services that WebView connects to during
-    startup such as CrashReceiverService and MetricsBridgeService but
-    VariationService is the most dominant one. This is only recorded once during
-    WebView startup.
-  </summary>
-</histogram>
-
-<histogram name="Android.WebView.CookieManager.SameSiteAttributeValue"
-    enum="CookieSameSiteString" expires_after="M90">
-  <obsolete>
-    Removed Jan 2021 for cleanup (no longer needed): https://crbug.com/1165012
-  </obsolete>
-  <owner>chlily@chromium.org</owner>
-  <owner>torne@chromium.org</owner>
-  <summary>
-    The value of the cookie's SameSite attribute, if any. This is logged once
-    per creation of a cookie via the CookieManager API. These cookies are double
-    counted (once from CookieManager, logged in this histogram, and once from
-    CanonicalCookie::Create, logged in Cookie.SameSiteAttributeValue).
-  </summary>
-</histogram>
-
-<histogram name="Android.WebView.CookieManager.SameSiteNoneIsSecure"
-    enum="Boolean" expires_after="M90">
-  <obsolete>
-    Removed Jan 2021 for cleanup (no longer needed): https://crbug.com/1165012
-  </obsolete>
-  <owner>chlily@chromium.org</owner>
-  <owner>torne@chromium.org</owner>
-  <summary>
-    If a cookie was SameSite=None, this records whether or not it was Secure.
-    Logged once per attempt to set a SameSite=None cookie via CookieManager API.
-    These cookies are double counted (once from CookieManager, logged in this
-    histogram, and once from CookieMonster::SetCanonicalCookie, logged in
-    Cookie.SameSiteNoneIsSecure).
-  </summary>
-</histogram>
-
-<histogram name="Android.WebView.CustomDefaultVideoPoster" enum="Boolean"
-    expires_after="2021-08-01">
-  <obsolete>
-    Removed Aug 2021.
-  </obsolete>
-  <owner>torne@chromium.org</owner>
-  <owner>src/android_webview/OWNERS</owner>
-  <summary>
-    Records true if the embedding app's implementation of the
-    WebChromeClient.getDefaultVideoPoster() callback returned a custom default
-    poster, or false if it did not.
-  </summary>
-</histogram>
-
 <histogram name="Android.WebView.DarkMode.ForceDarkBehavior"
     enum="WebViewForceDarkBehavior" expires_after="2022-07-24">
   <owner>michaelbai@chromium.org</owner>
@@ -4003,11 +3339,6 @@
     {Android_WebView_DevUi_NavType}
   </summary>
   <token key="Android_WebView_DevUi_NavType">
-    <variant name="">
-      <obsolete>
-        Base histogram. Use suffixes of this histogram instead.
-      </obsolete>
-    </variant>
     <variant name=".AnyMethod" summary="User navigation through any means"/>
     <variant name=".FromIntent"
         summary="User navigation through the bottom navigation bar"/>
@@ -4068,47 +3399,6 @@
   </summary>
 </histogram>
 
-<histogram name="Android.WebView.EverAttachedToWindow" enum="Boolean"
-    expires_after="2021-06-09">
-  <obsolete>
-    Removed in June 2021.
-  </obsolete>
-  <owner>michaelbai@chromium.org</owner>
-  <owner>boliu@chromium.org</owner>
-  <summary>
-    Records if WebView was ever attached to a window when WebView is destroyed.
-    If WebView is killed by platform, nothing will be recorded.
-  </summary>
-</histogram>
-
-<histogram name="Android.WebView.ExtraHeaders.Valid" enum="BooleanValid"
-    expires_after="M88">
-  <obsolete>
-    Invalid headers instead just throw an exception in M89+.
-  </obsolete>
-  <owner>torne@chromium.org</owner>
-  <owner>src/android_webview/OWNERS</owner>
-  <summary>
-    Recorded when an app passes extra headers to
-    WebView.loadUrl(url,extra_headers). We check if the header names and values
-    are valid.
-  </summary>
-</histogram>
-
-<histogram name="Android.WebView.ExtraHeadersRedirect"
-    enum="WebViewExtraHeadersRedirect" expires_after="2021-02-14">
-  <obsolete>
-    Removed Nov 2020; experiment is done and the data isn't useful.
-  </obsolete>
-  <owner>torne@chromium.org</owner>
-  <owner>src/android_webview/OWNERS</owner>
-  <summary>
-    Records the redirect type each time that a request is redirected, if that
-    request contains extra headers added by the
-    WebView.loadUrl(url,extra_headers) API.
-  </summary>
-</histogram>
-
 <histogram name="Android.WebView.ForceDarkBehavior"
     enum="WebViewForceDarkBehavior" expires_after="2022-06-03">
   <owner>peter@chromium.org</owner>
@@ -4141,111 +3431,6 @@
   </summary>
 </histogram>
 
-<histogram name="Android.WebView.LoadDataWithBaseUrl.BaseUrl"
-    enum="WebViewUrlScheme" expires_after="2021-05-05">
-  <obsolete>
-    Removed Mar 2021; we're not planning any short-term work in this area.
-  </obsolete>
-  <owner>ntfschr@chromium.org</owner>
-  <owner>torne@chromium.org</owner>
-  <owner>src/android_webview/OWNERS</owner>
-  <summary>
-    Records the scheme for the baseUrl parameter to loadDataWithBaseURL. This
-    also records if this value is &quot;empty&quot;, as determined by
-    TextUtils.isEmpty().
-  </summary>
-</histogram>
-
-<histogram name="Android.WebView.LoadDataWithBaseUrl.HistoryUrl"
-    enum="HistoryUrlType" expires_after="M85">
-  <obsolete>
-    Removed Jan 2021 (had already expired)
-  </obsolete>
-  <owner>jamwalla@chromium.org</owner>
-  <summary>
-    Records whether the historyUrl parameter to loadDataWithBaseUrl is empty/
-    null, the same as the baseUrl parameter, or different from baseUrl.
-  </summary>
-</histogram>
-
-<histogram name="Android.WebView.LoadDataWithBaseUrl.HistoryUrl.{Timing}"
-    enum="URLValueForLoadDataWithBaseURL" expires_after="M95">
-  <obsolete>
-    Removed Sep 2021. See result in crbug.com/1244746.
-  </obsolete>
-  <owner>rakina@chromium.org</owner>
-  <owner>src/android_webview/OWNERS</owner>
-  <summary>
-    Records whether the history_url_for_data_url parameter used for a
-    loadDataWithBaseURL commit is empty/null, invalid, the same as the baseUrl
-    parameter, the same as the data: URL, or an entirely different value.
-    Recorded at navigation at {Timing}.
-  </summary>
-  <token key="Timing">
-    <variant name="DidCommitNavigation"/>
-    <variant name="NavigationRequestCreated"/>
-  </token>
-</histogram>
-
-<histogram name="Android.WebView.LoadDataWithBaseUrl.IsInMainFrame"
-    enum="BooleanMainFrame" expires_after="M95">
-  <obsolete>
-    Removed Sep 2021. See result in crbug.com/1244746.
-  </obsolete>
-  <owner>rakina@chromium.org</owner>
-  <owner>src/android_webview/OWNERS</owner>
-  <summary>
-    When the base_url_for_data_url parameter is set and the URL is a data: URL
-    (NavigationRequest::IsLoadDataWithBaseURL() returns true), records whether
-    the navigation happens on a main frame or a subframe. Recorded when the
-    NavigationRequest is created.
-  </summary>
-</histogram>
-
-<histogram name="Android.WebView.LoadDataWithBaseUrl.LoadingUrl"
-    enum="URLValueForLoadDataWithBaseURL" expires_after="M95">
-  <obsolete>
-    Removed Sep 2021. See result in crbug.com/1244746.
-  </obsolete>
-  <owner>rakina@chromium.org</owner>
-  <owner>src/android_webview/OWNERS</owner>
-  <summary>
-    Records whether the loading URL (DidCommitParams' URL) used for a
-    loadDataWithBaseURL commit is empty/null, invalid, the same as the baseUrl
-    parameter, the same as the data: URL, or an entirely different value.
-    Recorded at DidCommitNavigation.
-  </summary>
-</histogram>
-
-<histogram
-    name="Android.WebView.LoadDataWithBaseUrl.NextNavigationIsSameDocument"
-    enum="Boolean" expires_after="M95">
-  <obsolete>
-    Removed Sep 2021. See result in crbug.com/1244746.
-  </obsolete>
-  <owner>rakina@chromium.org</owner>
-  <owner>src/android_webview/OWNERS</owner>
-  <summary>
-    Records whether a navigation that happens on/away from a document loaded
-    through a loadDataWithBaseUrl navigation is same-document or not. Recorded
-    at DidCommitNavigation.
-  </summary>
-</histogram>
-
-<histogram name="Android.WebView.LoadDataWithBaseUrl.SameUrlReplacement"
-    enum="Boolean" expires_after="M95">
-  <obsolete>
-    Removed Sep 2021. See result in crbug.com/1244746.
-  </obsolete>
-  <owner>rakina@chromium.org</owner>
-  <owner>src/android_webview/OWNERS</owner>
-  <summary>
-    Records whether a loadDataWithBaseURL navigation is categorized as a
-    same-URL-replacement navigation or not. Recorded from
-    NavigationRequest::StartNavigation().
-  </summary>
-</histogram>
-
 <histogram name="Android.WebView.LoadUrl.UrlScheme" enum="WebViewUrlScheme"
     expires_after="2022-03-29">
   <owner>ntfschr@chromium.org</owner>
@@ -4414,42 +3599,6 @@
   </summary>
 </histogram>
 
-<histogram name="Android.WebView.PageTimeSpent.{Scheme}" units="ms"
-    expires_after="2022-07-01">
-  <obsolete>
-    Removed in October 2021.
-  </obsolete>
-  <owner>mvanouwerkerk@chromium.org</owner>
-  <owner>src/android_webview/OWNERS</owner>
-  <summary>
-    Time spent on a page with a {Scheme} scheme. This is measured from
-    navigation commit to the start of the next navigation.
-  </summary>
-  <token key="Scheme">
-    <variant name="App"/>
-    <variant name="Blob"/>
-    <variant name="Chrome"/>
-    <variant name="ChromeNative"/>
-    <variant name="Content"/>
-    <variant name="CustomTab"/>
-    <variant name="Data"/>
-    <variant name="Devtools"/>
-    <variant name="Document"/>
-    <variant name="File"/>
-    <variant name="Filesystem"/>
-    <variant name="Ftp"/>
-    <variant name="Http"/>
-    <variant name="Https"/>
-    <variant name="Inline"/>
-    <variant name="Intent"/>
-    <variant name="Jar"/>
-    <variant name="JavaScript"/>
-    <variant name="Other"/>
-    <variant name="Sms"/>
-    <variant name="Tel"/>
-  </token>
-</histogram>
-
 <histogram name="Android.WebView.PageTimeSpent2.{Scheme}{Party}" units="ms"
     expires_after="2022-07-01">
   <owner>mvanouwerkerk@chromium.org</owner>
@@ -4491,19 +3640,6 @@
   </token>
 </histogram>
 
-<histogram name="Android.WebView.PrefLoadTime" units="ms"
-    expires_after="2020-12-01">
-  <obsolete>
-    Removed in November 2020.
-  </obsolete>
-  <owner>rmcelrath@chromium.org</owner>
-  <owner>src/android_webview/OWNERS</owner>
-  <summary>
-    Records how long WebView blocks startup to load prefs from disk. This is
-    recorded once per browser process startup.
-  </summary>
-</histogram>
-
 <histogram name="Android.WebView.SafeMode.ActionsCount"
     units="SafeMode actions" expires_after="2022-06-08">
   <owner>ntfschr@chromium.org</owner>
@@ -4575,40 +3711,6 @@
   </summary>
 </histogram>
 
-<histogram name="Android.WebView.SetProxyOverride.BypassRules"
-    enum="BooleanPresent" expires_after="2021-08-01">
-  <obsolete>
-    Removed in July 2021.
-  </obsolete>
-  <owner>ntfschr@chromium.org</owner>
-  <owner>src/android_webview/OWNERS</owner>
-  <summary>
-    Records whether bypass rules were present in SetProxyOverride calls.
-  </summary>
-</histogram>
-
-<histogram name="Android.WebView.SetProxyOverride.ProxySchemeFilterType"
-    enum="AndroidWebViewProxySchemeFilterType" expires_after="2021-08-01">
-  <obsolete>
-    Removed in July 2021.
-  </obsolete>
-  <owner>ntfschr@chromium.org</owner>
-  <owner>src/android_webview/OWNERS</owner>
-  <summary>
-    Records the type of scheme filter in SetProxyOverride calls.
-  </summary>
-</histogram>
-
-<histogram name="Android.WebView.SetProxyOverride.ProxyUrlType"
-    enum="AndroidWebViewProxyUrlType" expires_after="2021-08-01">
-  <obsolete>
-    Removed in July 2021.
-  </obsolete>
-  <owner>ntfschr@chromium.org</owner>
-  <owner>src/android_webview/OWNERS</owner>
-  <summary>Records the type of url in SetProxyOverride calls.</summary>
-</histogram>
-
 <histogram name="Android.WebView.ShouldInterceptRequest.InterceptionType"
     enum="InterceptionType" expires_after="2022-05-10">
   <owner>ntfschr@chromium.org</owner>
@@ -4620,27 +3722,6 @@
   </summary>
 </histogram>
 
-<histogram
-    name="Android.WebView.ShouldInterceptRequest.NullInputStream.ResponseStatusCode"
-    enum="HttpResponseCode" expires_after="2020-06-14">
-  <obsolete>
-    Removed 2021-05
-  </obsolete>
-  <owner>timvolodine@chromium.org</owner>
-  <owner>tobiasjs@chromium.org</owner>
-  <owner>ntfschr@chromium.org</owner>
-  <owner>src/android_webview/OWNERS</owner>
-  <summary>
-    Records the custom response status code for the intercepted requests where
-    input stream is null. In case status code is invalid (or has not been
-    specified by the app) a zero status code is recorded. This UMA is needed in
-    order to track specific usages of request interception where the behavior
-    with network service enabled is different from the old code path (for more
-    details see go/wv-ns-behavior-differences). This data is recorded regardless
-    of whether the network service is enabled or disabled.
-  </summary>
-</histogram>
-
 <histogram name="Android.WebView.SingleOrMultiProcess"
     enum="AndroidWebViewSingleOrMultiProcess" expires_after="2022-01-22">
   <owner>alexmitra@chromium.org</owner>
@@ -4705,35 +3786,6 @@
   </summary>
 </histogram>
 
-<histogram name="Android.WebView.Startup.DataDirLockAttempts" units="attempts"
-    expires_after="2020-12-10">
-  <obsolete>
-    Removed 2020-10
-  </obsolete>
-  <owner>torne@chromium.org</owner>
-  <owner>src/android_webview/OWNERS</owner>
-  <summary>
-    How many attempts it took to acquire the WebView data directory lock. A
-    value of 0 indicates that we reached the max retry count and failed to
-    acquire the lock successfully at all.
-  </summary>
-</histogram>
-
-<histogram name="Android.WebView.Startup.InitAlreadyStarted" enum="Boolean"
-    expires_after="2020-12-10">
-  <obsolete>
-    Removed 2020-10
-  </obsolete>
-  <owner>torne@chromium.org</owner>
-  <owner>src/android_webview/OWNERS</owner>
-  <summary>
-    Records whether we already tried to initialize WebView in the current
-    process previously, upon entry to the initialization function. This should
-    never be true under normal circumstances and may indicate an application
-    catching an exception thrown by initialization and discarding it.
-  </summary>
-</histogram>
-
 <histogram name="Android.WebView.Startup.InitType"
     enum="AndroidWebViewInitType" expires_after="2022-06-21">
   <owner>torne@chromium.org</owner>
@@ -4792,20 +3844,6 @@
   </summary>
 </histogram>
 
-<histogram name="Android.WebView.UserAgent.Valid" enum="WebViewUserAgentType"
-    expires_after="M88">
-  <obsolete>
-    Invalid UAs instead just throw an exception in M89+.
-  </obsolete>
-  <owner>torne@chromium.org</owner>
-  <owner>src/android_webview/OWNERS</owner>
-  <summary>
-    Recorded when an app passes a custom user agent to
-    WebSettings.setUserAgentString. We check if the UA is valid, and if not we
-    try to interpret it in several ways and record which (if any) made sense.
-  </summary>
-</histogram>
-
 <histogram name="Android.WebView.Visibility.Global" enum="WebViewVisibility"
     expires_after="2022-06-01">
   <owner>idries@google.com</owner>
@@ -4868,63 +3906,6 @@
   </summary>
 </histogram>
 
-<histogram name="Android.WebView.WebViewOpenWebVisible.Global"
-    enum="WebViewOpenWebVisibility" expires_after="2022-06-01">
-  <obsolete>
-    Removed in December 2021, use Android.WebView.VisibleScheme.Global instead
-    which breaks down all the visible time spent into more schemes.
-  </obsolete>
-  <owner>idries@google.com</owner>
-  <owner>ntfschr@chromium.org</owner>
-  <owner>src/android_webview/OWNERS</owner>
-  <summary>
-    Identical to Android.WebView.Visibility.Global except that the
-    OpenWebContentVisible bucket only records time that any WebView is visible
-    and displaying contents which originate from the 'Open Web' (in this context
-    that is taken to mean having an HTTP or HTTPS scheme). The
-    OpenWebContentNotVisible bucket records time when the WebView was not
-    visible or not displaying contents which originate from an HTTP or HTTPS
-    scheme.
-  </summary>
-</histogram>
-
-<histogram name="Android.WebView.WebViewOpenWebVisible.PerWebView"
-    enum="WebViewOpenWebVisibility" expires_after="2022-06-01">
-  <obsolete>
-    Deprecated in December 2021, use Android.WebView.VisibleScheme.PerWebView
-    instead which breaks down all the visible time spent into more schemes.
-  </obsolete>
-  <owner>idries@google.com</owner>
-  <owner>ntfschr@chromium.org</owner>
-  <owner>src/android_webview/OWNERS</owner>
-  <summary>
-    Identical to Android.WebView.Visibility.PerWebView except that the
-    OpenWebContentVisible bucket only records time that each WebView is visible
-    and displaying contents which originate from the 'Open Web' (in this context
-    that is taken to mean having an HTTP or HTTPS scheme). The
-    OpenWebContentNotVisible bucket records time when each WebView was not
-    visible or not displaying contents which originate from an HTTP or HTTPS
-    scheme.
-  </summary>
-</histogram>
-
-<histogram name="Android.WebView.WebViewOpenWebVisible.ScreenPortion"
-    enum="WebViewOpenWebScreenPortion" expires_after="2021-09-01">
-  <obsolete>
-    Obsolete from 2021-07, use
-    Android.WebView.WebViewOpenWebVisible.ScreenPortion2 instead. That metric
-    has a separate bucket recording when the coverage is exactly zero.
-  </obsolete>
-  <owner>idries@google.com</owner>
-  <owner>ntfschr@chromium.org</owner>
-  <owner>src/android_webview/OWNERS</owner>
-  <summary>
-    Records the number of seconds that WebView is displaying Open Web Content
-    for each bucket of screen proportion, updated with every Metrics upload. For
-    the purposes of bucketing this is rounded down.
-  </summary>
-</histogram>
-
 <histogram name="Android.WebView.WebViewOpenWebVisible.ScreenPortion2"
     enum="WebViewOpenWebScreenPortion" expires_after="2022-08-01">
   <owner>idries@google.com</owner>
diff --git a/tools/metrics/histograms/metadata/ash/histograms.xml b/tools/metrics/histograms/metadata/ash/histograms.xml
index dfbced7..d2070cc 100644
--- a/tools/metrics/histograms/metadata/ash/histograms.xml
+++ b/tools/metrics/histograms/metadata/ash/histograms.xml
@@ -467,7 +467,7 @@
 
 <histogram name="Ash.CaptureModeController.BarButtons.{TabletOrClamshell}"
     enum="CaptureModeBarButtonType" expires_after="2022-09-29">
-  <owner>chinsenj@chromium.org</owner>
+  <owner>afakhry@chromium.org</owner>
   <owner>gzadina@google.com</owner>
   <summary>
     Recorded whenever a button from the bar widget displayed in Capture mode is
@@ -492,7 +492,7 @@
 <histogram
     name="Ash.CaptureModeController.CaptureConfiguration.{TabletOrClamshell}"
     enum="CaptureModeConfiguration" expires_after="2022-09-29">
-  <owner>chinsenj@chromium.org</owner>
+  <owner>afakhry@chromium.org</owner>
   <owner>gzadina@google.com</owner>
   <summary>
     Recorded whenever a user performs a screen capture in {TabletOrClamshell}.
@@ -505,7 +505,7 @@
 <histogram
     name="Ash.CaptureModeController.CaptureRegionAdjusted.{TabletOrClamshell}"
     units="adjustments" expires_after="2022-09-29">
-  <owner>chinsenj@chromium.org</owner>
+  <owner>afakhry@chromium.org</owner>
   <owner>gzadina@google.com</owner>
   <summary>
     Recorded whenever a user is in a region capture session in
@@ -519,7 +519,7 @@
 
 <histogram name="Ash.CaptureModeController.ConsecutiveScreenshots"
     units="consecutive screenshots" expires_after="2022-09-09">
-  <owner>chinsenj@chromium.org</owner>
+  <owner>afakhry@chromium.org</owner>
   <owner>gzadina@google.com</owner>
   <summary>
     Records the number of consecutive screenshots the user takes within 5s of
@@ -542,7 +542,7 @@
 
 <histogram name="Ash.CaptureModeController.EntryPoint.{TabletOrClamshell}"
     enum="CaptureModeEntryType" expires_after="2022-09-09">
-  <owner>chinsenj@chromium.org</owner>
+  <owner>afakhry@chromium.org</owner>
   <owner>gzadina@google.com</owner>
   <summary>
     Recorded whenever Capture mode is entered from {TabletOrClamshell}. The
@@ -597,7 +597,6 @@
 <histogram name="Ash.CaptureModeController.QuickAction"
     enum="CaptureQuickAction" expires_after="2022-09-09">
   <owner>shidi@chromium.org</owner>
-  <owner>chinsenj@chromium.org</owner>
   <summary>
     Track all quick actions on screenshot notification. Including: Edit in
     backlight, Go to Files, Delete File.
@@ -618,7 +617,6 @@
 <histogram name="Ash.CaptureModeController.ScreenRecordingLength"
     units="seconds" expires_after="2022-09-09">
   <owner>afakhry@chromium.org</owner>
-  <owner>chinsenj@chromium.org</owner>
   <owner>gzadina@google.com</owner>
   <summary>
     Records the time of a successful video recording in capture mode. This
@@ -654,7 +652,7 @@
 
 <histogram name="Ash.CaptureModeController.SwitchesFromInitialCaptureMode"
     enum="Boolean" expires_after="2022-09-09">
-  <owner>chinsenj@chromium.org</owner>
+  <owner>afakhry@chromium.org</owner>
   <owner>gzadina@google.com</owner>
   <summary>
     Emits true if a user has switched capture modes (fullscreen, region, window)
@@ -1074,7 +1072,7 @@
 
 <histogram name="Ash.Desks.ConsecutiveDailyVisits" units="days"
     expires_after="2022-04-24">
-  <owner>chinsenj@chromium.org</owner>
+  <owner>afakhry@chromium.org</owner>
   <owner>janetmac@chromium.org</owner>
   <summary>
     Emitted when a desk is destroyed or a user activates a desk they haven't
@@ -1089,8 +1087,8 @@
 </histogram>
 
 <histogram name="Ash.Desks.DeskLifetime_{DeskIndex}" units="hr"
-    expires_after="2022-02-19">
-  <owner>chinsenj@chromium.org</owner>
+    expires_after="2023-01-13">
+  <owner>afakhry@chromium.org</owner>
   <owner>janetmac@chromium.org</owner>
   <summary>
     The lifetime of the desk at index {DeskIndex} (1-indexed). Emitted when a
@@ -1323,7 +1321,7 @@
 
 <histogram name="Ash.Desks.WeeklyActiveDesks" units="active desks"
     expires_after="2022-03-24">
-  <owner>chinsenj@chromium.org</owner>
+  <owner>afakhry@chromium.org</owner>
   <owner>janetmac@chromium.org</owner>
   <summary>
     The number of unique weekly active desks a user has interacted with in a
@@ -2268,7 +2266,6 @@
 
 <histogram name="Ash.Overview.ArrowKeyPresses" units="units"
     expires_after="2022-04-08">
-  <owner>chinsenj@chromium.org</owner>
   <owner>nupurjain@chromium.org</owner>
   <owner>sammiequon@chromium.org</owner>
   <summary>
@@ -2289,7 +2286,6 @@
 </histogram>
 
 <histogram name="Ash.Overview.Items" units="units" expires_after="2022-04-08">
-  <owner>chinsenj@chromium.org</owner>
   <owner>nupurjain@chromium.org</owner>
   <owner>sammiequon@chromium.org</owner>
   <summary>
@@ -2300,7 +2296,6 @@
 
 <histogram name="Ash.Overview.KeyPressesOverItemsRatio" units="%"
     expires_after="2022-04-08">
-  <owner>chinsenj@chromium.org</owner>
   <owner>nupurjain@chromium.org</owner>
   <owner>sammiequon@chromium.org</owner>
   <summary>
@@ -2312,7 +2307,6 @@
 
 <histogram name="Ash.Overview.OverviewClosedItems" units="units"
     expires_after="2022-04-08">
-  <owner>chinsenj@chromium.org</owner>
   <owner>nupurjain@chromium.org</owner>
   <owner>sammiequon@chromium.org</owner>
   <summary>
@@ -2344,7 +2338,6 @@
 
 <histogram name="Ash.Overview.SelectionDepth" units="items"
     expires_after="2022-04-08">
-  <owner>chinsenj@chromium.org</owner>
   <owner>nupurjain@chromium.org</owner>
   <owner>sammiequon@chromium.org</owner>
   <owner>tclaiborne@chromium.org</owner>
@@ -2367,7 +2360,6 @@
 
 <histogram name="Ash.Overview.TimeBetweenActiveWindowChanges" units="seconds"
     expires_after="2022-04-08">
-  <owner>chinsenj@chromium.org</owner>
   <owner>nupurjain@chromium.org</owner>
   <owner>sammiequon@chromium.org</owner>
   <summary>
@@ -2380,7 +2372,6 @@
 
 <histogram name="Ash.Overview.TimeBetweenUse" units="ms"
     expires_after="2022-04-08">
-  <owner>chinsenj@chromium.org</owner>
   <owner>nupurjain@chromium.org</owner>
   <owner>sammiequon@chromium.org</owner>
   <summary>
@@ -2392,7 +2383,6 @@
 
 <histogram name="Ash.Overview.TimeInOverview" units="ms"
     expires_after="2022-04-08">
-  <owner>chinsenj@chromium.org</owner>
   <owner>nupurjain@chromium.org</owner>
   <owner>sammiequon@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/blink/histograms.xml b/tools/metrics/histograms/metadata/blink/histograms.xml
index 0c08bfd..23f28372 100644
--- a/tools/metrics/histograms/metadata/blink/histograms.xml
+++ b/tools/metrics/histograms/metadata/blink/histograms.xml
@@ -173,20 +173,6 @@
   </summary>
 </histogram>
 
-<histogram name="Blink.Canvas.ContextType" enum="CanvasContextType"
-    expires_after="2022-01-31">
-  <obsolete>
-    Retired in M96, replaced by HTMLCanvasElement_* buckets in
-    Blink.UseCounter.Features wich records at most one count per page visit per
-    context type, as opposed to one count per context creation.
-  </obsolete>
-  <owner>fserb@chromium.org</owner>
-  <owner>aaronhk@chromium.org</owner>
-  <summary>
-    Records the context type names used to create canvas rendering contexts.
-  </summary>
-</histogram>
-
 <histogram name="Blink.Canvas.CreateImageBitmapSource"
     enum="CanvasCreateImageBitmapSource" expires_after="2022-06-05">
   <owner>fserb@chromium.org</owner>
@@ -196,102 +182,6 @@
   </summary>
 </histogram>
 
-<histogram base="true" name="Blink.Canvas.DrawImage.Duration"
-    units="microseconds" expires_after="2021-07-14">
-  <obsolete>
-    Retired in M93, replaced by Blink.Canvas.DrawImage.Duration2 which has a
-    wider range.
-  </obsolete>
-  <owner>fserb@chromium.org</owner>
-  <owner>aaronhk@chromium.org</owner>
-  <summary>
-    Time spent on the main thread during a 2D canvas drawImage API call.
-
-    Note: This metric drops reports on clients with low-resolution clocks, which
-    means these reports will be biased against a portion of the population on
-    Windows. See Windows.HasHighResolutionTimeTicks for the affected sample.
-  </summary>
-</histogram>
-
-<histogram base="true" name="Blink.Canvas.DrawImage.Duration2"
-    units="microseconds" expires_after="M93">
-  <obsolete>
-    Retired in M94.
-  </obsolete>
-  <owner>fserb@chromium.org</owner>
-  <owner>aaronhk@chromium.org</owner>
-  <summary>
-    Time spent on the main thread during a 2D canvas drawImage API call.
-
-    This differs from Blink.Canvas.DrawImage.Duration in that it uses buckets up
-    to ten seconds rather than ten milliseconds (due to a change in the
-    underlying histogramming function called).
-
-    Note: This metric drops reports on clients with low-resolution clocks, which
-    means these reports will be biased against a portion of the population on
-    Windows. See Windows.HasHighResolutionTimeTicks for the affected sample.
-  </summary>
-</histogram>
-
-<histogram
-    name="Blink.Canvas.DrawImage.SqrtNumberOfPixels{BlinkCanvasDrawImageSizeType}"
-    units="sqrt(pixels)" expires_after="M93">
-  <obsolete>
-    Retired in M94.
-  </obsolete>
-  <owner>fserb@chromium.org</owner>
-  <owner>aaronhk@chromium.org</owner>
-  <summary>
-    Stores the square root of the number of pixels drawn into a Canvas.
-    Different histograms per CanvasImageSource. {BlinkCanvasDrawImageSizeType}
-  </summary>
-  <token key="BlinkCanvasDrawImageSizeType">
-    <variant name="">
-      <obsolete>
-        Base histogram. Use suffixes of this histogram instead.
-      </obsolete>
-    </variant>
-    <variant name=".Canvas.CPU" summary=""/>
-    <variant name=".Canvas.GPU" summary=""/>
-    <variant name=".CssImage" summary=""/>
-    <variant name=".ImageBitmap" summary=""/>
-    <variant name=".ImageElement" summary=""/>
-    <variant name=".OffscreenCanvas.CPU" summary=""/>
-    <variant name=".OffscreenCanvas.GPU" summary=""/>
-    <variant name=".SVG" summary=""/>
-    <variant name=".Unknown" summary=""/>
-    <variant name=".Video" summary=""/>
-    <variant name=".VideoFrame" summary=""/>
-  </token>
-</histogram>
-
-<histogram base="true" name="Blink.Canvas.GetImageDataScaledDuration"
-    units="microseconds * 10/sqrt(pixels)" expires_after="M93">
-  <obsolete>
-    Retired in M94.
-  </obsolete>
-  <owner>fserb@chromium.org</owner>
-  <owner>aaronhk@chromium.org</owner>
-  <summary>
-    Time in microseconds * 10 (10e-5 seconds) spent on the 2D canvas
-    getImageData API call, divided by the square root of the total number of
-    pixels of the image extracted from the canvas. Smaller is faster.
-
-    The idea behind this elaborated unit is that the bigger the area of a canvas
-    the longer the operation GetImageData takes, so we want a way to measure the
-    overall performance regardless of the size of the canvas.
-
-    Warning: This metric may include reports from clients with low-resolution
-    clocks (i.e. on Windows, ref. |TimeTicks::IsHighResolution()|). Such reports
-    will cause this metric to have an abnormal distribution. When considering
-    revising this histogram, see UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES for the
-    solution.
-
-    Warning: this histogram was expired from 2021-01-31 to 2021-05-10; data may
-    be missing.
-  </summary>
-</histogram>
-
 <histogram name="Blink.Canvas.GPUFallbackToCPU"
     enum="CanvasGPUFallbackToCPUScenario" expires_after="2022-06-12">
   <owner>fserb@chromium.org</owner>
@@ -316,20 +206,6 @@
   </summary>
 </histogram>
 
-<histogram name="Blink.Canvas.ImageSmoothingQuality"
-    enum="ImageSmoothingQuality" expires_after="2022-01-31">
-  <obsolete>
-    Removed in M92. This metric is added to do some research, and the research
-    is now completed.
-  </obsolete>
-  <owner>fserb@chromium.org</owner>
-  <owner>yiyix@chromium.org</owner>
-  <summary>
-    Records the image smoothing quality used by draw image. Users can choose
-    between none, low, medium or high image quality.
-  </summary>
-</histogram>
-
 <histogram name="Blink.Canvas.IsComposited" enum="Boolean"
     expires_after="2021-01-31">
   <owner>aaronhk@chromium.org</owner>
@@ -373,33 +249,6 @@
   </summary>
 </histogram>
 
-<histogram base="true" name="Blink.Canvas.PutImageDataScaledDuration"
-    units="microseconds * 10/sqrt(pixels)" expires_after="M93">
-  <obsolete>
-    Retired in M94.
-  </obsolete>
-  <owner>fserb@chromium.org</owner>
-  <owner>aaronhk@chromium.org</owner>
-  <summary>
-    Time in microseconds * 10 (10e-5 seconds) spent on the 2D canvas
-    putImageData API call, divided by the square root of the total number of
-    pixels of the imageData painted into the canvas. Smaller is faster.
-
-    The idea behind this elaborated unit is that the bigger the area of a canvas
-    the longer the operation PutImageData takes, so we want a way to measure the
-    overall performance regardless of the size of the canvas.
-
-    Warning: This metric may include reports from clients with low-resolution
-    clocks (i.e. on Windows, ref. |TimeTicks::IsHighResolution()|). Such reports
-    will cause this metric to have an abnormal distribution. When considering
-    revising this histogram, see UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES for the
-    solution.
-
-    Warning: this histogram was expired from 2021-01-31 to 2021-05-10; data may
-    be missing.
-  </summary>
-</histogram>
-
 <histogram name="Blink.Canvas.RasterDuration{BlinkCanvasRasterDurationType}"
     units="microseconds" expires_after="2022-01-31">
   <owner>aaronhk@chromium.org</owner>
@@ -434,11 +283,6 @@
     functions in canvas. {CanvasRequestedImageMimeTypeFunctions}
   </summary>
   <token key="CanvasRequestedImageMimeTypeFunctions">
-    <variant name="">
-      <obsolete>
-        Base histogram. Use suffixes of this histogram instead.
-      </obsolete>
-    </variant>
     <variant name="_convertToBlobPromise"
         summary="Image formats passed to OffscreenCanvas.convertToBlob
                  (promise)"/>
@@ -482,38 +326,6 @@
   </summary>
 </histogram>
 
-<histogram base="true" name="Blink.Canvas.ToBlob.CompleteEncodingDelay"
-    units="microseconds" expires_after="2021-01-31">
-  <obsolete>
-    Removed in M89. Obsolete and unused. This has been replaced with
-    Blink.Canvas.ToBlob.TotalEncodingDelay
-  </obsolete>
-  <owner>fserb@chromium.org</owner>
-  <owner>aaronhk@chromium.org</owner>
-  <summary>
-    This metric measures the total time spent on completing encoding all the
-    rows of an image (jpeg or png), as part of a canvas.toBlob API call.
-    Encoding occurs during one or more idle periods on the main thread and the
-    waiting time of the next idle period is included in the measurement. If the
-    code has swtiched to force encoding path, we will not measure the delay in
-    this metric.
-
-    This metric is useful in helping us adjust the IdleTaskCompleteTimeoutDelay
-    in canvas.toBlob. When the encoding idle task is delayed for longer than
-    IdleTaskCompleteTimeoutDelay, the browser will switch to a non-idle task to
-    force encoding to happen on the main thread.
-
-    In addition, metric values from OffscreenCanvas.convertToBlob API call are
-    also gathered into this histogram, because the logic flow is exactly the
-    same as canvas.toBlob. It's worth to note that the values can come from idle
-    tasks on either main or worker thread.
-
-    Note: This metric drops reports on clients with low-resolution clocks, which
-    means these reports will be biased against a portion of the population on
-    Windows. See Windows.HasHighResolutionTimeTicks for the affected sample.
-  </summary>
-</histogram>
-
 <histogram base="true" name="Blink.Canvas.ToBlob.IdleTaskStatus"
     enum="IdleTaskStatus" expires_after="2021-01-31">
   <owner>fserb@chromium.org</owner>
@@ -560,35 +372,6 @@
   </token>
 </histogram>
 
-<histogram base="true" name="Blink.Canvas.ToBlob.InitiateEncodingDelay"
-    units="microseconds" expires_after="2021-01-31">
-  <obsolete>
-    Removed in M89. Obsolete and unused. This has been replaced with
-    Blink.Canvas.ToBlob.InitialEncodingDelay
-  </obsolete>
-  <owner>fserb@chromium.org</owner>
-  <owner>aaronhk@chromium.org</owner>
-  <summary>
-    This metric measures the time spent from initiating image encoding (jpeg or
-    png) on idle task to the actual execution time of initiation, as part of a
-    canvas.toBlob API call.
-
-    This metric is useful in helping us adjust the IdleTaskStartTimeoutDelay in
-    canvas.toBlob. When the initialization idle task is delayed for longer than
-    IdleTaskStartTimeoutDelay, the browser will switch to a non-idle task to
-    force initialization and encoding to occur on the main thread.
-
-    In addition, metric values from OffscreenCanvas.convertToBlob API call are
-    also gathered into this histogram, because the logic flow is exactly the
-    same as canvas.toBlob. It's worth to note that the values can come from idle
-    tasks on either main or worker thread.
-
-    Note: This metric drops reports on clients with low-resolution clocks, which
-    means these reports will be biased against a portion of the population on
-    Windows. See Windows.HasHighResolutionTimeTicks for the affected sample.
-  </summary>
-</histogram>
-
 <histogram name="Blink.Canvas.ToBlob.ScaledDuration{BlinkCanvasToBlobType}"
     units="microseconds/sqrt(pixels)" expires_after="2022-04-04">
   <owner>fserb@chromium.org</owner>
@@ -608,11 +391,6 @@
     {BlinkCanvasToBlobType}
   </summary>
   <token key="BlinkCanvasToBlobType">
-    <variant name="">
-      <obsolete>
-        Base histogram. Use suffixes of this histogram instead.
-      </obsolete>
-    </variant>
     <variant name=".JPEG" summary=""/>
     <variant name=".PNG" summary=""/>
     <variant name=".WEBP" summary=""/>
@@ -674,38 +452,8 @@
     solution. {BlinkCanvasToDataURLMimeType}
   </summary>
   <token key="BlinkCanvasToDataURLMimeType">
-    <variant name="">
-      <obsolete>
-        Base histogram. Use suffixes of this histogram instead.
-      </obsolete>
-    </variant>
-    <variant name=".BMP" summary="">
-      <obsolete>
-        Removed in Oct 2016
-      </obsolete>
-    </variant>
-    <variant name=".GIF" summary="">
-      <obsolete>
-        Removed in Oct 2016
-      </obsolete>
-    </variant>
-    <variant name=".ICON" summary="">
-      <obsolete>
-        Removed in Oct 2016
-      </obsolete>
-    </variant>
     <variant name=".JPEG" summary=""/>
     <variant name=".PNG" summary=""/>
-    <variant name=".TIFF" summary="">
-      <obsolete>
-        Removed in Oct 2016
-      </obsolete>
-    </variant>
-    <variant name=".Unknown" summary="">
-      <obsolete>
-        Removed in Oct 2016
-      </obsolete>
-    </variant>
     <variant name=".WEBP" summary=""/>
   </token>
 </histogram>
@@ -749,29 +497,6 @@
   </summary>
 </histogram>
 
-<histogram name="Blink.Clipboard.Paste.Image" enum="ClipboardPastedImageUrls"
-    expires_after="2021-09-05">
-  <obsolete>
-    Retired in Sept 2021 as enough data was collected.
-  </obsolete>
-  <owner>huangdarwin@chromium.org</owner>
-  <owner>snianu@microsoft.com</owner>
-  <summary>
-    When HTML is parsed during a paste operation, image elements may have a
-    source attribute pointing to a local file url, http(s) url, cid or other url
-    type which doesn't render the image when it's pasted inside a content
-    editable region. This metric will help us determine how many paste
-    operations have an image with a src url attribute containing the url types
-    mentioned above and whether there is an RTF format in the clipboard along
-    with HTML or not. The RTF format data will help us decide whether we need to
-    support paste for images that are in RTF format types when it's available on
-    the clipboard along with HTML format so the apps can use it to insert the
-    images which then can be used by the Browser to replace the src urls of the
-    images in the HTML markup with a Blob url. This would enable the site to
-    store the images when it's pasted inside a content editable region.
-  </summary>
-</histogram>
-
 <histogram name="Blink.ColorGamut.Destination" enum="Gamut" expires_after="M85">
   <owner>brianosman@chromium.org</owner>
   <owner>mcasas@google.com</owner>
@@ -798,71 +523,6 @@
   </summary>
 </histogram>
 
-<histogram
-    name="Blink.Compositing.LayerPromotionCount{BlinkCompositingLayerCount}"
-    units="units" expires_after="2021-12-05">
-  <owner>pdr@chromium.org</owner>
-  <owner>paint-dev@chromium.org</owner>
-  <summary>
-    Number of composited layers based on various reasons. Recorded when a page
-    finishes its layer updates. {BlinkCompositingLayerCount}
-  </summary>
-  <token key="BlinkCompositingLayerCount">
-    <variant name="">
-      <obsolete>
-        Base histogram. Use suffixes of this histogram instead.
-      </obsolete>
-    </variant>
-    <variant name=".ActiveAnimation" summary="">
-      <obsolete>
-        Deprecated as of 2021-10.
-      </obsolete>
-    </variant>
-    <variant name=".AssumedOverlap" summary="">
-      <obsolete>
-        Deprecated as of 2021-10.
-      </obsolete>
-    </variant>
-    <variant name=".IndirectComposited" summary="">
-      <obsolete>
-        Deprecated as of 2021-10.
-      </obsolete>
-    </variant>
-    <variant name=".Overlap" summary="">
-      <obsolete>
-        Deprecated as of 2021-10.
-      </obsolete>
-    </variant>
-    <variant name=".TotalComposited" summary="">
-      <obsolete>
-        Deprecated as of 2021-10.
-      </obsolete>
-    </variant>
-  </token>
-</histogram>
-
-<histogram base="true" name="Blink.CompositingAssignments.UpdateTime"
-    units="microseconds" expires_after="M97">
-  <obsolete>
-    No longer recorded after M97 with the launch of CompositeAfterPaint.
-  </obsolete>
-<!-- Name completed by histogram_suffixes name="BlinkUpdateTimePreFCPSuffixes" -->
-
-<!-- Name completed by histogram_suffixes name="BlinkUpdateTimePostFCPSuffixes" -->
-
-<!-- Name completed by histogram_suffixes name="BlinkUpdateTimeAggregatedSuffixes" -->
-
-  <owner>schenney@chromium.org</owner>
-  <owner>paint-dev@chromium.org</owner>
-  <summary>
-    Time spent updating compositing assignments in the Blink document lifecycle.
-    This is the legacy compositing approach.
-
-    Note: this histogram has stopped recording metrics on machines with
-    low-resolution clocks.
-  </summary>
-</histogram>
-
 <histogram base="true" name="Blink.CompositingCommit.UpdateTime"
     units="microseconds" expires_after="2022-07-17">
 <!-- Name completed by histogram_suffixes name="BlinkUpdateTimePreFCPSuffixes" -->
@@ -1002,20 +662,6 @@
   </summary>
 </histogram>
 
-<histogram name="Blink.CookieStore.MatchType" enum="CookieStoreMatchType"
-    expires_after="M87">
-  <obsolete>
-    Removed in M87.
-  </obsolete>
-  <owner>ayui@chromium.org</owner>
-  <owner>pwnall@chromium.org</owner>
-  <summary>
-    Records match type option set by the developer for Cookie Store query APIs
-    to measure the usage of the option during Origin Trial to better evaluate
-    the best shape of the API.
-  </summary>
-</histogram>
-
 <histogram name="Blink.CSSPaintValue.PaintOffThread"
     enum="BooleanCompositorCSSPaint" expires_after="2022-03-08">
   <owner>xidachen@chromium.org</owner>
@@ -1053,20 +699,6 @@
   </summary>
 </histogram>
 
-<histogram name="Blink.DecodedImage.DensitySizeCorrectionDetected"
-    enum="BooleanPresent" expires_after="2021-01-03">
-  <obsolete>
-    Removed in M89.
-  </obsolete>
-  <owner>schenney@chromium.org</owner>
-  <owner>noam.j.rosenthal@gmail.com</owner>
-  <owner>paint-dev@chromium.org</owner>
-  <summary>
-    Indicates whether a decoded image contains density correction metadata, i.e.
-    when its resolution and dimension EXIF values match.
-  </summary>
-</histogram>
-
 <histogram name="Blink.DecodedImage.JpegDensity.KiBWeighted"
     units="0.01 bits per pixel" expires_after="2022-10-01">
   <owner>jyrki@google.com</owner>
@@ -1080,16 +712,6 @@
   </summary>
 </histogram>
 
-<histogram name="Blink.DecodedImage.Orientation" enum="DecodedImageOrientation"
-    expires_after="M81">
-  <obsolete>
-    Removed in M88.
-  </obsolete>
-  <owner>andrescj@chromium.org</owner>
-  <owner>rob.buis@samsung.org</owner>
-  <summary>Image orientation inferred during decode.</summary>
-</histogram>
-
 <histogram name="Blink.DecodedImage.WebPDensity.KiBWeighted"
     units="0.01 bits per pixel" expires_after="2022-10-01">
   <owner>jyrki@google.com</owner>
@@ -1304,22 +926,6 @@
   </summary>
 </histogram>
 
-<histogram name="Blink.Fonts.Enumeration.Duration" units="ms"
-    expires_after="2021-10-15">
-  <obsolete>
-    Removed in M89. Obsolete and unused. This has been replaced with
-    Fonts.AccessAPI.EnumerationCache.*
-  </obsolete>
-  <owner>pwnall@chromium.org</owner>
-  <owner>layout-dev@chromium.org</owner>
-  <owner>storage-dev@chromium.org</owner>
-  <summary>
-    Records duration of enumerating available fonts for font name metadata.
-    Reported on Mac platforms only, when the fonts are enumerated from the
-    system APIs if there is a cache miss.
-  </summary>
-</histogram>
-
 <histogram name="Blink.Fonts.FontFamilyMatchAttempts.System" units="attempts"
     expires_after="2021-12-20">
   <owner>caraitto@chromium.org</owner>
@@ -1818,13 +1424,7 @@
     emitted when all data is received. {BlinkIncrementalDecodeImageTypes}
   </summary>
   <token key="BlinkIncrementalDecodeImageTypes"
-      variants="BlinkIncrementalDecodeImageTypes">
-    <variant name="">
-      <obsolete>
-        Base histogram. Use suffixes of this histogram instead.
-      </obsolete>
-    </variant>
-  </token>
+      variants="BlinkIncrementalDecodeImageTypes"/>
 </histogram>
 
 <histogram
@@ -1845,13 +1445,7 @@
     {BlinkIncrementalDecodeImageTypes}
   </summary>
   <token key="BlinkIncrementalDecodeImageTypes"
-      variants="BlinkIncrementalDecodeImageTypes">
-    <variant name="">
-      <obsolete>
-        Base histogram. Use suffixes of this histogram instead.
-      </obsolete>
-    </variant>
-  </token>
+      variants="BlinkIncrementalDecodeImageTypes"/>
 </histogram>
 
 <histogram name="Blink.ImageDecoders.Jpeg.Area" units="pixels"
@@ -2197,250 +1791,6 @@
   </summary>
 </histogram>
 
-<histogram base="true" name="Blink.MainFrame.AnimateRatio" units="%"
-    expires_after="2020-11-08">
-  <obsolete>
-    Removed M88 due to lack of use.
-  </obsolete>
-  <owner>schenney@chromium.org</owner>
-  <owner>paint-dev@chromium.org</owner>
-<!-- Name completed by histogram_suffixes name="BlinkMainFrameUpdateTimeSuffixes" -->
-
-  <summary>
-    The percentage of time between a BeginMainFrame and paint results commit in
-    Blink that is used for computing Main Frame animations and rAF callbacks.
-  </summary>
-</histogram>
-
-<histogram base="true" name="Blink.MainFrame.CompositingAssignmentsRatio"
-    units="%" expires_after="2020-11-08">
-  <obsolete>
-    Removed M88 due to lack of use.
-  </obsolete>
-  <owner>schenney@chromium.org</owner>
-  <owner>paint-dev@chromium.org</owner>
-<!-- Name completed by histogram_suffixes name="BlinkMainFrameUpdateTimeSuffixes" -->
-
-  <summary>
-    The percentage of time between a BeginMainFrame and paint results commit in
-    Blink that is used for computing compositing assignments.
-  </summary>
-</histogram>
-
-<histogram base="true" name="Blink.MainFrame.CompositingCommitRatio" units="%"
-    expires_after="2020-11-08">
-  <obsolete>
-    Removed M88 due to lack of use.
-  </obsolete>
-  <owner>schenney@chromium.org</owner>
-  <owner>paint-dev@chromium.org</owner>
-<!-- Name completed by histogram_suffixes name="BlinkMainFrameUpdateTimeSuffixes" -->
-
-  <summary>
-    The percentage of time between a BeginMainFrame and paint results commit in
-    Blink that is used for committing paint results to the compositor.
-  </summary>
-</histogram>
-
-<histogram base="true" name="Blink.MainFrame.CompositingInputsRatio" units="%"
-    expires_after="2020-11-08">
-  <obsolete>
-    Removed M88 due to lack of use.
-  </obsolete>
-  <owner>schenney@chromium.org</owner>
-  <owner>paint-dev@chromium.org</owner>
-<!-- Name completed by histogram_suffixes name="BlinkMainFrameUpdateTimeSuffixes" -->
-
-  <summary>
-    The percentage of time between a BeginMainFrame and paint results commit in
-    Blink that is used for computing compositing inputs.
-  </summary>
-</histogram>
-
-<histogram base="true" name="Blink.MainFrame.CompositingRatio" units="%"
-    expires_after="2020-11-08">
-  <obsolete>
-    Removed M88 due to lack of use.
-  </obsolete>
-  <owner>schenney@chromium.org</owner>
-  <owner>paint-dev@chromium.org</owner>
-<!-- Name completed by histogram_suffixes name="BlinkMainFrameUpdateTimeSuffixes" -->
-
-  <summary>
-    The percentage of time between a BeginMainFrame and paint results commit in
-    Blink that is used for computing Compositing.
-  </summary>
-</histogram>
-
-<histogram base="true" name="Blink.MainFrame.ForcedStyleAndLayoutRatio"
-    units="%" expires_after="2021-01-10">
-  <obsolete>
-    Removed M88 due to lack of use.
-  </obsolete>
-  <owner>schenney@chromium.org</owner>
-  <owner>paint-dev@chromium.org</owner>
-<!-- Name completed by histogram_suffixes name="BlinkMainFrameUpdateTimeSuffixes" -->
-
-  <summary>
-    The time between each paint results commit used in computing forced style
-    recalc and layouts for this document and ancestors as a percentage of the
-    Blink local frame lifecycle update time.
-  </summary>
-</histogram>
-
-<histogram base="true" name="Blink.MainFrame.HandleInputEventsRatio" units="%"
-    expires_after="2020-11-08">
-  <obsolete>
-    Removed M88 due to lack of use.
-  </obsolete>
-  <owner>schenney@chromium.org</owner>
-  <owner>paint-dev@chromium.org</owner>
-<!-- Name completed by histogram_suffixes name="BlinkMainFrameUpdateTimeSuffixes" -->
-
-  <summary>
-    The percentage of time between a BeginMainFrame and paint results commit in
-    Blink that is used for processing rAF aligned input.
-  </summary>
-</histogram>
-
-<histogram base="true" name="Blink.MainFrame.HitTestDocumentUpdateRatio"
-    units="%" expires_after="2020-11-08">
-  <obsolete>
-    Removed M88 due to lack of use.
-  </obsolete>
-  <owner>schenney@chromium.org</owner>
-  <owner>paint-dev@chromium.org</owner>
-<!-- Name completed by histogram_suffixes name="BlinkMainFrameUpdateTimeSuffixes" -->
-
-  <summary>
-    The percentage of time between a BeginMainFrame and paint results commit in
-    Blink that is used for updating the document in preparation for hit testing.
-  </summary>
-</histogram>
-
-<histogram base="true" name="Blink.MainFrame.ImplCompositorCommitRatio"
-    units="%" expires_after="2020-11-08">
-  <obsolete>
-    Removed M88 due to lack of use.
-  </obsolete>
-  <owner>schenney@chromium.org</owner>
-  <owner>paint-dev@chromium.org</owner>
-<!-- Name completed by histogram_suffixes name="BlinkMainFrameUpdateTimeSuffixes" -->
-
-  <summary>
-    The percentage of time between a BeginMainFrame and paint results commit in
-    Blink that is used for committing the layer tree to the impl thread,
-    excluding time spend waiting for the CC thread to begin processing.
-  </summary>
-</histogram>
-
-<histogram base="true" name="Blink.MainFrame.IntersectionObservationRatio"
-    units="%" expires_after="2021-08-09">
-  <obsolete>
-    Removed M88 due to lack of use.
-  </obsolete>
-  <owner>schenney@chromium.org</owner>
-  <owner>paint-dev@chromium.org</owner>
-<!-- Name completed by histogram_suffixes name="BlinkMainFrameUpdateTimeSuffixes" -->
-
-  <summary>
-    The percentage of time between a BeginMainFrame and paint results commit in
-    Blink that is used for computing Intersection Observations.
-  </summary>
-</histogram>
-
-<histogram base="true" name="Blink.MainFrame.LayoutRatio" units="%"
-    expires_after="2021-03-21">
-  <obsolete>
-    Removed M88 due to lack of use.
-  </obsolete>
-  <owner>schenney@chromium.org</owner>
-  <owner>paint-dev@chromium.org</owner>
-<!-- Name completed by histogram_suffixes name="BlinkMainFrameUpdateTimeSuffixes" -->
-
-  <summary>
-    The percentage of time between a BeginMainFrame and paint results commit in
-    Blink that is used for computing Layout.
-  </summary>
-</histogram>
-
-<histogram base="true" name="Blink.MainFrame.PaintRatio" units="%"
-    expires_after="2021-03-21">
-  <obsolete>
-    Removed M88 due to lack of use.
-  </obsolete>
-  <owner>schenney@chromium.org</owner>
-  <owner>paint-dev@chromium.org</owner>
-<!-- Name completed by histogram_suffixes name="BlinkMainFrameUpdateTimeSuffixes" -->
-
-  <summary>
-    The percentage of time between a BeginMainFrame and paint results commit in
-    Blink that is used for computing Paint.
-  </summary>
-</histogram>
-
-<histogram base="true" name="Blink.MainFrame.PrePaintRatio" units="%"
-    expires_after="2020-11-08">
-  <obsolete>
-    Removed M88 due to lack of use.
-  </obsolete>
-  <owner>schenney@chromium.org</owner>
-  <owner>paint-dev@chromium.org</owner>
-<!-- Name completed by histogram_suffixes name="BlinkMainFrameUpdateTimeSuffixes" -->
-
-  <summary>
-    The percentage of time between a BeginMainFrame and paint results commit in
-    Blink that is used for computing PrePaint.
-  </summary>
-</histogram>
-
-<histogram base="true" name="Blink.MainFrame.ScrollingCoordinatorRatio"
-    units="%" expires_after="M88">
-  <obsolete>
-    Merged into Blink.MainFrame.CompositingCommitRatio in
-    http://crrev.com/815947 in M88.
-  </obsolete>
-  <owner>schenney@chromium.org</owner>
-  <owner>paint-dev@chromium.org</owner>
-<!-- Name completed by histogram_suffixes name="BlinkMainFrameUpdateTimeSuffixes" -->
-
-  <summary>
-    The percentage of the total BeginMainFrame execution time used for the
-    ScrollingCoordinator update in Blink, for each frame that we record.
-    Recorded in ScrollingCoordinator::UpdateAfterPaint.
-  </summary>
-</histogram>
-
-<histogram base="true" name="Blink.MainFrame.StyleRatio" units="%"
-    expires_after="2020-11-08">
-  <obsolete>
-    Removed M88 due to lack of use.
-  </obsolete>
-  <owner>schenney@chromium.org</owner>
-  <owner>paint-dev@chromium.org</owner>
-<!-- Name completed by histogram_suffixes name="BlinkMainFrameUpdateTimeSuffixes" -->
-
-  <summary>
-    The percentage of time between a BeginMainFrame and paint results commit in
-    Blink that is used for computing Style.
-  </summary>
-</histogram>
-
-<histogram base="true" name="Blink.MainFrame.UpdateLayersRatio" units="%"
-    expires_after="2021-03-21">
-  <obsolete>
-    Removed M88 due to lack of use.
-  </obsolete>
-  <owner>schenney@chromium.org</owner>
-  <owner>paint-dev@chromium.org</owner>
-<!-- Name completed by histogram_suffixes name="BlinkMainFrameUpdateTimeSuffixes" -->
-
-  <summary>
-    The percentage of time between a BeginMainFrame and paint results commit in
-    Blink that is spent in LayerTreeHost::UpdateLayers.
-  </summary>
-</histogram>
-
 <histogram base="true" name="Blink.MainFrame.UpdateTime" units="microseconds"
     expires_after="2022-07-03">
 <!-- Name completed by histogram_suffixes name="BlinkUpdateTimePreFCPSuffixes" -->
@@ -2459,21 +1809,6 @@
   </summary>
 </histogram>
 
-<histogram name="Blink.MainFrame.WaitForCommitRatio" units="%"
-    expires_after="2020-11-08">
-  <obsolete>
-    Removed M88 due to lack of use.
-  </obsolete>
-  <owner>schenney@chromium.org</owner>
-  <owner>paint-dev@chromium.org</owner>
-<!-- Name completed by histogram_suffixes name="BlinkMainFrameUpdateTimeSuffixes" -->
-
-  <summary>
-    The percentage of time between a BeginMainFrame and paint results commit in
-    Blink that is spent waiting for the CC thread to begin processing.
-  </summary>
-</histogram>
-
 <histogram base="true" name="Blink.MediaIntersectionObserver.UpdateTime"
     units="microseconds" expires_after="2022-10-10">
 <!-- Name completed by histogram_suffixes name="BlinkUpdateTimePreFCPSuffixes" -->
@@ -2527,22 +1862,6 @@
   </summary>
 </histogram>
 
-<histogram name="Blink.MemoryCache.RevalidationPolicy.PerDocument"
-    enum="RevalidationPolicy" expires_after="2021-06-20">
-  <obsolete>
-    Removed in M89. Obsolete and unused. This has been replaced with
-    Blink.MemoryCache.RevalidationPolicy with feature
-    ScopeMemoryCachePerContext.
-  </obsolete>
-  <owner>shivanisha@chromium.org</owner>
-  <owner>privacy-sandbox-dev@chromium.org</owner>
-  <summary>
-    RevalidationPolicy used for requests for each resource type. Logged only if
-    the resource is found in the memory cache and if the same document had
-    loaded this resource earlier.
-  </summary>
-</histogram>
-
 <histogram name="Blink.MemoryCache.RevalidationPolicy.PerTopFrameSite"
     enum="RevalidationPolicy" expires_after="2021-12-26">
   <owner>shivanisha@chromium.org</owner>
@@ -2587,21 +1906,6 @@
   </summary>
 </histogram>
 
-<histogram name="Blink.OffscreenCanvas.ContextType" enum="CanvasContextType"
-    expires_after="2021-10-17">
-  <obsolete>
-    Retired in M96, replaced by OffscreenCanvas_* buckets in
-    Blink.UseCounter.Features wich records at most one count per page visit per
-    context type, as opposed to one count per context creation.
-  </obsolete>
-  <owner>aaronhk@chromium.org</owner>
-  <owner>fserb@chromium.org</owner>
-  <summary>
-    Records the context type names used to create offscreen canvas rendering
-    contexts. Recorded in OffscreenCanvas::GetCanvasRenderingContext
-  </summary>
-</histogram>
-
 <histogram name="Blink.OffscreenCanvas.NewOffscreenCanvas" enum="Boolean"
     expires_after="2022-06-05">
   <owner>aaronhk@chromium.org</owner>
@@ -2638,35 +1942,6 @@
   </summary>
 </histogram>
 
-<histogram name="Blink.Paint.CachedItemPercentage" units="%"
-    expires_after="2021-07-01">
-  <obsolete>
-    Removed June 2021.
-  </obsolete>
-  <owner>wangxianzhu@chromium.org</owner>
-  <owner>paint-dev@chromium.org</owner>
-  <summary>
-    Percentage of cached display items among all display items. Higher value
-    means higher performance beause of less paint invalidation. Recorded when we
-    finish updating paint in the Blink document lifecycle.
-  </summary>
-</histogram>
-
-<histogram name="Blink.Paint.CachedSubsequencePercentage" units="%"
-    expires_after="2021-07-01">
-  <obsolete>
-    Removed June 2021.
-  </obsolete>
-  <owner>wangxianzhu@chromium.org</owner>
-  <owner>paint-dev@chromium.org</owner>
-  <summary>
-    Percentage of cached subsequences of display items among all subsequences.
-    Higher value means higher performance beause of less subsequence
-    invalidation. Recorded when we finish updating paint in the Blink document
-    lifecycle.
-  </summary>
-</histogram>
-
 <histogram base="true" name="Blink.Paint.UpdateTime" units="microseconds"
     expires_after="2022-07-03">
 <!-- Name completed by histogram_suffixes name="BlinkUpdateTimePreFCPSuffixes" -->
@@ -2714,26 +1989,6 @@
   </summary>
 </histogram>
 
-<histogram name="Blink.Render.DisplayLockActivationReason"
-    enum="DisplayLockActivationReason" expires_after="2021-12-12">
-  <obsolete>
-    Removed 11/2021.
-  </obsolete>
-  <owner>vmpstr@chromium.org</owner>
-  <owner>chrishtr@chromium.org</owner>
-  <summary>This indicates the reason for display-locking activation</summary>
-</histogram>
-
-<histogram name="Blink.ResourceFetcher.StaleWhileRevalidateDuration" units="ms"
-    expires_after="2020-04-05">
-  <obsolete>
-    Removed 12/2020.
-  </obsolete>
-  <owner>dtapuska@chromium.org</owner>
-  <owner>kenjibaheux@google.com</owner>
-  <summary>Duration of completed stale revalidation attempts.</summary>
-</histogram>
-
 <histogram name="Blink.ResourceLoadScheduler.TrafficBytes.KBPerFrameStatus"
     enum="RendererSchedulerFrameType2" expires_after="2020-02-02">
   <owner>toyoshim@chromium.org</owner>
@@ -2825,77 +2080,6 @@
   </summary>
 </histogram>
 
-<histogram name="Blink.Script.ForceDeferredScripts.Mainframe" units="count"
-    expires_after="2021-05-02">
-  <obsolete>
-    Obsoleted Feb 2021.
-  </obsolete>
-  <owner>dougarnett@chromium.org</owner>
-  <owner>hiroshige@chromium.org</owner>
-  <summary>
-    Number of synchronous scripts that were deferred for a mainframe by the
-    ForceDeferScriptIntervention. This includes both inline scripts and external
-    scripts that had their execution deferred until after parsing completes.
-    Recorded when there is at least one force deferred script for the frame.
-  </summary>
-</histogram>
-
-<histogram name="Blink.Script.ForceDeferredScripts.Mainframe.External"
-    units="count" expires_after="2021-05-02">
-  <obsolete>
-    Obsoleted Feb 2021.
-  </obsolete>
-  <owner>dougarnett@chromium.org</owner>
-  <owner>hiroshige@chromium.org</owner>
-  <summary>
-    Number of external synchronous scripts that were deferred for a mainframe by
-    the ForceDeferScriptIntervention. Recorded when there is at least one force
-    deferred script for the frame (not necessarily external).
-  </summary>
-</histogram>
-
-<histogram name="Blink.Script.ForceDeferredScripts.Subframe" units="count"
-    expires_after="2021-02-28">
-  <obsolete>
-    Obsoleted Feb 2021.
-  </obsolete>
-  <owner>dougarnett@chromium.org</owner>
-  <owner>hiroshige@chromium.org</owner>
-  <summary>
-    Number of synchronous scripts that were deferred for a subframe by the
-    ForceDeferScriptIntervention. This includes both inline scripts and external
-    scripts that had their execution deferred until after parsing completes.
-    Recorded when there is at least one force deferred script for the frame.
-  </summary>
-</histogram>
-
-<histogram name="Blink.Script.ForceDeferredScripts.Subframe.External"
-    units="count" expires_after="2021-02-28">
-  <obsolete>
-    Obsoleted Feb 2021.
-  </obsolete>
-  <owner>dougarnett@chromium.org</owner>
-  <owner>hiroshige@chromium.org</owner>
-  <summary>
-    Number of external synchronous scripts that were deferred for a subframe by
-    the ForceDeferScriptIntervention. Recorded when there is at least one force
-    deferred script for the frame (not necessarily external).
-  </summary>
-</histogram>
-
-<histogram name="Blink.Script.SchedulingType" enum="ScriptSchedulingType"
-    expires_after="2021-07-11">
-  <obsolete>
-    Removed in M90 because we collected enough insights.
-  </obsolete>
-  <owner>kouhei@chromium.org</owner>
-  <owner>hiroshige@chromium.org</owner>
-  <summary>
-    Number of script elements for each scheduling type, recorded for each
-    successful #prepare-a-script.
-  </summary>
-</histogram>
-
 <histogram base="true" name="Blink.ScrollDocumentUpdate.UpdateTime"
     units="microseconds" expires_after="2022-07-03">
 <!-- Name completed by histogram_suffixes name="BlinkUpdateTimePreFCPSuffixes" -->
@@ -2916,33 +2100,6 @@
   </summary>
 </histogram>
 
-<histogram base="true" name="Blink.ScrollingCoordinator.UpdateTime"
-    units="microseconds" expires_after="M88">
-  <obsolete>
-    Merged into Blink.CompositingCommit.UpdateTime in http://crrev.com/815947 in
-    M88.
-  </obsolete>
-<!-- Name completed by histogram_suffixes name="BlinkUpdateTimePreFCPSuffixes" -->
-
-<!-- Name completed by histogram_suffixes name="BlinkUpdateTimePostFCPSuffixes" -->
-
-<!-- Name completed by histogram_suffixes name="BlinkUpdateTimeAggregatedSuffixes" -->
-
-  <owner>pdr@chromium.org</owner>
-  <owner>paint-dev@chromium.org</owner>
-  <summary>
-    The time it took to update scrolling coordinator data (scroll gesture
-    regions, touch event rects, and main thread scrolling reasons). These values
-    are calculated during the main-thread blink lifecycle.
-
-    Warning: This metric may include reports from clients with low-resolution
-    clocks (i.e. on Windows, ref. |TimeTicks::IsHighResolution()|). Such reports
-    will cause this metric to have an abnormal distribution. When considering
-    revising this histogram, see UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES for the
-    solution.
-  </summary>
-</histogram>
-
 <histogram base="true" name="Blink.ServiceDocumentUpdate.UpdateTime"
     units="microseconds" expires_after="2022-07-03">
 <!-- Name completed by histogram_suffixes name="BlinkUpdateTimePreFCPSuffixes" -->
@@ -2994,19 +2151,6 @@
   </summary>
 </histogram>
 
-<histogram name="Blink.Sms.Receive.DestroyedReason"
-    enum="WebOTPServiceDestroyedReason" expires_after="2021-12-12">
-  <obsolete>
-    Deprecated 09/2021. We have learned about the pattern of the destruction.
-  </obsolete>
-  <owner>yigu@chromium.org</owner>
-  <owner>goto@chromium.org</owner>
-  <owner>web-identity@google.com</owner>
-  <summary>
-    Records the reason an SMS Service is destroyed before request completion.
-  </summary>
-</histogram>
-
 <histogram name="Blink.Sms.Receive.Infobar" enum="WebOTPServiceInfobarAction"
     expires_after="2022-06-26">
   <owner>yigu@chromium.org</owner>
@@ -3128,18 +2272,6 @@
   </summary>
 </histogram>
 
-<histogram name="Blink.SpatialNavigation.Advance" units="microseconds"
-    expires_after="M85">
-  <obsolete>
-    Expired in M85.
-  </obsolete>
-  <owner>bokan@chromium.org</owner>
-  <summary>
-    Time it takes to find best candidate element, set focus or scroll for given
-    directional input.
-  </summary>
-</histogram>
-
 <histogram base="true" name="Blink.Style.UpdateTime" units="microseconds"
     expires_after="2022-06-05">
 <!-- Name completed by histogram_suffixes name="BlinkUpdateTimePreFCPSuffixes" -->
@@ -3345,37 +2477,6 @@
   </summary>
 </histogram>
 
-<histogram name="Blink.UseCounter.FeaturePolicy.PotentialViolation"
-    enum="FeaturePolicyFeature" expires_after="2021-01-03">
-  <obsolete>
-    Remoted in M91.
-  </obsolete>
-  <owner>iclelland@chromium.org</owner>
-  <owner>feature-control@chromium.org</owner>
-  <summary>
-    Tracks potential violations of feature policies in a document. This is
-    emitted once all the conditions for violating the feature policy are met in
-    the document; regardless of whether or not the feature has been disabled in
-    the document; in other words, it still records a potential violation even if
-    there won't be a violation report generated. Note that this is recorded once
-    per document.
-  </summary>
-</histogram>
-
-<histogram name="Blink.UseCounter.FeaturePolicy.ProposalWouldChangeBehaviour"
-    enum="FeaturePolicyFeature" expires_after="2021-03-07">
-  <obsolete>
-    Removed in M88.
-  </obsolete>
-  <owner>iclelland@chromium.org</owner>
-  <owner>feature-control@chromium.org</owner>
-  <summary>
-    Counts when the result of calling IsFeatureEnabled for an Execution Context
-    would be different under the proposal in https://crbug.com/937171. Each
-    feature is counted only once per execution context.
-  </summary>
-</histogram>
-
 <histogram name="Blink.UseCounter.Features" enum="FeatureObserver"
     expires_after="never">
 <!-- expires-never: used by Chrome Platform Status dashboard -->
@@ -3429,20 +2530,6 @@
   </summary>
 </histogram>
 
-<histogram name="Blink.UseCounter.PermissionsPolicy.Allow"
-    enum="FeaturePolicyFeature" expires_after="2022-05-30">
-  <obsolete>
-    Obsolete in 2021-09. Use Blink.UseCounter.PermissionsPolicy.Allow2 instead.
-  </obsolete>
-  <owner>iclelland@chromium.org</owner>
-  <owner>feature-control@chromium.org</owner>
-  <summary>
-    Counts the use of a specific permissions policy feature via the
-    &quot;allow&quot; attribute on iframes. Each policy is counted only once per
-    page load.
-  </summary>
-</histogram>
-
 <histogram name="Blink.UseCounter.PermissionsPolicy.Allow2"
     enum="FeaturePolicyFeature" expires_after="2022-05-30">
   <owner>iclelland@chromium.org</owner>
@@ -3455,20 +2542,6 @@
   </summary>
 </histogram>
 
-<histogram name="Blink.UseCounter.PermissionsPolicy.Header"
-    enum="FeaturePolicyFeature" expires_after="2022-05-30">
-  <obsolete>
-    Obsolete in 2021-09. Use Blink.UseCounter.PermissionsPolicy.Header2 instead.
-  </obsolete>
-  <owner>iclelland@chromium.org</owner>
-  <owner>feature-control@chromium.org</owner>
-  <summary>
-    Counts the use of a specific permissions policy feature via both
-    &quot;Feature-Policy&quot; and &quot;Permissions-Policy&quot; HTTP response
-    header. Each policy is counted only once per page load.
-  </summary>
-</histogram>
-
 <histogram name="Blink.UseCounter.PermissionsPolicy.Header2"
     enum="FeaturePolicyFeature" expires_after="2022-05-30">
   <owner>iclelland@chromium.org</owner>
@@ -3610,13 +2683,7 @@
     histogram. {BlinkVisibleLoadTimeSuffixes}
   </summary>
   <token key="BlinkVisibleLoadTimeSuffixes"
-      variants="BlinkVisibleLoadTimeSuffixes">
-    <variant name="">
-      <obsolete>
-        Base histogram. Use suffixes of this histogram instead.
-      </obsolete>
-    </variant>
-  </token>
+      variants="BlinkVisibleLoadTimeSuffixes"/>
 </histogram>
 
 <histogram
@@ -3631,13 +2698,7 @@
     histogram. {BlinkVisibleLoadTimeSuffixes}
   </summary>
   <token key="BlinkVisibleLoadTimeSuffixes"
-      variants="BlinkVisibleLoadTimeSuffixes">
-    <variant name="">
-      <obsolete>
-        Base histogram. Use suffixes of this histogram instead.
-      </obsolete>
-    </variant>
-  </token>
+      variants="BlinkVisibleLoadTimeSuffixes"/>
 </histogram>
 
 <histogram base="true" name="Blink.WaitForCommit.UpdateTime"
@@ -3867,326 +2928,6 @@
   </token>
 </histogram>
 
-<histogram name="BlinkGC.CollectionRate" units="%" expires_after="M84">
-  <obsolete>
-    Replaced by V8.GC.Cycle.CollectionRate.Full.Cpp since M94.
-  </obsolete>
-  <owner>haraken@chromium.org</owner>
-  <summary>
-    The percentage of objects that have been collected by a Blink GC. 0 is
-    passed if there were no objects when a Blink GC started.
-  </summary>
-</histogram>
-
-<histogram name="BlinkGC.GCReason" enum="GCReason" expires_after="M84">
-  <obsolete>
-    Replaced by V8.GC.Cycle.Reason.Full since M94.
-  </obsolete>
-  <owner>haraken@chromium.org</owner>
-  <summary>A type of Blink GC.</summary>
-</histogram>
-
-<histogram name="BlinkGC.LowMemoryPageNavigationGC.Reduction" units="MB"
-    expires_after="M77">
-  <obsolete>
-    Dropped due to move to Oilpan library in M94.
-  </obsolete>
-  <owner>keishi@chromium.org</owner>
-  <summary>
-    Amount of memory reduced with the GC triggered on page navigation while the
-    device is in a low memory state. Only implemented on low end Android devices
-    at the moment. Memory usage is approximated by summing the memory usage of
-    the following allocators: BlinkGC, PartitionAlloc, and V8 main thread
-    isolate heap.
-  </summary>
-</histogram>
-
-<histogram name="BlinkGC.MainThreadMarkingThroughput" units="MB/s"
-    expires_after="M95">
-  <obsolete>
-    Dropped due to move to Oilpan library in M94.
-  </obsolete>
-  <owner>mlippautz@chromium.org</owner>
-  <owner>oilpan-reviews@chromium.org</owner>
-  <summary>
-    Marking throughput considering all durations of all marking phases on the
-    main thread and marked bytes within one Blink garbage collection cycle.
-    Reported once per garbage collection cycle at the end. Only reported for
-    platforms supporting high resolution clocks and when more than 1MB of live
-    objects have been found.
-  </summary>
-</histogram>
-
-<histogram name="BlinkGC.ObjectSizeAfterGC" units="KB" expires_after="M95">
-  <obsolete>
-    Replaced by V8.GC.Cycle.Objects.After.Full since M94.
-  </obsolete>
-  <owner>haraken@chromium.org</owner>
-  <owner>mlippautz@chromium.org</owner>
-  <owner>oilpan-reviews@chromium.org</owner>
-  <summary>
-    The size of allocated objects just after Blink GC is triggered. Reported
-    once per garbage collection at the end.
-  </summary>
-</histogram>
-
-<histogram name="BlinkGC.ObjectSizeBeforeGC" units="KB" expires_after="M95">
-  <obsolete>
-    Replaced by V8.GC.Cycle.Objects.Before.Full since M94.
-  </obsolete>
-  <owner>haraken@chromium.org</owner>
-  <owner>mlippautz@chromium.org</owner>
-  <owner>oilpan-reviews@chromium.org</owner>
-  <summary>
-    The size of allocated objects just before Blink GC is triggered. Reported
-    once per garbage collection at the end.
-  </summary>
-</histogram>
-
-<histogram name="BlinkGC.ObjectSizeFreedByHeapCompaction" units="KB"
-    expires_after="M81">
-  <obsolete>
-    Replaced by V8.GC.Cycle.Memory.Freed.Full since M94.
-  </obsolete>
-  <owner>haraken@chromium.org</owner>
-  <summary>
-    The total size of objects freed by BlinkGC's heap compaction. This is
-    recorded once following each compaction.
-  </summary>
-</histogram>
-
-<histogram name="BlinkGC.TimeForAtomicPhase" units="ms" expires_after="M95">
-  <obsolete>
-    Replaced by V8.GC.Cycle.MainThread.Full.Atomic.Cpp since M94.
-  </obsolete>
-  <owner>mlippautz@chromium.org</owner>
-  <owner>oilpan-reviews@chromium.org</owner>
-  <summary>
-    Duration of overall garbage collection time when the garbage collector is
-    invoked for finishing a garbage collection. This includes finishing up any
-    already running garbage collection operation. Reported once per garbage
-    collection at the end.
-  </summary>
-</histogram>
-
-<histogram name="BlinkGC.TimeForAtomicPhaseMarking" units="ms"
-    expires_after="M95">
-  <obsolete>
-    Replaced by V8.GC.Cycle.MainThread.Full.Atomic.Mark.Cpp since M94.
-  </obsolete>
-  <owner>mlippautz@chromium.org</owner>
-  <owner>oilpan-reviews@chromium.org</owner>
-  <summary>
-    Duration of finishing marking the transitive closure of objects during the
-    final Blink garbage collection pause. Reported once per garbage collection
-    at the end.
-  </summary>
-</histogram>
-
-<histogram name="BlinkGC.TimeForCompleteSweep" units="ms" expires_after="M95">
-  <obsolete>
-    Covered by V8.GC.Event.MainThread.Full.Incremental.Sweep since M94.
-  </obsolete>
-  <owner>bikineev@chromium.org</owner>
-  <owner>oilpan-reviews@chromium.org</owner>
-  <summary>
-    Duration of the pause that completes sweeping. Reported once per garbage
-    collection at the end.
-  </summary>
-</histogram>
-
-<histogram name="BlinkGC.TimeForGCCycle" units="ms" expires_after="M95">
-  <obsolete>
-    Replaced by V8.GC.Cycle.MainThread.Full.Cpp since M94.
-  </obsolete>
-  <owner>mlippautz@chromium.org</owner>
-  <owner>oilpan-reviews@chromium.org</owner>
-  <summary>
-    Sum of all durations of individual phases within one Blink garbage
-    collection. Reported once per garbage collection at the end.
-  </summary>
-</histogram>
-
-<histogram name="BlinkGC.TimeForGlobalWeakProcessing" units="ms"
-    expires_after="M95">
-  <obsolete>
-    Replaced by V8.GC.Cycle.MainThread.Full.Atomic.Weak.Cpp since M94.
-  </obsolete>
-  <owner>haraken@chromium.org</owner>
-  <owner>mlippautz@chromium.org</owner>
-  <owner>oilpan-reviews@chromium.org</owner>
-  <summary>
-    Duration of time taken to run global weak processing of Blink GC. Reported
-    once per garbage collection at the end.
-  </summary>
-</histogram>
-
-<histogram name="BlinkGC.TimeForHeapCompaction" units="ms" expires_after="M95">
-  <obsolete>
-    Replaced by V8.GC.Event.MainThread.Full.Compact.Cpp since M94.
-  </obsolete>
-  <owner>haraken@chromium.org</owner>
-  <owner>mlippautz@chromium.org</owner>
-  <owner>oilpan-reviews@chromium.org</owner>
-  <summary>
-    Duration of wall time taken to run BlinkGC's heap compaction. Reported once
-    per garbage collection at the end.
-  </summary>
-</histogram>
-
-<histogram name="BlinkGC.TimeForIncrementalMarking" units="ms"
-    expires_after="2022-02-13">
-  <obsolete>
-    Covered by V8.GC.Cycle.MainThread.Full.Mark.Cpp and
-    V8.GC.Cycle.MainThread.Full.Atomic.Mark.Cpp since M94.
-  </obsolete>
-  <owner>mlippautz@chromium.org</owner>
-  <owner>oilpan-reviews@chromium.org</owner>
-  <summary>
-    Sum of all durations of incremental marking phases within one Blink garbage
-    collection cycle. Reported once per garbage collection at the end.
-  </summary>
-</histogram>
-
-<histogram name="BlinkGC.TimeForInvokingPreFinalizers" units="ms"
-    expires_after="M95">
-  <obsolete>
-    Dropped due to move to Oilpan library in M94.
-  </obsolete>
-  <owner>haraken@chromium.org</owner>
-  <owner>mlippautz@chromium.org</owner>
-  <owner>oilpan-reviews@chromium.org</owner>
-  <summary>
-    Duration of time taken to run ThreadState::InvokePreFinalizers(). Reported
-    once per garbage collection at the end.
-  </summary>
-</histogram>
-
-<histogram name="BlinkGC.TimeForMarkingRoots" units="ms" expires_after="M98">
-  <obsolete>
-    Dropped due to move to Oilpan library in M94.
-  </obsolete>
-  <owner>omerkatz@chromium.org</owner>
-  <owner>oilpan-reviews@chromium.org</owner>
-  <summary>
-    Duration of marking roots within one Blink garbage collection. Reported once
-    per garbage collection at the end.
-  </summary>
-</histogram>
-
-<histogram name="BlinkGC.TimeForMarking{BlinkGCTimingTypes}" units="ms"
-    expires_after="M98">
-  <obsolete>
-    Covered by V8.GC.Cycle.Full.Mark.Cpp and
-    V8.GC.Cycle.MainThread.Full.Mark.Cpp since M94.
-  </obsolete>
-  <owner>omerkatz@chromium.org</owner>
-  <owner>oilpan-reviews@chromium.org</owner>
-  <summary>
-    Accumulated sum of all durations of individual phases contributing to
-    marking (main thread and background helpers) within one Blink garbage
-    collection. Reported once per garbage collection at the end.
-    {BlinkGCTimingTypes}
-  </summary>
-  <token key="BlinkGCTimingTypes">
-    <variant name=""/>
-    <variant name=".Background" summary="Includes only background helpers"/>
-    <variant name=".Foreground" summary="Includes only the main thread"/>
-  </token>
-</histogram>
-
-<histogram name="BlinkGC.TimeForNestedInV8" units="ms" expires_after="M95">
-  <obsolete>
-    Dropped due to move to Oilpan library in M94.
-  </obsolete>
-  <owner>mlippautz@chromium.org</owner>
-  <owner>oilpan-reviews@chromium.org</owner>
-  <summary>
-    Duration of the time of Blink garbage collection spent nested in a V8
-    garbage collection. Reported once per garbage collection at the end.
-  </summary>
-</histogram>
-
-<histogram name="BlinkGC.TimeForSweepingBackground" units="ms"
-    expires_after="M95">
-  <obsolete>
-    Covered by V8.GC.Cycle.Full.Sweep.Cpp and
-    V8.GC.Cycle.MainThread.Full.Sweep.Cpp since M94.
-  </obsolete>
-  <owner>bikineev@chromium.org</owner>
-  <owner>oilpan-reviews@chromium.org</owner>
-  <summary>
-    Sum of all durations of individual phases contributing to sweeping on
-    background helpers within one Blink garbage collection. Reported once per
-    garbage collection at the end.
-  </summary>
-</histogram>
-
-<histogram name="BlinkGC.TimeForSweepingForeground" units="ms"
-    expires_after="2022-01-09">
-  <obsolete>
-    Replaced by V8.GC.Cycle.MainThread.Full.Mark.Cpp since M94.
-  </obsolete>
-  <owner>bikineev@chromium.org</owner>
-  <owner>oilpan-reviews@chromium.org</owner>
-  <summary>
-    Sum of all durations of individual phases contributing to sweeping on the
-    main thread within one Blink garbage collection. Reported once per garbage
-    collection at the end.
-  </summary>
-</histogram>
-
-<histogram name="BlinkGC.TimeForSweepingSum" units="ms" expires_after="M95">
-  <obsolete>
-    Replaced by V8.GC.Cycle.Full.Mark.Cpp since M94.
-  </obsolete>
-  <owner>bikineev@chromium.org</owner>
-  <owner>oilpan-reviews@chromium.org</owner>
-  <summary>
-    Accumulated sum of all durations of individual phases contributing to
-    sweeping (main thread and background helpers) within one Blink garbage
-    collection. Reported once per garbage collection at the end.
-  </summary>
-</histogram>
-
-<histogram name="BlinkGC.TimeForTotalCollectGarbage" units="ms"
-    expires_after="M84">
-  <obsolete>
-    Replaced by V8.GC.Cycle.MainThread.Full since M94.
-  </obsolete>
-  <owner>haraken@chromium.org</owner>
-  <owner>hpayer@chromium.org</owner>
-  <summary>
-    Duration of total Blink garbage collection time when the garbage collector
-    is invoked for finishing a garbage collection. This includes finishing up
-    any already running garbage collection operation as well as potentially
-    performing a follow-up collection synchronously. Recorded each time after
-    invoking the Blink garbage collector.
-  </summary>
-</histogram>
-
-<histogram name="BlinkGC.TotalAllocatedSpace" units="KB" expires_after="M95">
-  <obsolete>
-    Dropped due to move to Oilpan library in M94.
-  </obsolete>
-  <owner>haraken@chromium.org</owner>
-  <owner>oilpan-reviews@chromium.org</owner>
-  <summary>
-    The total size of allocated space in OS when a Blink GC is triggered.
-  </summary>
-</histogram>
-
-<histogram name="BlinkGC.TotalObjectSpace" units="KB" expires_after="M95">
-  <obsolete>
-    Dropped due to move to Oilpan library in M94.
-  </obsolete>
-  <owner>haraken@chromium.org</owner>
-  <owner>oilpan-reviews@chromium.org</owner>
-  <summary>
-    The total size of object space in all threads when a Blink GC is triggered.
-  </summary>
-</histogram>
-
 </histograms>
 
 </histogram-configuration>
diff --git a/tools/metrics/histograms/metadata/content/histograms.xml b/tools/metrics/histograms/metadata/content/histograms.xml
index 3e572954..4ae4e15b 100644
--- a/tools/metrics/histograms/metadata/content/histograms.xml
+++ b/tools/metrics/histograms/metadata/content/histograms.xml
@@ -36,22 +36,6 @@
   </summary>
 </histogram>
 
-<histogram name="ContentCapture.CaptureContentTime" units="microseconds"
-    expires_after="M87">
-  <obsolete>
-    Replaced by ContentCapture.CaptureContentTime2 since M87, the new histogram
-    records the grouping time additionally.
-  </obsolete>
-  <owner>michaelbai@chromium.org</owner>
-  <owner>src/third_party/blink/renderer/core/content_capture/OWNERS</owner>
-  <summary>
-    The time taken to capture the on-screen content.
-
-    Note that this metrics is only recorded on clients on which a
-    high-resolution clock is available.
-  </summary>
-</histogram>
-
 <histogram name="ContentCapture.CaptureContentTime2" units="microseconds"
     expires_after="2022-12-01">
   <owner>michaelbai@chromium.org</owner>
@@ -88,17 +72,6 @@
   </summary>
 </histogram>
 
-<histogram name="ContentCapture.SentContentCount" units="count"
-    expires_after="2021-04-04">
-  <obsolete>
-    Replaced by ContentCapture.SentContentCount2 in M89, which uses standard
-    buckets for 10k counts rather than exact linear buckets.
-  </obsolete>
-  <owner>michaelbai@chromium.org</owner>
-  <owner>src/third_party/blink/renderer/core/content_capture/OWNERS</owner>
-  <summary>The total number of content captures sent for a document.</summary>
-</histogram>
-
 <histogram name="ContentCapture.SentContentCount2" units="count"
     expires_after="2022-12-01">
   <owner>michaelbai@chromium.org</owner>
@@ -278,16 +251,6 @@
   <summary>The default notification setting at profile open.</summary>
 </histogram>
 
-<histogram name="ContentSettings.DefaultPluginsSetting" enum="ContentSetting"
-    expires_after="2021-06-19">
-  <obsolete>
-    Removed as of 09/2020. Flash / Plugins is deprecated.
-  </obsolete>
-  <owner>engedy@chromium.org</owner>
-  <owner>src/components/permissions/PERMISSIONS_OWNERS</owner>
-  <summary>The default plugins setting at profile open.</summary>
-</histogram>
-
 <histogram name="ContentSettings.DefaultPopupsSetting" enum="ContentSetting"
     expires_after="2022-06-19">
   <owner>engedy@chromium.org</owner>
@@ -335,18 +298,6 @@
   </summary>
 </histogram>
 
-<histogram name="ContentSettings.EphemeralFlashPermission"
-    enum="ContentSettings.EphemeralFlashPermission" expires_after="M89">
-  <obsolete>
-    Flash / Plugins is deprecated since M87 and removed in M89.
-  </obsolete>
-  <owner>engedy@chromium.org</owner>
-  <owner>src/components/permissions/PERMISSIONS_OWNERS</owner>
-  <summary>
-    Records the number of times Flash permission is granted for a host.
-  </summary>
-</histogram>
-
 <histogram name="ContentSettings.Exceptions" units="units"
     expires_after="never">
 <!-- expires-never: tracked as an important privacy metric. -->
@@ -474,60 +425,6 @@
   </summary>
 </histogram>
 
-<histogram name="ContentSettings.PermissionRequested_InsecureOrigin"
-    enum="PermissionType" expires_after="M90">
-  <obsolete>
-    Removed as of July 2021. No permissions are supported for insecure origins.
-  </obsolete>
-  <owner>dominickn@chromium.org</owner>
-  <owner>engedy@chromium.org</owner>
-  <owner>hkamila@chromium.org</owner>
-  <summary>
-    Number of times a given permission was requested by an insecure origin and
-    the user has the permission set to prompt (i.e. not blocked or allowed).
-
-    Note this is probably not the metric you want - it does not correspond to
-    the total number of times websites request a permission. Also, because
-    specific permissions have code that can automatically block or grant
-    permissions based on things like incognito, installed extensions etc., this
-    does also not correspond to the number of times users are prompted to allow
-    permissions.
-
-    For a better metric to track how often users are prompted, use
-    ContentSettings.PermissionsActions*.
-
-    See https://crbug.com/638076 for more details.
-  </summary>
-</histogram>
-
-<histogram name="ContentSettings.PermissionRequested_SecureOrigin"
-    enum="PermissionType" expires_after="M90">
-  <obsolete>
-    Removed as of July 2021. No permissions are supported for insecure origins.
-    So we don't need to differentiate between Secure and Insecure origins.
-    ContentSettings.PermissionRequested histogram is enough.
-  </obsolete>
-  <owner>dominickn@chromium.org</owner>
-  <owner>engedy@chromium.org</owner>
-  <owner>hkamila@chromium.org</owner>
-  <summary>
-    Number of times a given permission was requested by a secure origin and the
-    user has the permission set to prompt (i.e. not blocked or allowed).
-
-    Note this is probably not the metric you want - it does not correspond to
-    the total number of times websites request a permission. Also, because
-    specific permissions have code that can automatically block or grant
-    permissions based on things like incognito, installed extensions etc., this
-    does also not correspond to the number of times users are prompted to allow
-    permissions.
-
-    For a better metric to track how often users are prompted, use
-    ContentSettings.PermissionsActions*.
-
-    See https://crbug.com/638076 for more details.
-  </summary>
-</histogram>
-
 <histogram name="ContentSettings.Plugins" enum="ContentSettingPluginsAction"
     expires_after="M77">
   <owner>tommycli@chromium.org</owner>
@@ -633,86 +530,6 @@
   </summary>
 </histogram>
 
-<histogram name="ContentSuggestions.Feed.AppLifecycle.Events"
-    enum="AppLifecycleEvent" expires_after="M89">
-  <obsolete>
-    Removed along with FeedV1 in M89
-  </obsolete>
-  <owner>carlosk@chromium.org</owner>
-  <owner>harringtond@chromium.org</owner>
-  <owner>feed@chromium.org</owner>
-  <summary>
-    Android: count of app lifecycle events reported to the Feed library.
-    Recorded as these events occur; e.g. when Chrome is foregrounded,
-    backgrounded, or the user clears cached browsing data.
-  </summary>
-</histogram>
-
-<histogram name="ContentSuggestions.Feed.AppLifecycle.NumRowsForDeletion"
-    units="count" expires_after="M89">
-  <obsolete>
-    Removed along with FeedV1 in M89
-  </obsolete>
-  <owner>carlosk@chromium.org</owner>
-  <owner>harringtond@chromium.org</owner>
-  <owner>feed@chromium.org</owner>
-  <summary>
-    Android: number of rows present in a history deletion that's causing all
-    current suggestions to be deleted. Each row should correspond to a url that
-    is beign removed from history. Is not emitted when entire history is being
-    cleared.
-  </summary>
-</histogram>
-
-<histogram name="ContentSuggestions.Feed.AutoplayEvent"
-    enum="FeedAutoplayEvent" expires_after="M95">
-  <obsolete>
-    Removed as of 05/2021. Replaced by ContentSuggestions.Feed.VideoPlayEvent.
-  </obsolete>
-  <owner>jianli@chromium.org</owner>
-  <owner>feed@chromium.org</owner>
-  <summary>
-    Android: records events triggered during the video autoplay.
-  </summary>
-</histogram>
-
-<histogram name="ContentSuggestions.Feed.AvailableOffline.Opened"
-    enum="Boolean" expires_after="M89">
-  <obsolete>
-    Removed along with FeedV1 in M89
-  </obsolete>
-  <owner>carlosk@chromium.org</owner>
-  <owner>harringtond@chromium.org</owner>
-  <owner>feed@chromium.org</owner>
-  <summary>
-    When an article on the NTP is opened by the user, records whether it is
-    available offline. When an article is available offline, it will have an
-    offline badge in the UI on the NTP. Opening the article occurs when the user
-    navigates from the NTP to the article itself, which can be either in the
-    current tab or a new tab. Note that this is independent of whether the
-    article is actually opened as an offline page or not.
-  </summary>
-</histogram>
-
-<histogram name="ContentSuggestions.Feed.AvailableOffline.Shown" enum="Boolean"
-    expires_after="M89">
-  <obsolete>
-    Removed along with FeedV1 in M89
-  </obsolete>
-  <owner>carlosk@chromium.org</owner>
-  <owner>harringtond@chromium.org</owner>
-  <owner>feed@chromium.org</owner>
-  <summary>
-    Records whether an article on the NTP that is shown to the user was also
-    available offline. When an article is available offline, it will have an
-    offline badge in the UI. The initial offline status of the article is used
-    for this metric. While it is possible for the offline status to change while
-    the article is on screen, this metric will not be re-emitted. The criteria
-    for an article to be &quot;shown&quot; is for 2/3 of its vertical height to
-    be on screen for any amount of time.
-  </summary>
-</histogram>
-
 <histogram name="ContentSuggestions.Feed.BrokenNTPHierarchy"
     enum="NTPBrokenViewHierarchyRelationship" expires_after="M99">
   <owner>adamta@google.com</owner>
@@ -774,20 +591,6 @@
   <summary>Actions related to the feed controls.</summary>
 </histogram>
 
-<histogram name="ContentSuggestions.Feed.Count" units="entries"
-    expires_after="M89">
-  <obsolete>
-    Removed along with FeedV1 in M89
-  </obsolete>
-  <owner>carlosk@chromium.org</owner>
-  <owner>harringtond@chromium.org</owner>
-  <owner>feed@chromium.org</owner>
-  <summary>
-    Tracks the number of entries for the Feed storage after database perform get
-    all keys request.
-  </summary>
-</histogram>
-
 <histogram name="ContentSuggestions.Feed.DisplayStatusOnOpen"
     enum="ContentSuggestionsDisplayStatus" expires_after="2022-06-26">
   <owner>carlosk@chromium.org</owner>
@@ -801,52 +604,6 @@
   </summary>
 </histogram>
 
-<histogram name="ContentSuggestions.Feed.FetchPendingSpinner.Shown"
-    enum="FeedSpinnerType" expires_after="M89">
-  <obsolete>
-    Removed along with FeedV1 in M89
-  </obsolete>
-  <owner>carlosk@chromium.org</owner>
-  <owner>harringtond@chromium.org</owner>
-  <owner>feed@chromium.org</owner>
-  <summary>
-    Android: The type of spinner when the content suggestion shows the loading
-    spinner. This is tracked based on if the spinner is shown, not how long it
-    is visible.
-  </summary>
-</histogram>
-
-<histogram name="ContentSuggestions.Feed.FetchPendingSpinner.VisibleDuration"
-    units="ms" expires_after="M89">
-  <obsolete>
-    Removed along with FeedV1 in M89
-  </obsolete>
-  <owner>carlosk@chromium.org</owner>
-  <owner>harringtond@chromium.org</owner>
-  <owner>feed@chromium.org</owner>
-  <summary>
-    Android: How long the content suggestion loading spinner is shown when a
-    loading spinner finishes showing. This is tracked based on when the spinner
-    is enabled in the UI, not how long it is actually visible on screen.
-  </summary>
-</histogram>
-
-<histogram
-    name="ContentSuggestions.Feed.FetchPendingSpinner.VisibleDurationWithoutCompleting"
-    units="ms" expires_after="M89">
-  <obsolete>
-    Removed along with FeedV1 in M89
-  </obsolete>
-  <owner>carlosk@chromium.org</owner>
-  <owner>harringtond@chromium.org</owner>
-  <owner>feed@chromium.org</owner>
-  <summary>
-    Android: How long the content suggestion loading spinner is shown when a
-    spinner is destroyed without completing. This is tracked based on when the
-    spinner is enabled in the UI, not how long it is actually visible on screen.
-  </summary>
-</histogram>
-
 <histogram name="ContentSuggestions.Feed.ImageFetchStatus"
     enum="CombinedHttpResponseAndNetErrorCode" expires_after="2022-03-01">
   <owner>harringtond@chromium.org</owner>
@@ -858,58 +615,6 @@
   </summary>
 </histogram>
 
-<histogram name="ContentSuggestions.Feed.InterestHeader.NotInterestedInSource"
-    units="index" expires_after="M89">
-  <obsolete>
-    Removed along with FeedV1 in M89
-  </obsolete>
-  <owner>carlosk@chromium.org</owner>
-  <owner>harringtond@chromium.org</owner>
-  <owner>feed@chromium.org</owner>
-  <summary>
-    The position of an interest header on the NTP that the user has indicated
-    they aren't interested in the story's source.
-  </summary>
-</histogram>
-
-<histogram name="ContentSuggestions.Feed.InterestHeader.NotInterestedInTopic"
-    units="index" expires_after="M89">
-  <obsolete>
-    Removed along with FeedV1 in M89
-  </obsolete>
-  <owner>carlosk@chromium.org</owner>
-  <owner>harringtond@chromium.org</owner>
-  <owner>feed@chromium.org</owner>
-  <summary>
-    The position of an interest header on the NTP that the user has indicated
-    they aren't interested in the story's topic.
-  </summary>
-</histogram>
-
-<histogram name="ContentSuggestions.Feed.InternalError"
-    enum="FeedInternalError" expires_after="M89">
-  <obsolete>
-    Removed along with FeedV1 in M89
-  </obsolete>
-  <owner>carlosk@chromium.org</owner>
-  <owner>harringtond@chromium.org</owner>
-  <owner>feed@chromium.org</owner>
-  <summary>The Feed library encountered an error at any time.</summary>
-</histogram>
-
-<histogram name="ContentSuggestions.Feed.LoadKeysTime" units="ms"
-    expires_after="M89">
-  <obsolete>
-    Removed along with FeedV1 in M89
-  </obsolete>
-  <owner>carlosk@chromium.org</owner>
-  <owner>harringtond@chromium.org</owner>
-  <owner>feed@chromium.org</owner>
-  <summary>
-    The time it takes for the Feed to load all keys from the storage.
-  </summary>
-</histogram>
-
 <histogram base="true" name="ContentSuggestions.Feed.LoadStepLatency"
     units="ms" expires_after="2022-07-03">
   <owner>harringtond@chromium.org</owner>
@@ -935,19 +640,6 @@
   </summary>
 </histogram>
 
-<histogram name="ContentSuggestions.Feed.LoadTime" units="ms"
-    expires_after="M89">
-  <obsolete>
-    Removed along with FeedV1 in M89
-  </obsolete>
-  <owner>carlosk@chromium.org</owner>
-  <owner>harringtond@chromium.org</owner>
-  <owner>feed@chromium.org</owner>
-  <summary>
-    The time it takes for the Feed to load entries from the storage.
-  </summary>
-</histogram>
-
 <histogram name="ContentSuggestions.Feed.Network.CompressedResponseSizeKB"
     units="KB" expires_after="2022-07-03">
   <owner>carlosk@chromium.org</owner>
@@ -1032,19 +724,6 @@
   </summary>
 </histogram>
 
-<histogram name="ContentSuggestions.Feed.Network.TokenDuration" units="ms"
-    expires_after="M89">
-  <obsolete>
-    Removed along with FeedV1 in M89
-  </obsolete>
-  <owner>carlosk@chromium.org</owner>
-  <owner>harringtond@chromium.org</owner>
-  <owner>feed@chromium.org</owner>
-  <summary>
-    The amount of time it takes to get an access token for signed in users.
-  </summary>
-</histogram>
-
 <histogram name="ContentSuggestions.Feed.Network.TokenFetchStatus"
     enum="GoogleServiceAuthError" expires_after="2022-07-11">
   <owner>carlosk@chromium.org</owner>
@@ -1478,75 +1157,6 @@
   </summary>
 </histogram>
 
-<histogram name="ContentSuggestions.Feed.VisualElement.Clicked" units="index"
-    expires_after="M89">
-  <obsolete>
-    Removed along with FeedV1 in M89
-  </obsolete>
-  <owner>carlosk@chromium.org</owner>
-  <owner>harringtond@chromium.org</owner>
-  <owner>feed@chromium.org</owner>
-  <summary>
-    Android: The position of a clicked element in the stream not accounting for
-    header views. The top being 0. Position does not change after initial
-    layout. Specifically the position does not update if dismisses/removes are
-    performed. This is similar to NewTabPage.ContentSuggestions.Opened, but
-    records the specific elementType that was clicked.
-  </summary>
-</histogram>
-
-<histogram
-    name="ContentSuggestions.Feed.VisualElement.Clicked.TimeSinceElementFetched"
-    units="ms" expires_after="M89">
-  <obsolete>
-    Removed along with FeedV1 in M89
-  </obsolete>
-  <owner>carlosk@chromium.org</owner>
-  <owner>harringtond@chromium.org</owner>
-  <owner>feed@chromium.org</owner>
-  <summary>
-    Android: The time since the content was made available on the device. This
-    could be the time for when this content was retrieved from the server or the
-    time the data was pushed to the device. Recorded when the user clicks the
-    element.
-  </summary>
-</histogram>
-
-<histogram name="ContentSuggestions.Feed.VisualElement.Viewed" units="index"
-    expires_after="M89">
-  <obsolete>
-    Removed along with FeedV1 in M89
-  </obsolete>
-  <owner>carlosk@chromium.org</owner>
-  <owner>harringtond@chromium.org</owner>
-  <owner>feed@chromium.org</owner>
-  <summary>
-    Android: The position of a viewed element in the stream not accounting for
-    header views. The top being 0. Position does not change after initial
-    layout. Specifically the position does not update if dismisses/removes are
-    performed. This is similar to NewTabPage.ContentSuggestions.Shown, but
-    records the view after it has been on the screen for 500 milliseconds of
-    time instead of on predraw.
-  </summary>
-</histogram>
-
-<histogram
-    name="ContentSuggestions.Feed.VisualElement.Viewed.TimeSinceElementFetched"
-    units="ms" expires_after="M89">
-  <obsolete>
-    Removed along with FeedV1 in M89
-  </obsolete>
-  <owner>carlosk@chromium.org</owner>
-  <owner>harringtond@chromium.org</owner>
-  <owner>feed@chromium.org</owner>
-  <summary>
-    Android: The time since the content was made available on the device. This
-    could be the time for when this content was retrieved from the server or the
-    time the data was pushed to the device. Recorded when the element has been
-    on the screen for 500 milliseconds.
-  </summary>
-</histogram>
-
 <histogram name="ContentSuggestions.Feed.WebFeed.FollowByIdResult"
     enum="WebFeedSubscriptionRequestStatus" expires_after="2022-07-01">
   <owner>harringtond@chromium.org</owner>
@@ -1720,48 +1330,6 @@
   </summary>
 </histogram>
 
-<histogram
-    name="ContentSuggestions.Feed.ZeroStateRefreshCompleted.ContentCount"
-    units="count" expires_after="M89">
-  <obsolete>
-    Removed along with FeedV1 in M89
-  </obsolete>
-  <owner>carlosk@chromium.org</owner>
-  <owner>harringtond@chromium.org</owner>
-  <owner>feed@chromium.org</owner>
-  <summary>
-    How many top level features were in an initial article fetch, typically
-    clusters. Recorded when an article fetch completes and were previously in
-    zero state.
-  </summary>
-</histogram>
-
-<histogram name="ContentSuggestions.Feed.ZeroStateRefreshCompleted.TokenCount"
-    units="count" expires_after="M89">
-  <obsolete>
-    Removed along with FeedV1 in M89
-  </obsolete>
-  <owner>carlosk@chromium.org</owner>
-  <owner>harringtond@chromium.org</owner>
-  <owner>feed@chromium.org</owner>
-  <summary>
-    How many tokens were in an initial article fetch, typically ways to fetch
-    more articles. Recorded when an article fetch completes and were previously
-    in zero state.
-  </summary>
-</histogram>
-
-<histogram name="ContentSuggestions.Feed.ZeroStateShown.Reason"
-    enum="FeedZeroStateShowReason" expires_after="M89">
-  <obsolete>
-    Removed along with FeedV1 in M89
-  </obsolete>
-  <owner>carlosk@chromium.org</owner>
-  <owner>harringtond@chromium.org</owner>
-  <owner>feed@chromium.org</owner>
-  <summary>The reason the zero state (no articles) is shown to a user.</summary>
-</histogram>
-
 <histogram name="ContentSuggestions.Feed.{PlayType}Video.InitializationError"
     enum="FeedVideoInitializationError" expires_after="M96">
   <owner>jianli@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/custom_tabs/histograms.xml b/tools/metrics/histograms/metadata/custom_tabs/histograms.xml
index ca3417d..24e6930 100644
--- a/tools/metrics/histograms/metadata/custom_tabs/histograms.xml
+++ b/tools/metrics/histograms/metadata/custom_tabs/histograms.xml
@@ -228,31 +228,6 @@
   </summary>
 </histogram>
 
-<histogram base="true" name="CustomTabs.ResourcePrefetch.Duration" units="ms"
-    expires_after="M73">
-  <obsolete>
-    No longer recorded since M73.
-  </obsolete>
-  <owner>alexilin@chromium.org</owner>
-  <summary>
-    Time between the start of a detached resource request for resource prefetch
-    and its completion (failure or success). Android only.
-  </summary>
-</histogram>
-
-<histogram name="CustomTabs.ResourcePrefetch.FinalStatus" enum="NetErrorCodes"
-    expires_after="M73">
-  <obsolete>
-    No longer recorded since M73.
-  </obsolete>
-  <owner>alexilin@chromium.org</owner>
-  <summary>
-    Reports the final status of the detached request for resource prefetch,
-    including success. Recorded once per resource prefetch request. Android
-    only.
-  </summary>
-</histogram>
-
 <histogram name="CustomTabs.ShareOptionLocation" enum="ShareOptionLocation"
     expires_after="M105">
   <owner>sophey@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/direct/histograms.xml b/tools/metrics/histograms/metadata/direct/histograms.xml
index 625c947..5d8da66c 100644
--- a/tools/metrics/histograms/metadata/direct/histograms.xml
+++ b/tools/metrics/histograms/metadata/direct/histograms.xml
@@ -22,6 +22,91 @@
 
 <histograms>
 
+<histogram
+    name="DirectWrite.Fonts.Content.FallbackFamilyAndStyleForCodepointTime"
+    units="microseconds" expires_after="2022-06-15">
+  <owner>sky@chromium.org</owner>
+  <owner>jam@chromium.org</owner>
+  <summary>
+    The time in microseconds it takes to execute
+    DWriteFontProxyImpl::FallbackFamilyAndStyleForCodepoint.
+
+    This metric is reported for all users.
+  </summary>
+</histogram>
+
+<histogram name="DirectWrite.Fonts.Content.FindFamilyTime" units="microseconds"
+    expires_after="2022-06-15">
+  <owner>sky@chromium.org</owner>
+  <owner>jam@chromium.org</owner>
+  <summary>
+    The time in microseconds it takes to execute
+    DWriteFontProxyImpl::FindFamily.
+
+    This metric is reported for all users.
+  </summary>
+</histogram>
+
+<histogram name="DirectWrite.Fonts.Content.GetFamilyCountTime"
+    units="microseconds" expires_after="2022-06-15">
+  <owner>sky@chromium.org</owner>
+  <owner>jam@chromium.org</owner>
+  <summary>
+    The time in microseconds it takes to execute
+    DWriteFontProxyImpl::GetFamilyCount.
+
+    This metric is reported for all users.
+  </summary>
+</histogram>
+
+<histogram name="DirectWrite.Fonts.Content.GetFamilyNamesTime"
+    units="microseconds" expires_after="2022-06-15">
+  <owner>sky@chromium.org</owner>
+  <owner>jam@chromium.org</owner>
+  <summary>
+    The time in microseconds it takes to execute
+    DWriteFontProxyImpl::GetFamilyNames.
+
+    This metric is reported for all users.
+  </summary>
+</histogram>
+
+<histogram name="DirectWrite.Fonts.Content.GetFontFilesTime"
+    units="microseconds" expires_after="2022-06-15">
+  <owner>sky@chromium.org</owner>
+  <owner>jam@chromium.org</owner>
+  <summary>
+    The time in microseconds it takes to execute
+    DWriteFontProxyImpl::GetFontFiles.
+
+    This metric is reported for all users.
+  </summary>
+</histogram>
+
+<histogram name="DirectWrite.Fonts.Content.InitializeTime" units="microseconds"
+    expires_after="2022-06-15">
+  <owner>sky@chromium.org</owner>
+  <owner>jam@chromium.org</owner>
+  <summary>
+    The time in microseconds it takes to execute
+    DWriteFontProxyImpl::Initialize.
+
+    This metric is reported for all users.
+  </summary>
+</histogram>
+
+<histogram name="DirectWrite.Fonts.Content.MapCharactersTime"
+    units="microseconds" expires_after="2022-06-15">
+  <owner>sky@chromium.org</owner>
+  <owner>jam@chromium.org</owner>
+  <summary>
+    The time in microseconds it takes to execute
+    DWriteFontProxyImpl::MapCharacters.
+
+    This metric is reported for all users.
+  </summary>
+</histogram>
+
 <histogram name="DirectWrite.Fonts.Gfx.InitializeLoopCount" units="units"
     expires_after="2022-10-15">
   <owner>drott@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/histogram_suffixes_list.xml b/tools/metrics/histograms/metadata/histogram_suffixes_list.xml
index 667160ec..8d33d03 100644
--- a/tools/metrics/histograms/metadata/histogram_suffixes_list.xml
+++ b/tools/metrics/histograms/metadata/histogram_suffixes_list.xml
@@ -1299,54 +1299,6 @@
   <affected-histogram name="Media.Audio.Bitstream"/>
 </histogram_suffixes>
 
-<histogram_suffixes name="BlinkCanvasDrawImageType" separator=".">
-  <suffix base="true" name="Canvas" label=""/>
-  <suffix base="true" name="CssImage" label=""/>
-  <suffix base="true" name="ImageBitmap" label=""/>
-  <suffix base="true" name="ImageElement" label=""/>
-  <suffix base="true" name="OffscreenCanvas" label=""/>
-  <suffix base="true" name="Others" label="">
-    <obsolete>
-      Deprecated 10/2018 with the addition of CssImage and Unknown.
-    </obsolete>
-  </suffix>
-  <suffix base="true" name="SVG" label=""/>
-  <suffix base="true" name="Unknown" label=""/>
-  <suffix base="true" name="Video" label=""/>
-  <suffix base="true" name="VideoFrame" label=""/>
-  <affected-histogram name="Blink.Canvas.DrawImage.Duration"/>
-  <affected-histogram name="Blink.Canvas.DrawImage.Duration2"/>
-</histogram_suffixes>
-
-<histogram_suffixes name="BlinkCanvasDurationBySource" separator=".">
-  <suffix name="CPU" label=""/>
-  <suffix name="DisplayList" label="">
-    <obsolete>
-      Deprecated 11/2017 with removal of Display List Canvas 2D mode.
-    </obsolete>
-  </suffix>
-  <suffix name="GPU" label=""/>
-  <affected-histogram name="Blink.Canvas.DrawImage.Duration.Canvas"/>
-  <affected-histogram name="Blink.Canvas.DrawImage.Duration.CssImage"/>
-  <affected-histogram name="Blink.Canvas.DrawImage.Duration.ImageBitmap"/>
-  <affected-histogram name="Blink.Canvas.DrawImage.Duration.ImageElement"/>
-  <affected-histogram name="Blink.Canvas.DrawImage.Duration.OffscreenCanvas"/>
-  <affected-histogram name="Blink.Canvas.DrawImage.Duration.SVG"/>
-  <affected-histogram name="Blink.Canvas.DrawImage.Duration.Unknown"/>
-  <affected-histogram name="Blink.Canvas.DrawImage.Duration.Video"/>
-  <affected-histogram name="Blink.Canvas.DrawImage.Duration2.Canvas"/>
-  <affected-histogram name="Blink.Canvas.DrawImage.Duration2.CssImage"/>
-  <affected-histogram name="Blink.Canvas.DrawImage.Duration2.ImageBitmap"/>
-  <affected-histogram name="Blink.Canvas.DrawImage.Duration2.ImageElement"/>
-  <affected-histogram name="Blink.Canvas.DrawImage.Duration2.OffscreenCanvas"/>
-  <affected-histogram name="Blink.Canvas.DrawImage.Duration2.SVG"/>
-  <affected-histogram name="Blink.Canvas.DrawImage.Duration2.Unknown"/>
-  <affected-histogram name="Blink.Canvas.DrawImage.Duration2.Video"/>
-  <affected-histogram name="Blink.Canvas.DrawImage.Duration2.VideoFrame"/>
-  <affected-histogram name="Blink.Canvas.GetImageData"/>
-  <affected-histogram name="Blink.Canvas.PutImageData"/>
-</histogram_suffixes>
-
 <histogram_suffixes name="BlinkCanvasOffscreenCommitType" separator=".">
   <suffix name="GPUCanvasGPUCompositingMain" label=""/>
   <suffix name="GPUCanvasGPUCompositingWorker" label=""/>
@@ -1359,13 +1311,6 @@
   <affected-histogram name="Blink.Canvas.OffscreenCommitTimer"/>
 </histogram_suffixes>
 
-<histogram_suffixes name="BlinkCanvasToBlobIdleEncodAndDelayType" separator=".">
-  <suffix name="JPEG" label=""/>
-  <suffix name="PNG" label=""/>
-  <affected-histogram name="Blink.Canvas.ToBlob.CompleteEncodingDelay"/>
-  <affected-histogram name="Blink.Canvas.ToBlob.InitiateEncodingDelay"/>
-</histogram_suffixes>
-
 <histogram_suffixes name="BlinkGCReason" separator="_">
   <suffix name="ConservativeGC" label="Conservative GC"/>
   <suffix name="ForcedGC" label="Forced GC"/>
@@ -1380,32 +1325,14 @@
   <suffix name="ThreadTerminationGC" label="Thread termination GC"/>
   <suffix name="UnifiedHeapGC" label="Unified heap GC"/>
   <affected-histogram name="BlinkGC.AtomicPhaseMarking"/>
-  <affected-histogram name="BlinkGC.CollectionRate"/>
-  <affected-histogram name="BlinkGC.TimeForTotalCollectGarbage"/>
 </histogram_suffixes>
 
 <histogram_suffixes name="BlinkMainFrameUpdateTimeSuffixes" separator=".">
   <suffix name="1msTo5ms" label="Ratio when main frame between 1ms and 5ms."/>
   <suffix name="LessThan1ms" label="Ratio when main frame shorter than 1ms."/>
   <suffix name="MoreThan5ms" label="Ratio when main frame longer than 5ms."/>
-  <affected-histogram name="Blink.MainFrame.AnimateRatio"/>
-  <affected-histogram name="Blink.MainFrame.CompositingCommitRatio"/>
-  <affected-histogram name="Blink.MainFrame.CompositingInputsRatio"/>
-  <affected-histogram name="Blink.MainFrame.CompositingRatio"/>
-  <affected-histogram name="Blink.MainFrame.ForcedStyleAndLayoutRatio"/>
-  <affected-histogram name="Blink.MainFrame.HandleInputEventsRatio"/>
-  <affected-histogram name="Blink.MainFrame.HitTestDocumentUpdateRatio"/>
-  <affected-histogram name="Blink.MainFrame.ImplCompositorCommitRatio"/>
-  <affected-histogram name="Blink.MainFrame.IntersectionObservationRatio"/>
-  <affected-histogram name="Blink.MainFrame.LayoutRatio"/>
-  <affected-histogram name="Blink.MainFrame.PaintRatio"/>
-  <affected-histogram name="Blink.MainFrame.PrePaintRatio"/>
   <affected-histogram name="Blink.MainFrame.ProxyCommitRatio"/>
-  <affected-histogram name="Blink.MainFrame.ScrollingCoordinatorRatio"/>
   <affected-histogram name="Blink.MainFrame.StyleAndLayoutRatio"/>
-  <affected-histogram name="Blink.MainFrame.StyleRatio"/>
-  <affected-histogram name="Blink.MainFrame.UpdateLayersRatio"/>
-  <affected-histogram name="Blink.MainFrame.WaitForCommitRatio"/>
 </histogram_suffixes>
 
 <histogram_suffixes name="BlinkUpdateTimeAggregatedSuffixes" separator=".">
@@ -1434,7 +1361,6 @@
   <affected-histogram name="Blink.PrePaint.UpdateTime"/>
   <affected-histogram name="Blink.ProxyCommit.UpdateTime"/>
   <affected-histogram name="Blink.ScrollDocumentUpdate.UpdateTime"/>
-  <affected-histogram name="Blink.ScrollingCoordinator.UpdateTime"/>
   <affected-histogram name="Blink.ServiceDocumentUpdate.UpdateTime"/>
   <affected-histogram name="Blink.Style.UpdateTime"/>
   <affected-histogram name="Blink.StyleAndLayout.UpdateTime"/>
@@ -1468,7 +1394,6 @@
   <affected-histogram name="Blink.PrePaint.UpdateTime"/>
   <affected-histogram name="Blink.ProxyCommit.UpdateTime"/>
   <affected-histogram name="Blink.ScrollDocumentUpdate.UpdateTime"/>
-  <affected-histogram name="Blink.ScrollingCoordinator.UpdateTime"/>
   <affected-histogram name="Blink.ServiceDocumentUpdate.UpdateTime"/>
   <affected-histogram name="Blink.Style.UpdateTime"/>
   <affected-histogram name="Blink.StyleAndLayout.UpdateTime"/>
@@ -1502,7 +1427,6 @@
   <affected-histogram name="Blink.PrePaint.UpdateTime"/>
   <affected-histogram name="Blink.ProxyCommit.UpdateTime"/>
   <affected-histogram name="Blink.ScrollDocumentUpdate.UpdateTime"/>
-  <affected-histogram name="Blink.ScrollingCoordinator.UpdateTime"/>
   <affected-histogram name="Blink.ServiceDocumentUpdate.UpdateTime"/>
   <affected-histogram name="Blink.Style.UpdateTime"/>
   <affected-histogram name="Blink.StyleAndLayout.UpdateTime"/>
@@ -2105,6 +2029,7 @@
   <suffix name="OriginTrials" label=""/>
   <suffix name="TrustTokenKeyCommitments" label=""/>
   <suffix name="WebViewAppsPackageNamesAllowlist" label=""/>
+  <suffix name="WebViewEmptyComponent" label=""/>
   <affected-histogram
       name="ComponentUpdater.AndroidComponentLoader.LoadStatus"/>
 </histogram_suffixes>
@@ -2679,10 +2604,6 @@
 <histogram_suffixes name="ContentSuggestionUndoableActions" separator=".">
   <suffix name="Commit" label="Action was committed"/>
   <suffix name="Undo" label="Action was undone"/>
-  <affected-histogram
-      name="ContentSuggestions.Feed.InterestHeader.NotInterestedInSource"/>
-  <affected-histogram
-      name="ContentSuggestions.Feed.InterestHeader.NotInterestedInTopic"/>
   <affected-histogram name="NewTabPage.ContentSuggestions.DismissedUnvisited"/>
   <affected-histogram name="NewTabPage.ContentSuggestions.DismissedVisited"/>
 </histogram_suffixes>
@@ -5015,12 +4936,6 @@
   <suffix name="InterestHeader" label="Interest header"/>
   <suffix name="Tooltip" label="Tooltip"/>
   <suffix name="UnknownElementType" label="Unknown element type"/>
-  <affected-histogram name="ContentSuggestions.Feed.VisualElement.Clicked"/>
-  <affected-histogram
-      name="ContentSuggestions.Feed.VisualElement.Clicked.TimeSinceElementFetched"/>
-  <affected-histogram name="ContentSuggestions.Feed.VisualElement.Viewed"/>
-  <affected-histogram
-      name="ContentSuggestions.Feed.VisualElement.Viewed.TimeSinceElementFetched"/>
 </histogram_suffixes>
 
 <histogram_suffixes name="FeedIsSynthetic" separator=".">
@@ -5102,19 +5017,12 @@
   <suffix name="MoreButton" label="More button"/>
   <suffix name="SyntheticToken" label="Synthetic token"/>
   <suffix name="ZeroStateRefresh" label="Zero state refresh"/>
-  <affected-histogram
-      name="ContentSuggestions.Feed.FetchPendingSpinner.VisibleDuration"/>
-  <affected-histogram
-      name="ContentSuggestions.Feed.FetchPendingSpinner.VisibleDurationWithoutCompleting"/>
 </histogram_suffixes>
 
 <histogram_suffixes name="FeedStorageType" separator="." ordering="prefix,2">
   <suffix name="ContentStorage" label="Database for content storage."/>
   <suffix name="JournalStorage" label="Database for journal storage."/>
   <affected-histogram name="ContentSuggestions.Feed.CommitMutationCount"/>
-  <affected-histogram name="ContentSuggestions.Feed.Count"/>
-  <affected-histogram name="ContentSuggestions.Feed.LoadKeysTime"/>
-  <affected-histogram name="ContentSuggestions.Feed.LoadTime"/>
   <affected-histogram name="ContentSuggestions.Feed.OperationCommitTime"/>
 </histogram_suffixes>
 
@@ -5204,14 +5112,6 @@
   <affected-histogram name="FileBrowser.CrostiniSharedPaths.Depth"/>
 </histogram_suffixes>
 
-<histogram_suffixes name="FirstDownload" separator=".">
-  <obsolete>
-    Removed 2020-10.
-  </obsolete>
-  <suffix name="FirstDownload" label="First download after startup."/>
-  <affected-histogram name="MobileDownload.Background"/>
-</histogram_suffixes>
-
 <histogram_suffixes name="FirstPacketSplit" separator="_">
   <suffix name="first_packet_intact"
       label="with GET/POST headers often using only 1 packet"/>
@@ -9188,7 +9088,6 @@
   <suffix name="SuccessWithSuggestions"
       label="Successfully fetched search suggestion data which contained
              suggestions"/>
-  <affected-histogram name="NewTabPage.SearchSuggestions.RequestLatencyV2"/>
 </histogram_suffixes>
 
 <histogram_suffixes name="NewTabPageIconTypes" separator=".">
@@ -9298,9 +9197,7 @@
     </obsolete>
   </suffix>
   <affected-histogram name="NewTabPage.MostVisited"/>
-  <affected-histogram name="NewTabPage.MostVisitedAge"/>
   <affected-histogram name="NewTabPage.SuggestionsImpression"/>
-  <affected-histogram name="NewTabPage.SuggestionsImpressionAge"/>
   <affected-histogram name="NewTabPage.TileTitle"/>
   <affected-histogram name="NewTabPage.TileTitleClicked"/>
   <affected-histogram name="NewTabPage.TileType"/>
@@ -9685,7 +9582,6 @@
   <affected-histogram name="OfflinePages.Background.FinalSavePageResult"/>
   <affected-histogram name="OfflinePages.Background.LoadingErrorStatusCode"/>
   <affected-histogram name="OfflinePages.Background.OfflinerRequestStatus"/>
-  <affected-histogram name="OfflinePages.Background.OffliningPreviewStatus"/>
   <affected-histogram name="OfflinePages.Background.SavePageFromCCT"/>
   <affected-histogram name="OfflinePages.Background.TimeToCanceled"/>
   <affected-histogram name="OfflinePages.Background.TimeToSaved"/>
@@ -13668,7 +13564,6 @@
   <suffix name="XSLStyleSheet" label=""/>
   <affected-histogram name="Blink.MemoryCache.RevalidationPolicy"/>
   <affected-histogram name="Blink.MemoryCache.RevalidationPolicy.Dead"/>
-  <affected-histogram name="Blink.MemoryCache.RevalidationPolicy.PerDocument"/>
   <affected-histogram
       name="Blink.MemoryCache.RevalidationPolicy.PerTopFrameSite"/>
   <affected-histogram name="Blink.MemoryCache.RevalidationPolicy.Preload"/>
diff --git a/tools/metrics/histograms/metadata/mobile/histograms.xml b/tools/metrics/histograms/metadata/mobile/histograms.xml
index e166664..32fb015 100644
--- a/tools/metrics/histograms/metadata/mobile/histograms.xml
+++ b/tools/metrics/histograms/metadata/mobile/histograms.xml
@@ -51,22 +51,6 @@
   </summary>
 </histogram>
 
-<histogram name="Mobile.AppMenu.SimilarSelection"
-    enum="AppMenuSimilarSelectionType" expires_after="M95">
-  <obsolete>
-    Deprecated as of 03/2021.
-  </obsolete>
-  <owner>gangwu@chromium.org</owner>
-  <owner>twellington@chromium.org</owner>
-  <summary>
-    Android: This metrics is recorded when users made similar selections on app
-    menu within a period of time. ex. a user selected downloading the current
-    page. After 5 seconds, the user opened the app menu again and selected
-    download manager. In this case, we would consider that Download and Download
-    Manager are similar selections.
-  </summary>
-</histogram>
-
 <histogram name="Mobile.AppMenu.TimeToTakeAction.Abandoned" units="ms"
     expires_after="2022-06-26">
   <owner>gangwu@chromium.org</owner>
@@ -521,20 +505,6 @@
   </summary>
 </histogram>
 
-<histogram name="Mobile.SystemNotification.NotifyFailure"
-    enum="SystemNotificationType" expires_after="2021-10-31">
-  <obsolete>
-    Deprecated 10/2021
-  </obsolete>
-  <owner>knollr@chromium.org</owner>
-  <owner>xingliu@chromium.org</owner>
-  <summary>
-    Android: Represents the number of system notifications failed to be notified
-    by Android NotificationManager API. Recorded when the notification is shown
-    on Android.
-  </summary>
-</histogram>
-
 <histogram name="Mobile.SystemNotification.Shown" enum="SystemNotificationType"
     expires_after="never">
 <!-- expires-never: Core Android notification metrics. Used by multiple teams. -->
@@ -557,21 +527,6 @@
   </summary>
 </histogram>
 
-<histogram name="MobileDownload.Background"
-    enum="MobileDownloadBackgroundDownloadEvent" expires_after="2020-10-11">
-  <obsolete>
-    Removed 2020-10.
-  </obsolete>
-  <owner>hnakashima@chromium.org</owner>
-  <owner>hanxi@chromium.org</owner>
-  <owner>yfriedman@chromium.org</owner>
-  <summary>
-    Android: Records events for downloads started from the background, that is,
-    while the browser process is not running. Used to measure completion rate of
-    these downloads.
-  </summary>
-</histogram>
-
 <histogram name="MobileDownload.Background.TargetDeterminationResult"
     enum="MobileDownloadBackgroundTargetDeterminationResult"
     expires_after="2020-12-13">
@@ -723,26 +678,6 @@
 </histogram>
 
 <histogram
-    name="MobileFre.CctTos.EnterprisePolicyCheckSpeed{MobileFreInflationSpeedComparison}"
-    units="ms" expires_after="2022-05-01">
-  <obsolete>
-    Deprecated and replaced with MobileFre.CctTos.EnterprisePolicyCheckSpeed2.*.
-    as of 11/2020. The starting time of this metrics is changed to when the
-    fragment attached to the FirstRunActivity.
-  </obsolete>
-  <owner>skym@chromium.org</owner>
-  <owner>wenyufu@chromium.org</owner>
-  <summary>
-    Android: Records the time it takes from object initialization to the
-    enterprise policy check completing. This check is often skipped when its
-    result becomes irrelevant. Histogram has suffixes depending if the timing
-    was faster or slower than inflation.
-  </summary>
-  <token key="MobileFreInflationSpeedComparison"
-      variants="MobileFreInflationSpeedComparison"/>
-</histogram>
-
-<histogram
     name="MobileFre.CctTos.IsDeviceOwnedCheckSpeed2{MobileFreInflationSpeedComparison}"
     units="ms" expires_after="2022-05-01">
   <owner>skym@chromium.org</owner>
@@ -756,25 +691,6 @@
       variants="MobileFreInflationSpeedComparison"/>
 </histogram>
 
-<histogram
-    name="MobileFre.CctTos.IsDeviceOwnedCheckSpeed{MobileFreInflationSpeedComparison}"
-    units="ms" expires_after="2022-05-01">
-  <obsolete>
-    Deprecated and replaced with MobileFre.CctTos.IsDeviceOwnedCheckSpeed2.*. as
-    of 11/2020. The starting time of this metrics is changed to when the
-    fragment attached to the FirstRunActivity.
-  </obsolete>
-  <owner>skym@chromium.org</owner>
-  <owner>wenyufu@chromium.org</owner>
-  <summary>
-    Android: Records the time it takes from object initialization to the device
-    ownership check completing. Histogram has suffixes depending if the timing
-    was faster or slower than inflation.
-  </summary>
-  <token key="MobileFreInflationSpeedComparison"
-      variants="MobileFreInflationSpeedComparison"/>
-</histogram>
-
 <histogram name="MobileFre.CctTos.LoadingDuration" units="ms"
     expires_after="2022-05-01">
   <owner>skym@chromium.org</owner>
@@ -866,29 +782,6 @@
   </summary>
 </histogram>
 
-<histogram name="MobileFre.PolicyServiceInitDelayAfterNative{PolicyPresence}"
-    units="ms" expires_after="2022-05-01">
-  <obsolete>
-    Replaced by MobileFre.PolicyServiceInitDelayAfterNative.*2 due to bug fix as
-    of 05/2021. See https://crbug.com/1208942.
-  </obsolete>
-  <owner>skym@chromium.org</owner>
-  <owner>wenyufu@chromium.org</owner>
-  <summary>
-    Android: The amount of time spent between native initialized and the
-    decision &quot;whether we need to wait to load policy service from
-    native&quot; has been made. The decision is made when no on-device policy is
-    found, or policy service is fully initialized. If the decision has been made
-    before native initialization, SystemClock#elapsedRealTime was recorded, in
-    which does not provide much useful information. Recorded during FRE only,
-    when on-device policy is {PolicyPresence}
-  </summary>
-  <token key="PolicyPresence">
-    <variant name=".WithoutPolicy" summary="not presented."/>
-    <variant name=".WithPolicy" summary="presented."/>
-  </token>
-</histogram>
-
 <histogram name="MobileFre.PolicyServiceInitDelayAfterNative{PolicyPresence}2"
     units="ms" expires_after="2022-05-01">
   <owner>skym@chromium.org</owner>
@@ -1187,22 +1080,6 @@
   </summary>
 </histogram>
 
-<histogram name="MobileStartup.Experimental.LaunchCause" enum="LaunchCause"
-    expires_after="2022-01-07">
-  <obsolete>
-    Removed Experimental label in 02/2021.
-  </obsolete>
-  <owner>mthiesse@chromium.org</owner>
-  <owner>tedchoc@chromium.org</owner>
-  <owner>yfriedman@chromium.org</owner>
-  <summary>
-    Records what caused Chrome to be launched.
-
-    Recorded for all types of ChromeActivity, in all cases where Chrome becomes
-    visible to the user.
-  </summary>
-</histogram>
-
 <histogram name="MobileStartup.IntentToCreationTime" units="ms"
     expires_after="2022-07-03">
   <owner>tedchoc@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/new_tab_page/histograms.xml b/tools/metrics/histograms/metadata/new_tab_page/histograms.xml
index 8f224ffa9..e9f100f 100644
--- a/tools/metrics/histograms/metadata/new_tab_page/histograms.xml
+++ b/tools/metrics/histograms/metadata/new_tab_page/histograms.xml
@@ -79,19 +79,6 @@
   </summary>
 </histogram>
 
-<histogram name="NewTabPage.BackgroundService.NextImage.RequestLatency"
-    units="ms" expires_after="M90">
-  <obsolete>
-    Never recorded. Marked obsolete 01/2021.
-  </obsolete>
-  <owner>yyushkina@chromium.org</owner>
-  <summary>
-    The time it took until a request from the New Tab Page for the next Backdrop
-    Image in a collection was served. The duration is measured from time the
-    client invokes the service call, to the time it receives the callback.
-  </summary>
-</histogram>
-
 <histogram name="NewTabPage.Carts.AppliedDiscount" units="count"
     expires_after="2022-06-30">
   <owner>meiliang@chromium.org</owner>
@@ -833,56 +820,6 @@
   </summary>
 </histogram>
 
-<histogram name="NewTabPage.Lens.LensSupportStatus" enum="LensSupportStatus"
-    expires_after="2021-11-18">
-  <obsolete>
-    Removed 2021-03. Switch to use NewTabPage.LensSupportStatus.
-  </obsolete>
-  <owner>yusuyoutube@google.com</owner>
-  <owner>benwgold@google.com</owner>
-  <owner>fgorski@chromium.org</owner>
-  <owner>wylieb@chromium.org</owner>
-  <owner>lens-chrome@google.com</owner>
-  <summary>
-    Whether the user supports the camera assisted search with Google Lens and if
-    not, the reason why. Recorded only if the feature is enabled when the New
-    Tab Page search box is loaded.
-  </summary>
-</histogram>
-
-<histogram name="NewTabPage.Lens.TimeSpentInLens" units="ms"
-    expires_after="M94">
-  <obsolete>
-    Removed 2021-03. Switch to use NewTabPage.TimeSpentBeforeDismissLens.
-  </obsolete>
-  <owner>yusuyoutube@google.com</owner>
-  <owner>benwgold@google.com</owner>
-  <owner>fgorski@chromium.org</owner>
-  <owner>wylieb@chromium.org</owner>
-  <owner>lens-chrome@google.com</owner>
-  <summary>
-    Logged the elapsed time between the time when user enters the Google Lens
-    and the time when user returns to Chrome. Only logged on Android.
-  </summary>
-</histogram>
-
-<histogram name="NewTabPage.LensSupportStatus" enum="LensSupportStatus"
-    expires_after="2021-11-18">
-  <obsolete>
-    Removed 2021-05. Switch to use Lens.Omnibox.LensSupportStatus.
-  </obsolete>
-  <owner>yusuyoutube@google.com</owner>
-  <owner>benwgold@google.com</owner>
-  <owner>fgorski@chromium.org</owner>
-  <owner>wylieb@chromium.org</owner>
-  <owner>lens-chrome@google.com</owner>
-  <summary>
-    Whether the user supports the camera assisted search with Google Lens and if
-    not, the reason why. Recorded only if the feature is enabled when the New
-    Tab Page search box is loaded.
-  </summary>
-</histogram>
-
 <histogram name="NewTabPage.LoadTime" units="ms" expires_after="2022-07-11">
   <owner>tiborg@chromium.org</owner>
   <owner>yyushkina@chromium.org</owner>
@@ -1217,20 +1154,6 @@
   </summary>
 </histogram>
 
-<histogram name="NewTabPage.MostVisitedAge" units="seconds"
-    expires_after="2021-08-09">
-  <obsolete>
-    Removed 2021-07.
-  </obsolete>
-  <owner>tiborg@chromium.org</owner>
-  <owner>yyushkina@chromium.org</owner>
-  <owner>chrome-desktop-ntp@google.com</owner>
-  <summary>
-    The age of the data at click time, that is, the elapsed time since the
-    suggestion was generated by a ranking algorithm.
-  </summary>
-</histogram>
-
 <histogram name="NewTabPage.MostVisitedMigratedDefaultAppType"
     enum="TypeOfDeletedMostVisitedApp" expires_after="M103">
   <owner>dibyapal@chromium.org</owner>
@@ -1251,21 +1174,6 @@
   </summary>
 </histogram>
 
-<histogram name="NewTabPage.NumberOfPreinstalledApps" units="count"
-    expires_after="M97">
-  <obsolete>
-    Removed from code in 2022-01.
-  </obsolete>
-  <owner>phillis@chromium.org</owner>
-  <owner>desktop-pwas-team@google.com</owner>
-  <summary>
-    The number of preinstalled apps that are in NTP tiles. Recorded when new tab
-    page is loaded. Recorded before reloading the suggestions, navigating to a
-    URL, switching tabs, changing the active window or closing the tab/shutting
-    down Chrome.
-  </summary>
-</histogram>
-
 <histogram name="NewTabPage.NumberOfTiles" units="units"
     expires_after="2022-06-12">
   <owner>tiborg@chromium.org</owner>
@@ -1373,90 +1281,6 @@
   </summary>
 </histogram>
 
-<histogram name="NewTabPage.Promo.EnhancedProtectionPromo"
-    enum="AndroidEnhancedProtectionPromoAction" expires_after="2022-05-15">
-  <obsolete>
-    Removed from code in 2021-12.
-  </obsolete>
-  <owner>drubery@chromium.org</owner>
-  <owner>chrome-safebrowsing-core@google.com</owner>
-  <summary>
-    Records events related to the Safe Browsing Enhanced Protection promo card
-    on the new tab page. Recorded for Android only.
-  </summary>
-</histogram>
-
-<histogram
-    name="NewTabPage.Promo.EnhancedProtectionPromo.ImpressionUntilAction"
-    units="units" expires_after="2022-05-22">
-  <obsolete>
-    Removed from code in 2021-12.
-  </obsolete>
-  <owner>drubery@chromium.org</owner>
-  <owner>chrome-safebrowsing-core@google.com</owner>
-  <summary>
-    The number of times that the Safe Browsing Enhanced Protection promo was
-    shown to the user before they took action. Recorded when the user accepts
-    the homepage promo. Recorded for Android only.
-  </summary>
-</histogram>
-
-<histogram
-    name="NewTabPage.Promo.EnhancedProtectionPromo.ImpressionUntilDismissal"
-    units="units" expires_after="2022-05-29">
-  <obsolete>
-    Removed from code in 2021-12.
-  </obsolete>
-  <owner>drubery@chromium.org</owner>
-  <owner>chrome-safebrowsing-core@google.com</owner>
-  <summary>
-    The number of times that the Safe Browsing Enhanced Protection promo was
-    shown to the user before it was manually dismissed. Recorded when the user
-    dismisses the promo. Recorded for Android only.
-  </summary>
-</histogram>
-
-<histogram name="NewTabPage.Promo.HomepagePromo"
-    enum="AndroidHomepagePromoAction" expires_after="2021-05-09">
-  <obsolete>
-    Removed in January 2021.
-  </obsolete>
-  <owner>wenyufu@chromium.org</owner>
-  <owner>twellington@chromium.org</owner>
-  <summary>
-    Records events related to the homepage promo card on the new tab page.
-    Recorded for Android only.
-  </summary>
-</histogram>
-
-<histogram name="NewTabPage.Promo.HomepagePromo.ImpressionUntilAction"
-    units="units" expires_after="2021-05-09">
-  <obsolete>
-    Removed in January 2021.
-  </obsolete>
-  <owner>wenyufu@chromium.org</owner>
-  <owner>twellington@chromium.org</owner>
-  <summary>
-    The number of times that the homepage promo was shown to the user before
-    they took action. Recorded when the user accepts the homepage promo.
-    Recorded for Android only.
-  </summary>
-</histogram>
-
-<histogram name="NewTabPage.Promo.HomepagePromo.ImpressionUntilDismissal"
-    units="units" expires_after="2021-05-16">
-  <obsolete>
-    Removed in January 2021.
-  </obsolete>
-  <owner>wenyufu@chromium.org</owner>
-  <owner>twellington@chromium.org</owner>
-  <summary>
-    The number of times that the homepage promo was shown to the user before it
-    was manually dismissed. Recorded when the user dismisses the promo. Recorded
-    for Android only.
-  </summary>
-</histogram>
-
 <histogram name="NewTabPage.Promos.LinkClicked" units="count"
     expires_after="never">
 <!-- expires-never: part of top-line metric (internal: go/chrome-browser-nsm) -->
@@ -1574,36 +1398,6 @@
   </summary>
 </histogram>
 
-<histogram name="NewTabPage.RepeatableQueries.ExtractedCount" units="count"
-    expires_after="2021-10-25">
-  <obsolete>
-    Removed in 05 2021.
-  </obsolete>
-  <owner>mahmadi@chromium.org</owner>
-  <owner>chrome-desktop-ntp@google.com</owner>
-  <summary>
-    The number of normalized search queries extracted from the in-memory URL DB
-    and examined (e.g., sorted) to produce repeatable query suggestions for
-    signed-out users. Triggered every time a signed-out user opens a new tab
-    when the repeatable queries are enabled.
-  </summary>
-</histogram>
-
-<histogram name="NewTabPage.RepeatableQueries.ExtractionDuration" units="ms"
-    expires_after="2021-10-25">
-  <obsolete>
-    Removed in 05 2021.
-  </obsolete>
-  <owner>mahmadi@chromium.org</owner>
-  <owner>chrome-desktop-ntp@google.com</owner>
-  <summary>
-    The length of time it takes to extract normalized search queries from the
-    in-memory URL DB and examine (e.g., sort) them to produce repeatable query
-    suggestions for signed-out users. Triggered every time a signed-out user
-    opens a new tab when the repeatable queries are enabled.
-  </summary>
-</histogram>
-
 <histogram name="NewTabPage.RequestThrottler.PerDay" units="requests"
     expires_after="2021-07-01">
   <owner>freedjm@chromium.org</owner>
@@ -1669,64 +1463,6 @@
   </summary>
 </histogram>
 
-<histogram name="NewTabPage.SearchSuggestions.IndexClicked" units="units"
-    expires_after="never">
-  <obsolete>
-    Not recorded since deprecation of search suggestions. Marked obsolete
-    09/2021.
-  </obsolete>
-  <owner>dbeam@chromium.org</owner>
-  <owner>yyushkina@chromium.org</owner>
-  <owner>chrome-analysis-team@google.com</owner>
-  <summary>
-    The index of the search suggestion tile that was clicked on the local NTP.
-
-    This histogram is of special interest to the chrome-analysis-team@. Do not
-    change its semantics or retire it without talking to them first.
-  </summary>
-</histogram>
-
-<histogram name="NewTabPage.SearchSuggestions.RequestLatencyV2" units="ms"
-    expires_after="M82">
-  <obsolete>
-    Not recorded since deprecation of search suggestions. Marked obsolete
-    09/2021.
-  </obsolete>
-  <owner>dbeam@chromium.org</owner>
-  <owner>yyushkina@chromium.org</owner>
-  <summary>
-    The time it took until a request from the New Tab page for the search
-    suggestions script was served. Recorded only on the local NTP.
-  </summary>
-</histogram>
-
-<histogram name="NewTabPage.SearchSuggestions.RequestStatusV2"
-    enum="NTPSearchSuggestionsRequestStatus" expires_after="M82">
-  <obsolete>
-    Not recorded since deprecation of search suggestions. Marked obsolete
-    09/2021.
-  </obsolete>
-  <owner>dbeam@chromium.org</owner>
-  <owner>yyushkina@chromium.org</owner>
-  <summary>
-    Whether a request was made for search suggestions on NTP load; and if a
-    request was not made, the reason why.
-  </summary>
-</histogram>
-
-<histogram name="NewTabPage.SearchSuggestions.ShownCount" units="units"
-    expires_after="M82">
-  <obsolete>
-    Not recorded since deprecation of search suggestions. Marked obsolete
-    09/2021.
-  </obsolete>
-  <owner>dbeam@chromium.org</owner>
-  <owner>yyushkina@chromium.org</owner>
-  <summary>
-    The number of search suggestion tiles that were shown on the local NTP.
-  </summary>
-</histogram>
-
 <histogram name="NewTabPage.ShoppingTasks.ProductClick" units="index"
     expires_after="2022-07-01">
   <owner>danpeng@google.com</owner>
@@ -1779,95 +1515,6 @@
   </summary>
 </histogram>
 
-<histogram name="NewTabPage.Snippets.DatabaseLoadTime" units="ms"
-    expires_after="M85">
-  <obsolete>
-    Removed in October 2020.
-  </obsolete>
-  <owner>freedjm@chromium.org</owner>
-  <owner>feed@chromium.org</owner>
-  <summary>
-    Android: The time it took to load the database of persisted content
-    suggestions. Recorded only when the database is loaded successfully.
-  </summary>
-</histogram>
-
-<histogram name="NewTabPage.Snippets.EnteredState" enum="NTPSnippetsState"
-    expires_after="M85">
-  <obsolete>
-    Removed in October 2020.
-  </obsolete>
-  <owner>freedjm@chromium.org</owner>
-  <owner>feed@chromium.org</owner>
-  <summary>
-    Android: The state of the RemoteSuggestionsProvider. Recorded when the state
-    changes, typically once at startup and rarely afterwards, e.g. on database
-    errors.
-  </summary>
-</histogram>
-
-<histogram name="NewTabPage.Snippets.FetchHttpResponseOrErrorCode"
-    enum="CombinedHttpResponseAndNetErrorCode" expires_after="M85">
-  <obsolete>
-    Removed in October 2020.
-  </obsolete>
-  <owner>freedjm@chromium.org</owner>
-  <owner>feed@chromium.org</owner>
-  <summary>
-    Response or error codes encountered when attempting to fetch snippets.
-  </summary>
-</histogram>
-
-<histogram name="NewTabPage.Snippets.FetchResult" enum="NtpSnippetsFetchResult"
-    expires_after="M85">
-  <obsolete>
-    Removed in October 2020.
-  </obsolete>
-  <owner>freedjm@chromium.org</owner>
-  <owner>feed@chromium.org</owner>
-  <summary>
-    Result of attempting a fetch, logged once per issued trigger.
-  </summary>
-</histogram>
-
-<histogram name="NewTabPage.Snippets.FetchTime" units="ms" expires_after="M85">
-  <obsolete>
-    Removed in October 2020.
-  </obsolete>
-  <owner>freedjm@chromium.org</owner>
-  <owner>feed@chromium.org</owner>
-  <summary>
-    Time spent fetching snippets. Only recorded for fetch attempts that resulted
-    in an actual network request.
-  </summary>
-</histogram>
-
-<histogram name="NewTabPage.Snippets.FetchTimeLocal" units="minutes"
-    expires_after="M85">
-  <obsolete>
-    Removed in October 2020.
-  </obsolete>
-  <owner>freedjm@chromium.org</owner>
-  <owner>feed@chromium.org</owner>
-  <summary>
-    Records the time of the day in minutes when a snippets background fetch was
-    initiated. Counts minutes since midnight UTC.
-  </summary>
-</histogram>
-
-<histogram name="NewTabPage.Snippets.FetchTimeUTC" units="minutes"
-    expires_after="M85">
-  <obsolete>
-    Removed in October 2020.
-  </obsolete>
-  <owner>freedjm@chromium.org</owner>
-  <owner>feed@chromium.org</owner>
-  <summary>
-    Records the time of the day in minutes when a snippets background fetch was
-    initiated. Counts minutes since midnight local time.
-  </summary>
-</histogram>
-
 <histogram name="NewTabPage.Snippets.IncompleteSnippetsAfterFetch"
     enum="Boolean" expires_after="M85">
   <owner>maybelle@chromium.org</owner>
@@ -1877,59 +1524,6 @@
   </summary>
 </histogram>
 
-<histogram name="NewTabPage.Snippets.NumArticles" units="articles"
-    expires_after="M85">
-  <obsolete>
-    Removed in October 2020.
-  </obsolete>
-  <owner>freedjm@chromium.org</owner>
-  <owner>feed@chromium.org</owner>
-  <summary>
-    Android: The number of snippet articles available to show on the NTP, logged
-    once every time the list is updated.
-  </summary>
-</histogram>
-
-<histogram name="NewTabPage.Snippets.NumArticlesFetched" units="articles"
-    expires_after="M85">
-  <obsolete>
-    Removed in October 2020.
-  </obsolete>
-  <owner>freedjm@chromium.org</owner>
-  <owner>feed@chromium.org</owner>
-  <summary>
-    Android: The number of valid snippet articles fetched from the server,
-    logged every time a fetch finishes successfully.
-  </summary>
-</histogram>
-
-<histogram name="NewTabPage.Snippets.NumArticlesZeroDueToDiscarded"
-    units="articles" expires_after="M85">
-  <obsolete>
-    Removed in October 2020.
-  </obsolete>
-  <owner>freedjm@chromium.org</owner>
-  <owner>feed@chromium.org</owner>
-  <summary>
-    Android: The number of snippet articles discarded by the user, logged every
-    time the list is updated resulting in an empty list (all articles
-    discarded).
-  </summary>
-</histogram>
-
-<histogram name="NewTabPage.Snippets.NumIncompleteSnippets" units="snippets"
-    expires_after="M85">
-  <obsolete>
-    Removed in October 2020.
-  </obsolete>
-  <owner>freedjm@chromium.org</owner>
-  <owner>feed@chromium.org</owner>
-  <summary>
-    The number of snippets that we discard per fetch due to having incomplete
-    data.
-  </summary>
-</histogram>
-
 <histogram name="NewTabPage.SuggestionsImpression" enum="MostVisitedTileIndex"
     expires_after="2022-06-12">
   <owner>tiborg@chromium.org</owner>
@@ -1941,71 +1535,6 @@
   </summary>
 </histogram>
 
-<histogram name="NewTabPage.SuggestionsImpressionAge" units="seconds"
-    expires_after="2021-08-09">
-  <obsolete>
-    Removed 2021-07.
-  </obsolete>
-  <owner>tiborg@chromium.org</owner>
-  <owner>yyushkina@chromium.org</owner>
-  <owner>chrome-desktop-ntp@google.com</owner>
-  <summary>
-    The age of the data at impression time, that is, the elapsed time since the
-    suggestion was generated by a ranking algorithm.
-  </summary>
-</histogram>
-
-<histogram name="NewTabPage.TasksSurface.Lens.LensSupportStatus"
-    enum="LensSupportStatus" expires_after="2021-11-18">
-  <obsolete>
-    Removed 2021-03. Switch to use NewTabPage.TasksSurface.LensSupportStatus.
-  </obsolete>
-  <owner>yusuyoutube@google.com</owner>
-  <owner>benwgold@google.com</owner>
-  <owner>fgorski@chromium.org</owner>
-  <owner>wylieb@chromium.org</owner>
-  <owner>lens-chrome@google.com</owner>
-  <summary>
-    Whether the user supports the camera assisted search with Google Lens and if
-    not, the reason why. Recorded only if the feature is enabled when the Task
-    Surface search box is loaded.
-  </summary>
-</histogram>
-
-<histogram name="NewTabPage.TasksSurface.Lens.TimeSpentInLens" units="ms"
-    expires_after="M94">
-  <obsolete>
-    Removed 2021-03. Switch to use
-    NewTabPage.TasksSurface.TimeSpentBeforeDismissLens.
-  </obsolete>
-  <owner>yusuyoutube@google.com</owner>
-  <owner>benwgold@google.com</owner>
-  <owner>fgorski@chromium.org</owner>
-  <owner>wylieb@chromium.org</owner>
-  <owner>lens-chrome@google.com</owner>
-  <summary>
-    Logged the elapsed time between the time when user enters the Google Lens
-    and the time when user returns to Chrome. Only logged on Android.
-  </summary>
-</histogram>
-
-<histogram name="NewTabPage.TasksSurface.LensSupportStatus"
-    enum="LensSupportStatus" expires_after="2021-11-18">
-  <obsolete>
-    Removed 2021-05. Switch to use Lens.Omnibox.LensSupportStatus.
-  </obsolete>
-  <owner>yusuyoutube@google.com</owner>
-  <owner>benwgold@google.com</owner>
-  <owner>fgorski@chromium.org</owner>
-  <owner>wylieb@chromium.org</owner>
-  <owner>lens-chrome@google.com</owner>
-  <summary>
-    Whether the user supports the camera assisted search with Google Lens and if
-    not, the reason why. Recorded only if the feature is enabled when the Task
-    Surface search box is loaded.
-  </summary>
-</histogram>
-
 <histogram name="NewTabPage.TasksSurface.TimeSpentBeforeDismissLens" units="ms"
     expires_after="M97">
   <owner>yusuyoutube@google.com</owner>
diff --git a/tools/metrics/histograms/metadata/offline/histograms.xml b/tools/metrics/histograms/metadata/offline/histograms.xml
index fa8afb0..fd8ebbd 100644
--- a/tools/metrics/histograms/metadata/offline/histograms.xml
+++ b/tools/metrics/histograms/metadata/offline/histograms.xml
@@ -142,21 +142,6 @@
   </summary>
 </histogram>
 
-<histogram name="OfflineIndicator.ShownDuration" units="ms"
-    expires_after="2021-07-11">
-  <obsolete>
-    Removed M90. Replaced by OfflineIndicator.ShownDurationV2.
-  </obsolete>
-  <owner>curranmax@chromium.org</owner>
-  <owner>tbansal@chromium.org</owner>
-  <owner>sinansahin@google.com</owner>
-  <owner>twellington@chromium.org</owner>
-  <summary>
-    The duration the offline indicator was shown. Recorded when the offline
-    indicator hides.
-  </summary>
-</histogram>
-
 <histogram name="OfflineIndicator.ShownDurationV2" units="ms"
     expires_after="2022-04-17">
   <owner>curranmax@chromium.org</owner>
@@ -467,19 +452,6 @@
   </summary>
 </histogram>
 
-<histogram name="OfflinePages.Background.OffliningPreviewStatus"
-    enum="BooleanEnabled" expires_after="2021-01-25">
-  <obsolete>
-    Removed 22 Jan 2021.
-  </obsolete>
-  <owner>petewil@chromium.org</owner>
-  <owner>offline-dev@chromium.org</owner>
-  <summary>
-    Whether any previews were selected for a page we were asked to make
-    available offline.
-  </summary>
-</histogram>
-
 <histogram name="OfflinePages.Background.RequestFailure.StartedAttemptCount"
     units="units" expires_after="2021-06-25">
   <owner>dougarnett@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/omnibox/histograms.xml b/tools/metrics/histograms/metadata/omnibox/histograms.xml
index 74f2118c..141dcaf 100644
--- a/tools/metrics/histograms/metadata/omnibox/histograms.xml
+++ b/tools/metrics/histograms/metadata/omnibox/histograms.xml
@@ -270,24 +270,6 @@
   </summary>
 </histogram>
 
-<histogram name="Omnibox.ElisionConfig" enum="OmniboxElisionConfig"
-    expires_after="M88">
-  <obsolete>
-    Removed 2020-10.
-  </obsolete>
-  <owner>livvielin@chromium.org</owner>
-  <owner>tommycli@chromium.org</owner>
-  <summary>
-    The state of URL elision in the omnibox, e.g. URL default behavior,
-    prevented by context menu option, or prevented by Chrome extension. This
-    histogram is recorded the first time ShouldPreventElision is called on
-    ChromeLocationBarModelDelegate with an existing profile. This occurs every
-    time a browser window is open, including when opening a new profile or
-    moving a tab to a new window. This metric measures the percentage of browser
-    windows that use each type of elision behavior.
-  </summary>
-</histogram>
-
 <histogram name="Omnibox.EnteredKeywordMode2" enum="OmniboxEnteredKeywordMode2"
     expires_after="2022-06-05">
   <owner>jdonnelly@chromium.org</owner>
@@ -363,28 +345,6 @@
   </summary>
 </histogram>
 
-<histogram name="Omnibox.IconOrFaviconShown" enum="SuggestionIconOrFaviconType"
-    expires_after="M83">
-  <obsolete>
-    Removed 2020-10.
-  </obsolete>
-  <owner>ender@chromium.org</owner>
-  <owner>jdonnelly@chromium.org</owner>
-  <owner>mpearson@chromium.org</owner>
-  <owner>chrome-android-omnibox-team@google.com</owner>
-  <summary>
-    Records suggestions decorated with specific icon or favicon at the time the
-    user exited the omnibox. Exiting the omnibox includes navigating (to entered
-    text or any suggestion), pressing the system back key, clearing omnibox,
-    blanking screen / locking the phone (whether intentionally or due to
-    inactivity), or closing the Chrome app. This metric is logged every time the
-    omnibox is exited, including when no answer is present in the list of
-    suggestions.
-
-    This histogram is related to Omnibox.SuggestionUsed.IconOrFaviconType.
-  </summary>
-</histogram>
-
 <histogram name="Omnibox.InputType" enum="OmniboxInputType"
     expires_after="2022-06-12">
   <owner>jdonnelly@chromium.org</owner>
@@ -457,55 +417,6 @@
   </summary>
 </histogram>
 
-<histogram name="Omnibox.Lens.LensSupportStatus" enum="LensSupportStatus"
-    expires_after="2021-11-18">
-  <obsolete>
-    Removed 2021-03. Switch to use Omnibox.LensSupportStatus.
-  </obsolete>
-  <owner>yusuyoutube@google.com</owner>
-  <owner>benwgold@google.com</owner>
-  <owner>fgorski@chromium.org</owner>
-  <owner>wylieb@chromium.org</owner>
-  <owner>lens-chrome@google.com</owner>
-  <summary>
-    Whether the user supports the camera assisted search with Google Lens and if
-    not, the reason why. Recorded only if the feature is enabled when the
-    omnibox is loaded.
-  </summary>
-</histogram>
-
-<histogram name="Omnibox.Lens.TimeSpentInLens" units="ms" expires_after="M94">
-  <obsolete>
-    Removed 2021-03. Switch to use Omnibox.TimeSpentBeforeDismissLens.
-  </obsolete>
-  <owner>yusuyoutube@google.com</owner>
-  <owner>benwgold@google.com</owner>
-  <owner>fgorski@chromium.org</owner>
-  <owner>wylieb@chromium.org</owner>
-  <owner>lens-chrome@google.com</owner>
-  <summary>
-    Logged the elapsed time between the time when user enters the Google Lens
-    and the time when user returns to Chrome. Only logged on Android.
-  </summary>
-</histogram>
-
-<histogram name="Omnibox.LensSupportStatus" enum="LensSupportStatus"
-    expires_after="2021-11-18">
-  <obsolete>
-    Removed 2021-05. Switch to use Lens.Omnibox.LensSupportStatus.
-  </obsolete>
-  <owner>yusuyoutube@google.com</owner>
-  <owner>benwgold@google.com</owner>
-  <owner>fgorski@chromium.org</owner>
-  <owner>wylieb@chromium.org</owner>
-  <owner>lens-chrome@google.com</owner>
-  <summary>
-    Whether the user supports the camera assisted search with Google Lens and if
-    not, the reason why. Recorded only if the feature is enabled when the
-    omnibox is loaded.
-  </summary>
-</histogram>
-
 <histogram name="Omnibox.LocalHistoryZeroSuggest.AsyncDeleteTime" units="ms"
     expires_after="2022-06-05">
   <owner>mahmadi@chromium.org</owner>
@@ -674,18 +585,6 @@
   </summary>
 </histogram>
 
-<histogram name="Omnibox.PasteAndGo" units="count" expires_after="M89">
-  <obsolete>
-    Removed 2021-01 due to redundancy with Omnibox.IsPasteAndGo.
-  </obsolete>
-  <owner>mpearson@chromium.org</owner>
-  <owner>jdonnelly@chromium.org</owner>
-  <summary>
-    The number of paste-and-go commands on the text in the omnibox. Reported
-    every time a paste-and-go command is done.
-  </summary>
-</histogram>
-
 <histogram name="Omnibox.PedalShown" enum="SuggestionPedalType"
     expires_after="2022-06-05">
   <owner>jdonnelly@chromium.org</owner>
@@ -829,19 +728,6 @@
   </summary>
 </histogram>
 
-<histogram name="Omnibox.ShowFullUrlsEnabled" enum="BooleanEnabled"
-    expires_after="M88">
-  <obsolete>
-    Removed 2020-10.
-  </obsolete>
-  <owner>livvielin@chromium.org</owner>
-  <owner>tommycli@chromium.org</owner>
-  <summary>
-    Tracks whether the context menu option to prevent URL elisions in the
-    omnibox is enabled. Recorded when the pref is toggled.
-  </summary>
-</histogram>
-
 <histogram name="Omnibox.Start.WantAsyncMatches" enum="Boolean"
     expires_after="2022-07-29">
   <owner>tommycli@chromium.org</owner>
@@ -1145,45 +1031,6 @@
   </summary>
 </histogram>
 
-<histogram name="Omnibox.UserTextCleared" enum="OmniboxUserTextCleared"
-    expires_after="M85">
-  <obsolete>
-    Removed 2021-01.
-  </obsolete>
-  <owner>kenjibaheux@chromium.org</owner>
-  <owner>mpearson@chromium.org</owner>
-  <owner>jdonnelly@chromium.org</owner>
-  <summary>
-    Counts the number of times that the user text is cleared. IME users are
-    sometimes in the situation that IME was unintentionally turned on and failed
-    to input latin alphabets (ASCII characters) or the opposite case. In that
-    case, users may delete all the text and the user text gets cleared. This
-    histogram helps us estimate how often this scenario happens.
-
-    Note that since we don't currently correlate &quot;text cleared&quot; events
-    with IME usage, this also captures many other cases where users clear the
-    text; though it explicitly doesn't log deleting all the permanent text as
-    the first action of an editing sequence (see comments in
-    OnAfterPossibleChange()).
-  </summary>
-</histogram>
-
-<histogram name="Omnibox.WarmupTime" units="ms" expires_after="M85">
-  <obsolete>
-    Deprecated as of 03/2021. Code is removed.
-  </obsolete>
-  <owner>etienneb@chromium.org</owner>
-  <owner>mpearson@chromium.org</owner>
-  <owner>jdonnelly@chromium.org</owner>
-  <summary>
-    Time it takes for the omnibox to process the first user interaction after
-    startup. This measures the time it takes to start all autocomplete providers
-    and their initialisation steps. This metric may be recorded twice as there
-    is the normal omnibox path and also the &quot;classify text&quot; path to
-    the autocomplete system.
-  </summary>
-</histogram>
-
 <histogram name="Omnibox.ZeroSuggest.Eligible.OnFocusV2"
     enum="ZeroSuggestEligibleOnFocus" expires_after="2022-06-19">
   <owner>mpearson@chromium.org</owner>
@@ -1244,22 +1091,6 @@
   </summary>
 </histogram>
 
-<histogram name="Omnibox.ZeroSuggestRequests" enum="OmniboxZeroSuggestRequests"
-    expires_after="2022-06-05">
-  <obsolete>
-    No longer reported as of 2021-12. Omnibox.ZeroSuggestRequests.Prefetch and
-    Omnibox.ZeroSuggestRequests.NonPrefetch are being reported instead.
-  </obsolete>
-  <owner>ender@chromium.org</owner>
-  <owner>tommycli@chromium.org</owner>
-  <owner>chrome-omnibox-team@google.com</owner>
-  <summary>
-    Counts about the number of zero suggest requests (requests for suggestions
-    when the user has focused but not modified the omnibox) the omnibox sent,
-    invalidated, and replies received.
-  </summary>
-</histogram>
-
 <histogram name="Omnibox.ZeroSuggestRequests.NonPrefetch"
     enum="OmniboxZeroSuggestRequests" expires_after="2022-06-05">
   <owner>mahmadi@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/others/histograms.xml b/tools/metrics/histograms/metadata/others/histograms.xml
index 79294f2..a18ebaf 100644
--- a/tools/metrics/histograms/metadata/others/histograms.xml
+++ b/tools/metrics/histograms/metadata/others/histograms.xml
@@ -500,7 +500,10 @@
     this allows reporting metrics when fewer than 6 auctions occurred during the
     page's lifetime. Therefore, the max value is 0b1111111 (7 bits, represents 6
     successful auctions), and the min value is 0b10 (represents a page that had
-    1 failed auction), and every value in between is valid.
+    1 failed auction), and every value in between is valid. Not updated for
+    auctions skipped due to enforcing the auction limit, see
+    NumAuctionsSkippedDueToAuctionLimit. Also, not updated if runAdAuction()
+    fails before the auction worklets run.
 
     See https://github.com/WICG/turtledove/blob/main/FLEDGE.md for the latest
     version of the FLEDGE explainer.
@@ -514,7 +517,25 @@
   <owner>privacy-sandbox-dev@chromium.org</owner>
   <summary>
     The number of auctions (runAdAuction() calls) per-page, from page load to
-    unload, reported at unload (if at least 1 auction completed).
+    unload, reported at unload (if at least 1 auction completed). Not updated
+    for auctions skipped due to enforcing the auction limit, see
+    NumAuctionsSkippedDueToAuctionLimit. Also, not updated if runAdAuction()
+    fails before the auction worklets run.
+
+    See https://github.com/WICG/turtledove/blob/main/FLEDGE.md for the latest
+    version of the FLEDGE explainer.
+  </summary>
+</histogram>
+
+<histogram name="Ads.InterestGroup.Auction.NumAuctionsSkippedDueToAuctionLimit"
+    units="auctions" expires_after="2022-03-31">
+  <owner>caraitto@chromium.org</owner>
+  <owner>pauljensen@chromium.org</owner>
+  <owner>privacy-sandbox-dev@chromium.org</owner>
+  <summary>
+    The number of auctions (runAdAuction() calls) per-page, from page load to
+    unload, reported at unload (if at least 1 auction completed) that were
+    skipped due to hitting the auction limit.
 
     See https://github.com/WICG/turtledove/blob/main/FLEDGE.md for the latest
     version of the FLEDGE explainer.
@@ -562,7 +583,10 @@
   <summary>
     The percentage of auctions (runAdAuction() calls) per-page that succeed,
     from page load to unload, reported at unload (if at least 1 auction
-    completed), rounded down to the nearest percent.
+    completed), rounded down to the nearest percent. Not updated for auctions
+    skipped due to enforcing the auction limit, see
+    NumAuctionsSkippedDueToAuctionLimit. Also, not updated if runAdAuction()
+    fails before the auction worklets run.
 
     See https://github.com/WICG/turtledove/blob/main/FLEDGE.md for the latest
     version of the FLEDGE explainer.
@@ -589,7 +613,9 @@
   <owner>privacy-sandbox-dev@chromium.org</owner>
   <summary>
     The duration between runAdAuction() calls, collected on a per-page basis,
-    reported on each auction.
+    reported on each auction. Not updated for auctions skipped due to enforcing
+    the auction limit, see NumAuctionsSkippedDueToAuctionLimit. Also, not
+    updated if runAdAuction() fails before the auction worklets run.
 
     See https://github.com/WICG/turtledove/blob/main/FLEDGE.md for the latest
     version of the FLEDGE explainer.
diff --git a/tools/metrics/histograms/metadata/search/histograms.xml b/tools/metrics/histograms/metadata/search/histograms.xml
index ecda8be..fd473382 100644
--- a/tools/metrics/histograms/metadata/search/histograms.xml
+++ b/tools/metrics/histograms/metadata/search/histograms.xml
@@ -1386,19 +1386,6 @@
   </summary>
 </histogram>
 
-<histogram name="Search.QueryTiles.NTP.Chip.SearchClicked" units="index"
-    expires_after="2022-04-01">
-  <obsolete>
-    Deprecated as of November 2021 since Query Tiles no longer use chips.
-  </obsolete>
-  <owner>shaktisahu@chromium.org</owner>
-  <owner>chrome-upboarding-eng@google.com</owner>
-  <summary>
-    Records the index of the query tile chip that was clicked from the fake
-    search box on the new tab page.
-  </summary>
-</histogram>
-
 <histogram name="Search.QueryTiles.RequestStatus"
     enum="QueryTilesRequestStatus" expires_after="2022-04-01">
   <owner>qinmin@chromium.org</owner>
@@ -1600,21 +1587,6 @@
   </summary>
 </histogram>
 
-<histogram name="Search.RelatedSearches.NumberOfSuggestionsClicked"
-    units="selected" expires_after="M95">
-  <obsolete>
-    Deprecated as of August 2021 in favor of
-    Search.RelatedSearches.NumberOfSuggestionsClicked2
-  </obsolete>
-  <owner>donnd@chromium.org</owner>
-  <owner>related-searches-vteam@google.com</owner>
-  <summary>
-    The number of suggestions that were chosen by the user in a bottom-sheet
-    session that showed any Related Searches. Written when the suggestions UI is
-    closed. Implemented for Android.
-  </summary>
-</histogram>
-
 <histogram name="Search.RelatedSearches.NumberOfSuggestionsClicked2"
     units="selected" expires_after="2022-06-26">
   <owner>donnd@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/translate/histograms.xml b/tools/metrics/histograms/metadata/translate/histograms.xml
index 71a8e03a..840329f 100644
--- a/tools/metrics/histograms/metadata/translate/histograms.xml
+++ b/tools/metrics/histograms/metadata/translate/histograms.xml
@@ -216,43 +216,6 @@
   </summary>
 </histogram>
 
-<histogram name="Translate.CompactInfobar.TranslationsPerPage"
-    units="translations" expires_after="2021-10-10">
-  <obsolete>
-    Removed M95. No longer used.
-  </obsolete>
-  <owner>megjablon@google.com</owner>
-  <owner>chrome-language@google.com</owner>
-  <summary>
-    Records the number of times a page is translated, every time the page is
-    translated. For instance on a page a) translation from A to B, we record
-    &quot;1&quot; b) from A to B - we record &quot;1&quot; and revert from B to
-    A - we record &quot;2&quot; c) from A to B - we record &quot;1&quot;, then
-    translation to C - we record &quot;2&quot;. d) from A to B - we record
-    &quot;1&quot;, then translation to C - we record &quot;2&quot; and then
-    revert to A - we record &quot;3&quot;. We increment the translation count:
-    a) every time the target language is changed. b) every time we revert to the
-    original language (user can only revert to original language and not
-    intermediate target languages). This essentially means 1 denotes &gt;= 1
-    translations, 2 is &gt;=2 translations per page and so on. This will give us
-    a sense of how often people try out several translates on a page, or flip
-    back and forth.
-  </summary>
-</histogram>
-
-<histogram name="Translate.ContentLanguage" enum="TranslateLanguage"
-    expires_after="2021-08-09">
-  <obsolete>
-    Removed M90. No longer used.
-  </obsolete>
-  <owner>kenjibaheux@google.com</owner>
-  <owner>chrome-language@google.com</owner>
-  <summary>
-    A page may provide a Content-Language HTTP header or a META tag. For each
-    page load, measures whether the Content-Language header exists and is valid.
-  </summary>
-</histogram>
-
 <histogram name="Translate.DeclineTranslate" units="units"
     expires_after="2022-06-30">
   <owner>megjablon@google.com</owner>
@@ -273,23 +236,6 @@
   </summary>
 </histogram>
 
-<histogram name="Translate.DeclineTranslateDismissUI" units="units"
-    expires_after="2021-04-04">
-  <obsolete>
-    Removed M91. No longer used.
-  </obsolete>
-  <owner>kenjibaheux@google.com</owner>
-  <owner>chrome-language@google.com</owner>
-  <summary>
-    The number of times the translate UI was closed without translating in the
-    way that the user doesn't deny translating explicityly, like pressing 'Nope'
-    button. This is counted on both the infobar and the bubble UI. We are
-    comparing this on infobar to that on bubble by A/B testing and expecting
-    that the user will click 'Nope' button on bubble less times than infobar. We
-    won't delete this histogram after the experiment.
-  </summary>
-</histogram>
-
 <histogram name="Translate.ExplicitLanguageAsk.Event"
     enum="TranslateExplicitAskPromptEventType" expires_after="2022-06-30">
   <owner>megjablon@google.com</owner>
@@ -350,19 +296,6 @@
   </summary>
 </histogram>
 
-<histogram name="Translate.HtmlLang" enum="TranslateLanguage"
-    expires_after="2021-04-04">
-  <obsolete>
-    Removed M90. No longer used.
-  </obsolete>
-  <owner>kenjibaheux@google.com</owner>
-  <owner>chrome-language@google.com</owner>
-  <summary>
-    A page may provide a lang attribute in html tag. For each page load,
-    measures whether the lang attribute exists and is valid.
-  </summary>
-</histogram>
-
 <histogram name="Translate.InfobarShown" enum="BooleanHit" expires_after="M85">
   <owner>megjablon@google.com</owner>
   <owner>chrome-language@google.com</owner>
@@ -439,59 +372,6 @@
   </summary>
 </histogram>
 
-<histogram name="Translate.LanguageSettingsIsShown" enum="BooleanShown"
-    expires_after="2021-08-09">
-  <obsolete>
-    Removed Feb 2021. Replaced with Main bucket of
-    LanguageSettings.PageImpression.
-  </obsolete>
-  <owner>megjablon@google.com</owner>
-  <owner>chrome-language@google.com</owner>
-  <summary>
-    Log everytime the language settings page is shown. This can be either user
-    visits chrome://settings/languages or user visits the advanced languages
-    card in chrome://settings. With this stat, we will be able to understand how
-    well the language model is doing by looking into how the number changes over
-    time. In additiona to that, we are also interested in how many users ever
-    visit the languages settings card.
-  </summary>
-</histogram>
-
-<histogram name="Translate.LanguageVerification"
-    enum="TranslateLanguageVerification" expires_after="M78">
-  <obsolete>
-    Removed M90. Replaced by Translate.LanguageDetection.LanguageVerification.
-  </obsolete>
-  <owner>megjablon@chromium.org</owner>
-  <owner>chrome-language@google.com</owner>
-  <summary>
-    For each page load, measures whether the provided HTML language (i.e. the
-    page lang attribute if it exists, otherwise the header Content-Language
-    value) matches the language determined by CLD. Beyond directly matching or
-    mismatching the HTML language, CLD can complement the HTML language. For
-    example, suppose the HTML language is 'zh' (general Chinese), a language
-    code that the Translate server does not support. In this case, CLD can
-    detect a subcode like '-TW' or '-CN', resulting in language codes 'zh-TW'
-    and 'zh-CN', which the Translate server supports. This is referred to as
-    &quot;complementing a language subcode&quot;.
-  </summary>
-</histogram>
-
-<histogram name="Translate.LocalesOnDisabledByPrefs" enum="LanguageName"
-    expires_after="2021-01-31">
-  <obsolete>
-    Removed M94. No longer used.
-  </obsolete>
-  <owner>kenjibaheux@google.com</owner>
-  <owner>chrome-language@google.com</owner>
-  <summary>
-    Logs the user locale when the Translate feature is disabled by the user.
-    This is recorded each time a webpage is loaded and prefs for translation is
-    checked. This allows us to investigate the correlation between the user
-    locale and the usage rates of the Translate.
-  </summary>
-</histogram>
-
 <histogram name="Translate.MenuTranslation.IsAvailable" enum="BooleanAvailable"
     expires_after="2022-06-30">
   <owner>cuianthony@google.com</owner>
@@ -515,19 +395,6 @@
   </summary>
 </histogram>
 
-<histogram name="Translate.MobileMenuTranslate.Shown" enum="Boolean"
-    expires_after="2021-04-04">
-  <obsolete>
-    Removed 10/09/2020. Replaced with Translate.MenuTranslation.IsAvailable.
-  </obsolete>
-  <owner>megjablon@google.com</owner>
-  <owner>chrome-language@google.com</owner>
-  <summary>
-    Whether the 'Translate' app menu entry is shown to the user. Emitted when
-    the app menu (three dots) is shown and a translation could be performed.
-  </summary>
-</histogram>
-
 <histogram name="Translate.ModifyOriginalLang" units="units"
     expires_after="2022-06-30">
   <owner>megjablon@google.com</owner>
@@ -797,47 +664,6 @@
   </summary>
 </histogram>
 
-<histogram name="Translate.PageLoad.TriggerDecision.AllTriggerDecisions"
-    enum="TranslateTriggerDecision" expires_after="M92">
-  <obsolete>
-    Removed M92. This histogram was used to estimate the impact of masking on
-    Translate.PageLoad.TriggerDecision. We found that masking was having minimal
-    impact, so this histogram is no longer needed.
-  </obsolete>
-  <owner>curranmax@google.com</owner>
-  <owner>megjablon@google.com</owner>
-  <owner>chrome-language@google.com</owner>
-  <summary>
-    Logs all trigger decisions (reason for the initial state of Translate) for
-    each page load. Each trigger decision is recorded as a separate sample, so
-    there may more than one value recorded per page load. If Translate is
-    initialized multiple times in a single page load, then we may record the
-    same enum multiple times for a single page load. Additionally we may record
-    no samples in cases were the page is abandoned before we process trigger
-    decisions. These values are logged at the end of the page load or the first
-    time Chrome is backgrounded during the page load, whichever comes first.
-  </summary>
-</histogram>
-
-<histogram name="Translate.PageLoad.TriggerDecision.TotalCount"
-    units="trigger decisions" expires_after="M92">
-  <obsolete>
-    Removed M92. This histogram was used to estimate the impact of masking on
-    Translate.PageLoad.TriggerDecision. We found that masking was having minimal
-    impact, so this histogram is no longer needed.
-  </obsolete>
-  <owner>curranmax@google.com</owner>
-  <owner>megjablon@google.com</owner>
-  <owner>chrome-language@google.com</owner>
-  <summary>
-    Logs the total number of trigger decisions (reason for the initial state of
-    Translate) for each page load. If Translate is initialized multiple times in
-    a single page load, then the same enum value could be counted multiple
-    times. This value is logged at the end of the page load or the first time
-    Chrome is backgrounded during the page load, whichever comes first.
-  </summary>
-</histogram>
-
 <histogram name="Translate.PageScheme" enum="TranslateScheme"
     expires_after="M77">
   <owner>megjablon@google.com</owner>
@@ -874,20 +700,6 @@
   </summary>
 </histogram>
 
-<histogram name="Translate.Ranker.Timer.CalculateScore" units="ms"
-    expires_after="M77">
-  <obsolete>
-    Removed M91. Replaced by
-    Translate.PageLoad.Ranker.Timer.ShouldOfferTranslation.
-  </obsolete>
-  <owner>rogerm@google.com</owner>
-  <owner>chrome-language@google.com</owner>
-  <summary>
-    Time taken for the TranslateRanker to use the translate ranker model to
-    calculate a score for the translation, in ms.
-  </summary>
-</histogram>
-
 <histogram name="Translate.Ranker.Timer.DownloadModel" units="ms"
     expires_after="M77">
   <owner>rogerm@google.com</owner>
@@ -917,22 +729,6 @@
   </summary>
 </histogram>
 
-<histogram name="Translate.Ranker.Timer.ShouldOfferTranslation" units="ms"
-    expires_after="M77">
-  <obsolete>
-    Removed M91. Replaced by
-    Translate.PageLoad.Ranker.Timer.ShouldOfferTranslation.
-  </obsolete>
-  <owner>rogerm@google.com</owner>
-  <owner>chrome-language@google.com</owner>
-  <summary>
-    Time taken for the TranslateRanker to decide if a given translation should
-    be offered or not, in ms. This includes the time taken to extract the
-    relevant features upon which to base the decision, as well as the time taken
-    to calculate the result.
-  </summary>
-</histogram>
-
 <histogram name="Translate.Ranker.Timer.WriteModel" units="ms"
     expires_after="M77">
   <owner>rogerm@google.com</owner>
@@ -943,19 +739,6 @@
   </summary>
 </histogram>
 
-<histogram name="Translate.ReportLanguageDetectionError" units="units"
-    expires_after="2021-01-31">
-  <obsolete>
-    Removed M94. No longer used.
-  </obsolete>
-  <owner>kenjibaheux@google.com</owner>
-  <owner>chrome-language@google.com</owner>
-  <summary>
-    The number of times the &quot;report this error&quot; of options menu is
-    selected in the translate infobar.
-  </summary>
-</histogram>
-
 <histogram name="Translate.RevertTranslation" units="units"
     expires_after="2022-06-30">
   <owner>megjablon@google.com</owner>
@@ -1022,39 +805,6 @@
   </summary>
 </histogram>
 
-<histogram name="Translate.TimeToBeReady" units="ms" expires_after="M77">
-  <obsolete>
-    Removed M90. Replaced with Translate.Translation.TimeToBeReady.
-  </obsolete>
-  <owner>megjablon@google.com</owner>
-  <owner>chrome-language@google.com</owner>
-  <summary>
-    The time from injecting scripts for Chrome Translate to being ready to
-    perform translation.
-  </summary>
-</histogram>
-
-<histogram name="Translate.TimeToLoad" units="ms" expires_after="M77">
-  <obsolete>
-    Removed M90. Replaced with Translate.Translation.TimeToLoad.
-  </obsolete>
-  <owner>kenjibaheux@google.com</owner>
-  <owner>chrome-language@google.com</owner>
-  <summary>
-    The time from injecting scripts for Chrome Translate to the finishing loads
-    of all depending libraries.
-  </summary>
-</histogram>
-
-<histogram name="Translate.TimeToTranslate" units="ms" expires_after="M77">
-  <obsolete>
-    Removed M90. Replaced with Translate.Translation.TimeToTranslate.
-  </obsolete>
-  <owner>kenjibaheux@google.com</owner>
-  <owner>chrome-language@google.com</owner>
-  <summary>The time from starting translation to the completion.</summary>
-</histogram>
-
 <histogram name="Translate.Translate" enum="BooleanTranslate"
     expires_after="2022-06-30">
   <owner>megjablon@google.com</owner>
@@ -1271,21 +1021,6 @@
   </summary>
 </histogram>
 
-<histogram name="Translate.UndisplayableLanguage" enum="LanguageName"
-    expires_after="M81">
-  <obsolete>
-    Removed 01/09/2021. Not used anymore.
-  </obsolete>
-  <owner>megjablon@google.com</owner>
-  <owner>chrome-language@google.com</owner>
-  <summary>
-    Logs an undisplayable language included in the language list sent by the
-    Translate server. The Translate server sends the list each time the user
-    runs Chrome. This metrics tells us that there is a language which UI should
-    support but doesn't.
-  </summary>
-</histogram>
-
 <histogram name="Translate.UnsupportedLanguageAtInitiation" enum="LanguageName"
     expires_after="M81">
   <owner>megjablon@google.com</owner>
diff --git a/tools/metrics/histograms/metadata/web_apk/histograms.xml b/tools/metrics/histograms/metadata/web_apk/histograms.xml
index e3fc116a..37fb631 100644
--- a/tools/metrics/histograms/metadata/web_apk/histograms.xml
+++ b/tools/metrics/histograms/metadata/web_apk/histograms.xml
@@ -123,25 +123,6 @@
   </summary>
 </histogram>
 
-<histogram name="WebApk.Install.RequestTokenDuration" units="ms"
-    expires_after="2021-07-18">
-  <obsolete>
-    Removed 03/2021
-  </obsolete>
-  <owner>hartmanng@chromium.org</owner>
-  <owner>rayankans@chromium.org</owner>
-  <owner>
-    src/chrome/android/java/src/org/chromium/chrome/browser/webapps/OWNERS
-  </owner>
-  <summary>
-    Records the amount of time which has elapsed from the &quot;install infobar
-    showing&quot; to the &quot;install request being sent to Google Play&quot;.
-    This time includes the time that it takes to request the WebAPK token and
-    the time that it takes to compute the hashes for the launcher icon and the
-    badge icon.
-  </summary>
-</histogram>
-
 <histogram name="WebApk.Install.RequestTokenDurationV2" units="ms"
     expires_after="2022-05-29">
   <owner>hartmanng@chromium.org</owner>
@@ -218,13 +199,7 @@
     The &quot;Shell APK version&quot; is documented in shell_apk_version.gni
     {WebApkDistributorType}
   </summary>
-  <token key="WebApkDistributorType" variants="WebApkDistributorType">
-    <variant name="">
-      <obsolete>
-        Base histogram. Use suffixes of this histogram instead.
-      </obsolete>
-    </variant>
-  </token>
+  <token key="WebApkDistributorType" variants="WebApkDistributorType"/>
 </histogram>
 
 <histogram name="WebApk.Startup.Cold.NewStyle.ShellLaunchToSplashscreenVisible"
@@ -286,13 +261,7 @@
   <summary>
     Records that a WebAPK was uninstalled. {WebApkDistributorType}
   </summary>
-  <token key="WebApkDistributorType" variants="WebApkDistributorType">
-    <variant name="">
-      <obsolete>
-        Base histogram. Use suffixes of this histogram instead.
-      </obsolete>
-    </variant>
-  </token>
+  <token key="WebApkDistributorType" variants="WebApkDistributorType"/>
 </histogram>
 
 <histogram name="WebApk.Update.GooglePlayUpdateResult"
diff --git a/tools/metrics/histograms/metadata/webapps/histograms.xml b/tools/metrics/histograms/metadata/webapps/histograms.xml
index 5aff2d2..43d4be6 100644
--- a/tools/metrics/histograms/metadata/webapps/histograms.xml
+++ b/tools/metrics/histograms/metadata/webapps/histograms.xml
@@ -407,38 +407,6 @@
   <summary>Records the result code of Web App installs.</summary>
 </histogram>
 
-<histogram base="true"
-    name="Webapp.InstallResultExtensionDisabledReason.System.Profiles"
-    enum="ExtensionDisableReason" expires_after="2020-12-31">
-  <obsolete>
-    Removed 11/2020, as the relevant code path is no longer executed after BMO
-    launch.
-  </obsolete>
-  <owner>calamity@chromium.org</owner>
-  <owner>ortuno@chromium.org</owner>
-  <summary>
-    Records why System Web App .crx extension is disabled during System Web App
-    installs. Certain properties of the profile might cause Extensions to be
-    disabled as soon as they are installed. For example, kiosk profile might set
-    policies to disable all Extensions, including System Web Apps which are
-    considered Extensions.
-  </summary>
-</histogram>
-
-<histogram base="true"
-    name="Webapp.InstallResultExtensionError.System.Profiles"
-    enum="ExtensionInstallationCrxInstallError" expires_after="2020-12-31">
-  <obsolete>
-    Removed 11/2020, as the relevant code path is no longer executed after BMO
-    launch.
-  </obsolete>
-  <owner>calamity@chromium.org</owner>
-  <owner>ortuno@chromium.org</owner>
-  <summary>
-    Records .crx extension error code when System Web App install fails.
-  </summary>
-</histogram>
-
 <histogram name="WebApp.Launcher.LaunchResult"
     enum="WebAppLauncherLaunchResult" expires_after="2022-07-01">
   <owner>davidbienvenu@chromium.org</owner>
@@ -459,24 +427,6 @@
   </summary>
 </histogram>
 
-<histogram name="WebApp.Migration.UserDisplayModeCleanUp"
-    enum="BooleanMigrated" expires_after="2021-01-01">
-  <obsolete>
-    Removed 12/2020.
-  </obsolete>
-  <owner>alancutter@chromium.org</owner>
-  <owner>dxie@google.com</owner>
-  <owner>loyso@google.com</owner>
-  <owner>mgiuca@google.com</owner>
-  <summary>
-    The result of deploying a follow up migration after a migration bug
-    (https://crbug.com/1125020) that caused users' web apps to open in a browser
-    tab instead of a standalone window. This follow up migration only runs once
-    at startup for all Chrome profiles. If it fails it will retry until
-    successful on subsequent start ups, metrics are only recorded on success.
-  </summary>
-</histogram>
-
 <histogram name="WebApp.Mover.Result" enum="WebAppMoverResult"
     expires_after="2022-04-24">
   <owner>dmurph@chromium.org</owner>
@@ -596,21 +546,6 @@
   <summary>Records the result of shortcut creation for PWA.</summary>
 </histogram>
 
-<histogram name="WebApp.Shortcuts.Deletion.Success" enum="BooleanSuccess"
-    expires_after="M93">
-  <obsolete>
-    Removed on M93. Not needed anymore.
-  </obsolete>
-  <owner>phillis@chromium.org</owner>
-  <owner>dmurph@chromium.org</owner>
-  <owner>sunggch@microsoft.com</owner>
-  <summary>
-    Records the result of shortcut deletion for a PWA. This occurs when an
-    installed PWA is uninstalled, which can be triggered by user, policy admin,
-    or sync system depending on the situation.
-  </summary>
-</histogram>
-
 <histogram name="Webapp.SyncInitiatedUninstallResult" enum="BooleanSuccess"
     expires_after="2022-04-24">
   <owner>alancutter@chromium.org</owner>
@@ -700,33 +635,6 @@
   </summary>
 </histogram>
 
-<histogram name="WebApp.UninstallDialog.AppMenuUninstallSuccess" enum="Boolean"
-    expires_after="M95">
-  <obsolete>
-    Removed in M92.
-  </obsolete>
-  <owner>dmurph@chromium.org</owner>
-  <owner>desktop-pwas-team@google.com</owner>
-  <summary>
-    Records the result of uninstalling a WebApp when the user clicks on
-    &quot;Uninstall&quot; from the 3-dot menu of the respective WebApp window.
-  </summary>
-</histogram>
-
-<histogram name="WebApp.UninstallDialog.AppsPageUninstallSuccess"
-    enum="Boolean" expires_after="M95">
-  <obsolete>
-    Removed in M92.
-  </obsolete>
-  <owner>dmurph@chromium.org</owner>
-  <owner>desktop-pwas-team@google.com</owner>
-  <summary>
-    Records the result of uninstalling an app from the chrome://apps page. This
-    happens whena user right-clicks on a greyed out webapp icon in the
-    chrome://apps page and clicks &quot;Uninstall&quot;.
-  </summary>
-</histogram>
-
 <histogram name="Webapp.UninstallDialogAction"
     enum="WebappUninstallDialogAction" expires_after="2022-05-22">
   <owner>benwells@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/xr/histograms.xml b/tools/metrics/histograms/metadata/xr/histograms.xml
index 158868af..d8009bd 100644
--- a/tools/metrics/histograms/metadata/xr/histograms.xml
+++ b/tools/metrics/histograms/metadata/xr/histograms.xml
@@ -84,19 +84,6 @@
   </summary>
 </histogram>
 
-<histogram name="XR.WebXR.ReferenceSpace.Requested" enum="XRReferenceSpaceType"
-    expires_after="2021-03-15">
-  <obsolete>
-    Removed 03/2021.
-  </obsolete>
-  <owner>alcooper@chromium.org</owner>
-  <owner>xr-dev@chromium.org</owner>
-  <summary>
-    Records which reference space was requested when a site calls
-    XRSession.requestReferenceSpace().
-  </summary>
-</histogram>
-
 <histogram name="XR.WebXR.ReferenceSpace.Succeeded" enum="XRReferenceSpaceType"
     expires_after="2022-06-05">
   <owner>alcooper@chromium.org</owner>
diff --git a/tools/metrics/ukm/ukm.xml b/tools/metrics/ukm/ukm.xml
index 43197c36..15c65114 100644
--- a/tools/metrics/ukm/ukm.xml
+++ b/tools/metrics/ukm/ukm.xml
@@ -14510,6 +14510,24 @@
   </metric>
 </event>
 
+<event name="PasswordManager.PasswordChangeTriggered">
+  <owner>selakovic@google.com</owner>
+  <owner>chrome-duplex@google.com</owner>
+  <summary>
+    Metrics related to password change. The Password Checkup in Chrome Settings
+    aims to help users find their leaked passwords. After getting list of
+    compromised passwords, user can trigger password change. On some limited
+    number of sites automated password change is available and on others only
+    manual change is available. We collect what type of password change user
+    triggered.
+  </summary>
+  <metric name="PasswordChangeType" enum="PasswordChangeType">
+    <summary>
+      Records what type of password change the user triggers.
+    </summary>
+  </metric>
+</event>
+
 <event name="PasswordManager.WellKnownChangePasswordResult">
   <owner>kazinova@google.com</owner>
   <owner>vasilii@chromium.org</owner>
diff --git a/tools/perf/BUILD.gn b/tools/perf/BUILD.gn
index 7f8fc6a..84b96c7 100644
--- a/tools/perf/BUILD.gn
+++ b/tools/perf/BUILD.gn
@@ -52,6 +52,7 @@
 
   data = [
     "//tools/perf/",
+    "//.vpython3",
 
     # Field trial config
     "//tools/variations/",
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json
index 6a9a4ac5..22e1038 100644
--- a/tools/perf/core/perfetto_binary_roller/binary_deps.json
+++ b/tools/perf/core/perfetto_binary_roller/binary_deps.json
@@ -6,7 +6,7 @@
         },
         "win": {
             "hash": "fac45c375ca5740c962699bf1badc5ef6ef9d1a6",
-            "remote_path": "perfetto_binaries/trace_processor_shell/win/800e2c1852fe3d9af65b9fa22e7b6af1471a2b22/trace_processor_shell.exe"
+            "remote_path": "perfetto_binaries/trace_processor_shell/win/d7dfde628cc24c1ae8a0fa46f91878794def8640/trace_processor_shell.exe"
         },
         "linux_arm": {
             "hash": "58893933be305d3bfe0a72ebebcacde2ac3ca893",
@@ -14,7 +14,7 @@
         },
         "mac": {
             "hash": "b260bbb0f435d260abb220022d6573caad908616",
-            "remote_path": "perfetto_binaries/trace_processor_shell/mac/49527d76232feac4b03c562bd021e3e72554af68/trace_processor_shell"
+            "remote_path": "perfetto_binaries/trace_processor_shell/mac/d7dfde628cc24c1ae8a0fa46f91878794def8640/trace_processor_shell"
         },
         "mac_arm64": {
             "hash": "c0397e87456ad6c6a7aa0133e5b81c97adbab4ab",
@@ -22,7 +22,7 @@
         },
         "linux": {
             "hash": "5e8d99ad4425aed5bcd5a79eefbe7470fced823b",
-            "remote_path": "perfetto_binaries/trace_processor_shell/linux/800e2c1852fe3d9af65b9fa22e7b6af1471a2b22/trace_processor_shell"
+            "remote_path": "perfetto_binaries/trace_processor_shell/linux/d7dfde628cc24c1ae8a0fa46f91878794def8640/trace_processor_shell"
         }
     },
     "power_profile.sql": {
diff --git a/tools/perf/run_gtest_benchmark.py b/tools/perf/run_gtest_benchmark.py
index 4c68826..90953b13 100755
--- a/tools/perf/run_gtest_benchmark.py
+++ b/tools/perf/run_gtest_benchmark.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env vpython
+#!/usr/bin/env vpython3
 # 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.
diff --git a/tools/v8_context_snapshot/BUILD.gn b/tools/v8_context_snapshot/BUILD.gn
index d868696..a06eae4 100644
--- a/tools/v8_context_snapshot/BUILD.gn
+++ b/tools/v8_context_snapshot/BUILD.gn
@@ -28,15 +28,9 @@
 
 config("use_v8_context_snapshot") {
   defines = []
-  if (include_both_v8_snapshots) {
-    defines += [ "INCLUDE_BOTH_V8_SNAPSHOTS" ]
-  }
   if (use_v8_context_snapshot) {
     defines += [
       "USE_V8_CONTEXT_SNAPSHOT",
-
-      # NOTE: android builds may include both 64 and 32 bit snapshot files. For
-      # this reason, android code generally does not use this define.
       "V8_CONTEXT_SNAPSHOT_FILENAME=\"$v8_context_snapshot_filename\"",
     ]
   }
diff --git a/tools/v8_context_snapshot/v8_context_snapshot.gni b/tools/v8_context_snapshot/v8_context_snapshot.gni
index e18eb3a..f3b1b78 100644
--- a/tools/v8_context_snapshot/v8_context_snapshot.gni
+++ b/tools/v8_context_snapshot/v8_context_snapshot.gni
@@ -13,27 +13,17 @@
 import("//v8/gni/v8.gni")
 
 declare_args() {
-  # If set, both snapshots are included. At this time, this only applicable to
-  # android. In other words, it will not work correctly on other platforms.
-  # Building the context snapshots on android requires building blink multiple
-  # times. To avoid impacting developer productivity the context snapshot is
-  # only built for official builds.
-  include_both_v8_snapshots = false
-}
-
-declare_args() {
   # TODO(crbug.com/764576): Enable the feature on more environments.
   # Disable in mac and win cross builds since building Blink twice is slow.
   use_v8_context_snapshot =
-      include_both_v8_snapshots ||
-      (!is_chromeos && !is_android && !is_chromecast && !is_fuchsia &&
-       !(host_os == "mac" && current_cpu == "x86") &&
-       # Android may build for both 64 bit and 32bit. When this happens, the
-       # v8_target_cpu will not equal the target_cpu (for example,
-       # v8_target_cpu == "arm" but target_os == "arm64").
-       (v8_target_cpu == target_cpu || target_os == "android") &&
-       !(host_toolchain == default_toolchain && is_msan) &&
-       !(is_win && host_os != "win") && !(is_mac && host_os != "mac"))
+      !is_chromeos && !is_android && !is_chromecast && !is_fuchsia &&
+      !(host_os == "mac" && current_cpu == "x86") &&
+      # Android may build for both 64 bit and 32bit. When this happens, the
+      # v8_target_cpu will not equal the target_cpu (for example,
+      # v8_target_cpu == "arm" but target_os == "arm64").
+      (v8_target_cpu == target_cpu || target_os == "android") &&
+      !(host_toolchain == default_toolchain && is_msan) &&
+      !(is_win && host_os != "win") && !(is_mac && host_os != "mac")
 
   # We use a different filename for arm64 macOS builds so that the arm64 and
   # x64 snapshots can live side-by-side in a universal macOS app.
diff --git a/ui/aura/host_frame_rate_throttler.cc b/ui/aura/host_frame_rate_throttler.cc
index 68284254..5dbc30d5 100644
--- a/ui/aura/host_frame_rate_throttler.cc
+++ b/ui/aura/host_frame_rate_throttler.cc
@@ -5,6 +5,7 @@
 #include "ui/aura/host_frame_rate_throttler.h"
 
 #include "base/containers/contains.h"
+#include "build/build_config.h"
 #include "components/viz/common/surfaces/frame_sink_id.h"
 #include "components/viz/host/host_frame_sink_manager.h"
 #include "ui/aura/env.h"
@@ -13,7 +14,11 @@
 
 namespace aura {
 
+#if defined(OS_WIN)
 constexpr uint8_t kDefaultThrottleFps = 1;
+#else
+constexpr uint8_t kDefaultThrottleFps = 20;
+#endif
 
 HostFrameRateThrottler& HostFrameRateThrottler::GetInstance() {
   static base::NoDestructor<HostFrameRateThrottler> instance;
diff --git a/ui/aura/window_tree_host_platform.cc b/ui/aura/window_tree_host_platform.cc
index e80c9dd..74d4e27 100644
--- a/ui/aura/window_tree_host_platform.cc
+++ b/ui/aura/window_tree_host_platform.cc
@@ -13,6 +13,7 @@
 #include "build/build_config.h"
 #include "ui/aura/client/cursor_client.h"
 #include "ui/aura/env.h"
+#include "ui/aura/host_frame_rate_throttler.h"
 #include "ui/aura/window.h"
 #include "ui/aura/window_event_dispatcher.h"
 #include "ui/aura/window_tree_host_observer.h"
@@ -294,4 +295,11 @@
   SetNativeWindowOcclusionState(aura_occlusion_state, {});
 }
 
+void WindowTreeHostPlatform::SetFrameRateThrottleEnabled(bool enabled) {
+  if (enabled)
+    HostFrameRateThrottler::GetInstance().AddHost(this);
+  else
+    HostFrameRateThrottler::GetInstance().RemoveHost(this);
+}
+
 }  // namespace aura
diff --git a/ui/aura/window_tree_host_platform.h b/ui/aura/window_tree_host_platform.h
index 8a4bbc2..3626276 100644
--- a/ui/aura/window_tree_host_platform.h
+++ b/ui/aura/window_tree_host_platform.h
@@ -84,6 +84,7 @@
   void OnMouseEnter() override;
   void OnOcclusionStateChanged(
       ui::PlatformWindowOcclusionState occlusion_state) override;
+  void SetFrameRateThrottleEnabled(bool enabled) override;
 
   // Overridden from aura::WindowTreeHost:
   bool CaptureSystemKeyEventsImpl(
diff --git a/ui/base/win/scoped_ole_initializer.cc b/ui/base/win/scoped_ole_initializer.cc
index 5b116af4..4c2fd67 100644
--- a/ui/base/win/scoped_ole_initializer.cc
+++ b/ui/base/win/scoped_ole_initializer.cc
@@ -6,6 +6,8 @@
 
 #include <ole2.h>
 
+#include <ostream>
+
 #include "base/check_op.h"
 
 namespace ui {
diff --git a/ui/chromeos/strings/network_element_localized_strings_provider.cc b/ui/chromeos/strings/network_element_localized_strings_provider.cc
index 26f0b8c..653c8100 100644
--- a/ui/chromeos/strings/network_element_localized_strings_provider.cc
+++ b/ui/chromeos/strings/network_element_localized_strings_provider.cc
@@ -411,6 +411,8 @@
                           chromeos::features::ShouldUseAttachApn());
   html_source->AddBoolean("esimPolicyEnabled",
                           chromeos::features::IsESimPolicyEnabled());
+  html_source->AddBoolean("extendedOpenVpnSettingsEnabled",
+                          ash::features::IsExtendedOpenVpnSettingsEnabled());
 }
 
 void AddConfigLocalizedStrings(content::WebUIDataSource* html_source) {
diff --git a/ui/display/manager/display_configurator_unittest.cc b/ui/display/manager/display_configurator_unittest.cc
index 7e4704da..4dece1d 100644
--- a/ui/display/manager/display_configurator_unittest.cc
+++ b/ui/display/manager/display_configurator_unittest.cc
@@ -245,7 +245,7 @@
 
     // Force system compositor mode to simulate on-device configurator behavior.
     base::CommandLine::ForCurrentProcess()->AppendSwitch(
-        chromeos::switches::kForceSystemCompositorMode);
+        ash::switches::kForceSystemCompositorMode);
 
     native_display_delegate_ = new TestNativeDisplayDelegate(log_.get());
     configurator_.SetDelegateForTesting(
diff --git a/ui/ozone/platform/wayland/host/wayland_toplevel_window.cc b/ui/ozone/platform/wayland/host/wayland_toplevel_window.cc
index a23ec63..d7eae5d 100644
--- a/ui/ozone/platform/wayland/host/wayland_toplevel_window.cc
+++ b/ui/ozone/platform/wayland/host/wayland_toplevel_window.cc
@@ -518,6 +518,16 @@
   self->OnDeskChanged(state);
 }
 
+void WaylandToplevelWindow::StartThrottle(void* data, zaura_surface* surface) {
+  WaylandToplevelWindow* self = static_cast<WaylandToplevelWindow*>(data);
+  self->delegate()->SetFrameRateThrottleEnabled(true);
+}
+
+void WaylandToplevelWindow::EndThrottle(void* data, zaura_surface* surface) {
+  WaylandToplevelWindow* self = static_cast<WaylandToplevelWindow*>(data);
+  self->delegate()->SetFrameRateThrottleEnabled(false);
+}
+
 bool WaylandToplevelWindow::RunMoveLoop(const gfx::Vector2d& drag_offset) {
   DCHECK(connection()->window_drag_controller());
   return connection()->window_drag_controller()->Drag(this, drag_offset);
@@ -775,11 +785,8 @@
   DCHECK(shell_toplevel_);
   if (connection()->zaura_shell() && !aura_surface_) {
     static constexpr zaura_surface_listener zaura_surface_listener = {
-        &OcclusionChanged,
-        &LockFrame,
-        &UnlockFrame,
-        &OcclusionStateChanged,
-        &DeskChanged,
+        &OcclusionChanged, &LockFrame,     &UnlockFrame, &OcclusionStateChanged,
+        &DeskChanged,      &StartThrottle, &EndThrottle,
     };
     aura_surface_.reset(zaura_shell_get_aura_surface(
         connection()->zaura_shell()->wl_object(), root_surface()->surface()));
diff --git a/ui/ozone/platform/wayland/host/wayland_toplevel_window.h b/ui/ozone/platform/wayland/host/wayland_toplevel_window.h
index 9e267914..5625ec8d 100644
--- a/ui/ozone/platform/wayland/host/wayland_toplevel_window.h
+++ b/ui/ozone/platform/wayland/host/wayland_toplevel_window.h
@@ -109,6 +109,8 @@
                                     zaura_surface* surface,
                                     uint32_t mode);
   static void DeskChanged(void* data, zaura_surface* surface, int state);
+  static void StartThrottle(void* data, zaura_surface* surface);
+  static void EndThrottle(void* data, zaura_surface* surface);
 
   // Calls UpdateWindowShape, set_input_region and set_opaque_region
   // for this toplevel window.
diff --git a/ui/ozone/platform/wayland/host/wayland_zaura_shell.cc b/ui/ozone/platform/wayland/host/wayland_zaura_shell.cc
index 1b5585f7..8f95db02 100644
--- a/ui/ozone/platform/wayland/host/wayland_zaura_shell.cc
+++ b/ui/ozone/platform/wayland/host/wayland_zaura_shell.cc
@@ -20,7 +20,7 @@
 
 namespace {
 constexpr uint32_t kMinVersion = 1;
-constexpr uint32_t kMaxVersion = 28;
+constexpr uint32_t kMaxVersion = 29;
 }
 
 // static
diff --git a/ui/platform_window/platform_window_delegate.cc b/ui/platform_window/platform_window_delegate.cc
index 1600f10..c72cba5 100644
--- a/ui/platform_window/platform_window_delegate.cc
+++ b/ui/platform_window/platform_window_delegate.cc
@@ -46,4 +46,6 @@
   return absl::nullopt;
 }
 
+void PlatformWindowDelegate::SetFrameRateThrottleEnabled(bool enabled) {}
+
 }  // namespace ui
diff --git a/ui/platform_window/platform_window_delegate.h b/ui/platform_window/platform_window_delegate.h
index 73b1ef6..931c605 100644
--- a/ui/platform_window/platform_window_delegate.h
+++ b/ui/platform_window/platform_window_delegate.h
@@ -135,6 +135,9 @@
   // in positioning child windows, which must be repositioned if the originally
   // intended position caused the surface to be constrained.
   virtual absl::optional<OwnedWindowAnchor> GetOwnedWindowAnchorAndRectInPx();
+
+  // Enables or disables frame rate throttling.
+  virtual void SetFrameRateThrottleEnabled(bool enabled);
 };
 
 }  // namespace ui
diff --git a/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar.html b/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar.html
index 87795c3..4d2e344 100644
--- a/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar.html
+++ b/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar.html
@@ -66,6 +66,10 @@
           --cr-icon-button-fill-color: currentColor;
           --cr-icon-button-focus-outline-color: white;
         }
+
+        :host-context([enable-branding-update]) cr-icon-button {
+          --cr-icon-button-focus-outline-color: var(--cr-focus-outline-color);
+        }
       }
 
       #centeredContent {
diff --git a/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar_search_field.html b/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar_search_field.html
index c854656..91237a31 100644
--- a/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar_search_field.html
+++ b/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar_search_field.html
@@ -31,6 +31,9 @@
           --cr-icon-button-fill-color: var(
               --cr-toolbar-search-field-input-icon-color,
               var(--google-grey-700));
+          --cr-icon-button-focus-outline-color: var(
+              --cr-toolbar-icon-button-focus-outline-color,
+              var(--cr-focus-outline-color));
         }
       }
 
diff --git a/url/url_canon.h b/url/url_canon.h
index ae113474..1c699f5 100644
--- a/url/url_canon.h
+++ b/url/url_canon.h
@@ -12,7 +12,6 @@
 
 #include "base/component_export.h"
 #include "base/export_template.h"
-#include "base/memory/raw_ptr.h"
 #include "url/third_party/mozilla/url_parse.h"
 
 namespace url {
@@ -140,7 +139,9 @@
     return true;
   }
 
-  raw_ptr<T> buffer_;
+  // `buffer_` is not a raw_ptr<...> for performance reasons (based on analysis
+  // of sampling profiler data).
+  T* buffer_;
   int buffer_len_;
 
   // Used characters in the buffer.
diff --git a/weblayer/test/BUILD.gn b/weblayer/test/BUILD.gn
index 72964702..c40e4cdb 100644
--- a/weblayer/test/BUILD.gn
+++ b/weblayer/test/BUILD.gn
@@ -75,8 +75,7 @@
     deps = [ "//third_party/icu:icu_assets" ]
     if (use_v8_context_snapshot) {
       deps += [ "//tools/v8_context_snapshot:v8_context_snapshot_assets" ]
-    }
-    if (!use_v8_context_snapshot || include_both_v8_snapshots) {
+    } else {
       deps += [ "//v8:v8_external_startup_data_assets" ]
     }
   }