diff --git a/DEPS b/DEPS
index 8c7900c..ff28dce 100644
--- a/DEPS
+++ b/DEPS
@@ -300,7 +300,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
-  'src_internal_revision': '81fbe9c25bdaf47289eb8fdd6a0405ccd372b82e',
+  'src_internal_revision': 'ecf5106d74f447df12fc97a4e374e05d005942ca',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
@@ -308,7 +308,7 @@
   # 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': 'ac7f43cd4bad11b77aaa46d0d6b5ec5d06bf716c',
+  'v8_revision': 'c42072b224c98f718f5ad17d3d54e10e2ba2f2c1',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
@@ -324,7 +324,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling BoringSSL
   # and whatever else without interference from each other.
-  'boringssl_revision': '72a60506ded3407454d6ddc1d848c266020c0c82',
+  'boringssl_revision': 'f8bb652b01d3b34a20ddbaaa35def260783ee734',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Fuchsia sdk
   # and whatever else without interference from each other.
@@ -396,7 +396,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': '986c1da746d466f1079e62047f7a176e244b51f4',
+  'devtools_frontend_revision': '01c5df59684df8989c4926853e9a26f897f38c72',
   # 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.
@@ -448,7 +448,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libavif
   # and whatever else without interference from each other.
-  'libavif_revision': '3d4c39cd8f6cb32264ad22616c3125942bd70768',
+  'libavif_revision': '9100176307d34dd843f76923a03c0a8b7b1769d7',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling crabbyavif
   # and whatever else without interference from each other.
@@ -1295,7 +1295,7 @@
 
   'src/clank': {
     'url': Var('chrome_git') + '/clank/internal/apps.git' + '@' +
-    'fd469e55225096ec457b144a1e051ed0604552e2',
+    'c26b94f4715522654c821b1ec6dbc77e241e1515',
     'condition': 'checkout_android and checkout_src_internal',
   },
 
@@ -1509,7 +1509,7 @@
       'packages': [
           {
                'package': 'chromium/third_party/android_build_tools/error_prone',
-               'version': 'YE3pEw2WCnBXxGYzRUWClmkwiGF1u9FtWzN_NpztxlgC',
+               'version': 'V6_XfH4kpaWINZD2dKHYp2_PuYLe0ay7fzbaY2HUrSMC',
           },
       ],
       'condition': 'checkout_android and non_git_source',
@@ -1657,7 +1657,7 @@
     Var('chromium_git') + '/external/github.com/chromium/content_analysis_sdk.git' + '@' + '9a408736204513e0e95dd2ab3c08de0d95963efc',
 
   'src/third_party/dav1d/libdav1d':
-    Var('chromium_git') + '/external/github.com/videolan/dav1d.git' + '@' + 'a7a40a3fde967f227d1ab00370141c491bc8e638',
+    Var('chromium_git') + '/external/github.com/videolan/dav1d.git' + '@' + 'ed004fe95d47e30e8248764fddbb08b77ba13187',
 
   'src/third_party/dawn':
     Var('dawn_git') + '/dawn.git' + '@' +  Var('dawn_revision'),
@@ -1768,13 +1768,13 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'b5210fdb7491d082965270b650c8080930b91c26',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '6dec85272d23ae587984cdd78eae428ce3b2ad9b',
 
   'src/third_party/devtools-frontend/src':
     Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'),
 
   'src/third_party/devtools-frontend-internal': {
-      'url': Var('chrome_git') + '/devtools/devtools-internal.git' + '@' + '3ed9eeaf4c42ea7a10232f1df5dcd94ea68452b5',
+      'url': Var('chrome_git') + '/devtools/devtools-internal.git' + '@' + '5ab078e56cd2ba95a134785b8c2ad8d43db28f19',
     'condition': 'checkout_src_internal',
   },
 
@@ -2049,7 +2049,7 @@
     Var('chromium_git') + '/external/libaddressinput.git' + '@' + 'e8712e415627f22d0b00ebee8db99547077f39bd',
 
   'src/third_party/libaom/source/libaom':
-    Var('aomedia_git') + '/aom.git' + '@' +  '3177866cdd22b0d1392e80681e907b68850903ea',
+    Var('aomedia_git') + '/aom.git' + '@' +  '3817481261f8675ef24f327b3dbcdcebbfa389e1',
 
   'src/third_party/libavif/src':
     Var('chromium_git') + '/external/github.com/AOMediaCodec/libavif.git' + '@' + Var('libavif_revision'),
@@ -2266,7 +2266,7 @@
     Var('pdfium_git') + '/pdfium.git' + '@' +  Var('pdfium_revision'),
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' + 'ba40a4dd8c1e0ccd0f6d5b567802a00b717562b4',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' + '71aaedeaea2b288f27d67d853fb7c4da9762796e',
 
   'src/base/tracing/test/data': {
     'bucket': 'perfetto',
@@ -2600,10 +2600,10 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + '1b6371436a0a60e6b9a4ae2a40a8eba198e3af02',
 
   'src/third_party/webgpu-cts/src':
-    Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + '1746bcbc10a809cbadb3b131675b885ed08d9da5',
+    Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + '9d029d337ae1832b5a7b9bf049a87076c12f749d',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '59ba4ef4b5502e177d3608a5ad62bcd343eb2a93',
+    Var('webrtc_git') + '/src.git' + '@' + '8c09a115e576e1a82ae7715fbc18ddc45609171e',
 
   # Wuffs' canonical repository is at github.com/google/wuffs, but we use
   # Skia's mirror of Wuffs, the same as in upstream Skia's DEPS file.
@@ -2743,7 +2743,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/help_app/app',
-        'version': 'Rn4JtVszfBfjZr7tj1o4yY7uFixA0zf3cizyCFT82vUC',
+        'version': 'RlxrAZbJURNWeJnj9fybs_LDcbvjGjPa82Jx5_rz6K8C',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
@@ -2754,7 +2754,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/media_app/app',
-        'version': 'EyeuSAaRE9TvYDbmBGb_7B8eQBp5MZEYdFNyVhZYamIC',
+        'version': 'uLcuCv4vP-zbrrt6V9EDkeKSlPWvYejOMIU3WfWaDcQC',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
@@ -4171,7 +4171,7 @@
 
   'src/chrome/browser/platform_experience/win': {
       'url': Var('chrome_git') + '/chrome/browser/platform_experience/win.git' + '@' +
-        '993e39a68a443c35b5b642f2e3ee844c26e1220a',
+        'f8bbf7f750c82f7463cebabcaee82182b709859f',
       'condition': 'checkout_src_internal',
   },
 
@@ -4385,7 +4385,7 @@
 
   'src/ios_internal':  {
       'url': Var('chrome_git') + '/chrome/ios_internal.git' + '@' +
-        '2859786c197bdbcd36e02c749007b8b55801e48a',
+        'f17fd6e8cd1a625d259c96d0dfcfdd41b0687ef1',
       'condition': 'checkout_ios and checkout_src_internal',
   },
 
diff --git a/android_webview/lib/aw_main_delegate.cc b/android_webview/lib/aw_main_delegate.cc
index 6e73257..74d5f96 100644
--- a/android_webview/lib/aw_main_delegate.cc
+++ b/android_webview/lib/aw_main_delegate.cc
@@ -247,12 +247,6 @@
 
 void AwMainDelegate::PreSandboxStartup() {
   TRACE_EVENT0("startup", "AwMainDelegate::PreSandboxStartup");
-#if defined(ARCH_CPU_ARM_FAMILY)
-  // Create an instance of the CPU class to parse /proc/cpuinfo and cache
-  // cpu_brand info.
-  base::CPU cpu_info;
-#endif
-
   const base::CommandLine& command_line =
       *base::CommandLine::ForCurrentProcess();
 
diff --git a/ash/birch/birch_coral_grouped_icon_image.cc b/ash/birch/birch_coral_grouped_icon_image.cc
index a745f32..8514cd56 100644
--- a/ash/birch/birch_coral_grouped_icon_image.cc
+++ b/ash/birch/birch_coral_grouped_icon_image.cc
@@ -41,11 +41,11 @@
 
 CoralGroupedIconImage::CoralGroupedIconImage(
     const std::vector<gfx::ImageSkia>& icon_images,
-    const int extra_tabs_number,
+    int extra_number,
     const ui::ColorProvider* color_provider)
     : gfx::CanvasImageSource(gfx::Size(kBackgroundSize, kBackgroundSize)),
       icon_images_(icon_images),
-      extra_tabs_number_(extra_tabs_number),
+      extra_number_(extra_number),
       color_provider_(color_provider) {}
 
 CoralGroupedIconImage::~CoralGroupedIconImage() = default;
@@ -53,16 +53,14 @@
 // static
 ui::ImageModel CoralGroupedIconImage::DrawCoralGroupedIconImage(
     const std::vector<gfx::ImageSkia>& icons_images,
-    int extra_tabs_number) {
+    int extra_number) {
   auto image_generator = base::BindRepeating(
-      [](const std::vector<gfx::ImageSkia>& icon_images,
-         const int extra_tabs_number,
+      [](const std::vector<gfx::ImageSkia>& icon_images, const int extra_number,
          const ui::ColorProvider* color_provider) -> gfx::ImageSkia {
         return gfx::CanvasImageSource::MakeImageSkia<CoralGroupedIconImage>(
-            icon_images, extra_tabs_number, color_provider);
+            icon_images, extra_number, color_provider);
       },
-      /*icon_images=*/icons_images,
-      /*extra_number=*/extra_tabs_number);
+      icons_images, extra_number);
 
   return ui::ImageModel::FromImageGenerator(
       image_generator, gfx::Size(kBackgroundSize, kBackgroundSize));
@@ -97,7 +95,7 @@
     return;
   }
 
-  if (icon_count == 3) {
+  if (icon_count == 3 && !extra_number_) {
     // Draw the top-left icon image.
     canvas->DrawImageInt(icon_images_[0], kIconPaddingSmall, kIconPaddingSmall);
     // Draw the top-right icon image.
@@ -108,7 +106,7 @@
     return;
   }
 
-  CHECK_GE(icon_count, 4u);
+  CHECK_GE(icon_count, 3u);
   // Draw the top-left icon image.
   canvas->DrawImageInt(icon_images_[0], kIconPaddingSmall, kIconPaddingSmall);
   // Draw the top-right icon image.
@@ -122,7 +120,7 @@
     return;
   }
 
-  // Draw the bottom-right extra tab label circular background.
+  // Draw the bottom-right extra number label circular background.
   flags.setColor(
       color_provider_->GetColor(cros_tokens::kCrosSysPrimaryContainer));
   int icon_four_midpoint =
@@ -138,7 +136,7 @@
   gfx::FontList font_list({"Google Sans"}, gfx::Font::NORMAL, 10,
                           gfx::Font::Weight::NORMAL);
   canvas->DrawStringRectWithFlags(
-      base::NumberToString16(extra_tabs_number_), font_list,
+      base::NumberToString16(extra_number_), font_list,
       color_provider_->GetColor(cros_tokens::kCrosSysOnPrimaryContainer),
       string_bounds, gfx::Canvas::TEXT_ALIGN_CENTER);
 }
diff --git a/ash/birch/birch_coral_grouped_icon_image.h b/ash/birch/birch_coral_grouped_icon_image.h
index b4e96fa0..b02a4fe 100644
--- a/ash/birch/birch_coral_grouped_icon_image.h
+++ b/ash/birch/birch_coral_grouped_icon_image.h
@@ -16,7 +16,7 @@
 class CoralGroupedIconImage : public gfx::CanvasImageSource {
  public:
   CoralGroupedIconImage(const std::vector<gfx::ImageSkia>& icon_images,
-                        const int extra_tabs_number,
+                        int extra_number,
                         const ui::ColorProvider* color_provider);
   CoralGroupedIconImage(const CoralGroupedIconImage&) = delete;
   CoralGroupedIconImage& operator=(const CoralGroupedIconImage&) = delete;
@@ -26,7 +26,7 @@
   // image used in `BirchCoralItem` based on the number of icons.
   static ui::ImageModel DrawCoralGroupedIconImage(
       const std::vector<gfx::ImageSkia>& icons_images,
-      const int extra_tabs_number);
+      int extra_number);
 
   // gfx::CanvasImageSource:
   void Draw(gfx::Canvas* canvas) override;
@@ -35,9 +35,9 @@
   // Represents the icon images (maximum of 4 non-unique icons) that will make
   // up the coral image.
   std::vector<gfx::ImageSkia> icon_images_;
-  // Represents the number of extra tabs that are part of the coral grouping,
-  // will be painted as a label in the coral image.
-  const int extra_tabs_number_;
+  // Represents the number of extra tabs or apps that are part of the coral
+  // grouping, will be painted as a label in the coral image.
+  const int extra_number_;
   raw_ptr<const ui::ColorProvider> color_provider_;
 };
 
diff --git a/ash/birch/birch_coral_item.cc b/ash/birch/birch_coral_item.cc
index b54d4bb..97b7007 100644
--- a/ash/birch/birch_coral_item.cc
+++ b/ash/birch/birch_coral_item.cc
@@ -23,6 +23,7 @@
 
 constexpr int kCoralIconSize = 14;
 constexpr int kCoralAppIconDesiredSize = 64;
+constexpr int kCoralMaxSubIconsNum = 4;
 
 // Callback for the favicon load request in `GetFaviconImageCoral()`. If the
 // load fails, passes an empty `ui::ImageModel` to the `barrier_callback`.
@@ -48,6 +49,7 @@
     std::move(barrier_callback)
         .Run(std::move(ui::ImageModel::FromImageSkia(image)));
   } else {
+    // TODO(zxdan): Define a backup icon for apps.
     std::move(barrier_callback).Run(ui::ImageModel());
   }
 }
@@ -57,10 +59,12 @@
 void OnAllFaviconsRetrievedCoral(
     base::OnceCallback<void(const ui::ImageModel&, SecondaryIconType)>
         final_callback,
+    int extra_number,
     const std::vector<ui::ImageModel>& loaded_icons) {
   std::vector<gfx::ImageSkia> resized_icons;
 
   for (const auto& loaded_icon : loaded_icons) {
+    // TODO(zxdan): Once all favicons have backup icons, change this to CHECK.
     if (!loaded_icon.IsEmpty()) {
       // Only a `ui::ImageModel` constructed from a `gfx::ImageSkia` produces a
       // valid result from `GetImage()`. Vector icons will not work.
@@ -71,10 +75,9 @@
     }
   }
 
-  // TODO(owenzhang): Hook up correct extra_number calculation.
   ui::ImageModel composed_image =
       CoralGroupedIconImage::DrawCoralGroupedIconImage(
-          /*icons_images=*/resized_icons, /*extra_tabs_number=*/7);
+          /*icons_images=*/resized_icons, extra_number);
 
   std::move(final_callback)
       .Run(std::move(composed_image), SecondaryIconType::kNoIcon);
@@ -153,23 +156,36 @@
 // TODO(b/362530155): Consider refactoring icon loading logic into
 // `CoralGroupedIconImage`.
 void BirchCoralItem::LoadIcon(LoadIconCallback original_callback) const {
-  // Barrier callback that collects the results of multiple favicon loads and
-  // runs the original load_icon callback.
-  const auto barrier_callback = base::BarrierCallback<const ui::ImageModel&>(
-      /*num_callbacks=*/page_urls_.size() + app_ids_.size(),
-      /*done_callback=*/base::BindOnce(OnAllFaviconsRetrievedCoral,
-                                       std::move(original_callback)));
+  const int page_num = page_urls_.size();
+  const int app_num = app_ids_.size();
+  const int total_count = page_num + app_num;
 
-  for (const auto& url : page_urls_) {
-    // For each `url`, retrieve the icon using favicon_service, and run the
+  // If the total number of pages and apps exceeds the limit of number of sub
+  // icons, only show 3 icons and one extra number label. Otherwise, show all
+  // the icons.
+  const int icon_requests =
+      total_count > kCoralMaxSubIconsNum ? 3 : total_count;
+
+  // Barrier callback that collects the results of multiple favicon loads and
+  // runs the original load icon callback.
+  const auto barrier_callback = base::BarrierCallback<const ui::ImageModel&>(
+      /*num_callbacks=*/icon_requests,
+      /*done_callback=*/base::BindOnce(
+          OnAllFaviconsRetrievedCoral, std::move(original_callback),
+          /*extra_number=*/total_count > icon_requests
+              ? total_count - icon_requests
+              : 0));
+
+  for (int i = 0; i < std::min(icon_requests, page_num); i++) {
+    // For each `url`, retrieve the icon using favicon service, and run the
     // `barrier_callback` with the image result.
-    GetFaviconImageCoral(url, barrier_callback);
+    GetFaviconImageCoral(page_urls_[i], barrier_callback);
   }
 
-  for (const auto& id : app_ids_) {
+  for (int i = 0; i < icon_requests - page_num; i++) {
     // For each `id`, retrieve the icon using `saved_desk_delegate`, and run the
     // `barrier_callback` with the image result.
-    GetAppIconCoral(id, barrier_callback);
+    GetAppIconCoral(app_ids_[i], barrier_callback);
   }
 }
 
diff --git a/ash/birch/birch_coral_provider.cc b/ash/birch/birch_coral_provider.cc
index a8f1cea..4598cc5a 100644
--- a/ash/birch/birch_coral_provider.cc
+++ b/ash/birch/birch_coral_provider.cc
@@ -160,18 +160,12 @@
 BirchCoralProvider::BirchCoralProvider(BirchModel* birch_model)
     : birch_model_(birch_model) {
   g_instance = this;
-
-  if (features::IsTabClusterUIEnabled()) {
-    Shell::Get()->tab_cluster_ui_controller()->AddObserver(this);
-  }
+  Shell::Get()->tab_cluster_ui_controller()->AddObserver(this);
   coral_item_remover_ = std::make_unique<CoralItemRemover>();
 }
 
 BirchCoralProvider::~BirchCoralProvider() {
-  if (features::IsTabClusterUIEnabled()) {
-    Shell::Get()->tab_cluster_ui_controller()->RemoveObserver(this);
-  }
-
+  Shell::Get()->tab_cluster_ui_controller()->RemoveObserver(this);
   g_instance = nullptr;
 }
 
diff --git a/ash/constants/ash_features.cc b/ash/constants/ash_features.cc
index 32677595..5c4851b 100644
--- a/ash/constants/ash_features.cc
+++ b/ash/constants/ash_features.cc
@@ -1571,7 +1571,7 @@
 // Make Sanitize available. This feature provides a "soft reset" option in CrOS
 // settings. This soft reset will disable extensions and reset some of the
 // settings to default.
-BASE_FEATURE(kSanitize, "CrosSanitize", base::FEATURE_DISABLED_BY_DEFAULT);
+BASE_FEATURE(kSanitize, "CrosSanitize", base::FEATURE_ENABLED_BY_DEFAULT);
 
 // When enabled, `SmbService` is created on user session startup task completed.
 BASE_FEATURE(kSmbServiceIsCreatedOnUserSessionStartUpTaskCompleted,
@@ -2981,9 +2981,6 @@
              "SystemProxyForSystemServices",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
-// Enables the UI to show tab cluster info.
-BASE_FEATURE(kTabClusterUI, "TabClusterUI", base::FEATURE_DISABLED_BY_DEFAULT);
-
 // Enables the UI to allow Chromebook hotspot functionality for experimental
 // carriers, modem and modem FW.
 BASE_FEATURE(kTetheringExperimentalFunctionality,
@@ -4807,10 +4804,6 @@
   return base::FeatureList::IsEnabled(kFeatureManagementTimeOfDayWallpaper);
 }
 
-bool IsTabClusterUIEnabled() {
-  return base::FeatureList::IsEnabled(kTabClusterUI);
-}
-
 bool IsTouchscreenMappingExperienceEnabled() {
   return base::FeatureList::IsEnabled(kEnableTouchscreenMappingExperience);
 }
diff --git a/ash/constants/ash_features.h b/ash/constants/ash_features.h
index bd574da..9a15a42 100644
--- a/ash/constants/ash_features.h
+++ b/ash/constants/ash_features.h
@@ -963,7 +963,6 @@
 COMPONENT_EXPORT(ASH_CONSTANTS) BASE_DECLARE_FEATURE(kSystemTrayShadow);
 COMPONENT_EXPORT(ASH_CONSTANTS)
 BASE_DECLARE_FEATURE(kSystemProxyForSystemServices);
-COMPONENT_EXPORT(ASH_CONSTANTS) BASE_DECLARE_FEATURE(kTabClusterUI);
 COMPONENT_EXPORT(ASH_CONSTANTS) BASE_DECLARE_FEATURE(kTelemetryExtension);
 COMPONENT_EXPORT(ASH_CONSTANTS)
 BASE_DECLARE_FEATURE(kTetheringExperimentalFunctionality);
@@ -1452,7 +1451,6 @@
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsTimeOfDayScreenSaverEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsTimeOfDayWallpaperEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS)
-COMPONENT_EXPORT(ASH_CONSTANTS) bool IsTabClusterUIEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsTouchscreenMappingExperienceEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsTouchpadInDiagnosticsAppEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsTouchscreenInDiagnosticsAppEnabled();
diff --git a/ash/shell.cc b/ash/shell.cc
index bdf1eb3..c4e0b37 100644
--- a/ash/shell.cc
+++ b/ash/shell.cc
@@ -1467,7 +1467,7 @@
   frame_throttling_controller_ = std::make_unique<FrameThrottlingController>(
       context_factory->GetHostFrameSinkManager());
 
-  if (features::IsTabClusterUIEnabled()) {
+  if (features::IsBirchCoralEnabled()) {
     tab_cluster_ui_controller_ = std::make_unique<TabClusterUIController>();
   }
 
diff --git a/ash/webui/boca_ui/boca_ui.cc b/ash/webui/boca_ui/boca_ui.cc
index 3ebcf65..b1df531 100644
--- a/ash/webui/boca_ui/boca_ui.cc
+++ b/ash/webui/boca_ui/boca_ui.cc
@@ -72,7 +72,8 @@
   // Enables the page to load images. The page is restricted to only loading
   // images from data URLs passed to the page.
   host_source->OverrideContentSecurityPolicy(
-      network::mojom::CSPDirectiveName::ImgSrc, "img-src data:;");
+      network::mojom::CSPDirectiveName::ImgSrc,
+      "img-src data: https://lh3.googleusercontent.com;");
 
   // For testing
   host_source->OverrideContentSecurityPolicy(
diff --git a/ash/webui/common/resources/sea_pen/constants.ts b/ash/webui/common/resources/sea_pen/constants.ts
index b187de6..ec3231b 100644
--- a/ash/webui/common/resources/sea_pen/constants.ts
+++ b/ash/webui/common/resources/sea_pen/constants.ts
@@ -21,6 +21,7 @@
 export type SeaPenImageId = number;
 
 export interface SeaPenSamplePrompt {
+  id: number;
   prompt: string;
   preview: Url;
 }
diff --git a/ash/webui/common/resources/sea_pen/sea_pen_metrics_logger.ts b/ash/webui/common/resources/sea_pen/sea_pen_metrics_logger.ts
index d11b662b..54eb2bf 100644
--- a/ash/webui/common/resources/sea_pen/sea_pen_metrics_logger.ts
+++ b/ash/webui/common/resources/sea_pen/sea_pen_metrics_logger.ts
@@ -7,6 +7,7 @@
 import {FreeformTab, QUERY, Query} from './constants.js';
 import {SeaPenTemplateId} from './sea_pen_generated.mojom-webui.js';
 import {SeaPenPaths} from './sea_pen_router_element.js';
+import {SeaPenSamplePromptId} from './sea_pen_untranslated_constants.js';
 import {isPersonalizationApp} from './sea_pen_utils.js';
 
 const WALLPAPER_FREEFORM = 999;
@@ -20,7 +21,8 @@
   SEA_PEN_SUGGESTION_CLICKED = `Ash.SeaPen.Freeform.Suggestion.Clicked`,
   SEA_PEN_SUGGESTION_SHUFFLE_CLICKED =
       `Ash.SeaPen.Freeform.Suggestion.Shuffle.Clicked`,
-  SEA_PEN_SAMPLE_PROMPT_CLICKED = `Ash.SeaPen.Freeform.SamplePrompt.Clicked`,
+  SEA_PEN_SAMPLE_PROMPT_CLICKED =
+      `Ash.SeaPen.Freeform.SamplePrompt.SampleClicked`,
   SEA_PEN_SAMPLE_PROMPT_SHUFFLE_CLICKED =
       `Ash.SeaPen.Freeform.SamplePrompt.Shuffle.Clicked`,
   SEA_PEN_FREEFORM_TAB_CLICKED = `Ash.SeaPen.Freeform.Tab.Clicked`,
@@ -122,9 +124,10 @@
       HistogramName.SEA_PEN_SUGGESTION_SHUFFLE_CLICKED, true);
 }
 
-export function logSamplePromptClicked() {
-  chrome.metricsPrivate.recordBoolean(
-      HistogramName.SEA_PEN_SAMPLE_PROMPT_CLICKED, true);
+export function logSamplePromptClicked(id: SeaPenSamplePromptId) {
+  chrome.metricsPrivate.recordEnumerationValue(
+      HistogramName.SEA_PEN_SAMPLE_PROMPT_CLICKED, id,
+      SeaPenSamplePromptId.MAX_VALUE);
 }
 
 export function logSamplePromptShuffleClicked() {
diff --git a/ash/webui/common/resources/sea_pen/sea_pen_samples_element.ts b/ash/webui/common/resources/sea_pen/sea_pen_samples_element.ts
index 89315d14..0e8ef1e 100644
--- a/ash/webui/common/resources/sea_pen/sea_pen_samples_element.ts
+++ b/ash/webui/common/resources/sea_pen/sea_pen_samples_element.ts
@@ -65,7 +65,7 @@
 
   private onClickSample_(e: WallpaperGridItemSelectedEvent&
                          {model: {sample: SeaPenSamplePrompt}}) {
-    logSamplePromptClicked();
+    logSamplePromptClicked(e.model.sample.id);
     this.dispatchEvent(new SeaPenSampleSelectedEvent(e.model.sample.prompt));
   }
 }
diff --git a/ash/webui/common/resources/sea_pen/sea_pen_untranslated_constants.ts b/ash/webui/common/resources/sea_pen/sea_pen_untranslated_constants.ts
index b84a506..ce3bf71 100644
--- a/ash/webui/common/resources/sea_pen/sea_pen_untranslated_constants.ts
+++ b/ash/webui/common/resources/sea_pen/sea_pen_untranslated_constants.ts
@@ -37,172 +37,221 @@
   '3D render',
 ];
 
-export const SEA_PEN_SAMPLES: SeaPenSamplePrompt[] = [
-  {
-    prompt:
-        '3D rendering of chrome spheres and sphere shapes on a background of chrome architectural elements. The composition is a modern minimalistic concept for advertising, branding or presentation with copy space. A large arch stands in front of an abstract background in the style of minimalism. Studio lighting. Closeup shot.',
-    preview: {
-      url:
-          'https://www.gstatic.com/chromecast/home/chromeos/sea_pen/freeform/3d_rendering_of_chrome_spheres.jpeg',
-    },
-  },
-  {
-    prompt:
-        'a beautiful cosmic nebula galaxy with a fast futuristic spaceship, blue, pink and purple hues',
-    preview: {
-      url:
-          'https://www.gstatic.com/chromecast/home/chromeos/sea_pen/freeform/a_beautiful_cosmic_nebula_galaxy_with_a_fast_futuristic_spaceship.jpeg',
-    },
-  },
-  {
-    prompt:
-        'a cat playing a flying v while riding a unicorn, with a lightning bolt in the background',
-    preview: {
-      url:
-          'https://www.gstatic.com/chromecast/home/chromeos/sea_pen/freeform/a_cat_playing_a_flying_v_while_riding_a_unicorn.jpeg',
-    },
-  },
-  {
-    prompt:
-        'a close up of several white and yellow flowers against a purple background, a still from an animated film, a delicate pale pink flower with long petals and a green stem placed between two large yellow tulips against a blue sky with no leaves or foliage in pastel colors, a cinematic still shot with high resolution and very detailed intricate details, octane render with beautiful lighting, volumetric light and moody light.',
-    preview: {
-      url:
-          'https://www.gstatic.com/chromecast/home/chromeos/sea_pen/freeform/a_close_up_of_several_white_and_yellow_flowers.jpeg',
-    },
-  },
-  {
-    prompt:
-        'a closeup of an intricate, dew-covered pond lily in the rain. The focus is on the delicate petals and light-reflecting water droplets, capturing their soft pastel colors against a blue background. Shot from eye level using natural light',
-    preview: {
-      url:
-          'https://www.gstatic.com/chromecast/home/chromeos/sea_pen/freeform/a_closeup_of_an_intricate_dew-covered_pond_lily.jpeg',
-    },
-  },
-  {
-    prompt:
-        'a colorful treehouse with rounded shapes, rendered in the style of cinema4d, playful character design, futuristic chromatic waves, organic architecture, 3D render, cute and dreamy scene with a bright color palette using vibrant colors like pink, blue, green, orange, red, and yellow, purple, shown from the front view without a background and in high resolution',
-    preview: {
-      url:
-          'https://www.gstatic.com/chromecast/home/chromeos/sea_pen/freeform/a_colorful_treehouse_with_rounded_shapes.jpeg',
-    },
-  },
-  {
-    prompt:
-        'a dalmatian dog in front of a pink background in a full body dynamic pose, shot with high resolution photography hyper realistic stock background with color grading',
-    preview: {
-      url:
-          'https://www.gstatic.com/chromecast/home/chromeos/sea_pen/freeform/a_dalmatian_dog.jpeg',
-    },
-  },
-  {
-    prompt:
-        'a Delorean with neon lights driving through the galaxy with stars whizzing by',
-    preview: {
-      url:
-          'https://www.gstatic.com/chromecast/home/chromeos/sea_pen/freeform/a_delorean_with_neon_lights.jpeg',
-    },
-  },
-  {
-    prompt:
-        'a futuristic motorcycle made of black paper with neon lights dark background',
-    preview: {
-      url:
-          'https://www.gstatic.com/chromecast/home/chromeos/sea_pen/freeform/a_futuristic_motorcycle_made_of_black_paper.jpeg',
-    },
-  },
-  {
-    prompt:
-        'A huge spaceship floats over the city, which is illuminated by many lights. The entire scene has an oil painting style and presents dark tones. It creates a mysterious atmosphere with futuristic sci-fi elements. This artwork was created using C4D software and OC renderer to highlight high resolution and high details. There were also some urban buildings in close range, adding a depth of field effect. The scene is rendered in the style of an oil painting, presenting dark tones and creating a mysterious atmosphere with futuristic sci-fi elements.',
-    preview: {
-      url:
-          'https://www.gstatic.com/chromecast/home/chromeos/sea_pen/freeform/a_huge_spaceship_floats_over_the_city.jpeg',
-    },
-  },
-  {
-    prompt:
-        'a Japanese animation illustration of a cat sitting on a windowsill watching the rain in the afternoon',
-    preview: {
-      url:
-          'https://www.gstatic.com/chromecast/home/chromeos/sea_pen/freeform/a_japanese_animation_illustration_of_a_cat_sitting.jpeg',
-    },
-  },
-  {
-    prompt:
-        'a long exposure photo of a bioluminescent beach at sunset with stars in the sky, beautiful pastel colors, 4k',
-    preview: {
-      url:
-          'https://www.gstatic.com/chromecast/home/chromeos/sea_pen/freeform/a_long_exposure_photo_of_a_bioluminescent_beach.jpeg',
-    },
-  },
-  {
-    prompt:
-        'a minimal professional photo of glittery black sand dunes with dramatic shadows',
-    preview: {
-      url:
-          'https://www.gstatic.com/chromecast/home/chromeos/sea_pen/freeform/a_minimal_professional_photo_of_glittery_black_sand.jpeg',
-    },
-  },
-  {
-    prompt:
-        'a photo of a tree made of stars with a beautiful night sky and galaxy in the background',
-    preview: {
-      url:
-          'https://www.gstatic.com/chromecast/home/chromeos/sea_pen/freeform/a_photo_of_a_tree_made_of_stars.jpeg',
-    },
-  },
-  {
-    prompt:
-        'a photograph of a moon over a lake with falling cherry blossom leaves, 4k',
-    preview: {
-      url:
-          'https://www.gstatic.com/chromecast/home/chromeos/sea_pen/freeform/a_photograph_of_a_moon_over_a_lake.jpeg',
-    },
-  },
-  {
-    prompt:
-        'a piece of white organza floating in the middle, above a white marble arch with a cream background',
-    preview: {
-      url:
-          'https://www.gstatic.com/chromecast/home/chromeos/sea_pen/freeform/a_piece_of_white_organza_floating_in_the_middle.jpeg',
-    },
-  },
-  {
-    prompt: 'a steampunk space ship flying through a sand storm',
-    preview: {
-      url:
-          'https://www.gstatic.com/chromecast/home/chromeos/sea_pen/freeform/a_steampunk_space_ship_flying_through.jpeg',
-    },
-  },
-  {
-    prompt:
-        'a surreal white tiger with gold eyes and gold stripes sitting on a white sand dune',
-    preview: {
-      url:
-          'https://www.gstatic.com/chromecast/home/chromeos/sea_pen/freeform/a_surreal_white_tiger_with_gold_eyes.jpeg',
-    },
-  },
-  {
-    prompt:
-        'anime path overlooking the ocean with falling cherry blossom leaves',
-    preview: {
-      url:
-          'https://www.gstatic.com/chromecast/home/chromeos/sea_pen/freeform/anime_path_overlooking_the_ocean.jpeg',
-    },
-  },
-  {
-    prompt:
-        'minimal still life of a few Papaver rhea stems in a glass vase on a black background',
-    preview: {
-      url:
-          'https://www.gstatic.com/chromecast/home/chromeos/sea_pen/freeform/minimal_still_life_of_a_few_Papaver_rhea_stems.jpeg',
-    },
-  },
-  {
-    prompt:
-        'photo of a night sky with a meteor shower and a few trees in the foreground, with purple and pink hues',
-    preview: {
-      url:
-          'https://www.gstatic.com/chromecast/home/chromeos/sea_pen/freeform/photo_of_a_night_sky_with_a_meteor_shower.jpeg',
-    },
-  },
-];
+// These values are used for metrics; do not change or reuse values.
+export enum SeaPenSamplePromptId {
+  CHROME_SPHERES = 0,
+  GALAXY_WITH_SPACESHIP = 1,
+  CAT_RIDING_UNICORN = 2,
+  ANIMATED_FLOWERS = 3,
+  LILY_IN_RAIN = 4,
+  COLORFUL_TREEHOUSE = 5,
+  DALMATION = 6,
+  DELOREAN = 7,
+  BLACK_MOTORCYCLE = 8,
+  SPACESHIP_OVER_CITY = 9,
+  CAT_ON_WINDOWSILL = 10,
+  BIOLUMINESCENT_BEACH = 11,
+  BLACK_SAND_DUNES = 12,
+  TREE_MADE_OF_STARS = 13,
+  MOON_OVER_LAKE = 14,
+  MARBLE_ARCH = 15,
+  STEAMPUNK_SPACESHIP = 16,
+  WHITE_TIGER = 17,
+  ANIME_PATH_OVERLOOKING_OCEAN = 18,
+  PAPAVER_RHEA_STEMS = 19,
+  METEOR_SHOWER = 20,
+
+  MAX_VALUE = METEOR_SHOWER,
+}
+
+export const SEA_PEN_SAMPLES: SeaPenSamplePrompt[] =
+    [
+      {
+        id: SeaPenSamplePromptId.CHROME_SPHERES,
+        prompt:
+            '3D rendering of chrome spheres and sphere shapes on a background of chrome architectural elements. The composition is a modern minimalistic concept for advertising, branding or presentation with copy space. A large arch stands in front of an abstract background in the style of minimalism. Studio lighting. Closeup shot.',
+        preview: {
+          url:
+              'https://www.gstatic.com/chromecast/home/chromeos/sea_pen/freeform/3d_rendering_of_chrome_spheres.jpeg',
+        },
+      },
+      {
+        id: SeaPenSamplePromptId.GALAXY_WITH_SPACESHIP,
+        prompt:
+            'a beautiful cosmic nebula galaxy with a fast futuristic spaceship, blue, pink and purple hues',
+        preview: {
+          url:
+              'https://www.gstatic.com/chromecast/home/chromeos/sea_pen/freeform/a_beautiful_cosmic_nebula_galaxy_with_a_fast_futuristic_spaceship.jpeg',
+        },
+      },
+      {
+        id: SeaPenSamplePromptId.CAT_RIDING_UNICORN,
+        prompt:
+            'a cat playing a flying v while riding a unicorn, with a lightning bolt in the background',
+        preview: {
+          url:
+              'https://www.gstatic.com/chromecast/home/chromeos/sea_pen/freeform/a_cat_playing_a_flying_v_while_riding_a_unicorn.jpeg',
+        },
+      },
+      {
+        id: SeaPenSamplePromptId.ANIMATED_FLOWERS,
+        prompt:
+            'a close up of several white and yellow flowers against a purple background, a still from an animated film, a delicate pale pink flower with long petals and a green stem placed between two large yellow tulips against a blue sky with no leaves or foliage in pastel colors, a cinematic still shot with high resolution and very detailed intricate details, octane render with beautiful lighting, volumetric light and moody light.',
+        preview: {
+          url:
+              'https://www.gstatic.com/chromecast/home/chromeos/sea_pen/freeform/a_close_up_of_several_white_and_yellow_flowers.jpeg',
+        },
+      },
+      {
+        id: SeaPenSamplePromptId.LILY_IN_RAIN,
+        prompt:
+            'a closeup of an intricate, dew-covered pond lily in the rain. The focus is on the delicate petals and light-reflecting water droplets, capturing their soft pastel colors against a blue background. Shot from eye level using natural light',
+        preview: {
+          url:
+              'https://www.gstatic.com/chromecast/home/chromeos/sea_pen/freeform/a_closeup_of_an_intricate_dew-covered_pond_lily.jpeg',
+        },
+      },
+      {
+        id: SeaPenSamplePromptId.COLORFUL_TREEHOUSE,
+        prompt:
+            'a colorful treehouse with rounded shapes, rendered in the style of cinema4d, playful character design, futuristic chromatic waves, organic architecture, 3D render, cute and dreamy scene with a bright color palette using vibrant colors like pink, blue, green, orange, red, and yellow, purple, shown from the front view without a background and in high resolution',
+        preview: {
+          url:
+              'https://www.gstatic.com/chromecast/home/chromeos/sea_pen/freeform/a_colorful_treehouse_with_rounded_shapes.jpeg',
+        },
+      },
+      {
+        id: SeaPenSamplePromptId.DALMATION,
+        prompt:
+            'a dalmatian dog in front of a pink background in a full body dynamic pose, shot with high resolution photography hyper realistic stock background with color grading',
+        preview: {
+          url:
+              'https://www.gstatic.com/chromecast/home/chromeos/sea_pen/freeform/a_dalmatian_dog.jpeg',
+        },
+      },
+      {
+        id: SeaPenSamplePromptId.DELOREAN,
+        prompt:
+            'a Delorean with neon lights driving through the galaxy with stars whizzing by',
+        preview: {
+          url:
+              'https://www.gstatic.com/chromecast/home/chromeos/sea_pen/freeform/a_delorean_with_neon_lights.jpeg',
+        },
+      },
+      {
+        id: SeaPenSamplePromptId.BLACK_MOTORCYCLE,
+        prompt:
+            'a futuristic motorcycle made of black paper with neon lights dark background',
+        preview: {
+          url:
+              'https://www.gstatic.com/chromecast/home/chromeos/sea_pen/freeform/a_futuristic_motorcycle_made_of_black_paper.jpeg',
+        },
+      },
+      {
+        id: SeaPenSamplePromptId.SPACESHIP_OVER_CITY,
+        prompt:
+            'A huge spaceship floats over the city, which is illuminated by many lights. The entire scene has an oil painting style and presents dark tones. It creates a mysterious atmosphere with futuristic sci-fi elements. This artwork was created using C4D software and OC renderer to highlight high resolution and high details. There were also some urban buildings in close range, adding a depth of field effect. The scene is rendered in the style of an oil painting, presenting dark tones and creating a mysterious atmosphere with futuristic sci-fi elements.',
+        preview: {
+          url:
+              'https://www.gstatic.com/chromecast/home/chromeos/sea_pen/freeform/a_huge_spaceship_floats_over_the_city.jpeg',
+        },
+      },
+      {
+        id: SeaPenSamplePromptId.CAT_ON_WINDOWSILL,
+        prompt:
+            'a Japanese animation illustration of a cat sitting on a windowsill watching the rain in the afternoon',
+        preview: {
+          url:
+              'https://www.gstatic.com/chromecast/home/chromeos/sea_pen/freeform/a_japanese_animation_illustration_of_a_cat_sitting.jpeg',
+        },
+      },
+      {
+        id: SeaPenSamplePromptId.BIOLUMINESCENT_BEACH,
+        prompt:
+            'a long exposure photo of a bioluminescent beach at sunset with stars in the sky, beautiful pastel colors, 4k',
+        preview: {
+          url:
+              'https://www.gstatic.com/chromecast/home/chromeos/sea_pen/freeform/a_long_exposure_photo_of_a_bioluminescent_beach.jpeg',
+        },
+      },
+      {
+        id: SeaPenSamplePromptId.BLACK_SAND_DUNES,
+        prompt:
+            'a minimal professional photo of glittery black sand dunes with dramatic shadows',
+        preview: {
+          url:
+              'https://www.gstatic.com/chromecast/home/chromeos/sea_pen/freeform/a_minimal_professional_photo_of_glittery_black_sand.jpeg',
+        },
+      },
+      {
+        id: SeaPenSamplePromptId.TREE_MADE_OF_STARS,
+        prompt:
+            'a photo of a tree made of stars with a beautiful night sky and galaxy in the background',
+        preview: {
+          url:
+              'https://www.gstatic.com/chromecast/home/chromeos/sea_pen/freeform/a_photo_of_a_tree_made_of_stars.jpeg',
+        },
+      },
+      {
+        id: SeaPenSamplePromptId.MOON_OVER_LAKE,
+        prompt:
+            'a photograph of a moon over a lake with falling cherry blossom leaves, 4k',
+        preview: {
+          url:
+              'https://www.gstatic.com/chromecast/home/chromeos/sea_pen/freeform/a_photograph_of_a_moon_over_a_lake.jpeg',
+        },
+      },
+      {
+        id: SeaPenSamplePromptId.MARBLE_ARCH,
+        prompt:
+            'a piece of white organza floating in the middle, above a white marble arch with a cream background',
+        preview: {
+          url:
+              'https://www.gstatic.com/chromecast/home/chromeos/sea_pen/freeform/a_piece_of_white_organza_floating_in_the_middle.jpeg',
+        },
+      },
+      {
+        id: SeaPenSamplePromptId.STEAMPUNK_SPACESHIP,
+        prompt: 'a steampunk space ship flying through a sand storm',
+        preview: {
+          url:
+              'https://www.gstatic.com/chromecast/home/chromeos/sea_pen/freeform/a_steampunk_space_ship_flying_through.jpeg',
+        },
+      },
+      {
+        id: SeaPenSamplePromptId.WHITE_TIGER,
+        prompt:
+            'a surreal white tiger with gold eyes and gold stripes sitting on a white sand dune',
+        preview: {
+          url:
+              'https://www.gstatic.com/chromecast/home/chromeos/sea_pen/freeform/a_surreal_white_tiger_with_gold_eyes.jpeg',
+        },
+      },
+      {
+        id: SeaPenSamplePromptId.ANIME_PATH_OVERLOOKING_OCEAN,
+        prompt:
+            'anime path overlooking the ocean with falling cherry blossom leaves',
+        preview: {
+          url:
+              'https://www.gstatic.com/chromecast/home/chromeos/sea_pen/freeform/anime_path_overlooking_the_ocean.jpeg',
+        },
+      },
+      {
+        id: SeaPenSamplePromptId.PAPAVER_RHEA_STEMS,
+        prompt:
+            'minimal still life of a few Papaver rhea stems in a glass vase on a black background',
+        preview: {
+          url:
+              'https://www.gstatic.com/chromecast/home/chromeos/sea_pen/freeform/minimal_still_life_of_a_few_Papaver_rhea_stems.jpeg',
+        },
+      },
+      {
+        id: SeaPenSamplePromptId.METEOR_SHOWER,
+        prompt:
+            'photo of a night sky with a meteor shower and a few trees in the foreground, with purple and pink hues',
+        preview: {
+          url:
+              'https://www.gstatic.com/chromecast/home/chromeos/sea_pen/freeform/photo_of_a_night_sky_with_a_meteor_shower.jpeg',
+        },
+      },
+    ];
diff --git a/ash/wm/overview/overview_focus_cycler.cc b/ash/wm/overview/overview_focus_cycler.cc
index 4c18d41..621b795 100644
--- a/ash/wm/overview/overview_focus_cycler.cc
+++ b/ash/wm/overview/overview_focus_cycler.cc
@@ -263,7 +263,9 @@
 
   auto maybe_add_widget = [for_accessibility,
                            &traversable_widgets](views::Widget* widget) {
-    if (!widget ||
+    // The desks bar is invisible when in splitscreen. This is because of the
+    // high cost of creating and destroying it.
+    if (!widget || !widget->IsVisible() ||
         widget->GetNativeWindow()->layer()->GetTargetOpacity() == 0.f) {
       return;
     }
diff --git a/ash/wm/overview/overview_focus_cycler_unittest.cc b/ash/wm/overview/overview_focus_cycler_unittest.cc
index fb96d8da..0d63d16 100644
--- a/ash/wm/overview/overview_focus_cycler_unittest.cc
+++ b/ash/wm/overview/overview_focus_cycler_unittest.cc
@@ -19,6 +19,7 @@
 #include "ash/wm/overview/overview_item_view.h"
 #include "ash/wm/overview/overview_test_base.h"
 #include "ash/wm/overview/overview_test_util.h"
+#include "ash/wm/splitview/split_view_setup_view.h"
 #include "ash/wm/tablet_mode/tablet_mode_controller_test_api.h"
 #include "ash/wm/window_util.h"
 #include "base/test/scoped_feature_list.h"
@@ -345,6 +346,37 @@
   EXPECT_EQ(item3->item_widget()->GetContentsView(), GetFocusedView());
 }
 
+// Tests that the locations of the overview focus ring are as expected when
+// tabbing through split view setup UI. This tests the case were splitview is
+// entered via snapping a window while already in overview. Regression test for
+// http://b/369539129 for more details.
+TEST_P(OverviewFocusCyclerTest, TabbingWithSplitview) {
+  std::unique_ptr<aura::Window> window1 = CreateAppWindow();
+  std::unique_ptr<aura::Window> window2 = CreateAppWindow();
+
+  ToggleOverview();
+  SplitViewController::Get(Shell::GetPrimaryRootWindow())
+      ->SnapWindow(window1.get(), SnapPosition::kPrimary);
+
+  // Tab once to focus `item2`.
+  PressAndReleaseKey(ui::VKEY_TAB);
+  EXPECT_EQ(
+      GetOverviewItemForWindow(window2.get())->item_widget()->GetContentsView(),
+      GetFocusedView());
+
+  // Tab to the toast dismiss button and then the settings button.
+  OverviewGrid* grid = GetOverviewSession()->grid_list()[0].get();
+  PressAndReleaseKey(ui::VKEY_TAB);
+  EXPECT_EQ(grid->GetSplitViewSetupView()->GetViewByID(
+                SplitViewSetupView::kDismissButtonIDForTest),
+            GetFocusedView());
+
+  PressAndReleaseKey(ui::VKEY_TAB);
+  EXPECT_EQ(grid->GetSplitViewSetupView()->GetViewByID(
+                SplitViewSetupView::kSettingsButtonIDForTest),
+            GetFocusedView());
+}
+
 // ----------------------------------------------------------------------------
 // DesksOverviewFocusCyclerTest:
 
diff --git a/base/BUILD.gn b/base/BUILD.gn
index 8cf35e71..373e250 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -2034,6 +2034,7 @@
       "mac/close_nocancel.cc",
       "mac/code_signature.cc",
       "mac/code_signature.h",
+      "mac/code_signature_spi.h",
       "mac/info_plist_data.h",
       "mac/info_plist_data.mm",
       "mac/launch_application.h",
@@ -2049,6 +2050,8 @@
       "mac/mach_port_rendezvous.h",
       "mac/os_crash_dumps.cc",
       "mac/os_crash_dumps.h",
+      "mac/process_requirement.cc",
+      "mac/process_requirement.h",
       "mac/scoped_aedesc.h",
       "mac/scoped_authorizationref.h",
       "mac/scoped_authorizationref.mm",
@@ -3553,6 +3556,7 @@
   }
 
   defines = []
+  frameworks = []
 
   deps = [
     ":base",
@@ -3881,6 +3885,7 @@
       "mac/launch_application_unittest.mm",
       "mac/mac_util_unittest.mm",
       "mac/mach_port_rendezvous_unittest.cc",
+      "mac/process_requirement_unittest.cc",
       "mac/scoped_sending_event_unittest.mm",
       "message_loop/message_pump_apple_unittest.mm",
       "power_monitor/thermal_state_observer_mac_unittest.mm",
@@ -3889,6 +3894,7 @@
       "system/sys_info_mac_unittest.mm",
     ]
     data_deps += [ "//base/mac:launch_application_test_helper" ]
+    frameworks += [ "Security.framework" ]
   }
 
   if (is_ios) {
@@ -3938,7 +3944,7 @@
 
   if (is_mac) {
     sources += [ "message_loop/message_pump_kqueue_unittest.cc" ]
-    frameworks = [
+    frameworks += [
       "CoreFoundation.framework",
       "Foundation.framework",
     ]
diff --git a/base/cpu.cc b/base/cpu.cc
index 7dd34ea..4a85be7 100644
--- a/base/cpu.cc
+++ b/base/cpu.cc
@@ -34,12 +34,6 @@
 #define HWCAP2_MTE (1 << 18)
 #define HWCAP2_BTI (1 << 17)
 #endif
-
-struct ProcCpuInfo {
-  std::string brand;
-  uint8_t implementer = 0;
-  uint32_t part_number = 0;
-};
 #endif
 
 #if defined(ARCH_CPU_X86_FAMILY)
@@ -90,10 +84,10 @@
 }  // namespace internal
 #endif  // defined(ARCH_CPU_X86_FAMILY)
 
-CPU::CPU(bool require_branding) {
-  Initialize(require_branding);
+CPU::CPU() {
+  Initialize();
 }
-CPU::CPU() : CPU(true) {}
+
 CPU::CPU(CPU&&) = default;
 
 namespace {
@@ -153,79 +147,11 @@
 
 #endif  // ARCH_CPU_X86_FAMILY
 
-#if defined(ARCH_CPU_ARM_FAMILY) && \
-    (BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS))
-StringPairs::const_iterator FindFirstProcCpuKey(const StringPairs& pairs,
-                                                std::string_view key) {
-  return ranges::find_if(pairs, [key](const StringPairs::value_type& pair) {
-    return TrimWhitespaceASCII(pair.first, base::TRIM_ALL) == key;
-  });
-}
-
-// Parses information about the ARM processor. Note that depending on the CPU
-// package, processor configuration, and/or kernel version, this may only
-// report information about the processor on which this thread is running. This
-// can happen on heterogeneous-processor SoCs like Snapdragon 808, which has 4
-// Cortex-A53 and 2 Cortex-A57. Unfortunately there is not a universally
-// reliable way to examine the CPU part information for all cores.
-const ProcCpuInfo& ParseProcCpu() {
-  static const NoDestructor<ProcCpuInfo> info([]() {
-    // This function finds the value from /proc/cpuinfo under the key "model
-    // name" or "Processor". "model name" is used in Linux 3.8 and later (3.7
-    // and later for arm64) and is shown once per CPU. "Processor" is used in
-    // earler versions and is shown only once at the top of /proc/cpuinfo
-    // regardless of the number CPUs.
-    const char kModelNamePrefix[] = "model name";
-    const char kProcessorPrefix[] = "Processor";
-
-    std::string cpuinfo;
-    ReadFileToString(FilePath("/proc/cpuinfo"), &cpuinfo);
-    DCHECK(!cpuinfo.empty());
-
-    ProcCpuInfo info;
-
-    StringPairs pairs;
-    if (!SplitStringIntoKeyValuePairs(cpuinfo, ':', '\n', &pairs)) {
-      // TODO(crbug.com/368077955): This still hits and needs to be diagnosed.
-      DUMP_WILL_BE_NOTREACHED() << cpuinfo;
-      return info;
-    }
-
-    auto model_name = FindFirstProcCpuKey(pairs, kModelNamePrefix);
-    if (model_name == pairs.end())
-      model_name = FindFirstProcCpuKey(pairs, kProcessorPrefix);
-    if (model_name != pairs.end()) {
-      TrimWhitespaceASCII(model_name->second, TRIM_ALL, &info.brand);
-    }
-
-    auto implementer_string = FindFirstProcCpuKey(pairs, "CPU implementer");
-    if (implementer_string != pairs.end()) {
-      // HexStringToUInt() handles the leading whitespace on the value.
-      uint32_t implementer;
-      HexStringToUInt(implementer_string->second, &implementer);
-      if (!CheckedNumeric<uint32_t>(implementer)
-               .AssignIfValid(&info.implementer)) {
-        info.implementer = 0;
-      }
-    }
-
-    auto part_number_string = FindFirstProcCpuKey(pairs, "CPU part");
-    if (part_number_string != pairs.end())
-      HexStringToUInt(part_number_string->second, &info.part_number);
-
-    return info;
-  }());
-
-  return *info;
-}
-#endif  // defined(ARCH_CPU_ARM_FAMILY) && (BUILDFLAG(IS_ANDROID) ||
-        // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS))
-
 DEFINE_PROTECTED_DATA base::ProtectedMemory<CPU> g_cpu_instance;
 
 }  // namespace
 
-void CPU::Initialize(bool require_branding) {
+void CPU::Initialize() {
 #if defined(ARCH_CPU_X86_FAMILY)
   int cpu_info[4] = {-1};
 
@@ -358,26 +284,12 @@
     }
   }
 #elif defined(ARCH_CPU_ARM_FAMILY)
-#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
-  if (require_branding) {
-    const ProcCpuInfo& info = ParseProcCpu();
-
-    // Ensure the brand can be stored in the internal array.
-    SpanWriter writer{span(cpu_brand_)};
-    writer.Write(span(info.brand));
-    writer.Write('\0');
-
-    implementer_ = info.implementer;
-    part_number_ = info.part_number;
-  }
-
-#if defined(ARCH_CPU_ARM64)
+#if defined(ARCH_CPU_ARM64) && \
+    (BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS))
   // Check for Armv8.5-A BTI/MTE support, exposed via HWCAP2
   unsigned long hwcap2 = getauxval(AT_HWCAP2);
   has_mte_ = hwcap2 & HWCAP2_MTE;
   has_bti_ = hwcap2 & HWCAP2_BTI;
-#endif
-
 #elif BUILDFLAG(IS_WIN)
   // Windows makes high-resolution thread timing information available in
   // user-space.
@@ -406,7 +318,7 @@
 #endif
 
 const CPU& CPU::GetInstanceNoAllocation() {
-  static ProtectedMemoryInitializer cpu_initializer(g_cpu_instance, CPU(false));
+  static ProtectedMemoryInitializer cpu_initializer(g_cpu_instance, CPU());
 
   return *g_cpu_instance;
 }
diff --git a/base/cpu.h b/base/cpu.h
index 39df0c6..064a6f60 100644
--- a/base/cpu.h
+++ b/base/cpu.h
@@ -101,15 +101,6 @@
   }
   bool is_running_in_vm() const { return is_running_in_vm_; }
 
-#if defined(ARCH_CPU_ARM_FAMILY)
-  // The cpuinfo values for ARM cores are from the MIDR_EL1 register, a
-  // bitfield whose format is described in the core-specific manuals. E.g.,
-  // ARM Cortex-A57:
-  // https://developer.arm.com/documentation/ddi0488/h/system-control/aarch64-register-descriptions/main-id-register--el1.
-  uint8_t implementer() const { return implementer_; }
-  uint32_t part_number() const { return part_number_; }
-#endif
-
   // Armv8.5-A extensions for control flow and memory safety.
 #if defined(ARCH_CPU_ARM_FAMILY)
   bool has_mte() const { return has_mte_; }
@@ -133,8 +124,7 @@
 
  private:
   // Query the processor for CPUID information.
-  void Initialize(bool requires_branding);
-  explicit CPU(bool requires_branding);
+  void Initialize();
 
   int signature_ = 0;  // raw form of type, family, model, and stepping
   int type_ = 0;       // process type
@@ -143,10 +133,6 @@
   int stepping_ = 0;   // processor revision number
   int ext_model_ = 0;
   int ext_family_ = 0;
-#if defined(ARCH_CPU_ARM_FAMILY)
-  uint32_t part_number_ = 0;  // ARM MIDR part number
-  uint8_t implementer_ = 0;   // ARM MIDR implementer identifier
-#endif
 #if defined(ARCH_CPU_X86_FAMILY)
   bool has_mmx_ = false;
   bool has_sse_ = false;
diff --git a/base/cpu_unittest.cc b/base/cpu_unittest.cc
index aa575be4..4c1d5a808 100644
--- a/base/cpu_unittest.cc
+++ b/base/cpu_unittest.cc
@@ -181,22 +181,6 @@
 }
 #endif  // defined(ARCH_CPU_X86_FAMILY)
 
-#if defined(ARCH_CPU_ARM_FAMILY) && \
-    (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_CHROMEOS))
-TEST(CPU, ARMImplementerAndPartNumber) {
-  base::CPU cpu;
-
-  const std::string& cpu_brand = cpu.cpu_brand();
-
-  // Some devices, including on the CQ, do not report a cpu_brand
-  // https://crbug.com/1166533 and https://crbug.com/1167123.
-  EXPECT_EQ(cpu_brand, base::TrimWhitespaceASCII(cpu_brand, base::TRIM_ALL));
-  EXPECT_GT(cpu.implementer(), 0u);
-  EXPECT_GT(cpu.part_number(), 0u);
-}
-#endif  // defined(ARCH_CPU_ARM_FAMILY) && (BUILDFLAG(IS_LINUX) ||
-        // BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_CHROMEOS))
-
 #if BUILDFLAG(PROTECTED_MEMORY_ENABLED)
 TEST(CPUDeathTest, VerifyModifyingCPUInstanceNoAllocationCrashes) {
   const base::CPU& cpu = base::CPU::GetInstanceNoAllocation();
diff --git a/base/mac/code_signature.cc b/base/mac/code_signature.cc
index 9261ab97..f8965a5 100644
--- a/base/mac/code_signature.cc
+++ b/base/mac/code_signature.cc
@@ -86,4 +86,21 @@
   return ValidateGuestWithAttributes(attributes.get(), requirement);
 }
 
+ScopedCFTypeRef<SecRequirementRef> RequirementFromString(
+    std::string_view requirement_string) {
+  ScopedCFTypeRef<CFStringRef> requirement_string_cf(
+      base::SysUTF8ToCFStringRef(requirement_string));
+  ScopedCFTypeRef<SecRequirementRef> requirement;
+  OSStatus status = SecRequirementCreateWithString(
+      requirement_string_cf.get(), kSecCSDefaultFlags,
+      requirement.InitializeInto());
+  if (status != errSecSuccess) {
+    OSSTATUS_LOG(ERROR, status)
+        << "SecRequirementCreateWithString: " << requirement_string;
+    return base::apple::ScopedCFTypeRef<SecRequirementRef>(nullptr);
+  }
+
+  return requirement;
+}
+
 }  // namespace base::mac
diff --git a/base/mac/code_signature.h b/base/mac/code_signature.h
index a5beb8b..fe5fe0c 100644
--- a/base/mac/code_signature.h
+++ b/base/mac/code_signature.h
@@ -11,6 +11,7 @@
 
 #include <string_view>
 
+#include "base/apple/scoped_cftyperef.h"
 #include "base/base_export.h"
 
 namespace base::mac {
@@ -50,6 +51,13 @@
         SignatureValidationType::DynamicAndStatic,
     std::string_view info_plist_xml = {});
 
+// Create a SecRequirementRef from a requirement string.
+//
+// Returns a null reference if the requirement string was invalid.
+BASE_EXPORT
+base::apple::ScopedCFTypeRef<SecRequirementRef> RequirementFromString(
+    std::string_view requirement_string);
+
 }  // namespace base::mac
 
 #endif  // BASE_MAC_CODE_SIGNATURE_H_
diff --git a/base/mac/code_signature_spi.h b/base/mac/code_signature_spi.h
new file mode 100644
index 0000000..b5f5a301
--- /dev/null
+++ b/base/mac/code_signature_spi.h
@@ -0,0 +1,25 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_MAC_CODE_SIGNATURE_SPI_H_
+#define BASE_MAC_CODE_SIGNATURE_SPI_H_
+
+#include <unistd.h>
+
+extern "C" {
+
+// From
+// https://github.com/apple-oss-distributions/xnu/blob/main/bsd/sys/codesign.h
+
+#define CS_OPS_STATUS 0               /* return status */
+#define CS_OPS_TEAMID 14              /* get team id */
+#define CS_OPS_VALIDATION_CATEGORY 17 /* get process validation category */
+
+#define CS_MAX_TEAMID_LEN 64
+
+int csops(pid_t pid, unsigned int ops, void* useraddr, size_t usersize);
+
+}  // extern "C"
+
+#endif  // BASE_MAC_CODE_SIGNATURE_SPI_H_
diff --git a/base/mac/process_requirement.cc b/base/mac/process_requirement.cc
new file mode 100644
index 0000000..379febc
--- /dev/null
+++ b/base/mac/process_requirement.cc
@@ -0,0 +1,496 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/mac/process_requirement.h"
+
+#include <Kernel/kern/cs_blobs.h>
+#include <Security/Security.h>
+#include <stdint.h>
+#include <sys/errno.h>
+
+#include <optional>
+
+#include "base/apple/osstatus_logging.h"
+#include "base/apple/scoped_cftyperef.h"
+#include "base/check_op.h"
+#include "base/containers/span.h"
+#include "base/logging.h"
+#include "base/mac/code_signature.h"
+#include "base/mac/code_signature_spi.h"
+#include "base/mac/mac_util.h"
+#include "base/no_destructor.h"
+#include "base/notreached.h"
+#include "base/ranges/algorithm.h"
+#include "base/strings/strcat.h"
+#include "base/strings/string_util.h"
+#include "base/types/expected.h"
+
+using base::apple::ScopedCFTypeRef;
+
+namespace base::mac {
+
+enum class ValidationCategory : unsigned int {
+  Invalid = CS_VALIDATION_CATEGORY_INVALID,
+  Platform = CS_VALIDATION_CATEGORY_PLATFORM,
+  TestFlight = CS_VALIDATION_CATEGORY_TESTFLIGHT,
+  Development = CS_VALIDATION_CATEGORY_DEVELOPMENT,
+  AppStore = CS_VALIDATION_CATEGORY_APP_STORE,
+  Enterprise = CS_VALIDATION_CATEGORY_ENTERPRISE,
+  DeveloperId = CS_VALIDATION_CATEGORY_DEVELOPER_ID,
+  LocalSigning = CS_VALIDATION_CATEGORY_LOCAL_SIGNING,
+  Rosetta = CS_VALIDATION_CATEGORY_ROSETTA,
+  OopJit = CS_VALIDATION_CATEGORY_OOPJIT,
+  None = CS_VALIDATION_CATEGORY_NONE,
+};
+
+namespace {
+
+// Requirements derived from the designated requirements described in TN3127:
+// Inside Code Signing: Requirements
+// (https://developer.apple.com/documentation/technotes/tn3127-inside-code-signing-requirements).
+constexpr std::string_view kAnyDeveloperIdRequirement =
+    "(anchor apple generic and certificate "
+    "1[field.1.2.840.113635.100.6.2.6] exists and certificate "
+    "leaf[field.1.2.840.113635.100.6.1.13] exists)";
+constexpr std::string_view kAnyAppStoreRequirement =
+    "(anchor apple generic and certificate "
+    "leaf[field.1.2.840.113635.100.6.1.9] exists)";
+constexpr std::string_view kAnyDevelopmentRequirement =
+    "(anchor apple generic and certificate 1[field.1.2.840.113635.100.6.2.1] "
+    "exists)";
+
+// A requirement string that will match ad-hoc signed code. It will also match
+// code signed with non-Apple certificates, but those are not supported by
+// `ProcessRequirement`.
+constexpr std::string_view kNonAppleAnchorRequirement =
+    "!(anchor apple generic)";
+
+struct CSOpsSystemCallProviderImpl
+    : ProcessRequirement::CSOpsSystemCallProvider {
+  ~CSOpsSystemCallProviderImpl() override = default;
+
+  int csops(pid_t pid,
+            unsigned int ops,
+            void* useraddr,
+            size_t usersize) override {
+    return ::csops(pid, ops, useraddr, usersize);
+  }
+
+  bool SupportsValidationCategory() const override {
+    // macOS versions prior to macOS 13 do not support
+    // CS_OPS_VALIDATION_CATEGORY.
+    return MacOSMajorVersion() >= 13;
+  }
+};
+
+ProcessRequirement::CSOpsSystemCallProvider* DefaultCSOpsProvider() {
+  static NoDestructor<CSOpsSystemCallProviderImpl> default_provider;
+  return default_provider.get();
+}
+
+ProcessRequirement::CSOpsSystemCallProvider*& CSOpsProvider() {
+  static ProcessRequirement::CSOpsSystemCallProvider* provider =
+      DefaultCSOpsProvider();
+  return provider;
+}
+
+base::expected<std::string, int> TeamIdentifierOfCurrentProcess() {
+  struct {
+    uint32_t type;
+    uint32_t length;
+    char identifier[CS_MAX_TEAMID_LEN + 1];
+  } result_data;
+  int result = CSOpsProvider()->csops(getpid(), CS_OPS_TEAMID, &result_data,
+                                      sizeof(result_data));
+  if (result < 0) {
+    if (errno == ENOENT) {
+      // Process has no team identifier (ad-hoc signed, unsigned, etc).
+      return "";
+    }
+
+    PLOG(ERROR) << "csops(CS_OPS_TEAMID) failed";
+    return base::unexpected(errno);
+  }
+
+  return std::string(result_data.identifier);
+}
+
+base::expected<ValidationCategory, int> ValidationCategoryOfCurrentProcess() {
+  ValidationCategory validation_category = ValidationCategory::Invalid;
+  int result =
+      CSOpsProvider()->csops(getpid(), CS_OPS_VALIDATION_CATEGORY,
+                             &validation_category, sizeof(validation_category));
+  if (result < 0) {
+    PLOG(ERROR) << "csops(CS_OPS_VALIDATION_CATEGORY) failed";
+    return base::unexpected(errno);
+  }
+
+  return validation_category;
+}
+
+// Determine the validation category of the current process by evaluating
+// the current process's code signature against requirements that represent
+// each of the validation categories we're interested in.
+base::expected<ValidationCategory, OSStatus>
+FallbackValidationCategoryOfCurrentProcess() {
+  ScopedCFTypeRef<SecCodeRef> self_code;
+  if (OSStatus status =
+          SecCodeCopySelf(kSecCSDefaultFlags, self_code.InitializeInto())) {
+    OSSTATUS_LOG(ERROR, status)
+        << "Unable to derive validation category for current "
+           "process. Failed to copy code object";
+    return base::unexpected(status);
+  }
+
+  // Do initial validation without a requirement to detect problems with the
+  // code signature itself. We do basic validation only as the validation is
+  // secondary to requirement matching in this case.
+  if (OSStatus status = SecStaticCodeCheckValidity(
+          self_code.get(), kSecCSBasicValidateOnly, nullptr)) {
+    if (status == errSecCSUnsigned) {
+      return ValidationCategory::None;
+    }
+
+    OSSTATUS_LOG(ERROR, status)
+        << "Unable to derive validation category for current "
+           "process. Signature validation of current process failed";
+    return base::unexpected(status);
+  }
+
+  std::pair<ValidationCategory, std::string_view> supported_categories[] = {
+      {ValidationCategory::DeveloperId, kAnyDeveloperIdRequirement},
+      {ValidationCategory::AppStore, kAnyAppStoreRequirement},
+      {ValidationCategory::Development, kAnyDevelopmentRequirement},
+      {ValidationCategory::None, kNonAppleAnchorRequirement},
+  };
+
+  for (auto& [category, requirement] : supported_categories) {
+    OSStatus status =
+        SecStaticCodeCheckValidity(self_code.get(), kSecCSBasicValidateOnly,
+                                   RequirementFromString(requirement).get());
+    switch (status) {
+      case errSecSuccess:
+        // Requirement matched so we now know the validation category.
+        return category;
+
+      case errSecCSReqFailed:
+        // Requirement did not match. On to the next one.
+        continue;
+
+      default:
+        OSSTATUS_LOG(INFO, status)
+            << "Unexpected error when evaluating requirement " << requirement;
+    }
+  }
+
+  LOG(ERROR) << "Unable to derive validation category for current process. "
+                "Signature did not match any supported requirement.";
+  return base::unexpected(errSecFunctionFailed);
+}
+
+std::string RequirementStringForValidationCategory(
+    ValidationCategory category) {
+  // It is not meaningful to create a requirement string for an unsigned or
+  // ad-hoc signed process.
+  CHECK_NE(category, ValidationCategory::None);
+
+  switch (category) {
+    case ValidationCategory::DeveloperId:
+      return std::string(kAnyDeveloperIdRequirement);
+    case ValidationCategory::AppStore:
+      return std::string(kAnyAppStoreRequirement);
+    case ValidationCategory::Development:
+      return std::string(kAnyDevelopmentRequirement);
+    default:
+      NOTREACHED() << "Unsupported process validation category: "
+                   << static_cast<unsigned int>(category);
+  }
+}
+
+}  // namespace
+
+ProcessRequirement::Builder::Builder() = default;
+ProcessRequirement::Builder::~Builder() = default;
+
+ProcessRequirement::Builder::Builder(Builder&&) = default;
+ProcessRequirement::Builder& ProcessRequirement::Builder::operator=(Builder&&) =
+    default;
+
+ProcessRequirement::Builder ProcessRequirement::Builder::Identifier(
+    std::string identifier) && {
+  CHECK(identifier.size());
+  CHECK(identifiers_.empty());
+  identifiers_.push_back(std::move(identifier));
+  return std::move(*this);
+}
+
+ProcessRequirement::Builder ProcessRequirement::Builder::IdentifierIsOneOf(
+    std::vector<std::string> identifiers) && {
+  CHECK(identifiers.size());
+  CHECK(base::ranges::all_of(identifiers, &std::string::size));
+  CHECK(identifiers_.empty());
+  identifiers_ = std::move(identifiers);
+  return std::move(*this);
+}
+
+ProcessRequirement::Builder
+ProcessRequirement::Builder::SignedWithSameIdentity() && {
+  return std::move(*this).HasSameTeamIdentifier().HasSameCertificateType();
+}
+
+ProcessRequirement::Builder
+ProcessRequirement::Builder::HasSameTeamIdentifier() && {
+  CHECK(team_identifier_.empty());
+  if (auto team_identifier = TeamIdentifierOfCurrentProcess();
+      team_identifier.has_value()) {
+    team_identifier_ = std::move(*team_identifier);
+    return std::move(*this);
+  }
+
+  LOG(ERROR) << "HasSameTeamIdentifier failed to retrieve team identifier of "
+                "current process";
+  failed_ = true;
+  return std::move(*this);
+}
+
+ProcessRequirement::Builder
+ProcessRequirement::Builder::HasSameCertificateType() && {
+  CHECK(!validation_category_);
+
+  if (CSOpsProvider()->SupportsValidationCategory()) {
+    auto validation_category = ValidationCategoryOfCurrentProcess();
+    if (validation_category.has_value()) {
+      validation_category_ = *validation_category;
+    } else {
+      failed_ = true;
+    }
+  } else {
+    // Older macOS versions do not support CS_OPS_VALIDATION_CATEGORY. Derive
+    // the validation category via Security.framework instead.
+    static auto validation_category =
+        FallbackValidationCategoryOfCurrentProcess();
+    if (validation_category.has_value()) {
+      validation_category_ = *validation_category;
+    } else {
+      failed_ = true;
+    }
+  }
+
+  return std::move(*this);
+}
+
+ProcessRequirement::Builder ProcessRequirement::Builder::TeamIdentifier(
+    std::string team_identifier) && {
+  CHECK(team_identifier_.empty());
+  CHECK(base::ranges::all_of(team_identifier, base::IsAsciiAlphaNumeric<char>));
+  team_identifier_ = std::move(team_identifier);
+  return std::move(*this);
+}
+
+ProcessRequirement::Builder
+ProcessRequirement::Builder::DeveloperIdCertificateType() && {
+  validation_category_ = ValidationCategory::DeveloperId;
+  return std::move(*this);
+}
+
+ProcessRequirement::Builder
+ProcessRequirement::Builder::AppStoreCertificateType() && {
+  validation_category_ = ValidationCategory::AppStore;
+  return std::move(*this);
+}
+
+ProcessRequirement::Builder
+ProcessRequirement::Builder::DevelopmentCertificateType() && {
+  validation_category_ = ValidationCategory::Development;
+  return std::move(*this);
+}
+
+ProcessRequirement::Builder
+ProcessRequirement::Builder::CheckDynamicValidityOnly() && {
+  dynamic_validity_only_ = true;
+  return std::move(*this);
+}
+
+std::optional<ProcessRequirement> ProcessRequirement::Builder::Build() && {
+  if (failed_) {
+    VLOG(2)
+        << "ProcessRequirement::Builder::Build: failed validation -> nullopt";
+    return std::nullopt;
+  }
+
+  ValidationCategory validation_category =
+      validation_category_.value_or(ValidationCategory::None);
+
+  if (validation_category == ValidationCategory::None) {
+    CHECK(team_identifier_.empty())
+        << "A process requirement matching on a team identifier without "
+           "specifying a certificate type is unsafe.";
+  } else {
+    CHECK(team_identifier_.size())
+        << "A process requirement without a team identifier is unsafe as it "
+           "can be matched by any signing identity of that type.";
+  }
+
+  return ProcessRequirement(std::move(identifiers_),
+                            std::move(team_identifier_), validation_category,
+                            dynamic_validity_only_);
+}
+
+ProcessRequirement::ProcessRequirement(std::vector<std::string> identifiers,
+                                       std::string team_identifier,
+                                       ValidationCategory validation_category,
+                                       bool dynamic_validity_only)
+    : identifiers_(std::move(identifiers)),
+      team_identifier_(std::move(team_identifier)),
+      validation_category_(validation_category),
+      dynamic_validity_only_(dynamic_validity_only) {
+  CHECK(validation_category_ != ValidationCategory::Invalid);
+}
+
+ProcessRequirement::ProcessRequirement(ForTesting for_testing)
+    : for_testing_(for_testing),
+      validation_category_(ValidationCategory::Invalid) {}
+
+ProcessRequirement::~ProcessRequirement() = default;
+
+ProcessRequirement::ProcessRequirement(const ProcessRequirement&) = default;
+ProcessRequirement& ProcessRequirement::operator=(const ProcessRequirement&) =
+    default;
+
+ProcessRequirement::ProcessRequirement(ProcessRequirement&&) = default;
+ProcessRequirement& ProcessRequirement::operator=(ProcessRequirement&&) =
+    default;
+
+// static
+ProcessRequirement ProcessRequirement::AlwaysMatchesForTesting() {
+  return ProcessRequirement(ForTesting::AlwaysMatches);
+}
+
+// static
+ProcessRequirement ProcessRequirement::NeverMatchesForTesting() {
+  return ProcessRequirement(ForTesting::NeverMatches);
+}
+
+bool ProcessRequirement::RequiresSignatureValidation() const {
+  if (for_testing_.has_value()) {
+    // `ForTesting::AlwaysMatches` does not require validation because
+    // a test process is likely to be unsigned.
+    // `ForTesting::NeverMatches` will fail signature validation with
+    // `errSecCSUnsigned` if the process is unsigned, and will fail requirement
+    // evaluation if the process has a valid ad-hoc signature.
+    return for_testing_.value() == ForTesting::NeverMatches;
+  }
+
+  // All validation categories besides none (ad-hoc signature or unsigned)
+  // require validation.
+  // It is not useful to validate an ad-hoc signature as anyone can create
+  // an ad-hoc signature that matches this requirement.
+  return validation_category_ != ValidationCategory::None;
+}
+
+ScopedCFTypeRef<SecRequirementRef> ProcessRequirement::AsSecRequirement()
+    const {
+  if (for_testing_.has_value()) {
+    return AsSecRequirementForTesting(for_testing_.value());  // IN-TEST
+  }
+
+  if (!RequiresSignatureValidation()) {
+    VLOG(2) << "ProcessRequirement::AsSecRequirement -> nullptr";
+    return ScopedCFTypeRef<SecRequirementRef>{nullptr};
+  }
+
+  std::vector<std::string> clauses;
+
+  if (identifiers_.size()) {
+    std::vector<std::string> identifier_clauses;
+    for (const std::string& identifier : identifiers_) {
+      identifier_clauses.push_back(StrCat({"identifier \"", identifier, "\""}));
+    }
+    if (identifier_clauses.size() == 1) {
+      clauses.push_back(std::move(identifier_clauses.front()));
+    } else {
+      std::string identifier_clause =
+          base::JoinString(identifier_clauses, " or ");
+      clauses.push_back(StrCat({"(", identifier_clause, ")"}));
+    }
+  }
+
+  if (team_identifier_.size()) {
+    clauses.push_back(
+        StrCat({"certificate leaf[subject.OU] = ", team_identifier_}));
+  }
+
+  clauses.push_back(
+      RequirementStringForValidationCategory(validation_category_));
+
+  std::string requirement_string = base::JoinString(clauses, " and ");
+  VLOG(2) << "ProcessRequirement::AsSecRequirement -> " << requirement_string;
+  apple::ScopedCFTypeRef<SecRequirementRef> requirement =
+      RequirementFromString(requirement_string);
+  CHECK(requirement) << "ProcessRequirement::AsSecRequirement generated a "
+                        "requirement string that could not be parsed.";
+  return requirement;
+}
+
+// static
+ScopedCFTypeRef<SecRequirementRef>
+ProcessRequirement::AsSecRequirementForTesting(
+    ProcessRequirement::ForTesting for_testing) {
+  std::string requirement_string;
+  switch (for_testing) {
+    case ForTesting::AlwaysMatches: {
+      requirement_string = "(!info[ThisKeyDoesNotExist])";
+      break;
+    }
+    case ForTesting::NeverMatches: {
+      requirement_string = R"(identifier = "this is not the identifier")";
+      break;
+    }
+  }
+  ScopedCFTypeRef<SecRequirementRef> requirement =
+      RequirementFromString(requirement_string);
+  CHECK(requirement)
+      << "ProcessRequirement::AsSecRequirementForTesting generated a "
+         "requirement string that could not be parsed.";
+  return requirement;
+}
+
+// static
+void ProcessRequirement::SetCSOpsSystemCallProviderForTesting(
+    CSOpsSystemCallProvider* csops_provider) {
+  if (csops_provider) {
+    CSOpsProvider() = csops_provider;
+  } else {
+    CSOpsProvider() = DefaultCSOpsProvider();
+  }
+}
+
+bool ProcessRequirement::ValidateProcess(
+    audit_token_t audit_token,
+    base::span<const uint8_t> info_plist_data) const {
+  if (!RequiresSignatureValidation()) {
+    // No signature validation required. Return success.
+    return true;
+  }
+
+  // If the requirement specifies we are checking only the validity of the
+  // dynamic code then we must have Info.plist data.
+  if (dynamic_validity_only_) {
+    CHECK(info_plist_data.size())
+        << "info_plist_data is required when checking dynamic validity only.";
+  }
+
+  if (OSStatus status = ProcessIsSignedAndFulfillsRequirement(
+          audit_token, AsSecRequirement().get(),
+          dynamic_validity_only_ ? SignatureValidationType::DynamicOnly
+                                 : SignatureValidationType::DynamicAndStatic,
+          base::as_string_view(info_plist_data))) {
+    OSSTATUS_LOG(ERROR, status) << "ProcessIsSignedAndFulfillsRequirement";
+    return false;
+  }
+
+  return true;
+}
+
+}  // namespace base::mac
diff --git a/base/mac/process_requirement.h b/base/mac/process_requirement.h
new file mode 100644
index 0000000..ae37e06
--- /dev/null
+++ b/base/mac/process_requirement.h
@@ -0,0 +1,189 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_MAC_PROCESS_REQUIREMENT_H_
+#define BASE_MAC_PROCESS_REQUIREMENT_H_
+
+#include <Security/Security.h>
+#include <mach/mach.h>
+
+#include <optional>
+#include <string>
+#include <vector>
+
+#include "base/apple/scoped_cftyperef.h"
+#include "base/base_export.h"
+#include "base/containers/span.h"
+
+namespace base::mac {
+
+enum class ValidationCategory : unsigned int;
+
+// Represents constraints on the code signing identity of a peer process.
+//
+// `ProcessRequirement` is typically used to describe which processes are
+// permitted to establish IPC connections, and to validate that a connecting
+// process fulfills those constraints.
+class BASE_EXPORT ProcessRequirement {
+ public:
+  class BASE_EXPORT Builder {
+   public:
+    Builder();
+    ~Builder();
+
+    Builder(const Builder&) = delete;
+    Builder& operator=(const Builder&) = delete;
+    Builder(Builder&&);
+    Builder& operator=(Builder&&);
+
+    // The identifier in the signature must match `identifier`.
+    // Can be called at most once. See `IdentifierIsOneOf` if multiple
+    // identifiers can be accepted.
+    //
+    // The identifier is typically the executable name or bundle identifier of
+    // the application.
+    Builder Identifier(std::string identifier) &&;
+
+    // The identifier in the signature must match one of the values
+    // in`identifiers`.
+    // Can be called at most once.
+    //
+    // The identifier is typically the executable name or bundle identifier of
+    // the application.
+    Builder IdentifierIsOneOf(std::vector<std::string> identifiers) &&;
+
+    // Equivalent to HasSameTeamIdentifier().HasSameCertificateType()
+    Builder SignedWithSameIdentity() &&;
+
+    // The process must be signed with a certificate that uses the same
+    // team identifier as this process.
+    // Can be called at most once.
+    //
+    // Note: It is an error to call this without also limiting the certificate
+    // type via `HasSameCertificateType`, `DeveloperIdCertificateType`, etc.
+    Builder HasSameTeamIdentifier() &&;
+
+    // The process must be signed with the same type of certificate as this
+    // process.
+    // Can be called at most once.
+    Builder HasSameCertificateType() &&;
+
+    // The team identifier in the signing certificate matches `team_identifier`.
+    // Can be called at most once.
+    //
+    // Note: It is an error to call this without also limiting the certificate
+    // type via `HasSameCertificateType`, `DeveloperIdCertificateType`, etc.
+    Builder TeamIdentifier(std::string team_identifier) &&;
+
+    // The certificate used during signing is an Apple Developer ID certificate.
+    // Can be called at most once.
+    Builder DeveloperIdCertificateType() &&;
+
+    // The certificate used during signing is an Apple App Store certificate.
+    // Can be called at most once.
+    Builder AppStoreCertificateType() &&;
+
+    // The certificate used during signing is an Apple Development certificate
+    // that cannot be used for distributing applications.
+    // Can be called at most once.
+    Builder DevelopmentCertificateType() &&;
+
+    // Validate only the dynamic signature of the application without
+    // comparing it to the state of the application on disk.
+    //
+    // Note that when requesting dynamic validation it is necessary to
+    // supply the application's Info.plist data when performing
+    // code signature validation using the resulting requirement.
+    Builder CheckDynamicValidityOnly() &&;
+
+    // Consume the constraints and produce a ProcessRequirement.
+    // Returns `std::nullopt` on error.
+    std::optional<ProcessRequirement> Build() &&;
+
+   private:
+    std::vector<std::string> identifiers_;
+    std::string team_identifier_;
+    std::optional<ValidationCategory> validation_category_;
+    bool dynamic_validity_only_ = false;
+    bool failed_ = false;
+  };  // class Builder
+
+  // Use Builder::Build to construct a ProcessRequirement.
+  ProcessRequirement() = delete;
+  ~ProcessRequirement();
+
+  ProcessRequirement(const ProcessRequirement&);
+  ProcessRequirement& operator=(const ProcessRequirement&);
+  ProcessRequirement(ProcessRequirement&&);
+  ProcessRequirement& operator=(ProcessRequirement&&);
+
+  // Validate the process represented by `audit_token` against this requirement.
+  //
+  // If this requirement was created with `CheckDynamicValidityOnly()` then
+  // the target process's Info.plist data must be provided in `info_plist_data`.
+  bool ValidateProcess(audit_token_t audit_token,
+                       base::span<const uint8_t> info_plist_data = {}) const;
+
+  // Create a `SecRequirementRef` from the requirement.
+  // Will return `nullptr` if the requirement does not place any limits
+  // on the process, such as if `SignedWithSameIdentity()` was used
+  // from a process with an ad-hoc code signature.
+  //
+  // Prefer to use `ValidateProcess` when possible.
+  apple::ScopedCFTypeRef<SecRequirementRef> AsSecRequirement() const;
+
+  // Returns true if only the dynamic signature of the application
+  // should be validated without comparing it to the state of the
+  // application on disk.
+  bool ShouldCheckDynamicValidityOnly() const { return dynamic_validity_only_; }
+
+  static ProcessRequirement AlwaysMatchesForTesting();
+  static ProcessRequirement NeverMatchesForTesting();
+
+  struct CSOpsSystemCallProvider {
+    virtual ~CSOpsSystemCallProvider() = default;
+    virtual int csops(pid_t pid,
+                      unsigned int ops,
+                      void* useraddr,
+                      size_t usersize) = 0;
+    virtual bool SupportsValidationCategory() const = 0;
+  };
+
+  // Use `csops_provider` function in place of using the default provider which
+  // uses the `csops` system call for retrieving code signing information.
+  // Pass `nullptr` to reset to the default provider.
+  static void SetCSOpsSystemCallProviderForTesting(
+      CSOpsSystemCallProvider* csops_provider);
+
+ private:
+  ProcessRequirement(std::vector<std::string> identifiers,
+                     std::string team_identifier,
+                     ValidationCategory validation_category,
+                     bool dynamic_validity_only);
+
+  // Returns true if the code signature must be validated to enforce this
+  // requirement.
+  // This will be false for unsigned code and true for all signed code.
+  bool RequiresSignatureValidation() const;
+
+  enum ForTesting {
+    AlwaysMatches,
+    NeverMatches,
+  };
+
+  explicit ProcessRequirement(ForTesting for_testing);
+
+  static apple::ScopedCFTypeRef<SecRequirementRef> AsSecRequirementForTesting(
+      ForTesting for_testing);
+
+  std::vector<std::string> identifiers_;
+  std::string team_identifier_;
+  std::optional<ForTesting> for_testing_;
+  ValidationCategory validation_category_;
+  bool dynamic_validity_only_ = false;
+};
+
+}  // namespace base::mac
+
+#endif  // BASE_MAC_PROCESS_REQUIREMENT_H_
diff --git a/base/mac/process_requirement_unittest.cc b/base/mac/process_requirement_unittest.cc
new file mode 100644
index 0000000..3fbb7ab1
--- /dev/null
+++ b/base/mac/process_requirement_unittest.cc
@@ -0,0 +1,288 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/mac/process_requirement.h"
+
+#include <Kernel/kern/cs_blobs.h>
+#include <Security/Security.h>
+#include <stdint.h>
+
+#include "base/apple/scoped_cftyperef.h"
+#include "base/containers/span.h"
+#include "base/mac/code_signature_spi.h"
+#include "base/notreached.h"
+#include "base/strings/sys_string_conversions.h"
+#include "base/sys_byteorder.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::Return;
+
+namespace base::mac {
+namespace {
+
+constexpr std::string kTeamId = "ABCDEFG";
+constexpr std::string_view kExpectedDeveloperIdRequirementString =
+    "certificate leaf[subject.OU] = ABCDEFG and anchor apple generic and "
+    "certificate 1[field.1.2.840.113635.100.6.2.6] /* exists */ and "
+    "certificate leaf[field.1.2.840.113635.100.6.1.13] /* exists */";
+constexpr std::string_view kExpectedAppStoreRequirementString =
+    "certificate leaf[subject.OU] = ABCDEFG and anchor apple generic and "
+    "certificate leaf[field.1.2.840.113635.100.6.1.9] /* exists */";
+constexpr std::string_view kExpectedDevelopmentRequirementString =
+    "certificate leaf[subject.OU] = ABCDEFG and anchor apple generic "
+    "and certificate 1[field.1.2.840.113635.100.6.2.1] /* exists */";
+
+struct CSOpsSystemCallProviderForTesting
+    : ProcessRequirement::CSOpsSystemCallProvider {
+  // Dispatch from the system call interface to helper functions that can be
+  // mocked in a more natural fashion.
+  int csops(pid_t pid,
+            unsigned int ops,
+            void* useraddr,
+            size_t usersize) override {
+    switch (ops) {
+      case CS_OPS_TEAMID: {
+        struct TeamIdResult {
+          uint32_t type;
+          uint32_t length;
+          char identifier[CS_MAX_TEAMID_LEN + 1];
+        };
+        CHECK(usersize >= sizeof(TeamIdResult));
+        TeamIdResult* team_id_result = static_cast<TeamIdResult*>(useraddr);
+        int result = GetTeamIdentifier(team_id_result->identifier);
+        errno = result;
+        if (!result) {
+          team_id_result->type = 0;
+          team_id_result->length =
+              HostToNet32(strlen(team_id_result->identifier));
+        }
+        return result ? -1 : 0;
+      }
+      case CS_OPS_VALIDATION_CATEGORY: {
+        CHECK(usersize >= sizeof(unsigned int));
+        int result =
+            GetValidationCategory(static_cast<unsigned int*>(useraddr));
+        errno = result;
+        return result ? -1 : 0;
+      }
+      default:
+        NOTREACHED();
+    }
+  }
+
+  bool SupportsValidationCategory() const override {
+    // This test implementation supports returning the validation category on
+    // all macOS versions.
+    return true;
+  }
+
+  void SetTeamIdentifier(std::string team_id) {
+    ON_CALL(*this, GetTeamIdentifier)
+        .WillByDefault([team_id = std::move(team_id)](span<char> out_team_id) {
+          out_team_id.copy_prefix_from(team_id);
+          out_team_id[team_id.size()] = 0;
+          return 0;
+        });
+  }
+
+  void SetValidationCategory(unsigned int validation_category) {
+    ON_CALL(*this, GetValidationCategory)
+        .WillByDefault(
+            [validation_category](unsigned int* out_validation_category) {
+              *out_validation_category = validation_category;
+              return 0;
+            });
+  }
+
+  MOCK_METHOD(int, GetTeamIdentifier, ((base::span<char>)));
+  MOCK_METHOD(int, GetValidationCategory, (unsigned int*));
+};
+
+class ProcessRequirementTest : public testing::Test {
+ public:
+  void SetUp() override {
+    ProcessRequirement::SetCSOpsSystemCallProviderForTesting(&csops_provider_);
+
+    // Have all `csops` system calls fail with `ENOTSUP` by default.
+    ON_CALL(csops_provider_, GetTeamIdentifier).WillByDefault(Return(ENOTSUP));
+    ON_CALL(csops_provider_, GetValidationCategory)
+        .WillByDefault(Return(ENOTSUP));
+  }
+
+  void TearDown() override {
+    ProcessRequirement::SetCSOpsSystemCallProviderForTesting(nullptr);
+  }
+
+ protected:
+  testing::NiceMock<CSOpsSystemCallProviderForTesting> csops_provider_;
+};
+
+std::optional<std::string> AsRequirementString(
+    const ProcessRequirement& requirement) {
+  apple::ScopedCFTypeRef<SecRequirementRef> requirement_ref =
+      requirement.AsSecRequirement();
+  apple::ScopedCFTypeRef<CFStringRef> requirement_string;
+  OSStatus status =
+      SecRequirementCopyString(requirement_ref.get(), kSecCSDefaultFlags,
+                               requirement_string.InitializeInto());
+  if (status != errSecSuccess) {
+    return std::nullopt;
+  }
+
+  return SysCFStringRefToUTF8(requirement_string.get());
+}
+
+TEST_F(ProcessRequirementTest, FailedSystemCalls) {
+  EXPECT_FALSE(ProcessRequirement::Builder().HasSameCertificateType().Build());
+  EXPECT_FALSE(ProcessRequirement::Builder().HasSameTeamIdentifier().Build());
+  EXPECT_FALSE(ProcessRequirement::Builder().SignedWithSameIdentity().Build());
+}
+
+TEST_F(ProcessRequirementTest, DeveloperId) {
+  csops_provider_.SetTeamIdentifier(kTeamId);
+
+  // Explicitly specify the certificate type.
+  std::optional<ProcessRequirement> requirement =
+      ProcessRequirement::Builder()
+          .HasSameTeamIdentifier()
+          .DeveloperIdCertificateType()
+          .Build();
+  EXPECT_TRUE(requirement);
+  EXPECT_EQ(AsRequirementString(*requirement),
+            kExpectedDeveloperIdRequirementString);
+
+  // Same certificate type where current process is signed with Developer ID.
+  csops_provider_.SetValidationCategory(CS_VALIDATION_CATEGORY_DEVELOPER_ID);
+  requirement = ProcessRequirement::Builder()
+                    .HasSameTeamIdentifier()
+                    .HasSameCertificateType()
+                    .Build();
+  EXPECT_TRUE(requirement);
+  EXPECT_EQ(AsRequirementString(*requirement),
+            kExpectedDeveloperIdRequirementString);
+}
+
+TEST_F(ProcessRequirementTest, AppStore) {
+  csops_provider_.SetTeamIdentifier(kTeamId);
+
+  // Explicitly specify the certificate type.
+  std::optional<ProcessRequirement> requirement = ProcessRequirement::Builder()
+                                                      .HasSameTeamIdentifier()
+                                                      .AppStoreCertificateType()
+                                                      .Build();
+  EXPECT_TRUE(requirement);
+  EXPECT_EQ(AsRequirementString(*requirement),
+            kExpectedAppStoreRequirementString);
+
+  // Same certificate type where current process is signed with an App Store
+  // certificate.
+  csops_provider_.SetValidationCategory(CS_VALIDATION_CATEGORY_APP_STORE);
+  requirement = ProcessRequirement::Builder()
+                    .HasSameTeamIdentifier()
+                    .HasSameCertificateType()
+                    .Build();
+  EXPECT_TRUE(requirement);
+  EXPECT_EQ(AsRequirementString(*requirement),
+            kExpectedAppStoreRequirementString);
+}
+
+TEST_F(ProcessRequirementTest, Development) {
+  csops_provider_.SetTeamIdentifier(kTeamId);
+
+  // Explicitly specify the certificate type.
+  std::optional<ProcessRequirement> requirement =
+      ProcessRequirement::Builder()
+          .HasSameTeamIdentifier()
+          .DevelopmentCertificateType()
+          .Build();
+  EXPECT_TRUE(requirement);
+  EXPECT_EQ(AsRequirementString(*requirement),
+            kExpectedDevelopmentRequirementString);
+
+  // Same certificate type where current process is signed with a development
+  // certificate.
+  csops_provider_.SetValidationCategory(CS_VALIDATION_CATEGORY_DEVELOPMENT);
+  requirement = ProcessRequirement::Builder()
+                    .HasSameTeamIdentifier()
+                    .HasSameCertificateType()
+                    .Build();
+  EXPECT_TRUE(requirement);
+  EXPECT_EQ(AsRequirementString(*requirement),
+            kExpectedDevelopmentRequirementString);
+}
+
+TEST_F(ProcessRequirementTest, Identifier) {
+  std::optional<ProcessRequirement> requirement =
+      ProcessRequirement::Builder()
+          .Identifier("com.example.Application")
+          .TeamIdentifier(kTeamId)
+          .DeveloperIdCertificateType()
+          .Build();
+  EXPECT_TRUE(requirement);
+  EXPECT_EQ(AsRequirementString(*requirement),
+            "identifier \"com.example.Application\" and " +
+                std::string(kExpectedDeveloperIdRequirementString));
+
+  requirement = ProcessRequirement::Builder()
+                    .IdentifierIsOneOf({
+                        "com.example.ApplicationA",
+                        "com.example.ApplicationB",
+                        "com.example.ApplicationC",
+                    })
+                    .TeamIdentifier(kTeamId)
+                    .DeveloperIdCertificateType()
+                    .Build();
+  EXPECT_TRUE(requirement);
+  EXPECT_EQ(AsRequirementString(*requirement),
+            "(identifier \"com.example.ApplicationA\" or identifier "
+            "\"com.example.ApplicationB\" or identifier "
+            "\"com.example.ApplicationC\") and " +
+                std::string(kExpectedDeveloperIdRequirementString));
+}
+
+TEST_F(ProcessRequirementTest, AdHocOrUnsigned) {
+  // CS_OPS_TEAMID returns ENOENT for an ad-hoc signed or unsigned process.
+  ON_CALL(csops_provider_, GetTeamIdentifier).WillByDefault(Return(ENOENT));
+  csops_provider_.SetValidationCategory(CS_VALIDATION_CATEGORY_NONE);
+
+  std::optional<ProcessRequirement> requirement = ProcessRequirement::Builder()
+                                                      .HasSameTeamIdentifier()
+                                                      .HasSameCertificateType()
+                                                      .Build();
+  EXPECT_TRUE(requirement);
+  EXPECT_FALSE(requirement->AsSecRequirement());
+}
+
+TEST_F(ProcessRequirementTest, ValidationCategoryDetectionFallback) {
+  // Older versions of macOS do not support `CS_OPS_VALIDATION_CATEGORY` and
+  // will return EINVAL. ProcessRequirement falls back to inferring the
+  // validation category by checking the current process's code signature
+  // against code signing requirements. There is not a good way to intercept
+  // those for testing at the moment.
+}
+
+TEST_F(ProcessRequirementTest, InvalidCombinations) {
+  EXPECT_DEATH(
+      {
+        // A requirement that includes a team identifier but no certificate type
+        // will assert because it could be matched by a self-signed certificate
+        // type and is likely to be an error.
+        csops_provider_.SetTeamIdentifier(kTeamId);
+        ProcessRequirement::Builder().HasSameTeamIdentifier().Build();
+      },
+      "without specifying a certificate type is unsafe");
+
+  EXPECT_DEATH(
+      {
+        // A requirement that includes a certificate type but no team identifier
+        // will assert because it will match any signing identity of that type
+        // and is likely to be an error.
+        ProcessRequirement::Builder().DeveloperIdCertificateType().Build();
+      },
+      "without a team identifier is unsafe");
+}
+
+}  // namespace
+}  // namespace base::mac
diff --git a/build/OWNERS.setnoparent b/build/OWNERS.setnoparent
index b7867d2b..0dc09295 100644
--- a/build/OWNERS.setnoparent
+++ b/build/OWNERS.setnoparent
@@ -49,8 +49,9 @@
 # https://sites.google.com/a/chromium.org/dev/developers/how-tos/enterprise/adding-new-policies
 file://components/policy/ENTERPRISE_POLICY_OWNERS
 
-# This restriction is in place due to the complicated compliance regulations
-# around this code.
+# This restriction is in place due to the policies and compliance regulations around this code.
+file://components/search_engines/COMPLIANCE_OWNERS
+file://components/search_engines/search_engine_choice/COMPLIANCE_OWNERS
 file://chrome/android/java/src/org/chromium/chrome/browser/searchwidget/COMPLIANCE_OWNERS
 
 # Notification channels appear in system UI and are persisted forever by
diff --git a/chrome/BUILD.gn b/chrome/BUILD.gn
index 9b945eb..3049bfa 100644
--- a/chrome/BUILD.gn
+++ b/chrome/BUILD.gn
@@ -1236,6 +1236,7 @@
       ":optimization_guide_library",
       ":swiftshader_binaries",
       ":widevine_cdm_library",
+      "//components/privacy_sandbox/privacy_sandbox_attestations/preload:component_bundle",
     ]
 
     if (is_chrome_branded) {
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index 215c326..466a305 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -541,6 +541,7 @@
       "//components/browser_ui/bottomsheet/android:manager_java",
       "//components/browser_ui/client_certificate/android:java",
       "//components/browser_ui/contacts_picker/android:java",
+      "//components/browser_ui/desktop_windowing/android:java",
       "//components/browser_ui/device_lock/android:java",
       "//components/browser_ui/display_cutout/android:java",
       "//components/browser_ui/http_auth/android:java",
@@ -1230,6 +1231,7 @@
       "//components/browser_ui/bottomsheet/android:factory_java",
       "//components/browser_ui/bottomsheet/android:java",
       "//components/browser_ui/bottomsheet/android:manager_java",
+      "//components/browser_ui/desktop_windowing/android:java",
       "//components/browser_ui/device_lock/android:java",
       "//components/browser_ui/display_cutout/android:java",
       "//components/browser_ui/media/android:java",
@@ -1871,6 +1873,7 @@
       "//components/browser_ui/bottomsheet/android:java",
       "//components/browser_ui/bottomsheet/android:manager_java",
       "//components/browser_ui/bottomsheet/android/test:java",
+      "//components/browser_ui/desktop_windowing/android:java",
       "//components/browser_ui/device_lock/android:java",
       "//components/browser_ui/display_cutout/android:java",
       "//components/browser_ui/media/android:java",
diff --git a/chrome/android/DEPS b/chrome/android/DEPS
index ba5bc8a..3485fb5 100644
--- a/chrome/android/DEPS
+++ b/chrome/android/DEPS
@@ -32,6 +32,7 @@
   "+components/commerce/core",
   "+components/content_capture",
   "+components/content_settings",
+  "+components/browser_ui/desktop_windowing/android",
   "+components/download",
   "+components/embedder_support/android",
   "+components/external_intents/android",
diff --git a/chrome/android/features/tab_ui/BUILD.gn b/chrome/android/features/tab_ui/BUILD.gn
index 5cb74d4d..bf98800e 100644
--- a/chrome/android/features/tab_ui/BUILD.gn
+++ b/chrome/android/features/tab_ui/BUILD.gn
@@ -50,6 +50,7 @@
     "java/res/drawable/tab_grid_card_background.xml",
     "java/res/drawable/tab_grid_dialog_background.xml",
     "java/res/drawable/tab_grid_selection_list_icon.xml",
+    "java/res/drawable/tab_group_color_background.xml",
     "java/res/drawable/tab_group_color_icon.xml",
     "java/res/drawable/tab_group_dialog_color_icon.xml",
     "java/res/drawable/tab_group_favicon_quarter_bg.xml",
@@ -85,6 +86,7 @@
     "java/res/layout/tab_grid_dialog_toolbar.xml",
     "java/res/layout/tab_grid_dialog_toolbar_two_row.xml",
     "java/res/layout/tab_grid_message_card_item.xml",
+    "java/res/layout/tab_group_color_container.xml",
     "java/res/layout/tab_group_color_picker_container.xml",
     "java/res/layout/tab_group_favicon_cluster.xml",
     "java/res/layout/tab_group_favicon_quarter.xml",
diff --git a/chrome/android/features/tab_ui/java/res/drawable/tab_group_color_background.xml b/chrome/android/features/tab_ui/java/res/drawable/tab_group_color_background.xml
new file mode 100644
index 0000000..008d1fc3
--- /dev/null
+++ b/chrome/android/features/tab_ui/java/res/drawable/tab_group_color_background.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+Copyright 2024 The Chromium Authors
+Use of this source code is governed by a BSD-style license that can be
+found in the LICENSE file.
+-->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+  android:shape="oval">
+  <solid android:color="@macro/default_icon_color"/>
+  <stroke
+    android:width="@dimen/tab_group_color_icon_stroke"
+    android:color="@color/gm3_baseline_surface_light" />
+</shape>
diff --git a/chrome/android/features/tab_ui/java/res/layout/tab_group_color_container.xml b/chrome/android/features/tab_ui/java/res/layout/tab_group_color_container.xml
new file mode 100644
index 0000000..9d45b643
--- /dev/null
+++ b/chrome/android/features/tab_ui/java/res/layout/tab_group_color_container.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+Copyright 2024 The Chromium Authors
+Use of this source code is governed by a BSD-style license that can be
+found in the LICENSE file.
+-->
+<FrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:minWidth="@dimen/tab_group_color_icon_item_size"
+    android:minHeight="@dimen/tab_group_color_icon_item_size"
+    android:importantForAccessibility="no"
+    android:background="@drawable/tab_group_color_background" />
diff --git a/chrome/android/features/tab_ui/java/res/values/dimens.xml b/chrome/android/features/tab_ui/java/res/values/dimens.xml
index 01773e91..7bdbddd 100644
--- a/chrome/android/features/tab_ui/java/res/values/dimens.xml
+++ b/chrome/android/features/tab_ui/java/res/values/dimens.xml
@@ -83,6 +83,7 @@
     <dimen name="color_picker_inner_layer_inset">5dp</dimen>
     <dimen name="color_picker_selection_layer_inset">2dp</dimen>
     <dimen name="tab_group_color_icon_inset">2dp</dimen>
+    <dimen name="tab_group_color_icon_stroke">2dp</dimen>
     <dimen name="tab_group_color_icon_item_size">16dp</dimen>
     <dimen name="tab_group_color_picker_popup_padding">10dp</dimen>
     <dimen name="tab_group_visual_data_dialog_margin_padding">3dp</dimen>
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabCardViewBinderUtils.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabCardViewBinderUtils.java
new file mode 100644
index 0000000..5077e3b
--- /dev/null
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabCardViewBinderUtils.java
@@ -0,0 +1,65 @@
+// Copyright 2024 The Chromium Authors
+// 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.tasks.tab_management;
+
+import android.content.res.Resources;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import org.chromium.chrome.tab_ui.R;
+
+/** Shared utils for {@code Tab<Type>ViewBinder} such as List, Grid, Strip, etc. * */
+class TabCardViewBinderUtils {
+    /**
+     * Returns the checkmark level for selection mode.
+     *
+     * @param res The resources object.
+     * @param isSelected Whether the item is selected.
+     */
+    static int getCheckmarkLevel(@NonNull Resources res, boolean isSelected) {
+        return isSelected
+                ? res.getInteger(R.integer.list_item_level_selected)
+                : res.getInteger(R.integer.list_item_level_default);
+    }
+
+    /**
+     * Attaches the view from the provider to the container view or removes all views of the
+     * container if the view provider will not provide a view.
+     *
+     * @param containerView The container view to update.
+     * @param viewProvider The provider of the tab group color view to attach. Passing null will
+     *     detach all views from the container.
+     */
+    static void updateTabGroupColorView(
+            FrameLayout containerView, @Nullable TabGroupColorViewProvider viewProvider) {
+        if (viewProvider == null) {
+            containerView.setVisibility(View.GONE);
+            containerView.removeAllViews();
+        } else {
+            containerView.setVisibility(View.VISIBLE);
+            View colorView = viewProvider.getLazyView();
+            ViewGroup parentView = (ViewGroup) colorView.getParent();
+            if (parentView == containerView) return;
+
+            var layoutParams =
+                    new FrameLayout.LayoutParams(
+                            FrameLayout.LayoutParams.WRAP_CONTENT,
+                            FrameLayout.LayoutParams.WRAP_CONTENT);
+            layoutParams.gravity = Gravity.CENTER;
+            // If the parent view is null, attaching it will work. If the parent view is non-null
+            // and not the same as the containerView, as checked above, this will throw an
+            // exception. This is intended as we want to enforce an invariant that the provided
+            // views are eagerly detached from the view hierarchy.
+            containerView.addView(colorView, layoutParams);
+        }
+    }
+
+    private TabCardViewBinderUtils() {}
+}
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabCardViewBinderUtilsUnitTest.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabCardViewBinderUtilsUnitTest.java
new file mode 100644
index 0000000..d0ecd741
--- /dev/null
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabCardViewBinderUtilsUnitTest.java
@@ -0,0 +1,65 @@
+// Copyright 2024 The Chromium Authors
+// 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.tasks.tab_management;
+
+import static org.junit.Assert.assertEquals;
+
+import android.app.Activity;
+import android.content.Context;
+import android.view.View;
+import android.widget.FrameLayout;
+
+import androidx.test.ext.junit.rules.ActivityScenarioRule;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.chromium.base.Token;
+import org.chromium.base.test.BaseRobolectricTestRunner;
+import org.chromium.components.tab_groups.TabGroupColorId;
+import org.chromium.ui.base.TestActivity;
+
+/** Unit tests for {@link TabCardViewBinderUtils}. */
+@RunWith(BaseRobolectricTestRunner.class)
+public class TabCardViewBinderUtilsUnitTest {
+    @Rule
+    public ActivityScenarioRule<TestActivity> mActivityScenarioRule =
+            new ActivityScenarioRule<>(TestActivity.class);
+
+    private Context mContext;
+    private TabGroupColorViewProvider mTabGroupColorViewProvider;
+    private FrameLayout mContainerView;
+
+    @Before
+    public void setUp() {
+        mActivityScenarioRule.getScenario().onActivity(this::onActivityCreated);
+    }
+
+    private void onActivityCreated(Activity activity) {
+        mContext = activity;
+        mTabGroupColorViewProvider =
+                new TabGroupColorViewProvider(
+                        activity, new Token(2L, 1L), /* isIncognito= */ false, TabGroupColorId.RED);
+        mContainerView = new FrameLayout(activity);
+        activity.setContentView(mContainerView);
+    }
+
+    @Test
+    public void testUpdateTabGroupColorView() {
+        TabCardViewBinderUtils.updateTabGroupColorView(mContainerView, /* viewProvider= */ null);
+        assertEquals(View.GONE, mContainerView.getVisibility());
+        assertEquals(0, mContainerView.getChildCount());
+
+        TabCardViewBinderUtils.updateTabGroupColorView(mContainerView, mTabGroupColorViewProvider);
+        assertEquals(View.VISIBLE, mContainerView.getVisibility());
+        assertEquals(mTabGroupColorViewProvider.getLazyView(), mContainerView.getChildAt(0));
+
+        TabCardViewBinderUtils.updateTabGroupColorView(mContainerView, /* viewProvider= */ null);
+        assertEquals(View.GONE, mContainerView.getVisibility());
+        assertEquals(0, mContainerView.getChildCount());
+    }
+}
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogCoordinator.java
index b867a54..7e975cc 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogCoordinator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogCoordinator.java
@@ -45,10 +45,10 @@
 import org.chromium.chrome.browser.tasks.tab_management.TabListMediator.GridCardOnClickListenerProvider;
 import org.chromium.chrome.browser.tasks.tab_management.TabProperties.UiType;
 import org.chromium.chrome.browser.tasks.tab_management.TabUiMetricsHelper.TabGroupColorChangeActionType;
-import org.chromium.chrome.browser.ui.desktop_windowing.DesktopWindowStateProvider;
 import org.chromium.chrome.browser.ui.messages.snackbar.SnackbarManager;
 import org.chromium.chrome.tab_ui.R;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
+import org.chromium.components.browser_ui.desktop_windowing.DesktopWindowStateProvider;
 import org.chromium.components.browser_ui.widget.scrim.ScrimCoordinator;
 import org.chromium.components.data_sharing.DataSharingService;
 import org.chromium.ui.modaldialog.ModalDialogManager;
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediator.java
index 32a7317..1549000 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediator.java
@@ -59,12 +59,12 @@
 import org.chromium.chrome.browser.tasks.tab_management.TabUiMetricsHelper.TabGroupColorChangeActionType;
 import org.chromium.chrome.browser.tasks.tab_management.TabUiMetricsHelper.TabListEditorOpenMetricGroups;
 import org.chromium.chrome.browser.tinker_tank.TinkerTankDelegate;
-import org.chromium.chrome.browser.ui.desktop_windowing.AppHeaderState;
-import org.chromium.chrome.browser.ui.desktop_windowing.DesktopWindowStateProvider;
-import org.chromium.chrome.browser.ui.desktop_windowing.DesktopWindowStateProvider.AppHeaderObserver;
 import org.chromium.chrome.browser.ui.messages.snackbar.Snackbar;
 import org.chromium.chrome.browser.ui.messages.snackbar.SnackbarManager;
 import org.chromium.chrome.tab_ui.R;
+import org.chromium.components.browser_ui.desktop_windowing.AppHeaderState;
+import org.chromium.components.browser_ui.desktop_windowing.DesktopWindowStateProvider;
+import org.chromium.components.browser_ui.desktop_windowing.DesktopWindowStateProvider.AppHeaderObserver;
 import org.chromium.components.browser_ui.styles.SemanticColorUtils;
 import org.chromium.components.browser_ui.widget.gesture.BackPressHandler;
 import org.chromium.components.data_sharing.DataSharingService;
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridViewBinder.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridViewBinder.java
index 8ffe7be..27b1d99 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridViewBinder.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridViewBinder.java
@@ -474,7 +474,9 @@
 
         Context context = rootView.getContext();
         Resources res = rootView.getResources();
-        actionButton.getBackground().setLevel(getCheckmarkLevel(res, isSelected));
+        actionButton
+                .getBackground()
+                .setLevel(TabCardViewBinderUtils.getCheckmarkLevel(res, isSelected));
         DrawableCompat.setTintList(
                 actionButton.getBackground().mutate(),
                 TabUiThemeProvider.getToggleActionButtonBackgroundTintList(
@@ -509,13 +511,7 @@
         labelView.setData(tabCardLabelData);
     }
 
-    private static int getCheckmarkLevel(Resources res, boolean isSelected) {
-        return isSelected
-                ? res.getInteger(R.integer.list_item_level_selected)
-                : res.getInteger(R.integer.list_item_level_default);
-    }
-
-    static void setThumbnailFeatureForTesting(ThumbnailFetcher fetcher) {
+    static void setThumbnailFetcherForTesting(ThumbnailFetcher fetcher) {
         sThumbnailFetcherForTesting = fetcher;
         ResettersForTesting.register(() -> sThumbnailFetcherForTesting = null);
     }
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupColorViewProvider.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupColorViewProvider.java
new file mode 100644
index 0000000..3d6b5db
--- /dev/null
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupColorViewProvider.java
@@ -0,0 +1,91 @@
+// Copyright 2024 The Chromium Authors
+// 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.tasks.tab_management;
+
+import android.content.Context;
+import android.graphics.drawable.GradientDrawable;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.annotation.ColorInt;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import org.chromium.base.Token;
+import org.chromium.chrome.R;
+import org.chromium.components.tab_groups.TabGroupColorId;
+
+/** Provides a view for tab group color dots. */
+public class TabGroupColorViewProvider {
+    private final @NonNull Context mContext;
+    private final @NonNull Token mTabGroupId;
+    private final boolean mIsIncognito;
+
+    private @Nullable ViewGroup mViewGroup;
+    private @TabGroupColorId int mColorId;
+
+    /**
+     * @param context The context to use to use for creating the view.
+     * @param tabGroupId The tab group id for the group.
+     * @param isIncognito Whether the tab group is incognito.
+     * @param colorId The {@link TabGroupColorId} to show for the main color.
+     */
+    public TabGroupColorViewProvider(
+            @NonNull Context context,
+            @NonNull Token tabGroupId,
+            boolean isIncognito,
+            @TabGroupColorId int colorId) {
+        assert tabGroupId != null;
+        mContext = context;
+        mTabGroupId = tabGroupId;
+        mIsIncognito = isIncognito;
+        mColorId = colorId;
+    }
+
+    /** Returns the tab group id that this tab group color view provider is for. */
+    public @NonNull Token getTabGroupId() {
+        return mTabGroupId;
+    }
+
+    /**
+     * Sets the tab group color by id, this will update the view immediately if it exists.
+     *
+     * @param colorId The color id to use.
+     */
+    public void setTabGroupColorId(@TabGroupColorId int colorId) {
+        mColorId = colorId;
+
+        if (mViewGroup != null) {
+            updateColor();
+        }
+    }
+
+    /** Returns the color dot view, creating it if it does not exist. */
+    public @NonNull View getLazyView() {
+        if (mViewGroup == null) {
+            mViewGroup =
+                    (ViewGroup)
+                            LayoutInflater.from(mContext)
+                                    .inflate(R.layout.tab_group_color_container, null);
+            assert mViewGroup != null;
+
+            updateColor();
+        }
+        return mViewGroup;
+    }
+
+    private void updateColor() {
+        assert mViewGroup != null;
+
+        GradientDrawable drawable = (GradientDrawable) mViewGroup.getBackground();
+        assert drawable != null;
+
+        final @ColorInt int color =
+                ColorPickerUtils.getTabGroupColorPickerItemColor(mContext, mColorId, mIsIncognito);
+        drawable.setColor(color);
+        mViewGroup.invalidate();
+    }
+}
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupColorViewProviderUnitTest.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupColorViewProviderUnitTest.java
new file mode 100644
index 0000000..1680b55
--- /dev/null
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupColorViewProviderUnitTest.java
@@ -0,0 +1,104 @@
+// Copyright 2024 The Chromium Authors
+// 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.tasks.tab_management;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.drawable.GradientDrawable;
+import android.view.View;
+
+import androidx.test.ext.junit.rules.ActivityScenarioRule;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.chromium.base.Token;
+import org.chromium.base.test.BaseRobolectricTestRunner;
+import org.chromium.components.tab_groups.TabGroupColorId;
+import org.chromium.ui.base.TestActivity;
+
+/** Unit tests for {@link TabGroupColorViewProvider}. */
+@RunWith(BaseRobolectricTestRunner.class)
+public class TabGroupColorViewProviderUnitTest {
+    private static final Token REGULAR_TAB_GROUP_ID = new Token(3L, 4L);
+    private static final Token INCOGNITO_TAB_GROUP_ID = new Token(5L, 6L);
+
+    @Rule
+    public ActivityScenarioRule<TestActivity> mActivityScenarioRule =
+            new ActivityScenarioRule<>(TestActivity.class);
+
+    private Context mContext;
+    private TabGroupColorViewProvider mRegularColorViewProvider;
+    private TabGroupColorViewProvider mIncognitoColorViewProvider;
+
+    @Before
+    public void setUp() {
+        mActivityScenarioRule.getScenario().onActivity(this::onActivityCreated);
+    }
+
+    private void onActivityCreated(Activity activity) {
+        mContext = activity;
+        mRegularColorViewProvider =
+                new TabGroupColorViewProvider(
+                        activity,
+                        REGULAR_TAB_GROUP_ID,
+                        /* isIncognito= */ false,
+                        TabGroupColorId.RED);
+        mIncognitoColorViewProvider =
+                new TabGroupColorViewProvider(
+                        activity,
+                        INCOGNITO_TAB_GROUP_ID,
+                        /* isIncognito= */ true,
+                        TabGroupColorId.BLUE);
+    }
+
+    @Test
+    public void testGetTabGroupId() {
+        assertEquals(REGULAR_TAB_GROUP_ID, mRegularColorViewProvider.getTabGroupId());
+        assertEquals(INCOGNITO_TAB_GROUP_ID, mIncognitoColorViewProvider.getTabGroupId());
+    }
+
+    @Test
+    public void testColorView() {
+        verifyColorView(
+                mRegularColorViewProvider,
+                /* isIncognito= */ false,
+                TabGroupColorId.RED,
+                TabGroupColorId.CYAN);
+        verifyColorView(
+                mIncognitoColorViewProvider,
+                /* isIncognito= */ true,
+                TabGroupColorId.BLUE,
+                TabGroupColorId.PURPLE);
+    }
+
+    private void verifyColorView(
+            TabGroupColorViewProvider viewProvider,
+            boolean isIncognito,
+            @TabGroupColorId int initialColorId,
+            @TabGroupColorId int finalColorId) {
+        View colorView = viewProvider.getLazyView();
+        GradientDrawable drawable = (GradientDrawable) colorView.getBackground();
+        assertNotNull(drawable);
+
+        assertEquals(
+                ColorPickerUtils.getTabGroupColorPickerItemColor(
+                        mContext, initialColorId, isIncognito),
+                drawable.getColor().getDefaultColor());
+
+        viewProvider.setTabGroupColorId(finalColorId);
+        assertEquals(colorView, viewProvider.getLazyView());
+
+        assertEquals(
+                ColorPickerUtils.getTabGroupColorPickerItemColor(
+                        mContext, finalColorId, isIncognito),
+                drawable.getColor().getDefaultColor());
+    }
+}
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListViewBinder.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListViewBinder.java
index fba3e6e..0d67b13 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListViewBinder.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListViewBinder.java
@@ -249,7 +249,7 @@
 
             Context context = view.getContext();
             Resources res = view.getResources();
-            int level = getCheckmarkLevel(res, isSelected);
+            int level = TabCardViewBinderUtils.getCheckmarkLevel(res, isSelected);
             ColorStateList backgroundColorStateList =
                     getBackgroundColorStateList(context, isSelected, isIncognito);
 
@@ -315,12 +315,6 @@
         }
     }
 
-    private static int getCheckmarkLevel(Resources res, boolean isSelected) {
-        return isSelected
-                ? res.getInteger(R.integer.list_item_level_selected)
-                : res.getInteger(R.integer.list_item_level_default);
-    }
-
     private static ColorStateList getCheckedDrawableColorStateList(
             Context context, boolean isIncognito) {
         return ColorStateList.valueOf(
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabManagementDelegate.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabManagementDelegate.java
index db4c5b29..b0c56c9f 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabManagementDelegate.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabManagementDelegate.java
@@ -32,10 +32,10 @@
 import org.chromium.chrome.browser.tabmodel.IncognitoStateProvider;
 import org.chromium.chrome.browser.tabmodel.TabCreatorManager;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
-import org.chromium.chrome.browser.ui.desktop_windowing.DesktopWindowStateProvider;
 import org.chromium.chrome.browser.ui.edge_to_edge.EdgeToEdgeController;
 import org.chromium.chrome.browser.ui.messages.snackbar.SnackbarManager;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
+import org.chromium.components.browser_ui.desktop_windowing.DesktopWindowStateProvider;
 import org.chromium.components.browser_ui.widget.scrim.ScrimCoordinator;
 import org.chromium.components.tab_group_sync.TabGroupUiActionHandler;
 import org.chromium.ui.modaldialog.ModalDialogManager;
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabManagementDelegateImpl.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabManagementDelegateImpl.java
index d8fff1c..15a24acf 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabManagementDelegateImpl.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabManagementDelegateImpl.java
@@ -37,11 +37,11 @@
 import org.chromium.chrome.browser.tabmodel.TabCreatorManager;
 import org.chromium.chrome.browser.tabmodel.TabModelFilter;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
-import org.chromium.chrome.browser.ui.desktop_windowing.DesktopWindowStateProvider;
 import org.chromium.chrome.browser.ui.edge_to_edge.EdgeToEdgeController;
 import org.chromium.chrome.browser.ui.messages.snackbar.SnackbarManager;
 import org.chromium.chrome.browser.user_education.UserEducationHelper;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
+import org.chromium.components.browser_ui.desktop_windowing.DesktopWindowStateProvider;
 import org.chromium.components.browser_ui.widget.scrim.ScrimCoordinator;
 import org.chromium.components.tab_group_sync.TabGroupUiActionHandler;
 import org.chromium.ui.modaldialog.ModalDialogManager;
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherPaneCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherPaneCoordinator.java
index 3366a0e..e4245e5 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherPaneCoordinator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherPaneCoordinator.java
@@ -56,12 +56,12 @@
 import org.chromium.chrome.browser.tasks.tab_management.TabListCoordinator.TabListMode;
 import org.chromium.chrome.browser.tasks.tab_management.TabListMediator.GridCardOnClickListenerProvider;
 import org.chromium.chrome.browser.tasks.tab_management.TabSwitcherMessageManager.MessageUpdateObserver;
-import org.chromium.chrome.browser.ui.desktop_windowing.DesktopWindowStateProvider;
 import org.chromium.chrome.browser.ui.edge_to_edge.EdgeToEdgeController;
 import org.chromium.chrome.browser.ui.edge_to_edge.EdgeToEdgePadAdjuster;
 import org.chromium.chrome.browser.ui.edge_to_edge.EdgeToEdgeUtils;
 import org.chromium.chrome.tab_ui.R;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
+import org.chromium.components.browser_ui.desktop_windowing.DesktopWindowStateProvider;
 import org.chromium.components.browser_ui.widget.gesture.BackPressHandler;
 import org.chromium.components.browser_ui.widget.scrim.ScrimCoordinator;
 import org.chromium.ui.base.DeviceFormFactor;
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherPaneCoordinatorFactory.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherPaneCoordinatorFactory.java
index abaf40e1..bb38b62 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherPaneCoordinatorFactory.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherPaneCoordinatorFactory.java
@@ -29,11 +29,11 @@
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
 import org.chromium.chrome.browser.tabmodel.TabModelSelectorObserver;
 import org.chromium.chrome.browser.tasks.tab_management.TabListCoordinator.TabListMode;
-import org.chromium.chrome.browser.ui.desktop_windowing.DesktopWindowStateProvider;
 import org.chromium.chrome.browser.ui.edge_to_edge.EdgeToEdgeController;
 import org.chromium.chrome.browser.ui.messages.snackbar.SnackbarManager;
 import org.chromium.chrome.tab_ui.R;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
+import org.chromium.components.browser_ui.desktop_windowing.DesktopWindowStateProvider;
 import org.chromium.components.browser_ui.widget.scrim.ScrimCoordinator;
 import org.chromium.components.browser_ui.widget.scrim.ScrimCoordinator.SystemUiScrimDelegate;
 import org.chromium.ui.base.DeviceFormFactor;
diff --git a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherThumbnailTest.java b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherThumbnailTest.java
index 6d795118..25c6a20 100644
--- a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherThumbnailTest.java
+++ b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherThumbnailTest.java
@@ -57,7 +57,7 @@
     @Before
     public void setUp() {
         mActivityTestRule.startMainActivityWithURL(UrlConstants.NTP_URL);
-        TabGridViewBinder.setThumbnailFeatureForTesting(mNullThumbnailFetcher);
+        TabGridViewBinder.setThumbnailFetcherForTesting(mNullThumbnailFetcher);
     }
 
     @Test
diff --git a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediatorUnitTest.java b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediatorUnitTest.java
index 376d415..7d277d4 100644
--- a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediatorUnitTest.java
+++ b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediatorUnitTest.java
@@ -92,11 +92,11 @@
 import org.chromium.chrome.browser.tasks.tab_groups.TabGroupModelFilter;
 import org.chromium.chrome.browser.tasks.tab_groups.TabGroupModelFilterObserver;
 import org.chromium.chrome.browser.tasks.tab_management.MessageService.MessageType;
-import org.chromium.chrome.browser.ui.desktop_windowing.AppHeaderState;
-import org.chromium.chrome.browser.ui.desktop_windowing.DesktopWindowStateProvider;
 import org.chromium.chrome.browser.ui.messages.snackbar.Snackbar;
 import org.chromium.chrome.browser.ui.messages.snackbar.SnackbarManager;
 import org.chromium.chrome.tab_ui.R;
+import org.chromium.components.browser_ui.desktop_windowing.AppHeaderState;
+import org.chromium.components.browser_ui.desktop_windowing.DesktopWindowStateProvider;
 import org.chromium.components.data_sharing.DataSharingService;
 import org.chromium.components.data_sharing.DataSharingService.GroupDataOrFailureOutcome;
 import org.chromium.components.data_sharing.GroupMember;
diff --git a/chrome/android/features/tab_ui/tab_management_java_sources.gni b/chrome/android/features/tab_ui/tab_management_java_sources.gni
index 529a373..d0415689 100644
--- a/chrome/android/features/tab_ui/tab_management_java_sources.gni
+++ b/chrome/android/features/tab_ui/tab_management_java_sources.gni
@@ -78,6 +78,7 @@
   "//chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabCardLabelData.java",
   "//chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabCardLabelType.java",
   "//chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabCardLabelView.java",
+  "//chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabCardViewBinderUtils.java",
   "//chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogCoordinator.java",
   "//chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediator.java",
   "//chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMenuCoordinator.java",
@@ -89,6 +90,7 @@
   "//chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridView.java",
   "//chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridViewBinder.java",
   "//chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupColorPickerContainer.java",
+  "//chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupColorViewProvider.java",
   "//chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupCreationDialogManager.java",
   "//chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupFaviconCluster.java",
   "//chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupFaviconQuarter.java",
@@ -218,7 +220,9 @@
   "//chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/SharedGroupObserverUnitTest.java",
   "//chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/StrictButtonPressControllerUnitTest.java",
   "//chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabArchiveSettingsFragmentUnitTest.java",
+  "//chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabCardViewBinderUtilsUnitTest.java",
   "//chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMenuCoordinatorUnitTest.java",
+  "//chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupColorViewProviderUnitTest.java",
   "//chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupFaviconQuarterUnitTest.java",
   "//chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupListCoordinatorUnitTest.java",
   "//chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupListMediatorUnitTest.java",
diff --git a/chrome/android/java/DEPS b/chrome/android/java/DEPS
index 5fe9085..1a8d30d 100644
--- a/chrome/android/java/DEPS
+++ b/chrome/android/java/DEPS
@@ -28,6 +28,7 @@
   "+components/background_task_scheduler/android/java/src/org/chromium/components/background_task_scheduler",
   "+components/bookmarks/common/android/java/src/org/chromium/components/bookmarks",
   "+components/browser_ui/accessibility",
+  "+components/browser_ui/desktop_windowing/android",
   "+components/browser_ui/http_auth",
   "+components/browser_ui/notifications/android",
   "+components/browser_ui/settings/android",
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerChrome.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerChrome.java
index 9bcaf8d0..c32e915c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerChrome.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerChrome.java
@@ -33,8 +33,8 @@
 import org.chromium.chrome.browser.theme.ThemeColorProvider;
 import org.chromium.chrome.browser.theme.TopUiThemeColorProvider;
 import org.chromium.chrome.browser.toolbar.ControlContainer;
-import org.chromium.chrome.browser.ui.desktop_windowing.DesktopWindowStateProvider;
 import org.chromium.chrome.browser.util.ChromeAccessibilityUtil;
+import org.chromium.components.browser_ui.desktop_windowing.DesktopWindowStateProvider;
 import org.chromium.components.browser_ui.widget.gesture.SwipeGestureListener.ScrollDirection;
 import org.chromium.components.browser_ui.widget.gesture.SwipeGestureListener.SwipeHandler;
 import org.chromium.ui.resources.dynamics.DynamicResourceLoader;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerChromeTablet.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerChromeTablet.java
index c044b4e..3c5e173 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerChromeTablet.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerChromeTablet.java
@@ -33,7 +33,7 @@
 import org.chromium.chrome.browser.theme.TopUiThemeColorProvider;
 import org.chromium.chrome.browser.toolbar.ControlContainer;
 import org.chromium.chrome.browser.toolbar.ToolbarManager;
-import org.chromium.chrome.browser.ui.desktop_windowing.DesktopWindowStateProvider;
+import org.chromium.components.browser_ui.desktop_windowing.DesktopWindowStateProvider;
 import org.chromium.ui.base.WindowAndroid;
 import org.chromium.ui.dragdrop.DragAndDropDelegate;
 import org.chromium.ui.resources.dynamics.DynamicResourceLoader;
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 e17c159..1400bbed 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
@@ -84,11 +84,11 @@
 import org.chromium.chrome.browser.toolbar.ToolbarFeatures;
 import org.chromium.chrome.browser.toolbar.ToolbarManager;
 import org.chromium.chrome.browser.toolbar.top.tab_strip.TabStripTransitionCoordinator.TabStripTransitionDelegate;
-import org.chromium.chrome.browser.ui.desktop_windowing.AppHeaderState;
 import org.chromium.chrome.browser.ui.desktop_windowing.AppHeaderUtils;
-import org.chromium.chrome.browser.ui.desktop_windowing.DesktopWindowStateProvider;
-import org.chromium.chrome.browser.ui.desktop_windowing.DesktopWindowStateProvider.AppHeaderObserver;
 import org.chromium.chrome.browser.ui.system.StatusBarColorController;
+import org.chromium.components.browser_ui.desktop_windowing.AppHeaderState;
+import org.chromium.components.browser_ui.desktop_windowing.DesktopWindowStateProvider;
+import org.chromium.components.browser_ui.desktop_windowing.DesktopWindowStateProvider.AppHeaderObserver;
 import org.chromium.components.browser_ui.styles.SemanticColorUtils;
 import org.chromium.components.browser_ui.widget.scrim.ScrimProperties;
 import org.chromium.content_public.browser.LoadUrlParams;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/hub/HubLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/hub/HubLayout.java
index 591a94f..03060c91 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/hub/HubLayout.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/hub/HubLayout.java
@@ -54,9 +54,9 @@
 import org.chromium.chrome.browser.tab_ui.TabContentManager;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
 import org.chromium.chrome.browser.tabmodel.TabModelUtils;
-import org.chromium.chrome.browser.ui.desktop_windowing.AppHeaderState;
-import org.chromium.chrome.browser.ui.desktop_windowing.DesktopWindowStateProvider;
-import org.chromium.chrome.browser.ui.desktop_windowing.DesktopWindowStateProvider.AppHeaderObserver;
+import org.chromium.components.browser_ui.desktop_windowing.AppHeaderState;
+import org.chromium.components.browser_ui.desktop_windowing.DesktopWindowStateProvider;
+import org.chromium.components.browser_ui.desktop_windowing.DesktopWindowStateProvider.AppHeaderObserver;
 import org.chromium.components.browser_ui.styles.ChromeColors;
 import org.chromium.ui.base.DeviceFormFactor;
 import org.chromium.ui.base.LocalizationUtils;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/hub/HubLayoutUnitTest.java b/chrome/android/java/src/org/chromium/chrome/browser/hub/HubLayoutUnitTest.java
index 39be9334..bacd88e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/hub/HubLayoutUnitTest.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/hub/HubLayoutUnitTest.java
@@ -81,7 +81,7 @@
 import org.chromium.chrome.browser.tab.TabHidingType;
 import org.chromium.chrome.browser.tab_ui.TabContentManager;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
-import org.chromium.chrome.browser.ui.desktop_windowing.DesktopWindowStateProvider;
+import org.chromium.components.browser_ui.desktop_windowing.DesktopWindowStateProvider;
 import org.chromium.ui.base.TestActivity;
 import org.chromium.ui.resources.ResourceManager;
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/multiwindow/MultiInstanceManager.java b/chrome/android/java/src/org/chromium/chrome/browser/multiwindow/MultiInstanceManager.java
index d8f9a9ef..b1fc36b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/multiwindow/MultiInstanceManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/multiwindow/MultiInstanceManager.java
@@ -40,8 +40,8 @@
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
 import org.chromium.chrome.browser.tabmodel.TabModelSelectorTabModelObserver;
-import org.chromium.chrome.browser.ui.desktop_windowing.DesktopWindowStateProvider;
 import org.chromium.chrome.browser.util.AndroidTaskUtils;
+import org.chromium.components.browser_ui.desktop_windowing.DesktopWindowStateProvider;
 import org.chromium.components.browser_ui.widget.MenuOrKeyboardActionController;
 import org.chromium.ui.display.DisplayAndroidManager;
 import org.chromium.ui.modaldialog.ModalDialogManager;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/multiwindow/MultiInstanceManagerApi31.java b/chrome/android/java/src/org/chromium/chrome/browser/multiwindow/MultiInstanceManagerApi31.java
index ef86db04..e22f0e45 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/multiwindow/MultiInstanceManagerApi31.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/multiwindow/MultiInstanceManagerApi31.java
@@ -61,8 +61,8 @@
 import org.chromium.chrome.browser.tasks.tab_groups.TabGroupModelFilter;
 import org.chromium.chrome.browser.tasks.tab_management.TabUiFeatureUtilities;
 import org.chromium.chrome.browser.ui.desktop_windowing.AppHeaderUtils;
-import org.chromium.chrome.browser.ui.desktop_windowing.DesktopWindowStateProvider;
 import org.chromium.chrome.browser.util.AndroidTaskUtils;
+import org.chromium.components.browser_ui.desktop_windowing.DesktopWindowStateProvider;
 import org.chromium.components.browser_ui.widget.MenuOrKeyboardActionController;
 import org.chromium.components.favicon.LargeIconBridge;
 import org.chromium.components.feature_engagement.EventConstants;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/multiwindow/MultiWindowUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/multiwindow/MultiWindowUtils.java
index 9e7121de..4b3783b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/multiwindow/MultiWindowUtils.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/multiwindow/MultiWindowUtils.java
@@ -49,8 +49,8 @@
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
 import org.chromium.chrome.browser.tabmodel.TabWindowManager;
 import org.chromium.chrome.browser.ui.desktop_windowing.AppHeaderUtils;
-import org.chromium.chrome.browser.ui.desktop_windowing.DesktopWindowStateProvider;
 import org.chromium.chrome.browser.util.AndroidTaskUtils;
+import org.chromium.components.browser_ui.desktop_windowing.DesktopWindowStateProvider;
 import org.chromium.components.ukm.UkmRecorder;
 import org.chromium.ui.display.DisplayAndroidManager;
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/TabbedRootUiCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/TabbedRootUiCoordinator.java
index 98d878a..1e3b9b2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/TabbedRootUiCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/TabbedRootUiCoordinator.java
@@ -129,7 +129,6 @@
 import org.chromium.chrome.browser.ui.default_browser_promo.DefaultBrowserPromoUtils;
 import org.chromium.chrome.browser.ui.desktop_windowing.AppHeaderCoordinator;
 import org.chromium.chrome.browser.ui.desktop_windowing.AppHeaderUtils;
-import org.chromium.chrome.browser.ui.desktop_windowing.DesktopWindowStateProvider;
 import org.chromium.chrome.browser.ui.edge_to_edge.EdgeToEdgeController;
 import org.chromium.chrome.browser.ui.edge_to_edge.EdgeToEdgeControllerFactory;
 import org.chromium.chrome.browser.ui.messages.snackbar.SnackbarManager;
@@ -138,6 +137,7 @@
 import org.chromium.chrome.browser.webapps.PwaRestorePromoUtils;
 import org.chromium.components.browser_ui.accessibility.PageZoomCoordinator;
 import org.chromium.components.browser_ui.bottomsheet.EmptyBottomSheetObserver;
+import org.chromium.components.browser_ui.desktop_windowing.DesktopWindowStateProvider;
 import org.chromium.components.browser_ui.widget.CoordinatorLayoutForPointer;
 import org.chromium.components.browser_ui.widget.MenuOrKeyboardActionController;
 import org.chromium.components.browser_ui.widget.TouchEventObserver;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/AppThemeColorProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/AppThemeColorProvider.java
index 7a2a84e..93252214 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/AppThemeColorProvider.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/AppThemeColorProvider.java
@@ -18,8 +18,8 @@
 import org.chromium.chrome.browser.theme.ThemeColorProvider;
 import org.chromium.chrome.browser.theme.ThemeUtils;
 import org.chromium.chrome.browser.ui.desktop_windowing.AppHeaderUtils;
-import org.chromium.chrome.browser.ui.desktop_windowing.DesktopWindowStateProvider;
 import org.chromium.chrome.browser.ui.theme.BrandedColorScheme;
+import org.chromium.components.browser_ui.desktop_windowing.DesktopWindowStateProvider;
 import org.chromium.components.browser_ui.styles.ChromeColors;
 
 /** A ThemeColorProvider for the app theme (incognito or standard theming). */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/DEPS b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/DEPS
index b266164..2b42316 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/DEPS
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/DEPS
@@ -14,6 +14,7 @@
   "+ui/android/java/src/org/chromium/ui",
   "+url/android",
   "+components/browser_ui/bottomsheet/android/java",
+  "+components/browser_ui/desktop_windowing/android",
   "+content/public/android/java/src/org/chromium/content_public",
 ]
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java
index af12a99..320e3e0 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java
@@ -152,7 +152,6 @@
 import org.chromium.chrome.browser.ui.appmenu.AppMenuCoordinator;
 import org.chromium.chrome.browser.ui.appmenu.AppMenuDelegate;
 import org.chromium.chrome.browser.ui.appmenu.MenuButtonDelegate;
-import org.chromium.chrome.browser.ui.desktop_windowing.DesktopWindowStateProvider;
 import org.chromium.chrome.browser.ui.edge_to_edge.EdgeToEdgeController;
 import org.chromium.chrome.browser.ui.native_page.NativePage;
 import org.chromium.chrome.browser.ui.system.StatusBarColorController;
@@ -163,6 +162,7 @@
 import org.chromium.chrome.browser.util.BrowserUiUtils.ModuleTypeOnStartAndNtp;
 import org.chromium.chrome.browser.util.ChromeAccessibilityUtil;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
+import org.chromium.components.browser_ui.desktop_windowing.DesktopWindowStateProvider;
 import org.chromium.components.browser_ui.styles.ChromeColors;
 import org.chromium.components.browser_ui.widget.gesture.BackPressHandler;
 import org.chromium.components.browser_ui.widget.gesture.BackPressHandler.BackPressResult;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ui/RootUiCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/ui/RootUiCoordinator.java
index 8d075ac..39d199c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ui/RootUiCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ui/RootUiCoordinator.java
@@ -158,7 +158,6 @@
 import org.chromium.chrome.browser.ui.appmenu.AppMenuDelegate;
 import org.chromium.chrome.browser.ui.appmenu.AppMenuHandler;
 import org.chromium.chrome.browser.ui.appmenu.AppMenuObserver;
-import org.chromium.chrome.browser.ui.desktop_windowing.DesktopWindowStateProvider;
 import org.chromium.chrome.browser.ui.edge_to_edge.EdgeToEdgeController;
 import org.chromium.chrome.browser.ui.edge_to_edge.EdgeToEdgeControllerFactory;
 import org.chromium.chrome.browser.ui.edge_to_edge.EdgeToEdgeUtils;
@@ -177,6 +176,7 @@
 import org.chromium.components.browser_ui.bottomsheet.EmptyBottomSheetObserver;
 import org.chromium.components.browser_ui.bottomsheet.ExpandedSheetHelper;
 import org.chromium.components.browser_ui.bottomsheet.ManagedBottomSheetController;
+import org.chromium.components.browser_ui.desktop_windowing.DesktopWindowStateProvider;
 import org.chromium.components.browser_ui.device_lock.DeviceLockActivityLauncher;
 import org.chromium.components.browser_ui.device_lock.DeviceLockActivityLauncherSupplier;
 import org.chromium.components.browser_ui.util.ComposedBrowserControlsVisibilityDelegate;
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/desktop_windowing/AppHeaderCoordinatorBrowserTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/desktop_windowing/AppHeaderCoordinatorBrowserTest.java
index 5c8e8765..9ec059c 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/desktop_windowing/AppHeaderCoordinatorBrowserTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/desktop_windowing/AppHeaderCoordinatorBrowserTest.java
@@ -58,12 +58,12 @@
 import org.chromium.chrome.browser.toolbar.top.ToolbarTablet;
 import org.chromium.chrome.browser.toolbar.top.tab_strip.TabStripTransitionCoordinator;
 import org.chromium.chrome.browser.ui.desktop_windowing.AppHeaderCoordinator;
-import org.chromium.chrome.browser.ui.desktop_windowing.AppHeaderState;
 import org.chromium.chrome.browser.ui.desktop_windowing.AppHeaderUtils;
 import org.chromium.chrome.browser.ui.theme.BrandedColorScheme;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
 import org.chromium.chrome.test.R;
+import org.chromium.components.browser_ui.desktop_windowing.AppHeaderState;
 import org.chromium.content_public.browser.test.util.DOMUtils;
 import org.chromium.content_public.browser.test.util.JavaScriptUtils;
 import org.chromium.ui.InsetObserver;
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperManagerTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperManagerTest.java
index b48fbc9..80a9f5b 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperManagerTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperManagerTest.java
@@ -84,9 +84,9 @@
 import org.chromium.chrome.browser.tasks.tab_management.TabUiThemeUtil;
 import org.chromium.chrome.browser.toolbar.ToolbarFeatures;
 import org.chromium.chrome.browser.toolbar.ToolbarManager;
-import org.chromium.chrome.browser.ui.desktop_windowing.AppHeaderState;
-import org.chromium.chrome.browser.ui.desktop_windowing.DesktopWindowStateProvider;
 import org.chromium.chrome.browser.ui.system.StatusBarColorController;
+import org.chromium.components.browser_ui.desktop_windowing.AppHeaderState;
+import org.chromium.components.browser_ui.desktop_windowing.DesktopWindowStateProvider;
 import org.chromium.components.browser_ui.styles.ChromeColors;
 import org.chromium.components.browser_ui.styles.SemanticColorUtils;
 import org.chromium.components.browser_ui.widget.scrim.ScrimProperties;
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/multiwindow/MultiInstanceManagerApi31UnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/multiwindow/MultiInstanceManagerApi31UnitTest.java
index 84f8334b..c22cdb05 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/multiwindow/MultiInstanceManagerApi31UnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/multiwindow/MultiInstanceManagerApi31UnitTest.java
@@ -80,9 +80,9 @@
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
 import org.chromium.chrome.browser.tabmodel.TabModelSelectorBase;
 import org.chromium.chrome.browser.tabmodel.TabModelSelectorFactory;
-import org.chromium.chrome.browser.ui.desktop_windowing.AppHeaderState;
-import org.chromium.chrome.browser.ui.desktop_windowing.DesktopWindowStateProvider;
 import org.chromium.chrome.test.util.browser.tabmodel.MockTabModelSelector;
+import org.chromium.components.browser_ui.desktop_windowing.AppHeaderState;
+import org.chromium.components.browser_ui.desktop_windowing.DesktopWindowStateProvider;
 import org.chromium.components.browser_ui.widget.MenuOrKeyboardActionController;
 import org.chromium.components.tab_group_sync.TabGroupSyncService;
 import org.chromium.ui.modaldialog.ModalDialogManager;
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/multiwindow/MultiWindowUtilsUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/multiwindow/MultiWindowUtilsUnitTest.java
index ddb9bc1..aa4b1c7b 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/multiwindow/MultiWindowUtilsUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/multiwindow/MultiWindowUtilsUnitTest.java
@@ -38,9 +38,9 @@
 import org.chromium.chrome.browser.multiwindow.MultiWindowUtilsUnitTest.ShadowMultiInstanceManagerApi31;
 import org.chromium.chrome.browser.tabmodel.TabModel;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
-import org.chromium.chrome.browser.ui.desktop_windowing.AppHeaderState;
-import org.chromium.chrome.browser.ui.desktop_windowing.DesktopWindowStateProvider;
 import org.chromium.chrome.test.AutomotiveContextWrapperTestRule;
+import org.chromium.components.browser_ui.desktop_windowing.AppHeaderState;
+import org.chromium.components.browser_ui.desktop_windowing.DesktopWindowStateProvider;
 import org.chromium.components.embedder_support.util.UrlConstants;
 import org.chromium.url.GURL;
 
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/toolbar/AppThemeColorProviderUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/toolbar/AppThemeColorProviderUnitTest.java
index 4c31eafd..d1327b4 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/toolbar/AppThemeColorProviderUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/toolbar/AppThemeColorProviderUnitTest.java
@@ -29,9 +29,9 @@
 import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
 import org.chromium.chrome.browser.theme.ThemeColorProvider.TintObserver;
 import org.chromium.chrome.browser.theme.ThemeUtils;
-import org.chromium.chrome.browser.ui.desktop_windowing.AppHeaderState;
-import org.chromium.chrome.browser.ui.desktop_windowing.DesktopWindowStateProvider;
 import org.chromium.chrome.browser.ui.theme.BrandedColorScheme;
+import org.chromium.components.browser_ui.desktop_windowing.AppHeaderState;
+import org.chromium.components.browser_ui.desktop_windowing.DesktopWindowStateProvider;
 
 /** Unit tests for {@link AppThemeColorProvider}. */
 @RunWith(BaseRobolectricTestRunner.class)
diff --git a/chrome/app/chrome_main_delegate.cc b/chrome/app/chrome_main_delegate.cc
index 9c1a840..84d2e945 100644
--- a/chrome/app/chrome_main_delegate.cc
+++ b/chrome/app/chrome_main_delegate.cc
@@ -1526,13 +1526,6 @@
   SetUpInstallerPreferences(command_line);
 #endif
 
-#if defined(ARCH_CPU_ARM_FAMILY) && \
-    (BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS))
-  // Create an instance of the CPU class to parse /proc/cpuinfo and cache
-  // cpu_brand info.
-  base::CPU cpu_info;
-#endif
-
   // Initialize the user data dir for any process type that needs it.
   bool initialize_user_data_dir = chrome::ProcessNeedsProfileDir(process_type);
 #if BUILDFLAG(IS_CHROMEOS_LACROS)
diff --git a/chrome/app/os_settings_strings.grdp b/chrome/app/os_settings_strings.grdp
index e7bae11..fa835f85e 100644
--- a/chrome/app/os_settings_strings.grdp
+++ b/chrome/app/os_settings_strings.grdp
@@ -5523,7 +5523,7 @@
     Password
   </message>
   <message name="IDS_SETTINGS_PEOPLE_LOCK_SCREEN_PASSWORD_DESCRIPTION" desc="Description of the password authentication factor on the lock screen page.">
-    Device password or Google Account password
+    Set up a <ph name="DEVICE_TYPE">$1<ex>Chromebook</ex></ph> password
   </message>
   <message name="IDS_SETTINGS_PEOPLE_LOCK_SCREEN_SWITCH_LOCAL_PASSWORD_DESCRIPTION" desc="Description of the password authentication factor on the lock screen page.">
     Currently using Google Account password. You can set up a <ph name="DEVICE_TYPE">$1<ex>Chromebook</ex></ph> password to make it easier to sign in.
diff --git a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_PEOPLE_LOCK_SCREEN_PASSWORD_DESCRIPTION.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_PEOPLE_LOCK_SCREEN_PASSWORD_DESCRIPTION.png.sha1
index 0274116..ff6f53cb 100644
--- a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_PEOPLE_LOCK_SCREEN_PASSWORD_DESCRIPTION.png.sha1
+++ b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_PEOPLE_LOCK_SCREEN_PASSWORD_DESCRIPTION.png.sha1
@@ -1 +1 @@
-c8b2f97625b855db572ae34eff690c7e7fffcab0
\ No newline at end of file
+b7b402163c41f23b381d8961fd171e4d504d56bd
\ No newline at end of file
diff --git a/chrome/browser/DEPS b/chrome/browser/DEPS
index 8855a47..782c2632 100644
--- a/chrome/browser/DEPS
+++ b/chrome/browser/DEPS
@@ -71,6 +71,7 @@
   "+components/browser_sync",
   "+components/browser_ui/accessibility",
   "+components/browser_ui/bottomsheet",
+  "+components/browser_ui/desktop_windowing/android",
   "+components/browser_ui/modaldialog",
   "+components/browser_ui/notifications",
   "+components/browser_ui/photo_picker",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 9d94563..f37d74e 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -1845,18 +1845,6 @@
         {"10 suggestions if 2 or fewer URLs", kOmniboxDynamicMaxAutocomplete102,
          std::size(kOmniboxDynamicMaxAutocomplete102), nullptr}};
 
-const FeatureEntry::FeatureParam kOmniboxUniformRowHeight36[] = {
-    {"OmniboxRichSuggestionVerticalMargin", "4"}};
-const FeatureEntry::FeatureParam kOmniboxUniformRowHeight40[] = {
-    {"OmniboxRichSuggestionVerticalMargin", "6"}};
-
-const FeatureEntry::FeatureVariation kOmniboxSuggestionHeightVariations[] = {
-    {"36px omnibox suggestions", kOmniboxUniformRowHeight36,
-     std::size(kOmniboxUniformRowHeight36), nullptr},
-    {"40px omnibox suggestions", kOmniboxUniformRowHeight40,
-     std::size(kOmniboxUniformRowHeight40), nullptr},
-};
-
 const FeatureEntry::FeatureParam kOmniboxFontSize12[] = {
     {"OmniboxFontSizeNonTouchUI", "12"}};
 const FeatureEntry::FeatureParam kOmniboxFontSize13[] = {
@@ -6539,12 +6527,6 @@
                                     kOmniboxSquareSuggestionIconVariations,
                                     "OmniboxBundledExperimentV1")},
 
-    {"omnibox-uniform-suggestion-height",
-     flag_descriptions::kOmniboxSimplifiedUiUniformRowHeightName,
-     flag_descriptions::kOmniboxSimplifiedUiUniformRowHeightDescription, kOsAll,
-     FEATURE_WITH_PARAMS_VALUE_TYPE(omnibox::kUniformRowHeight,
-                                    kOmniboxSuggestionHeightVariations,
-                                    "Uniform Omnibox Suggest Heights")},
     {"omnibox-cr23-action-chips",
      flag_descriptions::kOmniboxCR23ActionChipsName,
      flag_descriptions::kOmniboxCR23ActionChipsDescription, kOsDesktop,
@@ -11855,6 +11837,9 @@
 #endif  // !BUILDFLAG(IS_ANDROID)
 
 #if BUILDFLAG(IS_ANDROID)
+    {"jump-start-omnibox", flag_descriptions::kJumpStartOmniboxName,
+     flag_descriptions::kJumpStartOmniboxDescription, kOsAndroid,
+     FEATURE_VALUE_TYPE(omnibox::kJumpStartOmnibox)},
     {"retain-omnibox-on-focus", flag_descriptions::kRetainOmniboxOnFocusName,
      flag_descriptions::kRetainOmniboxOnFocusDescription, kOsAndroid,
      FEATURE_VALUE_TYPE(omnibox::kRetainOmniboxOnFocus)},
diff --git a/chrome/browser/android/seccomp_support_detector.cc b/chrome/browser/android/seccomp_support_detector.cc
index 36b6c63..0e922fe 100644
--- a/chrome/browser/android/seccomp_support_detector.cc
+++ b/chrome/browser/android/seccomp_support_detector.cc
@@ -30,26 +30,8 @@
   }
 }
 
-// Per the comment in base/cpu.cc's ParseProcCpu(), unfortunately there is not
-// a universally reliable way to examine the CPU part information for all
-// cores, so the sampling effect of data collection via UMA will have to
-// suffice.
-void ReportArmCpu() {
-#if defined(ARCH_CPU_ARM_FAMILY)
-  base::CPU cpu;
-
-  // Compose the implementer (8 bits) and the part number (12 bits) into a
-  // single 20-bit number that can be recorded via UMA.
-  uint32_t composed = cpu.implementer();
-  composed <<= 12;
-  composed |= cpu.part_number();
-  base::UmaHistogramSparse("Android.ArmCpuPart", composed);
-#endif
-}
-
 }  // namespace
 
 void ReportSeccompSupport() {
   ReportKernelVersion();
-  ReportArmCpu();
 }
diff --git a/chrome/browser/ash/app_list/app_list_syncable_service.h b/chrome/browser/ash/app_list/app_list_syncable_service.h
index 70f2ed7..d6165e03 100644
--- a/chrome/browser/ash/app_list/app_list_syncable_service.h
+++ b/chrome/browser/ash/app_list/app_list_syncable_service.h
@@ -158,7 +158,8 @@
   static std::unique_ptr<base::ScopedClosureRunner>
   SetScopedModelUpdaterFactoryForTest(ModelUpdaterFactoryCallback callback);
 
-  using SyncItemMap = std::map<std::string, std::unique_ptr<SyncItem>>;
+  using SyncItemMap =
+      std::map<std::string, std::unique_ptr<SyncItem>, std::less<>>;
 
   // Populates the model when |profile|'s extension system is ready.
   explicit AppListSyncableService(Profile* profile);
diff --git a/chrome/browser/ash/app_mode/BUILD.gn b/chrome/browser/ash/app_mode/BUILD.gn
index 2eb1c51..adf0b285 100644
--- a/chrome/browser/ash/app_mode/BUILD.gn
+++ b/chrome/browser/ash/app_mode/BUILD.gn
@@ -56,8 +56,6 @@
     "kiosk_profile_load_failed_observer.h",
     "kiosk_system_session.cc",
     "kiosk_system_session.h",
-    "lacros_launcher.cc",
-    "lacros_launcher.h",
     "load_profile.cc",
     "load_profile.h",
     "pref_names.cc",
diff --git a/chrome/browser/ash/app_mode/crash_recovery_launcher.cc b/chrome/browser/ash/app_mode/crash_recovery_launcher.cc
index 8912638..148442b 100644
--- a/chrome/browser/ash/app_mode/crash_recovery_launcher.cc
+++ b/chrome/browser/ash/app_mode/crash_recovery_launcher.cc
@@ -32,13 +32,6 @@
 void CrashRecoveryLauncher::Start(OnDoneCallback callback) {
   done_callback_ = std::move(callback);
   SYSLOG(INFO) << "Starting crash recovery flow for app " << kiosk_app_id_;
-  lacros_launcher_ = std::make_unique<app_mode::LacrosLauncher>();
-  lacros_launcher_->Start(
-      base::BindOnce(&CrashRecoveryLauncher::OnLacrosLaunchComplete,
-                     weak_ptr_factory_.GetWeakPtr()));
-}
-
-void CrashRecoveryLauncher::OnLacrosLaunchComplete() {
   app_launcher_->Initialize();
 }
 
diff --git a/chrome/browser/ash/app_mode/crash_recovery_launcher.h b/chrome/browser/ash/app_mode/crash_recovery_launcher.h
index 88c362bc..e48986b 100644
--- a/chrome/browser/ash/app_mode/crash_recovery_launcher.h
+++ b/chrome/browser/ash/app_mode/crash_recovery_launcher.h
@@ -6,11 +6,12 @@
 #define CHROME_BROWSER_ASH_APP_MODE_CRASH_RECOVERY_LAUNCHER_H_
 
 #include <optional>
+#include <string>
 
 #include "base/functional/callback_forward.h"
+#include "base/scoped_observation.h"
 #include "chrome/browser/ash/app_mode/kiosk_app_launcher.h"
 #include "chrome/browser/ash/app_mode/kiosk_app_types.h"
-#include "chrome/browser/ash/app_mode/lacros_launcher.h"
 #include "chrome/browser/profiles/profile.h"
 
 namespace ash {
@@ -32,7 +33,6 @@
   void Start(OnDoneCallback callback);
 
  private:
-  void OnLacrosLaunchComplete();
   void InvokeDoneCallback(bool success,
                           const std::optional<std::string>& app_name);
 
@@ -51,11 +51,9 @@
   const raw_ref<Profile> profile_;
   OnDoneCallback done_callback_;
 
-  std::unique_ptr<app_mode::LacrosLauncher> lacros_launcher_;
   std::unique_ptr<KioskAppLauncher> app_launcher_;
   base::ScopedObservation<KioskAppLauncher, KioskAppLauncher::Observer>
       observation_{this};
-  base::WeakPtrFactory<CrashRecoveryLauncher> weak_ptr_factory_{this};
 };
 
 }  // namespace ash
diff --git a/chrome/browser/ash/app_mode/kiosk_app_launch_error.cc b/chrome/browser/ash/app_mode/kiosk_app_launch_error.cc
index 6553382..b49c129 100644
--- a/chrome/browser/ash/app_mode/kiosk_app_launch_error.cc
+++ b/chrome/browser/ash/app_mode/kiosk_app_launch_error.cc
@@ -44,8 +44,6 @@
     case Error::kUnableToRetrieveHash:
     case Error::kPolicyLoadFailed:
     case Error::kUserNotAllowlisted:
-    case Error::kLacrosDataMigrationStarted:
-    case Error::kLacrosBackwardDataMigrationStarted:
       return l10n_util::GetStringUTF8(IDS_KIOSK_APP_FAILED_TO_LAUNCH);
 
     case Error::kCryptohomedNotRunning:
diff --git a/chrome/browser/ash/app_mode/kiosk_app_launch_error.h b/chrome/browser/ash/app_mode/kiosk_app_launch_error.h
index a49ebc2..040422ab 100644
--- a/chrome/browser/ash/app_mode/kiosk_app_launch_error.h
+++ b/chrome/browser/ash/app_mode/kiosk_app_launch_error.h
@@ -37,9 +37,9 @@
     kExtensionsPolicyInvalid =
         15,  // The policy value of ExtensionInstallForcelist is invalid.
     kUserNotAllowlisted = 16,  // LoginPerformer disallowed this user.
-    kLacrosDataMigrationStarted = 17,
-    kLacrosBackwardDataMigrationStarted = 18,
-    kMaxValue = kLacrosBackwardDataMigrationStarted,  // Max value of errors.
+    // kLacrosDataMigrationStarted = 17,  // Deprecated
+    // kLacrosBackwardDataMigrationStarted = 18,  // Deprecated
+    kMaxValue = kUserNotAllowlisted,  // Max value of errors.
   };
 
   // Returns a message for given `error`.
diff --git a/chrome/browser/ash/app_mode/lacros_launcher.cc b/chrome/browser/ash/app_mode/lacros_launcher.cc
deleted file mode 100644
index fe923863..0000000
--- a/chrome/browser/ash/app_mode/lacros_launcher.cc
+++ /dev/null
@@ -1,59 +0,0 @@
-// Copyright 2023 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/ash/app_mode/lacros_launcher.h"
-
-#include "base/syslog_logging.h"
-#include "chrome/browser/ash/crosapi/browser_manager.h"
-#include "chrome/browser/ash/crosapi/browser_util.h"
-
-namespace {
-
-crosapi::BrowserManager* browser_manager() {
-  return crosapi::BrowserManager::Get();
-}
-
-bool IsLacrosEnabled() {
-  return crosapi::browser_util::IsLacrosEnabledInChromeKioskSession() ||
-         crosapi::browser_util::IsLacrosEnabledInWebKioskSession();
-}
-
-}  // namespace
-
-namespace app_mode {
-
-LacrosLauncher::LacrosLauncher() = default;
-LacrosLauncher::~LacrosLauncher() = default;
-
-void LacrosLauncher::Start(base::OnceClosure callback) {
-  if (!IsLacrosEnabled()) {
-    std::move(callback).Run();
-    return;
-  }
-
-  SYSLOG(INFO) << "Launching Lacros for kiosk session";
-  if (browser_manager()->IsRunning()) {
-    // Nothing to do if lacros is already running.
-    base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
-        FROM_HERE, std::move(callback));
-    return;
-  }
-
-  SYSLOG(INFO) << "Waiting for Lacros to start";
-  on_launched_ = std::move(callback);
-  browser_manager_observation_.Observe(browser_manager());
-
-  browser_manager()->EnsureLaunch();
-}
-
-void LacrosLauncher::OnStateChanged() {
-  if (browser_manager()->IsRunning()) {
-    SYSLOG(INFO) << "Lacros started";
-    browser_manager_observation_.Reset();
-    base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
-        FROM_HERE, std::move(on_launched_));
-  }
-}
-
-}  // namespace app_mode
diff --git a/chrome/browser/ash/app_mode/lacros_launcher.h b/chrome/browser/ash/app_mode/lacros_launcher.h
deleted file mode 100644
index ac15f5a8..0000000
--- a/chrome/browser/ash/app_mode/lacros_launcher.h
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright 2023 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_ASH_APP_MODE_LACROS_LAUNCHER_H_
-#define CHROME_BROWSER_ASH_APP_MODE_LACROS_LAUNCHER_H_
-
-#include "base/functional/callback_forward.h"
-#include "base/scoped_observation.h"
-#include "chrome/browser/ash/crosapi/browser_manager.h"
-#include "chrome/browser/ash/crosapi/browser_manager_observer.h"
-
-namespace app_mode {
-
-class LacrosLauncher : public crosapi::BrowserManagerObserver {
- public:
-  LacrosLauncher();
-  LacrosLauncher(const LacrosLauncher&) = delete;
-  LacrosLauncher& operator=(const LacrosLauncher&) = delete;
-  ~LacrosLauncher() override;
-
-  void Start(base::OnceClosure callback);
-
- private:
-  // crosapi::BrowserManagerObserver
-  void OnStateChanged() override;
-
-  base::OnceClosure on_launched_;
-
-  // Observe the launch state of `BrowserManager`, and launch the
-  // lacros-chrome when it is ready. This object is only used when Lacros is
-  // enabled.
-  base::ScopedObservation<crosapi::BrowserManager,
-                          crosapi::BrowserManagerObserver>
-      browser_manager_observation_{this};
-};
-
-}  // namespace app_mode
-
-#endif  // CHROME_BROWSER_ASH_APP_MODE_LACROS_LAUNCHER_H_
diff --git a/chrome/browser/ash/crosapi/crosapi_ash.cc b/chrome/browser/ash/crosapi/crosapi_ash.cc
index 6fc826a..ea2f746 100644
--- a/chrome/browser/ash/crosapi/crosapi_ash.cc
+++ b/chrome/browser/ash/crosapi/crosapi_ash.cc
@@ -169,7 +169,6 @@
 #include "chromeos/crosapi/mojom/multi_capture_service.mojom.h"
 #include "chromeos/crosapi/mojom/passkeys.mojom.h"
 #include "chromeos/crosapi/mojom/screen_manager.mojom.h"
-#include "chromeos/crosapi/mojom/select_file.mojom.h"
 #include "chromeos/crosapi/mojom/task_manager.mojom.h"
 #include "chromeos/crosapi/mojom/telemetry_diagnostic_routine_service.mojom.h"
 #include "chromeos/services/chromebox_for_meetings/public/cpp/service_connection.h"
@@ -997,11 +996,6 @@
   search_provider_ash_->BindReceiver(std::move(receiver));
 }
 
-void CrosapiAsh::REMOVED_0(
-    mojo::PendingReceiver<mojom::SelectFileDeprecated> receiver) {
-  NOTIMPLEMENTED();
-}
-
 void CrosapiAsh::BindSensorHalClient(
     mojo::PendingRemote<chromeos::sensors::mojom::SensorHalClient> remote) {
   chromeos::sensors::SensorHalDispatcher::GetInstance()->RegisterClient(
diff --git a/chrome/browser/ash/crosapi/crosapi_ash.h b/chrome/browser/ash/crosapi/crosapi_ash.h
index 975dbcd..42c46990 100644
--- a/chrome/browser/ash/crosapi/crosapi_ash.h
+++ b/chrome/browser/ash/crosapi/crosapi_ash.h
@@ -400,8 +400,6 @@
       mojo::PendingRemote<mojom::SearchControllerFactory> remote) override;
   void BindSearchControllerRegistry(
       mojo::PendingReceiver<mojom::SearchControllerRegistry> receiver) override;
-  void REMOVED_0(
-      mojo::PendingReceiver<mojom::SelectFileDeprecated> receiver) override;
   void BindSensorHalClient(
       mojo::PendingRemote<chromeos::sensors::mojom::SensorHalClient> remote)
       override;
diff --git a/chrome/browser/ash/login/app_mode/kiosk_launch_controller.cc b/chrome/browser/ash/login/app_mode/kiosk_launch_controller.cc
index f57c6ef..d9a5982 100644
--- a/chrome/browser/ash/login/app_mode/kiosk_launch_controller.cc
+++ b/chrome/browser/ash/login/app_mode/kiosk_launch_controller.cc
@@ -41,7 +41,6 @@
 #include "chrome/browser/ash/app_mode/kiosk_chrome_app_manager.h"
 #include "chrome/browser/ash/app_mode/kiosk_launch_state.h"
 #include "chrome/browser/ash/app_mode/kiosk_profile_load_failed_observer.h"
-#include "chrome/browser/ash/app_mode/lacros_launcher.h"
 #include "chrome/browser/ash/app_mode/load_profile.h"
 #include "chrome/browser/ash/app_mode/startup_app_launcher.h"
 #include "chrome/browser/ash/app_mode/web_app/web_kiosk_app_manager.h"
@@ -261,8 +260,6 @@
     CASE(kExtensionsLoadTimeout);
     CASE(kExtensionsPolicyInvalid);
     CASE(kUserNotAllowlisted);
-    CASE(kLacrosDataMigrationStarted);
-    CASE(kLacrosBackwardDataMigrationStarted);
   }
   NOTREACHED();
 #undef CASE
@@ -434,7 +431,12 @@
   CHECK_DEREF(network_ui_controller_.get()).SetProfile(&profile);
 
   InitializeKeyboard();
-  LaunchLacros();
+
+  if (network_ui_controller_->ShouldShowNetworkConfig()) {
+    network_ui_controller_->UserRequestedNetworkConfig();
+  } else {
+    InitializeLauncher();
+  }
 }
 
 void KioskLaunchController::InitializeKeyboard() {
@@ -447,22 +449,6 @@
   }
 }
 
-void KioskLaunchController::LaunchLacros() {
-  app_state_ = kLaunchingLacros;
-  lacros_launcher_ = std::make_unique<app_mode::LacrosLauncher>();
-  lacros_launcher_->Start(
-      base::BindOnce(&KioskLaunchController::OnLacrosLaunchComplete,
-                     weak_ptr_factory_.GetWeakPtr()));
-}
-
-void KioskLaunchController::OnLacrosLaunchComplete() {
-  if (network_ui_controller_->ShouldShowNetworkConfig()) {
-    network_ui_controller_->UserRequestedNetworkConfig();
-  } else {
-    InitializeLauncher();
-  }
-}
-
 void KioskLaunchController::InitializeLauncher() {
   DCHECK(!app_launcher_);
 
@@ -590,10 +576,6 @@
       // because that prevents re-launch on the next run.
       std::move(attempt_relaunch_).Run();
       break;
-    case Error::kLacrosDataMigrationStarted:
-    case Error::kLacrosBackwardDataMigrationStarted:
-      // The migration code handles the chrome restart, so nothing to do.
-      break;
     case Error::kHasPendingLaunch:
     case Error::kUnableToMount:
     case Error::kUnableToRemove:
diff --git a/chrome/browser/ash/login/app_mode/kiosk_launch_controller.h b/chrome/browser/ash/login/app_mode/kiosk_launch_controller.h
index ed132f0..5d427dcb 100644
--- a/chrome/browser/ash/login/app_mode/kiosk_launch_controller.h
+++ b/chrome/browser/ash/login/app_mode/kiosk_launch_controller.h
@@ -31,7 +31,6 @@
 
 namespace app_mode {
 class ForceInstallObserver;
-class LacrosLauncher;
 }  // namespace app_mode
 
 namespace ash {
@@ -173,13 +172,11 @@
 
  private:
   friend class KioskLaunchControllerTest;
-  friend class KioskLaunchControllerUsingLacrosTest;
 
   class ScopedAcceleratorDisabler;
 
   enum AppState {
     kCreatingProfile = 0,  // Profile is being created.
-    kLaunchingLacros,
     kInitLauncher,          // Launcher is initializing
     kInstallingApp,         // App is being installed.
     kInstallingExtensions,  // Force-installed extensions are being installed.
@@ -192,8 +189,6 @@
   void OnCancelAppLaunch();
   void OnNetworkConfigRequested();
   void InitializeKeyboard();
-  void LaunchLacros();
-  void OnLacrosLaunchComplete();
   void InitializeLauncher();
 
   // `KioskAppLauncher::Observer`
@@ -276,8 +271,6 @@
   // The function used to load the Kiosk profile. Overridable in tests.
   kiosk::LoadProfileCallback profile_loader_;
 
-  std::unique_ptr<app_mode::LacrosLauncher> lacros_launcher_;
-
   std::unique_ptr<AcceleratorController> accelerator_controller_;
   std::unique_ptr<ScopedAcceleratorDisabler> accelerator_disabler_;
 
diff --git a/chrome/browser/ash/login/app_mode/kiosk_launch_controller_unittest.cc b/chrome/browser/ash/login/app_mode/kiosk_launch_controller_unittest.cc
index 66822af..8526e60 100644
--- a/chrome/browser/ash/login/app_mode/kiosk_launch_controller_unittest.cc
+++ b/chrome/browser/ash/login/app_mode/kiosk_launch_controller_unittest.cc
@@ -42,14 +42,6 @@
 #include "chrome/browser/ash/app_mode/kiosk_profile_load_failed_observer.h"
 #include "chrome/browser/ash/app_mode/load_profile.h"
 #include "chrome/browser/ash/app_mode/web_app/web_kiosk_app_manager.h"
-#include "chrome/browser/ash/crosapi/browser_util.h"
-#include "chrome/browser/ash/crosapi/crosapi_ash.h"
-#include "chrome/browser/ash/crosapi/crosapi_manager.h"
-#include "chrome/browser/ash/crosapi/fake_browser_manager.h"
-#include "chrome/browser/ash/crosapi/force_installed_tracker_ash.h"
-#include "chrome/browser/ash/crosapi/idle_service_ash.h"
-#include "chrome/browser/ash/crosapi/test_crosapi_dependency_registry.h"
-#include "chrome/browser/ash/crosapi/test_crosapi_environment.h"
 #include "chrome/browser/ash/login/app_mode/network_ui_controller.h"
 #include "chrome/browser/ash/login/screens/fake_app_launch_splash_screen.h"
 #include "chrome/browser/ash/login/screens/network_error.h"
@@ -979,195 +971,4 @@
   EXPECT_EQ(num_launchers_created(), 1);
 }
 
-// This class tests `KioskLaunchController` when Lacros is enabled. In
-// particular we test Lacros-specific code paths, e.g. interactions with
-// crosapi.
-class KioskLaunchControllerUsingLacrosTest : public testing::Test {
- public:
-  using AppState = KioskLaunchController::AppState;
-
-  KioskLaunchControllerUsingLacrosTest() {
-    std::vector<base::test::FeatureRef> enabled =
-        ash::standalone_browser::GetFeatureRefs();
-    enabled.push_back(ash::standalone_browser::features::kWebKioskEnableLacros);
-    scoped_feature_list_.InitWithFeatures(enabled, {});
-    scoped_command_line_.GetProcessCommandLine()->AppendSwitch(
-        ash::switches::kEnableLacrosForTesting);
-  }
-
-  void SetUp() override {
-    crosapi_environment_.SetUp();
-    crosapi::IdleServiceAsh::DisableForTesting();
-    profile_ = crosapi_environment_.profile_manager()->CreateTestingProfile(
-        "testing_profile@test");
-
-    SetUpKioskAppId();
-    fake_user_manager_->AddWebKioskAppUser(kiosk_app_id().account_id);
-    fake_user_manager_->LoginUser(kiosk_app_id().account_id);
-    ASSERT_TRUE(crosapi::browser_util::IsLacrosEnabledInWebKioskSession());
-
-    keyboard_controller_client_ =
-        ChromeKeyboardControllerClientTestHelper::InitializeWithFake();
-
-    controller_ = std::make_unique<KioskLaunchController>(
-        /*host=*/nullptr, &screen_, FakeLoadProfileCallback(),
-        /*app_launched_callback=*/base::DoNothing(),
-        /*done_callback=*/base::DoNothing(),
-        /*attempt_restart=*/base::DoNothing(),
-        /*attempt_logout=*/base::DoNothing(),
-        base::BindRepeating(
-            &KioskLaunchControllerUsingLacrosTest::BuildFakeKioskAppLauncher,
-            base::Unretained(this)),
-        std::make_unique<FakeNetworkMonitor>(),
-        std::make_unique<FakeAcceleratorController>());
-  }
-
-  void TearDown() override {
-    profile_ = nullptr;
-    controller_.reset();
-    crosapi_environment_.TearDown();
-  }
-
-  auto HasState(AppState app_state, NetworkUIState network_state) {
-    return testing::AllOf(
-        testing::Field("app_state", &KioskLaunchController::app_state_,
-                       Eq(app_state)),
-        testing::Property("network_ui_state",
-                          &KioskLaunchController::GetNetworkUiStateForTesting,
-                          Eq(network_state)));
-  }
-
-  void RunUntilAppPrepared() {
-    controller().Start(kiosk_app(), /*auto_launch=*/false);
-    FinishLoadingProfile();
-    EXPECT_TRUE(WaitForNextAppLauncherCreation());
-    launcher().observers().NotifyAppInstalling();
-    launcher().observers().NotifyAppPrepared();
-  }
-
-  void FinishLoadingProfile() {
-    std::move(on_profile_loaded_callback_).Run(profile());
-  }
-
- protected:
-  crosapi::FakeBrowserManager& fake_browser_manager() {
-    return browser_manager_;
-  }
-
-  KioskAppId kiosk_app_id() { return kiosk_app_id_; }
-
-  KioskApp kiosk_app() {
-    return KioskApp{kiosk_app_id_,
-                    /*name=*/"test-app-name", /*icon=*/gfx::ImageSkia(),
-                    /*url=*/GURL(kInstallUrl)};
-  }
-
-  KioskLaunchController& controller() { return *controller_; }
-
-  FakeKioskAppLauncher& launcher() { return *app_launcher_; }
-
-  Profile* profile() { return profile_; }
-
-  crosapi::ForceInstalledTrackerAsh* force_installed_tracker() {
-    return crosapi::CrosapiManager::Get()
-        ->crosapi_ash()
-        ->force_installed_tracker_ash();
-  }
-
-  int num_launchers_created() { return app_launchers_created_; }
-
-  [[nodiscard]] bool WaitForNextAppLauncherCreation() {
-    launcher_waiter_.Clear();
-    return launcher_waiter_.Wait();
-  }
-
- private:
-  void SetUpKioskAppId() {
-    std::string email = policy::GenerateDeviceLocalAccountUserId(
-        kInstallUrl, policy::DeviceLocalAccountType::kWebKioskApp);
-    AccountId account_id(AccountId::FromUserEmail(email));
-    kiosk_app_id_ = KioskAppId::ForWebApp(account_id);
-  }
-
-  std::unique_ptr<KioskAppLauncher> BuildFakeKioskAppLauncher(
-      Profile*,
-      const KioskAppId& kiosk_app_id,
-      KioskAppLauncher::NetworkDelegate*) {
-    auto app_launcher = std::make_unique<FakeKioskAppLauncher>();
-    app_launcher_ = app_launcher.get();
-    app_launchers_created_++;
-    launcher_waiter_.SetValue(true);
-    return std::move(app_launcher);
-  }
-
-  LoadProfileCallback FakeLoadProfileCallback() {
-    return base::BindLambdaForTesting([&](const AccountId& app_account_id,
-                                          KioskAppType app_type,
-                                          LoadProfileResultCallback on_done) {
-      on_profile_loaded_callback_ = std::move(on_done);
-      return std::unique_ptr<CancellableJob>{};
-    });
-  }
-
-  FakeChromeUserManager& fake_user_manager() { return *fake_user_manager_; }
-
-  content::BrowserTaskEnvironment task_environment_{
-      base::test::TaskEnvironment::TimeSource::MOCK_TIME};
-  crosapi::TestCrosapiEnvironment crosapi_environment_;
-  user_manager::TypedScopedUserManager<ash::FakeChromeUserManager>
-      fake_user_manager_{std::make_unique<ash::FakeChromeUserManager>()};
-  raw_ptr<Profile> profile_;
-  crosapi::FakeBrowserManager browser_manager_;
-
-  std::unique_ptr<ChromeKeyboardControllerClientTestHelper>
-      keyboard_controller_client_;
-
-  std::unique_ptr<base::AutoReset<std::optional<bool>>>
-      can_configure_network_for_testing_;
-  LoadProfileResultCallback on_profile_loaded_callback_;
-  FakeAppLaunchSplashScreen screen_;
-  raw_ptr<FakeKioskAppLauncher, DanglingUntriaged> app_launcher_ =
-      nullptr;  // owned by `controller_`.
-  int app_launchers_created_ = 0;
-  base::test::TestFuture<bool> launcher_waiter_;
-  std::unique_ptr<KioskLaunchController> controller_;
-  KioskAppId kiosk_app_id_;
-
-  base::test::ScopedFeatureList scoped_feature_list_;
-  base::test::ScopedCommandLine scoped_command_line_;
-};
-
-TEST_F(KioskLaunchControllerUsingLacrosTest,
-       LacrosShouldBeLaunchedAfterProfileLoaded) {
-  controller().Start(kiosk_app(), /*auto_launch=*/false);
-
-  EXPECT_FALSE(fake_browser_manager().IsRunning());
-  FinishLoadingProfile();
-
-  EXPECT_TRUE(fake_browser_manager().IsRunning());
-}
-
-TEST_F(KioskLaunchControllerUsingLacrosTest,
-       LauncherShouldGetInitializedAfterLacrosLaunched) {
-  controller().Start(kiosk_app(), /*auto_launch=*/false);
-
-  EXPECT_EQ(num_launchers_created(), 0);
-  FinishLoadingProfile();
-
-  EXPECT_TRUE(WaitForNextAppLauncherCreation());
-  EXPECT_TRUE(launcher().initialize_called());
-}
-
-TEST_F(KioskLaunchControllerUsingLacrosTest,
-       ExtensionInstallShouldObserveThroughCrosapi) {
-  RunUntilAppPrepared();
-  EXPECT_THAT(controller(), HasState(AppState::kInstallingExtensions,
-                                     NetworkUIState::kNotShowing));
-
-  force_installed_tracker()->OnForceInstalledExtensionsReady();
-
-  EXPECT_THAT(controller(),
-              HasState(AppState::kInstalled, NetworkUIState::kNotShowing));
-}
-
 }  // namespace ash
diff --git a/chrome/browser/ash/policy/enrollment/auto_enrollment_type_checker.cc b/chrome/browser/ash/policy/enrollment/auto_enrollment_type_checker.cc
index 46e43289..90d53a2 100644
--- a/chrome/browser/ash/policy/enrollment/auto_enrollment_type_checker.cc
+++ b/chrome/browser/ash/policy/enrollment/auto_enrollment_type_checker.cc
@@ -80,18 +80,21 @@
   }
 }
 
-// Returns true if we are on Flex and FRE is enabled on Flex.
-static bool IsFlexAndFREOnFlexIsEnabled() {
-  return ash::switches::IsRevenBranding() &&
+// Returns true if we are on an officially branded Flex and FRE is enabled
+// on Flex.
+static bool IsOfficialGoogleFlexAndFREOnFlexIsEnabled() {
+  return IsOfficialGoogleFlex() &&
+         // FRE on Flex is enabled unless explicitly disabled ("never" enabled).
          base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
-             ash::switches::kEnterpriseEnableForcedReEnrollmentOnFlex) ==
-             AutoEnrollmentTypeChecker::kForcedReEnrollmentAlways;
+             ash::switches::kEnterpriseEnableForcedReEnrollmentOnFlex) !=
+             AutoEnrollmentTypeChecker::kForcedReEnrollmentNever;
 }
 
 // Returns true if FRE state keys are supported.
 static bool AreFREStateKeysSupported() {
   // TODO(b/331677599): Return IsOfficialGoogleOS().
-  return IsOfficialGoogleChrome() || IsFlexAndFREOnFlexIsEnabled();
+  return IsOfficialGoogleChrome() ||
+         IsOfficialGoogleFlexAndFREOnFlexIsEnabled();
 }
 
 // Kill switch config request parameters.
diff --git a/chrome/browser/ash/policy/enrollment/auto_enrollment_type_checker_unittest.cc b/chrome/browser/ash/policy/enrollment/auto_enrollment_type_checker_unittest.cc
index 6faad17..5cd05d1 100644
--- a/chrome/browser/ash/policy/enrollment/auto_enrollment_type_checker_unittest.cc
+++ b/chrome/browser/ash/policy/enrollment/auto_enrollment_type_checker_unittest.cc
@@ -64,6 +64,11 @@
     enrollment_test_helper_.EnableFREOnFlex();
   }
 
+  void SetUpFlexDeviceWithFREOnFlexDisabled() {
+    enrollment_test_helper_.SetUpFlexDevice();
+    enrollment_test_helper_.DisableFREOnFlex();
+  }
+
   void SetupFREEnabled() {
     command_line_.GetProcessCommandLine()->AppendSwitchASCII(
         ash::switches::kEnterpriseEnableForcedReEnrollment,
@@ -355,7 +360,7 @@
        FRERequiredOnFlexEnabledByCommandLineSwitch) {
   SetUpFlexDeviceWithFREOnFlexEnabled();
 
-  EXPECT_TRUE(AutoEnrollmentTypeChecker::IsFREEnabled());
+  EXPECT_EQ(AutoEnrollmentTypeChecker::IsFREEnabled(), is_google_branded_);
   EXPECT_EQ(AutoEnrollmentTypeChecker::GetFRERequirementAccordingToVPD(
                 &fake_statistics_provider_),
             AutoEnrollmentTypeChecker::FRERequirement::kDisabled);
@@ -372,8 +377,8 @@
 }
 
 TEST_F(AutoEnrollmentTypeCheckerTest,
-       FRERequiredOnFlexNotEnabledByCommandLineSwitch) {
-  enrollment_test_helper_.SetUpFlexDevice();
+       FRERequiredOnFlexFREOnFlexDisabledByCommandLineSwitch) {
+  SetUpFlexDeviceWithFREOnFlexDisabled();
 
   EXPECT_FALSE(AutoEnrollmentTypeChecker::IsFREEnabled());
   EXPECT_EQ(AutoEnrollmentTypeChecker::GetFRERequirementAccordingToVPD(
@@ -383,6 +388,20 @@
 
 // TODO(b/353731379): Remove when removing legacy state determination code.
 TEST_F(AutoEnrollmentTypeCheckerTest,
+       FRERequiredOnFlexFREOnFlexNoCommandLineSwitch) {
+  enrollment_test_helper_.SetUpFlexDevice();
+  AutoEnrollmentTypeChecker::SetUnifiedStateDeterminationKillSwitchForTesting(
+      false);
+
+  EXPECT_EQ(AutoEnrollmentTypeChecker::IsFREEnabled(), is_google_branded_);
+  EXPECT_EQ(AutoEnrollmentTypeChecker::GetFRERequirementAccordingToVPD(
+                &fake_statistics_provider_),
+            is_google_branded_
+                ? AutoEnrollmentTypeChecker::FRERequirement::kExplicitlyRequired
+                : AutoEnrollmentTypeChecker::FRERequirement::kDisabled);
+}
+
+TEST_F(AutoEnrollmentTypeCheckerTest,
        DetermineAutoEnrollmentCheckTypeOnFlexWhenTokenPresent) {
   enrollment_test_helper_.SetUpFlexDevice();
   enrollment_test_helper_.SetUpEnrollmentTokenConfig();
@@ -777,7 +796,7 @@
     if (device_os_ == DeviceOs::Nonchrome) {
       enrollment_test_helper_.SetUpNonchromeDevice();
     } else if (device_os_ == DeviceOs::FlexWithoutFRE) {
-      enrollment_test_helper_.SetUpFlexDevice();
+      SetUpFlexDeviceWithFREOnFlexDisabled();
     } else if (device_os_ == DeviceOs::FlexWithFRE) {
       SetUpFlexDeviceWithFREOnFlexEnabled();
     }
@@ -786,8 +805,8 @@
   }
 
   bool IsFRESupportedByDevice() {
-    return (google_branded_ && device_os_ == DeviceOs::Chrome) ||
-           device_os_ == DeviceOs::FlexWithFRE;
+    return google_branded_ && (device_os_ == DeviceOs::Chrome ||
+                               device_os_ == DeviceOs::FlexWithFRE);
   }
 
   bool IsOfficialGoogleOS() {
diff --git a/chrome/browser/ash/policy/enrollment/enrollment_test_helper.cc b/chrome/browser/ash/policy/enrollment/enrollment_test_helper.cc
index 8ac8161c..1bbcb8a 100644
--- a/chrome/browser/ash/policy/enrollment/enrollment_test_helper.cc
+++ b/chrome/browser/ash/policy/enrollment/enrollment_test_helper.cc
@@ -50,10 +50,15 @@
   oobe_configuration_.CheckConfiguration();
 }
 
-void EnrollmentTestHelper::EnableFREOnFlex() {
+void EnrollmentTestHelper::DisableFREOnFlex() {
   command_line_->GetProcessCommandLine()->AppendSwitchASCII(
       ash::switches::kEnterpriseEnableForcedReEnrollmentOnFlex,
-      AutoEnrollmentTypeChecker::kForcedReEnrollmentAlways);
+      AutoEnrollmentTypeChecker::kForcedReEnrollmentNever);
+}
+
+void EnrollmentTestHelper::EnableFREOnFlex() {
+  // This is a no-op right now, but we keep this in case we need to revert
+  // this CL.
 }
 
 const std::string*
diff --git a/chrome/browser/ash/policy/enrollment/enrollment_test_helper.h b/chrome/browser/ash/policy/enrollment/enrollment_test_helper.h
index 43a2f9bd..82efba20 100644
--- a/chrome/browser/ash/policy/enrollment/enrollment_test_helper.h
+++ b/chrome/browser/ash/policy/enrollment/enrollment_test_helper.h
@@ -34,8 +34,9 @@
   // Configures OobeConfiguration with an enrollment token for testing.
   void SetUpEnrollmentTokenConfig(
       const char config[] = kEnrollmentTokenOobeConfig);
-  // Forces FRE (Forced Re-Enrollment) to be enabled on Flex via command line
-  // switch.
+  // Ensures that FRE (Forced Re-Enrollment) is disabled on Flex.
+  void DisableFREOnFlex();
+  // Ensures that FRE (Forced Re-Enrollment) is enabled on Flex.
   void EnableFREOnFlex();
   // Obtains the enrollment token set in OOBE configuration, returning nullptr
   // if not present.
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index 633e3b6..1b3eac7 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -23,7 +23,11 @@
     "//chrome/browser/chromeos/extensions/contact_center_insights:unit_tests",
     "//chrome/browser/chromeos/extensions/desk_api:unit_tests",
     "//chrome/browser/chromeos/extensions/file_system_provider:unit_tests",
+    "//chrome/browser/chromeos/extensions/login_screen/login:unit_tests",
+    "//chrome/browser/chromeos/extensions/login_screen/login/cleanup:unit_tests",
+    "//chrome/browser/chromeos/extensions/login_screen/login/external_logout_done:unit_tests",
     "//chrome/browser/chromeos/extensions/login_screen/login/external_logout_request:unit_tests",
+    "//chrome/browser/chromeos/extensions/login_screen/login_screen_storage:unit_tests",
     "//chrome/browser/chromeos/extensions/login_screen/login_state:unit_tests",
     "//chrome/browser/chromeos/extensions/odfs_config_private:unit_tests",
     "//chrome/browser/chromeos/extensions/telemetry:unit_tests",
@@ -35,70 +39,41 @@
     "//chrome/browser/chromeos/reporting:unit_tests",
     "//chrome/browser/chromeos/reporting/network:unit_tests",
     "//chrome/browser/chromeos/reporting/websites:unit_tests",
+    "//chrome/browser/chromeos/tablet_mode:unit_tests",
     "//chrome/browser/chromeos/tast_support:unit_tests",
     "//chrome/browser/chromeos/video_conference:unit_tests",
   ]
 
-  if (is_chromeos_ash) {
-    deps += [
-      "//chrome/browser/chromeos/extensions/login_screen/login:unit_tests",
-      "//chrome/browser/chromeos/extensions/login_screen/login/cleanup:unit_tests",
-      "//chrome/browser/chromeos/extensions/login_screen/login/external_logout_done:unit_tests",
-      "//chrome/browser/chromeos/extensions/login_screen/login_screen_storage:unit_tests",
-      "//chrome/browser/chromeos/tablet_mode:unit_tests",
-    ]
-
-    if (use_cups) {
-      deps +=
-          [ "//chrome/browser/chromeos/extensions/printing_metrics:unit_tests" ]
-    }
+  if (use_cups) {
+    deps +=
+        [ "//chrome/browser/chromeos/extensions/printing_metrics:unit_tests" ]
   }
 }
 
 group("browser_tests") {
   testonly = true
 
-  deps = [ "//chrome/browser/chromeos/extensions/system_log:browser_tests" ]
-
-  if (is_chromeos_ash) {
-    deps += [
-      "//chrome/browser/chromeos/extensions/action_handlers:browser_tests",
-      "//chrome/browser/chromeos/extensions/contact_center_insights:browser_tests",
-      "//chrome/browser/chromeos/extensions/echo_private:browser_tests",
-      "//chrome/browser/chromeos/extensions/file_system_provider:browser_tests",
-      "//chrome/browser/chromeos/extensions/login_screen/login:browser_tests",
-      "//chrome/browser/chromeos/extensions/login_screen/login/cleanup:browser_tests",
-      "//chrome/browser/chromeos/extensions/login_screen/login_screen_storage:browser_tests",
-      "//chrome/browser/chromeos/extensions/login_screen/login_state:browser_tests",
-      "//chrome/browser/chromeos/extensions/odfs_config_private:browser_tests",
-      "//chrome/browser/chromeos/mahi:browser_tests",
-      "//chrome/browser/chromeos/network:browser_tests",
-      "//chrome/browser/chromeos/policy:browser_tests",
-      "//chrome/browser/chromeos/policy/dlp:browser_tests",
-      "//chrome/browser/chromeos/printing/print_preview:browser_tests",
-      "//chrome/browser/chromeos/reporting/network:browser_tests",
-      "//chrome/browser/chromeos/reporting/websites:browser_tests",
-      "//chrome/browser/chromeos/smart_reader:browser_tests",
-      "//chrome/browser/chromeos/video_conference:browser_tests",
-    ]
-  }
-}
-
-if (is_chromeos_lacros) {
-  group("lacros_chrome_browsertests") {
-    testonly = true
-
-    deps = [
-      "//chrome/browser/chromeos/extensions/contact_center_insights:lacros_chrome_browsertests",
-      "//chrome/browser/chromeos/extensions/login_screen/login_screen_storage:lacros_chrome_browsertests",
-      "//chrome/browser/chromeos/extensions/odfs_config_private:lacros_chrome_browsertests",
-      "//chrome/browser/chromeos/network:lacros_chrome_browsertests",
-      "//chrome/browser/chromeos/policy/dlp:lacros_chrome_browsertests",
-      "//chrome/browser/chromeos/printing/print_preview:lacros_chrome_browsertests",
-      "//chrome/browser/chromeos/reporting/network:lacros_chrome_browsertests",
-      "//chrome/browser/chromeos/tablet_mode:lacros_chrome_browsertests",
-    ]
-  }
+  deps = [
+    "//chrome/browser/chromeos/extensions/action_handlers:browser_tests",
+    "//chrome/browser/chromeos/extensions/contact_center_insights:browser_tests",
+    "//chrome/browser/chromeos/extensions/echo_private:browser_tests",
+    "//chrome/browser/chromeos/extensions/file_system_provider:browser_tests",
+    "//chrome/browser/chromeos/extensions/login_screen/login:browser_tests",
+    "//chrome/browser/chromeos/extensions/login_screen/login/cleanup:browser_tests",
+    "//chrome/browser/chromeos/extensions/login_screen/login_screen_storage:browser_tests",
+    "//chrome/browser/chromeos/extensions/login_screen/login_state:browser_tests",
+    "//chrome/browser/chromeos/extensions/odfs_config_private:browser_tests",
+    "//chrome/browser/chromeos/extensions/system_log:browser_tests",
+    "//chrome/browser/chromeos/mahi:browser_tests",
+    "//chrome/browser/chromeos/network:browser_tests",
+    "//chrome/browser/chromeos/policy:browser_tests",
+    "//chrome/browser/chromeos/policy/dlp:browser_tests",
+    "//chrome/browser/chromeos/printing/print_preview:browser_tests",
+    "//chrome/browser/chromeos/reporting/network:browser_tests",
+    "//chrome/browser/chromeos/reporting/websites:browser_tests",
+    "//chrome/browser/chromeos/smart_reader:browser_tests",
+    "//chrome/browser/chromeos/video_conference:browser_tests",
+  ]
 }
 
 if (!is_chromeos_device) {
diff --git a/chrome/browser/chromeos/container_app/BUILD.gn b/chrome/browser/chromeos/container_app/BUILD.gn
index 29c36ee..0c6a7619 100644
--- a/chrome/browser/chromeos/container_app/BUILD.gn
+++ b/chrome/browser/chromeos/container_app/BUILD.gn
@@ -38,8 +38,4 @@
     "//content/test:test_support",
     "//testing/gtest",
   ]
-
-  if (is_chromeos_lacros) {
-    deps += [ "//chromeos/startup" ]
-  }
 }
diff --git a/chrome/browser/chromeos/crosapi/BUILD.gn b/chrome/browser/chromeos/crosapi/BUILD.gn
index 49f12d9..e9ec3a0f 100644
--- a/chrome/browser/chromeos/crosapi/BUILD.gn
+++ b/chrome/browser/chromeos/crosapi/BUILD.gn
@@ -16,18 +16,8 @@
 
   deps = [
     "//base",
+    "//chrome/browser/ash/crosapi",
+    "//chrome/browser/ash/crosapi:test_support",
     "//chromeos/crosapi/mojom",
   ]
-  if (is_chromeos_lacros) {
-    deps += [
-      "//chromeos/lacros",
-      "//chromeos/startup",
-    ]
-  }
-  if (is_chromeos_ash) {
-    deps += [
-      "//chrome/browser/ash/crosapi",
-      "//chrome/browser/ash/crosapi:test_support",
-    ]
-  }
 }
diff --git a/chrome/browser/chromeos/echo/BUILD.gn b/chrome/browser/chromeos/echo/BUILD.gn
index 7f517a5..90d1c9ca 100644
--- a/chrome/browser/chromeos/echo/BUILD.gn
+++ b/chrome/browser/chromeos/echo/BUILD.gn
@@ -14,16 +14,8 @@
 
   public_deps = [ "//base" ]
 
-  deps = [ "//build:chromeos_buildflags" ]
-
-  if (is_chromeos_ash) {
-    deps += [ "//chromeos/ash/components/report:utils" ]
-  }
-
-  if (is_chromeos_lacros) {
-    deps += [
-      "//chromeos/crosapi/mojom",
-      "//chromeos/lacros",
-    ]
-  }
+  deps = [
+    "//build:chromeos_buildflags",
+    "//chromeos/ash/components/report:utils",
+  ]
 }
diff --git a/chrome/browser/chromeos/extensions/telemetry/api/common/BUILD.gn b/chrome/browser/chromeos/extensions/telemetry/api/common/BUILD.gn
index f04a3f06d..e8c410b0a 100644
--- a/chrome/browser/chromeos/extensions/telemetry/api/common/BUILD.gn
+++ b/chrome/browser/chromeos/extensions/telemetry/api/common/BUILD.gn
@@ -6,6 +6,7 @@
 import("//extensions/buildflags/buildflags.gni")
 assert(enable_extensions,
        "Cannot depend on extensions because enable_extensions=false.")
+assert(is_chromeos)
 
 source_set("common") {
   sources = [
@@ -159,35 +160,22 @@
     ":hardware_info_delegate",
     ":remote_probe_strategy",
     ":test_support",
+    "//ash/webui/shimless_rma/backend",
     "//base",
     "//base/test:test_support",
     "//build:chromeos_buildflags",
+    "//chrome/browser/ash/login/users:test_support",
     "//chrome/browser/extensions:test_support",
+    "//chrome/common/chromeos/extensions",
     "//chrome/test:test_support",
+    "//chromeos/ash/components/browser_context_helper",
     "//chromeos/crosapi/cpp/telemetry:test_support",
     "//components/sync_preferences:test_support",
+    "//components/user_manager",
     "//content/public/browser",
     "//extensions/common",
     "//net",
     "//net:test_support",
     "//testing/gtest",
   ]
-
-  if (is_chromeos_ash) {
-    deps += [
-      "//ash/webui/shimless_rma/backend",
-      "//chrome/browser/ash/login/users:test_support",
-      "//chrome/common/chromeos/extensions",
-      "//chromeos/ash/components/browser_context_helper",
-      "//components/user_manager",
-    ]
-  }
-
-  if (is_chromeos_lacros) {
-    deps += [
-      "//chromeos/crosapi/mojom",
-      "//chromeos/startup",
-      "//components/policy/core/common",
-    ]
-  }
 }
diff --git a/chrome/browser/chromeos/extensions/vpn_provider/BUILD.gn b/chrome/browser/chromeos/extensions/vpn_provider/BUILD.gn
index d3803379..f50553a5 100644
--- a/chrome/browser/chromeos/extensions/vpn_provider/BUILD.gn
+++ b/chrome/browser/chromeos/extensions/vpn_provider/BUILD.gn
@@ -7,7 +7,6 @@
 
 assert(enable_extensions,
        "Cannot depend on extensions because enable_extensions=false.")
-
 assert(is_chromeos)
 
 source_set("vpn_provider") {
@@ -25,18 +24,11 @@
     "//base",
     "//chrome/browser/profiles:profile",
     "//chrome/common/extensions/api",
+    "//chromeos/ash/components/login/login_state",
     "//chromeos/crosapi/mojom",
     "//components/keyed_service/content",
     "//content/public/browser",
     "//extensions/browser",
     "//extensions/common",
   ]
-
-  if (is_chromeos_ash) {
-    deps += [ "//chromeos/ash/components/login/login_state" ]
-  }
-
-  if (is_chromeos_lacros) {
-    deps += [ "//chromeos/lacros:lacros" ]
-  }
 }
diff --git a/chrome/browser/chromeos/mahi/BUILD.gn b/chrome/browser/chromeos/mahi/BUILD.gn
index 1656a7f3..2296d28b 100644
--- a/chrome/browser/chromeos/mahi/BUILD.gn
+++ b/chrome/browser/chromeos/mahi/BUILD.gn
@@ -23,8 +23,11 @@
   public_deps = [ "//chrome/browser:browser_public_dependencies" ]
 
   deps = [
+    "//ash",
+    "//ash/constants",
     "//base",
     "//build:chromeos_buildflags",
+    "//chrome/browser/ash/mahi",
     "//chrome/browser/content_extraction",
     "//chrome/browser/favicon",
     "//chrome/browser/screen_ai:screen_ai_service_router_factory",
@@ -45,18 +48,6 @@
     "//ui/gfx",
     "//url",
   ]
-
-  if (is_chromeos_ash) {
-    deps += [
-      "//ash",
-      "//ash/constants",
-      "//chrome/browser/ash/mahi",
-    ]
-  }
-
-  if (is_chromeos_lacros) {
-    deps += [ "//chromeos/lacros" ]
-  }
 }
 
 source_set("unit_tests") {
@@ -75,10 +66,6 @@
     "//testing/gmock",
     "//testing/gtest",
   ]
-
-  if (is_chromeos_lacros) {
-    deps += [ "//chromeos/startup" ]
-  }
 }
 
 source_set("browser_tests") {
@@ -109,11 +96,4 @@
     "//ui/gfx",
     "//url",
   ]
-
-  if (is_chromeos_lacros) {
-    deps += [
-      "//chromeos/lacros",
-      "//chromeos/startup",
-    ]
-  }
 }
diff --git a/chrome/browser/chromeos/network/BUILD.gn b/chrome/browser/chromeos/network/BUILD.gn
index f94f672..c941a6c 100644
--- a/chrome/browser/chromeos/network/BUILD.gn
+++ b/chrome/browser/chromeos/network/BUILD.gn
@@ -19,6 +19,7 @@
     "//chrome/browser:browser_process",
     "//chrome/browser/ui:browser_navigator_params_headers",
     "//chrome/common:constants",
+    "//chromeos/ash/components/network",
     "//chromeos/crosapi/mojom",
     "//components/device_event_log",
     "//components/policy/core/common:common_constants",
@@ -28,56 +29,26 @@
     "//content/public/browser",
     "//url",
   ]
-
-  if (is_chromeos_ash) {
-    deps += [ "//chromeos/ash/components/network" ]
-  }
-
-  if (is_chromeos_lacros) {
-    deps += [ "//chromeos/lacros" ]
-  }
 }
 
-if (is_chromeos_ash) {
-  source_set("browser_tests") {
-    testonly = true
+source_set("browser_tests") {
+  testonly = true
 
-    defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ]
+  defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ]
 
-    sources = [ "network_portal_signin_window_ash_browsertest.cc" ]
+  sources = [ "network_portal_signin_window_ash_browsertest.cc" ]
 
-    deps = [
-      ":network",
-      "//chrome/browser/ash/net",
-      "//chrome/browser/profiles:profile",
-      "//chrome/browser/ui",
-      "//chrome/browser/ui:browser_navigator_params_headers",
-      "//chrome/test:test_support_ui",
-      "//chromeos/ash/components/network/portal_detector",
-      "//components/captive_portal/content",
-      "//content/test:test_support",
-      "//testing/gtest",
-      "//url",
-    ]
-  }
-}
-
-if (is_chromeos_lacros) {
-  source_set("lacros_chrome_browsertests") {
-    testonly = true
-
-    defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ]
-
-    sources = [ "network_portal_signin_window_lacros_browsertest.cc" ]
-
-    deps = [
-      ":network",
-      "//chrome/test:test_support_ui",
-      "//chromeos/crosapi/mojom",
-      "//chromeos/lacros",
-      "//content/test:test_support",
-      "//testing/gtest",
-      "//url",
-    ]
-  }
+  deps = [
+    ":network",
+    "//chrome/browser/ash/net",
+    "//chrome/browser/profiles:profile",
+    "//chrome/browser/ui",
+    "//chrome/browser/ui:browser_navigator_params_headers",
+    "//chrome/test:test_support_ui",
+    "//chromeos/ash/components/network/portal_detector",
+    "//components/captive_portal/content",
+    "//content/test:test_support",
+    "//testing/gtest",
+    "//url",
+  ]
 }
diff --git a/chrome/browser/chromeos/network/network_portal_signin_window_lacros_browsertest.cc b/chrome/browser/chromeos/network/network_portal_signin_window_lacros_browsertest.cc
deleted file mode 100644
index b1036a6..0000000
--- a/chrome/browser/chromeos/network/network_portal_signin_window_lacros_browsertest.cc
+++ /dev/null
@@ -1,71 +0,0 @@
-// Copyright 2024 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/chromeos/network/network_portal_signin_window.h"
-
-#include "chrome/browser/chromeos/network/network_portal_signin_window.h"
-#include "chrome/test/base/in_process_browser_test.h"
-#include "chromeos/crosapi/mojom/network_change.mojom.h"
-#include "chromeos/lacros/lacros_service.h"
-#include "content/public/test/browser_test.h"
-#include "content/public/test/browser_test_utils.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "url/gurl.h"
-
-namespace chromeos {
-
-namespace {
-
-class FakeNetworkChange : public crosapi::mojom::NetworkChange {
- public:
-  FakeNetworkChange() = default;
-  FakeNetworkChange(const FakeNetworkChange&) = delete;
-  FakeNetworkChange& operator=(const FakeNetworkChange&) = delete;
-  ~FakeNetworkChange() override = default;
-
-  // crosapi::mojom::NetworkChange:
-  void AddObserver(mojo::PendingRemote<crosapi::mojom::NetworkChangeObserver>
-                       observer) override {}
-  void RequestPortalDetection() override { portal_detection_requested_++; }
-
-  int portal_detection_requested() { return portal_detection_requested_; }
-
- private:
-  int portal_detection_requested_ = 0;
-};
-
-}  // namespace
-
-class NetworkPortalSigninWindowLacrosBrowserTest : public InProcessBrowserTest {
- public:
-  NetworkPortalSigninWindowLacrosBrowserTest() = default;
-  ~NetworkPortalSigninWindowLacrosBrowserTest() override = default;
-};
-
-IN_PROC_BROWSER_TEST_F(NetworkPortalSigninWindowLacrosBrowserTest,
-                       RequestPortalDetection) {
-  auto* lacros_service = LacrosService::Get();
-  ASSERT_TRUE(lacros_service);
-  ASSERT_TRUE(lacros_service->IsAvailable<crosapi::mojom::NetworkChange>());
-
-  FakeNetworkChange fake_network_change;
-  mojo::Receiver<FakeNetworkChange> fake_network_change_receiver(
-      &fake_network_change);
-  lacros_service->InjectRemoteForTesting(
-      fake_network_change_receiver.BindNewPipeAndPassRemote());
-
-  content::CreateAndLoadWebContentsObserver web_contents_observer;
-
-  NetworkPortalSigninWindow::Get()->Show(
-      GURL("http://www.gstatic.com/generate_204"));
-  ASSERT_TRUE(NetworkPortalSigninWindow::Get()->GetBrowserForTesting());
-
-  web_contents_observer.Wait();
-
-  // Showing the window should generate a DidFinishNavigation event which should
-  // trigger a corresponding RequestPortalDetection call.
-  EXPECT_GE(fake_network_change.portal_detection_requested(), 1);
-}
-
-}  // namespace chromeos
diff --git a/chrome/browser/chromeos/platform_keys/BUILD.gn b/chrome/browser/chromeos/platform_keys/BUILD.gn
index e8ff5912..2a86f76 100644
--- a/chrome/browser/chromeos/platform_keys/BUILD.gn
+++ b/chrome/browser/chromeos/platform_keys/BUILD.gn
@@ -25,6 +25,7 @@
   deps = [
     "//base",
     "//build:chromeos_buildflags",
+    "//chrome/browser/ash/profiles",
     "//chrome/browser/profiles:profile",
     "//chromeos/crosapi/cpp",
     "//chromeos/crosapi/mojom",
@@ -39,12 +40,4 @@
     "//net",
     "//third_party/boringssl",
   ]
-
-  if (is_chromeos_ash) {
-    deps += [ "//chrome/browser/ash/profiles" ]
-  }
-
-  if (is_chromeos_lacros) {
-    deps += [ "//chromeos/lacros" ]
-  }
 }
diff --git a/chrome/browser/chromeos/printing/print_preview/BUILD.gn b/chrome/browser/chromeos/printing/print_preview/BUILD.gn
index 0e51d2e..5e0a3ec 100644
--- a/chrome/browser/chromeos/printing/print_preview/BUILD.gn
+++ b/chrome/browser/chromeos/printing/print_preview/BUILD.gn
@@ -29,6 +29,7 @@
   deps = [
     "//base",
     "//build:chromeos_buildflags",
+    "//chrome/browser/ash/printing/print_preview",
     "//chrome/common:chrome_features",
     "//chromeos/crosapi/mojom",
     "//components/device_event_log",
@@ -48,14 +49,6 @@
     "//ui/gfx/geometry",
     "//url",
   ]
-
-  if (is_chromeos_ash) {
-    deps += [ "//chrome/browser/ash/printing/print_preview" ]
-  }
-
-  if (is_chromeos_lacros) {
-    deps += [ "//chromeos/lacros" ]
-  }
 }
 
 source_set("unit_tests") {
@@ -88,59 +81,28 @@
   ]
 }
 
-if (is_chromeos_ash) {
-  source_set("browser_tests") {
-    testonly = true
+source_set("browser_tests") {
+  testonly = true
 
-    defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ]
+  defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ]
 
-    sources = [ "print_preview_webcontents_manager_browsertest.cc" ]
+  sources = [ "print_preview_webcontents_manager_browsertest.cc" ]
 
-    deps = [
-      ":print_preview",
-      "//base",
-      "//base/test:test_support",
-      "//chrome/browser/chromeos/printing/print_preview/test:test_support",
-      "//chrome/browser/ui",
-      "//chrome/common:chrome_features",
-      "//chrome/test:test_support_ui",
-      "//chromeos/crosapi/mojom",
-      "//content/public/browser",
-      "//content/test:test_support",
-      "//mojo/public/cpp/bindings",
-      "//testing/gmock",
-      "//testing/gtest",
-      "//ui/base",
-      "//url",
-    ]
-  }
-}
-
-if (is_chromeos_lacros) {
-  source_set("lacros_chrome_browsertests") {
-    testonly = true
-
-    defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ]
-
-    sources = [ "print_preview_webcontents_manager_browsertest.cc" ]
-
-    deps = [
-      ":print_preview",
-      "//base",
-      "//base/test:test_support",
-      "//chrome/browser/chromeos/printing/print_preview/test:test_support",
-      "//chrome/browser/ui",
-      "//chrome/common:chrome_features",
-      "//chrome/test:test_support_ui",
-      "//chromeos/crosapi/mojom",
-      "//chromeos/lacros",
-      "//content/public/browser",
-      "//content/test:test_support",
-      "//mojo/public/cpp/bindings",
-      "//testing/gmock",
-      "//testing/gtest",
-      "//ui/base",
-      "//url",
-    ]
-  }
+  deps = [
+    ":print_preview",
+    "//base",
+    "//base/test:test_support",
+    "//chrome/browser/chromeos/printing/print_preview/test:test_support",
+    "//chrome/browser/ui",
+    "//chrome/common:chrome_features",
+    "//chrome/test:test_support_ui",
+    "//chromeos/crosapi/mojom",
+    "//content/public/browser",
+    "//content/test:test_support",
+    "//mojo/public/cpp/bindings",
+    "//testing/gmock",
+    "//testing/gtest",
+    "//ui/base",
+    "//url",
+  ]
 }
diff --git a/chrome/browser/chromeos/smart_reader/BUILD.gn b/chrome/browser/chromeos/smart_reader/BUILD.gn
index 9fd881b..884aaeb 100644
--- a/chrome/browser/chromeos/smart_reader/BUILD.gn
+++ b/chrome/browser/chromeos/smart_reader/BUILD.gn
@@ -20,27 +20,21 @@
     "//chromeos/crosapi/mojom",
     "//mojo/public/cpp/bindings",
   ]
-
-  if (is_chromeos_lacros) {
-    deps += [ "//chromeos/lacros" ]
-  }
 }
 
-if (is_chromeos_ash) {
-  source_set("browser_tests") {
-    testonly = true
+source_set("browser_tests") {
+  testonly = true
 
-    defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ]
+  defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ]
 
-    sources = [ "smart_reader_client_browsertest.cc" ]
+  sources = [ "smart_reader_client_browsertest.cc" ]
 
-    deps = [
-      ":smart_reader",
-      "//base/test:test_support",
-      "//chrome/test:test_support_ui",
-      "//content/test:test_support",
-      "//testing/gmock",
-      "//testing/gtest",
-    ]
-  }
+  deps = [
+    ":smart_reader",
+    "//base/test:test_support",
+    "//chrome/test:test_support_ui",
+    "//content/test:test_support",
+    "//testing/gmock",
+    "//testing/gtest",
+  ]
 }
diff --git a/chrome/browser/chromeos/tablet_mode/BUILD.gn b/chrome/browser/chromeos/tablet_mode/BUILD.gn
index 964d7e4..7d30919 100644
--- a/chrome/browser/chromeos/tablet_mode/BUILD.gn
+++ b/chrome/browser/chromeos/tablet_mode/BUILD.gn
@@ -28,6 +28,7 @@
     "//chrome/browser/profiles:profile",
     "//chrome/browser/search",
     "//chrome/browser/ui:browser_list",
+    "//chrome/browser/ui/webui/ash/system_web_dialog",
     "//chrome/common",
     "//content/public/browser",
     "//content/public/common",
@@ -36,72 +37,45 @@
     "//ui/base",
     "//ui/display",
   ]
-
-  if (is_chromeos_ash) {
-    deps += [ "//chrome/browser/ui/webui/ash/system_web_dialog" ]
-  }
 }
 
-if (is_chromeos_ash) {
-  source_set("unit_tests") {
-    testonly = true
+source_set("unit_tests") {
+  testonly = true
 
-    sources = [ "chrome_content_browser_client_tablet_mode_part_unittest.cc" ]
+  sources = [ "chrome_content_browser_client_tablet_mode_part_unittest.cc" ]
 
-    deps = [
-      ":tablet_mode",
-      "//extensions/common:common_constants",
-      "//testing/gtest",
-      "//url",
-    ]
-  }
-
-  source_set("browser_tests") {
-    testonly = true
-
-    defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ]
-
-    sources = [
-      "chrome_content_browser_client_tablet_mode_part_browsertest.cc",
-      "tablet_mode_page_behavior_browsertest.cc",
-    ]
-
-    deps = [
-      "//chrome/browser",
-      "//chrome/browser/ash/system_web_apps",
-      "//chrome/browser/profiles:profile",
-      "//chrome/browser/ui",
-      "//chrome/browser/ui/ash/system_web_apps",
-      "//chrome/browser/ui/chromeos:test_support",
-      "//chrome/common",
-      "//chrome/common:constants",
-      "//chrome/test:test_support",
-      "//chrome/test:test_support_ui",
-      "//components/prefs",
-      "//content/public/browser",
-      "//content/test:test_support",
-      "//third_party/blink/public/common:headers",
-    ]
-  }
+  deps = [
+    ":tablet_mode",
+    "//extensions/common:common_constants",
+    "//testing/gtest",
+    "//url",
+  ]
 }
 
-if (is_chromeos_lacros) {
-  source_set("lacros_chrome_browsertests") {
-    testonly = true
+source_set("browser_tests") {
+  testonly = true
 
-    defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ]
+  defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ]
 
-    sources = [ "tablet_mode_page_behavior_browsertest.cc" ]
+  sources = [
+    "chrome_content_browser_client_tablet_mode_part_browsertest.cc",
+    "tablet_mode_page_behavior_browsertest.cc",
+  ]
 
-    deps = [
-      "//chrome/browser",
-      "//chrome/browser/ui",
-      "//chrome/browser/ui/chromeos:test_support",
-      "//chrome/common",
-      "//chrome/test:test_support",
-      "//content/public/browser",
-      "//content/test:test_support",
-      "//third_party/blink/public/common:headers",
-    ]
-  }
+  deps = [
+    "//chrome/browser",
+    "//chrome/browser/ash/system_web_apps",
+    "//chrome/browser/profiles:profile",
+    "//chrome/browser/ui",
+    "//chrome/browser/ui/ash/system_web_apps",
+    "//chrome/browser/ui/chromeos:test_support",
+    "//chrome/common",
+    "//chrome/common:constants",
+    "//chrome/test:test_support",
+    "//chrome/test:test_support_ui",
+    "//components/prefs",
+    "//content/public/browser",
+    "//content/test:test_support",
+    "//third_party/blink/public/common:headers",
+  ]
 }
diff --git a/chrome/browser/chromeos/video_conference/BUILD.gn b/chrome/browser/chromeos/video_conference/BUILD.gn
index 6a83fd1..bff8e88 100644
--- a/chrome/browser/chromeos/video_conference/BUILD.gn
+++ b/chrome/browser/chromeos/video_conference/BUILD.gn
@@ -35,10 +35,6 @@
     "//services/metrics/public/cpp:ukm_builders",
     "//third_party/blink/public/common:headers",
   ]
-
-  if (is_chromeos_lacros) {
-    deps += [ "//chromeos/lacros" ]
-  }
 }
 
 source_set("unit_tests") {
@@ -66,8 +62,12 @@
 
   deps = [
     ":video_conference",
+    "//ash",
+    "//ash/constants",
     "//base",
     "//base/test:test_support",
+    "//chrome/browser/ash/crosapi",
+    "//chrome/browser/ash/video_conference",
     "//chrome/browser/media/webrtc",
     "//chrome/browser/ui",
     "//chrome/test:test_support_ui",
@@ -77,13 +77,4 @@
     "//testing/gtest",
     "//url",
   ]
-
-  if (is_chromeos_ash) {
-    deps += [
-      "//ash",
-      "//ash/constants",
-      "//chrome/browser/ash/crosapi",
-      "//chrome/browser/ash/video_conference",
-    ]
-  }
 }
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index 466009d..5cd3bff 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -5574,6 +5574,11 @@
     "expiry_milestone": 122
   },
   {
+    "name": "jump-start-omnibox",
+    "owners": [ "ender@google.com", "chrome-mobile-search@google.com" ],
+    "expiry_milestone": 145
+  },
+  {
     "name": "keyboard-and-pointer-lock-prompt",
     "owners": ["takumif@chromium.org", "muyaoxu@google.com"],
     "expiry_milestone": 140
@@ -6753,11 +6758,6 @@
     "expiry_milestone": 120
   },
   {
-    "name": "omnibox-uniform-suggestion-height",
-    "owners": [ "manukh@chromium.org", "chrome-omnibox-team@google.com"],
-    "expiry_milestone": 120
-  },
-  {
     "name": "omnibox-zero-suggest-in-memory-caching",
     "owners": [ "khalidpeer@chromium.org", "mahmadi@chromium.org", "chrome-omnibox-team@google.com" ],
     "expiry_milestone": 130
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 957e3a7..e2f2398 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -2443,6 +2443,12 @@
     "Enables filtering of clusters in the zero state of the History Journeys "
     "WebUI.";
 
+const char kJumpStartOmniboxName[] = "Jump-start Omnibox";
+const char kJumpStartOmniboxDescription[] =
+    "Modifies cold- and warm start-up "
+    "process on low-end devices to reduce the time to active Omnibox, while "
+    "completing core systems initialization in the background.";
+
 const char kExtractRelatedSearchesFromPrefetchedZPSResponseName[] =
     "Extract Related Searches from Prefetched ZPS Response";
 const char kExtractRelatedSearchesFromPrefetchedZPSResponseDescription[] =
@@ -2801,11 +2807,6 @@
 const char kOmniboxShortcutBoostDescription[] =
     "Promote shortcuts to be default when available.";
 
-const char kOmniboxSimplifiedUiUniformRowHeightName[] =
-    "Omnibox Suggestion Row Height";
-const char kOmniboxSimplifiedUiUniformRowHeightDescription[] =
-    "Changes the row height of omnibox suggetions.";
-
 const char kOmniboxSimplifiedUiSquareSuggestIconName[] =
     "Omnibox Square Suggest Icons";
 const char kOmniboxSimplifiedUiSquareSuggestIconDescription[] =
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index b97c826..b731bcc1 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -1383,6 +1383,9 @@
 extern const char kJourneysZeroStateFilteringName[];
 extern const char kJourneysZeroStateFilteringDescription[];
 
+extern const char kJumpStartOmniboxName[];
+extern const char kJumpStartOmniboxDescription[];
+
 extern const char kExtractRelatedSearchesFromPrefetchedZPSResponseName[];
 extern const char kExtractRelatedSearchesFromPrefetchedZPSResponseDescription[];
 
@@ -1606,9 +1609,6 @@
 extern const char kOmniboxShortcutExpandingName[];
 extern const char kOmniboxShortcutExpandingDescription[];
 
-extern const char kOmniboxSimplifiedUiUniformRowHeightName[];
-extern const char kOmniboxSimplifiedUiUniformRowHeightDescription[];
-
 extern const char kOmniboxSimplifiedUiSquareSuggestIconName[];
 extern const char kOmniboxSimplifiedUiSquareSuggestIconDescription[];
 
diff --git a/chrome/browser/platform_experience/win b/chrome/browser/platform_experience/win
index 993e39a..f8bbf7f 160000
--- a/chrome/browser/platform_experience/win
+++ b/chrome/browser/platform_experience/win
@@ -1 +1 @@
-Subproject commit 993e39a68a443c35b5b642f2e3ee844c26e1220a
+Subproject commit f8bbf7f750c82f7463cebabcaee82182b709859f
diff --git a/chrome/browser/resources/ash/settings/os_people_page/lock_screen_subpage.ts b/chrome/browser/resources/ash/settings/os_people_page/lock_screen_subpage.ts
index ed16de0..59d949c6 100644
--- a/chrome/browser/resources/ash/settings/os_people_page/lock_screen_subpage.ts
+++ b/chrome/browser/resources/ash/settings/os_people_page/lock_screen_subpage.ts
@@ -174,6 +174,17 @@
         },
         readOnly: true,
       },
+
+      /**
+       * Whether the device account is managed.
+       */
+      deviceAccountManaged_: {
+        type: Boolean,
+        value() {
+          return loadTimeData.getBoolean('isDeviceAccountManaged');
+        },
+        readOnly: true,
+      },
     };
   }
 
@@ -191,6 +202,7 @@
   private showDisableRecoveryDialog_: boolean;
   private fingerprintBrowserProxy_: FingerprintBrowserProxy;
   private changePasswordFactorSetupEnabled_: boolean;
+  private deviceAccountManaged_: boolean;
 
   static get observers() {
     return [
@@ -445,15 +457,38 @@
     }
     assert(authToken === this.authToken);
 
-    const [{configured: hasGaiaPassword}, {configured: hasLocalPassword}] =
-        await Promise.all([
-          this.authFactorConfig.isConfigured(
-              this.authToken, AuthFactor.kGaiaPassword),
-          this.authFactorConfig.isConfigured(
-              this.authToken, AuthFactor.kLocalPassword),
-        ]);
-    this.showPasswordSettings_ = hasLocalPassword ||
-        (this.changePasswordFactorSetupEnabled_ && hasGaiaPassword);
+    const [
+      { configured: hasGaiaPassword },
+      { configured: hasLocalPassword },
+      { configured: hasPin },
+    ] = await Promise.all([
+      this.authFactorConfig.isConfigured(
+        this.authToken, AuthFactor.kGaiaPassword),
+      this.authFactorConfig.isConfigured(
+        this.authToken, AuthFactor.kLocalPassword),
+      this.authFactorConfig.isConfigured(
+        this.authToken, AuthFactor.kPin),
+    ]);
+
+    if (hasLocalPassword) {
+      // Local Password is the overriding factor here. We need to show change
+      // option here.
+      this.showPasswordSettings_ = true;
+    } else if (!this.deviceAccountManaged_) {
+      // Onto scenarios for non managed accounts now.
+      if (this.changePasswordFactorSetupEnabled_ && hasGaiaPassword) {
+        // If the gaia password is setup, for non managed users, we will allow
+        // them to switch to local password.
+        this.showPasswordSettings_ = true;
+      } else if (!hasGaiaPassword && hasPin) {
+        // At this point we know the user does not have a password
+        // and has a pin. We can allow them to set password.
+        this.showPasswordSettings_ = true;
+      }
+    } else {
+      // This is a safety reset.
+      this.showPasswordSettings_ = false;
+    }
   }
 
   /**
diff --git a/chrome/browser/resources/ash/settings/os_people_page/password_settings.html b/chrome/browser/resources/ash/settings/os_people_page/password_settings.html
index fae5db1..8e63922 100644
--- a/chrome/browser/resources/ash/settings/os_people_page/password_settings.html
+++ b/chrome/browser/resources/ash/settings/os_people_page/password_settings.html
@@ -25,7 +25,7 @@
           is="dom-if"
           if="[[hasNoPassword_(hasGaiaPassword_, hasLocalPassword_)]]">
         <div class="secondary" id="setupPasswordSecondaryLabel">
-          $i18n{lockScreenPasswordDescription}
+          $i18n{lockScreenSwitchSetLocalPasswordDescription}
         </div>
       </template>
       <template is="dom-if"
@@ -36,16 +36,9 @@
         </div>
       </template>
     </div>
-    <template
-        is="dom-if"
-        if="[[hasNoPassword_(hasGaiaPassword_, hasLocalPassword_)]]">
-      <cr-button aria-describedby="setupPasswordSecondaryLabel" disabled>
-        $i18n{lockScreenSetupPasswordButton}
-      </cr-button>
-    </template>
     <template is="dom-if"
-        if="[[canSwitchLocalPassword_(hasGaiaPassword_,
-            changePasswordFactorSetupEnabled_)]]">
+        if="[[shouldSetupPassword_(hasGaiaPassword_, hasLocalPassword_,
+              changePasswordFactorSetupEnabled_)]]">
       <cr-button id="switchLocalPasswordButton"
           aria-describedby="switchLocalPasswordSecondaryLabel"
           on-click="openSetLocalPasswordDialog_">
diff --git a/chrome/browser/resources/ash/settings/os_people_page/password_settings.ts b/chrome/browser/resources/ash/settings/os_people_page/password_settings.ts
index 2c66ce92..7f763e4 100644
--- a/chrome/browser/resources/ash/settings/os_people_page/password_settings.ts
+++ b/chrome/browser/resources/ash/settings/os_people_page/password_settings.ts
@@ -10,11 +10,6 @@
 import {getTemplate} from './password_settings.html.js';
 import {SettingsSetLocalPasswordDialogElement} from './set_local_password_dialog.js';
 
-enum PasswordType {
-  LOCAL = 'local',
-  GAIA = 'gaia',
-}
-
 export class SettingsPasswordSettingsElement extends PolymerElement {
   static get is() {
     return 'settings-password-settings' as const;
@@ -109,23 +104,8 @@
     return !this.hasPassword_();
   }
 
-  /**
-   * Computes the current |PasswordType| based on the values of
-   * hasGaiaPassword_ and hasLocalPassword_.
-   */
-  private passwordType_(): PasswordType|null {
-    // This control works only when there is at most one password.
-    assert(!(this.hasGaiaPassword_ && this.hasLocalPassword_));
-
-    if (this.hasGaiaPassword_) {
-      return PasswordType.GAIA;
-    }
-
-    if (this.hasLocalPassword_) {
-      return PasswordType.LOCAL;
-    }
-
-    return null;
+  private shouldSetupPassword_(): boolean {
+    return this.hasNoPassword_() || this.canSwitchLocalPassword_();
   }
 
   private setLocalPasswordDialog(): SettingsSetLocalPasswordDialogElement {
diff --git a/chrome/browser/resources/ash/settings/os_settings_icons.html b/chrome/browser/resources/ash/settings/os_settings_icons.html
index 773317e..d2ff3a1 100644
--- a/chrome/browser/resources/ash/settings/os_settings_icons.html
+++ b/chrome/browser/resources/ash/settings/os_settings_icons.html
@@ -414,7 +414,7 @@
       <g id="reduced-animations" viewBox="0 -960 960 960"><path d="M360-80q-58 0-109-22t-89-60q-38-38-60-89T80-360q0-81 42-148t110-102q20-39 49.5-68.5T350-728q33-68 101-110t149-42q58 0 109 22t89 60q38 38 60 89t22 109q0 85-42 150T728-350q-20 39-49.5 68.5T610-232q-35 68-102 110T360-80Zm0-80q33 0 63.5-10t56.5-30q-58 0-109-22t-89-60q-38-38-60-89t-22-109q-20 26-30 56.5T160-360q0 42 16 78t43 63q27 27 63 43t78 16Zm120-120q33 0 64.5-10t57.5-30q-59 0-110-22.5T403-403q-38-38-60.5-89T320-602q-20 26-30 57.5T280-480q0 42 15.5 78t43.5 63q27 28 63 43.5t78 15.5Zm120-120q18 0 34.5-3t33.5-9q22-60 6.5-115.5T621-621q-38-38-93.5-53.5T412-668q-6 17-9 33.5t-3 34.5q0 42 15.5 78t43.5 63q27 28 63 43.5t78 15.5Zm160-78q20-26 30-57.5t10-64.5q0-42-15.5-78T741-741q-27-28-63-43.5T600-800q-35 0-65.5 10T478-760q59 0 110 22.5t89 60.5q38 38 60.5 89T760-478Z"></path></g>
       <g id="end-of-life-offer" viewBox="0 0 20 20"><path d="M7 20V18H17V20H7ZM11 16V7.825L8.4 10.4L7 9L12 4L17 9L15.6 10.4L13 7.825V16H11Z"></path></g>
       <g id="explore" viewBox="0 0 20 20"><path fill-rule="evenodd" d="M9.854 18.167a8.144 8.144 0 0 1-3.125-.688 8.4 8.4 0 0 1-2.562-1.75 8.54 8.54 0 0 1-1.709-2.583A7.999 7.999 0 0 1 1.833 10c0-1.139.209-2.201.625-3.188A8.153 8.153 0 0 1 4.23 4.23a8.112 8.112 0 0 1 2.604-1.75A7.827 7.827 0 0 1 10 1.833c2.028 0 3.778.632 5.25 1.896 1.486 1.264 2.41 2.854 2.77 4.771h-1.77a6.056 6.056 0 0 0-1.333-2.667 6.267 6.267 0 0 0-2.438-1.75v.459A1.459 1.459 0 0 1 11 6.022H9.001v.999A.964.964 0 0 1 8.02 8h-1v2H8v1.958h-.98L3.709 8.646c-.041.222-.076.444-.104.666a5.54 5.54 0 0 0-.042.688c0 1.764.612 3.27 1.834 4.52 1.222 1.237 2.708 1.876 4.458 1.918v1.729Zm7.104-.459-2.562-2.583c-.278.167-.57.299-.875.396a3.396 3.396 0 0 1-.938.125c-1 0-1.847-.347-2.541-1.042A3.434 3.434 0 0 1 9 12.084c0-1 .347-1.848 1.042-2.542.694-.695 1.541-1.042 2.541-1.042.986 0 1.827.347 2.521 1.042a3.434 3.434 0 0 1 1.042 2.52c0 .348-.049.674-.146.98-.083.305-.208.59-.375.854l2.563 2.583-1.23 1.23Zm-4.395-3.791c.513 0 .95-.174 1.312-.521.361-.361.542-.799.542-1.313 0-.514-.18-.951-.542-1.312a1.72 1.72 0 0 0-1.292-.542c-.514 0-.951.18-1.312.542a1.72 1.72 0 0 0-.542 1.291c0 .514.174.952.521 1.313.361.361.799.542 1.313.542Z"></path></g>
-      <g id="face-gaze"><path fill-rule="evenodd" clip-rule="evenodd" d="M10 18c-2.233 0-4.125-.775-5.675-2.325C2.775 14.125 2 12.233 2 10c0-2.233.775-4.125 2.325-5.675C5.875 2.775 7.767 2 10 2c2.233 0 4.125.775 5.675 2.325C17.225 5.875 18 7.767 18 10c0 2.233-.775 4.125-2.325 5.675C14.125 17.225 12.233 18 10 18Zm0-2c1.667 0 3.083-.583 4.25-1.75C15.417 13.083 16 11.667 16 10c0-.283-.025-.558-.075-.825a4.537 4.537 0 0 0-.175-.825c-.25.05-.5.092-.75.125-.25.017-.5.025-.75.025A7.653 7.653 0 0 1 8.7 6.15a7.706 7.706 0 0 1-1.925 2.475A7.673 7.673 0 0 1 4 10.15c.05 1.633.65 3.017 1.8 4.15C6.967 15.433 8.367 16 10 16ZM4.4 7.85c.733-.383 1.292-.825 1.675-1.325C6.458 6.008 6.833 5.4 7.2 4.7a5.83 5.83 0 0 0-1.675 1.325A5.871 5.871 0 0 0 4.4 7.85ZM7.5 12c-.283 0-.525-.092-.725-.275A1.035 1.035 0 0 1 6.5 11c0-.283.092-.517.275-.7.2-.2.442-.3.725-.3s.517.1.7.3c.2.183.3.417.3.7s-.1.525-.3.725a.948.948 0 0 1-.7.275Zm6.75-5.5h.3c.1 0 .2-.008.3-.025a6.107 6.107 0 0 0-2.1-1.8A5.694 5.694 0 0 0 10 4h-.3c-.1 0-.192.008-.275.025.65.75 1.333 1.35 2.05 1.8.733.45 1.658.675 2.775.675ZM12.5 12c-.283 0-.525-.092-.725-.275A1.035 1.035 0 0 1 11.5 11c0-.283.092-.517.275-.7.2-.2.442-.3.725-.3s.517.1.7.3c.2.183.3.417.3.7s-.1.525-.3.725a.948.948 0 0 1-.7.275ZM-1 4V1c0-.55.192-1.017.575-1.4C-.025-.8.45-1 1-1h3v2H1v3h-2Zm5 17H1a1.99 1.99 0 0 1-1.425-.575A1.99 1.99 0 0 1-1 19v-3h2v3h3v2Zm12 0v-2h3v-3h2v3c0 .55-.2 1.025-.6 1.425-.383.383-.85.575-1.4.575h-3Zm3-17V1h-3v-2h3c.55 0 1.017.2 1.4.6.4.383.6.85.6 1.4v3h-2Z"></path></g>
+      <g id="face-gaze"><path fill-rule="evenodd" clip-rule="evenodd" d="M7.875 16.125V14.375H10.4792L9.14583 12.2708C9.14583 12.2569 9.13889 12.25 9.125 12.25C9.125 12.2361 9.125 12.2222 9.125 12.2083V8.875H10.875V11.7708L12.2708 14.0417C12.3542 14.1528 12.4097 14.2708 12.4375 14.3958C12.4792 14.5069 12.5 14.6319 12.5 14.7708C12.5 15.1458 12.3611 15.4653 12.0833 15.7292C11.8194 15.9931 11.4931 16.125 11.1042 16.125H7.875ZM5.9375 12.0833C5.35417 12.0833 4.86111 11.8819 4.45833 11.4792C4.05556 11.0764 3.85417 10.5833 3.85417 10C3.85417 9.43056 4.05556 8.94444 4.45833 8.54167C4.86111 8.125 5.34722 7.91667 5.91667 7.91667C6.5 7.91667 6.99306 8.11806 7.39583 8.52083C7.79861 8.92361 8 9.41667 8 10C8 10.5694 7.79861 11.0625 7.39583 11.4792C6.99306 11.8819 6.50694 12.0833 5.9375 12.0833ZM14.0833 12.0833C13.5 12.0833 13.0069 11.8819 12.6042 11.4792C12.2014 11.0764 12 10.5833 12 10C12 9.43056 12.2014 8.94444 12.6042 8.54167C13.0069 8.125 13.4931 7.91667 14.0625 7.91667C14.6458 7.91667 15.1389 8.11806 15.5417 8.52083C15.9444 8.92361 16.1458 9.41667 16.1458 10C16.1458 10.5694 15.9444 11.0625 15.5417 11.4792C15.1389 11.8819 14.6528 12.0833 14.0833 12.0833ZM3.33333 6.52083L2.14583 5.3125C2.61806 4.88194 3.13889 4.54167 3.70833 4.29167C4.27778 4.04167 4.875 3.91667 5.5 3.91667C6.125 3.91667 6.72917 4.03472 7.3125 4.27083C7.89583 4.50694 8.40972 4.85417 8.85417 5.3125L7.66667 6.52083C7.36111 6.25694 7.02083 6.04861 6.64583 5.89583C6.28472 5.72917 5.90278 5.64583 5.5 5.64583C5.09722 5.64583 4.70833 5.72917 4.33333 5.89583C3.97222 6.0625 3.63889 6.27083 3.33333 6.52083ZM12.3333 6.52083L11.1458 5.3125C11.5903 4.85417 12.1042 4.50694 12.6875 4.27083C13.2708 4.03472 13.875 3.91667 14.5 3.91667C15.125 3.91667 15.7292 4.03472 16.3125 4.27083C16.8958 4.50694 17.4097 4.85417 17.8542 5.3125L16.6667 6.52083C16.3472 6.25694 16.0069 6.04861 15.6458 5.89583C15.2847 5.72917 14.9028 5.64583 14.5 5.64583C14.0972 5.64583 13.7153 5.72917 13.3542 5.89583C12.9931 6.04861 12.6528 6.25694 12.3333 6.52083Z" fill="#1B1B1F"></path></g>
       <g id="fullscreen-magnifier" viewBox="0 0 20 20"><path d="M14 11V9h2V8h-2V6h-1v2h-2v1h2v2h1Z"></path><path fill-rule="evenodd" clip-rule="evenodd" d="M17.5 3h-15C1.6 3 1 3.7 1 4.556V15.5c0 .856.6 1.5 1.5 1.5h15c.9 0 1.5-.644 1.5-1.5v-11c0-.856-.6-1.5-1.5-1.5ZM17 15H3V5h14v10Z"></path></g>
       <g id="geolocation"><path d="M12 8C12 9.10457 11.1046 10 10 10C8.89543 10 8 9.10457 8 8C8 6.89543 8.89543 6 10 6C11.1046 6 12 6.89543 12 8Z" fill="#1B1B1F"></path><path fill-rule="evenodd" clip-rule="evenodd" d="M16 8C16 10.2091 14 13.5425 10 18C6 13.5425 4 10.2091 4 8C4 4.68629 6.68629 2 10 2C13.3137 2 16 4.68629 16 8ZM10 4C7.79086 4 6 5.79086 6 8C6 9.32543 7.318 11.7149 10 14.9435C12.682 11.7149 14 9.32543 14 8C14 5.79086 12.2091 4 10 4Z" fill="#1B1B1F"></path></g>
       <g id="google-drive" viewBox="0 0 20 20"><path fill-rule="evenodd" clip-rule="evenodd" d="M18.7333 12L13.0167 2H7.31665V2.00833L13.025 12H18.7333ZM8.27502 12.8334L5.41669 17.8334H16.35L19.2084 12.8334H8.27502ZM6.59167 3.26672L1.125 12.8334L3.98333 17.8251L9.45 8.26672C9.45 8.27506 6.59167 3.26672 6.59167 3.26672Z"></path></g>
diff --git a/chrome/browser/resources/data_sharing/data_sharing.html b/chrome/browser/resources/data_sharing/data_sharing.html
index fcf6b37..2dae53f8 100644
--- a/chrome/browser/resources/data_sharing/data_sharing.html
+++ b/chrome/browser/resources/data_sharing/data_sharing.html
@@ -3,7 +3,7 @@
   <head>
     <meta charset="utf-8">
     <title>Data Sharing Placeholder</title>
-    <link rel="stylesheet" href="//theme/colors.css?sets=chrome">
+    <link rel="stylesheet" href="//theme/colors.css?sets=ui,chrome">
   </head>
   <body>
     <data-sharing-app></data-sharing-app>
diff --git a/chrome/browser/resources/data_sharing/data_sharing_app.html b/chrome/browser/resources/data_sharing/data_sharing_app.html
index c55ef55..cc4704c 100644
--- a/chrome/browser/resources/data_sharing/data_sharing_app.html
+++ b/chrome/browser/resources/data_sharing/data_sharing_app.html
@@ -2,8 +2,14 @@
 #dialog-container {
   width: 448px;
   max-height: 600px;
+
+  /* TODO(kdubbs): Add in the rest of the variable overrides here. */
+  --skw-color-primary: var(--color-sys-primary);
 }
 </style>
 
+<!-- Import stylesheet so it's inside the shadow DOM -->
+<link rel="stylesheet" href="data_sharing_sdk.css">
+
 <!-- Container that hosts flow dialogs. -->
-<div id="dialog-container"></div>
\ No newline at end of file
+<div id="dialog-container"></div>
diff --git a/chrome/browser/resources/data_sharing/data_sharing_app.ts b/chrome/browser/resources/data_sharing/data_sharing_app.ts
index 7393916..4ffadf2 100644
--- a/chrome/browser/resources/data_sharing/data_sharing_app.ts
+++ b/chrome/browser/resources/data_sharing/data_sharing_app.ts
@@ -13,7 +13,6 @@
 
 import {ColorChangeUpdater} from '//resources/cr_components/color_change_listener/colors_css_updater.js';
 import {CustomElement} from 'chrome-untrusted://resources/js/custom_element.js';
-
 import {BrowserProxyImpl} from './browser_proxy.js';
 import type {BrowserProxy} from './browser_proxy.js';
 import {getTemplate} from './data_sharing_app.html.js';
@@ -85,11 +84,13 @@
     const groupId = params.get(UrlQueryParams.GROUP_ID);
     const tokenSecret = params.get(UrlQueryParams.TOKEN_SECRET);
     const tabGroupId = params.get(UrlQueryParams.TAB_GROUP_ID);
+    const parent = this.getRequiredElement('#dialog-container');
 
     switch (flow) {
       case FlowValues.SHARE:
         this.dataSharingSdk_
             .runInviteFlow({
+              parent,
               getShareLink: (params: DataSharingSdkGetLinkParams):
                   Promise<string> => {
                     this.makeTabGroupShared(tabGroupId!, params.groupId);
@@ -103,7 +104,7 @@
       case FlowValues.JOIN:
         // group_id and token_secret cannot be null for join flow.
         this.dataSharingSdk_
-            .runJoinFlow({groupId: groupId!, tokenSecret: tokenSecret!})
+            .runJoinFlow({parent, groupId: groupId!, tokenSecret: tokenSecret!})
             .then((res) => {
               this.browserProxy_.closeUi(res.status);
             });
@@ -112,6 +113,7 @@
         // group_id cannot be null for manage flow.
         this.dataSharingSdk_
             .runManageFlow({
+              parent,
               groupId: groupId!,
               getShareLink: (params: DataSharingSdkGetLinkParams):
                   Promise<string> => {
diff --git a/chrome/browser/resources/pdf/pdf_viewer.ts b/chrome/browser/resources/pdf/pdf_viewer.ts
index 59ca4951..2e621b9 100644
--- a/chrome/browser/resources/pdf/pdf_viewer.ts
+++ b/chrome/browser/resources/pdf/pdf_viewer.ts
@@ -123,8 +123,10 @@
 /**
  * The background color used for the regular viewer.
  */
+// LINT.IfChange(PdfBackgroundColor)
 const BACKGROUND_COLOR: number = 0xff525659;
 const CR23_BACKGROUND_COLOR: number = 0xff282828;
+// LINT.ThenChange(//components/pdf/common/pdf_util.cc:PdfBackgroundColor)
 
 export interface PdfViewerElement {
   $: {
diff --git a/chrome/browser/resources/print_preview/data/destination.ts b/chrome/browser/resources/print_preview/data/destination.ts
index 7fd468c8..49d24c2 100644
--- a/chrome/browser/resources/print_preview/data/destination.ts
+++ b/chrome/browser/resources/print_preview/data/destination.ts
@@ -411,13 +411,6 @@
             }
 
             this.printerStatusReason_ = statusReason;
-
-            // If this is the second printer status attempt, record the result.
-            if (this.printerStatusRetrySent_) {
-              NativeLayerCrosImpl.getInstance()
-                  .recordPrinterStatusRetrySuccessHistogram(
-                      !isPrinterUnreachable);
-            }
           }
           return Promise.resolve(this.key);
         });
diff --git a/chrome/browser/resources/print_preview/native_layer_cros.ts b/chrome/browser/resources/print_preview/native_layer_cros.ts
index 28137d7..8bee6d36 100644
--- a/chrome/browser/resources/print_preview/native_layer_cros.ts
+++ b/chrome/browser/resources/print_preview/native_layer_cros.ts
@@ -55,12 +55,6 @@
   requestPrinterStatusUpdate(printerId: string): Promise<PrinterStatus>;
 
   /**
-   * Records the histogram to capture if the retried printer status was
-   * able to get a valid response from the local printer.
-   */
-  recordPrinterStatusRetrySuccessHistogram(retrySuccessful: boolean): void;
-
-  /**
    * Selects all print servers with ids in |printServerIds| to query for their
    * printers.
    */
@@ -109,12 +103,6 @@
     return sendWithPromise('requestPrinterStatus', printerId);
   }
 
-  recordPrinterStatusRetrySuccessHistogram(retrySuccessful: boolean) {
-    chrome.send(
-        'metricsHandler:recordBooleanHistogram',
-        ['PrinterStatusRetrySuccess', retrySuccessful]);
-  }
-
   choosePrintServers(printServerIds: string[]) {
     chrome.send('choosePrintServers', [printServerIds]);
   }
diff --git a/chrome/browser/resources/tab_search/auto_tab_groups/auto_tab_groups_group.css b/chrome/browser/resources/tab_search/auto_tab_groups/auto_tab_groups_group.css
index b1cdb31..e918448 100644
--- a/chrome/browser/resources/tab_search/auto_tab_groups/auto_tab_groups_group.css
+++ b/chrome/browser/resources/tab_search/auto_tab_groups/auto_tab_groups_group.css
@@ -68,6 +68,7 @@
   line-height: 20px;
   overflow: hidden;
   text-overflow: ellipsis;
+  white-space: nowrap;
   width: 184px;
 }
 
diff --git a/chrome/browser/search_engines/android/java/src/org/chromium/chrome/browser/search_engines/choice_screen/ChoiceDialogCoordinator.java b/chrome/browser/search_engines/android/java/src/org/chromium/chrome/browser/search_engines/choice_screen/ChoiceDialogCoordinator.java
index bb6915f..e946d7a 100644
--- a/chrome/browser/search_engines/android/java/src/org/chromium/chrome/browser/search_engines/choice_screen/ChoiceDialogCoordinator.java
+++ b/chrome/browser/search_engines/android/java/src/org/chromium/chrome/browser/search_engines/choice_screen/ChoiceDialogCoordinator.java
@@ -17,6 +17,7 @@
 
 import org.chromium.base.Callback;
 import org.chromium.base.Log;
+import org.chromium.base.metrics.RecordUserAction;
 import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
 import org.chromium.chrome.browser.search_engines.R;
 import org.chromium.chrome.browser.search_engines.choice_screen.ChoiceDialogMediator.DialogType;
@@ -60,6 +61,7 @@
                     if (model != mModel) return;
 
                     mMediator.onDialogAdded();
+                    RecordUserAction.record("OsDefaultsChoiceDialogShown");
                 }
 
                 @Override
@@ -69,6 +71,7 @@
                     // TODO(b/365100489): Look into moving this (and maybe action button click?) to
                     // the `ModalDialogProperties.CONTROLLER` instead.
                     mMediator.onDialogDismissed();
+                    RecordUserAction.record("OsDefaultsChoiceDialogClosed");
                 }
             };
 
@@ -165,6 +168,7 @@
             case DialogType.CHOICE_CONFIRM -> {
                 mModel.set(ModalDialogProperties.CANCEL_ON_TOUCH_OUTSIDE, true);
                 mEmptyBackPressedCallback.remove();
+                RecordUserAction.record("OsDefaultsChoiceDialogUnblocked");
             }
             case DialogType.UNKNOWN -> throw new IllegalStateException();
         }
diff --git a/chrome/browser/search_engines/android/java/src/org/chromium/chrome/browser/search_engines/choice_screen/ChoiceDialogMediator.java b/chrome/browser/search_engines/android/java/src/org/chromium/chrome/browser/search_engines/choice_screen/ChoiceDialogMediator.java
index 3c3d033..b0c239e1 100644
--- a/chrome/browser/search_engines/android/java/src/org/chromium/chrome/browser/search_engines/choice_screen/ChoiceDialogMediator.java
+++ b/chrome/browser/search_engines/android/java/src/org/chromium/chrome/browser/search_engines/choice_screen/ChoiceDialogMediator.java
@@ -12,6 +12,7 @@
 import org.chromium.base.Callback;
 import org.chromium.base.Log;
 import org.chromium.base.ThreadUtils;
+import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.supplier.ObservableSupplier;
 import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
 import org.chromium.chrome.browser.lifecycle.PauseResumeWithNativeObserver;
@@ -61,6 +62,9 @@
  * </ul>
  */
 class ChoiceDialogMediator {
+    // These values are persisted to logs. Entries should not be renumbered and numeric values
+    // should never be reused.
+    // LINT.IfChange
     @IntDef({
         DialogType.UNKNOWN,
         DialogType.LOADING,
@@ -73,8 +77,11 @@
         int LOADING = 1;
         int CHOICE_LAUNCH = 2;
         int CHOICE_CONFIRM = 3;
+        int COUNT = 4;
     }
 
+    // LINT.ThenChange(//tools/metrics/histograms/enums.xml:OsDefaultsChoiceDialogStatus)
+
     /** See {@link #startObserving}. */
     interface Delegate {
         /** Rebuilds the view tp match the requested {@code dialogType}. */
@@ -149,6 +156,10 @@
                     public void onResumeWithNative() {
                         searchEngineChoiceService.refreshDeviceChoiceRequiredNow(
                                 RefreshReason.APP_RESUME);
+                        RecordHistogram.recordEnumeratedHistogram(
+                                "Search.OsDefaultsChoice.DialogStatusOnAppOpen",
+                                mDialogType,
+                                DialogType.COUNT);
                     }
 
                     @Override
@@ -167,7 +178,7 @@
         mDelegate = delegate;
 
         mObservationStartedTimeMillis = System.currentTimeMillis();
-        mDialogType = DialogType.LOADING;
+        changeDialogType(DialogType.LOADING);
 
         if (SearchEnginesFeatureUtils.clayBlockingEnableVerboseLogging()) {
             // TODO(b/355186707): Temporary log to be removed after e2e validation.
@@ -212,7 +223,7 @@
 
         mLifecycleDispatcher.unregister(mActivityLifecycleObserver);
         mIsDeviceChoiceRequiredSupplier.removeObserver(mIsDeviceChoiceRequiredObserver);
-        mDialogType = DialogType.UNKNOWN;
+        changeDialogType(DialogType.UNKNOWN);
 
         delegate.onMediatorDestroyed();
         if (SearchEnginesFeatureUtils.clayBlockingEnableVerboseLogging()) {
@@ -282,10 +293,18 @@
                                 ? mFirstServiceEventTimeMillis - mObservationStartedTimeMillis
                                 : "<N/A>");
             }
+            RecordHistogram.recordMediumTimesHistogram(
+                    "Search.OsDefaultsChoice.DelayFromDialogShownToFirstStatus",
+                    wasDialogShown ? mFirstServiceEventTimeMillis - mDialogAddedTimeMillis : 0);
+            RecordHistogram.recordMediumTimesHistogram(
+                    "Search.OsDefaultsChoice.DelayFromObservationToFirstStatus",
+                    mObservationStartedTimeMillis == null
+                            ? 0
+                            : mFirstServiceEventTimeMillis - mObservationStartedTimeMillis);
         }
 
         if (Boolean.TRUE.equals(isDeviceChoiceRequired) && !wasDialogDismissed) {
-            mDialogType = DialogType.CHOICE_LAUNCH;
+            changeDialogType(DialogType.CHOICE_LAUNCH);
             mDelegate.updateDialogType(DialogType.CHOICE_LAUNCH);
 
             if (!wasDialogShown) {
@@ -309,7 +328,7 @@
                     && (mDialogType == DialogType.LOADING
                             || mDialogType == DialogType.CHOICE_LAUNCH)) {
                 // This is the normal flow, showing confirmation after the choice has been made.
-                mDialogType = DialogType.CHOICE_CONFIRM;
+                changeDialogType(DialogType.CHOICE_CONFIRM);
                 mDelegate.updateDialogType(DialogType.CHOICE_CONFIRM);
                 mSearchEngineChoiceService.notifyDeviceChoiceBlockCleared();
                 return;
@@ -364,8 +383,20 @@
 
                         mDelegate.dismissDialog();
                         destroy();
+                        RecordHistogram.recordMediumTimesHistogram(
+                                "Search.OsDefaultsChoice.DelayFromDialogShownToFirstStatus",
+                                dialogTimeoutMillis);
+                        RecordHistogram.recordMediumTimesHistogram(
+                                "Search.OsDefaultsChoice.DelayFromObservationToFirstStatus",
+                                dialogTimeoutMillis);
                     },
                     dialogTimeoutMillis);
         }
     }
+
+    private void changeDialogType(@DialogType int type) {
+        mDialogType = type;
+        RecordHistogram.recordEnumeratedHistogram(
+                "Search.OsDefaultsChoice.DialogStatusChange", type, DialogType.COUNT);
+    }
 }
diff --git a/chrome/browser/ssl/https_upgrades_browsertest.cc b/chrome/browser/ssl/https_upgrades_browsertest.cc
index bdf0628..99b0e152 100644
--- a/chrome/browser/ssl/https_upgrades_browsertest.cc
+++ b/chrome/browser/ssl/https_upgrades_browsertest.cc
@@ -2007,13 +2007,16 @@
                        CancelTimeoutForFallbackNavigations) {
   net::EmbeddedTestServer http_server;
   net::EmbeddedTestServer https_server{net::EmbeddedTestServer::TYPE_HTTPS};
+
   // Make the HTTP server return a slow response.
   http_server.RegisterRequestHandler(base::BindRepeating(
       [](const net::test_server::HttpRequest& request)
           -> std::unique_ptr<net::test_server::HttpResponse> {
+        // The HTTP load needs to be slower than the 1 second timeout configured
+        // by the the test.
         auto slow_http_response =
             std::make_unique<net::test_server::DelayedHttpResponse>(
-                base::Seconds(1));
+                base::Seconds(2));
         slow_http_response->set_content_type("text/html");
         slow_http_response->set_content("hello from http");
         return std::move(slow_http_response);
@@ -2021,10 +2024,9 @@
   ASSERT_TRUE(http_server.Start());
   ASSERT_TRUE(https_server.Start());
 
-  // Set the timeout short enough, but not zero. We need to wait for the loads
-  // to finish for this test to work.
-  HttpsUpgradesNavigationThrottle::set_timeout_for_testing(
-      base::Milliseconds(250));
+  // Set the timeout short enough, but not zero. We can't set it to zero
+  // because it'll cancel the HTTPS load with timeout instead of an error.
+  HttpsUpgradesNavigationThrottle::set_timeout_for_testing(base::Seconds(1));
 
   HttpsUpgradesInterceptor::SetHttpPortForTesting(http_server.port());
   HttpsUpgradesInterceptor::SetHttpsPortForTesting(https_server.port());
@@ -2078,10 +2080,11 @@
       GURL url(base::StringPrintf("http://a.com:%d/redirect", http_port));
       return RedirectResponseHandler(url, request);
     }
-    // Over http, it prints a slow hello.
+    // Over http, it prints a slow hello. This should delay longer than the
+    // HTTPS upgrade timeout which is set to 1 second.
     auto slow_http_response =
         std::make_unique<net::test_server::DelayedHttpResponse>(
-            base::Seconds(1));
+            base::Seconds(2));
     slow_http_response->set_content_type("text/html");
     slow_http_response->set_content("hello from http");
     return std::move(slow_http_response);
@@ -2114,10 +2117,11 @@
   net::EmbeddedTestServer redirect_server_https{
       net::EmbeddedTestServer::TYPE_HTTPS};
 
-  // Set the timeout short enough, but not zero. We need to wait for the loads
-  // to finish for this test to work.
-  HttpsUpgradesNavigationThrottle::set_timeout_for_testing(
-      base::Milliseconds(250));
+  // Set the timeout short enough, but not zero. We can't set it to zero
+  // because it'll cancel the HTTPS load with timeout instead of an error.
+  // The HTTP load in this test needs to be slower than this timeout for the
+  // test to be meaningful.
+  HttpsUpgradesNavigationThrottle::set_timeout_for_testing(base::Seconds(1));
 
   // We don't know the ports without starting the servers and we can't start
   // the servers without registering request handlers. Pass them as refs so
diff --git a/chrome/browser/ui/android/desktop_windowing/BUILD.gn b/chrome/browser/ui/android/desktop_windowing/BUILD.gn
index 33409f6..1edbc7d 100644
--- a/chrome/browser/ui/android/desktop_windowing/BUILD.gn
+++ b/chrome/browser/ui/android/desktop_windowing/BUILD.gn
@@ -7,14 +7,13 @@
 android_library("java") {
   sources = [
     "java/src/org/chromium/chrome/browser/ui/desktop_windowing/AppHeaderCoordinator.java",
-    "java/src/org/chromium/chrome/browser/ui/desktop_windowing/AppHeaderState.java",
     "java/src/org/chromium/chrome/browser/ui/desktop_windowing/AppHeaderUtils.java",
-    "java/src/org/chromium/chrome/browser/ui/desktop_windowing/DesktopWindowStateProvider.java",
   ]
   deps = [
     "//base:base_java",
     "//chrome/browser/android/lifecycle:java",
     "//chrome/browser/browser_controls/android:java",
+    "//components/browser_ui/desktop_windowing/android:java",
     "//third_party/androidx:androidx_annotation_annotation_java",
     "//third_party/androidx:androidx_core_core_java",
     "//ui/android:ui_java",
@@ -24,7 +23,6 @@
 robolectric_library("junit") {
   sources = [
     "java/src/org/chromium/chrome/browser/ui/desktop_windowing/AppHeaderCoordinatorUnitTest.java",
-    "java/src/org/chromium/chrome/browser/ui/desktop_windowing/AppHeaderStateUnitTest.java",
     "java/src/org/chromium/chrome/browser/ui/desktop_windowing/AppHeaderUtilsUnitTest.java",
   ]
 
@@ -35,6 +33,7 @@
     "//base:base_junit_test_support",
     "//chrome/browser/android/lifecycle:java",
     "//chrome/browser/browser_controls/android:java",
+    "//components/browser_ui/desktop_windowing/android:java",
     "//third_party/androidx:androidx_core_core_java",
     "//third_party/androidx:androidx_test_core_java",
     "//third_party/androidx:androidx_test_ext_junit_java",
diff --git a/chrome/browser/ui/android/desktop_windowing/java/src/org/chromium/chrome/browser/ui/desktop_windowing/AppHeaderCoordinator.java b/chrome/browser/ui/android/desktop_windowing/java/src/org/chromium/chrome/browser/ui/desktop_windowing/AppHeaderCoordinator.java
index 9e1f42c7..f923a39 100644
--- a/chrome/browser/ui/android/desktop_windowing/java/src/org/chromium/chrome/browser/ui/desktop_windowing/AppHeaderCoordinator.java
+++ b/chrome/browser/ui/android/desktop_windowing/java/src/org/chromium/chrome/browser/ui/desktop_windowing/AppHeaderCoordinator.java
@@ -30,6 +30,8 @@
 import org.chromium.chrome.browser.lifecycle.SaveInstanceStateObserver;
 import org.chromium.chrome.browser.lifecycle.TopResumedActivityChangedObserver;
 import org.chromium.chrome.browser.ui.desktop_windowing.AppHeaderUtils.DesktopWindowHeuristicResult;
+import org.chromium.components.browser_ui.desktop_windowing.AppHeaderState;
+import org.chromium.components.browser_ui.desktop_windowing.DesktopWindowStateProvider;
 import org.chromium.ui.InsetObserver;
 import org.chromium.ui.InsetObserver.WindowInsetObserver;
 import org.chromium.ui.InsetsRectProvider;
diff --git a/chrome/browser/ui/android/desktop_windowing/java/src/org/chromium/chrome/browser/ui/desktop_windowing/AppHeaderCoordinatorUnitTest.java b/chrome/browser/ui/android/desktop_windowing/java/src/org/chromium/chrome/browser/ui/desktop_windowing/AppHeaderCoordinatorUnitTest.java
index c2eef61..cc39a131 100644
--- a/chrome/browser/ui/android/desktop_windowing/java/src/org/chromium/chrome/browser/ui/desktop_windowing/AppHeaderCoordinatorUnitTest.java
+++ b/chrome/browser/ui/android/desktop_windowing/java/src/org/chromium/chrome/browser/ui/desktop_windowing/AppHeaderCoordinatorUnitTest.java
@@ -47,6 +47,8 @@
 import org.chromium.chrome.browser.browser_controls.BrowserStateBrowserControlsVisibilityDelegate;
 import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
 import org.chromium.chrome.browser.ui.desktop_windowing.AppHeaderUtils.DesktopWindowHeuristicResult;
+import org.chromium.components.browser_ui.desktop_windowing.AppHeaderState;
+import org.chromium.components.browser_ui.desktop_windowing.DesktopWindowStateProvider;
 import org.chromium.ui.InsetObserver;
 import org.chromium.ui.InsetObserver.WindowInsetObserver;
 import org.chromium.ui.InsetsRectProvider;
diff --git a/chrome/browser/ui/android/desktop_windowing/java/src/org/chromium/chrome/browser/ui/desktop_windowing/AppHeaderUtils.java b/chrome/browser/ui/android/desktop_windowing/java/src/org/chromium/chrome/browser/ui/desktop_windowing/AppHeaderUtils.java
index 1162233..ed6232b9 100644
--- a/chrome/browser/ui/android/desktop_windowing/java/src/org/chromium/chrome/browser/ui/desktop_windowing/AppHeaderUtils.java
+++ b/chrome/browser/ui/android/desktop_windowing/java/src/org/chromium/chrome/browser/ui/desktop_windowing/AppHeaderUtils.java
@@ -11,6 +11,7 @@
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
 import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher.ActivityState;
+import org.chromium.components.browser_ui.desktop_windowing.DesktopWindowStateProvider;
 
 /** Utility class for the desktop windowing feature implementation. */
 // TODO (crbug/328055199): Rename this to DesktopWindowUtils.
diff --git a/chrome/browser/ui/android/desktop_windowing/java/src/org/chromium/chrome/browser/ui/desktop_windowing/AppHeaderUtilsUnitTest.java b/chrome/browser/ui/android/desktop_windowing/java/src/org/chromium/chrome/browser/ui/desktop_windowing/AppHeaderUtilsUnitTest.java
index 0846776..1d64a2fe 100644
--- a/chrome/browser/ui/android/desktop_windowing/java/src/org/chromium/chrome/browser/ui/desktop_windowing/AppHeaderUtilsUnitTest.java
+++ b/chrome/browser/ui/android/desktop_windowing/java/src/org/chromium/chrome/browser/ui/desktop_windowing/AppHeaderUtilsUnitTest.java
@@ -19,6 +19,8 @@
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
 import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher.ActivityState;
+import org.chromium.components.browser_ui.desktop_windowing.AppHeaderState;
+import org.chromium.components.browser_ui.desktop_windowing.DesktopWindowStateProvider;
 
 /** Unit tests for {@link AppHeaderUtils}. */
 @RunWith(BaseRobolectricTestRunner.class)
diff --git a/chrome/browser/ui/android/toolbar/BUILD.gn b/chrome/browser/ui/android/toolbar/BUILD.gn
index 1f40d76..43b9b51 100644
--- a/chrome/browser/ui/android/toolbar/BUILD.gn
+++ b/chrome/browser/ui/android/toolbar/BUILD.gn
@@ -149,6 +149,7 @@
     "//chrome/browser/ui/android/theme:java",
     "//chrome/browser/user_education:java",
     "//chrome/browser/util:java",
+    "//components/browser_ui/desktop_windowing/android:java",
     "//components/browser_ui/settings/android:java",
     "//components/browser_ui/styles/android:java",
     "//components/browser_ui/styles/android:java_resources",
@@ -365,6 +366,7 @@
     "//chrome/browser/ui/android/toolbar:java_resources",
     "//chrome/browser/user_education:java",
     "//chrome/test/android:chrome_java_unit_test_support",
+    "//components/browser_ui/desktop_windowing/android:java",
     "//components/browser_ui/settings/android:java",
     "//components/browser_ui/styles/android:java",
     "//components/browser_ui/theme/android:java_resources",
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarControlContainer.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarControlContainer.java
index 9eb8e1b..f58eeb07 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarControlContainer.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarControlContainer.java
@@ -44,8 +44,8 @@
 import org.chromium.chrome.browser.toolbar.ToolbarFeatures;
 import org.chromium.chrome.browser.toolbar.ToolbarProgressBar;
 import org.chromium.chrome.browser.toolbar.top.CaptureReadinessResult.TopToolbarBlockCaptureReason;
-import org.chromium.chrome.browser.ui.desktop_windowing.AppHeaderState;
-import org.chromium.chrome.browser.ui.desktop_windowing.DesktopWindowStateProvider;
+import org.chromium.components.browser_ui.desktop_windowing.AppHeaderState;
+import org.chromium.components.browser_ui.desktop_windowing.DesktopWindowStateProvider;
 import org.chromium.components.browser_ui.widget.ClipDrawableProgressBar.DrawingInfo;
 import org.chromium.components.browser_ui.widget.ViewResourceFrameLayout;
 import org.chromium.components.browser_ui.widget.gesture.SwipeGestureListener;
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarControlContainerTest.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarControlContainerTest.java
index 376e44f..e6993442 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarControlContainerTest.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarControlContainerTest.java
@@ -52,7 +52,7 @@
 import org.chromium.chrome.browser.toolbar.top.CaptureReadinessResult.TopToolbarBlockCaptureReason;
 import org.chromium.chrome.browser.toolbar.top.ToolbarControlContainer.ToolbarViewResourceAdapter;
 import org.chromium.chrome.browser.toolbar.top.ToolbarControlContainer.ToolbarViewResourceAdapter.ToolbarInMotionStage;
-import org.chromium.chrome.browser.ui.desktop_windowing.AppHeaderState;
+import org.chromium.components.browser_ui.desktop_windowing.AppHeaderState;
 import org.chromium.components.browser_ui.styles.ChromeColors;
 import org.chromium.ui.base.TestActivity;
 
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/TopToolbarCoordinator.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/TopToolbarCoordinator.java
index a89ebac..e9720ca4 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/TopToolbarCoordinator.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/TopToolbarCoordinator.java
@@ -49,8 +49,8 @@
 import org.chromium.chrome.browser.toolbar.top.tab_strip.TabStripTransitionCoordinator.TabStripHeightObserver;
 import org.chromium.chrome.browser.toolbar.top.tab_strip.TabStripTransitionCoordinator.TabStripTransitionDelegate;
 import org.chromium.chrome.browser.ui.appmenu.AppMenuButtonHelper;
-import org.chromium.chrome.browser.ui.desktop_windowing.DesktopWindowStateProvider;
 import org.chromium.chrome.browser.user_education.UserEducationHelper;
+import org.chromium.components.browser_ui.desktop_windowing.DesktopWindowStateProvider;
 import org.chromium.components.browser_ui.styles.ChromeColors;
 import org.chromium.components.feature_engagement.Tracker;
 import org.chromium.ui.resources.ResourceManager;
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/tab_strip/TabStripTransitionCoordinator.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/tab_strip/TabStripTransitionCoordinator.java
index 8400310..2a1e00a2c 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/tab_strip/TabStripTransitionCoordinator.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/tab_strip/TabStripTransitionCoordinator.java
@@ -23,10 +23,10 @@
 import org.chromium.chrome.browser.toolbar.ControlContainer;
 import org.chromium.chrome.browser.toolbar.R;
 import org.chromium.chrome.browser.toolbar.top.ToolbarLayout;
-import org.chromium.chrome.browser.ui.desktop_windowing.AppHeaderState;
 import org.chromium.chrome.browser.ui.desktop_windowing.AppHeaderUtils;
-import org.chromium.chrome.browser.ui.desktop_windowing.DesktopWindowStateProvider;
-import org.chromium.chrome.browser.ui.desktop_windowing.DesktopWindowStateProvider.AppHeaderObserver;
+import org.chromium.components.browser_ui.desktop_windowing.AppHeaderState;
+import org.chromium.components.browser_ui.desktop_windowing.DesktopWindowStateProvider;
+import org.chromium.components.browser_ui.desktop_windowing.DesktopWindowStateProvider.AppHeaderObserver;
 import org.chromium.ui.util.TokenHolder;
 
 /** Class used to manage tab strip visibility and height updates. */
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/tab_strip/TabStripTransitionCoordinatorUnitTest.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/tab_strip/TabStripTransitionCoordinatorUnitTest.java
index a652d4d3..11304dd 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/tab_strip/TabStripTransitionCoordinatorUnitTest.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/tab_strip/TabStripTransitionCoordinatorUnitTest.java
@@ -57,9 +57,9 @@
 import org.chromium.chrome.browser.toolbar.ToolbarFeatures;
 import org.chromium.chrome.browser.toolbar.top.tab_strip.TabStripTransitionCoordinator.TabStripHeightObserver;
 import org.chromium.chrome.browser.toolbar.top.tab_strip.TabStripTransitionCoordinator.TabStripTransitionDelegate;
-import org.chromium.chrome.browser.ui.desktop_windowing.AppHeaderState;
 import org.chromium.chrome.browser.ui.desktop_windowing.AppHeaderUtils.DesktopWindowModeState;
-import org.chromium.chrome.browser.ui.desktop_windowing.DesktopWindowStateProvider;
+import org.chromium.components.browser_ui.desktop_windowing.AppHeaderState;
+import org.chromium.components.browser_ui.desktop_windowing.DesktopWindowStateProvider;
 import org.chromium.ui.base.TestActivity;
 import org.chromium.ui.resources.Resource;
 import org.chromium.ui.resources.dynamics.ViewResourceAdapter;
diff --git a/chrome/browser/ui/ash/birch/birch_coral_provider_browsertest.cc b/chrome/browser/ui/ash/birch/birch_coral_provider_browsertest.cc
index 62ecb4a9..b90e7d9d 100644
--- a/chrome/browser/ui/ash/birch/birch_coral_provider_browsertest.cc
+++ b/chrome/browser/ui/ash/birch/birch_coral_provider_browsertest.cc
@@ -30,11 +30,7 @@
 
 class BirchCoralProviderTest : public extensions::PlatformAppBrowserTest {
  public:
-  BirchCoralProviderTest() {
-    scoped_feature_list_.InitWithFeatures(
-        {features::kBirchCoral, features::kTabClusterUI}, {});
-  }
-
+  BirchCoralProviderTest() = default;
   BirchCoralProviderTest(const BirchCoralProviderTest&) = delete;
   BirchCoralProviderTest& operator=(const BirchCoralProviderTest&) = delete;
   ~BirchCoralProviderTest() override = default;
@@ -58,7 +54,7 @@
   }
 
  private:
-  base::test::ScopedFeatureList scoped_feature_list_;
+  base::test::ScopedFeatureList scoped_feature_list_{features::kBirchCoral};
 };
 
 // Tests that the coral provider collects correct in-session tab and app data.
diff --git a/chrome/browser/ui/ash/main_extra_parts/chrome_browser_main_extra_parts_ash.cc b/chrome/browser/ui/ash/main_extra_parts/chrome_browser_main_extra_parts_ash.cc
index 5ec038e..aaf490dd 100644
--- a/chrome/browser/ui/ash/main_extra_parts/chrome_browser_main_extra_parts_ash.cc
+++ b/chrome/browser/ui/ash/main_extra_parts/chrome_browser_main_extra_parts_ash.cc
@@ -281,7 +281,7 @@
   system_tray_client_ = std::make_unique<SystemTrayClientImpl>();
   network_connect_delegate_->SetSystemTrayClient(system_tray_client_.get());
 
-  if (ash::features::IsTabClusterUIEnabled()) {
+  if (ash::features::IsBirchCoralEnabled()) {
     ash::TabClusterUIController* tab_cluster_ui_controller =
         ash::Shell::Get()->tab_cluster_ui_controller();
     DCHECK(tab_cluster_ui_controller);
diff --git a/chrome/browser/ui/ash/wm/coral_browsertest.cc b/chrome/browser/ui/ash/wm/coral_browsertest.cc
index 09481287..5db9cadd 100644
--- a/chrome/browser/ui/ash/wm/coral_browsertest.cc
+++ b/chrome/browser/ui/ash/wm/coral_browsertest.cc
@@ -62,8 +62,6 @@
  public:
   CoralBrowserTest() {
     set_launch_browser_for_testing(nullptr);
-    scoped_feature_list_.InitWithFeatures(
-        {features::kBirchCoral, features::kTabClusterUI}, {});
   }
   CoralBrowserTest(const CoralBrowserTest&) = delete;
   CoralBrowserTest& operator=(const CoralBrowserTest&) = delete;
@@ -88,7 +86,7 @@
   }
 
  private:
-  base::test::ScopedFeatureList scoped_feature_list_;
+  base::test::ScopedFeatureList scoped_feature_list_{features::kBirchCoral};
 };
 
 IN_PROC_BROWSER_TEST_F(CoralBrowserTest, PRE_PostLoginBrowser) {
diff --git a/chrome/browser/ui/lens/lens_overlay_blur_layer_delegate.cc b/chrome/browser/ui/lens/lens_overlay_blur_layer_delegate.cc
index d2a549b..1a19da5 100644
--- a/chrome/browser/ui/lens/lens_overlay_blur_layer_delegate.cc
+++ b/chrome/browser/ui/lens/lens_overlay_blur_layer_delegate.cc
@@ -29,11 +29,11 @@
 namespace lens {
 
 LensOverlayBlurLayerDelegate::LensOverlayBlurLayerDelegate(
-    ui::Layer* layer,
     content::RenderWidgetHost* background_view_host)
-    : layer_(layer), background_view_host_(background_view_host) {
-  CHECK(layer);
-  layer->set_delegate(this);
+    : background_view_host_(background_view_host) {
+  SetLayer(std::make_unique<ui::Layer>(ui::LAYER_TEXTURED));
+  layer()->SetFillsBoundsOpaquely(true);
+  layer()->set_delegate(this);
 
   render_widget_host_observer_.Observe(background_view_host);
 
@@ -83,7 +83,7 @@
   filter_flags.setImageFilter(filter);
 
   // Configure `canvas`.
-  gfx::SizeF layer_size(layer_->size());
+  gfx::SizeF layer_size(layer()->size());
   ui::PaintRecorder recorder(context, gfx::ToFlooredSize(layer_size));
   gfx::Canvas* const canvas = recorder.canvas();
 
@@ -130,12 +130,13 @@
 
 void LensOverlayBlurLayerDelegate::UpdateBackgroundImage(
     const SkBitmap& bitmap) {
-  if (bitmap.drawsNothing() || layer_->size().IsEmpty() ||
+  auto layer_size = layer()->size();
+  if (bitmap.drawsNothing() || layer_size.width() * layer_size.height() <= 0 ||
       AreBitmapsEqual(background_screenshot_, bitmap)) {
     return;
   }
   background_screenshot_ = bitmap;
-  layer_->SchedulePaint(gfx::Rect(layer_->size()));
+  layer()->SchedulePaint(gfx::Rect(layer_size));
 }
 
 }  // namespace lens
diff --git a/chrome/browser/ui/lens/lens_overlay_blur_layer_delegate.h b/chrome/browser/ui/lens/lens_overlay_blur_layer_delegate.h
index 91e821a..745de89 100644
--- a/chrome/browser/ui/lens/lens_overlay_blur_layer_delegate.h
+++ b/chrome/browser/ui/lens/lens_overlay_blur_layer_delegate.h
@@ -14,6 +14,7 @@
 #include "third_party/skia/include/core/SkBitmap.h"
 #include "ui/compositor/layer.h"
 #include "ui/compositor/layer_delegate.h"
+#include "ui/compositor/layer_owner.h"
 #include "ui/compositor/paint_context.h"
 
 namespace lens {
@@ -21,17 +22,18 @@
 // LayerDelegate for controlling the background blur behind the overlay. This
 // class is only a LayerDelegate to control the layer painting and does not own
 // the layer.
-class LensOverlayBlurLayerDelegate : public ui::LayerDelegate,
+class LensOverlayBlurLayerDelegate : public ui::LayerOwner,
+                                     public ui::LayerDelegate,
                                      public content::RenderWidgetHostObserver {
  public:
-  // Starts painting to the given layer with a blurred background image using
-  // the given render view as the background. Note: Initializing this class does
-  // not start capturing screenshots of the background view until
+  // Creates a layer and starts painting to it with a blurred background image
+  // using the given render view as the background. Note: Initializing this
+  // class does not start capturing screenshots of the background view until
   // StartBackgroundImageCapture(). Meaning, until StartBackgroundImageCapture,
   // this layer will paint a static blurred image that was taken on
   // initialization.
-  LensOverlayBlurLayerDelegate(ui::Layer* layer,
-                               content::RenderWidgetHost* background_view_host);
+  explicit LensOverlayBlurLayerDelegate(
+      content::RenderWidgetHost* background_view_host);
   ~LensOverlayBlurLayerDelegate() override;
 
   // Starts taking screenshots of the background view to use for blurring.
@@ -66,10 +68,6 @@
   // A timer to periodically take screenshots of the underlying page.
   base::RepeatingTimer screenshot_timer_;
 
-  // Pointer to the layer we are adding the blur to. Belongs to a child of the
-  // LensOverlayController so guaranteed to outlive this class.
-  raw_ptr<ui::Layer> layer_;
-
   // Pointer to the RenderWidgetHost to get the contents which we are
   // blurring. Owned by the live page web contents, so is possible to become
   // null.
diff --git a/chrome/browser/ui/lens/lens_overlay_controller.cc b/chrome/browser/ui/lens/lens_overlay_controller.cc
index 316400a7..d67fcd0 100644
--- a/chrome/browser/ui/lens/lens_overlay_controller.cc
+++ b/chrome/browser/ui/lens/lens_overlay_controller.cc
@@ -1778,6 +1778,9 @@
 
   gfx::Rect bounds = observed_view->GetLocalBounds();
   overlay_view_->SetBoundsRect(bounds);
+  if(lens_overlay_blur_layer_delegate_) {
+    lens_overlay_blur_layer_delegate_->layer()->SetBounds(bounds);
+  }
 }
 
 void LensOverlayController::OnWidgetDestroying(views::Widget* widget) {
@@ -2079,10 +2082,6 @@
   }
 
   if (lens::features::GetLensOverlayUseCustomBlur()) {
-    overlay_view_->SetPaintToLayer();
-    ui::Layer* background_layer = overlay_view_->layer();
-    background_layer->SetFillsBoundsOpaquely(true);
-
     content::RenderWidgetHost* live_page_widget_host =
         tab_->GetContents()
             ->GetPrimaryMainFrame()
@@ -2092,7 +2091,13 @@
     // Create the blur delegate which will start blurring the background;
     lens_overlay_blur_layer_delegate_ =
         std::make_unique<lens::LensOverlayBlurLayerDelegate>(
-            background_layer, live_page_widget_host);
+            live_page_widget_host);
+
+    // Add our blur layer to the view.
+    overlay_view_->SetPaintToLayer();
+    overlay_view_->layer()->Add(lens_overlay_blur_layer_delegate_->layer());
+    lens_overlay_blur_layer_delegate_->layer()->SetBounds(
+        overlay_view_->layer()->bounds());
     return;
   }
 
diff --git a/chrome/browser/ui/lens/lens_overlay_query_controller.cc b/chrome/browser/ui/lens/lens_overlay_query_controller.cc
index a7de36b..763afa5 100644
--- a/chrome/browser/ui/lens/lens_overlay_query_controller.cc
+++ b/chrome/browser/ui/lens/lens_overlay_query_controller.cc
@@ -7,6 +7,7 @@
 #include <optional>
 
 #include "base/base64url.h"
+#include "base/functional/bind.h"
 #include "base/logging.h"
 #include "base/rand_util.h"
 #include "base/task/bind_post_task.h"
@@ -59,7 +60,9 @@
 
 // The name string for the header for variations information.
 constexpr char kClientDataHeader[] = "X-Client-Data";
-constexpr char kHttpMethod[] = "POST";
+constexpr char kHttpGetMethod[] = "GET";
+constexpr char kHttpPostMethod[] = "POST";
+constexpr char kContentTypeKey[] = "Content-Type";
 constexpr char kContentType[] = "application/x-protobuf";
 constexpr char kDeveloperKey[] = "X-Developer-Key";
 constexpr char kSessionIdQueryParameterKey[] = "gsessionid";
@@ -152,6 +155,21 @@
   return headers;
 }
 
+std::vector<std::string> CreateVariationsHeaders(
+    variations::mojom::VariationsHeadersPtr variations) {
+  std::vector<std::string> headers;
+  if (variations.is_null()) {
+    return headers;
+  }
+
+  headers.push_back(kClientDataHeader);
+  // The endpoint is always a Google property.
+  headers.push_back(variations->headers_map.at(
+      variations::mojom::GoogleWebVisibility::FIRST_PARTY));
+
+  return headers;
+}
+
 std::map<std::string, std::string> AddStartTimeQueryParam(
     std::map<std::string, std::string> additional_search_query_params) {
   int64_t current_time_ms = base::Time::Now().InMillisecondsSinceUnixEpoch();
@@ -352,33 +370,11 @@
     base::OnceCallback<void(std::unique_ptr<EndpointFetcher>)>
         fetcher_created_callback,
     EndpointFetcherCallback fetched_response_callback) {
-  // Use OAuth if the flag is enabled and the user is logged in.
-  if (lens::features::UseOauthForLensOverlayRequests() && identity_manager_ &&
-      identity_manager_->HasPrimaryAccount(signin::ConsentLevel::kSignin)) {
-    signin::AccessTokenFetcher::TokenCallback token_callback =
-        base::BindOnce(&lens::CreateOAuthHeader)
-            .Then(base::BindOnce(&LensOverlayQueryController::FetchEndpoint,
-                                 weak_ptr_factory_.GetWeakPtr(), request_data,
-                                 std::move(fetcher_created_callback),
-                                 std::move(fetched_response_callback)));
-    signin::ScopeSet oauth_scopes;
-    oauth_scopes.insert(GaiaConstants::kLensOAuth2Scope);
-
-    // If an access token fetcher is already in flight, it is intentionally
-    // replaced by this newer one.
-    access_token_fetcher_ =
-        std::make_unique<signin::PrimaryAccountAccessTokenFetcher>(
-            kOAuthConsumerName, identity_manager_, oauth_scopes,
-            std::move(token_callback),
-            signin::PrimaryAccountAccessTokenFetcher::Mode::kWaitUntilAvailable,
-            signin::ConsentLevel::kSignin);
-    return;
-  }
-
-  // Fall back to fetching the endpoint directly using API key.
-  FetchEndpoint(request_data, std::move(fetcher_created_callback),
-                std::move(fetched_response_callback),
-                /*headers=*/std::vector<std::string>());
+  CreateOAuthHeadersAndContinue(
+      base::BindOnce(&LensOverlayQueryController::FetchEndpoint,
+                     weak_ptr_factory_.GetWeakPtr(), request_data,
+                     std::move(fetcher_created_callback),
+                     std::move(fetched_response_callback)));
 }
 
 void LensOverlayQueryController::PerformFetchRequest(
@@ -395,15 +391,8 @@
   CHECK(request->SerializeToString(&request_string));
 
   // Get client experiment variations to include in the request.
-  std::vector<std::string> cors_exempt_headers;
-  variations::mojom::VariationsHeadersPtr variations =
-      variations_client_->GetVariationsHeaders();
-  if (!variations.is_null()) {
-    cors_exempt_headers.push_back(kClientDataHeader);
-    // The endpoint is always a Google property.
-    cors_exempt_headers.push_back(variations->headers_map.at(
-        variations::mojom::GoogleWebVisibility::FIRST_PARTY));
-  }
+  std::vector<std::string> cors_exempt_headers =
+      CreateVariationsHeaders(variations_client_->GetVariationsHeaders());
 
   // Generate the URL to fetch to and include the server session id if present.
   GURL fetch_url = GURL(lens::features::GetLensOverlayEndpointURL());
@@ -423,7 +412,7 @@
               ? profile_->GetURLLoaderFactory().get()
               : g_browser_process->shared_url_loader_factory(),
           /*url=*/fetch_url,
-          /*http_method=*/kHttpMethod,
+          /*http_method=*/kHttpPostMethod,
           /*content_type=*/kContentType,
           base::Milliseconds(
               lens::features::GetLensOverlayServerRequestTimeout()),
@@ -462,7 +451,104 @@
 LensOverlayQueryController::LensServerFetchRequest::~LensServerFetchRequest() =
     default;
 
+void LensOverlayQueryController::FetchClusterInfoRequest() {
+  query_controller_state_ = QueryControllerState::kAwaitingClusterInfoResponse;
+
+  CreateOAuthHeadersAndContinue(base::BindOnce(
+      &LensOverlayQueryController::PerformClusterInfoFetchRequest,
+      weak_ptr_factory_.GetWeakPtr()));
+}
+
+void LensOverlayQueryController::PerformClusterInfoFetchRequest(
+    std::vector<std::string> request_headers) {
+  // Add protobuf content type to the request headers.
+  request_headers.push_back(kContentTypeKey);
+  request_headers.push_back(kContentType);
+
+  // Get client experiment variations to include in the request.
+  std::vector<std::string> cors_exempt_headers =
+      CreateVariationsHeaders(variations_client_->GetVariationsHeaders());
+
+  // Generate the URL to fetch.
+  GURL fetch_url = GURL(lens::features::GetLensOverlayClusterInfoEndpointUrl());
+
+  // Create the EndpointFetcher, responsible for making the request using our
+  // given params. Store in class variable to keep endpoint fetcher alive until
+  // the request is made.
+  cluster_info_endpoint_fetcher_ = std::make_unique<EndpointFetcher>(
+      /*url_loader_factory=*/profile_
+          ? profile_->GetURLLoaderFactory().get()
+          : g_browser_process->shared_url_loader_factory(),
+      /*url=*/fetch_url,
+      /*http_method=*/kHttpGetMethod,
+      /*content_type=*/kContentType,
+      base::Milliseconds(lens::features::GetLensOverlayServerRequestTimeout()),
+      /*post_data=*/"",
+      /*headers=*/request_headers,
+      /*cors_exempt_headers=*/cors_exempt_headers,
+      /*annotation_tag=*/kTrafficAnnotationTag, chrome::GetChannel(),
+      /*request_params=*/
+      EndpointFetcher::RequestParams::Builder()
+          .SetCredentialsMode(CredentialsMode::kInclude)
+          .Build());
+
+  // Finally, perform the request.
+  cluster_info_endpoint_fetcher_->PerformRequest(
+      base::BindOnce(
+          &LensOverlayQueryController::ClusterInfoFetchResponseHandler,
+          weak_ptr_factory_.GetWeakPtr()),
+      google_apis::GetAPIKey().c_str());
+}
+
+void LensOverlayQueryController::ClusterInfoFetchResponseHandler(
+    std::unique_ptr<EndpointResponse> response) {
+  cluster_info_endpoint_fetcher_.reset();
+  query_controller_state_ = QueryControllerState::kReceivedClusterInfoResponse;
+
+  if (response->http_status_code != google_apis::ApiErrorCode::HTTP_SUCCESS) {
+    // If there was an error with the cluster info request, we should still try
+    // and send the full image request as a fallback.
+    PrepareAndFetchFullImageRequest();
+    return;
+  }
+
+  lens::LensOverlayServerClusterInfoResponse server_response;
+  const std::string response_string = response->response;
+  bool parse_successful = server_response.ParseFromArray(
+      response_string.data(), response_string.size());
+  if (!parse_successful) {
+    // If there was an error with the cluster info request, we should still try
+    // and send the full image request as a fallback.
+    PrepareAndFetchFullImageRequest();
+    return;
+  }
+
+  // Store the cluster info.
+  cluster_info_ = std::make_optional<lens::LensOverlayClusterInfo>();
+  cluster_info_->set_server_session_id(server_response.server_session_id());
+  cluster_info_->set_search_session_id(server_response.search_session_id());
+
+  // Continue with the full image request which will use the session id from the
+  // cluster info we just received.
+  PrepareAndFetchFullImageRequest();
+}
+
 void LensOverlayQueryController::PrepareAndFetchFullImageRequest() {
+  if (query_controller_state_ ==
+      QueryControllerState::kAwaitingClusterInfoResponse) {
+    // If we are still waiting for the cluster info response, we can't send the
+    // full image request yet. Once the cluster info response is received,
+    // PrepareAndFetchFullImageRequest will be called again.
+    return;
+  }
+
+  // If the optimized flow is enabled, request the cluster info prior to making
+  // the full image request.
+  if (!cluster_info_ && lens::features::UseOptimizedRequestFlow()) {
+    FetchClusterInfoRequest();
+    return;
+  }
+
   // There can be multiple full image requests that are called. For example,
   // when translate mode is enabled after opening the overlay or when turning
   // translate mode back off after enabling. Reset if there is one pending.
@@ -564,35 +650,11 @@
   DCHECK_EQ(query_controller_state_,
             QueryControllerState::kAwaitingFullImageResponse);
 
-  // Use OAuth if the flag is enabled and the user is logged in.
-  if (lens::features::UseOauthForLensOverlayRequests() && identity_manager_ &&
-      identity_manager_->HasPrimaryAccount(signin::ConsentLevel::kSignin)) {
-    // Create callback that uses the tokens to create the OAuth headers and pass
-    // the headers to FullImageRequestDataReady.
-    signin::AccessTokenFetcher::TokenCallback token_callback =
-        base::BindOnce(&lens::CreateOAuthHeader)
-            .Then(base::BindOnce(
-                static_cast<void (LensOverlayQueryController::*)(
-                    int, std::vector<std::string>)>(
-                    &LensOverlayQueryController::FullImageRequestDataReady),
-                weak_ptr_factory_.GetWeakPtr(), sequence_id));
-    signin::ScopeSet oauth_scopes;
-    oauth_scopes.insert(GaiaConstants::kLensOAuth2Scope);
-
-    // If an access token fetcher is already in flight, it is intentionally
-    // replaced by this newer one.
-    access_token_fetcher_ =
-        std::make_unique<signin::PrimaryAccountAccessTokenFetcher>(
-            kOAuthConsumerName, identity_manager_, oauth_scopes,
-            std::move(token_callback),
-            signin::PrimaryAccountAccessTokenFetcher::Mode::kWaitUntilAvailable,
-            signin::ConsentLevel::kSignin);
-    return;
-  }
-
-  // Supply no OAuth Headers which will fallback to directly using API key.
-  FullImageRequestDataReady(sequence_id,
-                            /*headers=*/std::vector<std::string>());
+  CreateOAuthHeadersAndContinue(base::BindOnce(
+      static_cast<void (LensOverlayQueryController::*)(
+          int, std::vector<std::string>)>(
+          &LensOverlayQueryController::FullImageRequestDataReady),
+      weak_ptr_factory_.GetWeakPtr(), sequence_id));
 }
 
 void LensOverlayQueryController::FullImageRequestDataReady(
@@ -770,6 +832,31 @@
   return context;
 }
 
+void LensOverlayQueryController::CreateOAuthHeadersAndContinue(
+    OAuthHeadersCreatedCallback callback) {
+  // Use OAuth if the flag is enabled and the user is logged in.
+  if (lens::features::UseOauthForLensOverlayRequests() && identity_manager_ &&
+      identity_manager_->HasPrimaryAccount(signin::ConsentLevel::kSignin)) {
+    signin::AccessTokenFetcher::TokenCallback token_callback =
+        base::BindOnce(&lens::CreateOAuthHeader).Then(std::move(callback));
+    signin::ScopeSet oauth_scopes;
+    oauth_scopes.insert(GaiaConstants::kLensOAuth2Scope);
+
+    // If an access token fetcher is already in flight, it is intentionally
+    // replaced by this newer one.
+    access_token_fetcher_ =
+        std::make_unique<signin::PrimaryAccountAccessTokenFetcher>(
+            kOAuthConsumerName, identity_manager_, oauth_scopes,
+            std::move(token_callback),
+            signin::PrimaryAccountAccessTokenFetcher::Mode::kWaitUntilAvailable,
+            signin::ConsentLevel::kSignin);
+    return;
+  }
+
+  // Fall back to fetching the endpoint directly using API key.
+  std::move(callback).Run(std::vector<std::string>());
+}
+
 std::map<std::string, std::string>
 LensOverlayQueryController::AddVisualSearchInteractionLogData(
     std::map<std::string, std::string> additional_search_query_params,
@@ -1071,15 +1158,10 @@
   access_token_fetcher_.reset();
   std::string request_data_string;
   CHECK(request_data.SerializeToString(&request_data_string));
-  std::vector<std::string> cors_exempt_headers;
-  variations::mojom::VariationsHeadersPtr variations =
-      variations_client_->GetVariationsHeaders();
-  if (!variations.is_null()) {
-    cors_exempt_headers.push_back(kClientDataHeader);
-    // The endpoint is always a Google property.
-    cors_exempt_headers.push_back(variations->headers_map.at(
-        variations::mojom::GoogleWebVisibility::FIRST_PARTY));
-  }
+
+  // Get client experiment variations to include in the request.
+  std::vector<std::string> cors_exempt_headers =
+      CreateVariationsHeaders(variations_client_->GetVariationsHeaders());
 
   GURL fetch_url = GURL(lens::features::GetLensOverlayEndpointURL());
   if (cluster_info_.has_value()) {
@@ -1096,7 +1178,7 @@
               ? profile_->GetURLLoaderFactory().get()
               : g_browser_process->shared_url_loader_factory(),
           /*url=*/fetch_url,
-          /*http_method=*/kHttpMethod,
+          /*http_method=*/kHttpPostMethod,
           /*content_type=*/kContentType,
           base::Milliseconds(
               lens::features::GetLensOverlayServerRequestTimeout()),
diff --git a/chrome/browser/ui/lens/lens_overlay_query_controller.h b/chrome/browser/ui/lens/lens_overlay_query_controller.h
index 4fbcd350..c9a53703 100644
--- a/chrome/browser/ui/lens/lens_overlay_query_controller.h
+++ b/chrome/browser/ui/lens/lens_overlay_query_controller.h
@@ -58,6 +58,10 @@
 // Callback type alias for the thumbnail image creation.
 using LensOverlayThumbnailCreatedCallback =
     base::RepeatingCallback<void(const std::string&)>;
+// Callback type alias for the OAuth headers created.
+using OAuthHeadersCreatedCallback =
+    base::OnceCallback<void(std::vector<std::string>)>;
+
 // Manages queries on behalf of a Lens overlay.
 class LensOverlayQueryController {
  public:
@@ -167,14 +171,20 @@
     // StartQueryFlow has not been called and the query controller is
     // inactive.
     kOff = 0,
+    // StartQueryFlow has been called, but the cluster info has not been
+    // received so we cannot proceed to sending the full image request.
+    kAwaitingClusterInfoResponse = 1,
+    // The cluster info response has been received so we can proceed to sending
+    // the full image request.
+    kReceivedClusterInfoResponse = 2,
     // The full image response has not been received, or is no longer valid.
-    kAwaitingFullImageResponse = 1,
+    kAwaitingFullImageResponse = 3,
     // The full image response has been received and the query controller can
     // send interaction requests.
-    kReceivedFullImageResponse = 2,
+    kReceivedFullImageResponse = 4,
     // The full image response has been received and resulted in an error
     // response.
-    kReceivedFullImageErrorResponse = 3,
+    kReceivedFullImageErrorResponse = 5,
   };
 
   // Data class for constructing a fetch request to the Lens servers.
@@ -183,8 +193,7 @@
   // not need to be set, but should be included if it is set, use std::optional.
   struct LensServerFetchRequest {
    public:
-    LensServerFetchRequest(int sequence_id,
-                           base::TimeTicks query_start_time);
+    LensServerFetchRequest(int sequence_id, base::TimeTicks query_start_time);
     ~LensServerFetchRequest();
 
     // The sequence ID of the request this data belongs to. Used for cancelling
@@ -202,6 +211,20 @@
     std::unique_ptr<std::vector<std::string>> request_headers_;
   };
 
+  // Makes a LensOverlayServerClusterInfoRequest to get the cluster info. Will
+  // continue to the FullImageRequest once a response is received.
+  void FetchClusterInfoRequest();
+
+  // Creates the endpoint fetcher and sends the cluster info request.
+  void PerformClusterInfoFetchRequest(std::vector<std::string> request_headers);
+
+  // Handles the response from the cluster info request. If a successful request
+  // was made, kicks off the full image request to use the retrieved server
+  // session id. If the request failed, the full image request will still be
+  // tried, just without the server session id.
+  void ClusterInfoFetchResponseHandler(
+      std::unique_ptr<EndpointResponse> response);
+
   // Processes the screenshot and fetches a full image request.
   void PrepareAndFetchFullImageRequest();
 
@@ -258,6 +281,11 @@
   // Creates a client context proto to be attached to a server request.
   lens::LensOverlayClientContext CreateClientContext();
 
+  // Fetches the OAuth headers and calls the callback with the headers. If the
+  // OAuth cannot be retrieve (like if the user is not logged in), the callback
+  // will be called with an empty vector.
+  void CreateOAuthHeadersAndContinue(OAuthHeadersCreatedCallback callback);
+
   // Adds the visual search interaction log data param to the search query
   // params.
   std::map<std::string, std::string> AddVisualSearchInteractionLogData(
@@ -408,6 +436,9 @@
   // request has been made.
   std::unique_ptr<LensServerFetchRequest> latest_full_image_request_data_;
 
+  // The endpoint fetcher used for the cluster info request.
+  std::unique_ptr<EndpointFetcher> cluster_info_endpoint_fetcher_;
+
   // The endpoint fetcher used for the full image request.
   std::unique_ptr<EndpointFetcher> full_image_endpoint_fetcher_;
 
diff --git a/chrome/browser/ui/tabs/organization/tab_declutter_controller.cc b/chrome/browser/ui/tabs/organization/tab_declutter_controller.cc
index ebf121b1..71b1d6e 100644
--- a/chrome/browser/ui/tabs/organization/tab_declutter_controller.cc
+++ b/chrome/browser/ui/tabs/organization/tab_declutter_controller.cc
@@ -24,31 +24,24 @@
 namespace tabs {
 
 namespace {
-// TODO(b/362269642): Make this constant finch configurable.
-
-// Duration of inactivity after which a tab is considered stale.
-constexpr int kStaleThresholdDurationDays = 7;
-// Interval between a recomputation of stale tabs.
-constexpr base::TimeDelta kTimerIntervalMinutes = base::Minutes(10);
 // Minimum number of tabs in the tabstrip to show the nudge.
 constexpr int kMinTabCountForNudge = 15;
 // Minimum percentage of stale tabs in the tabstrip to show the nudge.
 constexpr double kStaleTabPercentageThreshold = 0.10;
-// Default interval after showing a nudge to prevent another nudge from being
-// shown.
-constexpr base::TimeDelta kDefaultNudgeTimerIntervalMinutes =
-    base::Minutes(6 * 60);
 }  // namespace
 
 TabDeclutterController::TabDeclutterController(TabStripModel* tab_strip_model)
-    : stale_tab_threshold_duration_(base::Days(kStaleThresholdDurationDays)),
-      declutter_timer_interval_minutes_(kTimerIntervalMinutes),
-      nudge_timer_interval_minutes_(kDefaultNudgeTimerIntervalMinutes),
+    : stale_tab_threshold_duration_(
+          features::kTabstripDeclutterStaleThresholdDuration.Get()),
+      declutter_timer_interval_(
+          features::kTabstripDeclutterTimerInterval.Get()),
+      nudge_timer_interval_(
+          features::kTabstripDeclutterNudgeTimerInterval.Get()),
       declutter_timer_(std::make_unique<base::RepeatingTimer>()),
       usage_tick_clock_(std::make_unique<UsageTickClock>(
           base::DefaultTickClock::GetInstance())),
       next_nudge_valid_time_ticks_(usage_tick_clock_->NowTicks() +
-                                   nudge_timer_interval_minutes_),
+                                   nudge_timer_interval_),
       tab_strip_model_(tab_strip_model) {
   StartDeclutterTimer();
 }
@@ -57,7 +50,7 @@
 
 void TabDeclutterController::StartDeclutterTimer() {
   declutter_timer_->Start(
-      FROM_HERE, declutter_timer_interval_minutes_,
+      FROM_HERE, declutter_timer_interval_,
       base::BindRepeating(&TabDeclutterController::ProcessStaleTabs,
                           base::Unretained(this)));
 }
@@ -72,7 +65,7 @@
 
   if (DeclutterNudgeCriteriaMet(tabs)) {
     next_nudge_valid_time_ticks_ =
-        usage_tick_clock_->NowTicks() + nudge_timer_interval_minutes_;
+        usage_tick_clock_->NowTicks() + nudge_timer_interval_;
 
     for (auto& observer : observers_) {
       observer.OnTriggerDeclutterUIVisibility(!tabs.empty());
@@ -181,9 +174,9 @@
 
 void TabDeclutterController::OnActionUIDismissed(
     base::PassKey<TabSearchContainer>) {
-  nudge_timer_interval_minutes_ = nudge_timer_interval_minutes_ * 2;
+  nudge_timer_interval_ = nudge_timer_interval_ * 2;
   next_nudge_valid_time_ticks_ =
-      usage_tick_clock_->NowTicks() + nudge_timer_interval_minutes_;
+      usage_tick_clock_->NowTicks() + nudge_timer_interval_;
 }
 
 void TabDeclutterController::SetTimerForTesting(
@@ -197,7 +190,7 @@
   usage_tick_clock_.reset();
   usage_tick_clock_ = std::make_unique<UsageTickClock>(tick_clock);
   next_nudge_valid_time_ticks_ =
-      usage_tick_clock_->NowTicks() + nudge_timer_interval_minutes_;
+      usage_tick_clock_->NowTicks() + nudge_timer_interval_;
 }
 
 }  // namespace tabs
diff --git a/chrome/browser/ui/tabs/organization/tab_declutter_controller.h b/chrome/browser/ui/tabs/organization/tab_declutter_controller.h
index 27cc899f..32f75e0 100644
--- a/chrome/browser/ui/tabs/organization/tab_declutter_controller.h
+++ b/chrome/browser/ui/tabs/organization/tab_declutter_controller.h
@@ -44,17 +44,15 @@
     return stale_tab_threshold_duration_;
   }
 
-  base::TimeDelta declutter_timer_interval_minutes() const {
-    return declutter_timer_interval_minutes_;
+  base::TimeDelta declutter_timer_interval() const {
+    return declutter_timer_interval_;
   }
 
   base::TimeTicks next_nudge_valid_time_ticks() const {
     return next_nudge_valid_time_ticks_;
   }
 
-  base::TimeDelta nudge_timer_interval_minutes() const {
-    return nudge_timer_interval_minutes_;
-  }
+  base::TimeDelta nudge_timer_interval() const { return nudge_timer_interval_; }
 
   void OnActionUIDismissed(base::PassKey<TabSearchContainer>);
 
@@ -78,9 +76,9 @@
   // Duration of inactivity after which a tab is considered stale.
   base::TimeDelta stale_tab_threshold_duration_;
   // Interval between a recomputation of stale tabs.
-  base::TimeDelta declutter_timer_interval_minutes_;
+  base::TimeDelta declutter_timer_interval_;
   // Interval after showing a nudge to prevent another nudge from being shown.
-  base::TimeDelta nudge_timer_interval_minutes_;
+  base::TimeDelta nudge_timer_interval_;
   // The timer that is responsible for calculating stale tabs on getting
   // triggered.
   std::unique_ptr<base::RepeatingTimer> declutter_timer_;
diff --git a/chrome/browser/ui/tabs/organization/tab_declutter_controller_browsertest.cc b/chrome/browser/ui/tabs/organization/tab_declutter_controller_browsertest.cc
index b01b281..688ca0e 100644
--- a/chrome/browser/ui/tabs/organization/tab_declutter_controller_browsertest.cc
+++ b/chrome/browser/ui/tabs/organization/tab_declutter_controller_browsertest.cc
@@ -143,7 +143,7 @@
   browser()->tab_strip_model()->AddToNewGroup(std::vector<int>{2});
 
   task_runner->FastForwardBy(
-      tab_declutter_controller()->declutter_timer_interval_minutes());
+      tab_declutter_controller()->declutter_timer_interval());
 
   EXPECT_EQ(fake_observer.stale_tabs_processed_count(), 1);
   EXPECT_EQ(fake_observer.trigger_declutter_ui_visibility_count(), 0);
@@ -174,7 +174,7 @@
 
   // Move forward in time to simulate declutter timer triggering.
   task_runner->FastForwardBy(
-      tab_declutter_controller()->nudge_timer_interval_minutes());
+      tab_declutter_controller()->nudge_timer_interval());
 
   EXPECT_GE(fake_observer.stale_tabs_processed_count(), 1);
   EXPECT_EQ(fake_observer.trigger_declutter_ui_visibility_count(), 1);
@@ -198,14 +198,14 @@
 
   // Move forward in time to simulate declutter timer triggering.
   task_runner->FastForwardBy(
-      tab_declutter_controller()->nudge_timer_interval_minutes());
+      tab_declutter_controller()->nudge_timer_interval());
 
   EXPECT_GE(fake_observer.stale_tabs_processed_count(), 1);
   EXPECT_EQ(fake_observer.trigger_declutter_ui_visibility_count(), 1);
 
   // Move forward in time to simulate declutter timer triggering.
   task_runner->FastForwardBy(
-      tab_declutter_controller()->nudge_timer_interval_minutes());
+      tab_declutter_controller()->nudge_timer_interval());
   EXPECT_EQ(fake_observer.trigger_declutter_ui_visibility_count(), 1);
 }
 
@@ -225,7 +225,7 @@
 
   // Move forward in time to simulate declutter timer triggering.
   task_runner->FastForwardBy(
-      tab_declutter_controller()->nudge_timer_interval_minutes());
+      tab_declutter_controller()->nudge_timer_interval());
 
   EXPECT_GE(fake_observer.stale_tabs_processed_count(), 1);
   EXPECT_EQ(fake_observer.trigger_declutter_ui_visibility_count(), 0);
@@ -249,7 +249,7 @@
 
   // Move forward in time to simulate declutter timer triggering.
   task_runner->FastForwardBy(
-      tab_declutter_controller()->nudge_timer_interval_minutes());
+      tab_declutter_controller()->nudge_timer_interval());
 
   EXPECT_GE(fake_observer.stale_tabs_processed_count(), 1);
   EXPECT_EQ(fake_observer.trigger_declutter_ui_visibility_count(), 0);
@@ -271,7 +271,7 @@
   browser()->window()->Activate();
 
   base::TimeDelta initial_nudge_interval =
-      tab_declutter_controller()->nudge_timer_interval_minutes();
+      tab_declutter_controller()->nudge_timer_interval();
 
   // Move time forward by the initial nudge timer interval and check the next
   // valid nudge time.
@@ -279,7 +279,7 @@
   tab_search_container()->GetWidget()->LayoutRootViewIfNecessary();
 
   EXPECT_EQ(initial_nudge_interval,
-            tab_declutter_controller()->nudge_timer_interval_minutes());
+            tab_declutter_controller()->nudge_timer_interval());
 
   TabSearchContainer* tab_search_container =
       BrowserView::GetBrowserViewForBrowser(browser())
@@ -298,11 +298,11 @@
                      ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON, 0));
 
   // After dismissal, the nudge interval should double.
-  EXPECT_EQ(tab_declutter_controller()->nudge_timer_interval_minutes(),
+  EXPECT_EQ(tab_declutter_controller()->nudge_timer_interval(),
             initial_nudge_interval * 2);
   EXPECT_EQ(tab_declutter_controller()->next_nudge_valid_time_ticks(),
             task_runner->GetMockTickClock()->NowTicks() +
-                tab_declutter_controller()->nudge_timer_interval_minutes());
+                tab_declutter_controller()->nudge_timer_interval());
 }
 
 IN_PROC_BROWSER_TEST_F(TabDeclutterControllerBrowserTest, TestDeclutterTabs) {
@@ -366,7 +366,7 @@
   tab_declutter_controller()->ExcludeFromStaleTabs(tab_to_exclude);
 
   task_runner->FastForwardBy(
-      tab_declutter_controller()->declutter_timer_interval_minutes());
+      tab_declutter_controller()->declutter_timer_interval());
 
   EXPECT_EQ(fake_observer.stale_tabs_processed_count(), 1);
 
diff --git a/chrome/browser/ui/ui_features.cc b/chrome/browser/ui/ui_features.cc
index 2b24106..4d32cec 100644
--- a/chrome/browser/ui/ui_features.cc
+++ b/chrome/browser/ui/ui_features.cc
@@ -6,6 +6,7 @@
 
 #include "base/feature_list.h"
 #include "base/metrics/field_trial_params.h"
+#include "base/time/time.h"
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
 #include "components/flags_ui/feature_entry.h"
@@ -302,6 +303,16 @@
              "TabstripDeclutter",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
+const base::FeatureParam<base::TimeDelta>
+    kTabstripDeclutterStaleThresholdDuration{
+        &kTabstripDeclutter, "stale_threshold_duration", base::Days(7)};
+
+const base::FeatureParam<base::TimeDelta> kTabstripDeclutterTimerInterval{
+    &kTabstripDeclutter, "declutter_timer_interval", base::Minutes(10)};
+
+const base::FeatureParam<base::TimeDelta> kTabstripDeclutterNudgeTimerInterval{
+    &kTabstripDeclutter, "nudge_timer_interval", base::Minutes(6 * 60)};
+
 bool IsTabstripDeclutterEnabled() {
   return base::FeatureList::IsEnabled(features::kTabstripDeclutter);
 }
diff --git a/chrome/browser/ui/ui_features.h b/chrome/browser/ui/ui_features.h
index f03162b..818cb38b 100644
--- a/chrome/browser/ui/ui_features.h
+++ b/chrome/browser/ui/ui_features.h
@@ -214,6 +214,17 @@
 
 BASE_DECLARE_FEATURE(kTabOrganizationEnableNudgeForEnterprise);
 
+// Duration of inactivity after which a tab is considered stale for declutter.
+extern const base::FeatureParam<base::TimeDelta>
+    kTabstripDeclutterStaleThresholdDuration;
+// Interval between a recomputation of stale tabs for declutter.
+extern const base::FeatureParam<base::TimeDelta>
+    kTabstripDeclutterTimerInterval;
+// Default interval after showing a nudge to prevent another nudge from being
+// shown for declutter.
+extern const base::FeatureParam<base::TimeDelta>
+    kTabstripDeclutterNudgeTimerInterval;
+
 // The target (and minimum) interval between proactive nudge triggers. Measured
 // against a clock that only runs while Chrome is in the foreground.
 extern const base::FeatureParam<base::TimeDelta> kTabOrganizationTriggerPeriod;
diff --git a/chrome/browser/ui/views/omnibox/omnibox_match_cell_view.cc b/chrome/browser/ui/views/omnibox/omnibox_match_cell_view.cc
index ffe26c3..5812a75 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_match_cell_view.cc
+++ b/chrome/browser/ui/views/omnibox/omnibox_match_cell_view.cc
@@ -52,8 +52,7 @@
 
 namespace {
 
-// The edge length of the favicon, answer icon, and entity backgrounds if the
-// kUniformRowHeight flag is enabled.
+// The edge length of the favicon, answer icon, and entity backgrounds.
 static constexpr int kUniformRowHeightIconSize = 28;
 
 // The gap between the left|right edge of the IPH background to the left|right
@@ -71,9 +70,7 @@
 
 // The edge length of the entity suggestions images.
 int GetEntityImageSize() {
-  return OmniboxFieldTrial::IsUniformRowHeightEnabled()
-             ? kUniformRowHeightIconSize
-             : 32;
+  return kUniformRowHeightIconSize;
 }
 
 // The radius of the rounded square backgrounds of icons, answers, and entities.
@@ -180,17 +177,15 @@
 // OmniboxMatchCellView:
 
 // static
-void OmniboxMatchCellView::ComputeMatchMaxWidths(
-    int contents_width,
-    int separator_width,
-    int description_width,
-    int iph_link_width,
-    int available_width,
-    bool description_on_separate_line,
-    bool allow_shrinking_contents,
-    int* contents_max_width,
-    int* description_max_width,
-    int* iph_link_max_width) {
+void OmniboxMatchCellView::ComputeMatchMaxWidths(int contents_width,
+                                                 int separator_width,
+                                                 int description_width,
+                                                 int iph_link_width,
+                                                 int available_width,
+                                                 bool allow_shrinking_contents,
+                                                 int* contents_max_width,
+                                                 int* description_max_width,
+                                                 int* iph_link_max_width) {
   available_width = std::max(available_width, 0);
 
   // The IPH link is top priority.
@@ -200,9 +195,8 @@
   *contents_max_width = std::min(contents_width, available_width);
   *description_max_width = std::min(description_width, available_width);
 
-  // If the description is empty, or the contents and description are on
-  // separate lines, each can get the full available width.
-  if (!description_width || description_on_separate_line)
+  // If the description is empty, contents can get the full available width.
+  if (!description_width)
     return;
 
   // If we want to display the description, we need to reserve enough space for
@@ -274,13 +268,16 @@
 
 void OmniboxMatchCellView::OnMatchUpdate(const OmniboxResultView* result_view,
                                          const AutocompleteMatch& match) {
-  is_search_type_ = AutocompleteMatch::IsSearchType(match.type);
-  is_iph_type_ = match.IsIPHSuggestion();
-  has_image_ = ShouldDisplayImage(match);
-  // Decide layout style once before Layout, while match data is available.
-  layout_style_ = has_image_ && !OmniboxFieldTrial::IsUniformRowHeightEnabled()
-                      ? LayoutStyle::TWO_LINE_SUGGESTION
-                      : LayoutStyle::ONE_LINE_SUGGESTION;
+  if (ShouldDisplayImage(match)) {
+    CHECK(AutocompleteMatch::IsSearchType(match.type));
+    layout_style_ = LayoutStyle::SEARCH_SUGGESTION_WITH_IMAGE;
+  } else if (AutocompleteMatch::IsSearchType(match.type)) {
+    layout_style_ = LayoutStyle::SEARCH_SUGGESTION;
+  } else if (match.IsIPHSuggestion()) {
+    layout_style_ = LayoutStyle::IPH_SUGGESTION;
+  } else {
+    layout_style_ = LayoutStyle::DEFAULT_NON_SEARCH_SUGGESTION;
+  }
 
   tail_suggest_ellipse_view_->SetVisible(
       !match.tail_suggest_common_prefix.empty());
@@ -290,18 +287,18 @@
           : kColorOmniboxText);
 
   // Set up the separator.
-  separator_view_->SetSize(layout_style_ == LayoutStyle::TWO_LINE_SUGGESTION ||
-                                   match.description.empty()
+  separator_view_->SetSize(match.description.empty()
                                ? gfx::Size()
                                : separator_view_->GetPreferredSize());
 
   // Set up the IPH link following the main IPH text.
   iph_link_view_->SetText(match.iph_link_text);
-  iph_link_view_->SetVisible(is_iph_type_);
+  iph_link_view_->SetVisible(layout_style_ == LayoutStyle::IPH_SUGGESTION);
 
   // Set up the small icon.
-  icon_view_->SetSize(has_image_ ? gfx::Size()
-                                 : icon_view_->GetPreferredSize());
+  icon_view_->SetSize(layout_style_ == LayoutStyle::SEARCH_SUGGESTION_WITH_IMAGE
+                          ? gfx::Size()
+                          : icon_view_->GetPreferredSize());
 
   // Used for non-weather answer images (e.g. calc answers).
   const auto apply_vector_icon = [=, this](const gfx::VectorIcon& vector_icon) {
@@ -334,10 +331,8 @@
   };
   if (match.type == AutocompleteMatchType::CALCULATOR) {
     apply_vector_icon(omnibox::kAnswerCalculatorIcon);
-    if (OmniboxFieldTrial::IsUniformRowHeightEnabled()) {
-      separator_view_->SetSize(gfx::Size());
-    }
-  } else if (!has_image_) {
+    separator_view_->SetSize(gfx::Size());
+  } else if (layout_style_ != LayoutStyle::SEARCH_SUGGESTION_WITH_IMAGE) {
     answer_image_view_->SetImage(ui::ImageModel());
     answer_image_view_->SetSize(gfx::Size());
   } else {
@@ -456,7 +451,9 @@
   // IPH text bounds should be centered within the IPH background when there's
   // no IPH icon. So make their `right_margin` equal to their text's x position.
   const int right_margin =
-      is_iph_type_ ? OmniboxMatchCellView::kMarginLeft + kIphTextIndent : 7;
+      layout_style_ == LayoutStyle::IPH_SUGGESTION
+          ? OmniboxMatchCellView::kMarginLeft + kIphTextIndent
+          : 7;
   return gfx::Insets::TLBR(vertical_margin, OmniboxMatchCellView::kMarginLeft,
                            vertical_margin, right_margin);
 }
@@ -464,7 +461,6 @@
 void OmniboxMatchCellView::Layout(PassKey) {
   LayoutSuperclass<views::View>(this);
 
-  const bool two_line = layout_style_ == LayoutStyle::TWO_LINE_SUGGESTION;
   const gfx::Rect child_area = GetContentsBounds();
   int x = child_area.x();
   int y = child_area.y();
@@ -473,66 +469,53 @@
 
   int image_x = GetImageIndent();
   views::ImageView* const image_view =
-      has_image_ ? answer_image_view_.get() : icon_view_.get();
+      layout_style_ == LayoutStyle::SEARCH_SUGGESTION_WITH_IMAGE
+          ? answer_image_view_.get()
+          : icon_view_.get();
   image_view->SetBounds(image_x, y, kImageBoundsWidth, row_height);
 
   const int text_indent = GetTextIndent() + tail_suggest_common_prefix_width_;
   x += text_indent;
   const int text_width = child_area.width() - text_indent;
 
-  if (two_line) {
-    if (description_view_->GetText().empty()) {
-      // This vertically centers content in the rare case that no description is
-      // provided.
-      content_view_->SetBounds(x, y, text_width, row_height);
-      description_view_->SetSize(gfx::Size());
-    } else {
-      content_view_->SetBounds(x, y, text_width,
-                               content_view_->GetLineHeight());
-      description_view_->SetBounds(
-          x, content_view_->bounds().bottom(), text_width,
-          description_view_->GetHeightForWidth(text_width));
-    }
-  } else {
-    int content_width = content_view_->GetPreferredSize().width();
-    int description_width = description_view_->GetPreferredSize().width();
-    const gfx::Size separator_size = separator_view_->GetPreferredSize();
-    int iph_link_width = iph_link_view_->GetPreferredSize().width();
-    ComputeMatchMaxWidths(
-        content_width, separator_size.width(), description_width,
-        iph_link_width, /*available_width=*/text_width,
-        /*description_on_separate_line=*/false, !is_search_type_,
-        &content_width, &description_width, &iph_link_width);
-    if (tail_suggest_ellipse_view_->GetVisible()) {
-      const int tail_suggest_ellipse_width =
-          tail_suggest_ellipse_view_->GetPreferredSize().width();
-      tail_suggest_ellipse_view_->SetBounds(x - tail_suggest_ellipse_width, y,
-                                            tail_suggest_ellipse_width,
-                                            row_height);
-    }
-    content_view_->SetBounds(x, y, content_width, row_height);
-    x += content_view_->width();
-    if (description_width) {
-      separator_view_->SetSize(separator_size);
-      separator_view_->SetBounds(x, y, separator_view_->width(), row_height);
-      x += separator_view_->width();
-      description_view_->SetBounds(x, y, description_width, row_height);
-      x += description_view_->width();
-    } else {
-      separator_view_->SetSize(gfx::Size());
-      description_view_->SetSize(gfx::Size());
-    }
-    iph_link_view_->SetBounds(x, y, iph_link_width, row_height);
+  int content_width = content_view_->GetPreferredSize().width();
+  int description_width = description_view_->GetPreferredSize().width();
+  const gfx::Size separator_size = separator_view_->GetPreferredSize();
+  int iph_link_width = iph_link_view_->GetPreferredSize().width();
+  ComputeMatchMaxWidths(
+      content_width, separator_size.width(), description_width, iph_link_width,
+      /*available_width=*/text_width,
+      /*allow_shrinking_contents=*/
+      layout_style_ != LayoutStyle::SEARCH_SUGGESTION &&
+          layout_style_ != LayoutStyle::SEARCH_SUGGESTION_WITH_IMAGE,
+      &content_width, &description_width, &iph_link_width);
+  if (tail_suggest_ellipse_view_->GetVisible()) {
+    const int tail_suggest_ellipse_width =
+        tail_suggest_ellipse_view_->GetPreferredSize().width();
+    tail_suggest_ellipse_view_->SetBounds(x - tail_suggest_ellipse_width, y,
+                                          tail_suggest_ellipse_width,
+                                          row_height);
   }
+  content_view_->SetBounds(x, y, content_width, row_height);
+  x += content_view_->width();
+  if (description_width) {
+    separator_view_->SetSize(separator_size);
+    separator_view_->SetBounds(x, y, separator_view_->width(), row_height);
+    x += separator_view_->width();
+    description_view_->SetBounds(x, y, description_width, row_height);
+    x += description_view_->width();
+  } else {
+    separator_view_->SetSize(gfx::Size());
+    description_view_->SetSize(gfx::Size());
+  }
+  iph_link_view_->SetBounds(x, y, iph_link_width, row_height);
 }
 
 gfx::Size OmniboxMatchCellView::CalculatePreferredSize(
     const views::SizeBounds& available_size) const {
-  int height = GetEntityImageSize() +
-               2 * OmniboxFieldTrial::kRichSuggestionVerticalMargin.Get();
-  if (layout_style_ == LayoutStyle::TWO_LINE_SUGGESTION)
-    height += description_view_->GetHeightForWidth(width() - GetTextIndent());
-  if (is_iph_type_) {
+  const int kMargin = 6;
+  int height = GetEntityImageSize() + 2 * kMargin;
+  if (layout_style_ == LayoutStyle::IPH_SUGGESTION) {
     height += 4;
   }
 
@@ -556,14 +539,14 @@
   // This number is independent of other layout numbers; i.e., it's not meant to
   // align with any other UI; it's just arbitrarily chosen by UX. Hence, it's
   // not derived from other matches' `indent` below.
-  if (is_iph_type_)
+  if (layout_style_ == LayoutStyle::IPH_SUGGESTION)
     return 2;
 
   // The entity, answer, and icon images are horizontally centered within their
   // bounds. So their center-line will be at `image_x+kImageBoundsWidth/2`. This
   // means their left x coordinate will depend on their actual sizes. Their
-  // widths depend on the state of `kSquareSuggestIcons`, its params, and
-  // `kUniformRowHeight`. This code guarantees when cr23_layout is true:
+  // widths depend on the state of `kSquareSuggestIcons` & its params. This code
+  // guarantees when cr23_layout is true:
   // a) Entities' left x coordinate is 16.
   // b) Entities, answers, and icons continue to be center-aligned.
   // c) Regardless of the state of those other features and their widths.
@@ -585,8 +568,10 @@
   // layout numbers; i.e., it's not meant to align with other UI; it's just
   // arbitrarily chosen by UX. Hence, it's not derived from other matches'
   // `indent` below.
-  if (is_iph_type_ && icon_view_->GetPreferredSize() == gfx::Size{})
+  if (layout_style_ == LayoutStyle::IPH_SUGGESTION &&
+      icon_view_->GetPreferredSize() == gfx::Size{}) {
     return kIphTextIndent;
+  }
 
   // For normal matches, the gap between the left edge of this view and the
   // left edge of its favicon or answer image.
@@ -597,7 +582,7 @@
   // to have inner padding, so the gap between the left edge of this
   // `OmniboxMatchCellView` and the IPH icon/text is actually larger than
   // `indent`.
-  if (is_iph_type_)
+  if (layout_style_ == LayoutStyle::IPH_SUGGESTION)
     indent -= kIphOffset;
 
   return indent;
diff --git a/chrome/browser/ui/views/omnibox/omnibox_match_cell_view.h b/chrome/browser/ui/views/omnibox/omnibox_match_cell_view.h
index b9608489..26471b5a 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_match_cell_view.h
+++ b/chrome/browser/ui/views/omnibox/omnibox_match_cell_view.h
@@ -42,11 +42,6 @@
   // Computes the maximum width, in pixels, that can be allocated for the two
   // parts of an autocomplete result, i.e. the contents and the description.
   //
-  // When |description_on_separate_line| is true, the caller will be displaying
-  // two separate lines of text, so both contents and description can take up
-  // the full available width. Otherwise, the contents and description are
-  // assumed to be on the same line, with a separator between them.
-  //
   // When |allow_shrinking_contents| is true, and the contents and description
   // are together on a line without enough space for both, the code tries to
   // divide the available space equally between the two, unless this would make
@@ -57,7 +52,6 @@
                                     int description_width,
                                     int iph_link_width,
                                     int available_width,
-                                    bool description_on_separate_line,
                                     bool allow_shrinking_contents,
                                     int* contents_max_width,
                                     int* description_max_width,
@@ -76,8 +70,6 @@
 
   // Determines if `match` should display an answer, calculator, or entity
   // image.
-  // If #omnibox-uniform-suggestion-height experiment flag is disabled, also
-  // determines whether `match` should be displayed on 1 or 2 lines.
   static bool ShouldDisplayImage(const AutocompleteMatch& match);
 
   void OnMatchUpdate(const OmniboxResultView* result_view,
@@ -105,8 +97,10 @@
 
  private:
   enum class LayoutStyle {
-    ONE_LINE_SUGGESTION,
-    TWO_LINE_SUGGESTION,
+    DEFAULT_NON_SEARCH_SUGGESTION,
+    SEARCH_SUGGESTION,
+    SEARCH_SUGGESTION_WITH_IMAGE,
+    IPH_SUGGESTION,
   };
 
   // How far to indent the icon, entity, or answer image from the left side of
@@ -123,10 +117,7 @@
 
   void SetTailSuggestCommonPrefixWidth(const std::u16string& common_prefix);
 
-  bool is_iph_type_ = false;
-  bool is_search_type_ = false;
-  bool has_image_ = false;
-  LayoutStyle layout_style_ = LayoutStyle::ONE_LINE_SUGGESTION;
+  LayoutStyle layout_style_ = LayoutStyle::DEFAULT_NON_SEARCH_SUGGESTION;
 
   // Weak pointers for easy reference.
   // An icon representing the type or content.
diff --git a/chrome/browser/ui/views/omnibox/omnibox_match_cell_view_unittest.cc b/chrome/browser/ui/views/omnibox/omnibox_match_cell_view_unittest.cc
index 2e4617f..b5662df 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_match_cell_view_unittest.cc
+++ b/chrome/browser/ui/views/omnibox/omnibox_match_cell_view_unittest.cc
@@ -19,7 +19,7 @@
   available_width = 200;
   OmniboxMatchCellView::ComputeMatchMaxWidths(
       contents_width, separator_width, description_width, iph_link_view_width,
-      available_width, false, true, &contents_max_width, &description_max_width,
+      available_width, true, &contents_max_width, &description_max_width,
       &iph_link_max_width);
   EXPECT_EQ(contents_max_width, contents_width);
   EXPECT_EQ(description_max_width, description_width);
@@ -32,7 +32,7 @@
   available_width = 20;
   OmniboxMatchCellView::ComputeMatchMaxWidths(
       contents_width, separator_width, description_width, iph_link_view_width,
-      available_width, false, true, &contents_max_width, &description_max_width,
+      available_width, true, &contents_max_width, &description_max_width,
       &iph_link_max_width);
   EXPECT_EQ(contents_max_width, 0);
   EXPECT_EQ(description_max_width, 0);
@@ -45,26 +45,12 @@
   available_width = 130;
   OmniboxMatchCellView::ComputeMatchMaxWidths(
       contents_width, separator_width, description_width, iph_link_view_width,
-      available_width, false, true, &contents_max_width, &description_max_width,
+      available_width, true, &contents_max_width, &description_max_width,
       &iph_link_max_width);
   EXPECT_EQ(contents_max_width, contents_width);
   EXPECT_EQ(description_max_width, 0);
   EXPECT_EQ(iph_link_max_width, 30);
 
-  // If contents and description are on separate lines, each can take the full
-  // available width.
-  contents_width = 300;
-  description_width = 100;
-  iph_link_view_width = 0;
-  available_width = 384;
-  OmniboxMatchCellView::ComputeMatchMaxWidths(
-      contents_width, separator_width, description_width, iph_link_view_width,
-      available_width, true, true, &contents_max_width, &description_max_width,
-      &iph_link_max_width);
-  EXPECT_EQ(contents_max_width, contents_width);
-  EXPECT_EQ(description_max_width, description_width);
-  EXPECT_EQ(iph_link_max_width, 0);
-
   // Both contents and description will be limited.
   contents_width = 310;
   description_width = 150;
@@ -72,7 +58,7 @@
   available_width = 430;
   OmniboxMatchCellView::ComputeMatchMaxWidths(
       contents_width, separator_width, description_width, iph_link_view_width,
-      available_width, false, true, &contents_max_width, &description_max_width,
+      available_width, true, &contents_max_width, &description_max_width,
       &iph_link_max_width);
   EXPECT_EQ(contents_max_width, kMinimumContentsWidth);
   EXPECT_EQ(description_max_width, available_width - kMinimumContentsWidth -
@@ -86,40 +72,12 @@
   available_width = 230;
   OmniboxMatchCellView::ComputeMatchMaxWidths(
       contents_width, separator_width, description_width, iph_link_view_width,
-      available_width, false, true, &contents_max_width, &description_max_width,
+      available_width, true, &contents_max_width, &description_max_width,
       &iph_link_max_width);
   EXPECT_EQ(contents_max_width, available_width - iph_link_max_width);
   EXPECT_EQ(description_max_width, 0);
   EXPECT_EQ(iph_link_max_width, 30);
 
-  // Large contents will be truncated but small description won't if two line
-  // suggestion.
-  contents_width = 400;
-  description_width = 100;
-  iph_link_view_width = 0;
-  available_width = 200;
-  OmniboxMatchCellView::ComputeMatchMaxWidths(
-      contents_width, separator_width, description_width, iph_link_view_width,
-      available_width, true, true, &contents_max_width, &description_max_width,
-      &iph_link_max_width);
-  EXPECT_EQ(contents_max_width, available_width);
-  EXPECT_EQ(description_max_width, description_width);
-  EXPECT_EQ(iph_link_max_width, 0);
-
-  // Large description will be truncated but small contents won't if two line
-  // suggestion.
-  contents_width = 100;
-  description_width = 400;
-  iph_link_view_width = 0;
-  available_width = 200;
-  OmniboxMatchCellView::ComputeMatchMaxWidths(
-      contents_width, separator_width, description_width, iph_link_view_width,
-      available_width, true, true, &contents_max_width, &description_max_width,
-      &iph_link_max_width);
-  EXPECT_EQ(contents_max_width, contents_width);
-  EXPECT_EQ(description_max_width, available_width);
-  EXPECT_EQ(iph_link_max_width, 0);
-
   // Half and half.
   contents_width = 395;
   description_width = 395;
@@ -127,7 +85,7 @@
   available_width = 700;
   OmniboxMatchCellView::ComputeMatchMaxWidths(
       contents_width, separator_width, description_width, iph_link_view_width,
-      available_width, false, true, &contents_max_width, &description_max_width,
+      available_width, true, &contents_max_width, &description_max_width,
       &iph_link_max_width);
   EXPECT_EQ(contents_max_width, 345);
   EXPECT_EQ(description_max_width, 345);
@@ -141,8 +99,8 @@
   available_width = 700;
   OmniboxMatchCellView::ComputeMatchMaxWidths(
       contents_width, separator_width, description_width, iph_link_view_width,
-      available_width, false, false, &contents_max_width,
-      &description_max_width, &iph_link_max_width);
+      available_width, false, &contents_max_width, &description_max_width,
+      &iph_link_max_width);
   EXPECT_EQ(contents_max_width, contents_width);
   EXPECT_EQ(description_max_width, available_width - contents_width -
                                        separator_width - iph_link_max_width);
@@ -156,7 +114,7 @@
   available_width = 699;
   OmniboxMatchCellView::ComputeMatchMaxWidths(
       contents_width, separator_width, description_width, iph_link_view_width,
-      available_width, false, true, &contents_max_width, &description_max_width,
+      available_width, true, &contents_max_width, &description_max_width,
       &iph_link_max_width);
   EXPECT_EQ(contents_max_width, 345);
   EXPECT_EQ(description_max_width, 344);
@@ -169,7 +127,7 @@
   available_width = 0;
   OmniboxMatchCellView::ComputeMatchMaxWidths(
       contents_width, separator_width, description_width, iph_link_view_width,
-      available_width, false, true, &contents_max_width, &description_max_width,
+      available_width, true, &contents_max_width, &description_max_width,
       &iph_link_max_width);
   EXPECT_EQ(contents_max_width, 0);
   EXPECT_EQ(description_max_width, 0);
diff --git a/chrome/browser/ui/views/omnibox/omnibox_text_view.cc b/chrome/browser/ui/views/omnibox/omnibox_text_view.cc
index 01b7df407..7987471 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_text_view.cc
+++ b/chrome/browser/ui/views/omnibox/omnibox_text_view.cc
@@ -45,10 +45,6 @@
 // entire font.
 static constexpr int kVerticalPadding = 3;
 
-// Dictionary and translation default number of lines for the FormattedString
-// subhead.
-constexpr size_t kDefaultMaxNumLines = 3;
-
 struct TextStyle {
   OmniboxPart part;
 
@@ -230,13 +226,10 @@
   for (const SuggestionAnswer::TextField& text_field : line.text_fields())
     AppendText(text_field, std::u16string());
   if (!line.text_fields().empty()) {
-    const int kMaxDisplayLines =
-        OmniboxFieldTrial::IsUniformRowHeightEnabled() ? 1 : 3;
     const SuggestionAnswer::TextField& first_field = line.text_fields().front();
     if (first_field.has_num_lines() && first_field.num_lines() > 1) {
       render_text_->SetMultiline(true);
-      render_text_->SetMaxLines(
-          std::min(kMaxDisplayLines, first_field.num_lines()));
+      render_text_->SetMaxLines(1);
     }
   }
 
@@ -273,10 +266,8 @@
   render_text_ = CreateRenderText(u"");
   if (formatted_string.fragments_size() > 0 &&
       AnswerHasDefinedMaxLines(answer_type)) {
-    const size_t kMaxDisplayLines =
-        OmniboxFieldTrial::IsUniformRowHeightEnabled() ? 1 : 3;
     render_text_->SetMultiline(true);
-    render_text_->SetMaxLines(std::min(kMaxDisplayLines, kDefaultMaxNumLines));
+    render_text_->SetMaxLines(1);
   }
   AppendTextWithStyling(formatted_string, /*fragment_index=*/0u, answer_type);
 }
diff --git a/chrome/browser/ui/views/webid/account_selection_modal_view.cc b/chrome/browser/ui/views/webid/account_selection_modal_view.cc
index d1f014d..5f729be 100644
--- a/chrome/browser/ui/views/webid/account_selection_modal_view.cc
+++ b/chrome/browser/ui/views/webid/account_selection_modal_view.cc
@@ -377,12 +377,6 @@
   SetLabelProperties(title_label_);
   title_label_->SetFocusBehavior(FocusBehavior::ALWAYS);
 
-  // Add the body.
-  body_label_ = header->AddChildView(std::make_unique<views::Label>(
-      l10n_util::GetStringUTF16(IDS_ACCOUNT_SELECTION_CHOOSE_AN_ACCOUNT),
-      views::style::CONTEXT_DIALOG_BODY_TEXT, views::style::STYLE_BODY_4));
-  SetLabelProperties(body_label_);
-  body_label_->SetFocusBehavior(FocusBehavior::ALWAYS);
   return header;
 }
 
@@ -426,7 +420,7 @@
     bool show_back_button,
     bool is_choose_an_account) {
   DCHECK(!show_back_button);
-  RemoveNonHeaderChildViews();
+  RemoveNonHeaderChildViewsAndUpdateHeaderIfNeeded();
 
   GURL idp_brand_icon_url = idp_list[0]->idp_metadata.brand_icon_url;
   // If `idp_brand_icon_url` is invalid, a globe icon is shown instead.
@@ -554,7 +548,7 @@
 void AccountSelectionModalView::ShowSingleAccountConfirmDialog(
     const content::IdentityRequestAccount& account,
     bool show_back_button) {
-  RemoveNonHeaderChildViews();
+  RemoveNonHeaderChildViewsAndUpdateHeaderIfNeeded();
 
   const content::IdentityProviderData& idp_data = *account.identity_provider;
   GURL idp_brand_icon_url = idp_data.idp_metadata.brand_icon_url;
@@ -634,7 +628,7 @@
 void AccountSelectionModalView::ShowRequestPermissionDialog(
     const content::IdentityRequestAccount& account,
     const content::IdentityProviderData& idp_data) {
-  RemoveNonHeaderChildViews();
+  RemoveNonHeaderChildViewsAndUpdateHeaderIfNeeded();
 
   GURL idp_brand_icon_url = idp_data.idp_metadata.brand_icon_url;
   GURL rp_brand_icon_url = idp_data.client_metadata.brand_icon_url;
@@ -828,7 +822,8 @@
   return title_label_;
 }
 
-void AccountSelectionModalView::RemoveNonHeaderChildViews() {
+void AccountSelectionModalView::
+    RemoveNonHeaderChildViewsAndUpdateHeaderIfNeeded() {
   // If removing progress bar, adjust the header margins so the rest of the
   // dialog doesn't get shifted when the progress bar is removed.
   if (has_progress_bar_) {
@@ -840,6 +835,16 @@
     has_progress_bar_ = false;
   }
 
+  // body_label_ does not apply to the loading modal so it's added to header
+  // here.
+  if (!body_label_) {
+    body_label_ = header_view_->AddChildView(std::make_unique<views::Label>(
+        l10n_util::GetStringUTF16(IDS_ACCOUNT_SELECTION_CHOOSE_AN_ACCOUNT),
+        views::style::CONTEXT_DIALOG_BODY_TEXT, views::style::STYLE_BODY_4));
+    SetLabelProperties(body_label_);
+    body_label_->SetFocusBehavior(FocusBehavior::ALWAYS);
+  }
+
   // Make sure not to keep dangling pointers around first. We do not reset
   // `header_view_`, `title_label_`, `body_label_` and `brand_icon_` because
   // this method does not remove the header.
diff --git a/chrome/browser/ui/views/webid/account_selection_modal_view.h b/chrome/browser/ui/views/webid/account_selection_modal_view.h
index 2c98cd7..66a1df0 100644
--- a/chrome/browser/ui/views/webid/account_selection_modal_view.h
+++ b/chrome/browser/ui/views/webid/account_selection_modal_view.h
@@ -138,8 +138,9 @@
   // Adds a progress bar at the top of the modal dialog.
   void AddProgressBar();
 
-  // Removes all child views and dangling pointers.
-  void RemoveNonHeaderChildViews();
+  // Removes all child views and dangling pointers and adjust header with
+  // progress bar and body label if needed.
+  void RemoveNonHeaderChildViewsAndUpdateHeaderIfNeeded();
 
   // Removes `combined_icons_` and all its child views, if available.
   void MaybeRemoveCombinedIconsView();
diff --git a/chrome/browser/ui/views/webid/account_selection_modal_view_browsertest.cc b/chrome/browser/ui/views/webid/account_selection_modal_view_browsertest.cc
index b157678c..317ed1f 100644
--- a/chrome/browser/ui/views/webid/account_selection_modal_view_browsertest.cc
+++ b/chrome/browser/ui/views/webid/account_selection_modal_view_browsertest.cc
@@ -123,8 +123,13 @@
     EXPECT_FALSE(dialog()->GetOkButton());
     EXPECT_FALSE(dialog()->GetCancelButton());
 
-    // Order: Brand icon, title, body
-    std::vector<std::string> expected_class_names = {"View", "Label", "Label"};
+    // Order: Brand icon, title and body for non loading UI.
+    std::vector<std::string> expected_class_names = {"View", "Label"};
+    bool is_loading_dialog =
+        !expect_visible_idp_icon && !expect_visible_combined_icons;
+    if (!is_loading_dialog) {
+      expected_class_names.push_back("Label");
+    }
     EXPECT_THAT(GetChildClassNames(header),
                 testing::ElementsAreArray(expected_class_names));
 
@@ -204,11 +209,13 @@
       EXPECT_EQ(dialog()->GetInitiallyFocusedView(), title_view);
     }
 
-    // Check body text.
-    views::Label* body_view = static_cast<views::Label*>(header_children[2]);
-    ASSERT_TRUE(body_view);
-    EXPECT_EQ(body_view->GetText(), kBodySignIn);
-    EXPECT_EQ(body_view->GetVisible(), expect_visible_body_label_);
+    if (!is_loading_dialog) {
+      // Check body text.
+      views::Label* body_view = static_cast<views::Label*>(header_children[2]);
+      ASSERT_TRUE(body_view);
+      EXPECT_EQ(body_view->GetText(), kBodySignIn);
+      EXPECT_EQ(body_view->GetVisible(), expect_visible_body_label_);
+    }
 
     // After the first header check, the consecutive header checks do not
     // necessarily have to focus on the title.
diff --git a/chrome/browser/ui/webui/ash/settings/integration_tests/os_settings_auto_screen_lock_browsertest.cc b/chrome/browser/ui/webui/ash/settings/integration_tests/os_settings_auto_screen_lock_browsertest.cc
index 25317c1..b19a8c7 100644
--- a/chrome/browser/ui/webui/ash/settings/integration_tests/os_settings_auto_screen_lock_browsertest.cc
+++ b/chrome/browser/ui/webui/ash/settings/integration_tests/os_settings_auto_screen_lock_browsertest.cc
@@ -7,6 +7,7 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/webui/ash/settings/test_support/os_settings_lock_screen_browser_test_base.h"
 #include "chrome/test/data/webui/chromeos/settings/test_api.test-mojom-test-utils.h"
+#include "chromeos/ash/components/osauth/public/common_types.h"
 #include "components/prefs/pref_service.h"
 #include "content/public/test/browser_test.h"
 
@@ -20,6 +21,9 @@
 class OSSettingsAutoScreenLockTest
     : public OSSettingsLockScreenBrowserTestBase {
  public:
+  OSSettingsAutoScreenLockTest()
+      : OSSettingsLockScreenBrowserTestBase(ash::AshAuthFactor::kGaiaPassword) {
+  }
   // Returns whether or not automatic screen lock is enabled according to
   // preferences.
   bool IsAutoScreenLockPrefEnabled() {
diff --git a/chrome/browser/ui/webui/ash/settings/integration_tests/os_settings_lock_screen_authentication_browsertest.cc b/chrome/browser/ui/webui/ash/settings/integration_tests/os_settings_lock_screen_authentication_browsertest.cc
index a5f6e8d1..9a1a33f8 100644
--- a/chrome/browser/ui/webui/ash/settings/integration_tests/os_settings_lock_screen_authentication_browsertest.cc
+++ b/chrome/browser/ui/webui/ash/settings/integration_tests/os_settings_lock_screen_authentication_browsertest.cc
@@ -63,7 +63,7 @@
     ASSERT_FALSE(cryptohome_.IsAuthenticated(GetAccountId()));
     lock_screen_settings.AssertAuthenticated(false);
 
-    AuthenticateUsingPassword();
+    Authenticate();
 
     ASSERT_TRUE(cryptohome_.IsAuthenticated(GetAccountId()));
     lock_screen_settings.AssertAuthenticated(true);
@@ -93,11 +93,11 @@
         cryptohome::ErrorWrapper::CreateFromErrorCodeOnly(
             user_data_auth::CRYPTOHOME_ERROR_AUTHORIZATION_KEY_FAILED));
 
-    AuthenticateUsingPassword();
+    Authenticate();
     ASSERT_FALSE(cryptohome_.IsAuthenticated(GetAccountId()));
     lock_screen_settings.AssertAuthenticated(false);
 
-    AuthenticateUsingPassword();
+    Authenticate();
 
     ASSERT_TRUE(cryptohome_.IsAuthenticated(GetAccountId()));
     lock_screen_settings.AssertAuthenticated(true);
diff --git a/chrome/browser/ui/webui/ash/settings/integration_tests/os_settings_notification_settings_browsertest.cc b/chrome/browser/ui/webui/ash/settings/integration_tests/os_settings_notification_settings_browsertest.cc
index a1be475..e096e25c 100644
--- a/chrome/browser/ui/webui/ash/settings/integration_tests/os_settings_notification_settings_browsertest.cc
+++ b/chrome/browser/ui/webui/ash/settings/integration_tests/os_settings_notification_settings_browsertest.cc
@@ -11,6 +11,7 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/webui/ash/settings/test_support/os_settings_lock_screen_browser_test_base.h"
 #include "chrome/test/data/webui/chromeos/settings/test_api.test-mojom-test-utils.h"
+#include "chromeos/ash/components/osauth/public/common_types.h"
 #include "components/prefs/pref_service.h"
 #include "content/public/test/browser_test.h"
 
@@ -21,7 +22,8 @@
 class OSSettingsNotificationSettingsTest
     : public OSSettingsLockScreenBrowserTestBase {
  public:
-  OSSettingsNotificationSettingsTest() {
+  OSSettingsNotificationSettingsTest()
+      : OSSettingsLockScreenBrowserTestBase(ash::AshAuthFactor::kGaiaPassword) {
     feature_list_.InitAndEnableFeature(ash::features::kLockScreenNotifications);
   }
 
diff --git a/chrome/browser/ui/webui/ash/settings/integration_tests/os_settings_password_setup_browsertest.cc b/chrome/browser/ui/webui/ash/settings/integration_tests/os_settings_password_setup_browsertest.cc
index c047fe08..d14c45a3 100644
--- a/chrome/browser/ui/webui/ash/settings/integration_tests/os_settings_password_setup_browsertest.cc
+++ b/chrome/browser/ui/webui/ash/settings/integration_tests/os_settings_password_setup_browsertest.cc
@@ -12,11 +12,14 @@
 
 namespace ash::settings {
 
-class OSSettingsPasswordSetupTest : public OSSettingsLockScreenBrowserTestBase {
+class OSSettingsAuthFactorSetupTest
+    : public OSSettingsLockScreenBrowserTestBase {
   using OSSettingsLockScreenBrowserTestBase::
       OSSettingsLockScreenBrowserTestBase;
 
  public:
+  explicit OSSettingsAuthFactorSetupTest(ash::AshAuthFactor type)
+      : OSSettingsLockScreenBrowserTestBase(type) {}
   mojom::PasswordSettingsApiAsyncWaiter GoToPasswordSettings(
       mojom::LockScreenSettingsAsyncWaiter& lock_screen_settings) {
     password_settings_remote_ =
@@ -29,12 +32,12 @@
   mojo::Remote<mojom::PasswordSettingsApi> password_settings_remote_;
 };
 
-class OSSettingsPasswordSetupTestWithGaiaPassword
-    : public OSSettingsPasswordSetupTest,
+class OSSettingsAuthFactorSetupTestWithGaiaPassword
+    : public OSSettingsAuthFactorSetupTest,
       public testing::WithParamInterface<bool> {
  public:
-  OSSettingsPasswordSetupTestWithGaiaPassword()
-      : OSSettingsPasswordSetupTest(ash::AshAuthFactor::kGaiaPassword) {
+  OSSettingsAuthFactorSetupTestWithGaiaPassword()
+      : OSSettingsAuthFactorSetupTest(ash::AshAuthFactor::kGaiaPassword) {
     std::vector<base::test::FeatureRef> enabled_features;
     std::vector<base::test::FeatureRef> disabled_features;
     if (GetParam()) {
@@ -50,20 +53,20 @@
 };
 
 INSTANTIATE_TEST_SUITE_P(All,
-                         OSSettingsPasswordSetupTestWithGaiaPassword,
+                         OSSettingsAuthFactorSetupTestWithGaiaPassword,
                          testing::Bool());
 
-class OSSettingsPasswordSetupTestWithLocalPassword
-    : public OSSettingsPasswordSetupTest {
+class OSSettingsAuthFactorSetupTestWithLocalPassword
+    : public OSSettingsAuthFactorSetupTest {
  public:
-  OSSettingsPasswordSetupTestWithLocalPassword()
-      : OSSettingsPasswordSetupTest(ash::AshAuthFactor::kLocalPassword) {}
+  OSSettingsAuthFactorSetupTestWithLocalPassword()
+      : OSSettingsAuthFactorSetupTest(ash::AshAuthFactor::kLocalPassword) {}
 };
 
 // If user has Gaia password, the control for changing passwords is shown if
 // `kChangePasswordFactorSetup` feature is enabled; otherwise, it should not be
 // shown.
-IN_PROC_BROWSER_TEST_P(OSSettingsPasswordSetupTestWithGaiaPassword,
+IN_PROC_BROWSER_TEST_P(OSSettingsAuthFactorSetupTestWithGaiaPassword,
                        Visibility) {
   mojom::LockScreenSettingsAsyncWaiter lock_screen_settings =
       OpenLockScreenSettingsAndAuthenticate();
@@ -80,10 +83,38 @@
 }
 
 // The control for changing passwords is shown if user has local password.
-IN_PROC_BROWSER_TEST_F(OSSettingsPasswordSetupTestWithLocalPassword, Shown) {
+IN_PROC_BROWSER_TEST_F(OSSettingsAuthFactorSetupTestWithLocalPassword, Shown) {
   mojom::LockScreenSettingsAsyncWaiter lock_screen_settings =
       OpenLockScreenSettingsAndAuthenticate();
   lock_screen_settings.AssertPasswordControlVisibility(true);
 }
 
+class OSSettingsAuthFactorSetupTestWithPinOnly
+    : public OSSettingsAuthFactorSetupTest {
+ public:
+  OSSettingsAuthFactorSetupTestWithPinOnly()
+      : OSSettingsAuthFactorSetupTest(ash::AshAuthFactor::kCryptohomePin) {
+    cryptohome_->set_supports_low_entropy_credentials(true);
+    cryptohome_->set_enable_auth_check(false);
+
+    scoped_feature_list_.InitAndEnableFeature(
+        features::kChangePasswordFactorSetup);
+  }
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+// The control for setting passwords is shown if user has pin only setup.
+IN_PROC_BROWSER_TEST_F(OSSettingsAuthFactorSetupTestWithPinOnly, Shown) {
+  mojom::LockScreenSettingsAsyncWaiter lock_screen_settings =
+      OpenLockScreenSettingsAndAuthenticate();
+  lock_screen_settings.AssertPasswordControlVisibility(true);
+  mojom::PasswordSettingsApiAsyncWaiter password_settings =
+      GoToPasswordSettings(lock_screen_settings);
+  password_settings.AssertCanOpenLocalPasswordDialog();
+  password_settings.AssertSubmitButtonDisabledForInvalidPasswordInput();
+  password_settings.AssertSubmitButtonEnabledForValidPasswordInput();
+}
+
 }  // namespace ash::settings
diff --git a/chrome/browser/ui/webui/ash/settings/integration_tests/os_settings_pin_setup_browsertest.cc b/chrome/browser/ui/webui/ash/settings/integration_tests/os_settings_pin_setup_browsertest.cc
index 58b08a9..0751f5a 100644
--- a/chrome/browser/ui/webui/ash/settings/integration_tests/os_settings_pin_setup_browsertest.cc
+++ b/chrome/browser/ui/webui/ash/settings/integration_tests/os_settings_pin_setup_browsertest.cc
@@ -22,6 +22,7 @@
 #include "chromeos/ash/components/osauth/impl/auth_surface_registry.h"
 #include "chromeos/ash/components/osauth/public/auth_engine_api.h"
 #include "chromeos/ash/components/osauth/public/auth_parts.h"
+#include "chromeos/ash/components/osauth/public/common_types.h"
 #include "components/policy/core/browser/browser_policy_connector.h"
 #include "components/policy/core/common/mock_configuration_policy_provider.h"
 #include "components/policy/core/common/policy_map.h"
@@ -69,7 +70,9 @@
 class OSSettingsPinSetupTest : public OSSettingsLockScreenBrowserTestBase,
                                public testing::WithParamInterface<PinType> {
  public:
-  OSSettingsPinSetupTest() : pin_type_(GetParam()) {
+  OSSettingsPinSetupTest()
+      : OSSettingsLockScreenBrowserTestBase(ash::AshAuthFactor::kGaiaPassword),
+        pin_type_(GetParam()) {
     switch (pin_type_) {
       case PinType::kPrefs:
         cryptohome_->set_supports_low_entropy_credentials(false);
@@ -507,7 +510,7 @@
   auto go_to_lock_screen_settings_and_authenticate = [&]() {
     if (ash::features::IsUseAuthPanelInSessionEnabled()) {
       OpenLockScreenSettings();
-      AuthenticateUsingPassword();
+      Authenticate();
       return mojom::LockScreenSettingsAsyncWaiter{
           lock_screen_settings_remote_.get()};
     } else {
@@ -536,7 +539,7 @@
 
     base::RunLoop().RunUntilIdle();
 
-    AuthenticateUsingPassword();
+    Authenticate();
 
     base::RunLoop().RunUntilIdle();
 
diff --git a/chrome/browser/ui/webui/ash/settings/integration_tests/os_settings_recovery_browsertest.cc b/chrome/browser/ui/webui/ash/settings/integration_tests/os_settings_recovery_browsertest.cc
index 7d17810..6f72a97d 100644
--- a/chrome/browser/ui/webui/ash/settings/integration_tests/os_settings_recovery_browsertest.cc
+++ b/chrome/browser/ui/webui/ash/settings/integration_tests/os_settings_recovery_browsertest.cc
@@ -8,6 +8,7 @@
 #include "base/test/scoped_feature_list.h"
 #include "chrome/browser/ui/webui/ash/settings/test_support/os_settings_lock_screen_browser_test_base.h"
 #include "chrome/test/data/webui/chromeos/settings/test_api.test-mojom-test-utils.h"
+#include "chromeos/ash/components/osauth/public/common_types.h"
 #include "components/policy/core/browser/browser_policy_connector.h"
 #include "components/policy/core/common/mock_configuration_policy_provider.h"
 #include "components/policy/core/common/policy_map.h"
@@ -22,7 +23,12 @@
 
 namespace ash::settings {
 
-class OSSettingsRecoveryTest : public OSSettingsLockScreenBrowserTestBase {};
+class OSSettingsRecoveryTest : public OSSettingsLockScreenBrowserTestBase {
+ public:
+  OSSettingsRecoveryTest()
+      : OSSettingsLockScreenBrowserTestBase(ash::AshAuthFactor::kGaiaPassword) {
+  }
+};
 
 // A test fixture that runs tests with recovery feature enabled but without
 // hardware support.
diff --git a/chrome/browser/ui/webui/ash/settings/pages/people/people_section.cc b/chrome/browser/ui/webui/ash/settings/pages/people/people_section.cc
index 625f096..8d074ac 100644
--- a/chrome/browser/ui/webui/ash/settings/pages/people/people_section.cc
+++ b/chrome/browser/ui/webui/ash/settings/pages/people/people_section.cc
@@ -226,7 +226,7 @@
 }
 
 void AddLockScreenPageStrings(content::WebUIDataSource* html_source,
-                              PrefService* pref_service) {
+                              Profile* profile) {
   static constexpr webui::LocalizedString kLocalizedStrings[] = {
       {"lockScreenNotificationTitle",
        IDS_ASH_SETTINGS_LOCK_SCREEN_NOTIFICATION_TITLE},
@@ -266,8 +266,6 @@
        IDS_SETTINGS_PEOPLE_LOCK_SCREEN_REMOVE_PIN_BUTTON},
       {"lockScreenPasswordLabel",
        IDS_SETTINGS_PEOPLE_LOCK_SCREEN_PASSWORD_LABEL},
-      {"lockScreenPasswordDescription",
-       IDS_SETTINGS_PEOPLE_LOCK_SCREEN_PASSWORD_DESCRIPTION},
       {"lockScreenPinLabel", IDS_SETTINGS_PEOPLE_LOCK_SCREEN_PIN_LABEL},
       {"lockScreenSetupPasswordButton",
        IDS_SETTINGS_PEOPLE_LOCK_SCREEN_SETUP_PASSWORD_BUTTON},
@@ -314,9 +312,10 @@
   html_source->AddLocalizedStrings(kLocalizedStrings);
 
   html_source->AddBoolean("quickUnlockEnabled", quick_unlock::IsPinEnabled());
-  html_source->AddBoolean("quickUnlockDisabledByPolicy",
-                          quick_unlock::IsPinDisabledByPolicy(
-                              pref_service, quick_unlock::Purpose::kAny));
+  html_source->AddBoolean(
+      "quickUnlockDisabledByPolicy",
+      quick_unlock::IsPinDisabledByPolicy(profile->GetPrefs(),
+                                          quick_unlock::Purpose::kAny));
   html_source->AddBoolean("lockScreenNotificationsEnabled",
                           ash::features::IsLockScreenNotificationsEnabled());
   html_source->AddBoolean(
@@ -330,6 +329,11 @@
       l10n_util::GetStringFUTF16(
           IDS_SETTINGS_PEOPLE_LOCK_SCREEN_SWITCH_LOCAL_PASSWORD_DESCRIPTION,
           ui::GetChromeOSDeviceName()));
+  html_source->AddString(
+      "lockScreenSwitchSetLocalPasswordDescription",
+      l10n_util::GetStringFUTF16(
+          IDS_SETTINGS_PEOPLE_LOCK_SCREEN_PASSWORD_DESCRIPTION,
+          ui::GetChromeOSDeviceName()));
   html_source->AddString("lockScreenFingerprintNotice",
                          l10n_util::GetStringFUTF16(
                              IDS_SETTINGS_PEOPLE_LOCK_SCREEN_FINGERPRINT_NOTICE,
@@ -570,7 +574,7 @@
       base::FeatureList::IsEnabled(omnibox::kDocumentProvider));
 
   AddAccountManagerPageStrings(html_source, profile());
-  AddLockScreenPageStrings(html_source, profile()->GetPrefs());
+  AddLockScreenPageStrings(html_source, profile());
   AddFingerprintListStrings(html_source);
   AddFingerprintResources(html_source, AreFingerprintSettingsAllowed());
   AddSetupFingerprintDialogStrings(html_source);
diff --git a/chrome/browser/ui/webui/ash/settings/test_support/os_settings_lock_screen_browser_test_base.cc b/chrome/browser/ui/webui/ash/settings/test_support/os_settings_lock_screen_browser_test_base.cc
index 360e940..4e31f27 100644
--- a/chrome/browser/ui/webui/ash/settings/test_support/os_settings_lock_screen_browser_test_base.cc
+++ b/chrome/browser/ui/webui/ash/settings/test_support/os_settings_lock_screen_browser_test_base.cc
@@ -11,6 +11,7 @@
 #include "ash/public/cpp/in_session_auth_dialog_controller.h"
 #include "ash/shell.h"
 #include "base/check_op.h"
+#include "base/notreached.h"
 #include "base/test/test_future.h"
 #include "chrome/browser/ash/login/test/logged_in_user_mixin.h"
 #include "chrome/browser/ash/login/test/user_auth_config.h"
@@ -24,7 +25,8 @@
 namespace ash::settings {
 
 OSSettingsLockScreenBrowserTestBase::OSSettingsLockScreenBrowserTestBase(
-    ash::AshAuthFactor password_type) {
+    ash::AshAuthFactor auth_factor_type)
+    : auth_factor_type_(auth_factor_type) {
   // We configure FakeUserDataAuthClient (via `cryptohome_`) here and not
   // later because the global PinBackend object reads whether or not
   // cryptohome PINs are supported on startup. If we set up
@@ -32,11 +34,16 @@
   // determine whether PINs are supported before we have configured
   // FakeUserDataAuthClient.
   test::UserAuthConfig config;
-  if (password_type == ash::AshAuthFactor::kGaiaPassword) {
+  if (auth_factor_type_ == ash::AshAuthFactor::kGaiaPassword) {
     config.WithOnlinePassword(kPassword);
-  } else {
-    CHECK_EQ(password_type, ash::AshAuthFactor::kLocalPassword);
+  } else if (auth_factor_type_ == ash::AshAuthFactor::kLocalPassword) {
+    CHECK_EQ(auth_factor_type_, ash::AshAuthFactor::kLocalPassword);
     config.WithLocalPassword(kPassword);
+  } else if (auth_factor_type_ == ash::AshAuthFactor::kCryptohomePin) {
+    CHECK_EQ(auth_factor_type_, ash::AshAuthFactor::kCryptohomePin);
+    config.WithCryptohomePin(kPin, kPinStubSalt);
+  } else {
+    NOTREACHED();
   }
 
   logged_in_user_mixin_ = std::make_unique<LoggedInUserMixin>(
@@ -85,6 +92,20 @@
       lock_screen_settings_remote_.get());
 }
 
+void OSSettingsLockScreenBrowserTestBase::Authenticate() {
+  switch (auth_factor_type_) {
+    case ash::AshAuthFactor::kCryptohomePin:
+      AuthenticateUsingPin();
+      break;
+    case ash::AshAuthFactor::kGaiaPassword:
+    case ash::AshAuthFactor::kLocalPassword:
+      AuthenticateUsingPassword();
+      break;
+    default:
+      NOTREACHED();
+  }
+}
+
 void OSSettingsLockScreenBrowserTestBase::AuthenticateUsingPassword() {
   auto* controller = static_cast<ActiveSessionAuthControllerImpl*>(
       Shell::Get()->active_session_auth_controller());
@@ -95,11 +116,20 @@
   base::RunLoop().RunUntilIdle();
 }
 
+void OSSettingsLockScreenBrowserTestBase::AuthenticateUsingPin() {
+  auto* controller = static_cast<ActiveSessionAuthControllerImpl*>(
+      Shell::Get()->active_session_auth_controller());
+
+  ActiveSessionAuthControllerImpl::TestApi(controller).SubmitPin(kPin);
+
+  base::RunLoop().RunUntilIdle();
+}
+
 mojom::LockScreenSettingsAsyncWaiter
 OSSettingsLockScreenBrowserTestBase::OpenLockScreenSettingsAndAuthenticate() {
   if (ash::features::IsUseAuthPanelInSessionEnabled()) {
     OpenLockScreenSettings();
-    AuthenticateUsingPassword();
+    Authenticate();
   } else {
     OpenLockScreenSettings().Authenticate(kPassword);
   }
@@ -131,7 +161,7 @@
 
     base::RunLoop().RunUntilIdle();
 
-    AuthenticateUsingPassword();
+    Authenticate();
 
     lock_screen_settings_remote_ =
         mojo::Remote(os_settings_driver.AssertOnLockScreenSettings());
diff --git a/chrome/browser/ui/webui/ash/settings/test_support/os_settings_lock_screen_browser_test_base.h b/chrome/browser/ui/webui/ash/settings/test_support/os_settings_lock_screen_browser_test_base.h
index 413dc05..853c5035 100644
--- a/chrome/browser/ui/webui/ash/settings/test_support/os_settings_lock_screen_browser_test_base.h
+++ b/chrome/browser/ui/webui/ash/settings/test_support/os_settings_lock_screen_browser_test_base.h
@@ -29,9 +29,11 @@
  public:
   // The password of the user that is set up by this fixture.
   static constexpr char kPassword[] = "the-password";
+  static constexpr char kPin[] = "596789";
+  static constexpr char kPinStubSalt[] = "pin-salt";
 
   explicit OSSettingsLockScreenBrowserTestBase(
-      ash::AshAuthFactor password_type = ash::AshAuthFactor::kGaiaPassword);
+      ash::AshAuthFactor auth_factor_type);
   ~OSSettingsLockScreenBrowserTestBase() override;
 
   void SetUpOnMainThread() override;
@@ -53,7 +55,7 @@
   // The account ID of the user set up by this fixture.
   const AccountId& GetAccountId();
 
-  void AuthenticateUsingPassword();
+  void Authenticate();
 
  protected:
   mojo::Remote<mojom::LockScreenSettings> lock_screen_settings_remote_;
@@ -62,12 +64,16 @@
   OSSettingsBrowserTestMixin os_settings_mixin_{&mixin_host_};
 
  private:
+  void AuthenticateUsingPassword();
+  void AuthenticateUsingPin();
+
   // Opens the os settings page and saves the test api remote in
   // `os_settings_driver_remote_`. Returns an async waiter to the remote.
   mojom::OSSettingsDriverAsyncWaiter OpenOSSettings(
       const std::string& relative_url = "");
 
   mojo::Remote<mojom::OSSettingsDriver> os_settings_driver_remote_;
+  ash::AshAuthFactor auth_factor_type_;
 };
 
 }  // namespace ash::settings
diff --git a/chrome/browser/ui/webui/settings/settings_ui.cc b/chrome/browser/ui/webui/settings/settings_ui.cc
index 247899cf..44a7f8f6 100644
--- a/chrome/browser/ui/webui/settings/settings_ui.cc
+++ b/chrome/browser/ui/webui/settings/settings_ui.cc
@@ -94,6 +94,7 @@
 #include "components/compose/core/browser/compose_features.h"
 #include "components/content_settings/core/common/features.h"
 #include "components/favicon_base/favicon_url_parser.h"
+#include "components/optimization_guide/core/optimization_guide_features.h"
 #include "components/password_manager/core/common/password_manager_features.h"
 #include "components/performance_manager/public/features.h"
 #include "components/permissions/features.h"
@@ -613,7 +614,8 @@
 
   html_source->AddBoolean(
       "enableAiSettingsPageRefresh",
-      base::FeatureList::IsEnabled(features::kAiSettingsPageRefresh));
+      base::FeatureList::IsEnabled(
+          optimization_guide::features::kAiSettingsPageRefresh));
 
   TryShowHatsSurveyWithTimeout();
 }
diff --git a/chrome/browser/ui/webui/tab_search/tab_search_page_handler_unittest.cc b/chrome/browser/ui/webui/tab_search/tab_search_page_handler_unittest.cc
index 97b00a5..c0c21a9 100644
--- a/chrome/browser/ui/webui/tab_search/tab_search_page_handler_unittest.cc
+++ b/chrome/browser/ui/webui/tab_search/tab_search_page_handler_unittest.cc
@@ -1015,7 +1015,7 @@
   EXPECT_CALL(page_, StaleTabsChanged(_)).Times(2);
 
   task_runner->FastForwardBy(
-      tab_declutter_controller()->declutter_timer_interval_minutes());
+      tab_declutter_controller()->declutter_timer_interval());
 }
 
 }  // namespace
diff --git a/chrome/build/mac-arm.pgo.txt b/chrome/build/mac-arm.pgo.txt
index 403d0241..d2d97c7 100644
--- a/chrome/build/mac-arm.pgo.txt
+++ b/chrome/build/mac-arm.pgo.txt
@@ -1 +1 @@
-chrome-mac-arm-main-1727719133-c86874a65e8e649e79a45b9186e24165b99bfba2-60a72b0afdb415164c8f72cb0cada4317e4464a1.profdata
+chrome-mac-arm-main-1727733539-f457fe1dda981e556023d8b9412d2bf61ac4f849-50c05f8ec43041d14a15892f6311f46743492a80.profdata
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt
index 6bf43ab..674d007 100644
--- a/chrome/build/mac.pgo.txt
+++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@
-chrome-mac-main-1727697472-c47b3850e96cd549ed93cbabebcd798e502f4e4a-306b38a11005d8104469667f442d189b81d82954.profdata
+chrome-mac-main-1727719133-e3a75110588cfd2ab65ac70578fe9ca4cc4d0a7a-60a72b0afdb415164c8f72cb0cada4317e4464a1.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index a7a9eca..b4bd611 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-main-1727697472-7bc3c617fa7075ed50b3d1790e7e338f94fcee0f-306b38a11005d8104469667f442d189b81d82954.profdata
+chrome-win64-main-1727719133-0cdb68a1bd1c984e95927f92045fa79201703397-60a72b0afdb415164c8f72cb0cada4317e4464a1.profdata
diff --git a/chrome/common/chrome_features.cc b/chrome/common/chrome_features.cc
index 6a3c8c6..74834ccd 100644
--- a/chrome/common/chrome_features.cc
+++ b/chrome/common/chrome_features.cc
@@ -32,13 +32,6 @@
              base::FEATURE_ENABLED_BY_DEFAULT);
 #endif
 
-#if !BUILDFLAG(IS_ANDROID)
-// Enable revamp of AI settings page.
-BASE_FEATURE(kAiSettingsPageRefresh,
-             "AiSettingsPageRefresh",
-             base::FEATURE_DISABLED_BY_DEFAULT);
-#endif
-
 #if BUILDFLAG(IS_CHROMEOS_ASH)
 BASE_FEATURE(kAppPreloadService,
              "AppPreloadService",
diff --git a/chrome/common/chrome_features.h b/chrome/common/chrome_features.h
index daad7cf..83365ad 100644
--- a/chrome/common/chrome_features.h
+++ b/chrome/common/chrome_features.h
@@ -38,11 +38,6 @@
 BASE_DECLARE_FEATURE(kAdaptiveScreenBrightnessLogging);
 #endif
 
-#if !BUILDFLAG(IS_ANDROID)
-COMPONENT_EXPORT(CHROME_FEATURES)
-BASE_DECLARE_FEATURE(kAiSettingsPageRefresh);
-#endif
-
 #if BUILDFLAG(IS_CHROMEOS_ASH)
 COMPONENT_EXPORT(CHROME_FEATURES) BASE_DECLARE_FEATURE(kAppPreloadService);
 #endif
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 9f66d724..44ace0c 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -648,14 +648,6 @@
     ]
   }
 
-  if (is_chromeos_ash && also_build_lacros_chrome) {
-    data_deps += [
-      # TODO(crbug.com/40281506): Try to drop building ordinary Lacros binary.
-      "//chrome:chrome(//build/toolchain/linux:lacros_clang_x64)",
-      "//chrome/test:test_lacros_chrome(//build/toolchain/linux:lacros_clang_x64)",
-    ]
-  }
-
   if (is_linux) {
     data_deps += [ "//components/crash/core/app:chrome_crashpad_handler" ]
   }
@@ -5539,7 +5531,6 @@
       "//chrome/browser/apps/link_capturing:app_service_browser_tests",
       "//chrome/browser/apps/link_capturing:features",
       "//chrome/browser/apps/link_capturing:test_support",
-      "//chrome/browser/chromeos:lacros_chrome_browsertests",
       "//chrome/browser/chromeos/app_mode",
       "//chrome/browser/chromeos/enterprise/cloud_storage",
       "//chrome/browser/chromeos/extensions/telemetry/api:browser_tests",
@@ -12204,14 +12195,8 @@
       deps += [ "//ui/views" ]
     }
 
-    # This target should not require the Chrome executable to run
-    # except it wants to build lacros as a secondary toolchain.
-    if (!(is_chromeos_ash && also_build_lacros_chrome)) {
-      assert_no_deps = [
-        "//chrome",
-        "//chrome/test:test_lacros_chrome",
-      ]
-    }
+    # This target should not require the Chrome executable to run.
+    assert_no_deps = [ "//chrome" ]
   }
 
   # Executable to measure time to load libraries.
@@ -12581,14 +12566,10 @@
 
 # This target is defined for Lacros testing.
 # See //chrome/test/base/chromeos/README.md for more information.
-if (is_chromeos) {
+if (is_chromeos_ash) {
   assert(use_aura)
 
-  if (is_chromeos_ash) {
-    _target_name = "test_ash_chrome"
-  } else {
-    _target_name = "test_lacros_chrome"
-  }
+  _target_name = "test_ash_chrome"
   executable(_target_name) {
     output_name = _target_name
     testonly = true
@@ -12628,63 +12609,38 @@
       "//chrome:packed_resources",
     ]
 
-    if (is_chromeos_ash) {
-      sources += [
-        "base/chromeos/test_ash_chrome_browser_main_extra_parts.cc",
-        "base/chromeos/test_ash_chrome_browser_main_extra_parts.h",
-      ]
-      deps += [
-        "//ash:test_support",
-        "//chrome/browser:main_extra_parts",
-        "//chrome/browser/ash/boot_times_recorder",
-        "//chrome/browser/ash/crosapi",
-        "//chrome/browser/ash/crosapi:test_support",
-        "//chrome/browser/ash/locale",
-        "//chrome/browser/ash/login/signin",
-        "//chrome/browser/ash/schedqos",
-        "//chromeos/services/machine_learning/public/cpp:stub",
-        "//components/exo/wayland:test_controller",
-        "//components/exo/wayland:ui_controls_protocol",
-        "//ui/base:test_support",
-      ]
+    sources += [
+      "base/chromeos/test_ash_chrome_browser_main_extra_parts.cc",
+      "base/chromeos/test_ash_chrome_browser_main_extra_parts.h",
+    ]
+    deps += [
+      "//ash:test_support",
+      "//chrome/browser:main_extra_parts",
+      "//chrome/browser/ash/boot_times_recorder",
+      "//chrome/browser/ash/crosapi",
+      "//chrome/browser/ash/crosapi:test_support",
+      "//chrome/browser/ash/locale",
+      "//chrome/browser/ash/login/signin",
+      "//chrome/browser/ash/schedqos",
+      "//chromeos/services/machine_learning/public/cpp:stub",
+      "//components/exo/wayland:test_controller",
+      "//components/exo/wayland:ui_controls_protocol",
+      "//ui/base:test_support",
+    ]
 
-      # crbug.com/1223310 There is an issue that for the second toolchain the built
-      # nacl_irt_x86_64.nexe is not in subfolder ash_clang_x64.
-      # This action will copy the related files to the correct folder.
-      if (enable_nacl && current_cpu == "x64" && also_build_ash_chrome) {
-        copy("copy_files_to_ash_clang_x64") {
-          sources = [
-            "${root_build_dir}/nacl_irt_x86_64.nexe",
-            "${root_build_dir}/nacl_irt_x86_64.nexe.debug",
-          ]
-          outputs = [ "${root_build_dir}/ash_clang_x64/{{source_file_part}}" ]
-          deps = [ "//ppapi/native_client:irt" ]
-        }
-        deps += [ ":copy_files_to_ash_clang_x64" ]
+    # crbug.com/1223310 There is an issue that for the second toolchain the built
+    # nacl_irt_x86_64.nexe is not in subfolder ash_clang_x64.
+    # This action will copy the related files to the correct folder.
+    if (enable_nacl && current_cpu == "x64" && also_build_ash_chrome) {
+      copy("copy_files_to_ash_clang_x64") {
+        sources = [
+          "${root_build_dir}/nacl_irt_x86_64.nexe",
+          "${root_build_dir}/nacl_irt_x86_64.nexe.debug",
+        ]
+        outputs = [ "${root_build_dir}/ash_clang_x64/{{source_file_part}}" ]
+        deps = [ "//ppapi/native_client:irt" ]
       }
-    }
-
-    if (is_chromeos_lacros) {
-      sources += [
-        "//chrome/test/chromeos/standalone_browser_test_controller.cc",
-        "//chrome/test/chromeos/standalone_browser_test_controller.h",
-        "base/chromeos/test_lacros_chrome_browser_main_extra_parts.cc",
-        "base/chromeos/test_lacros_chrome_browser_main_extra_parts.h",
-      ]
-      deps += [
-        "//chrome/browser:main_extra_parts",
-
-        # For StandaloneBrowserTestController.
-        "//chrome/browser/apps/app_service",
-        "//chrome/browser/extensions:test_support",
-        "//chrome/browser/ui",
-        "//chrome/browser/web_applications",
-        "//chromeos/lacros:lacros_paths",
-        "//components/webapps/browser",
-        "//components/webapps/common",
-        "//content/test:test_support",
-        "//extensions/browser",
-      ]
+      deps += [ ":copy_files_to_ash_clang_x64" ]
     }
 
     if (use_ozone) {
diff --git a/chrome/test/DEPS b/chrome/test/DEPS
index 8fbf7c6..9696e26 100644
--- a/chrome/test/DEPS
+++ b/chrome/test/DEPS
@@ -13,6 +13,7 @@
   "+components/autofill/core",
   "+components/bookmarks/browser",
   "+components/bookmarks/common",
+  "+components/browser_ui/desktop_windowing/android",
   "+components/captive_portal",
   "+components/color",
   "+components/commerce/core",
diff --git a/chrome/test/base/chromeos/test_chrome_base.cc b/chrome/test/base/chromeos/test_chrome_base.cc
index bcbfff8..33e85165 100644
--- a/chrome/test/base/chromeos/test_chrome_base.cc
+++ b/chrome/test/base/chromeos/test_chrome_base.cc
@@ -10,16 +10,11 @@
 #include "base/functional/bind.h"
 #include "base/test/allow_check_is_test_for_testing.h"
 #include "chrome/browser/chrome_browser_main.h"
+#include "chrome/test/base/chromeos/test_ash_chrome_browser_main_extra_parts.h"
 #include "content/public/browser/browser_main_parts.h"
 #include "headless/public/headless_shell.h"
 #include "ui/gfx/switches.h"
 
-#if BUILDFLAG(IS_CHROMEOS_ASH)
-#include "chrome/test/base/chromeos/test_ash_chrome_browser_main_extra_parts.h"
-#else
-#include "chrome/test/base/chromeos/test_lacros_chrome_browser_main_extra_parts.h"
-#endif
-
 namespace test {
 
 TestChromeBase::TestChromeBase(content::ContentMainParams params)
@@ -50,13 +45,8 @@
     content::BrowserMainParts* browser_main_parts) {
   browser_main_parts_ =
       static_cast<ChromeBrowserMainParts*>(browser_main_parts);
-#if BUILDFLAG(IS_CHROMEOS_ASH)
   browser_main_parts_->AddParts(
       std::make_unique<test::TestAshChromeBrowserMainExtraParts>());
-#else
-  browser_main_parts_->AddParts(
-      std::make_unique<test::TestLacrosChromeBrowserMainExtraParts>());
-#endif
 }
 
 }  // namespace test
diff --git a/chrome/test/base/chromeos/test_lacros_chrome_browser_main_extra_parts.cc b/chrome/test/base/chromeos/test_lacros_chrome_browser_main_extra_parts.cc
deleted file mode 100644
index 83efb74..0000000
--- a/chrome/test/base/chromeos/test_lacros_chrome_browser_main_extra_parts.cc
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright 2023 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "test_lacros_chrome_browser_main_extra_parts.h"
-
-#include "base/logging.h"
-#include "chrome/test/chromeos/standalone_browser_test_controller.h"
-#include "chromeos/crosapi/mojom/crosapi.mojom.h"
-#include "chromeos/crosapi/mojom/test_controller.mojom.h"
-#include "chromeos/lacros/lacros_service.h"
-
-namespace test {
-
-TestLacrosChromeBrowserMainExtraParts::TestLacrosChromeBrowserMainExtraParts() =
-    default;
-
-TestLacrosChromeBrowserMainExtraParts::
-    ~TestLacrosChromeBrowserMainExtraParts() = default;
-
-void TestLacrosChromeBrowserMainExtraParts::PostBrowserStart() {
-  auto* lacros_service = chromeos::LacrosService::Get();
-  CHECK(lacros_service->IsAvailable<crosapi::mojom::TestController>());
-  auto& ash_test_controller =
-      lacros_service->GetRemote<crosapi::mojom::TestController>();
-  standalone_browser_test_controller_ =
-      std::make_unique<StandaloneBrowserTestController>(ash_test_controller);
-}
-
-}  // namespace test
diff --git a/chrome/test/base/chromeos/test_lacros_chrome_browser_main_extra_parts.h b/chrome/test/base/chromeos/test_lacros_chrome_browser_main_extra_parts.h
deleted file mode 100644
index 0b03f13..0000000
--- a/chrome/test/base/chromeos/test_lacros_chrome_browser_main_extra_parts.h
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright 2023 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_TEST_BASE_CHROMEOS_TEST_LACROS_CHROME_BROWSER_MAIN_EXTRA_PARTS_H_
-#define CHROME_TEST_BASE_CHROMEOS_TEST_LACROS_CHROME_BROWSER_MAIN_EXTRA_PARTS_H_
-
-#include <memory>
-
-#include "chrome/browser/chrome_browser_main_extra_parts.h"
-
-class StandaloneBrowserTestController;
-
-namespace test {
-
-// This class adds test-only features to Lacros. It is used to build the
-// test_lacros_chrome binary, which is meant to be run together with certain Ash
-// browser tests. See also docs/lacros/test_linux_lacros.md.
-class TestLacrosChromeBrowserMainExtraParts
-    : public ChromeBrowserMainExtraParts {
- public:
-  TestLacrosChromeBrowserMainExtraParts();
-  TestLacrosChromeBrowserMainExtraParts(
-      const TestLacrosChromeBrowserMainExtraParts&) = delete;
-  TestLacrosChromeBrowserMainExtraParts& operator=(
-      const TestLacrosChromeBrowserMainExtraParts&) = delete;
-  ~TestLacrosChromeBrowserMainExtraParts() override;
-
-  void PostBrowserStart() override;
-
- private:
-  // A test controller that is registered with Ash's TestController
-  // service over crosapi to let Ash tests control this Lacros
-  // instance.
-  std::unique_ptr<StandaloneBrowserTestController>
-      standalone_browser_test_controller_;
-};
-
-}  // namespace test
-
-#endif  // CHROME_TEST_BASE_CHROMEOS_TEST_LACROS_CHROME_BROWSER_MAIN_EXTRA_PARTS_H_
diff --git a/chrome/test/chromeos/standalone_browser_test_controller.cc b/chrome/test/chromeos/standalone_browser_test_controller.cc
deleted file mode 100644
index 4a316f1..0000000
--- a/chrome/test/chromeos/standalone_browser_test_controller.cc
+++ /dev/null
@@ -1,527 +0,0 @@
-// Copyright 2021 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/test/chromeos/standalone_browser_test_controller.h"
-
-#include <memory>
-
-#include "base/check_is_test.h"
-#include "base/functional/bind.h"
-#include "base/functional/overloaded.h"
-#include "base/json/json_reader.h"
-#include "base/memory/raw_ptr.h"
-#include "base/test/values_test_util.h"
-#include "base/types/expected_macros.h"
-#include "base/values.h"
-#include "chrome/browser/apps/app_service/app_service_proxy.h"
-#include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
-#include "chrome/browser/extensions/chrome_test_extension_loader.h"
-#include "chrome/browser/extensions/component_loader.h"
-#include "chrome/browser/extensions/extension_keeplist_chromeos.h"
-#include "chrome/browser/extensions/extension_service.h"
-#include "chrome/browser/profiles/profile_manager.h"
-#include "chrome/browser/speech/tts_crosapi_util.h"
-#include "chrome/browser/ui/webui/print_preview/printer_handler.h"
-#include "chrome/browser/web_applications/isolated_web_apps/isolated_web_app_install_source.h"
-#include "chrome/browser/web_applications/isolated_web_apps/isolated_web_app_source.h"
-#include "chrome/browser/web_applications/isolated_web_apps/isolated_web_app_trust_checker.h"
-#include "chrome/browser/web_applications/mojom/user_display_mode.mojom.h"
-#include "chrome/browser/web_applications/web_app_command_scheduler.h"
-#include "chrome/browser/web_applications/web_app_install_info.h"
-#include "chrome/browser/web_applications/web_app_provider.h"
-#include "chrome/common/pref_names.h"
-#include "chrome/common/url_constants.h"
-#include "chromeos/crosapi/mojom/tts.mojom-forward.h"
-#include "components/prefs/pref_service.h"
-#include "components/webapps/browser/installable/installable_metrics.h"
-#include "content/public/browser/tts_utterance.h"
-#include "extensions/browser/extension_registry.h"
-#include "extensions/browser/extension_system.h"
-#include "extensions/common/extension.h"
-#include "extensions/common/manifest_constants.h"
-#include "url/origin.h"
-
-namespace {
-
-blink::mojom::DisplayMode WindowModeToDisplayMode(
-    apps::WindowMode window_mode) {
-  switch (window_mode) {
-    case apps::WindowMode::kBrowser:
-      return blink::mojom::DisplayMode::kBrowser;
-    case apps::WindowMode::kTabbedWindow:
-      return blink::mojom::DisplayMode::kTabbed;
-    case apps::WindowMode::kWindow:
-      return blink::mojom::DisplayMode::kStandalone;
-    case apps::WindowMode::kUnknown:
-      return blink::mojom::DisplayMode::kUndefined;
-  }
-}
-
-web_app::mojom::UserDisplayMode WindowModeToUserDisplayMode(
-    apps::WindowMode window_mode) {
-  switch (window_mode) {
-    case apps::WindowMode::kBrowser:
-      return web_app::mojom::UserDisplayMode::kBrowser;
-    case apps::WindowMode::kTabbedWindow:
-      return web_app::mojom::UserDisplayMode::kTabbed;
-    case apps::WindowMode::kWindow:
-      return web_app::mojom::UserDisplayMode::kStandalone;
-    case apps::WindowMode::kUnknown:
-      return web_app::mojom::UserDisplayMode::kBrowser;
-  }
-}
-
-void IsolatedWebAppInstallationDone(
-    const webapps::AppId& installed_app_id,
-    StandaloneBrowserTestController::InstallIsolatedWebAppCallback callback,
-    base::expected<web_app::InstallIsolatedWebAppCommandSuccess,
-                   web_app::InstallIsolatedWebAppCommandError> install_result) {
-  if (install_result.has_value()) {
-    std::move(callback).Run(
-        crosapi::mojom::InstallWebAppResult::NewAppId(installed_app_id));
-  } else {
-    std::move(callback).Run(
-        crosapi::mojom::InstallWebAppResult::NewErrorMessage(
-            install_result.error().message));
-  }
-}
-
-void OnIsolatedWebAppUrlInfoCreated(
-    const web_app::IsolatedWebAppInstallSource& install_source,
-    StandaloneBrowserTestController::InstallIsolatedWebAppCallback callback,
-    base::expected<web_app::IsolatedWebAppUrlInfo, std::string>
-        iwa_url_info_expected) {
-  ASSIGN_OR_RETURN(
-      auto iwa_url_info, iwa_url_info_expected, [&](const std::string& error) {
-        std::move(callback).Run(
-            crosapi::mojom::InstallWebAppResult::NewErrorMessage(error));
-      });
-
-  if (!install_source.source().dev_mode()) {
-    web_app::AddTrustedWebBundleIdForTesting(iwa_url_info.web_bundle_id());
-  }
-
-  Profile* profile = ProfileManager::GetPrimaryUserProfile();
-  auto* provider = web_app::WebAppProvider::GetForWebApps(profile);
-  provider->scheduler().InstallIsolatedWebApp(
-      iwa_url_info, install_source, /*expected_version=*/std::nullopt,
-      /*optional_keep_alive=*/nullptr, /*optional_profile_keep_alive=*/nullptr,
-      base::BindOnce(&IsolatedWebAppInstallationDone, iwa_url_info.app_id(),
-                     std::move(callback)));
-}
-
-class FakeExtensionPrinterHandler : public printing::PrinterHandler {
- public:
-  FakeExtensionPrinterHandler() = default;
-  FakeExtensionPrinterHandler(const FakeExtensionPrinterHandler&) = delete;
-  FakeExtensionPrinterHandler& operator=(const FakeExtensionPrinterHandler&) =
-      delete;
-  ~FakeExtensionPrinterHandler() override = default;
-
-  void Reset() override {}
-
-  void StartGetPrinters(AddedPrintersCallback added_printers_callback,
-                        GetPrintersDoneCallback done_callback) override {
-    added_printers_callback.Run(base::test::ParseJsonList(R"(
-      [ {
-        "description": "A virtual printer for testing",
-        "extensionId": "jbljdigmdjodgkcllikhggoepmmffbam",
-        "extensionName": "Test Printer Provider",
-        "id": "jbljdigmdjodgkcllikhggoepmmffbam:test-printer-01",
-        "name": "Test Printer 01"
-      }, {
-        "description": "A virtual printer for testing",
-        "extensionId": "jbljdigmdjodgkcllikhggoepmmffbam",
-        "extensionName": "Test Printer Provider",
-        "id": "jbljdigmdjodgkcllikhggoepmmffbam:test-printer-02",
-        "name": "Test Printer 02"
-      } ]
-    )"));
-    std::move(done_callback).Run();
-  }
-
-  void StartGetCapability(const std::string& destination_id,
-                          GetCapabilityCallback callback) override {
-    std::move(callback).Run(base::test::ParseJsonDict(R"(
-    {
-      "version": "1.0",
-      "printer": {
-        "supported_content_type": [
-          {"content_type": "application/pdf"}
-        ]
-      }
-    })"));
-  }
-
-  void StartGrantPrinterAccess(const std::string& printer_id,
-                               GetPrinterInfoCallback callback) override {
-    base::Value::Dict printer_info;
-    printer_info.Set("printerId", printer_id);
-    printer_info.Set("name", "Test Printer 01");
-
-    std::move(callback).Run(std::move(printer_info));
-  }
-
-  void StartPrint(const std::u16string& job_title,
-                  base::Value::Dict settings,
-                  scoped_refptr<base::RefCountedMemory> print_data,
-                  PrintCallback callback) override {
-    // Simulate a successful print job. "OK" means successful.
-    std::move(callback).Run(base::Value("OK"));
-  }
-};
-
-}  // namespace
-
-// With Lacros tts support enabled, all Lacros utterances will be sent to
-// Ash to be processed by TtsController in Ash. When the utterance is spoken
-// by a speech engine (provided by Ash or Lacros), we need to make sure that
-// Tts events are routed back to the UtteranceEventDelegate in Lacros.
-// This class can be set as UtteranceEventDelegate for Lacros Utterance used
-// for testing.
-class StandaloneBrowserTestController::LacrosUtteranceEventDelegate
-    : public content::UtteranceEventDelegate {
- public:
-  LacrosUtteranceEventDelegate(
-      StandaloneBrowserTestController* controller,
-      mojo::PendingRemote<crosapi::mojom::TtsUtteranceClient> client)
-      : controller_(controller), client_(std::move(client)) {}
-
-  LacrosUtteranceEventDelegate(const LacrosUtteranceEventDelegate&) = delete;
-  LacrosUtteranceEventDelegate& operator=(const LacrosUtteranceEventDelegate&) =
-      delete;
-  ~LacrosUtteranceEventDelegate() override = default;
-
-  // content::UtteranceEventDelegate methods:
-  void OnTtsEvent(content::TtsUtterance* utterance,
-                  content::TtsEventType event_type,
-                  int char_index,
-                  int char_length,
-                  const std::string& error_message) override {
-    // Forward the TtsEvent back to ash, so that ash browser test can verify
-    // that TtsEvent has been routed to the UtteranceEventDelegate in Lacros.
-    client_->OnTtsEvent(tts_crosapi_util::ToMojo(event_type), char_index,
-                        char_length, error_message);
-
-    if (utterance->IsFinished()) {
-      controller_->OnUtteranceFinished(utterance->GetId());
-    }
-    // Note: |this| is deleted if utterance->IsFinished().
-  }
-
- private:
-  // |controller_| is guaranteed to be valid during the lifetime of this class.
-  const raw_ptr<StandaloneBrowserTestController> controller_;
-  mojo::Remote<crosapi::mojom::TtsUtteranceClient> client_;
-};
-
-StandaloneBrowserTestController::StandaloneBrowserTestController(
-    mojo::Remote<crosapi::mojom::TestController>& test_controller) {
-  CHECK_IS_TEST();
-  test_controller->RegisterStandaloneBrowserTestController(
-      controller_receiver_.BindNewPipeAndPassRemoteWithVersion());
-  test_controller.FlushAsync();
-}
-
-StandaloneBrowserTestController::~StandaloneBrowserTestController() = default;
-
-void StandaloneBrowserTestController::InstallWebApp(
-    const std::string& start_url,
-    apps::WindowMode window_mode,
-    InstallWebAppCallback callback) {
-  auto info =
-      web_app::WebAppInstallInfo::CreateWithStartUrlForTesting(GURL(start_url));
-  info->title = u"Test Web App";
-  info->display_mode = WindowModeToDisplayMode(window_mode);
-  info->user_display_mode = WindowModeToUserDisplayMode(window_mode);
-  Profile* profile = ProfileManager::GetPrimaryUserProfile();
-  auto* provider = web_app::WebAppProvider::GetForWebApps(profile);
-  provider->scheduler().InstallFromInfoNoIntegrationForTesting(
-      std::move(info),
-      /*overwrite_existing_manifest_fields=*/false,
-      webapps::WebappInstallSource::SYNC,
-      base::BindOnce(&StandaloneBrowserTestController::WebAppInstallationDone,
-                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
-}
-
-void StandaloneBrowserTestController::InstallUnpackedExtension(
-    const std::string& path,
-    InstallUnpackedExtensionCallback callback) {
-  Profile* profile = ProfileManager::GetPrimaryUserProfile();
-  extensions::ChromeTestExtensionLoader loader(profile);
-  loader.LoadUnpackedExtensionAsync(
-      base::FilePath{path},
-      base::BindOnce([](const extensions::Extension* extension) {
-        return extension->id();
-      }).Then(std::move(callback)));
-}
-
-void StandaloneBrowserTestController::ObserveDomMessages(
-    mojo::PendingRemote<crosapi::mojom::DomMessageObserver> observer,
-    ObserveDomMessagesCallback callback) {
-  dom_message_observer_.Bind(std::move(observer));
-  dom_message_observer_.set_disconnect_handler(base::BindOnce(
-      &StandaloneBrowserTestController::OnDomMessageObserverDisconnected,
-      weak_ptr_factory_.GetWeakPtr()));
-
-  ASSERT_FALSE(dom_message_queue_.has_value());
-  dom_message_queue_.emplace();
-  dom_message_queue_->SetOnMessageAvailableCallback(
-      base::BindOnce(&StandaloneBrowserTestController::OnDomMessageQueueReady,
-                     weak_ptr_factory_.GetWeakPtr()));
-
-  std::move(callback).Run();
-}
-
-void StandaloneBrowserTestController::OnDomMessageObserverDisconnected() {
-  dom_message_queue_.reset();
-  dom_message_observer_.reset();
-}
-
-void StandaloneBrowserTestController::OnDomMessageQueueReady() {
-  std::string message;
-  ASSERT_TRUE(dom_message_queue_->PopMessage(&message));
-  dom_message_observer_->OnMessage(message);
-
-  dom_message_queue_->SetOnMessageAvailableCallback(
-      base::BindOnce(&StandaloneBrowserTestController::OnDomMessageQueueReady,
-                     weak_ptr_factory_.GetWeakPtr()));
-}
-
-void StandaloneBrowserTestController::InstallComponentExtension(
-    const std::string& path,
-    const std::string& extension_id,
-    InstallComponentExtensionCallback callback) {
-  Profile* profile = ProfileManager::GetPrimaryUserProfile();
-  extensions::ExtensionService* service =
-      extensions::ExtensionSystem::Get(profile)->extension_service();
-  service->component_loader()->AddComponentFromDirWithManifestFilename(
-      base::FilePath(path), extension_id, extensions::kManifestFilename,
-      extensions::kManifestFilename, std::move(callback));
-}
-
-void StandaloneBrowserTestController::RemoveComponentExtension(
-    const std::string& extension_id,
-    RemoveComponentExtensionCallback callback) {
-  Profile* profile = ProfileManager::GetPrimaryUserProfile();
-  extensions::ExtensionSystem::Get(profile)
-      ->extension_service()
-      ->RemoveComponentExtension(extension_id);
-  std::move(callback).Run();
-}
-
-void StandaloneBrowserTestController::LoadVpnExtension(
-    const std::string& extension_name,
-    LoadVpnExtensionCallback callback) {
-  std::string error;
-  auto extension = extensions::Extension::Create(
-      base::FilePath(), extensions::mojom::ManifestLocation::kUnpacked,
-      CreateVpnExtensionManifest(extension_name),
-      extensions::Extension::NO_FLAGS, &error);
-  if (!error.empty()) {
-    std::move(callback).Run(error);
-    return;
-  }
-
-  auto* extension_registry = extensions::ExtensionRegistry::Get(
-      ProfileManager::GetPrimaryUserProfile());
-  extension_registry->AddEnabled(extension);
-  extension_registry->TriggerOnLoaded(extension.get());
-
-  std::move(callback).Run(extension->id());
-}
-
-void StandaloneBrowserTestController::GetTtsVoices(
-    GetTtsVoicesCallback callback) {
-  std::vector<content::VoiceData> voices;
-  tts_crosapi_util::GetAllVoicesForTesting(  // IN-TEST
-      ProfileManager::GetActiveUserProfile(), GURL(), &voices);
-
-  std::vector<crosapi::mojom::TtsVoicePtr> mojo_voices;
-  for (const auto& voice : voices) {
-    mojo_voices.push_back(tts_crosapi_util::ToMojo(voice));
-  }
-
-  std::move(callback).Run(std::move(mojo_voices));
-}
-
-void StandaloneBrowserTestController::TtsSpeak(
-    crosapi::mojom::TtsUtterancePtr mojo_utterance,
-    mojo::PendingRemote<crosapi::mojom::TtsUtteranceClient> utterance_client) {
-  std::unique_ptr<content::TtsUtterance> lacros_utterance =
-      tts_crosapi_util::CreateUtteranceFromMojo(
-          mojo_utterance, /*should_always_be_spoken=*/true);
-  auto event_delegate = std::make_unique<LacrosUtteranceEventDelegate>(
-      this, std::move(utterance_client));
-  lacros_utterance->SetEventDelegate(event_delegate.get());
-  lacros_utterance_event_delegates_.emplace(lacros_utterance->GetId(),
-                                            std ::move(event_delegate));
-  tts_crosapi_util::SpeakForTesting(std::move(lacros_utterance));
-}
-
-void StandaloneBrowserTestController::InstallSubApp(
-    const webapps::AppId& parent_app_id,
-    const std::string& sub_app_path,
-    InstallSubAppCallback callback) {
-  GURL parent_app_url(
-      base::StrCat({chrome::kIsolatedAppScheme, url::kStandardSchemeSeparator,
-                    parent_app_id}));
-
-  GURL start_url = parent_app_url.Resolve(sub_app_path);
-  auto info =
-      web_app::WebAppInstallInfo::CreateWithStartUrlForTesting(start_url);
-  info->parent_app_id = parent_app_id;
-  info->parent_app_manifest_id = parent_app_url;
-  info->title = u"Sub App";
-
-  Profile* profile = ProfileManager::GetPrimaryUserProfile();
-  auto* provider = web_app::WebAppProvider::GetForWebApps(profile);
-  provider->scheduler().InstallFromInfoNoIntegrationForTesting(
-      std::move(info),
-      /*overwrite_existing_manifest_fields=*/false,
-      webapps::WebappInstallSource::SUB_APP,
-      base::BindOnce(&StandaloneBrowserTestController::WebAppInstallationDone,
-                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
-}
-
-void StandaloneBrowserTestController::InstallIsolatedWebApp(
-    crosapi::mojom::IsolatedWebAppLocationPtr location,
-    bool dev_mode,
-    InstallIsolatedWebAppCallback callback) {
-  web_app::IsolatedWebAppInstallSource install_source = ([&]() {
-    if (dev_mode) {
-      if (location->is_bundle_path()) {
-        return web_app::IsolatedWebAppInstallSource::FromDevUi(
-            web_app::IwaSourceBundleDevModeWithFileOp(
-                base::FilePath(location->get_bundle_path()),
-                web_app::IwaSourceBundleDevFileOp::kCopy));
-      } else {
-        return web_app::IsolatedWebAppInstallSource::FromDevUi(
-            web_app::IwaSourceProxy(
-                url::Origin::Create(location->get_proxy_origin())));
-      }
-    } else {
-      return web_app::IsolatedWebAppInstallSource::FromGraphicalInstaller(
-          web_app::IwaSourceBundleProdModeWithFileOp(
-              base::FilePath(location->get_bundle_path()),
-              web_app::IwaSourceBundleProdFileOp::kCopy));
-    }
-  })();
-
-  web_app::IsolatedWebAppUrlInfo::CreateFromIsolatedWebAppSource(
-      install_source.source(),
-      base::BindOnce(&OnIsolatedWebAppUrlInfoCreated, install_source,
-                     std::move(callback)));
-}
-
-void StandaloneBrowserTestController::SetWebAppSettingsPref(
-    const std::string& policy,
-    SetWebAppSettingsPrefCallback callback) {
-  CHECK(callback);
-
-  auto result = base::JSONReader::ReadAndReturnValueWithError(
-      policy, base::JSONParserOptions::JSON_ALLOW_TRAILING_COMMAS);
-  if (!result.has_value()) {
-    std::move(callback).Run(/*success=*/false);
-    return;
-  }
-  if (!result->is_list()) {
-    std::move(callback).Run(/*success=*/false);
-    return;
-  }
-
-  ProfileManager::GetPrimaryUserProfile()->GetPrefs()->SetList(
-      prefs::kWebAppSettings, std::move(*result).TakeList());
-
-  std::move(callback).Run(/*success=*/true);
-}
-
-void StandaloneBrowserTestController::SetWebAppInstallForceListPref(
-    const std::string& policy,
-    SetWebAppInstallForceListPrefCallback callback) {
-  CHECK(callback);
-
-  auto result = base::JSONReader::ReadAndReturnValueWithError(
-      policy, base::JSONParserOptions::JSON_ALLOW_TRAILING_COMMAS);
-  if (!result.has_value()) {
-    std::move(callback).Run(/*success=*/false);
-    return;
-  }
-  if (!result->is_list()) {
-    std::move(callback).Run(/*success=*/false);
-    return;
-  }
-
-  ProfileManager::GetPrimaryUserProfile()->GetPrefs()->SetList(
-      prefs::kWebAppInstallForceList, std::move(*result).TakeList());
-
-  std::move(callback).Run(/*success=*/true);
-}
-
-// Uses a fake extension printer handler to process printing requests from ash
-// browser tests.
-void StandaloneBrowserTestController::SetFakeExtensionPrinterHandler(
-    SetFakeExtensionPrinterHandlerCallback callback) {
-  printing::ExtensionPrinterServiceProviderLacros::GetForBrowserContext(
-      ProfileManager::GetPrimaryUserProfile())
-      ->SetPrinterHandlerForTesting(
-          std::make_unique<FakeExtensionPrinterHandler>());
-  std::move(callback).Run();
-}
-
-void StandaloneBrowserTestController::OnUtteranceFinished(int utterance_id) {
-  // Delete the utterace event delegate object when the utterance is finished.
-  lacros_utterance_event_delegates_.erase(utterance_id);
-}
-
-void StandaloneBrowserTestController::GetExtensionKeeplist(
-    GetExtensionKeeplistCallback callback) {
-  auto mojo_keeplist = crosapi::mojom::ExtensionKeepList::New();
-
-  for (const auto& id :
-       extensions::GetExtensionsRunInOSAndStandaloneBrowser()) {
-    mojo_keeplist->extensions_run_in_os_and_standalonebrowser.push_back(
-        std::string(id));
-  }
-
-  for (const auto& id :
-       extensions::GetExtensionAppsRunInOSAndStandaloneBrowser()) {
-    mojo_keeplist->extension_apps_run_in_os_and_standalonebrowser.push_back(
-        std::string(id));
-  }
-
-  for (const auto& id : extensions::GetExtensionsRunInOSOnly()) {
-    mojo_keeplist->extensions_run_in_os_only.push_back(std::string(id));
-  }
-
-  for (const auto& id : extensions::GetExtensionAppsRunInOSOnly()) {
-    mojo_keeplist->extension_apps_run_in_os_only.push_back(std::string(id));
-  }
-
-  std::move(callback).Run(std::move(mojo_keeplist));
-}
-
-void StandaloneBrowserTestController::WebAppInstallationDone(
-    InstallWebAppCallback callback,
-    const webapps::AppId& installed_app_id,
-    webapps::InstallResultCode code) {
-  std::move(callback).Run(code == webapps::InstallResultCode::kSuccessNewInstall
-                              ? installed_app_id
-                              : "");
-}
-
-base::Value::Dict StandaloneBrowserTestController::CreateVpnExtensionManifest(
-    const std::string& extension_name) {
-  base::Value::Dict manifest;
-
-  manifest.Set(extensions::manifest_keys::kName, extension_name);
-  manifest.Set(extensions::manifest_keys::kVersion, "1.0");
-  manifest.Set(extensions::manifest_keys::kManifestVersion, 2);
-
-  base::Value::List permissions;
-  permissions.Append("vpnProvider");
-  manifest.Set(extensions::manifest_keys::kPermissions, std::move(permissions));
-
-  return manifest;
-}
diff --git a/chrome/test/chromeos/standalone_browser_test_controller.h b/chrome/test/chromeos/standalone_browser_test_controller.h
deleted file mode 100644
index c92c243..0000000
--- a/chrome/test/chromeos/standalone_browser_test_controller.h
+++ /dev/null
@@ -1,109 +0,0 @@
-// Copyright 2021 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_TEST_CHROMEOS_STANDALONE_BROWSER_TEST_CONTROLLER_H_
-#define CHROME_TEST_CHROMEOS_STANDALONE_BROWSER_TEST_CONTROLLER_H_
-
-#include <memory>
-#include <string>
-
-#include "base/types/expected.h"
-#include "base/values.h"
-#include "chromeos/crosapi/mojom/test_controller.mojom.h"
-#include "chromeos/crosapi/mojom/tts.mojom-forward.h"
-#include "components/services/app_service/public/cpp/app_types.h"
-#include "components/webapps/browser/install_result_code.h"
-#include "components/webapps/common/web_app_id.h"
-#include "content/public/test/browser_test_utils.h"
-#include "mojo/public/cpp/bindings/receiver.h"
-#include "mojo/public/cpp/bindings/remote.h"
-
-// Created in lacros-chrome (test_lacros_chrome) and registered with
-// ash-chrome's test controller over crosapi to let the Ash browser tests that
-// require Lacros to send commands to this lacros-chrome instance.
-class StandaloneBrowserTestController
-    : public crosapi::mojom::StandaloneBrowserTestController {
- public:
-  explicit StandaloneBrowserTestController(
-      mojo::Remote<crosapi::mojom::TestController>& test_controller);
-  ~StandaloneBrowserTestController() override;
-
-  void InstallWebApp(const std::string& start_url,
-                     apps::WindowMode window_mode,
-                     InstallWebAppCallback callback) override;
-
-  void LoadVpnExtension(const std::string& extension_name,
-                        LoadVpnExtensionCallback callback) override;
-
-  void GetTtsVoices(GetTtsVoicesCallback callback) override;
-
-  void GetExtensionKeeplist(GetExtensionKeeplistCallback callback) override;
-
-  void TtsSpeak(crosapi::mojom::TtsUtterancePtr mojo_utterance,
-                mojo::PendingRemote<crosapi::mojom::TtsUtteranceClient>
-                    utterance_client) override;
-
-  void InstallSubApp(const webapps::AppId& parent_app_id,
-                     const std::string& sub_app_path,
-                     InstallSubAppCallback callback) override;
-
-  void InstallIsolatedWebApp(crosapi::mojom::IsolatedWebAppLocationPtr location,
-                             bool dev_mode,
-                             InstallIsolatedWebAppCallback callback) override;
-
-  void SetWebAppSettingsPref(const std::string& web_app_settings_json,
-                             SetWebAppSettingsPrefCallback callback) override;
-
-  // This does not wait for background page or any views to finish (or even
-  // start) loading.
-  void InstallUnpackedExtension(
-      const std::string& path,
-      InstallUnpackedExtensionCallback callback) override;
-
-  void InstallComponentExtension(
-      const std::string& path,
-      const std::string& extension_id,
-      InstallComponentExtensionCallback callback) override;
-
-  void RemoveComponentExtension(
-      const std::string& extension_id,
-      RemoveComponentExtensionCallback callback) override;
-
-  void ObserveDomMessages(
-      mojo::PendingRemote<crosapi::mojom::DomMessageObserver> observer,
-      ObserveDomMessagesCallback callback) override;
-
-  void SetWebAppInstallForceListPref(
-      const std::string& web_app_settings_json,
-      SetWebAppInstallForceListPrefCallback callback) override;
-
-  void SetFakeExtensionPrinterHandler(
-      SetFakeExtensionPrinterHandlerCallback callback) override;
-
- private:
-  class LacrosUtteranceEventDelegate;
-
-  void OnUtteranceFinished(int utterance_id);
-  void WebAppInstallationDone(InstallWebAppCallback callback,
-                              const webapps::AppId& installed_app_id,
-                              webapps::InstallResultCode code);
-  base::Value::Dict CreateVpnExtensionManifest(
-      const std::string& extension_name);
-  void OnDomMessageObserverDisconnected();
-  void OnDomMessageQueueReady();
-
-  mojo::Receiver<crosapi::mojom::StandaloneBrowserTestController>
-      controller_receiver_{this};
-
-  // Lacros utterance event delegates by utterance id.
-  std::map<int, std::unique_ptr<LacrosUtteranceEventDelegate>>
-      lacros_utterance_event_delegates_;
-
-  mojo::Remote<crosapi::mojom::DomMessageObserver> dom_message_observer_;
-  std::optional<content::DOMMessageQueue> dom_message_queue_;
-
-  base::WeakPtrFactory<StandaloneBrowserTestController> weak_ptr_factory_{this};
-};
-
-#endif  // CHROME_TEST_CHROMEOS_STANDALONE_BROWSER_TEST_CONTROLLER_H_
diff --git a/chrome/test/data/webui/chromeos/BUILD.gn b/chrome/test/data/webui/chromeos/BUILD.gn
index f06133f..37073e2 100644
--- a/chrome/test/data/webui/chromeos/BUILD.gn
+++ b/chrome/test/data/webui/chromeos/BUILD.gn
@@ -233,7 +233,7 @@
     "network/network_ip_config_test.js",
     "network/network_list_item_test.js",
     "network/network_list_test.js",
-    "network/network_nameservers_test.js",
+    "network/network_nameservers_test.ts",
     "network/network_password_input_test.js",
     "network/network_property_list_mojo_test.js",
     "network/network_proxy_exclusions_test.js",
diff --git a/chrome/test/data/webui/chromeos/network/network_nameservers_test.js b/chrome/test/data/webui/chromeos/network/network_nameservers_test.js
deleted file mode 100644
index 086a007a..0000000
--- a/chrome/test/data/webui/chromeos/network/network_nameservers_test.js
+++ /dev/null
@@ -1,96 +0,0 @@
-// Copyright 2020 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import 'chrome://os-settings/strings.m.js';
-import 'chrome://resources/ash/common/network/network_nameservers.js';
-
-import {ConnectionStateType, PolicySource} from 'chrome://resources/mojo/chromeos/services/network_config/public/mojom/network_types.mojom-webui.js';
-import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
-
-suite('NetworkNameserversTest', function() {
-  /** @type {!NetworkNameservers|undefined} */
-  let nameservers;
-
-  setup(function() {
-    nameservers = document.createElement('network-nameservers');
-    document.body.appendChild(nameservers);
-    flush();
-  });
-
-  test('Select nameservers', async () => {
-    assertTrue(!!nameservers.$.nameserverType);
-
-    // Default nameserver type is 'automatic'.
-    assertEquals('automatic', nameservers.$.nameserverType.selected);
-    assertTrue(nameservers.$$('cr-radio-button[name=automatic]').checked);
-    assertFalse(nameservers.$$('cr-radio-button[name=google]').checked);
-    assertFalse(nameservers.$$('cr-radio-button[name=custom]').checked);
-
-    nameservers.$.nameserverType.selected = 'google';
-    assertFalse(nameservers.$$('cr-radio-button[name=automatic]').checked);
-    assertTrue(nameservers.$$('cr-radio-button[name=google]').checked);
-    assertFalse(nameservers.$$('cr-radio-button[name=custom]').checked);
-
-    nameservers.$.nameserverType.selected = 'custom';
-    assertFalse(nameservers.$$('cr-radio-button[name=automatic]').checked);
-    assertFalse(nameservers.$$('cr-radio-button[name=google]').checked);
-    assertTrue(nameservers.$$('cr-radio-button[name=custom]').checked);
-  });
-
-  test('Disabled UI state', function() {
-    const radioGroup = nameservers.$.nameserverType;
-    assertFalse(radioGroup.disabled);
-
-    nameservers.disabled = true;
-
-    assertTrue(radioGroup.disabled);
-  });
-
-  test(
-      'Do not apply observed changes for static config type when connected',
-      function() {
-        const ipAddress = '8.8.8.2';
-        nameservers.$.nameserverType.selected = 'custom';
-        nameservers.managedProperties = {
-          ipAddressConfigType: {
-            activeValue: 'DHCP',
-          },
-          staticIpConfig: {
-            nameServers: {
-              activeValue: ['8.8.8.2', '8.8.8.8', '0.0.0.0', '0.0.0.0'],
-            },
-          },
-          connectionState: ConnectionStateType.kConnected,
-          nameServersConfigType: {
-            activeValue: 'Static',
-          },
-          guid: 'f19a0128-0b37-490a-bfc9-d04031f27d2a',
-        };
-        flush();
-
-        assertEquals(
-            ipAddress, nameservers.$$('cr-input[id=nameserver0]').value);
-
-        nameservers.managedProperties = {
-          ipAddressConfigType: {
-            activeValue: 'DHCP',
-          },
-          staticIpConfig: {
-            nameServers: {
-              activeValue: ['0.0.0.2', '8.8.8.8', '0.0.0.0', '0.0.0.0'],
-            },
-          },
-          connectionState: ConnectionStateType.kConnected,
-          nameServersConfigType: {
-            activeValue: 'Static',
-          },
-          guid: 'f19a0128-0b37-490a-bfc9-d04031f27d2a',
-        };
-        flush();
-
-        assertEquals(
-            ipAddress, nameservers.$$('cr-input[id=nameserver0]').value);
-      });
-});
diff --git a/chrome/test/data/webui/chromeos/network/network_nameservers_test.ts b/chrome/test/data/webui/chromeos/network/network_nameservers_test.ts
new file mode 100644
index 0000000..70bd94d
--- /dev/null
+++ b/chrome/test/data/webui/chromeos/network/network_nameservers_test.ts
@@ -0,0 +1,120 @@
+// Copyright 2020 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'chrome://os-settings/strings.m.js';
+import 'chrome://resources/ash/common/network/network_nameservers.js';
+
+import type {CrInputElement} from 'chrome://resources/ash/common/cr_elements/cr_input/cr_input.js';
+import type {CrRadioButtonElement} from 'chrome://resources/ash/common/cr_elements/cr_radio_button/cr_radio_button.js';
+import type {CrRadioGroupElement} from 'chrome://resources/ash/common/cr_elements/cr_radio_group/cr_radio_group.js';
+import type {NetworkNameserversElement} from 'chrome://resources/ash/common/network/network_nameservers.js';
+import {OncMojo} from 'chrome://resources/ash/common/network/onc_mojo.js';
+import {IPConfigType, NetworkType, PolicySource} from 'chrome://resources/mojo/chromeos/services/network_config/public/mojom/network_types.mojom-webui.js';
+import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
+
+suite('NetworkNameserversTest', () => {
+  let nameservers: NetworkNameserversElement|undefined;
+
+  setup(() => {
+    nameservers = document.createElement('network-nameservers');
+    document.body.appendChild(nameservers);
+    flush();
+  });
+
+  test('Select nameservers', async () => {
+    assertTrue(!!nameservers);
+    const nameserverTypeRadioGroup =
+        nameservers.shadowRoot!.querySelector<CrRadioGroupElement>(
+            '#nameserverType');
+    assertTrue(!!nameserverTypeRadioGroup);
+
+    // Default nameserver type is 'automatic'.
+    const automaticButton =
+        nameservers.shadowRoot!.querySelector<CrRadioButtonElement>(
+            'cr-radio-button[name=automatic]');
+    assertTrue(!!automaticButton);
+    const googleButton =
+        nameservers.shadowRoot!.querySelector<CrRadioButtonElement>(
+            'cr-radio-button[name=google]');
+    assertTrue(!!googleButton);
+    const customButton =
+        nameservers.shadowRoot!.querySelector<CrRadioButtonElement>(
+            'cr-radio-button[name=custom]');
+    assertTrue(!!customButton);
+
+    assertEquals('automatic', nameserverTypeRadioGroup.selected);
+    assertTrue(automaticButton.checked);
+    assertFalse(googleButton.checked);
+    assertFalse(customButton.checked);
+
+    nameserverTypeRadioGroup.selected = 'google';
+    assertFalse(automaticButton.checked);
+    assertTrue(googleButton.checked);
+    assertFalse(customButton.checked);
+
+    nameserverTypeRadioGroup.selected = 'custom';
+    assertFalse(automaticButton.checked);
+    assertFalse(googleButton.checked);
+    assertTrue(customButton.checked);
+  });
+
+  test('Disabled UI state', () => {
+    assertTrue(!!nameservers);
+    const nameserverTypeRadioGroup =
+        nameservers.shadowRoot!.querySelector<CrRadioGroupElement>(
+            '#nameserverType');
+    assertTrue(!!nameserverTypeRadioGroup);
+    assertFalse(nameserverTypeRadioGroup.disabled);
+
+    nameservers.disabled = true;
+
+    assertTrue(nameserverTypeRadioGroup.disabled);
+  });
+
+  test(
+      'Do not apply observed changes for static config type when connected',
+      () => {
+        assertTrue(!!nameservers);
+        const nameserverTypeRadioGroup =
+            nameservers.shadowRoot!.querySelector<CrRadioGroupElement>(
+                '#nameserverType');
+        assertTrue(!!nameserverTypeRadioGroup);
+
+        const ipAddress = '8.8.8.2';
+        nameserverTypeRadioGroup.selected = 'custom';
+
+        const managedProperties = OncMojo.getDefaultManagedProperties(
+            NetworkType.kEthernet, 'f19a0128-0b37-490a-bfc9-d04031f27d2a',
+            'name');
+        managedProperties.staticIpConfig = {
+          gateway: undefined,
+          ipAddress: undefined,
+          nameServers: {
+            activeValue: ['8.8.8.2', '8.8.8.8', '0.0.0.0', '0.0.0.0'],
+            policySource: PolicySource.kNone,
+            policyValue: undefined,
+          },
+          routingPrefix: undefined,
+          type: IPConfigType.kIPv4,
+          webProxyAutoDiscoveryUrl: undefined,
+        };
+        managedProperties.ipAddressConfigType.activeValue = 'DHCP';
+        managedProperties.nameServersConfigType.activeValue = 'Static';
+        nameservers.managedProperties = managedProperties;
+        flush();
+
+        const customNameServerInput =
+            nameservers.shadowRoot!.querySelector<CrInputElement>(
+                'cr-input[id=nameserver0]');
+        assertTrue(!!customNameServerInput);
+        assertEquals(ipAddress, customNameServerInput.value);
+
+        managedProperties.staticIpConfig.nameServers!.activeValue =
+            ['0.0.0.2', '8.8.8.8', '0.0.0.0', '0.0.0.0'];
+        flush();
+
+        assertEquals(ipAddress, customNameServerInput.value);
+      });
+});
diff --git a/chrome/test/data/webui/cr_components/certificate_manager/local_certs_section_v2_test.ts b/chrome/test/data/webui/cr_components/certificate_manager/local_certs_section_v2_test.ts
index 0246446..dec891c 100644
--- a/chrome/test/data/webui/cr_components/certificate_manager/local_certs_section_v2_test.ts
+++ b/chrome/test/data/webui/cr_components/certificate_manager/local_certs_section_v2_test.ts
@@ -290,4 +290,36 @@
     const linkRow = customSection!.querySelector('cr-link-row');
     assertEquals('5 certificates', linkRow!.subLabel);
   });
+
+  // <if expr="not is_chromeos">
+  test('os certs set by policy, disable OS certs toggle', async () => {
+    const metadata: CertManagementMetadata = {
+      includeSystemTrustStore: true,
+      numUserAddedSystemCerts: 0,
+      isIncludeSystemTrustStoreManaged: true,
+      numPolicyCerts: 5,
+    };
+    testProxy.handler.setCertManagementMetadata(metadata);
+    initializeElement();
+
+    await testProxy.handler.whenCalled('getCertManagementMetadata');
+    await microtasksFinished();
+    assertTrue(localCertsSection.$.importOsCerts.disabled);
+  });
+
+  test('os certs not set by policy, enable OS certs toggle', async () => {
+    const metadata: CertManagementMetadata = {
+      includeSystemTrustStore: true,
+      numUserAddedSystemCerts: 0,
+      isIncludeSystemTrustStoreManaged: false,
+      numPolicyCerts: 5,
+    };
+    testProxy.handler.setCertManagementMetadata(metadata);
+    initializeElement();
+
+    await testProxy.handler.whenCalled('getCertManagementMetadata');
+    await microtasksFinished();
+    assertFalse(localCertsSection.$.importOsCerts.disabled);
+  });
+  // </if>
 });
diff --git a/chrome/test/data/webui/cr_components/chromeos/BUILD.gn b/chrome/test/data/webui/cr_components/chromeos/BUILD.gn
index a098e36..5070331 100644
--- a/chrome/test/data/webui/cr_components/chromeos/BUILD.gn
+++ b/chrome/test/data/webui/cr_components/chromeos/BUILD.gn
@@ -66,7 +66,6 @@
     "network/network_ip_config_test.js",
     "network/network_list_item_test.js",
     "network/network_list_test.js",
-    "network/network_nameservers_test.js",
     "network/network_password_input_test.js",
     "network/network_property_list_mojo_test.js",
     "network/network_proxy_exclusions_test.js",
diff --git a/chrome/test/data/webui/flags/app_test.ts b/chrome/test/data/webui/flags/app_test.ts
index 36c93eda..3dec3e0 100644
--- a/chrome/test/data/webui/flags/app_test.ts
+++ b/chrome/test/data/webui/flags/app_test.ts
@@ -218,16 +218,20 @@
     return promise.then(() => {
       assertFalse(isVisible(app.getRequiredElement('.no-match')));
       const noMatchMsg: NodeListOf<HTMLElement> =
-          app.$all('.tab-content .no-match');
+          app.shadowRoot!.querySelectorAll('.tab-content .no-match');
       assertTrue(!!noMatchMsg[0]);
       assertEquals(
           2,
-          app.$all(`#tab-content-available flags-experiment:not(.hidden)`)
+          app.shadowRoot!
+              .querySelectorAll(
+                  `#tab-content-available flags-experiment:not(.hidden)`)
               .length);
       assertTrue(!!noMatchMsg[1]);
       assertEquals(
           1,
-          app.$all(`#tab-content-unavailable flags-experiment:not(.hidden)`)
+          app.shadowRoot!
+              .querySelectorAll(
+                  `#tab-content-unavailable flags-experiment:not(.hidden)`)
               .length);
     });
   });
@@ -238,16 +242,20 @@
     return promise.then(() => {
       assertTrue(isVisible(app.getRequiredElement('.no-match')));
       const noMatchMsg: NodeListOf<HTMLElement> =
-          app.$all('.tab-content .no-match');
+          app.shadowRoot!.querySelectorAll('.tab-content .no-match');
       assertTrue(!!noMatchMsg[0]);
       assertEquals(
           0,
-          app.$all(`#tab-content-available flags-experiment:not(.hidden)`)
+          app.shadowRoot!
+              .querySelectorAll(
+                  `#tab-content-available flags-experiment:not(.hidden)`)
               .length);
       assertTrue(!!noMatchMsg[1]);
       assertEquals(
           0,
-          app.$all(`#tab-content-unavailable flags-experiment:not(.hidden)`)
+          app.shadowRoot!
+              .querySelectorAll(
+                  `#tab-content-unavailable flags-experiment:not(.hidden)`)
               .length);
     });
   });
diff --git a/chrome/test/data/webui/print_preview/native_layer_cros_stub.ts b/chrome/test/data/webui/print_preview/native_layer_cros_stub.ts
index 600f03a..c9d081a2 100644
--- a/chrome/test/data/webui/print_preview/native_layer_cros_stub.ts
+++ b/chrome/test/data/webui/print_preview/native_layer_cros_stub.ts
@@ -51,7 +51,6 @@
       'setupPrinter',
       'choosePrintServers',
       'getPrintServersConfig',
-      'recordPrinterStatusRetrySuccessHistogram',
       'getShowManagePrinters',
       'observeLocalPrinters',
     ]);
@@ -146,11 +145,6 @@
     return Promise.resolve(this.printServersConfig_!);
   }
 
-  recordPrinterStatusRetrySuccessHistogram(retrySuccessful: boolean) {
-    this.methodCalled(
-        'recordPrinterStatusRetrySuccessHistogram', retrySuccessful);
-  }
-
   recordPrintAttemptOutcome() {}
 
   setPrintServersConfig(printServersConfig: PrintServersConfig) {
diff --git a/chrome/test/data/webui/print_preview/printer_status_test.ts b/chrome/test/data/webui/print_preview/printer_status_test.ts
index b431b7f..c82951e 100644
--- a/chrome/test/data/webui/print_preview/printer_status_test.ts
+++ b/chrome/test/data/webui/print_preview/printer_status_test.ts
@@ -387,10 +387,6 @@
         .then(() => {
           assertEquals(
               PRINTER_ICON_GREY, getIconString(dropdown, destination.key));
-          assertEquals(
-              0,
-              nativeLayerCros.getCallCount(
-                  'recordPrinterStatusRetrySuccessHistogram'));
           return waitBeforeNextRender(destinationSelect);
         })
         .then(() => {
@@ -399,14 +395,6 @@
               2, nativeLayerCros.getCallCount('requestPrinterStatusUpdate'));
           assertEquals(
               PRINTER_ICON_GREEN, getIconString(dropdown, destination.key));
-          assertEquals(
-              1,
-              nativeLayerCros.getCallCount(
-                  'recordPrinterStatusRetrySuccessHistogram'));
-          assertEquals(
-              true,
-              nativeLayerCros.getArgs(
-                  'recordPrinterStatusRetrySuccessHistogram')[0]);
         });
   });
 
diff --git a/chrome/test/data/webui/settings/settings_browsertest.cc b/chrome/test/data/webui/settings/settings_browsertest.cc
index 2c2eb3fa..24728e7 100644
--- a/chrome/test/data/webui/settings/settings_browsertest.cc
+++ b/chrome/test/data/webui/settings/settings_browsertest.cc
@@ -12,6 +12,7 @@
 #include "chrome/test/base/web_ui_mocha_browser_test.h"
 #include "components/compose/buildflags.h"
 #include "components/content_settings/core/common/features.h"
+#include "components/optimization_guide/core/optimization_guide_features.h"
 #include "components/performance_manager/public/features.h"
 #include "components/permissions/features.h"
 #include "components/privacy_sandbox/privacy_sandbox_features.h"
@@ -162,7 +163,7 @@
 class SettingsAiPageTest : public SettingsBrowserTest {
  private:
   base::test::ScopedFeatureList scoped_feature_list_{
-      features::kAiSettingsPageRefresh};
+      optimization_guide::features::kAiSettingsPageRefresh};
 };
 
 IN_PROC_BROWSER_TEST_F(SettingsAiPageTest, ExperimentalAdvancedPage) {
diff --git a/chromeos/ash/components/dbus/fwupd/dbus_constants.h b/chromeos/ash/components/dbus/fwupd/dbus_constants.h
index c1e3af2..59542d2 100644
--- a/chromeos/ash/components/dbus/fwupd/dbus_constants.h
+++ b/chromeos/ash/components/dbus/fwupd/dbus_constants.h
@@ -74,9 +74,17 @@
 // "1" is the bitflag for an internal device. Defined here:
 // https://github.com/fwupd/fwupd/blob/main/libfwupd/fwupd-enums.h
 const uint64_t kInternalDeviceFlag = 1;
+// "100000000"(9th bit) is the bit representing FWUPD_DEVICE_FLAG_NEEDS_REBOOT
+// in FwupdDeviceFlags. It signifies whether the device requires a system reboot
+// to apply a firmware update.
+// Defined here:
+// https://github.com/fwupd/fwupd/blob/main/libfwupd/fwupd-enums.h#L304-L311
+const uint64_t kNeedsRebootDeviceFlag = 1llu << 8;
+
 // "100000000"(9th bit) is the bit release flag for a trusted report.
 // Defined here: https://github.com/fwupd/fwupd/blob/main/libfwupd/fwupd-enums.h
 const uint64_t kTrustedReportsReleaseFlag = 1llu << 8;
+
 // "10000"(5th bit) is the fwupd feature flag to allow interactive requests.
 // Defined here: https://github.com/fwupd/fwupd/blob/main/libfwupd/fwupd-enums.h
 const uint64_t kRequestsFeatureFlag = 1llu << 4;
diff --git a/chromeos/ash/components/dbus/fwupd/fake_fwupd_client.cc b/chromeos/ash/components/dbus/fwupd/fake_fwupd_client.cc
index b3078e5d..50499e3 100644
--- a/chromeos/ash/components/dbus/fwupd/fake_fwupd_client.cc
+++ b/chromeos/ash/components/dbus/fwupd/fake_fwupd_client.cc
@@ -36,7 +36,8 @@
 
   // Add a fake device.
   devices.emplace_back(/*id=*/kFakeDeviceIdForTesting,
-                       /*device_name=*/"fake_device");
+                       /*device_name=*/"fake_device",
+                       /*needs_reboot*/ false);
 
   for (auto& observer : observers_)
     observer.OnDeviceListResponse(&devices);
diff --git a/chromeos/ash/components/dbus/fwupd/fwupd_client.cc b/chromeos/ash/components/dbus/fwupd/fwupd_client.cc
index 92516ab..2734c4b 100644
--- a/chromeos/ash/components/dbus/fwupd/fwupd_client.cc
+++ b/chromeos/ash/components/dbus/fwupd/fwupd_client.cc
@@ -46,6 +46,8 @@
 
 // Dict key for the IsInternal device flag.
 const char kIsInternalKey[] = "IsInternal";
+// Dict key for the Reboot device flag.
+const char kNeedsRebootKey[] = "NeedsReboot";
 // Dict key for the HasTrustedReport release flag.
 const char kHasTrustedReportKey[] = "HasTrustedReport";
 
@@ -345,8 +347,10 @@
           const bool is_internal =
               (value_uint64 & kInternalDeviceFlag) == kInternalDeviceFlag;
           result.Set(kIsInternalKey, is_internal);
-        }
-        if (key == "TrustFlags") {
+          const bool needs_reboot =
+              (value_uint64 & kNeedsRebootDeviceFlag) == kNeedsRebootDeviceFlag;
+          result.Set(kNeedsRebootKey, needs_reboot);
+        } else if (key == "TrustFlags") {
           uint64_t value_uint64 = 0;
           variant_reader.PopUint64(&value_uint64);
           const bool has_trusted_report =
@@ -524,8 +528,10 @@
         return;
       }
 
+      std::optional<bool> needs_reboot = dict.FindBool(kNeedsRebootKey);
+
       FIRMWARE_LOG(DEBUG) << "fwupd: Device found: " << *id << " " << *name;
-      devices.emplace_back(*id, *name);
+      devices.emplace_back(*id, *name, needs_reboot.value_or(false));
     }
 
     FIRMWARE_LOG(USER) << "fwupd: Devices found: " << devices.size();
diff --git a/chromeos/ash/components/dbus/fwupd/fwupd_client_unittest.cc b/chromeos/ash/components/dbus/fwupd/fwupd_client_unittest.cc
index 7ff08c1..02a8d0f 100644
--- a/chromeos/ash/components/dbus/fwupd/fwupd_client_unittest.cc
+++ b/chromeos/ash/components/dbus/fwupd/fwupd_client_unittest.cc
@@ -31,8 +31,10 @@
 namespace {
 const char kFakeDeviceIdForTesting[] = "0123";
 const char kFakeDeviceNameForTesting[] = "Fake Device";
+const bool kFakeNeedsRebootForTesting = false;
 const char kFakeInternalDeviceIdForTesting[] = "4567";
 const char kFakeInternalDeviceNameForTesting[] = "Fake Internal Device";
+const bool kFakeInternalNeedsRebootForTesting = true;
 const char kFakeUpdateVersionForTesting[] = "1.0.0";
 const char kFakeUpdateDescriptionForTesting[] =
     "This is a fake update for testing.";
@@ -332,7 +334,8 @@
 
     device_array_writer.OpenDictEntry(&dict_writer);
     dict_writer.AppendString(kFlagsKey);
-    dict_writer.AppendVariantOfUint64(kInternalDeviceFlag);
+    dict_writer.AppendVariantOfUint64(kInternalDeviceFlag |
+                                      kNeedsRebootDeviceFlag);
     device_array_writer.CloseContainer(&dict_writer);
 
     response_array_writer.CloseContainer(&device_array_writer);
@@ -345,7 +348,8 @@
     run_loop_.Quit();
 
     FwupdDeviceList expected_devices = {
-        FwupdDevice(kFakeDeviceIdForTesting, kFakeDeviceNameForTesting)};
+        FwupdDevice(kFakeDeviceIdForTesting, kFakeDeviceNameForTesting,
+                    kFakeNeedsRebootForTesting)};
     EXPECT_EQ(*devices, expected_devices);
   }
 
@@ -353,9 +357,11 @@
     run_loop_.Quit();
 
     FwupdDeviceList expected_devices = {
-        FwupdDevice(kFakeDeviceIdForTesting, kFakeDeviceNameForTesting),
+        FwupdDevice(kFakeDeviceIdForTesting, kFakeDeviceNameForTesting,
+                    kFakeNeedsRebootForTesting),
         FwupdDevice(kFakeInternalDeviceIdForTesting,
-                    kFakeInternalDeviceNameForTesting),
+                    kFakeInternalDeviceNameForTesting,
+                    kFakeInternalNeedsRebootForTesting),
     };
     EXPECT_EQ(*devices, expected_devices);
   }
diff --git a/chromeos/ash/components/dbus/fwupd/fwupd_device.cc b/chromeos/ash/components/dbus/fwupd/fwupd_device.cc
index 91a7d935..f59c3b51 100644
--- a/chromeos/ash/components/dbus/fwupd/fwupd_device.cc
+++ b/chromeos/ash/components/dbus/fwupd/fwupd_device.cc
@@ -8,8 +8,10 @@
 
 FwupdDevice::FwupdDevice() = default;
 
-FwupdDevice::FwupdDevice(const std::string& id, const std::string& device_name)
-    : id(id), device_name(device_name) {}
+FwupdDevice::FwupdDevice(const std::string& id,
+                         const std::string& device_name,
+                         bool needs_reboot)
+    : id(id), device_name(device_name), needs_reboot(needs_reboot) {}
 
 FwupdDevice::FwupdDevice(const FwupdDevice& other) = default;
 FwupdDevice& FwupdDevice::operator=(const FwupdDevice& other) = default;
diff --git a/chromeos/ash/components/dbus/fwupd/fwupd_device.h b/chromeos/ash/components/dbus/fwupd/fwupd_device.h
index 9d87c01..7466935 100644
--- a/chromeos/ash/components/dbus/fwupd/fwupd_device.h
+++ b/chromeos/ash/components/dbus/fwupd/fwupd_device.h
@@ -15,7 +15,9 @@
 // Structure to hold FwupdDevice data received from fwupd.
 struct COMPONENT_EXPORT(ASH_DBUS_FWUPD) FwupdDevice {
   FwupdDevice();
-  FwupdDevice(const std::string& id, const std::string& device_name);
+  FwupdDevice(const std::string& id,
+              const std::string& device_name,
+              bool needs_reboot);
   FwupdDevice(const FwupdDevice& other);
   FwupdDevice& operator=(const FwupdDevice& other);
   ~FwupdDevice();
@@ -24,6 +26,7 @@
 
   std::string id;
   std::string device_name;
+  bool needs_reboot;
 };
 
 using FwupdDeviceList = std::vector<FwupdDevice>;
diff --git a/chromeos/crosapi/mojom/BUILD.gn b/chromeos/crosapi/mojom/BUILD.gn
index d0ac1e1..57ed727 100644
--- a/chromeos/crosapi/mojom/BUILD.gn
+++ b/chromeos/crosapi/mojom/BUILD.gn
@@ -110,7 +110,6 @@
     "resource_manager.mojom",
     "screen_ai_downloader.mojom",
     "screen_manager.mojom",
-    "select_file.mojom",
     "sharesheet.mojom",
     "smart_reader.mojom",
     "speech_recognition.mojom",
diff --git a/chromeos/crosapi/mojom/crosapi.mojom b/chromeos/crosapi/mojom/crosapi.mojom
index 9146b2fe..9db46e9 100644
--- a/chromeos/crosapi/mojom/crosapi.mojom
+++ b/chromeos/crosapi/mojom/crosapi.mojom
@@ -102,7 +102,6 @@
 import "chromeos/crosapi/mojom/resource_manager.mojom";
 import "chromeos/crosapi/mojom/screen_ai_downloader.mojom";
 import "chromeos/crosapi/mojom/screen_manager.mojom";
-import "chromeos/crosapi/mojom/select_file.mojom";
 import "chromeos/crosapi/mojom/sharesheet.mojom";
 import "chromeos/crosapi/mojom/smart_reader.mojom";
 import "chromeos/crosapi/mojom/speech_recognition.mojom";
@@ -662,10 +661,6 @@
   // Added in M86.
   BindScreenManager@1(pending_receiver<ScreenManager> receiver);
 
-  // Deprecated.
-  // Added in M86.
-  REMOVED_0@0(pending_receiver<SelectFileDeprecated> receiver);
-
   // Binds the SensorHalClient interface for IIO sensors' data.
   // Added in M90.
   [MinVersion=14]
diff --git a/chromeos/crosapi/mojom/select_file.mojom b/chromeos/crosapi/mojom/select_file.mojom
deleted file mode 100644
index b4d70bd..0000000
--- a/chromeos/crosapi/mojom/select_file.mojom
+++ /dev/null
@@ -1,151 +0,0 @@
-// Copyright 2020 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-module crosapi.mojom;
-
-import "mojo/public/mojom/base/file_path.mojom";
-import "mojo/public/mojom/base/string16.mojom";
-import "url/mojom/url.mojom";
-
-// Deprecated.
-[Extensible, RenamedFrom="crosapi.mojom.SelectFileDialogType"]
-enum SelectFileDialogTypeDeprecated {
-  // For opening a file.
-  [Default] kOpenFile = 0,
-
-  // Like kOpenFile, but allowing multiple files to open.
-  kOpenMultiFile = 1,
-
-  // For opening a folder.
-  kFolder = 2,
-
-  // Like kFolder, but the dialog UI shows it's specifically for "upload".
-  kUploadFolder = 3,
-
-  // Like kFolder, but folder creation is disabled.
-  kExistingFolder = 4,
-
-  // For saving into a file, allowing a nonexistent file to be selected.
-  kSaveAsFile = 5,
-};
-
-// Deprecated.
-[Extensible, RenamedFrom="crosapi.mojom.AllowedPaths"]
-enum AllowedPathsDeprecated {
-  // Any type of path, whether local/native or remote/virtual.
-  [Default] kAnyPath,
-  // Set when the caller cannot handle virtual volumes (e.g. File System
-  // Provider [FSP] volumes like "File System for Dropbox"). When opening files,
-  // the dialog will create a native replica of the file and return its path.
-  // When saving files, the dialog will hide virtual volumes.
-  kNativePath,
-  // Set when the caller can open files via URL. For example, when opening a
-  // .gdoc file from Google Drive the file is "opened" by navigating to
-  // docs.google.com.
-  kAnyPathOrUrl,
-};
-
-// Deprecated.
-[RenamedFrom="crosapi.mojom.SelectFileTypeInfo"]
-struct SelectFileTypeInfoDeprecated {
-  // The list of allowed file extensions. For example:
-  // { { "htm", "html" }, { "txt" } }
-  // Only pass more than one extension in the inner vector if the extensions
-  // are equivalent.
-  array<array<string>> REMOVED_0@0;
-
-  // Overrides the system descriptions of specified extensions. For example,
-  // when saving a web page, "Webpage, HTML only" is used for {"htm", "html"}
-  // and "Webpage, Single File" is used for {"mhtml"}. Length must match the
-  // length of |extensions|. If empty or if the length doesn't match then
-  // the system descriptions will be used.
-  array<mojo_base.mojom.String16> REMOVED_1@1;
-
-  // One-based index of the file type (in the extensions list) to select by
-  // default. Zero means no selection. (The index is one-based for consistency
-  // with the C++ code and because mojo doesn't allow nullable primitive types.)
-  // For example, when saving a web page, we use the index of {"mhtml"} so the
-  // user sees "Webpage, Single File" as the default choice.
-  int32 REMOVED_2@2;
-
-  // Whether or not there will be a filter added for all files (*.*).
-  bool REMOVED_3@3;
-
-  // Types of paths/volumes allowed. Unrelated to file extensions.
-  AllowedPathsDeprecated REMOVED_4@4;
-};
-
-// Deprecated.
-[RenamedFrom="crosapi.mojom.SelectFileOptions"]
-struct SelectFileOptionsDeprecated {
-  // Dialog type.
-  SelectFileDialogTypeDeprecated REMOVED_0@0;
-
-  // Window title.
-  mojo_base.mojom.String16 REMOVED_1@1;
-
-  // Default path to open.
-  mojo_base.mojom.FilePath REMOVED_2@2;
-
-  // Optional file type configuration. In the common case for file open (Ctrl-O)
-  // this is null.
-  SelectFileTypeInfoDeprecated? REMOVED_3@3;
-
-  // Internal window ID for the top-level shell window that owns the dialog.
-  // If provided, the dialog will be window-modal to that shell window.
-  // If empty, a modeless dialog will be opened on the default display.
-  string REMOVED_4@4;
-
-  // Optional URL of the caller that created the dialog.
-  [MinVersion=1]url.mojom.Url? REMOVED_5@5;
-};
-
-// Deprecated.
-[Extensible, RenamedFrom="crosapi.mojom.SelectFileResult"]
-enum SelectFileResultDeprecated {
-  // Everything worked.
-  [Default] kSuccess = 0,
-
-  // Failed because the dialog couldn't find the owning top-level window.
-  kInvalidShellWindow = 1,
-};
-
-// Deprecated.
-[RenamedFrom="crosapi.mojom.SelectedFileInfo"]
-struct SelectedFileInfoDeprecated {
-  // Path to the file, as seen in the UI.
-  mojo_base.mojom.FilePath REMOVED_0@0;
-
-  // The actual local path to the selected file. This can be a snapshot file
-  // with a human unreadable name like /blah/.d41d8cd98f00b204e9800998ecf8427e.
-  // |local_path| can differ from |file_path| for drive files (e.g.
-  // /drive_cache/temporary/d41d8cd98f00b204e9800998ecf8427e vs.
-  // /special/drive/foo.txt).
-  mojo_base.mojom.FilePath REMOVED_1@1;
-
-  // Optional display name of the file. UTF-8 encoded. If empty, the UI should
-  // display the base name portion of |file_path|.
-  string REMOVED_2@2;
-
-  // Optional URL to access the file. If the caller can use a URL to access a
-  // file, |url| should be used instead of |local_path|. For example, when
-  // opening a .gdoc file from Google Drive the |local_path| is a placeholder
-  // file that should be "opened" by navigating to docs.google.com.
-  url.mojom.Url? REMOVED_3@3;
-};
-
-// Deprecated.
-[Uuid="8c0eb5f3-4c90-4bfd-8a0f-377c99f09125",
-    RenamedFrom="crosapi.mojom.SelectFile"]
-interface SelectFileDeprecated {
-  // Selects one or more files. If the dialog is closed or canceled without
-  // selecting a file, or if there is an error, |files| will be empty.
-  // When saving a file or opening a single file, |file_type_index| is the
-  // one-based index of the selected file type from the SelectFileTypeInfo
-  // extension list, otherwise it is zero.
-  Select@0(SelectFileOptionsDeprecated options) =>
-      (SelectFileResultDeprecated result,
-       array<SelectedFileInfoDeprecated> files,
-       int32 file_type_index);
-};
diff --git a/chromeos/lacros/lacros_service.cc b/chromeos/lacros/lacros_service.cc
index 79711ef..db778c4 100644
--- a/chromeos/lacros/lacros_service.cc
+++ b/chromeos/lacros/lacros_service.cc
@@ -112,7 +112,6 @@
 #include "chromeos/crosapi/mojom/resource_manager.mojom.h"
 #include "chromeos/crosapi/mojom/screen_ai_downloader.mojom.h"
 #include "chromeos/crosapi/mojom/screen_manager.mojom.h"
-#include "chromeos/crosapi/mojom/select_file.mojom.h"
 #include "chromeos/crosapi/mojom/sharesheet.mojom.h"
 #include "chromeos/crosapi/mojom/smart_reader.mojom.h"
 #include "chromeos/crosapi/mojom/speech_recognition.mojom.h"
@@ -590,9 +589,6 @@
   ConstructRemote<crosapi::mojom::ScreenManager,
                   &crosapi::mojom::Crosapi::BindScreenManager,
                   Crosapi::MethodMinVersions::kBindScreenManagerMinVersion>();
-  ConstructRemote<crosapi::mojom::SelectFile,
-                  &crosapi::mojom::Crosapi::BindSelectFile,
-                  Crosapi::MethodMinVersions::kBindSelectFileMinVersion>();
   ConstructRemote<
       crosapi::mojom::SearchControllerRegistry,
       &crosapi::mojom::Crosapi::BindSearchControllerRegistry,
diff --git a/clank b/clank
index fd469e5..c26b94f 160000
--- a/clank
+++ b/clank
@@ -1 +1 @@
-Subproject commit fd469e55225096ec457b144a1e051ed0604552e2
+Subproject commit c26b94f4715522654c821b1ec6dbc77e241e1515
diff --git a/components/BUILD.gn b/components/BUILD.gn
index 201d300..dae11cc5 100644
--- a/components/BUILD.gn
+++ b/components/BUILD.gn
@@ -1273,6 +1273,7 @@
       "//components/background_task_scheduler:components_background_task_scheduler_junit_tests",
       "//components/browser_ui/bottomsheet/android/internal:junit_tests",
       "//components/browser_ui/client_certificate/android:junit",
+      "//components/browser_ui/desktop_windowing/android:junit",
       "//components/browser_ui/media/android:junit",
       "//components/browser_ui/photo_picker/android:junit",
       "//components/browser_ui/settings/android:junit",
diff --git a/components/browser_ui/desktop_windowing/android/BUILD.gn b/components/browser_ui/desktop_windowing/android/BUILD.gn
new file mode 100644
index 0000000..f449fc2
--- /dev/null
+++ b/components/browser_ui/desktop_windowing/android/BUILD.gn
@@ -0,0 +1,27 @@
+# Copyright 2024 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/config/android/rules.gni")
+
+android_library("java") {
+  sources = [
+    "java/src/org/chromium/components/browser_ui/desktop_windowing/AppHeaderState.java",
+    "java/src/org/chromium/components/browser_ui/desktop_windowing/DesktopWindowStateProvider.java",
+  ]
+
+  deps = [
+    "//base:base_java",
+    "//third_party/androidx:androidx_annotation_annotation_java",
+  ]
+}
+
+robolectric_library("junit") {
+  sources = [ "java/src/org/chromium/components/browser_ui/desktop_windowing/AppHeaderStateUnitTest.java" ]
+
+  deps = [
+    ":java",
+    "//base:base_junit_test_support",
+    "//third_party/junit:junit",
+  ]
+}
diff --git a/components/browser_ui/desktop_windowing/android/OWNERS b/components/browser_ui/desktop_windowing/android/OWNERS
new file mode 100644
index 0000000..c6038fbb
--- /dev/null
+++ b/components/browser_ui/desktop_windowing/android/OWNERS
@@ -0,0 +1,2 @@
+wenyufu@chromium.org
+aishwaryarj@google.com
diff --git a/chrome/browser/ui/android/desktop_windowing/java/src/org/chromium/chrome/browser/ui/desktop_windowing/AppHeaderState.java b/components/browser_ui/desktop_windowing/android/java/src/org/chromium/components/browser_ui/desktop_windowing/AppHeaderState.java
similarity index 97%
rename from chrome/browser/ui/android/desktop_windowing/java/src/org/chromium/chrome/browser/ui/desktop_windowing/AppHeaderState.java
rename to components/browser_ui/desktop_windowing/android/java/src/org/chromium/components/browser_ui/desktop_windowing/AppHeaderState.java
index 284161e..2507018 100644
--- a/chrome/browser/ui/android/desktop_windowing/java/src/org/chromium/chrome/browser/ui/desktop_windowing/AppHeaderState.java
+++ b/components/browser_ui/desktop_windowing/android/java/src/org/chromium/components/browser_ui/desktop_windowing/AppHeaderState.java
@@ -2,7 +2,7 @@
 // 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.ui.desktop_windowing;
+package org.chromium.components.browser_ui.desktop_windowing;
 
 import android.graphics.Rect;
 
diff --git a/chrome/browser/ui/android/desktop_windowing/java/src/org/chromium/chrome/browser/ui/desktop_windowing/AppHeaderStateUnitTest.java b/components/browser_ui/desktop_windowing/android/java/src/org/chromium/components/browser_ui/desktop_windowing/AppHeaderStateUnitTest.java
similarity index 98%
rename from chrome/browser/ui/android/desktop_windowing/java/src/org/chromium/chrome/browser/ui/desktop_windowing/AppHeaderStateUnitTest.java
rename to components/browser_ui/desktop_windowing/android/java/src/org/chromium/components/browser_ui/desktop_windowing/AppHeaderStateUnitTest.java
index 6d3596f..b89805d 100644
--- a/chrome/browser/ui/android/desktop_windowing/java/src/org/chromium/chrome/browser/ui/desktop_windowing/AppHeaderStateUnitTest.java
+++ b/components/browser_ui/desktop_windowing/android/java/src/org/chromium/components/browser_ui/desktop_windowing/AppHeaderStateUnitTest.java
@@ -2,7 +2,7 @@
 // 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.ui.desktop_windowing;
+package org.chromium.components.browser_ui.desktop_windowing;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
diff --git a/chrome/browser/ui/android/desktop_windowing/java/src/org/chromium/chrome/browser/ui/desktop_windowing/DesktopWindowStateProvider.java b/components/browser_ui/desktop_windowing/android/java/src/org/chromium/components/browser_ui/desktop_windowing/DesktopWindowStateProvider.java
similarity index 97%
rename from chrome/browser/ui/android/desktop_windowing/java/src/org/chromium/chrome/browser/ui/desktop_windowing/DesktopWindowStateProvider.java
rename to components/browser_ui/desktop_windowing/android/java/src/org/chromium/components/browser_ui/desktop_windowing/DesktopWindowStateProvider.java
index 08a5d10a..7c97ed3 100644
--- a/chrome/browser/ui/android/desktop_windowing/java/src/org/chromium/chrome/browser/ui/desktop_windowing/DesktopWindowStateProvider.java
+++ b/components/browser_ui/desktop_windowing/android/java/src/org/chromium/components/browser_ui/desktop_windowing/DesktopWindowStateProvider.java
@@ -2,7 +2,7 @@
 // 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.ui.desktop_windowing;
+package org.chromium.components.browser_ui.desktop_windowing;
 
 import androidx.annotation.ColorInt;
 
diff --git a/components/commerce/core/commerce_types.h b/components/commerce/core/commerce_types.h
index 12bc536..1f8abbd 100644
--- a/components/commerce/core/commerce_types.h
+++ b/components/commerce/core/commerce_types.h
@@ -228,6 +228,7 @@
     GURL image_url;
     std::map<ProductDimensionId, Value> product_dimension_values;
     std::vector<DescriptionText> summary;
+    GURL buying_options_url;
   };
 
   // A map of each product dimension ID to its human readable name.
diff --git a/components/commerce/core/compare/product_specifications_server_proxy.cc b/components/commerce/core/compare/product_specifications_server_proxy.cc
index 5fc810f..12a038c4 100644
--- a/components/commerce/core/compare/product_specifications_server_proxy.cc
+++ b/components/commerce/core/compare/product_specifications_server_proxy.cc
@@ -29,6 +29,7 @@
     "https://memex-pa.googleapis.com/v1/shopping/products:specifications";
 
 const char kAltTextKey[] = "alternativeText";
+const char kBuyingOptionsURLKey[] = "buyingOptionsUrl";
 const char kDescriptionKey[] = "description";
 const char kFaviconUrlKey[] = "faviconUrl";
 const char kGPCKey[] = "gpcId";
@@ -390,6 +391,12 @@
       product.image_url = GURL(*image_url);
     }
 
+    const std::string* buying_options_url =
+        spec.GetDict().FindString(kBuyingOptionsURLKey);
+    if (buying_options_url) {
+      product.buying_options_url = GURL(*buying_options_url);
+    }
+
     const base::Value::List* product_spec_values =
         spec.GetDict().FindList(kProductSpecificationValuesKey);
     if (!product_spec_values) {
diff --git a/components/commerce/core/compare/product_specifications_server_proxy_unittest.cc b/components/commerce/core/compare/product_specifications_server_proxy_unittest.cc
index e1fc95f..ff771fe 100644
--- a/components/commerce/core/compare/product_specifications_server_proxy_unittest.cc
+++ b/components/commerce/core/compare/product_specifications_server_proxy_unittest.cc
@@ -59,6 +59,7 @@
               }
             ],
             "imageUrl": "http://example.com/image.png",
+            "buyingOptionsUrl": "http://example.com/jackpot",
             "productSpecificationValues": [
               {
                 "key": "100000",
@@ -176,6 +177,8 @@
             ASSERT_EQ("Circle", spec->products[0].title);
             ASSERT_EQ("http://example.com/image.png",
                       spec->products[0].image_url.spec());
+            ASSERT_EQ("http://example.com/jackpot",
+                      spec->products[0].buying_options_url.spec());
             ASSERT_EQ("Circle is round", spec->products[0].summary[0].text);
             ASSERT_EQ("http://example.com/circle/",
                       spec->products[0].summary[0].urls[0].url.spec());
@@ -237,6 +240,8 @@
                      ASSERT_EQ("Circle", spec->products[0].title);
                      ASSERT_EQ("http://example.com/image.png",
                                spec->products[0].image_url.spec());
+                     ASSERT_EQ("http://example.com/jackpot",
+                               spec->products[0].buying_options_url.spec());
                      ASSERT_EQ("Circle is round",
                                spec->products[0].summary[0].text);
                      ASSERT_EQ("http://example.com/circle/",
diff --git a/components/flags_ui/resources/BUILD.gn b/components/flags_ui/resources/BUILD.gn
index 69b1695..9b8c80c6 100644
--- a/components/flags_ui/resources/BUILD.gn
+++ b/components/flags_ui/resources/BUILD.gn
@@ -13,7 +13,10 @@
     "experiment.ts",
     "app.ts",
   ]
-  css_files = [ "experiment.css" ]
+  css_files = [
+    "app.css",
+    "experiment.css",
+  ]
   html_to_wrapper_template = "detect"
 
   non_web_component_files = [ "flags_browser_proxy.ts" ]
diff --git a/components/flags_ui/resources/app.css b/components/flags_ui/resources/app.css
new file mode 100644
index 0000000..bad48ff
--- /dev/null
+++ b/components/flags_ui/resources/app.css
@@ -0,0 +1,483 @@
+/* Copyright 2024 The Chromium Authors
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file. */
+
+/* #css_wrapper_metadata_start
+ * #type=style-lit
+ * #scheme=relative
+ * #css_wrapper_metadata_end */
+
+:host {
+  color: var(--primary-color);
+  display: flex;
+  flex-direction: column;
+  font-size: 0.8125rem;
+  height: 100%;
+
+  --ease-in-out: cubic-bezier(0.4, 0.0, 0.2, 1);
+  --shadow-color: rgba(0, 0, 0, 0.1);
+
+  --google-blue-300-rgb: 138, 180, 248;
+  --google-blue-300: rgb(var(--google-blue-300-rgb));
+  --google-blue-400: rgb(102, 157, 246);
+  --google-blue-500-rgb: 66, 133, 244;
+  --google-blue-500: rgb(var(--google-blue-500-rgb));
+  --google-blue-600-rgb: 26, 115, 232;
+  --google-blue-600: rgb(var(--google-blue-600-rgb));
+  --google-blue-700: rgb(25, 103, 210);
+  --google-grey-100: rgb(241, 243, 244);
+  --google-grey-200-rgb: 232, 234, 237;
+  --google-grey-200: rgb(var(--google-grey-200-rgb));
+  --google-grey-300: rgb(218, 220, 224);
+  --google-grey-500: rgb(154, 160, 166);
+  --google-grey-700: rgb(95, 99, 104);
+  --google-grey-900-rgb: 32, 33, 36;
+  --google-grey-900: rgb(var(--google-grey-900-rgb));
+  /* --google-grey-900 + 4% white blended together. */
+  --google-grey-900-white-4-percent: #292a2d;
+  --google-red-300: rgb(242, 139, 130);
+  --google-red-700: rgb(197, 34, 31);
+
+  --interactive-color: var(--google-blue-600);
+  --primary-color: var(--google-grey-900);
+  --secondary-color: var(--google-grey-700);
+  --warning-color: var(--google-red-700);
+
+  --focus-shadow-color: rgba(var(--google-blue-600-rgb), 0.4);
+  --input-background: var(--google-grey-100);
+  --link-color: var(--google-blue-700);
+  --separator-color: rgba(0, 0, 0, .06);
+  --toolbar-color: white;
+
+  background: white;
+}
+
+@media (prefers-color-scheme: dark) {
+  :host {
+    --interactive-color: var(--google-blue-300);
+    --primary-color: var(--google-grey-200);
+    --secondary-color: var(--google-grey-500);
+    --warning-color: var(--google-red-300);
+
+    --focus-shadow-color: rgba(var(--google-blue-300-rgb), 0.4);
+    --input-background: rgba(0, 0, 0, .3);
+    --link-color: var(--google-blue-300);
+    --separator-color: rgba(255, 255, 255, .1);
+    --toolbar-color: var(--google-grey-900-white-4-percent);
+
+    background: var(--google-grey-900);
+  }
+}
+
+a {
+  color: var(--link-color);
+  font-size: .8125rem;
+}
+
+h1 {
+  color: var(--primary-color);
+  font-size: 1.4625rem;
+  font-weight: 400;
+  margin-top: 0;
+  padding: 0;
+}
+
+button {
+  cursor: pointer;
+  font-weight: 500;
+}
+
+#body-container {
+  flex: 1;
+  /* Force the vertical scrollbar to always be displayed, to avoid UI jumps.
+  */
+  overflow-y: scroll;
+}
+
+#flagsTemplate {
+  --side-padding: 20px;
+  box-sizing: border-box;
+  padding: 1rem var(--side-padding) 8px;
+  margin: 0 auto;
+  max-width: calc(700px + 2 * var(--side-padding));
+  width: 100%;
+}
+
+:focus-visible {
+  box-shadow: 0 0 0 2px var(--focus-shadow-color);
+  outline: none;
+}
+
+@media (forced-colors: active) {
+  :focus-visible {
+    /* Outline is needed for Windows HCM. Color of outline does not matter; it
+      * is overridden by the OS. */
+    box-shadow: none;
+    outline: 2px solid transparent;
+  }
+}
+
+#flags-app-container {
+  display: flex;
+  flex-direction: column;
+  height: 100%;
+}
+
+@media (prefers-color-scheme: dark) {
+  #flagsTemplate {
+    background: rgba(255, 255, 255, .04);
+  }
+}
+
+.flex {
+  align-self: center;
+  flex: 1 1 auto;
+}
+
+.flex-container {
+  display: flex;
+  padding: 8px 1em;
+}
+
+#flagsTemplate>.flex-container:first-child {
+  padding: 0;
+}
+
+#header {
+  background: var(--toolbar-color);
+  box-shadow: 0 2px 2px 0 var(--shadow-color);
+  box-sizing: border-box;
+}
+
+@media (prefers-color-scheme: dark) {
+  #header {
+    border-bottom: 1px solid var(--separator-color);
+  }
+}
+
+#header .flex-container {
+  box-sizing: border-box;
+  margin: 0 auto;
+  max-width: 700px;
+}
+
+#header .flex-container .flex:first-child {
+  text-align: left;
+  /* csschecker-disable-line left-right */
+}
+
+#header .flex-container .flex:last-child {
+  text-align: right;
+  /* csschecker-disable-line left-right */
+}
+
+.hidden {
+  display: none;
+}
+
+.search-container {
+  margin-inline-end: 8px;
+  position: relative;
+}
+
+#search {
+  background: var(--input-background)
+      url(chrome://resources/images/icon_search.svg) no-repeat 8px 50%;
+  border: 1px solid transparent;
+  border-radius: 3px;
+  box-sizing: border-box;
+  color: inherit;
+  font-size: .8125rem;
+  padding: 12px 36px;
+  width: 100%;
+}
+
+#search::placeholder {
+  color: rgba(var(--google-grey-900-rgb), .71);
+}
+
+@media (prefers-color-scheme: dark) {
+  #search {
+    background-image: url(chrome://resources/images/dark/icon_search.svg);
+  }
+
+  #search::placeholder {
+    color: rgba(var(--google-grey-200-rgb), .51);
+  }
+}
+
+.clear-search {
+  -webkit-mask-image: url(chrome://resources/images/icon_cancel.svg);
+  -webkit-mask-size: 20px;
+  background: var(--secondary-color);
+  border: 0;
+  display: none;
+  height: 20px;
+  opacity: 0.5;
+  position: absolute;
+  right: 8px;
+  top: calc(50% - 10px);
+  width: 20px;
+}
+
+.clear-search:focus {
+  opacity: 1;
+  outline: 0;
+}
+
+:host(.searching) .clear-search {
+  display: inline-block;
+}
+
+:host(.searching) .blurb-container,
+:host(.searching) #promos {
+  display: none;
+}
+
+.blurb-container {
+  color: var(--primary-color);
+  font-size: .875rem;
+  line-height: 1.4;
+  margin-bottom: 1rem;
+}
+
+.blurb-warning {
+  color: var(--warning-color);
+  text-transform: uppercase;
+}
+
+.screen-reader-only {
+  clip: rect(0, 0, 0, 0);
+  display: inline-block;
+  position: fixed;
+}
+
+#promos {
+  color: var(--secondary-color);
+  font-size: .875rem;
+}
+
+button:not(.primary) {
+  background: none;
+  border: 1px solid var(--google-grey-300);
+  border-radius: 3px;
+  color: var(--google-blue-600);
+  font-size: .8125rem;
+  padding: 8px 12px;
+}
+
+@media (prefers-color-scheme: dark) {
+  button:not(.primary) {
+    border-color: var(--google-grey-700);
+    color: var(--google-blue-300);
+  }
+}
+
+button:not(.primary):hover {
+  background: rgba(var(--google-blue-500-rgb), .04);
+}
+
+@media (prefers-color-scheme: dark) {
+  button:not(.primary):hover {
+    background: rgba(var(--google-blue-300-rgb), .08);
+  }
+}
+
+.tabs {
+  display: flex;
+  width: 100%;
+}
+
+.tab {
+  color: var(--secondary-color);
+  cursor: pointer;
+  display: block;
+  flex: 1;
+  font-size: 1rem;
+  font-weight: normal;
+  padding: 14px 21px;
+  position: relative;
+  text-align: center;
+  text-decoration: none;
+  transition: color 200ms var(--ease-in-out);
+  user-select: none;
+  width: 50%;
+  z-index: 1;
+}
+
+.tab::before {
+  background: currentColor;
+  bottom: 0;
+  content: '';
+  display: block;
+  height: 1px;
+  left: 0;
+  position: absolute;
+  transition: height 200ms var(--ease-in-out);
+  width: 100%;
+}
+
+.tabs .tab.selected,
+.tabs .tab.selected:focus {
+  color: var(--interactive-color);
+}
+
+.tabs .tab.selected::before,
+.tabs .tab.selected:focus::before {
+  height: 2px;
+}
+
+html:not(.focus-outline-visible) .tab:focus {
+  outline: 0;
+}
+
+.tab-content {
+  display: none;
+  line-height: 25px;
+  /* Bottom padding should be greater than evaluated height of needs-restart
+  */
+  padding-bottom: 100px;
+  position: relative;
+}
+
+.tab-content.selected,
+.tab-content.selected .template {
+  display: block;
+}
+
+/* Restart tray. */
+#needs-restart {
+  background: var(--toolbar-color);
+  bottom: 0;
+  box-shadow: 0 -2px 2px 0 var(--shadow-color);
+  box-sizing: border-box;
+  left: 0;
+  opacity: 0;
+  padding: 16px;
+  position: fixed;
+  transform: translateY(300px);
+  transition: all 225ms var(--ease-in-out);
+  width: 100%;
+  z-index: 10;
+}
+
+#needs-restart .flex:last-child {
+  text-align: right;
+  /* csschecker-disable-line left-right */
+}
+
+#needs-restart.show {
+  opacity: 1;
+  transform: translate(0);
+}
+
+.no-match {
+  margin-top: 75px;
+}
+
+.restart-notice {
+  font-size: .9375rem;
+  line-height: 1.4;
+}
+
+button.primary {
+  background: var(--link-color);
+  border: 0;
+  border-radius: 3px;
+  color: white;
+  font-size: .875rem;
+  padding: 14px 38px;
+}
+
+@media (prefers-color-scheme: dark) {
+  button.primary {
+    color: var(--google-grey-900);
+  }
+}
+
+button.primary:-webkit-any(:active, :hover) {
+  background: var(--google-blue-600);
+}
+
+#version {
+  color: var(--secondary-color);
+  text-align: right;
+  /* csschecker-disable-line left-right */
+}
+
+<if expr="chromeos_lacros or chromeos_ash">
+#os-flags-link-container {
+  background-image: url(chrome://resources/images/os_flags_app_icon.svg);
+}
+</if>
+
+@media (max-width: 360px) {
+  #experiment-reset-all {
+    font-size: .65rem;
+    padding: 2px 8px;
+  }
+
+  #flagsTemplate>.flex-container {
+    flex-direction: column;
+  }
+
+  #version {
+    text-align: initial;
+  }
+
+  button.primary {
+    padding: 8px;
+  }
+
+  #search {
+    padding: 8px 36px;
+  }
+}
+
+@media (max-width: 480px) {
+  #flagsTemplate>.flex-container:first-child:not('.version') {
+    flex-direction: column;
+    text-align: left;
+    /* csschecker-disable-line left-right */
+  }
+
+  #flagsTemplate>.flex-container:first-child .flex {
+    width: 100%;
+  }
+
+  #needs-restart {
+    padding: 8px 12px;
+  }
+
+  button.primary {
+    padding: 8px 16px;
+  }
+
+  .restart-notice {
+    font-size: .8125rem;
+    padding: 4px;
+  }
+
+  :host(.searching) flags-experiment::part(body) {
+    overflow: visible;
+    white-space: normal;
+  }
+}
+
+@media (max-width: 732px) {
+  #version,
+  .blurb-warning {
+    display: block;
+    margin-bottom: 1rem;
+  }
+}
+
+@media (max-height: 400px) {
+  #header {
+    position: relative;
+  }
+
+  #flagsTemplate {
+    padding-top: 1.5rem;
+  }
+}
diff --git a/components/flags_ui/resources/app.html b/components/flags_ui/resources/app.html
index f492bd3..ea48a57 100644
--- a/components/flags_ui/resources/app.html
+++ b/components/flags_ui/resources/app.html
@@ -1,479 +1,3 @@
-<style>
-  :host {
-    color: var(--primary-color);
-    display: flex;
-    flex-direction: column;
-    font-size: 0.8125rem;
-    height: 100%;
-
-    --ease-in-out: cubic-bezier(0.4, 0.0, 0.2, 1);
-    --shadow-color: rgba(0, 0, 0, 0.1);
-
-    --google-blue-300-rgb: 138, 180, 248;
-    --google-blue-300: rgb(var(--google-blue-300-rgb));
-    --google-blue-400: rgb(102, 157, 246);
-    --google-blue-500-rgb: 66, 133, 244;
-    --google-blue-500: rgb(var(--google-blue-500-rgb));
-    --google-blue-600-rgb: 26, 115, 232;
-    --google-blue-600: rgb(var(--google-blue-600-rgb));
-    --google-blue-700: rgb(25, 103, 210);
-    --google-grey-100: rgb(241, 243, 244);
-    --google-grey-200-rgb: 232, 234, 237;
-    --google-grey-200: rgb(var(--google-grey-200-rgb));
-    --google-grey-300: rgb(218, 220, 224);
-    --google-grey-500: rgb(154, 160, 166);
-    --google-grey-700: rgb(95, 99, 104);
-    --google-grey-900-rgb: 32, 33, 36;
-    --google-grey-900: rgb(var(--google-grey-900-rgb));
-    /* --google-grey-900 + 4% white blended together. */
-    --google-grey-900-white-4-percent: #292a2d;
-    --google-red-300: rgb(242, 139, 130);
-    --google-red-700: rgb(197, 34, 31);
-
-    --interactive-color: var(--google-blue-600);
-    --primary-color: var(--google-grey-900);
-    --secondary-color: var(--google-grey-700);
-    --warning-color: var(--google-red-700);
-
-    --focus-shadow-color: rgba(var(--google-blue-600-rgb), 0.4);
-    --input-background: var(--google-grey-100);
-    --link-color: var(--google-blue-700);
-    --separator-color: rgba(0, 0, 0, .06);
-    --toolbar-color: white;
-
-    background: white;
-  }
-
-  @media (prefers-color-scheme: dark) {
-    :host {
-      --interactive-color: var(--google-blue-300);
-      --primary-color: var(--google-grey-200);
-      --secondary-color: var(--google-grey-500);
-      --warning-color: var(--google-red-300);
-
-      --focus-shadow-color: rgba(var(--google-blue-300-rgb), 0.4);
-      --input-background: rgba(0, 0, 0, .3);
-      --link-color: var(--google-blue-300);
-      --separator-color: rgba(255, 255, 255, .1);
-      --toolbar-color: var(--google-grey-900-white-4-percent);
-
-      background: var(--google-grey-900);
-    }
-  }
-
-  a {
-    color: var(--link-color);
-    font-size: .8125rem;
-  }
-
-  h1 {
-    color: var(--primary-color);
-    font-size: 1.4625rem;
-    font-weight: 400;
-    margin-top: 0;
-    padding: 0;
-  }
-
-  button {
-    cursor: pointer;
-    font-weight: 500;
-  }
-
-  #body-container {
-    flex: 1;
-    /* Force the vertical scrollbar to always be displayed, to avoid UI jumps.
-    */
-    overflow-y: scroll;
-  }
-
-  #flagsTemplate {
-    --side-padding: 20px;
-    box-sizing: border-box;
-    padding: 1rem var(--side-padding) 8px;
-    margin: 0 auto;
-    max-width: calc(700px + 2 * var(--side-padding));
-    width: 100%;
-  }
-
-  :focus-visible {
-    box-shadow: 0 0 0 2px var(--focus-shadow-color);
-    outline: none;
-  }
-
-  @media (forced-colors: active) {
-    :focus-visible {
-      /* Outline is needed for Windows HCM. Color of outline does not matter; it
-        * is overridden by the OS. */
-      box-shadow: none;
-      outline: 2px solid transparent;
-    }
-  }
-
-  #flags-app-container {
-    display: flex;
-    flex-direction: column;
-    height: 100%;
-  }
-
-  @media (prefers-color-scheme: dark) {
-    #flagsTemplate {
-      background: rgba(255, 255, 255, .04);
-    }
-  }
-
-  .flex {
-    align-self: center;
-    flex: 1 1 auto;
-  }
-
-  .flex-container {
-    display: flex;
-    padding: 8px 1em;
-  }
-
-  #flagsTemplate>.flex-container:first-child {
-    padding: 0;
-  }
-
-  #header {
-    background: var(--toolbar-color);
-    box-shadow: 0 2px 2px 0 var(--shadow-color);
-    box-sizing: border-box;
-  }
-
-  @media (prefers-color-scheme: dark) {
-    #header {
-      border-bottom: 1px solid var(--separator-color);
-    }
-  }
-
-  #header .flex-container {
-    box-sizing: border-box;
-    margin: 0 auto;
-    max-width: 700px;
-  }
-
-  #header .flex-container .flex:first-child {
-    text-align: left;
-    /* csschecker-disable-line left-right */
-  }
-
-  #header .flex-container .flex:last-child {
-    text-align: right;
-    /* csschecker-disable-line left-right */
-  }
-
-  .hidden {
-    display: none;
-  }
-
-  .search-container {
-    margin-inline-end: 8px;
-    position: relative;
-  }
-
-  #search {
-    background: var(--input-background)
-        url(chrome://resources/images/icon_search.svg) no-repeat 8px 50%;
-    border: 1px solid transparent;
-    border-radius: 3px;
-    box-sizing: border-box;
-    color: inherit;
-    font-size: .8125rem;
-    padding: 12px 36px;
-    width: 100%;
-  }
-
-  #search::placeholder {
-    color: rgba(var(--google-grey-900-rgb), .71);
-  }
-
-  @media (prefers-color-scheme: dark) {
-    #search {
-      background-image: url(chrome://resources/images/dark/icon_search.svg);
-    }
-
-    #search::placeholder {
-      color: rgba(var(--google-grey-200-rgb), .51);
-    }
-  }
-
-  .clear-search {
-    -webkit-mask-image: url(chrome://resources/images/icon_cancel.svg);
-    -webkit-mask-size: 20px;
-    background: var(--secondary-color);
-    border: 0;
-    display: none;
-    height: 20px;
-    opacity: 0.5;
-    position: absolute;
-    right: 8px;
-    top: calc(50% - 10px);
-    width: 20px;
-  }
-
-  .clear-search:focus {
-    opacity: 1;
-    outline: 0;
-  }
-
-  :host(.searching) .clear-search {
-    display: inline-block;
-  }
-
-  :host(.searching) .blurb-container,
-  :host(.searching) #promos {
-    display: none;
-  }
-
-  .blurb-container {
-    color: var(--primary-color);
-    font-size: .875rem;
-    line-height: 1.4;
-    margin-bottom: 1rem;
-  }
-
-  .blurb-warning {
-    color: var(--warning-color);
-    text-transform: uppercase;
-  }
-
-  .screen-reader-only {
-    clip: rect(0, 0, 0, 0);
-    display: inline-block;
-    position: fixed;
-  }
-
-  #promos {
-    color: var(--secondary-color);
-    font-size: .875rem;
-  }
-
-  button:not(.primary) {
-    background: none;
-    border: 1px solid var(--google-grey-300);
-    border-radius: 3px;
-    color: var(--google-blue-600);
-    font-size: .8125rem;
-    padding: 8px 12px;
-  }
-
-  @media (prefers-color-scheme: dark) {
-    button:not(.primary) {
-      border-color: var(--google-grey-700);
-      color: var(--google-blue-300);
-    }
-  }
-
-  button:not(.primary):hover {
-    background: rgba(var(--google-blue-500-rgb), .04);
-  }
-
-  @media (prefers-color-scheme: dark) {
-    button:not(.primary):hover {
-      background: rgba(var(--google-blue-300-rgb), .08);
-    }
-  }
-
-  .tabs {
-    display: flex;
-    width: 100%;
-  }
-
-  .tab {
-    color: var(--secondary-color);
-    cursor: pointer;
-    display: block;
-    flex: 1;
-    font-size: 1rem;
-    font-weight: normal;
-    padding: 14px 21px;
-    position: relative;
-    text-align: center;
-    text-decoration: none;
-    transition: color 200ms var(--ease-in-out);
-    user-select: none;
-    width: 50%;
-    z-index: 1;
-  }
-
-  .tab::before {
-    background: currentColor;
-    bottom: 0;
-    content: '';
-    display: block;
-    height: 1px;
-    left: 0;
-    position: absolute;
-    transition: height 200ms var(--ease-in-out);
-    width: 100%;
-  }
-
-  .tabs .tab.selected,
-  .tabs .tab.selected:focus {
-    color: var(--interactive-color);
-  }
-
-  .tabs .tab.selected::before,
-  .tabs .tab.selected:focus::before {
-    height: 2px;
-  }
-
-  html:not(.focus-outline-visible) .tab:focus {
-    outline: 0;
-  }
-
-  .tab-content {
-    display: none;
-    line-height: 25px;
-    /* Bottom padding should be greater than evaluated height of needs-restart
-    */
-    padding-bottom: 100px;
-    position: relative;
-  }
-
-  .tab-content.selected,
-  .tab-content.selected .template {
-    display: block;
-  }
-
-  /* Restart tray. */
-  #needs-restart {
-    background: var(--toolbar-color);
-    bottom: 0;
-    box-shadow: 0 -2px 2px 0 var(--shadow-color);
-    box-sizing: border-box;
-    left: 0;
-    opacity: 0;
-    padding: 16px;
-    position: fixed;
-    transform: translateY(300px);
-    transition: all 225ms var(--ease-in-out);
-    width: 100%;
-    z-index: 10;
-  }
-
-  #needs-restart .flex:last-child {
-    text-align: right;
-    /* csschecker-disable-line left-right */
-  }
-
-  #needs-restart.show {
-    opacity: 1;
-    transform: translate(0);
-  }
-
-  .no-match {
-    margin-top: 75px;
-  }
-
-  .restart-notice {
-    font-size: .9375rem;
-    line-height: 1.4;
-  }
-
-  button.primary {
-    background: var(--link-color);
-    border: 0;
-    border-radius: 3px;
-    color: white;
-    font-size: .875rem;
-    padding: 14px 38px;
-  }
-
-  @media (prefers-color-scheme: dark) {
-    button.primary {
-      color: var(--google-grey-900);
-    }
-  }
-
-  button.primary:-webkit-any(:active, :hover) {
-    background: var(--google-blue-600);
-  }
-
-  #version {
-    color: var(--secondary-color);
-    text-align: right;
-    /* csschecker-disable-line left-right */
-  }
-
-<if expr="chromeos_lacros or chromeos_ash">
-  #os-flags-link-container {
-    background-image: url(chrome://resources/images/os_flags_app_icon.svg);
-  }
-</if>
-
-  @media (max-width: 360px) {
-    #experiment-reset-all {
-      font-size: .65rem;
-      padding: 2px 8px;
-    }
-
-    #flagsTemplate>.flex-container {
-      flex-direction: column;
-    }
-
-    #version {
-      text-align: initial;
-    }
-
-    button.primary {
-      padding: 8px;
-    }
-
-    #search {
-      padding: 8px 36px;
-    }
-  }
-
-  @media (max-width: 480px) {
-    #flagsTemplate>.flex-container:first-child:not('.version') {
-      flex-direction: column;
-      text-align: left;
-      /* csschecker-disable-line left-right */
-    }
-
-    #flagsTemplate>.flex-container:first-child .flex {
-      width: 100%;
-    }
-
-    #needs-restart {
-      padding: 8px 12px;
-    }
-
-    button.primary {
-      padding: 8px 16px;
-    }
-
-    .restart-notice {
-      font-size: .8125rem;
-      padding: 4px;
-    }
-
-    :host(.searching) flags-experiment::part(body) {
-      overflow: visible;
-      white-space: normal;
-    }
-  }
-
-  @media (max-width: 732px) {
-    #version,
-    .blurb-warning {
-      display: block;
-      margin-bottom: 1rem;
-    }
-  }
-
-  @media (max-height: 400px) {
-    #header {
-      position: relative;
-    }
-
-    #flagsTemplate {
-      padding-top: 1.5rem;
-    }
-  }
-</style>
 <div id="header">
   <div class="flex-container">
     <div class="flex search-container">
diff --git a/components/flags_ui/resources/app.ts b/components/flags_ui/resources/app.ts
index a096eac..7516430 100644
--- a/components/flags_ui/resources/app.ts
+++ b/components/flags_ui/resources/app.ts
@@ -10,13 +10,15 @@
 import './experiment.js';
 
 import {assert} from 'chrome://resources/js/assert.js';
-import {CustomElement, emptyHTML} from 'chrome://resources/js/custom_element.js';
+import {emptyHTML} from 'chrome://resources/js/custom_element.js';
 import {FocusOutlineManager} from 'chrome://resources/js/focus_outline_manager.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {PromiseResolver} from 'chrome://resources/js/promise_resolver.js';
 import {getDeepActiveElement} from 'chrome://resources/js/util.js';
+import {CrLitElement} from 'chrome://resources/lit/v3_0/lit.rollup.js';
 
-import {getTemplate} from './app.html.js';
+import {getCss} from './app.css.js';
+import {getHtml} from './app.html.js';
 import type {ExperimentElement as FlagsExperimentElement} from './experiment.js';
 import type {ExperimentalFeaturesData, Feature} from './flags_browser_proxy.js';
 import {FlagsBrowserProxyImpl} from './flags_browser_proxy.js';
@@ -43,7 +45,8 @@
     this.flagsAppElement = el;
     this.searchBox =
         this.flagsAppElement.getRequiredElement<HTMLInputElement>('#search');
-    this.noMatchMsg = this.flagsAppElement.$all('.tab-content .no-match');
+    this.noMatchMsg = this.flagsAppElement.shadowRoot!.querySelectorAll(
+        '.tab-content .no-match');
   }
 
   /**
@@ -120,8 +123,9 @@
 
     // Available experiments
     const availableExperiments =
-        this.flagsAppElement.$all<FlagsExperimentElement>(
-            '#tab-content-available flags-experiment');
+        this.flagsAppElement.shadowRoot!
+            .querySelectorAll<FlagsExperimentElement>(
+                '#tab-content-available flags-experiment');
     assert(this.noMatchMsg[0]);
     this.noMatchMsg[0].classList.toggle(
         'hidden',
@@ -130,8 +134,9 @@
     // <if expr="not is_ios">
     // Unavailable experiments, which are undefined on iOS.
     const unavailableExperiments =
-        this.flagsAppElement.$all<FlagsExperimentElement>(
-            '#tab-content-unavailable flags-experiment');
+        this.flagsAppElement.shadowRoot!
+            .querySelectorAll<FlagsExperimentElement>(
+                '#tab-content-unavailable flags-experiment');
     assert(this.noMatchMsg[1]);
     this.noMatchMsg[1].classList.toggle(
         'hidden',
@@ -153,11 +158,12 @@
       return Promise.resolve();
     }
 
-    const selectedTab = this.flagsAppElement.tabs.find(
+    const selectedTab = this.flagsAppElement.getTabs().find(
         tab => tab.panelEl.classList.contains('selected'))!;
     const selectedTabId = selectedTab.panelEl.id;
     const queryString = `#${selectedTabId} flags-experiment:not(.hidden)`;
-    const total = this.flagsAppElement.$all(queryString).length;
+    const total =
+        this.flagsAppElement.shadowRoot!.querySelectorAll(queryString).length;
     if (total) {
       return this.flagsAppElement.announceStatus(
           total === 1 ?
@@ -185,43 +191,68 @@
   }
 }
 
-export class FlagsAppElement extends CustomElement {
+export class FlagsAppElement extends CrLitElement {
   static get is() {
     return 'flags-app';
   }
 
-  static override get template() {
-    return getTemplate();
+  static override get styles() {
+    return getCss();
+  }
+
+  override render() {
+    return getHtml.bind(this)();
   }
 
   private announceStatusDelayMs: number = 100;
   private featuresResolver: PromiseResolver<void> = new PromiseResolver();
-  private flagSearch: FlagSearch = new FlagSearch(this);
+  private flagSearch: FlagSearch|null = null;
   private lastChanged: HTMLElement|null = null;
   // <if expr="not is_ios">
   private lastFocused: HTMLElement|null = null;
-  private restartButton: HTMLButtonElement =
-      this.getRequiredElement<HTMLButtonElement>('#experiment-restart-button');
 
   // Whether the current URL is chrome://flags/deprecated. Only updated on
   // initial load.
   private isFlagsDeprecatedUrl_: boolean = false;
   // </if>
 
-  tabs: Tab[] = [
-    {
-      tabEl: this.getRequiredElement('#tab-available'),
-      panelEl: this.getRequiredElement('#tab-content-available'),
-    },
-    // <if expr="not is_ios">
-    {
-      tabEl: this.getRequiredElement('#tab-unavailable'),
-      panelEl: this.getRequiredElement('#tab-content-unavailable'),
-    },
-    // </if>
-  ];
+  getRequiredElement<K extends keyof HTMLElementTagNameMap>(query: K):
+      HTMLElementTagNameMap[K];
+  getRequiredElement<E extends HTMLElement = HTMLElement>(query: string): E;
+  getRequiredElement(query: string) {
+    const el = this.shadowRoot!.querySelector(query);
+    assert(el);
+    assert(el instanceof HTMLElement);
+    return el;
+  }
 
-  connectedCallback() {
+  getTabs(): Tab[] {
+    return [
+      {
+        tabEl: this.getRequiredElement('#tab-available'),
+        panelEl: this.getRequiredElement('#tab-content-available'),
+      },
+      // <if expr="not is_ios">
+      {
+        tabEl: this.getRequiredElement('#tab-unavailable'),
+        panelEl: this.getRequiredElement('#tab-content-unavailable'),
+      },
+      // </if>
+    ];
+  }
+
+  // <if expr="not is_ios">
+  private getRestartButton(): HTMLButtonElement {
+    return this.getRequiredElement<HTMLButtonElement>(
+        '#experiment-restart-button');
+  }
+  // </if>
+
+  override connectedCallback() {
+    super.connectedCallback();
+
+    this.flagSearch = new FlagSearch(this);
+
     // <if expr="not is_ios">
     const pathname = new URL(window.location.href).pathname;
     this.isFlagsDeprecatedUrl_ =
@@ -250,7 +281,7 @@
           loadTimeData.getString('deprecatedPageWarningExplanation');
       this.getRequiredElement<HTMLInputElement>('#search').placeholder =
           loadTimeData.getString('deprecatedSearchPlaceholder');
-      for (const element of this.$all('.no-match')) {
+      for (const element of this.shadowRoot!.querySelectorAll('.no-match')) {
         element.textContent = loadTimeData.getString('deprecatedNoResults');
       }
     }
@@ -262,6 +293,7 @@
   }
 
   setSearchDebounceDelayMsForTesting(delay: number) {
+    assert(this.flagSearch);
     this.flagSearch.setSearchDebounceDelayMsForTesting(delay);
   }
 
@@ -288,7 +320,7 @@
    * Toggles necessary attributes to display selected tab.
    */
   private selectTab(selectedTabEl: HTMLElement) {
-    for (const tab of this.tabs) {
+    for (const tab of this.getTabs()) {
       const isSelectedTab = tab.tabEl === selectedTabEl;
       tab.tabEl.classList.toggle('selected', isSelectedTab);
       tab.tabEl.setAttribute('aria-selected', String(isSelectedTab));
@@ -302,7 +334,7 @@
    * that data. It expects an object structure like the above.
    * @param experimentalFeaturesData Information about all experiments.
    */
-  private render(experimentalFeaturesData: ExperimentalFeaturesData) {
+  private render_(experimentalFeaturesData: ExperimentalFeaturesData) {
     const defaultFeatures: Feature[] = [];
     const nonDefaultFeatures: Feature[] = [];
 
@@ -325,12 +357,12 @@
     this.showRestartToast(experimentalFeaturesData.needsRestart);
 
     // <if expr="not is_ios">
-    this.restartButton.onclick = () =>
+    this.getRestartButton().onclick = () =>
         FlagsBrowserProxyImpl.getInstance().restartBrowser();
     // </if>
 
     // Tab panel selection.
-    for (const tab of this.tabs) {
+    for (const tab of this.getTabs()) {
       tab.tabEl.addEventListener('click', e => {
         e.preventDefault();
         this.selectTab(tab.tabEl);
@@ -368,7 +400,7 @@
         e.preventDefault();
         // <if expr="not is_ios">
         this.lastFocused = this.lastChanged;
-        this.restartButton.focus();
+        this.getRestartButton().focus();
         // </if>
       }
     });
@@ -431,6 +463,7 @@
   /** Reset all flags to their default values and refresh the UI. */
   private resetAllFlags() {
     FlagsBrowserProxyImpl.getInstance().resetAllFlags();
+    assert(this.flagSearch);
     this.flagSearch.clearSearch();
     this.announceStatus(loadTimeData.getString('reset-acknowledged'));
     this.showRestartToast(true);
@@ -482,7 +515,7 @@
     this.getRequiredElement('#needs-restart').classList.toggle('show', show);
     // There is no restart button on iOS.
     // <if expr="not is_ios">
-    this.restartButton.disabled = !show;
+    this.getRestartButton().disabled = !show;
     // </if>
     if (show) {
       this.getRequiredElement('#needs-restart').setAttribute('role', 'alert');
@@ -496,7 +529,7 @@
   private returnExperimentalFeatures(experimentalFeaturesData:
                                          ExperimentalFeaturesData) {
     const bodyContainer = this.getRequiredElement('#body-container');
-    this.render(experimentalFeaturesData);
+    this.render_(experimentalFeaturesData);
 
     if (experimentalFeaturesData.showBetaChannelPromotion) {
       this.getRequiredElement<HTMLSpanElement>('#channel-promo-beta').hidden =
@@ -512,18 +545,17 @@
 
     bodyContainer.style.visibility = 'visible';
 
+    assert(this.flagSearch);
     this.flagSearch.init();
 
     // <if expr="chromeos_ash">
-    const ownerWarningDiv = this.$<HTMLParagraphElement>('#owner-warning');
-    if (ownerWarningDiv) {
-      ownerWarningDiv.hidden = !experimentalFeaturesData.showOwnerWarning;
-    }
+    const ownerWarningDiv = this.getRequiredElement('#owner-warning');
+    ownerWarningDiv.hidden = !experimentalFeaturesData.showOwnerWarning;
     // </if>
 
     // <if expr="chromeos_lacros or chromeos_ash">
-    const systemFlagsLinkDiv = this.$<HTMLElement>('#os-link-container');
-    if (systemFlagsLinkDiv && !experimentalFeaturesData.showSystemFlagsLink) {
+    const systemFlagsLinkDiv = this.getRequiredElement('#os-link-container');
+    if (!experimentalFeaturesData.showSystemFlagsLink) {
       systemFlagsLinkDiv.style.display = 'none';
     }
     // </if>
@@ -537,13 +569,13 @@
    * in the list instead of going to the top of the page.
    */
   private setupRestartButton() {
-    this.restartButton.addEventListener('keydown', e => {
+    this.getRestartButton().addEventListener('keydown', e => {
       if (e.shiftKey && e.key === 'Tab' && this.lastFocused) {
         e.preventDefault();
         this.lastFocused.focus();
       }
     });
-    this.restartButton.addEventListener('blur', () => {
+    this.getRestartButton().addEventListener('blur', () => {
       this.lastFocused = null;
     });
   }
@@ -556,4 +588,7 @@
   }
 }
 
+// Exported as AppElement to be used by the auto-generated .html.ts file.
+export type AppElement = FlagsAppElement;
+
 customElements.define(FlagsAppElement.is, FlagsAppElement);
diff --git a/components/history_embeddings/history_embeddings_features.cc b/components/history_embeddings/history_embeddings_features.cc
index d5edcdb8..ce3723c 100644
--- a/components/history_embeddings/history_embeddings_features.cc
+++ b/components/history_embeddings/history_embeddings_features.cc
@@ -77,6 +77,11 @@
                                                       "UseMlIntentClassifier",
                                                       false);
 
+const base::FeatureParam<int> kMockIntentClassifierDelayMS(
+    &kHistoryEmbeddings,
+    "MockIntentClassifierDelayMS",
+    0);
+
 const base::FeatureParam<bool> kEnableAnswers(&kHistoryEmbeddings,
                                               "EnableAnswers",
                                               false);
@@ -89,6 +94,10 @@
                                                      "MlAnswererMinScore",
                                                      0.5);
 
+const base::FeatureParam<int> kMockAnswererDelayMS(&kHistoryEmbeddings,
+                                                   "MockAnswererDelayMS",
+                                                   0);
+
 const base::FeatureParam<bool> kEnableImagesForResults(&kHistoryEmbeddings,
                                                        "EnableImagesForResults",
                                                        false);
diff --git a/components/history_embeddings/history_embeddings_features.h b/components/history_embeddings/history_embeddings_features.h
index fedbf42..62b487e 100644
--- a/components/history_embeddings/history_embeddings_features.h
+++ b/components/history_embeddings/history_embeddings_features.h
@@ -61,6 +61,10 @@
 // used).
 extern const base::FeatureParam<bool> kUseMlIntentClassifier;
 
+// Specifies the delay in milliseconds to use for the mock intent classifier for
+// local development.
+extern const base::FeatureParam<int> kMockIntentClassifierDelayMS;
+
 // Specifies whether to answer queries using an answerer (mock or ML). This
 // can be considered a toggle for v2 functionality.
 extern const base::FeatureParam<bool> kEnableAnswers;
@@ -71,6 +75,10 @@
 // Specifies the min score for generated answer from the ML answerer.
 extern const base::FeatureParam<double> kMlAnswererMinScore;
 
+// Specifies the delay in milliseconds to use for the mock answerer for local
+// development.
+extern const base::FeatureParam<int> kMockAnswererDelayMS;
+
 // Specifies whether to show images in results for search results on the
 // chrome://history page.
 extern const base::FeatureParam<bool> kEnableImagesForResults;
diff --git a/components/history_embeddings/mock_answerer.cc b/components/history_embeddings/mock_answerer.cc
index 8f20dbd..1f37532 100644
--- a/components/history_embeddings/mock_answerer.cc
+++ b/components/history_embeddings/mock_answerer.cc
@@ -5,6 +5,8 @@
 #include "components/history_embeddings/mock_answerer.h"
 
 #include "base/task/sequenced_task_runner.h"
+#include "base/time/time.h"
+#include "components/history_embeddings/history_embeddings_features.h"
 #include "components/optimization_guide/proto/features/history_answer.pb.h"
 
 namespace history_embeddings {
@@ -22,10 +24,12 @@
   optimization_guide::proto::Answer answer;
   answer.set_text(std::string("This is the answer to query '") + query +
                   std::string("'."));
-  base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
-      FROM_HERE, base::BindOnce(std::move(callback),
-                                AnswererResult{ComputeAnswerStatus::SUCCESS,
-                                               query, answer}));
+  base::SequencedTaskRunner::GetCurrentDefault()->PostDelayedTask(
+      FROM_HERE,
+      base::BindOnce(
+          std::move(callback),
+          AnswererResult{ComputeAnswerStatus::SUCCESS, query, answer}),
+      base::Milliseconds(kMockAnswererDelayMS.Get()));
 }
 
 }  // namespace history_embeddings
diff --git a/components/history_embeddings/mock_intent_classifier.cc b/components/history_embeddings/mock_intent_classifier.cc
index 464cc8e..ffb03e1 100644
--- a/components/history_embeddings/mock_intent_classifier.cc
+++ b/components/history_embeddings/mock_intent_classifier.cc
@@ -6,6 +6,8 @@
 
 #include "base/strings/string_util.h"
 #include "base/task/sequenced_task_runner.h"
+#include "base/time/time.h"
+#include "components/history_embeddings/history_embeddings_features.h"
 
 namespace history_embeddings {
 
@@ -19,19 +21,34 @@
 void MockIntentClassifier::ComputeQueryIntent(
     std::string query,
     ComputeQueryIntentCallback callback) {
-  std::vector<std::string> query_intent_indicating_words(
-      {"can ", "who ", "what ", "when ", "where ", "why ", "does ", "how ",
-       "do "});
+  std::vector<std::string> query_intent_indicating_words({
+      "can ",
+      "could ",
+      "do ",
+      "does ",
+      "how ",
+      "should ",
+      "what ",
+      "when ",
+      "where ",
+      "whether ",
+      "which ",
+      "who ",
+      "whose ",
+      "why ",
+      "would ",
+  });
   query = base::ToLowerASCII(query);
   bool is_query_answerable =
       query.ends_with('?') ||
       std::ranges::any_of(
           query_intent_indicating_words,
           [&](std::string_view start) { return query.starts_with(start); });
-  base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
+  base::SequencedTaskRunner::GetCurrentDefault()->PostDelayedTask(
       FROM_HERE,
       base::BindOnce(std::move(callback), ComputeIntentStatus::SUCCESS,
-                     is_query_answerable));
+                     is_query_answerable),
+      base::Milliseconds(kMockIntentClassifierDelayMS.Get()));
 }
 
 }  // namespace history_embeddings
diff --git a/components/input/web_input_event_builders_mac_unittest.mm b/components/input/web_input_event_builders_mac_unittest.mm
index d2569e8..6b7c240 100644
--- a/components/input/web_input_event_builders_mac_unittest.mm
+++ b/components/input/web_input_event_builders_mac_unittest.mm
@@ -11,6 +11,7 @@
 
 #include <Carbon/Carbon.h>
 #import <Cocoa/Cocoa.h>
+#include <IOKit/hidsystem/IOLLEvent.h>  // for NX_ constants
 #include <stddef.h>
 
 #import <string_view>
@@ -51,14 +52,22 @@
 
 // Modifier keys, grouped into left/right pairs.
 const ModifierKey kModifierKeys[] = {
-    {kVK_Shift, 1 << 1, NSEventModifierFlagShift},            // Left Shift
-    {kVK_RightShift, 1 << 2, NSEventModifierFlagShift},       // Right Shift
-    {kVK_Command, 1 << 3, NSEventModifierFlagCommand},        // Left Command
-    {kVK_RightCommand, 1 << 4, NSEventModifierFlagCommand},   // Right Command
-    {kVK_Option, 1 << 5, NSEventModifierFlagOption},          // Left Alt
-    {kVK_RightOption, 1 << 6, NSEventModifierFlagOption},     // Right Alt
-    {kVK_Control, 1 << 0, NSEventModifierFlagControl},        // Left Control
-    {kVK_RightControl, 1 << 13, NSEventModifierFlagControl},  // Right Control
+    // Left Shift
+    {kVK_Shift, NX_DEVICELSHIFTKEYMASK, NSEventModifierFlagShift},
+    // Right Shift
+    {kVK_RightShift, NX_DEVICERSHIFTKEYMASK, NSEventModifierFlagShift},
+    // Left Command
+    {kVK_Command, NX_DEVICELCMDKEYMASK, NSEventModifierFlagCommand},
+    // Right Command
+    {kVK_RightCommand, NX_DEVICERCMDKEYMASK, NSEventModifierFlagCommand},
+    // Left Option
+    {kVK_Option, NX_DEVICELALTKEYMASK, NSEventModifierFlagOption},
+    // Right Option
+    {kVK_RightOption, NX_DEVICERALTKEYMASK, NSEventModifierFlagOption},
+    // Left Control
+    {kVK_Control, NX_DEVICELCTLKEYMASK, NSEventModifierFlagControl},
+    // Right Control
+    {kVK_RightControl, NX_DEVICERCTLKEYMASK, NSEventModifierFlagControl},
 };
 
 NSEvent* BuildFakeKeyEvent(NSUInteger key_code,
@@ -260,7 +269,7 @@
 }
 
 // Test that individual modifier keys are still reported correctly, even if the
-// undocumented device dependent modifier flags are not set.
+// device dependent modifier flags are not set.
 TEST(WebInputEventBuilderMacTest, MissingUndocumentedModifierFlags) {
   for (const auto& key : kModifierKeys) {
     NSEvent* mac_event = BuildFakeKeyEvent(key.mac_key_code, 0,
diff --git a/components/lens/lens_features.cc b/components/lens/lens_features.cc
index 9fcf9fc8..64dbbe1b 100644
--- a/components/lens/lens_features.cc
+++ b/components/lens/lens_features.cc
@@ -261,6 +261,15 @@
         &kLensOverlayContextualSearchbox,
         "use-video-context-for-multimodal-requests", false};
 
+constexpr base::FeatureParam<bool>
+    kUseOptimizedRequestFlow{
+        &kLensOverlayContextualSearchbox,
+        "use-optimized-request-flow", false};
+
+constexpr base::FeatureParam<std::string> kLensOverlayClusterInfoEndpointUrl{
+    &kLensOverlayContextualSearchbox, "cluster-info-endpoint-url",
+    "https://lensfrontend-pa.googleapis.com/v1/gsessionid"};
+
 constexpr base::FeatureParam<std::string> kHomepageURLForLens{
     &kLensStandalone, "lens-homepage-url", "https://lens.google.com/v3/"};
 
@@ -490,6 +499,14 @@
   return kUseVideoContextForMultimodalLensOverlayRequests.Get();
 }
 
+bool UseOptimizedRequestFlow() {
+  return kUseOptimizedRequestFlow.Get();
+}
+
+std::string GetLensOverlayClusterInfoEndpointUrl() {
+  return kLensOverlayClusterInfoEndpointUrl.Get();
+}
+
 bool UsePdfsAsContext() {
   return kUsePdfsAsContext.Get();
 }
diff --git a/components/lens/lens_features.h b/components/lens/lens_features.h
index 1a281b5..f13f95c 100644
--- a/components/lens/lens_features.h
+++ b/components/lens/lens_features.h
@@ -337,6 +337,17 @@
 COMPONENT_EXPORT(LENS_FEATURES)
 extern bool UseVideoContextForMultimodalLensOverlayRequests();
 
+// Returns whether to use the new optimized request flow which makes a request
+// to get the cluster info prior to uploading any image or page content bytes.
+// This also decouples sending the images and page content bytes in the same
+// request.
+COMPONENT_EXPORT(LENS_FEATURES)
+extern bool UseOptimizedRequestFlow();
+
+// Returns the finch configured endpoint URL for the cluster info request.
+COMPONENT_EXPORT(LENS_FEATURES)
+extern std::string GetLensOverlayClusterInfoEndpointUrl();
+
 // Returns whether to include PDFs from the underlying page in the request to be
 // used as page context.
 COMPONENT_EXPORT(LENS_FEATURES)
diff --git a/components/omnibox/browser/omnibox_field_trial.cc b/components/omnibox/browser/omnibox_field_trial.cc
index fe3bfea6..dee0637 100644
--- a/components/omnibox/browser/omnibox_field_trial.cc
+++ b/components/omnibox/browser/omnibox_field_trial.cc
@@ -633,15 +633,6 @@
     "OmniboxSquareSuggestIconWeather",
     true);
 
-bool OmniboxFieldTrial::IsUniformRowHeightEnabled() {
-  return base::FeatureList::IsEnabled(omnibox::kUniformRowHeight);
-}
-
-const base::FeatureParam<int> OmniboxFieldTrial::kRichSuggestionVerticalMargin(
-    &omnibox::kUniformRowHeight,
-    "OmniboxRichSuggestionVerticalMargin",
-    6);
-
 bool OmniboxFieldTrial::IsGM3TextStyleEnabled() {
   return base::FeatureList::IsEnabled(omnibox::kOmniboxSteadyStateTextStyle);
 }
diff --git a/components/omnibox/browser/omnibox_field_trial.h b/components/omnibox/browser/omnibox_field_trial.h
index eaa45f0..22ba479 100644
--- a/components/omnibox/browser/omnibox_field_trial.h
+++ b/components/omnibox/browser/omnibox_field_trial.h
@@ -317,16 +317,6 @@
 // Gray rounded rect background for weather icons.
 extern const base::FeatureParam<bool> kSquareSuggestIconWeather;
 
-// Omnibox UI simplification - uniform row heights.
-// Returns true if the feature to enable uniform row height is enabled.
-bool IsUniformRowHeightEnabled();
-
-// Specifies the row height in pixels for omnibox suggestions.
-extern const base::FeatureParam<int> kSuggestionRowHeight;
-// Specifies the vertical margin to use in one-line rich entity and answer
-// suggestions.
-extern const base::FeatureParam<int> kRichSuggestionVerticalMargin;
-
 // Omnibox GM3 - text style.
 // Returns true if the feature to enable GM3 text styling is enabled.
 bool IsGM3TextStyleEnabled();
diff --git a/components/omnibox/browser/search_suggestion_parser.cc b/components/omnibox/browser/search_suggestion_parser.cc
index f8a736ec..66dcce7 100644
--- a/components/omnibox/browser/search_suggestion_parser.cc
+++ b/components/omnibox/browser/search_suggestion_parser.cc
@@ -846,16 +846,9 @@
             continue;
         }
         if (ui::GetDeviceFormFactor() == ui::DEVICE_FORM_FACTOR_DESKTOP) {
-          if (OmniboxFieldTrial::IsUniformRowHeightEnabled()) {
-            // If calculator results are going to be displayed on 1 line,
-            // keep everything in the match contents
-            match_contents = l10n_util::GetStringFUTF16(
-                IDS_OMNIBOX_ONE_LINE_CALCULATOR_SUGGESTION_TEMPLATE, query,
-                suggestion);
-          } else {
-            annotation = has_equals_prefix ? suggestion : match_contents;
-            match_contents = query;
-          }
+          match_contents = l10n_util::GetStringFUTF16(
+              IDS_OMNIBOX_ONE_LINE_CALCULATOR_SUGGESTION_TEMPLATE, query,
+              suggestion);
         }
       }
 
diff --git a/components/omnibox/common/omnibox_features.cc b/components/omnibox/common/omnibox_features.cc
index fc3c3fa6..305dc75 100644
--- a/components/omnibox/common/omnibox_features.cc
+++ b/components/omnibox/common/omnibox_features.cc
@@ -15,108 +15,85 @@
 #endif
 
 namespace omnibox {
+namespace {
+constexpr bool IS_ANDROID = !!BUILDFLAG(IS_ANDROID);
+constexpr bool IS_IOS = !!BUILDFLAG(IS_IOS);
 
-constexpr auto enabled_by_default_desktop_only =
-#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS)
-    base::FEATURE_DISABLED_BY_DEFAULT;
-#else
-    base::FEATURE_ENABLED_BY_DEFAULT;
-#endif
+constexpr base::FeatureState DISABLED = base::FEATURE_DISABLED_BY_DEFAULT;
+constexpr base::FeatureState ENABLED = base::FEATURE_ENABLED_BY_DEFAULT;
 
-constexpr auto enabled_by_default_android_only =
-#if BUILDFLAG(IS_ANDROID)
-    base::FEATURE_ENABLED_BY_DEFAULT;
-#else
-    base::FEATURE_DISABLED_BY_DEFAULT;
-#endif
-
-constexpr auto enabled_by_default_desktop_android =
-#if BUILDFLAG(IS_IOS)
-    base::FEATURE_DISABLED_BY_DEFAULT;
-#else
-    base::FEATURE_ENABLED_BY_DEFAULT;
-#endif
-
-constexpr auto enabled_by_default_desktop_ios =
-#if BUILDFLAG(IS_ANDROID)
-    base::FEATURE_DISABLED_BY_DEFAULT;
-#else
-    base::FEATURE_ENABLED_BY_DEFAULT;
-#endif
+constexpr base::FeatureState enable_if(bool condition) {
+  return condition ? ENABLED : DISABLED;
+}
+}  // namespace
 
 // Feature to enable showing thumbnail in front of the Omnibox clipboard image
 // search suggestion.
 BASE_FEATURE(kImageSearchSuggestionThumbnail,
              "ImageSearchSuggestionThumbnail",
-             enabled_by_default_android_only);
+             enable_if(IS_ANDROID));
 
 // Feature used to allow users to remove suggestions from clipboard.
 BASE_FEATURE(kOmniboxRemoveSuggestionsFromClipboard,
              "OmniboxRemoveSuggestionsFromClipboard",
-             enabled_by_default_android_only);
+             enable_if(IS_ANDROID));
 
 // When enabled, uses the grouping framework with prefixed suggestions (i.e.
 // autocomplete_grouper_sections.h) to limit and group (but not sort) matches.
 BASE_FEATURE(kGroupingFrameworkForNonZPS,
              "OmniboxGroupingFrameworkForNonZPS",
-             enabled_by_default_android_only);
+             enable_if(IS_ANDROID));
 
 // Demotes the relevance scores when comparing suggestions based on the
 // suggestion's |AutocompleteMatchType| and the user's |PageClassification|.
 // This feature's main job is to contain the DemoteByType parameter.
-BASE_FEATURE(kOmniboxDemoteByType,
-             "OmniboxDemoteByType",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+BASE_FEATURE(kOmniboxDemoteByType, "OmniboxDemoteByType", DISABLED);
 
 // When enabled, deduping prefers non-shortcut provider matches, while still
 // treating fuzzy provider matches as the least preferred.
 BASE_FEATURE(kPreferNonShortcutMatchesWhenDeduping,
              "OmniboxPreferNonShortcutMatchesWhenDeduping",
-             base::FEATURE_ENABLED_BY_DEFAULT);
+             ENABLED);
 
 // Feature used to cap max zero suggestions shown according to the param
 // OmniboxMaxZeroSuggestMatches. If omitted,
 // OmniboxUIExperimentMaxAutocompleteMatches will be used instead. If present,
 // OmniboxMaxZeroSuggestMatches will override
 // OmniboxUIExperimentMaxAutocompleteMatches when |from_omnibox_focus| is true.
-BASE_FEATURE(kMaxZeroSuggestMatches,
-             "OmniboxMaxZeroSuggestMatches",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+BASE_FEATURE(kMaxZeroSuggestMatches, "OmniboxMaxZeroSuggestMatches", DISABLED);
 
 // Feature used to cap max suggestions shown according to the params
 // UIMaxAutocompleteMatches and UIMaxAutocompleteMatchesByProvider.
 BASE_FEATURE(kUIExperimentMaxAutocompleteMatches,
              "OmniboxUIExperimentMaxAutocompleteMatches",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+             DISABLED);
 
 // Feature used to cap the number of URL-type matches shown within the
 // Omnibox. If enabled, the number of URL-type matches is limited (unless
 // there are no more non-URL matches available.) If enabled, there is a
 // companion parameter - OmniboxMaxURLMatches - which specifies the maximum
 // desired number of URL-type matches.
-BASE_FEATURE(kOmniboxMaxURLMatches,
-             "OmniboxMaxURLMatches",
-             base::FEATURE_ENABLED_BY_DEFAULT);
+BASE_FEATURE(kOmniboxMaxURLMatches, "OmniboxMaxURLMatches", ENABLED);
 
 // Feature used to cap max suggestions to a dynamic limit based on how many URLs
 // would be shown. E.g., show up to 10 suggestions if doing so would display no
 // URLs; else show up to 8 suggestions if doing so would include 1 or more URLs.
 BASE_FEATURE(kDynamicMaxAutocomplete,
              "OmniboxDynamicMaxAutocomplete",
-             enabled_by_default_desktop_android);
+             enable_if(!IS_IOS));
 
 // If enabled, takes the search intent query params into account for triggering
 // switch to tab actions on matches.
 BASE_FEATURE(kDisambiguateTabMatchingForEntitySuggestions,
              "DisambiguateTabMatchingForEntitySuggestions",
-             base::FEATURE_ENABLED_BY_DEFAULT);
+             ENABLED);
 
 // Used to adjust the relevance for the local history zero-prefix suggestions.
 // If enabled, the relevance is determined by this feature's companion
 // parameter, OmniboxFieldTrial::kLocalHistoryZeroSuggestRelevanceScore.
 BASE_FEATURE(kAdjustLocalHistoryZeroSuggestRelevanceScore,
              "AdjustLocalHistoryZeroSuggestRelevanceScore",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+             DISABLED);
 
 // Enables on-clobber (i.e., when the user clears the whole omnibox text)
 // zero-prefix suggestions on the Open Web, that are contextual to the current
@@ -124,19 +101,19 @@
 // eligible to send the current page URL to the suggest server.
 BASE_FEATURE(kClobberTriggersContextualWebZeroSuggest,
              "OmniboxClobberTriggersContextualWebZeroSuggest",
-             enabled_by_default_desktop_android);
+             enable_if(!IS_IOS));
 
 // Enables on-clobber (i.e., when the user clears the whole omnibox text)
 // zero-prefix suggestions on the SRP.
 BASE_FEATURE(kClobberTriggersSRPZeroSuggest,
              "OmniboxClobberTriggersSRPZeroSuggest",
-             enabled_by_default_desktop_android);
+             enable_if(!IS_IOS));
 
 // Enables local history zero-prefix suggestions in every context in which the
 // remote zero-prefix suggestions are enabled.
 BASE_FEATURE(kLocalHistoryZeroSuggestBeyondNTP,
              "LocalHistoryZeroSuggestBeyondNTP",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+             DISABLED);
 
 // If enabled, SearchProvider uses `normalized_term` instead of `term` from the
 // `keyword_search_terms` table. `normalized_term` is the original search term
@@ -147,7 +124,7 @@
 // mismatches in the terms.
 BASE_FEATURE(kNormalizeSearchSuggestions,
              "NormalizeSearchSuggestions",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+             DISABLED);
 
 // Mainly used to enable sending INTERACTION_CLOBBER focus type for zero-prefix
 // requests with an empty input on Web/SRP on Mobile. Enabled by default on
@@ -155,36 +132,34 @@
 // OmniboxEditModel for triggering zero-suggest prefetching on Web/SRP.
 BASE_FEATURE(kOmniboxOnClobberFocusTypeOnContent,
              "OmniboxOnClobberFocusTypeOnContent",
-             enabled_by_default_desktop_android);
+             enable_if(!IS_IOS));
 
 // If enabled, zero prefix suggestions will be stored using an in-memory caching
 // service, instead of using the existing prefs-based cache.
 BASE_FEATURE(kZeroSuggestInMemoryCaching,
              "ZeroSuggestInMemoryCaching",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+             DISABLED);
 
 // Enables the use of a request debouncer to throttle the number of ZPS prefetch
 // requests initiated over a given period of time (to help minimize the
 // performance impact of ZPS prefetching on the remote Suggest service).
 BASE_FEATURE(kZeroSuggestPrefetchDebouncing,
              "ZeroSuggestPrefetchDebouncing",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+             DISABLED);
 
 // Enables prefetching of the zero prefix suggestions for eligible users on NTP.
-BASE_FEATURE(kZeroSuggestPrefetching,
-             "ZeroSuggestPrefetching",
-             base::FEATURE_ENABLED_BY_DEFAULT);
+BASE_FEATURE(kZeroSuggestPrefetching, "ZeroSuggestPrefetching", ENABLED);
 
 // Enables prefetching of the zero prefix suggestions for eligible users on SRP.
 BASE_FEATURE(kZeroSuggestPrefetchingOnSRP,
              "ZeroSuggestPrefetchingOnSRP",
-             enabled_by_default_desktop_ios);
+             enable_if(!IS_ANDROID));
 
 // Enables prefetching of the zero prefix suggestions for eligible users on the
 // Web (i.e. non-NTP and non-SRP URLs).
 BASE_FEATURE(kZeroSuggestPrefetchingOnWeb,
              "ZeroSuggestPrefetchingOnWeb",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+             DISABLED);
 
 // Features to provide head and tail non personalized search suggestion from
 // compact on device models. More specifically, feature name with suffix
@@ -192,72 +167,64 @@
 // non-incognito mode respectively.
 BASE_FEATURE(kOnDeviceHeadProviderIncognito,
              "OmniboxOnDeviceHeadProviderIncognito",
-             base::FEATURE_ENABLED_BY_DEFAULT);
+             ENABLED);
 BASE_FEATURE(kOnDeviceHeadProviderNonIncognito,
              "OmniboxOnDeviceHeadProviderNonIncognito",
-             base::FEATURE_ENABLED_BY_DEFAULT);
+             ENABLED);
 BASE_FEATURE(kOnDeviceHeadProviderKorean,
              "OmniboxOnDeviceHeadProviderKorean",
-             base::FEATURE_DISABLED_BY_DEFAULT);
-BASE_FEATURE(kOnDeviceTailModel,
-             "OmniboxOnDeviceTailModel",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+             DISABLED);
+BASE_FEATURE(kOnDeviceTailModel, "OmniboxOnDeviceTailModel", DISABLED);
 
 // If enabled, the relevant AutocompleteProviders will store "title" data in
 // AutocompleteMatch::contents and "URL" data in AutocompleteMatch::description
 // for URL-based omnibox suggestions (see crbug.com/1202964 for more details).
 BASE_FEATURE(kStoreTitleInContentsAndUrlInDescription,
              "OmniboxStoreTitleInContentsAndUrlInDescription",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+             DISABLED);
 
 // Feature used to fetch document suggestions.
 BASE_FEATURE(kDocumentProvider,
              "OmniboxDocumentProvider",
-             enabled_by_default_desktop_only);
+             enable_if(!IS_ANDROID && !IS_IOS));
 
 // If enabled, the 'Show Google Drive Suggestions' setting is removed and Drive
 // suggestions are available to all clients who meet the other requirements.
 BASE_FEATURE(kDocumentProviderNoSetting,
              "OmniboxDocumentProviderNoSetting",
-             base::FEATURE_ENABLED_BY_DEFAULT);
+             ENABLED);
 
 // If enabled, the requirement to be in an active Sync state is removed and
 // Drive suggestions are available to all clients who meet the other
 // requirements.
 BASE_FEATURE(kDocumentProviderNoSyncRequirement,
              "OmniboxDocumentProviderNoSyncRequirement",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+             DISABLED);
 
 // Feature to determine if the HQP should double as a domain provider by
 // suggesting up to the provider limit for each of the user's highly visited
 // domains.
-BASE_FEATURE(kDomainSuggestions,
-             "OmniboxDomainSuggestions",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+BASE_FEATURE(kDomainSuggestions, "OmniboxDomainSuggestions", DISABLED);
 
 // Feature to determine if omnibox should use a pref based data collection
 // consent helper instead of a history sync based one.
 BASE_FEATURE(kPrefBasedDataCollectionConsentHelper,
              "PrefBasedDataCollectionConsentHelper",
-             base::FEATURE_ENABLED_BY_DEFAULT);
+             ENABLED);
 
 // If enabled, clipboard suggestion will not show the clipboard content until
 // the user clicks the reveal button.
 BASE_FEATURE(kClipboardSuggestionContentHidden,
              "ClipboardSuggestionContentHidden",
-             enabled_by_default_android_only);
+             enable_if(IS_ANDROID));
 
 // If enabled, uses the Chrome Refresh 2023 design's shape for action chips in
 // the omnibox suggestion popup.
-BASE_FEATURE(kCr2023ActionChips,
-             "Cr2023ActionChips",
-             base::FEATURE_ENABLED_BY_DEFAULT);
+BASE_FEATURE(kCr2023ActionChips, "Cr2023ActionChips", ENABLED);
 
 // If enabled, uses the Chrome Refresh 2023 design's icons for action chips in
 // the omnibox suggestion popup.
-BASE_FEATURE(kCr2023ActionChipsIcons,
-             "Cr2023ActionChipsIcons",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+BASE_FEATURE(kCr2023ActionChipsIcons, "Cr2023ActionChipsIcons", DISABLED);
 
 // If enabled, makes Most Visited Tiles a Horizontal render group.
 // Horizontal render group decomposes aggregate suggestions (such as old Most
@@ -265,7 +232,7 @@
 // element in the carousel.
 BASE_FEATURE(kMostVisitedTilesHorizontalRenderGroup,
              "OmniboxMostVisitedTilesHorizontalRenderGroup",
-             enabled_by_default_android_only);
+             enable_if(IS_ANDROID));
 
 // If enabled, expands autocompletion to possibly (depending on params) include
 // suggestion titles and non-prefixes as opposed to be restricted to URL
@@ -273,113 +240,90 @@
 // accommodate the autocompletions.
 BASE_FEATURE(kRichAutocompletion,
              "OmniboxRichAutocompletion",
-             enabled_by_default_desktop_ios);
+             enable_if(!IS_ANDROID));
 
 // Feature used to enable Pedals in the NTP Realbox.
-BASE_FEATURE(kNtpRealboxPedals,
-             "NtpRealboxPedals",
-             base::FEATURE_ENABLED_BY_DEFAULT);
+BASE_FEATURE(kNtpRealboxPedals, "NtpRealboxPedals", ENABLED);
 
 // If enabled, adds a grey square background to search icons, and makes answer
 // icon square instead of round.
 // TODO(manukh): Partially launched; still experimenting with
 //  `OmniboxSquareSuggestIconWeather`. Clean up when that param launches and
 //  reaches stable.
-BASE_FEATURE(kSquareSuggestIcons,
-             "OmniboxSquareIcons",
-             base::FEATURE_ENABLED_BY_DEFAULT);
-
-// If enabled, forces omnibox suggestion rows to be uniformly sized.
-// TODO(manukh): Clean up feature code 9/12 when m117 reaches stable; we're
-//   launching the rest of CR23 in m117.
-BASE_FEATURE(kUniformRowHeight,
-             "OmniboxUniformRowHeight",
-             base::FEATURE_ENABLED_BY_DEFAULT);
+BASE_FEATURE(kSquareSuggestIcons, "OmniboxSquareIcons", ENABLED);
 
 // If enabled, shows the omnibox suggestions popup in WebUI.
-BASE_FEATURE(kWebUIOmniboxPopup,
-             "WebUIOmniboxPopup",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+BASE_FEATURE(kWebUIOmniboxPopup, "WebUIOmniboxPopup", DISABLED);
 
 // If enabled, Omnibox "expanded state" height is increased from 42 px to 44 px.
-BASE_FEATURE(kExpandedStateHeight,
-             "OmniboxExpandedStateHeight",
-             base::FEATURE_ENABLED_BY_DEFAULT);
+BASE_FEATURE(kExpandedStateHeight, "OmniboxExpandedStateHeight", ENABLED);
 
 // If enabled, Omnibox "expanded state" corner radius is increased from 8px to
 // 16px.
-BASE_FEATURE(kExpandedStateShape,
-             "OmniboxExpandedStateShape",
-             base::FEATURE_ENABLED_BY_DEFAULT);
+BASE_FEATURE(kExpandedStateShape, "OmniboxExpandedStateShape", ENABLED);
 
 // If enabled, Omnibox "expanded state" colors are updated to match CR23
 // guidelines.
-BASE_FEATURE(kExpandedStateColors,
-             "OmniboxExpandedStateColors",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+BASE_FEATURE(kExpandedStateColors, "OmniboxExpandedStateColors", DISABLED);
 
 // If enabled, Omnibox "expanded state" icons are updated to match CR23
 // guidelines.
 BASE_FEATURE(kExpandedStateSuggestIcons,
              "OmniboxExpandedStateSuggestIcons",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+             DISABLED);
 
 // If enabled, Omnibox "expanded state" layout is updated to match CR23
 // guidelines.
-BASE_FEATURE(kExpandedLayout,
-             "OmniboxExpandedLayout",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+BASE_FEATURE(kExpandedLayout, "OmniboxExpandedLayout", DISABLED);
 
 // If enabled, the shape of the "hover fill" that's rendered for Omnibox
 // suggestions is updated to match CR23 guidelines.
 BASE_FEATURE(kSuggestionHoverFillShape,
              "OmniboxSuggestionHoverFillShape",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+             DISABLED);
 
 // When enabled, use Assistant for omnibox voice query recognition instead of
 // Android's built-in voice recognition service. Only works on Android.
 BASE_FEATURE(kOmniboxAssistantVoiceSearch,
              "OmniboxAssistantVoiceSearch",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+             DISABLED);
 
 // If enabled, Omnibox LHS and RHS icons are updated to match CR23
 // guidelines.
 BASE_FEATURE(kOmniboxCR23SteadyStateIcons,
              "kOmniboxCR23SteadyStateIcons",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+             DISABLED);
 
 // If enabled, Omnibox "steady state" background color is updated to match CR23
 // guidelines.
 BASE_FEATURE(kOmniboxSteadyStateBackgroundColor,
              "OmniboxSteadyStateBackgroundColor",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+             DISABLED);
 
 // If enabled, Omnibox "steady state" height is increased from 28 dp to 34 dp to
 // match CR23 guidelines.
 // TODO(manukh): Clean up feature code 9/12 when m117 reaches stable; we're
 //   launching the rest of CR23 in m117.
-BASE_FEATURE(kOmniboxSteadyStateHeight,
-             "OmniboxSteadyStateHeight",
-             base::FEATURE_ENABLED_BY_DEFAULT);
+BASE_FEATURE(kOmniboxSteadyStateHeight, "OmniboxSteadyStateHeight", ENABLED);
 
 // If enabled, Omnibox "steady state" text style is updated to match CR23
 // guidelines.
 BASE_FEATURE(kOmniboxSteadyStateTextStyle,
              "OmniboxSteadyStateTextStyle",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+             DISABLED);
 
 // If enabled, Omnibox "steady state" text color is updated to match CR23
 // guidelines.
 BASE_FEATURE(kOmniboxSteadyStateTextColor,
              "OmniboxSteadyStateTextColor",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+             DISABLED);
 
 // Android only flag that controls whether the new security indicator should be
 // used, on non-Android platforms this is controlled through the
 // ChromeRefresh2023 flag.
 BASE_FEATURE(kUpdatedConnectionSecurityIndicators,
              "OmniboxUpdatedConnectionSecurityIndicators",
-             base::FEATURE_ENABLED_BY_DEFAULT);
+             ENABLED);
 
 // Feature used to default typed navigations to use HTTPS instead of HTTP.
 // This only applies to navigations that don't have a scheme such as
@@ -389,13 +333,13 @@
 // necessary.
 BASE_FEATURE(kDefaultTypedNavigationsToHttps,
              "OmniboxDefaultTypedNavigationsToHttps",
-             base::FEATURE_ENABLED_BY_DEFAULT);
+             ENABLED);
 
 // Override the delay to create a spare renderer when the omnibox is focused
 // on Android.
 BASE_FEATURE(kOverrideAndroidOmniboxSpareRendererDelay,
              "OverrideAndroidOmniboxSpareRendererDelay",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+             DISABLED);
 
 // Parameter name used to look up the delay before falling back to the HTTP URL
 // while trying an HTTPS URL. The parameter is treated as a TimeDelta, so the
@@ -407,74 +351,64 @@
 
 // If enabled, logs Omnibox URL scoring signals to OmniboxEventProto for
 // training the ML scoring models.
-BASE_FEATURE(kLogUrlScoringSignals,
-             "LogUrlScoringSignals",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+BASE_FEATURE(kLogUrlScoringSignals, "LogUrlScoringSignals", DISABLED);
 
 // If true, enables history scoring signal annotator for populating history
 // scoring signals associated with Search suggestions. These signals will be
 // empty for Search suggestions otherwise.
 BASE_FEATURE(kEnableHistoryScoringSignalsAnnotatorForSearches,
              "EnableHistoryScoringSignalsAnnotatorForSearches",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+             DISABLED);
 
 // If enabled, (floating-point) ML model scores are mapped to (integral)
 // relevance scores by means of a piecewise function. This allows for the
 // integration of URL model scores with search traditional scores.
 BASE_FEATURE(kMlUrlPiecewiseMappedSearchBlending,
              "MlUrlPiecewiseMappedSearchBlending",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+             DISABLED);
 
 // If enabled, the ML scoring service will make use of an in-memory ML score
 // cache in order to speed up the overall scoring process.
 BASE_FEATURE(kMlUrlScoreCaching,
              "MlUrlScoreCaching",
-             enabled_by_default_desktop_only);
+             enable_if(!IS_ANDROID && !IS_IOS));
 
 // If enabled, runs the ML scoring model to assign new relevance scores to the
 // URL suggestions and reranks them.
-BASE_FEATURE(kMlUrlScoring, "MlUrlScoring", enabled_by_default_desktop_only);
+BASE_FEATURE(kMlUrlScoring, "MlUrlScoring", enable_if(!IS_ANDROID && !IS_IOS));
 
 // If enabled, specifies how URL model scores integrate with search traditional
 // scores.
-BASE_FEATURE(kMlUrlSearchBlending,
-             "MlUrlSearchBlending",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+BASE_FEATURE(kMlUrlSearchBlending, "MlUrlSearchBlending", DISABLED);
 
 // If enabled, creates Omnibox autocomplete URL scoring model. Prerequisite for
 // `kMlUrlScoring` & `kMlUrlSearchBlending`.
 BASE_FEATURE(kUrlScoringModel,
              "UrlScoringModel",
-             enabled_by_default_desktop_only);
+             enable_if(!IS_ANDROID && !IS_IOS));
 
 // Actions in Suggest is a data-driven feature; it's considered enabled when the
 // data is available.
 // The feature flag below helps us tune feature behaviors.
-BASE_FEATURE(kActionsInSuggest,
-             "OmniboxActionsInSuggest",
-             base::FEATURE_ENABLED_BY_DEFAULT);
+BASE_FEATURE(kActionsInSuggest, "OmniboxActionsInSuggest", ENABLED);
 
 BASE_FEATURE(kAnimateSuggestionsListAppearance,
              "AnimateSuggestionsListAppearance",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+             DISABLED);
 
-BASE_FEATURE(kOmniboxAnswerActions,
-             "OmniboxAnswerActions",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+BASE_FEATURE(kOmniboxAnswerActions, "OmniboxAnswerActions", DISABLED);
 
 // If enabled, treats categorical suggestions just like the entity suggestions
 // by reusing the `ACMatchType::SEARCH_SUGGEST_ENTITY` and reports the original
 // `omnibox::TYPE_CATEGORICAL_QUERY` to the server.
-BASE_FEATURE(kCategoricalSuggestions,
-             "CategoricalSuggestions",
-             base::FEATURE_ENABLED_BY_DEFAULT);
+BASE_FEATURE(kCategoricalSuggestions, "CategoricalSuggestions", ENABLED);
 
 // If enabled, merges the suggestion subtypes for the remote suggestions and the
 // local verbatim and history suggestion duplicates at the provider level. This
 // is needed for omnibox::kCategoricalSuggestions to function correctly but is
 // being controlled by a separate feature in case there are unintended side
 // effects beyond the categorical suggestions.
-BASE_FEATURE(kMergeSubtypes, "MergeSubtypes", base::FEATURE_ENABLED_BY_DEFAULT);
+BASE_FEATURE(kMergeSubtypes, "MergeSubtypes", ENABLED);
 
 // If enabled, sends a signal when a user touches down on a search suggestion to
 // |SearchPrefetchService|. |SearchPrefetchService| will then prefetch
@@ -482,19 +416,19 @@
 // are enabled.
 BASE_FEATURE(kOmniboxTouchDownTriggerForPrefetch,
              "OmniboxTouchDownTriggerForPrefetch",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+             DISABLED);
 
 // If enabled, site search engines featured by policy are shown on @ state in
 // the omnibox above starter pack suggestions.
 BASE_FEATURE(kShowFeaturedEnterpriseSiteSearch,
              "ShowFeaturedEnterpriseSiteSearch",
-             enabled_by_default_desktop_only);
+             enable_if(!IS_ANDROID && !IS_IOS));
 
 // Enables an informational IPH message at the bottom of the Omnibox directing
 // users to featured Enterprise search engines created by policy.
 BASE_FEATURE(kShowFeaturedEnterpriseSiteSearchIPH,
              "ShowFeaturedEnterpriseSiteSearchIPH",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+             DISABLED);
 
 // If enabled, site search engines defined by policy are saved into prefs and
 // committed to the keyword database, so that they can be accessed from the
@@ -506,23 +440,21 @@
 // in the implementation.
 BASE_FEATURE(kSiteSearchSettingsPolicy,
              "SiteSearchSettingsPolicy",
-             enabled_by_default_desktop_only);
+             enable_if(!IS_ANDROID && !IS_IOS));
 
 // Enables additional site search providers for the Site search Starter Pack.
 BASE_FEATURE(kStarterPackExpansion,
              "StarterPackExpansion",
-             enabled_by_default_desktop_only);
+             enable_if(!IS_ANDROID && !IS_IOS));
 
 // Enables an informational IPH message at the bottom of the Omnibox directing
 // users to certain starter pack engines.
-BASE_FEATURE(kStarterPackIPH,
-             "StarterPackIPH",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+BASE_FEATURE(kStarterPackIPH, "StarterPackIPH", DISABLED);
 
 // If enabled, |SearchProvider| will not function in Zero Suggest.
 BASE_FEATURE(kAblateSearchProviderWarmup,
              "AblateSearchProviderWarmup",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+             DISABLED);
 
 // When enabled, removes unrecognized TemplateURL parameters, rather than
 // keeping them verbatim. This feature will ensure that the new versions of
@@ -531,40 +463,30 @@
 // the placeholder will be replaced with an empty string.
 BASE_FEATURE(kDropUnrecognizedTemplateUrlParameters,
              "DropUnrecognizedTemplateUrlParameters",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+             DISABLED);
 
 // If enabled, hl= is reported in search requests (applicable to iOS only).
 BASE_FEATURE(kReportApplicationLanguageInSearchRequest,
              "ReportApplicationLanguageInSearchRequest",
-             base::FEATURE_ENABLED_BY_DEFAULT);
+             ENABLED);
 
 // Enable asynchronous Omnibox/Suggest view inflation.
-BASE_FEATURE(kOmniboxAsyncViewInflation,
-             "OmniboxAsyncViewInflation",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+BASE_FEATURE(kOmniboxAsyncViewInflation, "OmniboxAsyncViewInflation", DISABLED);
 
 // Use FusedLocationProvider on Android to fetch device location.
-BASE_FEATURE(kUseFusedLocationProvider,
-             "UseFusedLocationProvider",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+BASE_FEATURE(kUseFusedLocationProvider, "UseFusedLocationProvider", DISABLED);
 
 // Enables storing successful query/match in the shortcut database On Android.
-BASE_FEATURE(kOmniboxShortcutsAndroid,
-             "OmniboxShortcutsAndroid",
-             base::FEATURE_ENABLED_BY_DEFAULT);
+BASE_FEATURE(kOmniboxShortcutsAndroid, "OmniboxShortcutsAndroid", ENABLED);
 
 // Enables deletion of old shortcuts on profile load.
-BASE_FEATURE(kOmniboxDeleteOldShortcuts,
-             "OmniboxDeleteOldShortcuts",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+BASE_FEATURE(kOmniboxDeleteOldShortcuts, "OmniboxDeleteOldShortcuts", DISABLED);
 
 #if BUILDFLAG(IS_ANDROID)
 // Enable the Elegant Text Height attribute on the UrlBar.
 // This attribute increases line height by up to 60% to accommodate certain
 // scripts (e.g. Burmese).
-BASE_FEATURE(kOmniboxElegantTextHeight,
-             "OmniboxElegantTextHeight",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+BASE_FEATURE(kOmniboxElegantTextHeight, "OmniboxElegantTextHeight", DISABLED);
 
 // Whether the contents of the omnibox should be retained on focus as opposed to
 // being cleared. When this feature flag is enabled and the omnibox contents are
@@ -572,23 +494,21 @@
 // selected so as to allow for easy replacement by the user. Note that even with
 // this feature flag enabled, only large screen devices with an attached
 // keyboard and precision pointer will exhibit a change in behavior.
-BASE_FEATURE(kRetainOmniboxOnFocus,
-             "RetainOmniboxOnFocus",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+BASE_FEATURE(kRetainOmniboxOnFocus, "RetainOmniboxOnFocus", DISABLED);
+
+// Accelerates time from cold start to focused Omnibox on low-end devices,
+// prioritizing Omnibox focus and background initialization.
+BASE_FEATURE(kJumpStartOmnibox, "JumpStartOmnibox", DISABLED);
 
 namespace android {
 static jlong JNI_OmniboxFeatureMap_GetNativeMap(JNIEnv* env) {
   static base::NoDestructor<base::android::FeatureMap> kFeatureMap(
-      std::vector<const base::Feature*>{{
-          &kOmniboxAnswerActions,
-          &kAnimateSuggestionsListAppearance,
-          &kOmniboxTouchDownTriggerForPrefetch,
-          &kOmniboxAsyncViewInflation,
-          &kRichAutocompletion,
-          &kUseFusedLocationProvider,
-          &kOmniboxElegantTextHeight,
-          &kRetainOmniboxOnFocus,
-      }});
+      std::vector<const base::Feature*>{
+          {&kOmniboxAnswerActions, &kAnimateSuggestionsListAppearance,
+           &kOmniboxTouchDownTriggerForPrefetch, &kOmniboxAsyncViewInflation,
+           &kRichAutocompletion, &kUseFusedLocationProvider,
+           &kOmniboxElegantTextHeight, &kRetainOmniboxOnFocus,
+           &kJumpStartOmnibox}});
 
   return reinterpret_cast<jlong>(kFeatureMap.get());
 }
diff --git a/components/omnibox/common/omnibox_features.h b/components/omnibox/common/omnibox_features.h
index 5891b1d2..b7d1de8 100644
--- a/components/omnibox/common/omnibox_features.h
+++ b/components/omnibox/common/omnibox_features.h
@@ -81,7 +81,6 @@
 BASE_DECLARE_FEATURE(kRichAutocompletion);
 BASE_DECLARE_FEATURE(kNtpRealboxPedals);
 BASE_DECLARE_FEATURE(kSquareSuggestIcons);
-BASE_DECLARE_FEATURE(kUniformRowHeight);
 BASE_DECLARE_FEATURE(kWebUIOmniboxPopup);
 BASE_DECLARE_FEATURE(kExpandedStateHeight);
 BASE_DECLARE_FEATURE(kExpandedStateShape);
@@ -162,6 +161,7 @@
 
 #if BUILDFLAG(IS_ANDROID)
 BASE_DECLARE_FEATURE(kRetainOmniboxOnFocus);
+BASE_DECLARE_FEATURE(kJumpStartOmnibox);
 #endif  // BUILDFLAG(IS_ANDROID)
 
 // `ShortcutsProvider` features.
diff --git a/components/optimization_guide/core/optimization_guide_features.cc b/components/optimization_guide/core/optimization_guide_features.cc
index fb2a095..b7e8042 100644
--- a/components/optimization_guide/core/optimization_guide_features.cc
+++ b/components/optimization_guide/core/optimization_guide_features.cc
@@ -18,7 +18,6 @@
 #include "base/strings/string_util.h"
 #include "base/strings/to_string.h"
 #include "base/system/sys_info.h"
-#include "build/build_config.h"
 #include "components/optimization_guide/core/feature_registry/mqls_feature_registry.h"
 #include "components/optimization_guide/core/insertion_ordered_set.h"
 #include "components/optimization_guide/core/model_execution/feature_keys.h"
@@ -141,7 +140,11 @@
 // Whether to use the on device model service in optimization guide.
 BASE_FEATURE(kOptimizationGuideOnDeviceModel,
              "OptimizationGuideOnDeviceModel",
+#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
+             base::FEATURE_ENABLED_BY_DEFAULT);
+#else
              base::FEATURE_DISABLED_BY_DEFAULT);
+#endif
 
 // Whether to allow on device model evaluation for Compose. This has no effect
 // if OptimizationGuideOnDeviceModel is off.
@@ -170,6 +173,13 @@
              "OnDeviceModelValidation",
              base::FEATURE_ENABLED_BY_DEFAULT);
 
+#if !BUILDFLAG(IS_ANDROID)
+// Enable the "Synapse" refreshed AI settings page.
+BASE_FEATURE(kAiSettingsPageRefresh,
+             "AiSettingsPageRefresh",
+             base::FEATURE_DISABLED_BY_DEFAULT);
+#endif
+
 // The default value here is a bit of a guess.
 // TODO(crbug.com/40163041): This should be tuned once metrics are available.
 base::TimeDelta PageTextExtractionOutstandingRequestsGracePeriod() {
diff --git a/components/optimization_guide/core/optimization_guide_features.h b/components/optimization_guide/core/optimization_guide_features.h
index d2b0ea7..bc79fbf9 100644
--- a/components/optimization_guide/core/optimization_guide_features.h
+++ b/components/optimization_guide/core/optimization_guide_features.h
@@ -18,6 +18,7 @@
 #include "base/feature_list.h"
 #include "base/metrics/field_trial_params.h"
 #include "base/time/time.h"
+#include "build/build_config.h"
 #include "components/optimization_guide/core/model_execution/feature_keys.h"
 #include "components/optimization_guide/core/optimization_guide_enums.h"
 #include "components/optimization_guide/proto/hints.pb.h"
@@ -78,6 +79,11 @@
 COMPONENT_EXPORT(OPTIMIZATION_GUIDE_FEATURES)
 BASE_DECLARE_FEATURE(kOnDeviceModelValidation);
 
+#if !BUILDFLAG(IS_ANDROID)
+COMPONENT_EXPORT(OPTIMIZATION_GUIDE_FEATURES)
+BASE_DECLARE_FEATURE(kAiSettingsPageRefresh);
+#endif
+
 typedef base::EnumSet<proto::RequestContext,
                       proto::RequestContext_MIN,
                       proto::RequestContext_MAX>
diff --git a/components/optimization_guide/proto/features/product_specifications.proto b/components/optimization_guide/proto/features/product_specifications.proto
index 1c45f77..e778ef5 100644
--- a/components/optimization_guide/proto/features/product_specifications.proto
+++ b/components/optimization_guide/proto/features/product_specifications.proto
@@ -88,6 +88,9 @@
 
   // Generated summary description for the product.
   repeated DescriptionText summary_description = 5;
+
+  // Buying options URL (jackpot URL).
+  string buying_options_url = 6;
 }
 
 // Can contain any available identifiers for the product specification.
diff --git a/components/page_load_metrics/browser/observers/use_counter_page_load_metrics_observer.cc b/components/page_load_metrics/browser/observers/use_counter_page_load_metrics_observer.cc
index 98a9bc5..7f289963 100644
--- a/components/page_load_metrics/browser/observers/use_counter_page_load_metrics_observer.cc
+++ b/components/page_load_metrics/browser/observers/use_counter_page_load_metrics_observer.cc
@@ -351,8 +351,8 @@
            WebDXFeature::kDetailsName},
           {WebFeature::kElementCheckVisibility,
            WebDXFeature::kElementCheckVisibility},
-          {WebFeature::kHTMLSearchElement, WebDXFeature::kHTMLSearchElement},
-          {WebFeature::kHTMLUnsafeMethods, WebDXFeature::kHTMLUnsafeMethods},
+          {WebFeature::kHTMLUnsafeMethods, WebDXFeature::kParseHtmlUnsafe},
+          {WebFeature::kHTMLSearchElement, WebDXFeature::kSearch},
           {WebFeature::kDialogElement, WebDXFeature::kDialog},
           {WebFeature::kV8DocumentPictureInPicture_RequestWindow_Method,
            WebDXFeature::kDocumentPictureInPicture},
@@ -520,6 +520,7 @@
           {CSSSampleId::kMaskPosition, WebDXFeature::kMasks},
           {CSSSampleId::kMaskMode, WebDXFeature::kMasks},
           {CSSSampleId::kMask, WebDXFeature::kMasks},
+          {CSSSampleId::kPaintOrder, WebDXFeature::kPaintOrder},
       }};
 
   return *kMap;
diff --git a/components/pdf/common/BUILD.gn b/components/pdf/common/BUILD.gn
index 59d5f4c..2c1d4aa8 100644
--- a/components/pdf/common/BUILD.gn
+++ b/components/pdf/common/BUILD.gn
@@ -3,6 +3,7 @@
 # found in the LICENSE file.
 
 import("//extensions/buildflags/buildflags.gni")
+import("//pdf/features.gni")
 
 # enable_pdf is not required. Some platforms may not have PDF enabled, but some
 # existing files still use the constants.
@@ -17,16 +18,23 @@
     "pdf_util.h",
   ]
 
+  public_deps = [ "//skia" ]
+
   deps = [
     "//base",
     "//content/public/common",
     "//extensions/buildflags",
+    "//pdf:buildflags",
     "//url",
   ]
 
   if (enable_extensions) {
     deps += [ "//extensions/common:common_constants" ]
   }
+
+  if (enable_pdf) {
+    deps += [ "//pdf:features" ]
+  }
 }
 
 source_set("unit_tests") {
diff --git a/components/pdf/common/DEPS b/components/pdf/common/DEPS
index 24b6327..a493e9f 100644
--- a/components/pdf/common/DEPS
+++ b/components/pdf/common/DEPS
@@ -1,4 +1,7 @@
 include_rules = [
   "+extensions/buildflags/buildflags.h",
   "+extensions/common/constants.h",
+  "+pdf/buildflags.h",
+  "+pdf/pdf_features.h",
+  "+third_party/skia/include/core/SkColor.h",
 ]
\ No newline at end of file
diff --git a/components/pdf/common/pdf_util.cc b/components/pdf/common/pdf_util.cc
index 4ee965e09..7d2e8212 100644
--- a/components/pdf/common/pdf_util.cc
+++ b/components/pdf/common/pdf_util.cc
@@ -7,12 +7,29 @@
 #include "base/metrics/histogram_macros.h"
 #include "content/public/common/url_utils.h"
 #include "extensions/buildflags/buildflags.h"
+#include "pdf/buildflags.h"
 #include "url/origin.h"
 
+#if BUILDFLAG(ENABLE_PDF)
+#include "base/feature_list.h"
+#include "pdf/pdf_features.h"
+#endif  // BUILDFLAG(ENABLE_PDF)
+
 #if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "extensions/common/constants.h"  // nogncheck
 #endif  // BUILDFLAG(ENABLE_EXTENSIONS)
 
+namespace {
+
+// LINT.IfChange(PdfBackgroundColor)
+constexpr SkColor kPdfExtensionBackgroundColor = SkColorSetRGB(82, 86, 89);
+#if BUILDFLAG(ENABLE_PDF)
+constexpr SkColor kPdfExtensionBackgroundColorCr23 = SkColorSetRGB(40, 40, 40);
+#endif  // BUILDFLAG(ENABLE_PDF)
+// LINT.ThenChange(//chrome/browser/resources/pdf/pdf_viewer.ts:PdfBackgroundColor)
+
+}  // namespace
+
 void ReportPDFLoadStatus(PDFLoadStatus status) {
   UMA_HISTOGRAM_ENUMERATION("PDF.LoadStatus2", status,
                             PDFLoadStatus::kPdfLoadStatusCount);
@@ -34,3 +51,12 @@
   return IsPdfExtensionOrigin(origin) ||
          content::IsPdfInternalPluginAllowedOrigin(origin);
 }
+
+SkColor GetPdfBackgroundColor() {
+#if BUILDFLAG(ENABLE_PDF)
+  if (base::FeatureList::IsEnabled(chrome_pdf::features::kPdfCr23)) {
+    return kPdfExtensionBackgroundColorCr23;
+  }
+#endif  // BUILDFLAG(ENABLE_PDF)
+  return kPdfExtensionBackgroundColor;
+}
diff --git a/components/pdf/common/pdf_util.h b/components/pdf/common/pdf_util.h
index 6c4d270..0ce608d 100644
--- a/components/pdf/common/pdf_util.h
+++ b/components/pdf/common/pdf_util.h
@@ -5,6 +5,8 @@
 #ifndef COMPONENTS_PDF_COMMON_PDF_UTIL_H_
 #define COMPONENTS_PDF_COMMON_PDF_UTIL_H_
 
+#include "third_party/skia/include/core/SkColor.h"
+
 namespace url {
 class Origin;
 }  // namespace url
@@ -31,4 +33,7 @@
 // parent of the frame that contains the in-process plugin.
 bool IsPdfInternalPluginAllowedOrigin(const url::Origin& origin);
 
+// Returns the background color of the PDF extension.
+SkColor GetPdfBackgroundColor();
+
 #endif  // COMPONENTS_PDF_COMMON_PDF_UTIL_H_
diff --git a/components/policy/resources/templates/policy_definitions/CertificateManagement/CAPlatformIntegrationEnabled.yaml b/components/policy/resources/templates/policy_definitions/CertificateManagement/CAPlatformIntegrationEnabled.yaml
index b601a51..5a565a6 100644
--- a/components/policy/resources/templates/policy_definitions/CertificateManagement/CAPlatformIntegrationEnabled.yaml
+++ b/components/policy/resources/templates/policy_definitions/CertificateManagement/CAPlatformIntegrationEnabled.yaml
@@ -23,10 +23,8 @@
   type: boolean
 # Intentionally not supported on ChromeOS as there is no "platform" certificate
 # store to integrate with.
-future_on:
-- chrome.linux
-- chrome.mac
-- chrome.win
-- android
+supported_on:
+- chrome.*:131-
+- android:131-
 tags: []
 type: main
diff --git a/components/privacy_sandbox/privacy_sandbox_attestations/preload/BUILD.gn b/components/privacy_sandbox/privacy_sandbox_attestations/preload/BUILD.gn
index d72aa9ba..35adfd4 100644
--- a/components/privacy_sandbox/privacy_sandbox_attestations/preload/BUILD.gn
+++ b/components/privacy_sandbox/privacy_sandbox_attestations/preload/BUILD.gn
@@ -14,3 +14,11 @@
 
   outputs = [ "$root_out_dir/$privacy_sandbox_attestations_component/{{source_file_part}}" ]
 }
+
+if (is_mac) {
+  bundle_data("component_bundle") {
+    sources = privacy_sandbox_attestations_resources
+
+    outputs = [ "{{bundle_contents_dir}}/Libraries/$privacy_sandbox_attestations_component/{{source_file_part}}" ]
+  }
+}
diff --git a/components/resources/search_engine_choice_scaled_resources.grdp b/components/resources/search_engine_choice_scaled_resources.grdp
index 87d186a..dd6e403f 100644
--- a/components/resources/search_engine_choice_scaled_resources.grdp
+++ b/components/resources/search_engine_choice_scaled_resources.grdp
@@ -41,8 +41,7 @@
   <structure type="chrome_scaled_image" name="IDR_PRIVACYWALL_ORG_PNG" file="search_engine_choice/privacywall_org.png" />
   <structure type="chrome_scaled_image" name="IDR_QWANT_COM_PNG" file="search_engine_choice/qwant_com.png" />
   <structure type="chrome_scaled_image" name="IDR_SEARCH_BRAVE_COM_PNG" file="search_engine_choice/search_brave_com.png" />
-  <structure type="chrome_scaled_image" name="IDR_SEZNAM_CZ_PNG" file="search_engine_choice/seznam_cz.png" />
-  <structure type="chrome_scaled_image" name="IDR_SEZNAM_SK_PNG" file="search_engine_choice/seznam_cz.png" />
+  <structure type="chrome_scaled_image" name="IDR_SEZNAM_PNG" file="search_engine_choice/seznam_cz.png" />
   <structure type="chrome_scaled_image" name="IDR_SE_YAHOO_COM_PNG" file="search_engine_choice/yahoo_com.png" />
   <structure type="chrome_scaled_image" name="IDR_SG_YAHOO_COM_PNG" file="search_engine_choice/yahoo_com.png" />
   <structure type="chrome_scaled_image" name="IDR_TH_YAHOO_COM_PNG" file="search_engine_choice/yahoo_com.png" />
diff --git a/components/search_engine_descriptions_strings.grd b/components/search_engine_descriptions_strings.grd
index 9f76d7c..1677ba6 100644
--- a/components/search_engine_descriptions_strings.grd
+++ b/components/search_engine_descriptions_strings.grd
@@ -134,6 +134,7 @@
     <file path="strings/search_engine_descriptions_strings_gl.xtb" lang="gl" />
     <file path="strings/search_engine_descriptions_strings_gu.xtb" lang="gu" />
     <file path="strings/search_engine_descriptions_strings_he.xtb" lang="he" />
+    <file path="strings/search_engine_descriptions_strings_hi.xtb" lang="hi" />
     <file path="strings/search_engine_descriptions_strings_hr.xtb" lang="hr" />
     <file path="strings/search_engine_descriptions_strings_hu.xtb" lang="hu" />
     <file path="strings/search_engine_descriptions_strings_id.xtb" lang="id" />
@@ -156,6 +157,8 @@
     <file path="strings/search_engine_descriptions_strings_sl.xtb" lang="sl" />
     <file path="strings/search_engine_descriptions_strings_sr.xtb" lang="sr" />
     <file path="strings/search_engine_descriptions_strings_sv.xtb" lang="sv" />
+    <file path="strings/search_engine_descriptions_strings_sw.xtb" lang="sw" />
+    <file path="strings/search_engine_descriptions_strings_ta.xtb" lang="ta" />
     <file path="strings/search_engine_descriptions_strings_te.xtb" lang="te" />
     <file path="strings/search_engine_descriptions_strings_th.xtb" lang="th" />
     <file path="strings/search_engine_descriptions_strings_tr.xtb" lang="tr" />
diff --git a/components/search_engines/COMPLIANCE_OWNERS b/components/search_engines/COMPLIANCE_OWNERS
new file mode 100644
index 0000000..fc38338
--- /dev/null
+++ b/components/search_engines/COMPLIANCE_OWNERS
@@ -0,0 +1,4 @@
+dgn@chromium.org
+ender@google.com
+jdonnelly@chromium.org
+orinj@chromium.org
diff --git a/components/search_engines/OWNERS b/components/search_engines/OWNERS
index fd82f29..dcc41b1 100644
--- a/components/search_engines/OWNERS
+++ b/components/search_engines/OWNERS
@@ -4,8 +4,14 @@
 # registrations we aim to keep long term.
 dgn@chromium.org
 
-per-file search_engine_choice*=file://components/search_engines/search_engine_choice/OWNERS
-per-file generated_marketing_snippets*=file://components/search_engines/search_engine_choice/OWNERS
+per-file search_engine_choice*=set noparent
+per-file search_engine_choice*=file://components/search_engines/search_engine_choice/COMPLIANCE_OWNERS
 
-# TODO: noparent it.
-per-file choice_made_location.h=file://components/search_engines/search_engine_choice/OWNERS
+per-file generated_marketing_snippets*=set noparent
+per-file generated_marketing_snippets*=file://components/search_engines/search_engine_choice/COMPLIANCE_OWNERS
+
+per-file choice_made_location.h=set noparent
+per-file choice_made_location.h=file://components/search_engines/search_engine_choice/COMPLIANCE_OWNERS
+
+per-file prepopulated_engines.json=set noparent
+per-file prepopulated_engines.json=file://components/search_engines/COMPLIANCE_OWNERS
diff --git a/components/search_engines/android/java/src/org/chromium/components/search_engines/SearchEngineChoiceServiceUnitTest.java b/components/search_engines/android/java/src/org/chromium/components/search_engines/SearchEngineChoiceServiceUnitTest.java
index ebef33f..ef55665 100644
--- a/components/search_engines/android/java/src/org/chromium/components/search_engines/SearchEngineChoiceServiceUnitTest.java
+++ b/components/search_engines/android/java/src/org/chromium/components/search_engines/SearchEngineChoiceServiceUnitTest.java
@@ -66,6 +66,9 @@
         configureClayBlockingFeature(mIsClayBlockingEnabled, /* isDarkLaunchEnabled= */ false);
 
         doReturn(Promise.rejected()).when(mDelegate).getDeviceCountry();
+        doReturn(new ObservableSupplierImpl<>(false))
+                .when(mDelegate)
+                .getIsDeviceChoiceRequiredSupplier();
     }
 
     @Test
diff --git a/components/search_engines/prepopulated_engines.json b/components/search_engines/prepopulated_engines.json
index d96cb2aa..46ca9d2c 100644
--- a/components/search_engines/prepopulated_engines.json
+++ b/components/search_engines/prepopulated_engines.json
@@ -312,27 +312,9 @@
       ]
     },
 
-    "seznam_cz": {
+    "seznam": {
       "name": "Seznam.cz",
-      "keyword": "seznam.cz",
-      "favicon_url": "https://search.seznam.cz/favicon.ico",
-      "search_url": "https://search.seznam.cz/?q={searchTerms}",
-      "suggest_url": "https://suggest.seznam.cz/fulltext_ff?phrase={searchTerms}",
-      "new_tab_url": "https://search.seznam.cz/newtab",
-      "type": "SEARCH_ENGINE_SEZNAM",
-      "id": 25,
-      "regulatory_extensions": [
-        {
-          "variant": "RegulatoryExtensionType::kAndroidEEA",
-          "search_params": "sourceid=srch_ga_cs"
-        }
-      ]
-    },
-
-    // TODO(b/335397380): Remove this entry once b/322513019 is fixed.
-    "seznam_sk": {
-      "name": "Seznam.cz",
-      "keyword": "seznam.sk",
+      "keyword": "seznam",
       "favicon_url": "https://search.seznam.cz/favicon.ico",
       "search_url": "https://search.seznam.cz/?q={searchTerms}",
       "suggest_url": "https://suggest.seznam.cz/fulltext_ff?phrase={searchTerms}",
diff --git a/components/search_engines/reconciling_template_url_data_holder.cc b/components/search_engines/reconciling_template_url_data_holder.cc
index fe77effb..20ec195 100644
--- a/components/search_engines/reconciling_template_url_data_holder.cc
+++ b/components/search_engines/reconciling_template_url_data_holder.cc
@@ -24,7 +24,19 @@
 ReconcilingTemplateURLDataHolder::GetOrComputeKeyword() const {
   std::u16string keyword = search_engine_->keyword();
 
-  if (!search_engine_->created_from_play_api || keyword != u"yahoo.com") {
+  if (!search_engine_->created_from_play_api) {
+    return {std::move(keyword), false};
+  }
+
+  // Old Play API 'seznam.cz' and 'seznam.sk' have been consolidated to
+  // 'seznam'.
+  if (keyword.starts_with(u"seznam.")) {
+    return {u"seznam", true};
+  }
+
+  // Old Play API 'yahoo.com' entries are reconciled with country-specific
+  // definitions.
+  if (keyword != u"yahoo.com") {
     return {std::move(keyword), false};
   }
 
@@ -58,7 +70,7 @@
     result = std::move(*engine_iter);
   } else if (switches::kReconcileWithAllKnownEngines.Get()) {
     auto all_engines = TemplateURLPrepopulateData::GetAllPrepopulatedEngines();
-    for (auto engine : all_engines) {
+    for (const auto* engine : all_engines) {
       if (engine->keyword == keyword) {
         result = TemplateURLDataFromPrepopulatedEngine(*engine);
         break;
@@ -84,7 +96,7 @@
     result = std::move(*engine_iter);
   } else if (switches::kReconcileWithAllKnownEngines.Get()) {
     auto all_engines = TemplateURLPrepopulateData::GetAllPrepopulatedEngines();
-    for (auto engine : all_engines) {
+    for (const auto* engine : all_engines) {
       if (engine->id == prepopulate_id) {
         result = TemplateURLDataFromPrepopulatedEngine(*engine);
         break;
diff --git a/components/search_engines/reconciling_template_url_data_holder_unittest.cc b/components/search_engines/reconciling_template_url_data_holder_unittest.cc
index 242ad384..d81b340 100644
--- a/components/search_engines/reconciling_template_url_data_holder_unittest.cc
+++ b/components/search_engines/reconciling_template_url_data_holder_unittest.cc
@@ -64,7 +64,7 @@
 }
 
 TEST_F(ReconcilingTemplateURLDataHolderTest,
-       GetOrComputeKeyword_Computed_EligibleFromPlay) {
+       GetOrComputeKeyword_Computed_EligibleFromPlay_Yahoo) {
   auto supplied_engine = GenerateDummyTemplateURLData("yahoo.com");
   supplied_engine->created_from_play_api = true;
   supplied_engine->SetURL("https://de.yahoo.com");
@@ -77,6 +77,22 @@
 }
 
 TEST_F(ReconcilingTemplateURLDataHolderTest,
+       GetOrComputeKeyword_Computed_EligibleFromPlay_Seznam) {
+  const char* const variants[] = {"seznam.cz", "seznam.sk"};
+
+  for (const auto* variant : variants) {
+    auto supplied_engine = GenerateDummyTemplateURLData(variant);
+    supplied_engine->created_from_play_api = true;
+    holder_.SetSearchEngineBypassingReconciliationForTesting(
+        std::move(supplied_engine));
+
+    auto [keyword, is_generated] = holder_.GetOrComputeKeyword();
+    ASSERT_EQ(keyword, u"seznam");
+    ASSERT_TRUE(is_generated);
+  }
+}
+
+TEST_F(ReconcilingTemplateURLDataHolderTest,
        FindMatchingBuiltInDefinitionsById_UnknownID) {
   auto engine = holder_.FindMatchingBuiltInDefinitionsById(~0);
   // Expect to see no definitions.
diff --git a/components/search_engines/search_engine_choice/COMPLIANCE_OWNERS b/components/search_engines/search_engine_choice/COMPLIANCE_OWNERS
new file mode 100644
index 0000000..1887010
--- /dev/null
+++ b/components/search_engines/search_engine_choice/COMPLIANCE_OWNERS
@@ -0,0 +1,5 @@
+dgn@chromium.org
+droger@chromium.org
+
+# Backup owners
+file://components/search_engines/COMPLIANCE_OWNERS
diff --git a/components/search_engines/search_engine_choice/OWNERS b/components/search_engines/search_engine_choice/OWNERS
deleted file mode 100644
index 6c1ceef..0000000
--- a/components/search_engines/search_engine_choice/OWNERS
+++ /dev/null
@@ -1,3 +0,0 @@
-dgn@chromium.org
-droger@chromium.org
-jyammine@google.com
\ No newline at end of file
diff --git a/components/search_engines/search_engine_choice/generated_marketing_snippets.cc b/components/search_engines/search_engine_choice/generated_marketing_snippets.cc
index 4e2d54dc..2a6b903 100644
--- a/components/search_engines/search_engine_choice/generated_marketing_snippets.cc
+++ b/components/search_engines/search_engine_choice/generated_marketing_snippets.cc
@@ -48,10 +48,7 @@
   if (engine_keyword == TemplateURLPrepopulateData::qwant.keyword) {
     return IDS_QWANT_SEARCH_DESCRIPTION;
   }
-  if (engine_keyword == TemplateURLPrepopulateData::seznam_cz.keyword) {
-    return IDS_SEZNAM_SEARCH_DESCRIPTION;
-  }
-  if (engine_keyword == TemplateURLPrepopulateData::seznam_sk.keyword) {
+  if (engine_keyword == TemplateURLPrepopulateData::seznam.keyword) {
     return IDS_SEZNAM_SEARCH_DESCRIPTION;
   }
   if (engine_keyword == TemplateURLPrepopulateData::yahoo.keyword) {
diff --git a/components/search_engines/search_engine_countries-inc.cc b/components/search_engines/search_engine_countries-inc.cc
index 468d6ca..1cc16fc 100644
--- a/components/search_engines/search_engine_countries-inc.cc
+++ b/components/search_engines/search_engine_countries-inc.cc
@@ -259,7 +259,7 @@
 // Czech Republic
 constexpr EngineAndTier engines_CZ[] = {
     {SearchEngineTier::kTopEngines, &google},
-    {SearchEngineTier::kTopEngines, &seznam_cz},
+    {SearchEngineTier::kTopEngines, &seznam},
     {SearchEngineTier::kTopEngines, &bing},
     {SearchEngineTier::kTopEngines, &duckduckgo},
     {SearchEngineTier::kTopEngines, &ecosia},
@@ -967,7 +967,7 @@
     {SearchEngineTier::kTopEngines, &google},
     {SearchEngineTier::kTopEngines, &duckduckgo},
     {SearchEngineTier::kTopEngines, &bing},
-    {SearchEngineTier::kTopEngines, &seznam_sk},
+    {SearchEngineTier::kTopEngines, &seznam},
     {SearchEngineTier::kTopEngines, &yahoo_emea},
     {SearchEngineTier::kRemainingEngines, &ecosia},
     {SearchEngineTier::kRemainingEngines, &qwant},
diff --git a/components/search_engines/search_engine_utils.cc b/components/search_engines/search_engine_utils.cc
index 99449d3..ede9427 100644
--- a/components/search_engines/search_engine_utils.cc
+++ b/components/search_engines/search_engine_utils.cc
@@ -2,11 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifdef UNSAFE_BUFFERS_BUILD
-// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
-#pragma allow_unsafe_buffers
-#endif
-
 #include "components/search_engines/search_engine_utils.h"
 
 #include "components/google/core/common/google_util.h"
@@ -43,19 +38,15 @@
     return TemplateURLPrepopulateData::google.type;
 
   // Now check the rest of the prepopulate data.
-  for (size_t i = 0; i < TemplateURLPrepopulateData::kAllEnginesLength; ++i) {
-    // First check the main search URL.
-    if (SameDomain(
-            url, GURL(TemplateURLPrepopulateData::kAllEngines[i]->search_url)))
-      return TemplateURLPrepopulateData::kAllEngines[i]->type;
+  for (const auto* engine : TemplateURLPrepopulateData::kAllEngines) {
+    if (SameDomain(url, GURL(engine->search_url))) {
+      return engine->type;
+    }
 
-    // Then check the alternate URLs.
-    for (size_t j = 0;
-         j < TemplateURLPrepopulateData::kAllEngines[i]->alternate_urls.size();
-         ++j) {
-      if (SameDomain(url, GURL(TemplateURLPrepopulateData::kAllEngines[i]
-                                   ->alternate_urls[j])))
-        return TemplateURLPrepopulateData::kAllEngines[i]->type;
+    for (const auto* alt_url : engine->alternate_urls) {
+      if (SameDomain(url, GURL(alt_url))) {
+        return engine->type;
+      }
     }
   }
 
diff --git a/components/search_engines/template_url_data_util.cc b/components/search_engines/template_url_data_util.cc
index dd382e0..18364f4 100644
--- a/components/search_engines/template_url_data_util.cc
+++ b/components/search_engines/template_url_data_util.cc
@@ -2,11 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifdef UNSAFE_BUFFERS_BUILD
-// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
-#pragma allow_unsafe_buffers
-#endif
-
 #include "components/search_engines/template_url_data_util.h"
 
 #include <string>
diff --git a/components/search_engines/template_url_prepopulate_data.cc b/components/search_engines/template_url_prepopulate_data.cc
index 208ef4a..6c32b0e6 100644
--- a/components/search_engines/template_url_prepopulate_data.cc
+++ b/components/search_engines/template_url_prepopulate_data.cc
@@ -2,11 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifdef UNSAFE_BUFFERS_BUILD
-// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
-#pragma allow_unsafe_buffers
-#endif
-
 #include "components/search_engines/template_url_prepopulate_data.h"
 
 #include <algorithm>
@@ -302,8 +297,7 @@
   // case of IDs shared across multiple entries, we might be returning the
   // wrong one for the profile country. We can look into better heuristics in
   // future work.
-  for (size_t i = 0; i < kAllEnginesLength; ++i) {
-    const PrepopulatedEngine* engine = kAllEngines[i];
+  for (const PrepopulatedEngine* engine : kAllEngines) {
     if (engine->id == prepopulated_id) {
       return TemplateURLDataFromPrepopulatedEngine(*engine);
     }
@@ -328,9 +322,8 @@
                                         /*use_first_as_fallback=*/true);
 }
 
-std::vector<const PrepopulatedEngine*> GetAllPrepopulatedEngines() {
-  return std::vector<const PrepopulatedEngine*>(
-      &kAllEngines[0], &kAllEngines[0] + kAllEnginesLength);
+const base::span<const PrepopulatedEngine* const> GetAllPrepopulatedEngines() {
+  return kAllEngines;
 }
 
 std::vector<std::unique_ptr<TemplateURLData>>
diff --git a/components/search_engines/template_url_prepopulate_data.h b/components/search_engines/template_url_prepopulate_data.h
index fb2d7340..6d987297 100644
--- a/components/search_engines/template_url_prepopulate_data.h
+++ b/components/search_engines/template_url_prepopulate_data.h
@@ -11,6 +11,7 @@
 #include <string>
 #include <vector>
 
+#include "base/containers/span.h"
 #include "base/memory/raw_ptr.h"
 #include "build/build_config.h"
 
@@ -102,7 +103,7 @@
     search_engines::SearchEngineChoiceService* search_engine_choice_service);
 
 // Returns all prepopulated engines for all locales.
-std::vector<const PrepopulatedEngine*> GetAllPrepopulatedEngines();
+const base::span<const PrepopulatedEngine* const> GetAllPrepopulatedEngines();
 
 // Returns all the prepopulated engines that are used in the EEA region.
 std::vector<std::unique_ptr<TemplateURLData>>
diff --git a/components/search_engines/template_url_prepopulate_data_unittest.cc b/components/search_engines/template_url_prepopulate_data_unittest.cc
index 8901253..afffbc4c 100644
--- a/components/search_engines/template_url_prepopulate_data_unittest.cc
+++ b/components/search_engines/template_url_prepopulate_data_unittest.cc
@@ -2,11 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifdef UNSAFE_BUFFERS_BUILD
-// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
-#pragma allow_unsafe_buffers
-#endif
-
 #include "components/search_engines/template_url_prepopulate_data.h"
 
 #include <stddef.h>
@@ -555,9 +550,9 @@
     // Custom with a country TLD and almost no query params:
     "http://www.google.ru/search?q={searchTerms}"
   };
-  for (size_t i = 0; i < std::size(kGoogleURLs); ++i) {
+  for (const char* google_url : kGoogleURLs) {
     EXPECT_EQ(TemplateURLPrepopulateData::google.type,
-              GetEngineType(kGoogleURLs[i]));
+              GetEngineType(google_url));
   }
 
   // Non-Google URLs.
@@ -568,8 +563,8 @@
       // Aggressively match types by checking just TLD+1.
       "http://someothersite.yahoo.com/",
   };
-  for (size_t i = 0; i < std::size(kYahooURLs); ++i) {
-    EXPECT_EQ(SEARCH_ENGINE_YAHOO, GetEngineType(kYahooURLs[i]));
+  for (const char* yahoo_url : kYahooURLs) {
+    EXPECT_EQ(SEARCH_ENGINE_YAHOO, GetEngineType(yahoo_url));
   }
 
   // URLs for engines not present in country-specific lists.
@@ -595,7 +590,7 @@
 
 TEST_F(TemplateURLPrepopulateDataTest, GetEngineTypeForAllPrepopulatedEngines) {
   using PrepopulatedEngine = TemplateURLPrepopulateData::PrepopulatedEngine;
-  const std::vector<const PrepopulatedEngine*> all_engines =
+  const auto all_engines =
       TemplateURLPrepopulateData::GetAllPrepopulatedEngines();
   for (const PrepopulatedEngine* engine : all_engines) {
     std::unique_ptr<TemplateURLData> data =
@@ -607,7 +602,7 @@
 
 TEST_F(TemplateURLPrepopulateDataTest, CheckSearchURLDetection) {
   using PrepopulatedEngine = TemplateURLPrepopulateData::PrepopulatedEngine;
-  const std::vector<const PrepopulatedEngine*> all_engines =
+  const auto all_engines =
       TemplateURLPrepopulateData::GetAllPrepopulatedEngines();
   for (const PrepopulatedEngine* engine : all_engines) {
     std::unique_ptr<TemplateURLData> data =
@@ -631,7 +626,7 @@
       62, 63, 64, 65, 66, 68, 70, 74, 75, 76, 77, 78, 79, 80, 81, 85, 90,
   };
   using PrepopulatedEngine = TemplateURLPrepopulateData::PrepopulatedEngine;
-  const std::vector<const PrepopulatedEngine*> all_engines =
+  const auto all_engines =
       TemplateURLPrepopulateData::GetAllPrepopulatedEngines();
   for (const PrepopulatedEngine* engine : all_engines) {
     std::unique_ptr<TemplateURLData> data =
diff --git a/components/strings/search_engine_descriptions_strings_hi.xtb b/components/strings/search_engine_descriptions_strings_hi.xtb
new file mode 100644
index 0000000..b8c8313
--- /dev/null
+++ b/components/strings/search_engine_descriptions_strings_hi.xtb
@@ -0,0 +1,9 @@
+
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<!-- This file is generated using
+http://go/chrome-search-engines-marketing-snippets-script.
+Do not modify it manually. -->
+<translationbundle lang="hi">
+  <translation id="IDS_GOOGLE_SEARCH_DESCRIPTION">Google की मदद से ज़रूरी जानकारी पाएं</translation>
+</translationbundle>
diff --git a/components/strings/search_engine_descriptions_strings_no.xtb b/components/strings/search_engine_descriptions_strings_no.xtb
index db9eafe..8ea2a6f 100644
--- a/components/strings/search_engine_descriptions_strings_no.xtb
+++ b/components/strings/search_engine_descriptions_strings_no.xtb
@@ -6,8 +6,6 @@
 Do not modify it manually. -->
 <translationbundle lang="no">
   <translation id="IDS_BING_SEARCH_DESCRIPTION">Drevet av ChatGPTs GPT-4</translation>
-  <translation id="IDS_ECOSIA_SEARCH_DESCRIPTION">Plant trær mens du søker på nettet. Over 200 millioner plantede trær.</translation>
   <translation id="IDS_GOOGLE_SEARCH_DESCRIPTION">Finn det du trenger med Google</translation>
-  <translation id="IDS_OCEANHERO_SEARCH_DESCRIPTION">Søkene dine renser havene og strender for plast.</translation>
   <translation id="IDS_PRIVACYWALL_SEARCH_DESCRIPTION">En søkemotor som beskytter ditt personvern.</translation>
 </translationbundle>
diff --git a/components/strings/search_engine_descriptions_strings_sw.xtb b/components/strings/search_engine_descriptions_strings_sw.xtb
new file mode 100644
index 0000000..d1298c7
--- /dev/null
+++ b/components/strings/search_engine_descriptions_strings_sw.xtb
@@ -0,0 +1,9 @@
+
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<!-- This file is generated using
+http://go/chrome-search-engines-marketing-snippets-script.
+Do not modify it manually. -->
+<translationbundle lang="sw">
+  <translation id="IDS_GOOGLE_SEARCH_DESCRIPTION">Pata unachohitaji ukitumia Google</translation>
+</translationbundle>
diff --git a/components/strings/search_engine_descriptions_strings_ta.xtb b/components/strings/search_engine_descriptions_strings_ta.xtb
new file mode 100644
index 0000000..31b509f
--- /dev/null
+++ b/components/strings/search_engine_descriptions_strings_ta.xtb
@@ -0,0 +1,9 @@
+
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<!-- This file is generated using
+http://go/chrome-search-engines-marketing-snippets-script.
+Do not modify it manually. -->
+<translationbundle lang="ta">
+  <translation id="IDS_GOOGLE_SEARCH_DESCRIPTION">உங்களுக்குத் தேவையானவற்றை Google மூலம் கண்டறியுங்கள்</translation>
+</translationbundle>
diff --git a/components/subresource_filter/content/shared/browser/page_load_statistics.cc b/components/subresource_filter/content/shared/browser/page_load_statistics.cc
index eee01d75..ad672e1 100644
--- a/components/subresource_filter/content/shared/browser/page_load_statistics.cc
+++ b/components/subresource_filter/content/shared/browser/page_load_statistics.cc
@@ -119,12 +119,10 @@
         aggregated_document_statistics_.evaluation_total_cpu_duration,
         base::Microseconds(1), base::Seconds(10), 50);
   } else {
-    CHECK(aggregated_document_statistics_.evaluation_total_wall_duration
-              .is_zero(),
-          base::NotFatalUntil::M132);
-    CHECK(
-        aggregated_document_statistics_.evaluation_total_cpu_duration.is_zero(),
-        base::NotFatalUntil::M132);
+    DCHECK(aggregated_document_statistics_.evaluation_total_wall_duration
+               .is_zero());
+    DCHECK(aggregated_document_statistics_.evaluation_total_cpu_duration
+               .is_zero());
   }
 }
 
diff --git a/components/variations/BUILD.gn b/components/variations/BUILD.gn
index b24db92a..a69e200c 100644
--- a/components/variations/BUILD.gn
+++ b/components/variations/BUILD.gn
@@ -63,6 +63,8 @@
     "pref_names.h",
     "processed_study.cc",
     "processed_study.h",
+    "seed_reader_writer.cc",
+    "seed_reader_writer.h",
     "seed_response.cc",
     "seed_response.h",
     "study_filtering.cc",
@@ -228,6 +230,7 @@
     "net/variations_command_line_unittest.cc",
     "net/variations_http_headers_unittest.cc",
     "processed_study_unittest.cc",
+    "seed_reader_writer_unittest.cc",
     "simulate_for_crosstalk_unittest.cc",
     "study_filtering_unittest.cc",
     "synthetic_trial_registry_unittest.cc",
diff --git a/components/variations/seed_reader_writer.cc b/components/variations/seed_reader_writer.cc
new file mode 100644
index 0000000..36fe0e6
--- /dev/null
+++ b/components/variations/seed_reader_writer.cc
@@ -0,0 +1,130 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/variations/seed_reader_writer.h"
+
+#include "base/files/file.h"
+#include "base/files/file_path.h"
+#include "base/files/important_file_writer.h"
+#include "base/functional/bind.h"
+#include "base/logging.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/strings/string_util.h"
+#include "base/task/sequenced_task_runner.h"
+#include "components/prefs/pref_service.h"
+#include "components/variations/pref_names.h"
+
+namespace variations {
+namespace {
+
+// Histogram suffix used by ImportantFileWriter for recording seed file write
+// information.
+// TODO(crbug.com/369080917): Add this back when we begin writing.
+// constexpr char kSeedWriterHistogramSuffix[] = "VariationsSeedsV1";
+
+// Returns true if a seed should be written to its new storage.
+bool ShouldWriteToNewSeedStorage(version_info::Channel channel) {
+  return channel == version_info::Channel::CANARY ||
+         channel == version_info::Channel::DEV ||
+         channel == version_info::Channel::BETA;
+}
+
+// Serializes and returns seed data used during write to disk. Will be run
+// asynchronously on a background thread.
+std::optional<std::string> DoSerialize(std::string seed_data) {
+  // TODO(crbug.com/370480037): Begin doing seed compression here instead of in
+  // VariationsSeedStore.
+  return seed_data;
+}
+
+}  // namespace
+
+SeedReaderWriter::SeedReaderWriter(
+    PrefService* local_state,
+    const base::FilePath& seed_file_path,
+    const version_info::Channel channel,
+    scoped_refptr<base::SequencedTaskRunner> file_task_runner)
+    : local_state_(local_state),
+      channel_(channel),
+      file_task_runner_(std::move(file_task_runner)) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  if (!seed_file_path.empty()) {
+    seed_writer_ = std::make_unique<base::ImportantFileWriter>(
+        seed_file_path, file_task_runner_, std::string());
+  }
+}
+
+SeedReaderWriter::~SeedReaderWriter() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+}
+
+void SeedReaderWriter::StoreValidatedSeed(const std::string& seed_data) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  local_state_->SetString(prefs::kVariationsCompressedSeed, seed_data);
+  MaybeScheduleSeedFileWrite(seed_data);
+}
+
+void SeedReaderWriter::ClearSeed() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  local_state_->ClearPref(prefs::kVariationsCompressedSeed);
+  MaybeScheduleSeedFileWrite(std::string());
+}
+
+void SeedReaderWriter::SetTimerForTesting(base::OneShotTimer* timer_override) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  if (seed_writer_) {
+    seed_writer_->SetTimerForTesting(timer_override);  // IN-TEST
+  }
+}
+
+bool SeedReaderWriter::HasPendingWriteForTesting() const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  return seed_writer_ ? seed_writer_->HasPendingWrite() : false;
+}
+
+// static
+base::FilePath SeedReaderWriter::GetFilePath(
+    const base::FilePath& user_data_dir,
+    const base::FilePath::CharType* filename) {
+  return user_data_dir.empty() ? base::FilePath()
+                               : user_data_dir.Append(filename);
+}
+
+base::ImportantFileWriter::BackgroundDataProducerCallback
+SeedReaderWriter::GetSerializedDataProducerForBackgroundSequence() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  // DoSerialize() will be run on a background thread different than the one
+  // this function runs on, so `seed_data_` is passed as a copy to avoid
+  // potential race condition in which the `seed_data_ is potentially modified
+  // at the same time DoSerialize() attempts to access it. We cannot use
+  // std::move here as we may attempt to read `seed_data_` from memory after a
+  // write and before we modify `seed_data_` again, in which case unexpected
+  // empty data would be read.
+  // TODO(crbug.com/370539202) Potentially use std::move instead of copy if we
+  // are able to move seed data out of memory.
+  return base::BindOnce(&DoSerialize, seed_data_);
+}
+
+void SeedReaderWriter::MaybeScheduleSeedFileWrite(
+    const std::string& seed_data) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  if (seed_writer_ && ShouldWriteToNewSeedStorage(channel_)) {
+    // Set `seed_data_`, this will be used later by the background serialization
+    // and can be changed multiple times before a scheduled write completes, in
+    // which case the background serializer will use the `seed_data_` set at the
+    // last call of this function.
+    seed_data_ = seed_data;
+    // `seed_writer_` will eventually call
+    // GetSerializedDataProducerForBackgroundSequence() on *this* object to get
+    // a callback that will be run asynchronously. This callback will be used to
+    // call the DoSerialize() function which will return the seed data to write
+    // to the file. This write will also be asynchronous and on a different
+    // thread. Note that it is okay to call this while a write is already
+    // occurring in a background thread and that this will result in a new write
+    // being scheduled.
+    seed_writer_->ScheduleWriteWithBackgroundDataSerializer(this);
+  }
+}
+
+}  // namespace variations
diff --git a/components/variations/seed_reader_writer.h b/components/variations/seed_reader_writer.h
new file mode 100644
index 0000000..d9ced07
--- /dev/null
+++ b/components/variations/seed_reader_writer.h
@@ -0,0 +1,97 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_VARIATIONS_SEED_READER_WRITER_H_
+#define COMPONENTS_VARIATIONS_SEED_READER_WRITER_H_
+
+#include <string>
+
+#include "base/component_export.h"
+#include "base/files/file_path.h"
+#include "base/files/important_file_writer.h"
+#include "base/functional/callback.h"
+#include "base/memory/raw_ptr.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/sequence_checker.h"
+#include "base/task/sequenced_task_runner.h"
+#include "base/task/task_traits.h"
+#include "base/task/thread_pool.h"
+#include "base/version_info/channel.h"
+
+class PrefService;
+
+namespace variations {
+
+// Handles reading and writing seeds to disk.
+class COMPONENT_EXPORT(VARIATIONS) SeedReaderWriter
+    : public base::ImportantFileWriter::BackgroundDataSerializer {
+ public:
+  // `local_state` provides access to the local state prefs. Must not be null.
+  // `seed_file_path` denotes a file containing a seed. Note that Android
+  // Webview intentionally uses an empty path as it will continue using local
+  // state to store seeds.
+  // `channel` describes the browser's release channel.
+  // `file_task_runner` handles IO-related tasks. Must not be null.
+  SeedReaderWriter(PrefService* local_state,
+                   const base::FilePath& seed_file_path,
+                   const version_info::Channel channel,
+                   scoped_refptr<base::SequencedTaskRunner> file_task_runner =
+                       base::ThreadPool::CreateSequencedTaskRunner(
+                           {base::MayBlock(), base::TaskPriority::USER_VISIBLE,
+                            base::TaskShutdownBehavior::BLOCK_SHUTDOWN}));
+
+  SeedReaderWriter(const SeedReaderWriter&) = delete;
+  SeedReaderWriter& operator=(const SeedReaderWriter&) = delete;
+
+  ~SeedReaderWriter() override;
+
+  // Schedules a write of `serialized_data` to disk. For some clients (see
+  // ShouldWriteToNewSeedStorage()), two writes are scheduled.
+  void StoreValidatedSeed(const std::string& serialized_data);
+
+  // Clears seed data by overriding it with an empty string.
+  void ClearSeed();
+
+  // Overrides the timer used for scheduling writes with `timer_override`.
+  void SetTimerForTesting(base::OneShotTimer* timer_override);
+
+  // Returns whether or not `seed_writer_` currently has a write
+  // scheduled/pending.
+  bool HasPendingWriteForTesting() const;
+
+  // Returns the file path used to store a seed. If `user_data_dir` is empty, an
+  // empty file path is returned.
+  static base::FilePath GetFilePath(const base::FilePath& user_data_dir,
+                                    const base::FilePath::CharType* filename);
+
+ private:
+  // Returns the serialized data to be written to disk. This is done
+  // asynchronously during the write process.
+  base::ImportantFileWriter::BackgroundDataProducerCallback
+  GetSerializedDataProducerForBackgroundSequence() override;
+
+  // Schedules `seed_data` to be written using `seed_writer_`.
+  void MaybeScheduleSeedFileWrite(const std::string& seed_data);
+
+  // Pref service used to persist seeds.
+  raw_ptr<PrefService> local_state_;
+
+  // Channel the client is apart of.
+  const version_info::Channel channel_;
+
+  // Task runner for IO-related operations.
+  const scoped_refptr<base::SequencedTaskRunner> file_task_runner_;
+
+  // Helper for safely writing a seed. Null if a seed file path is not given.
+  std::unique_ptr<base::ImportantFileWriter> seed_writer_;
+
+  // The compressed seed data.
+  std::string seed_data_;
+
+  SEQUENCE_CHECKER(sequence_checker_);
+};
+
+}  // namespace variations
+
+#endif  // COMPONENTS_VARIATIONS_SEED_READER_WRITER_H_
diff --git a/components/variations/seed_reader_writer_unittest.cc b/components/variations/seed_reader_writer_unittest.cc
new file mode 100644
index 0000000..0ec37d4a
--- /dev/null
+++ b/components/variations/seed_reader_writer_unittest.cc
@@ -0,0 +1,233 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/variations/seed_reader_writer.h"
+
+#include "base/files/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/test/task_environment.h"
+#include "base/threading/thread.h"
+#include "base/timer/mock_timer.h"
+#include "components/prefs/testing_pref_service.h"
+#include "components/variations/pref_names.h"
+#include "components/variations/variations_seed_store.h"
+#include "components/variations/variations_test_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/zlib/google/compression_utils.h"
+
+namespace variations {
+namespace {
+
+using ::testing::TestWithParam;
+using ::testing::Values;
+
+const base::FilePath::CharType kSeedFilename[] = FILE_PATH_LITERAL("TestSeed");
+
+VariationsSeed CreateTestSeed() {
+  VariationsSeed seed;
+  seed.add_study()->set_name("TestStudy");
+  return seed;
+}
+
+std::string SerializeSeed(const VariationsSeed& seed) {
+  std::string serialized_seed;
+  seed.SerializeToString(&serialized_seed);
+  return serialized_seed;
+}
+
+class SeedReaderWriterTest : public TestWithParam<version_info::Channel> {
+ public:
+  SeedReaderWriterTest() : file_writer_thread_("SeedReaderWriter Test thread") {
+    file_writer_thread_.Start();
+    if (!temp_dir_.CreateUniqueTempDir()) {
+      ADD_FAILURE() << "Failed to create temp directory.";
+    }
+    VariationsSeedStore::RegisterPrefs(local_state_.registry());
+  }
+  ~SeedReaderWriterTest() override = default;
+
+ protected:
+  base::Thread file_writer_thread_;
+  base::ScopedTempDir temp_dir_;
+  TestingPrefServiceSimple local_state_;
+  base::MockOneShotTimer timer_;
+};
+
+class SeedReaderWriterPreStableTest : public SeedReaderWriterTest {};
+
+class SeedReaderWriterStableAndUnknownTest : public SeedReaderWriterTest {};
+
+// Verifies that pre-stable clients write latest seeds to Local State and the
+// new seed file.
+TEST_P(SeedReaderWriterPreStableTest, WriteSeed) {
+  base::FilePath temp_seed_file_path =
+      SeedReaderWriter::GetFilePath(temp_dir_.GetPath(), kSeedFilename);
+
+  // Initialize seed_reader_writer with test thread and timer.
+  SeedReaderWriter seed_reader_writer(&local_state_, temp_seed_file_path,
+                                      GetParam(),
+                                      file_writer_thread_.task_runner());
+  seed_reader_writer.SetTimerForTesting(&timer_);
+
+  // Create and store seed. Note that the data format written here is contrived.
+  const std::string serialized_seed = SerializeSeed(CreateTestSeed());
+  seed_reader_writer.StoreValidatedSeed(serialized_seed);
+
+  // Force write.
+  timer_.Fire();
+  file_writer_thread_.FlushForTesting();
+
+  // Verify seed stored correctly, should be found in both prefs and the seed
+  // file.
+  std::string seed_file_data;
+  EXPECT_TRUE(base::ReadFileToString(temp_seed_file_path, &seed_file_data));
+  EXPECT_EQ(seed_file_data, serialized_seed);
+  EXPECT_EQ(local_state_.GetString(prefs::kVariationsCompressedSeed),
+            serialized_seed);
+}
+
+// Verifies that pre-stable clients clear latest seeds in Local State and the
+// new seed file.
+TEST_P(SeedReaderWriterPreStableTest, ClearSeed) {
+  base::FilePath temp_seed_file_path =
+      SeedReaderWriter::GetFilePath(temp_dir_.GetPath(), kSeedFilename);
+
+  // Initialize seed_reader_writer with test thread and timer.
+  SeedReaderWriter seed_reader_writer(&local_state_, temp_seed_file_path,
+                                      GetParam(),
+                                      file_writer_thread_.task_runner());
+  seed_reader_writer.SetTimerForTesting(&timer_);
+
+  // Create and store seed. Note that the data format written here is contrived.
+  const std::string serialized_seed = SerializeSeed(CreateTestSeed());
+  seed_reader_writer.StoreValidatedSeed(serialized_seed);
+
+  // Force write.
+  timer_.Fire();
+  file_writer_thread_.FlushForTesting();
+
+  // Verify seed stored correctly.
+  std::string seed_file_data;
+  EXPECT_TRUE(base::ReadFileToString(temp_seed_file_path, &seed_file_data));
+  EXPECT_EQ(seed_file_data, serialized_seed);
+  EXPECT_EQ(local_state_.GetString(prefs::kVariationsCompressedSeed),
+            serialized_seed);
+
+  // Clear seed and force write.
+  seed_reader_writer.ClearSeed();
+  timer_.Fire();
+  file_writer_thread_.FlushForTesting();
+
+  // Verify seed cleared correctly in both prefs and the seed file.
+  EXPECT_TRUE(base::ReadFileToString(temp_seed_file_path, &seed_file_data));
+  EXPECT_EQ(seed_file_data, std::string());
+  EXPECT_EQ(local_state_.GetString(prefs::kVariationsCompressedSeed),
+            std::string());
+}
+
+INSTANTIATE_TEST_SUITE_P(All,
+                         SeedReaderWriterPreStableTest,
+                         Values(version_info::Channel::CANARY,
+                                version_info::Channel::DEV,
+                                version_info::Channel::BETA));
+
+// Verifies that stable and unknown channel clients write latest seeds only to
+// Local State.
+TEST_P(SeedReaderWriterStableAndUnknownTest, WriteSeed) {
+  base::FilePath temp_seed_file_path =
+      SeedReaderWriter::GetFilePath(temp_dir_.GetPath(), kSeedFilename);
+
+  // Initialize seed_reader_writer with test thread and timer.
+  SeedReaderWriter seed_reader_writer(&local_state_, temp_seed_file_path,
+                                      GetParam(),
+                                      file_writer_thread_.task_runner());
+  seed_reader_writer.SetTimerForTesting(&timer_);
+
+  // Create and store seed. Note that the data format written here is contrived.
+  const std::string serialized_seed = SerializeSeed(CreateTestSeed());
+  seed_reader_writer.StoreValidatedSeed(serialized_seed);
+
+  // Ensure there's no pending write.
+  EXPECT_FALSE(seed_reader_writer.HasPendingWriteForTesting());
+  ASSERT_FALSE(timer_.IsRunning());
+
+  // Verify seed stored correctly, should only be found in prefs.
+  std::string seed_file_data;
+  EXPECT_FALSE(base::ReadFileToString(temp_seed_file_path, &seed_file_data));
+  EXPECT_EQ(local_state_.GetString(prefs::kVariationsCompressedSeed),
+            serialized_seed);
+}
+
+// Verifies that stable and unknown channel clients clear latest seeds only in
+// Local State.
+TEST_P(SeedReaderWriterStableAndUnknownTest, ClearSeed) {
+  base::FilePath temp_seed_file_path =
+      SeedReaderWriter::GetFilePath(temp_dir_.GetPath(), kSeedFilename);
+
+  // Initialize seed_reader_writer with test thread and timer.
+  SeedReaderWriter seed_reader_writer(&local_state_, temp_seed_file_path,
+                                      GetParam(),
+                                      file_writer_thread_.task_runner());
+  seed_reader_writer.SetTimerForTesting(&timer_);
+
+  // Create and store seed. Note that the data format written here is contrived.
+  const std::string serialized_seed = SerializeSeed(CreateTestSeed());
+  seed_reader_writer.StoreValidatedSeed(serialized_seed);
+
+  // Verify seed stored correctly.
+  EXPECT_EQ(local_state_.GetString(prefs::kVariationsCompressedSeed),
+            serialized_seed);
+
+  // Clear seed and ensure there's no pending write.
+  seed_reader_writer.ClearSeed();
+  EXPECT_FALSE(seed_reader_writer.HasPendingWriteForTesting());
+  ASSERT_FALSE(timer_.IsRunning());
+
+  // Verify seed cleared correctly in prefs.
+  std::string seed_file_data;
+  EXPECT_FALSE(base::ReadFileToString(temp_seed_file_path, &seed_file_data));
+  EXPECT_EQ(local_state_.GetString(prefs::kVariationsCompressedSeed),
+            std::string());
+}
+
+INSTANTIATE_TEST_SUITE_P(All,
+                         SeedReaderWriterStableAndUnknownTest,
+                         Values(version_info::Channel::STABLE,
+                                version_info::Channel::UNKNOWN));
+
+// Verifies that writing latest seeds with an empty path for `seed_file_path`
+// does not cause a crash.
+TEST_P(SeedReaderWriterTest, EmptySeedFilePathIsValid) {
+  base::FilePath temp_seed_file_path =
+      SeedReaderWriter::GetFilePath(temp_dir_.GetPath(), kSeedFilename);
+
+  // Initialize seed_reader_writer with test thread and timer and an empty file
+  // path.
+  SeedReaderWriter seed_reader_writer(&local_state_,
+                                      /*seed_file_path=*/base::FilePath(),
+                                      GetParam(),
+                                      file_writer_thread_.task_runner());
+  seed_reader_writer.SetTimerForTesting(&timer_);
+
+  // Create and store seed. Note that the data format written here is contrived.
+  const std::string serialized_seed = SerializeSeed(CreateTestSeed());
+  seed_reader_writer.StoreValidatedSeed(serialized_seed);
+
+  // Ensure there's no pending write.
+  EXPECT_FALSE(seed_reader_writer.HasPendingWriteForTesting());
+  ASSERT_FALSE(timer_.IsRunning());
+
+  // Verify seed stored correctly, should only be found in prefs.
+  EXPECT_EQ(local_state_.GetString(prefs::kVariationsCompressedSeed),
+            serialized_seed);
+}
+INSTANTIATE_TEST_SUITE_P(All,
+                         SeedReaderWriterTest,
+                         Values(version_info::Channel::CANARY,
+                                version_info::Channel::DEV,
+                                version_info::Channel::BETA,
+                                version_info::Channel::STABLE,
+                                version_info::Channel::UNKNOWN));
+}  // namespace
+}  // namespace variations
diff --git a/components/viz/service/display_embedder/skia_output_device_dcomp.cc b/components/viz/service/display_embedder/skia_output_device_dcomp.cc
index 34fe6199..4335f3f 100644
--- a/components/viz/service/display_embedder/skia_output_device_dcomp.cc
+++ b/components/viz/service/display_embedder/skia_output_device_dcomp.cc
@@ -11,6 +11,7 @@
 #include "base/debug/alias.h"
 #include "base/memory/scoped_refptr.h"
 #include "components/viz/common/resources/shared_image_format.h"
+#include "components/viz/common/switches.h"
 #include "components/viz/service/display_embedder/skia_output_surface_dependency.h"
 #include "gpu/command_buffer/common/mailbox.h"
 #include "gpu/command_buffer/service/feature_info.h"
@@ -113,6 +114,12 @@
   capabilities_.output_surface_origin = gfx::SurfaceOrigin::kTopLeft;
   capabilities_.number_of_buffers =
       gl::DirectCompositionRootSurfaceBufferCount();
+  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kDoubleBufferCompositing)) {
+    // Use switch "double-buffer-compositing" to force 1 |max_pending_swaps|
+    // when the feature |DCompTripleBufferRootSwapChain| is enabled.
+    capabilities_.number_of_buffers = 2;
+  }
   if (feature_info->workarounds().supports_two_yuv_hardware_overlays) {
     capabilities_.allowed_yuv_overlay_count = 2;
   }
@@ -126,7 +133,8 @@
           : OutputSurface::DCSupportLevel::kDCLayers;
   capabilities_.supports_post_sub_buffer = true;
   capabilities_.supports_delegated_ink = presenter_->SupportsDelegatedInk();
-  capabilities_.pending_swap_params.max_pending_swaps = 1;
+  capabilities_.pending_swap_params.max_pending_swaps =
+      capabilities_.number_of_buffers - 1;
   capabilities_.renderer_allocates_images = true;
   capabilities_.supports_viewporter = presenter_->SupportsViewporter();
   capabilities_.supports_non_backed_solid_color_overlays = true;
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index c69bd7c9..70e143f 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -1275,7 +1275,6 @@
     "loader/url_loader_factory_utils.cc",
     "loader/url_loader_factory_utils.h",
     "loader/url_loader_throttles.cc",
-    "locks/lock_manager.cc",
     "locks/lock_manager.h",
     "log_console_message.cc",
     "log_console_message.h",
diff --git a/content/browser/interest_group/ad_auction_service_impl.cc b/content/browser/interest_group/ad_auction_service_impl.cc
index fdd0321..f91f9ea 100644
--- a/content/browser/interest_group/ad_auction_service_impl.cc
+++ b/content/browser/interest_group/ad_auction_service_impl.cc
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include <cstddef>
 #ifdef UNSAFE_BUFFERS_BUILD
 // TODO(crbug.com/342213636): Remove this and spanify to fix the errors.
 #pragma allow_unsafe_buffers
@@ -448,13 +449,16 @@
   // on-device auction.
   if (base::FeatureList::IsEnabled(features::kFledgeUsePreconnectCache) &&
       !config.server_response.has_value()) {
-    PreconnectToBuyerOrigins(config);
+    size_t n_owners_cached = PreconnectToBuyerOrigins(config);
     for (const blink::AuctionConfig& component_config :
          config.non_shared_params.component_auctions) {
       if (!component_config.server_response.has_value()) {
-        PreconnectToBuyerOrigins(component_config);
+        n_owners_cached += PreconnectToBuyerOrigins(component_config);
       }
     }
+    base::UmaHistogramCounts100(
+        "Ads.InterestGroup.Auction.NumOwnerOriginsCachedForPreconnect",
+        n_owners_cached);
   }
 
   std::unique_ptr<AuctionRunner> auction = AuctionRunner::CreateAndStart(
@@ -1248,11 +1252,12 @@
       render_frame_host().GetPage());
 }
 
-void AdAuctionServiceImpl::PreconnectToBuyerOrigins(
+size_t AdAuctionServiceImpl::PreconnectToBuyerOrigins(
     const blink::AuctionConfig& config) {
   if (!config.non_shared_params.interest_group_buyers) {
-    return;
+    return 0;
   }
+  size_t n_owners_cached = 0;
   for (const auto& buyer : *config.non_shared_params.interest_group_buyers) {
     std::optional<url::Origin> signals_origin;
     if (GetInterestGroupManager().GetCachedOwnerAndSignalsOrigins(
@@ -1261,6 +1266,7 @@
           net::NetworkAnonymizationKey::CreateSameSite(
               net::SchemefulSite(buyer));
       PreconnectSocket(buyer.GetURL(), network_anonymization_key);
+      n_owners_cached += 1;
       if (signals_origin) {
         // We preconnect to the signals origin and not the full signals URL so
         // that we do not need to store the full URL in memory. Preconnecting
@@ -1270,6 +1276,7 @@
       }
     }
   }
+  return n_owners_cached;
 }
 
 }  // namespace content
diff --git a/content/browser/interest_group/ad_auction_service_impl.h b/content/browser/interest_group/ad_auction_service_impl.h
index 688db75..46768c8 100644
--- a/content/browser/interest_group/ad_auction_service_impl.h
+++ b/content/browser/interest_group/ad_auction_service_impl.h
@@ -210,8 +210,9 @@
   // For each buyer in `config`, preconnect to its origin and bidding signals
   // origin if the origins have been cached from previous interest group joins
   // or auctions. This function needs to be called separately to preconnect to
-  // origins for `config`'s component auctions.
-  void PreconnectToBuyerOrigins(const blink::AuctionConfig& config);
+  // origins for `config`'s component auctions. Returns the number of buyers
+  // that were preconnected.
+  size_t PreconnectToBuyerOrigins(const blink::AuctionConfig& config);
 
   // To avoid race conditions associated with top frame navigations (mentioned
   // in document_service.h), we need to save the values of the main frame
diff --git a/content/browser/interest_group/auction_runner_unittest.cc b/content/browser/interest_group/auction_runner_unittest.cc
index b1244d6..792089b 100644
--- a/content/browser/interest_group/auction_runner_unittest.cc
+++ b/content/browser/interest_group/auction_runner_unittest.cc
@@ -17178,6 +17178,10 @@
                                                            /*value=*/95),
                             BuildPrivateAggregationRequest(/*bucket=*/60,
                                                            /*value=*/400)))));
+
+  histogram_tester_->ExpectUniqueSample(
+      "Ads.InterestGroup.Auction.PABaseValueUsed",
+      auction_worklet::mojom::BaseValue::kAverageCodeFetchTime, 5);
 }
 
 TEST_F(AuctionRunnerTest, PrivateAggregationMetricsPerParticipant) {
@@ -17474,6 +17478,30 @@
                                                            /*value=*/0),
                             BuildPrivateAggregationRequest(/*bucket=*/61,
                                                            /*value=*/100)))));
+
+  // Things that are requested on 2 buyers, seller, plus 2 reporting occur
+  // 5 times; even if some of those reports are 0 because they're not computed
+  // for some clients.
+  //
+  // Those that are per-generateBid() occur 2 times.
+  //
+  // The list below is not exhaustive.
+  histogram_tester_->ExpectBucketCount(
+      "Ads.InterestGroup.Auction.PABaseValueUsed",
+      auction_worklet::mojom::BaseValue::kParticipatingInterestGroupCount, 5);
+  histogram_tester_->ExpectBucketCount(
+      "Ads.InterestGroup.Auction.PABaseValueUsed",
+      auction_worklet::mojom::BaseValue::kPercentScriptsTimeout, 5);
+  histogram_tester_->ExpectBucketCount(
+      "Ads.InterestGroup.Auction.PABaseValueUsed",
+      auction_worklet::mojom::BaseValue::kRegularInterestGroupsUsed, 2);
+  histogram_tester_->ExpectBucketCount(
+      "Ads.InterestGroup.Auction.PABaseValueUsed",
+      auction_worklet::mojom::BaseValue::kPercentRegularInterestGroupQuotaUsed,
+      2);
+  histogram_tester_->ExpectBucketCount(
+      "Ads.InterestGroup.Auction.PABaseValueUsed",
+      auction_worklet::mojom::BaseValue::kBidRejectReason, 0);
 }
 
 TEST_F(AuctionRunnerTest, ComponentAuctionPrivateAggregationTimeMetrics) {
diff --git a/content/browser/interest_group/interest_group_browsertest.cc b/content/browser/interest_group/interest_group_browsertest.cc
index 46f38d5..0b532f5 100644
--- a/content/browser/interest_group/interest_group_browsertest.cc
+++ b/content/browser/interest_group/interest_group_browsertest.cc
@@ -25754,32 +25754,6 @@
 
   content_browser_client_->AddToAllowList({signals_origin, owner_origin});
 
-  // Create the interest group we'll be using throughout the test.
-  GURL ad_url = owner_test_server->GetURL("a.test", "/echo?render_winner");
-  blink::InterestGroup interest_group =
-      blink::TestInterestGroupBuilder(
-          /*owner=*/url::Origin::Create(script_url),
-          /*name=*/"interest_group")
-          .SetBiddingUrl(script_url)
-          .SetTrustedBiddingSignalsUrl(trusted_bidding_signals_url)
-          .SetTrustedBiddingSignalsKeys({{"key1"}})
-          .SetAds({{{ad_url, std::nullopt}}})
-          .Build();
-
-  // Join the interest group with no ads so that when we run an auction, it will
-  // get filtered after it's loaded -- but we'll still preconnect to the server.
-  // This join will cache the bidding signals and owner origins.
-  blink::InterestGroup interest_group_without_ads = interest_group;
-  interest_group_without_ads.ads = std::nullopt;
-  AttachInterestGroupObserver();
-  manager_->JoinInterestGroup(interest_group_without_ads, joining_url);
-  WaitForAccessObserved({{"global", TestInterestGroupObserver::kJoin,
-                          interest_group_without_ads.owner, "interest_group"}});
-  std::optional<url::Origin> cached_signals_origin;
-  EXPECT_TRUE(manager_->GetCachedOwnerAndSignalsOrigins(
-      interest_group_without_ads.owner, cached_signals_origin));
-  EXPECT_EQ(cached_signals_origin, signals_origin);
-
   std::string config_template;
   if (UseConfigWithComponentAuction()) {
     config_template = R"({
@@ -25811,10 +25785,45 @@
                     "c.test", "/interest_group/decision_logic.js"),
                 owner_origin);
 
+  // We have no interest groups & nothing cached at this point.
   base::HistogramTester histogram_tester;
-  auto result = RunAuctionAndWait(auction_config);
+  auto result1 = RunAuctionAndWait(auction_config);
   histogram_tester.ExpectUniqueSample("Ads.InterestGroup.Auction.Result",
                                       AuctionResult::kNoInterestGroups, 1);
+  histogram_tester.ExpectUniqueSample(
+      "Ads.InterestGroup.Auction.NumOwnerOriginsCachedForPreconnect", 0, 1);
+
+  // Create the interest group we'll be using throughout the test.
+  GURL ad_url = owner_test_server->GetURL("a.test", "/echo?render_winner");
+  blink::InterestGroup interest_group =
+      blink::TestInterestGroupBuilder(
+          /*owner=*/url::Origin::Create(script_url),
+          /*name=*/"interest_group")
+          .SetBiddingUrl(script_url)
+          .SetTrustedBiddingSignalsUrl(trusted_bidding_signals_url)
+          .SetTrustedBiddingSignalsKeys({{"key1"}})
+          .SetAds({{{ad_url, std::nullopt}}})
+          .Build();
+
+  // Join the interest group with no ads so that when we run an auction, it will
+  // get filtered after it's loaded -- but we'll still preconnect to the server.
+  // This join will cache the bidding signals and owner origins.
+  blink::InterestGroup interest_group_without_ads = interest_group;
+  interest_group_without_ads.ads = std::nullopt;
+  AttachInterestGroupObserver();
+  manager_->JoinInterestGroup(interest_group_without_ads, joining_url);
+  WaitForAccessObserved({{"global", TestInterestGroupObserver::kJoin,
+                          interest_group_without_ads.owner, "interest_group"}});
+  std::optional<url::Origin> cached_signals_origin;
+  EXPECT_TRUE(manager_->GetCachedOwnerAndSignalsOrigins(
+      interest_group_without_ads.owner, cached_signals_origin));
+  EXPECT_EQ(cached_signals_origin, signals_origin);
+
+  auto result2 = RunAuctionAndWait(auction_config);
+  histogram_tester.ExpectUniqueSample("Ads.InterestGroup.Auction.Result",
+                                      AuctionResult::kNoInterestGroups, 2);
+  histogram_tester.ExpectBucketCount(
+      "Ads.InterestGroup.Auction.NumOwnerOriginsCachedForPreconnect", 1, 1);
 
   // We've preconnected to each server but received no requests.
   owner_connection_listener.WaitForAcceptedSockets(1u);
@@ -25830,10 +25839,12 @@
   WaitForAccessObserved({{"global", TestInterestGroupObserver::kJoin,
                           interest_group.owner, "interest_group"}});
 
-  auto result2 = RunAuctionAndWait(auction_config);
-  histogram_tester.ExpectTotalCount("Ads.InterestGroup.Auction.Result", 2);
+  auto result3 = RunAuctionAndWait(auction_config);
+  histogram_tester.ExpectTotalCount("Ads.InterestGroup.Auction.Result", 3);
   histogram_tester.ExpectBucketCount("Ads.InterestGroup.Auction.Result",
-                                     AuctionResult::kNoInterestGroups, 1);
+                                     AuctionResult::kNoInterestGroups, 2);
+  histogram_tester.ExpectBucketCount(
+      "Ads.InterestGroup.Auction.NumOwnerOriginsCachedForPreconnect", 1, 2);
 
   // We *did not* open any new connections. However, we've requested code and
   // signals, implying we've used the existing connections.
diff --git a/content/browser/interest_group/interest_group_pa_report_util.cc b/content/browser/interest_group/interest_group_pa_report_util.cc
index 3c7b2f3..2b62feb 100644
--- a/content/browser/interest_group/interest_group_pa_report_util.cc
+++ b/content/browser/interest_group/interest_group_pa_report_util.cc
@@ -17,6 +17,7 @@
 #include "base/check_op.h"
 #include "base/containers/flat_map.h"
 #include "base/feature_list.h"
+#include "base/metrics/histogram_functions.h"
 #include "base/notreached.h"
 #include "base/numerics/clamped_math.h"
 #include "components/aggregation_service/aggregation_coordinator_utils.h"
@@ -49,6 +50,8 @@
     const std::optional<auction_worklet::mojom::RejectReason> reject_reason,
     const PrivateAggregationParticipantData& participant_data,
     const PrivateAggregationTimings& timings) {
+  base::UmaHistogramEnumeration("Ads.InterestGroup.Auction.PABaseValueUsed",
+                                base_value);
   // The mojom API declaration should ensure base_value is one of these cases.
   switch (base_value) {
     case auction_worklet::mojom::BaseValue::kWinningBid:
diff --git a/content/browser/locks/lock_manager.cc b/content/browser/locks/lock_manager.cc
deleted file mode 100644
index feb6f937..0000000
--- a/content/browser/locks/lock_manager.cc
+++ /dev/null
@@ -1,411 +0,0 @@
-// Copyright 2017 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "content/browser/locks/lock_manager.h"
-
-#include <algorithm>
-#include <list>
-#include <memory>
-#include <tuple>
-#include <utility>
-#include <vector>
-
-#include "base/containers/contains.h"
-#include "base/containers/flat_map.h"
-#include "base/functional/bind.h"
-#include "base/memory/raw_ptr.h"
-#include "base/uuid.h"
-#include "content/public/browser/browser_thread.h"
-#include "content/public/browser/content_browser_client.h"
-#include "content/public/common/content_client.h"
-#include "ipc/ipc_message.h"
-#include "mojo/public/cpp/bindings/associated_remote.h"
-#include "mojo/public/cpp/bindings/self_owned_associated_receiver.h"
-#include "third_party/abseil-cpp/absl/utility/utility.h"
-
-using blink::mojom::LockMode;
-
-namespace content {
-
-namespace {
-
-// Guaranteed to be smaller than any result of LockManager::NextLockId().
-constexpr int64_t kPreemptiveLockId = 0;
-
-// A LockHandle is passed to the client when a lock is granted. As long as the
-// handle is held, the lock is held. Dropping the handle - either explicitly
-// by script or by process termination - causes the lock to be released. The
-// connection can also be closed here when a lock is stolen.
-class LockHandleImpl final : public blink::mojom::LockHandle {
- public:
-  static mojo::SelfOwnedAssociatedReceiverRef<blink::mojom::LockHandle> Create(
-      base::WeakPtr<LockManager> context,
-      storage::BucketId bucket_id,
-      int64_t lock_id,
-      mojo::PendingAssociatedRemote<blink::mojom::LockHandle>* remote) {
-    return mojo::MakeSelfOwnedAssociatedReceiver(
-        std::make_unique<LockHandleImpl>(std::move(context), bucket_id,
-                                         lock_id),
-        remote->InitWithNewEndpointAndPassReceiver());
-  }
-
-  LockHandleImpl(base::WeakPtr<LockManager> context,
-                 storage::BucketId bucket_id,
-                 int64_t lock_id)
-      : context_(context), bucket_id_(bucket_id), lock_id_(lock_id) {}
-
-  LockHandleImpl(const LockHandleImpl&) = delete;
-  LockHandleImpl& operator=(const LockHandleImpl&) = delete;
-
-  ~LockHandleImpl() override {
-    if (context_)
-      context_->ReleaseLock(bucket_id_, lock_id_);
-  }
-
-  // Called when the handle will be released from this end of the pipe. It
-  // nulls out the context so that the lock will not be double-released.
-  void Close() { context_.reset(); }
-
- private:
-  base::WeakPtr<LockManager> context_;
-  const storage::BucketId bucket_id_;
-  const int64_t lock_id_;
-};
-
-}  // namespace
-
-// A requested or held lock. When granted, a LockHandle will be minted
-// and passed to the held callback. Eventually the client will drop the
-// handle, which will notify the context and remove this.
-class LockManager::Lock {
- public:
-  Lock(const std::string& name,
-       LockMode mode,
-       int64_t lock_id,
-       const ReceiverState& receiver_state,
-       mojo::AssociatedRemote<blink::mojom::LockRequest> request)
-      : name_(name),
-        mode_(mode),
-        lock_id_(lock_id),
-        client_id_(receiver_state.client_id),
-        request_(std::move(request)) {}
-
-  // Grant a lock request. This mints a LockHandle and returns it over the
-  // request pipe.
-  void Grant(LockManager* lock_manager, storage::BucketId bucket_id) {
-    DCHECK(lock_manager);
-    DCHECK(!lock_manager_);
-    DCHECK(request_);
-    DCHECK(!handle_);
-
-    lock_manager_ = lock_manager->weak_ptr_factory_.GetWeakPtr();
-
-    mojo::PendingAssociatedRemote<blink::mojom::LockHandle> remote;
-    handle_ =
-        LockHandleImpl::Create(lock_manager_, bucket_id, lock_id_, &remote);
-    request_->Granted(std::move(remote));
-    request_.reset();
-  }
-
-  // Break a granted lock. This terminates the connection, signaling an error
-  // on the other end of the pipe.
-  void Break() {
-    DCHECK(!request_);
-    DCHECK(handle_);
-    DCHECK(lock_manager_);
-
-    LockHandleImpl* impl = static_cast<LockHandleImpl*>(handle_->impl());
-    // Explicitly close the LockHandle first; this ensures that when the
-    // connection is subsequently closed it will not re-entrantly try to drop
-    // the lock.
-    impl->Close();
-    handle_->Close();
-  }
-
-  const std::string& name() const { return name_; }
-  LockMode mode() const { return mode_; }
-  int64_t lock_id() const { return lock_id_; }
-  const std::string& client_id() const { return client_id_; }
-  bool is_granted() const { return !!handle_; }
-
- private:
-  const std::string name_;
-  const LockMode mode_;
-  const int64_t lock_id_;
-  const std::string client_id_;
-  // Set only once the lock is granted.
-  base::WeakPtr<LockManager> lock_manager_;
-
-  // Exactly one of the following is non-null at any given time.
-
-  // |request_| is valid until the lock is granted (or failure).
-  mojo::AssociatedRemote<blink::mojom::LockRequest> request_;
-
-  // Once granted, |handle_| holds this end of the pipe that lets us monitor
-  // for the other end going away.
-  mojo::SelfOwnedAssociatedReceiverRef<blink::mojom::LockHandle> handle_;
-};
-
-LockManager::LockManager() = default;
-
-LockManager::~LockManager() = default;
-
-// The BucketState class manages and exposes the state of lock requests
-// for a given bucket.
-class LockManager::BucketState {
- public:
-  explicit BucketState(LockManager* lock_manager)
-      : lock_manager_(lock_manager) {}
-  ~BucketState() = default;
-
-  // Helper function for breaking the lock at the front of a given request
-  // queue.
-  void BreakFront(std::list<Lock>& request_queue) {
-    Lock& broken_lock = request_queue.front();
-    lock_id_to_iterator_.erase(broken_lock.lock_id());
-    broken_lock.Break();
-    request_queue.pop_front();
-  }
-
-  // Steals a lock for a given resource.
-  //
-  // Breaks any currently held locks and inserts a new lock at the front of the
-  // request queue and grants it.
-  void PreemptLock(int64_t lock_id,
-                   const std::string& name,
-                   LockMode mode,
-                   mojo::AssociatedRemote<blink::mojom::LockRequest> request,
-                   const ReceiverState& receiver_state) {
-    // Preempting shared locks is not supported.
-    DCHECK_EQ(mode, LockMode::EXCLUSIVE);
-    std::list<Lock>& request_queue = resource_names_to_requests_[name];
-    while (!request_queue.empty() && request_queue.front().is_granted())
-      BreakFront(request_queue);
-    request_queue.emplace_front(name, mode, lock_id, receiver_state,
-                                std::move(request));
-    auto it = request_queue.begin();
-    lock_id_to_iterator_.emplace(it->lock_id(), it);
-    it->Grant(lock_manager_, receiver_state.bucket_id);
-  }
-
-  void AddRequest(int64_t lock_id,
-                  const std::string& name,
-                  LockMode mode,
-                  mojo::AssociatedRemote<blink::mojom::LockRequest> request,
-                  WaitMode wait,
-                  const ReceiverState& receiver_state) {
-    DCHECK(wait != WaitMode::PREEMPT);
-    std::list<Lock>& request_queue = resource_names_to_requests_[name];
-    bool can_grant = request_queue.empty() ||
-                     (request_queue.back().is_granted() &&
-                      request_queue.back().mode() == LockMode::SHARED &&
-                      mode == LockMode::SHARED);
-
-    if (!can_grant && wait == WaitMode::NO_WAIT) {
-      request->Failed();
-      return;
-    }
-
-    request_queue.emplace_back(name, mode, lock_id, receiver_state,
-                               std::move(request));
-    auto it = --(request_queue.end());
-    lock_id_to_iterator_.emplace(it->lock_id(), it);
-    if (can_grant) {
-      it->Grant(lock_manager_, receiver_state.bucket_id);
-    }
-  }
-
-  void EraseLock(int64_t lock_id, storage::BucketId bucket_id) {
-    // Note - the two lookups here could be replaced with one if the
-    // lock_id_to_iterator_ map also stored a reference to the request queue.
-    auto iterator_it = lock_id_to_iterator_.find(lock_id);
-    if (iterator_it == lock_id_to_iterator_.end())
-      return;
-
-    auto lock_it = iterator_it->second;
-    lock_id_to_iterator_.erase(iterator_it);
-
-    auto request_it = resource_names_to_requests_.find(lock_it->name());
-    if (request_it == resource_names_to_requests_.end())
-      return;
-
-    std::list<Lock>& request_queue = request_it->second;
-#if DCHECK_IS_ON()
-    auto check_it = request_queue.begin();
-    bool found = false;
-    for (; check_it != request_queue.end(); ++check_it) {
-      found = check_it == lock_it;
-      if (found)
-        break;
-    }
-    DCHECK(found);
-#endif
-
-    request_queue.erase(lock_it);
-    if (request_queue.empty()) {
-      resource_names_to_requests_.erase(request_it);
-      return;
-    }
-
-    // If, after erasing the lock from the request queue, the front of the
-    // queue is ungranted, then we have just erased the only granted lock. In
-    // this situation it will be necessary then to grant the next lock or locks
-    // (locks in the case that there is more than one SHARED lock at the front
-    // of the request queue now).
-    if (request_queue.front().is_granted())
-      return;
-
-    if (request_queue.front().mode() == LockMode::EXCLUSIVE) {
-      request_queue.front().Grant(lock_manager_, bucket_id);
-    } else {
-      DCHECK(request_queue.front().mode() == LockMode::SHARED);
-      for (auto grantee = request_queue.begin();
-           grantee != request_queue.end() &&
-           grantee->mode() == LockMode::SHARED;
-           ++grantee) {
-        DCHECK(!grantee->is_granted());
-        grantee->Grant(lock_manager_, bucket_id);
-      }
-    }
-  }
-
-  bool IsEmpty() const { return lock_id_to_iterator_.empty(); }
-
-  std::pair<std::vector<blink::mojom::LockInfoPtr>,
-            std::vector<blink::mojom::LockInfoPtr>>
-  Snapshot() const {
-    std::vector<blink::mojom::LockInfoPtr> requests;
-    std::vector<blink::mojom::LockInfoPtr> held;
-    for (const auto& name_queue_pair : resource_names_to_requests_) {
-      auto& request_queue = name_queue_pair.second;
-      if (request_queue.empty())
-        continue;
-      for (const auto& lock : request_queue) {
-        std::vector<blink::mojom::LockInfoPtr>& target =
-            lock.is_granted() ? held : requests;
-        target.emplace_back(std::in_place, lock.name(), lock.mode(),
-                            lock.client_id());
-      }
-    }
-    return std::make_pair(std::move(requests), std::move(held));
-  }
-
- private:
-  // BucketState::resource_names_to_requests_ maps a resource name to
-  // that resource's associated request queue for a given bucket.
-  //
-  // A resource's request queue is a list of Lock objects representing lock
-  // requests against that resource. All the granted locks for a resource reside
-  // at the front of the resource's
-  // request queue.
-  base::flat_map<std::string, std::list<Lock>> resource_names_to_requests_;
-
-  // BucketState::lock_id_to_iterator_ maps a lock's id to the
-  // iterator pointing to its location in its associated request queue.
-  base::flat_map<int64_t, std::list<Lock>::iterator> lock_id_to_iterator_;
-
-  // Any OriginState is owned by a LockManager so a raw pointer back to an
-  // OriginState's owning LockManager is safe.
-  const raw_ptr<LockManager> lock_manager_;
-};
-
-LockManager::ReceiverState::ReceiverState(std::string client_id,
-                                          storage::BucketId bucket_id)
-    : client_id(std::move(client_id)), bucket_id(bucket_id) {}
-LockManager::ReceiverState::ReceiverState() = default;
-LockManager::ReceiverState::ReceiverState(const ReceiverState& other) = default;
-LockManager::ReceiverState::~ReceiverState() = default;
-
-void LockManager::BindReceiver(
-    storage::BucketId bucket_id,
-    mojo::PendingReceiver<blink::mojom::LockManager> receiver) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  // TODO(jsbell): This should reflect the 'environment id' from HTML,
-  // and be the same opaque string seen in Service Worker client ids.
-  const std::string client_id =
-      base::Uuid::GenerateRandomV4().AsLowercaseString();
-
-  receivers_.Add(this, std::move(receiver), {client_id, bucket_id});
-}
-
-void LockManager::RequestLock(
-    const std::string& name,
-    LockMode mode,
-    WaitMode wait,
-    mojo::PendingAssociatedRemote<blink::mojom::LockRequest> request_remote) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  if (wait == WaitMode::PREEMPT && mode != LockMode::EXCLUSIVE) {
-    mojo::ReportBadMessage("Invalid option combination");
-    return;
-  }
-
-  if (name.length() > 0 && name[0] == '-') {
-    mojo::ReportBadMessage("Reserved name");
-    return;
-  }
-
-  mojo::AssociatedRemote<blink::mojom::LockRequest> request(
-      std::move(request_remote));
-  const auto& context = receivers_.current_context();
-  if (context.bucket_id.is_null()) {
-    request->Failed();
-    return;
-  }
-
-  if (!base::Contains(buckets_, context.bucket_id))
-    buckets_.emplace(context.bucket_id, this);
-
-  int64_t lock_id = NextLockId();
-  request.set_disconnect_handler(base::BindOnce(&LockManager::ReleaseLock,
-                                                base::Unretained(this),
-                                                context.bucket_id, lock_id));
-
-  BucketState& bucket_state = buckets_.find(context.bucket_id)->second;
-  if (wait == WaitMode::PREEMPT) {
-    bucket_state.PreemptLock(lock_id, name, mode, std::move(request), context);
-  } else
-    bucket_state.AddRequest(lock_id, name, mode, std::move(request), wait,
-                            context);
-}
-
-void LockManager::ReleaseLock(storage::BucketId bucket_id, int64_t lock_id) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  auto bucket_id_it = buckets_.find(bucket_id);
-  if (bucket_id_it == buckets_.end())
-    return;
-
-  BucketState& state = bucket_id_it->second;
-  state.EraseLock(lock_id, bucket_id);
-  if (state.IsEmpty())
-    buckets_.erase(bucket_id);
-}
-
-void LockManager::QueryState(QueryStateCallback callback) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  storage::BucketId bucket_id = receivers_.current_context().bucket_id;
-
-  auto bucket_id_it = buckets_.find(bucket_id);
-  if (bucket_id_it == buckets_.end()) {
-    std::move(callback).Run(std::vector<blink::mojom::LockInfoPtr>(),
-                            std::vector<blink::mojom::LockInfoPtr>());
-    return;
-  }
-  DCHECK(!bucket_id.is_null());
-  BucketState& state = bucket_id_it->second;
-  auto requested_held_pair = state.Snapshot();
-  std::move(callback).Run(std::move(requested_held_pair.first),
-                          std::move(requested_held_pair.second));
-}
-
-int64_t LockManager::NextLockId() {
-  int64_t lock_id = ++next_lock_id_;
-  DCHECK_GT(lock_id, kPreemptiveLockId);
-  return lock_id;
-}
-
-}  // namespace content
diff --git a/content/browser/locks/lock_manager.h b/content/browser/locks/lock_manager.h
index 2c23fdb2..6b68aee 100644
--- a/content/browser/locks/lock_manager.h
+++ b/content/browser/locks/lock_manager.h
@@ -5,18 +5,33 @@
 #ifndef CONTENT_BROWSER_LOCKS_LOCK_MANAGER_H_
 #define CONTENT_BROWSER_LOCKS_LOCK_MANAGER_H_
 
+#include <algorithm>
 #include <cstdint>
+#include <list>
 #include <map>
 #include <memory>
 #include <string>
+#include <tuple>
+#include <utility>
+#include <vector>
 
+#include "base/containers/contains.h"
+#include "base/containers/flat_map.h"
+#include "base/functional/bind.h"
 #include "base/functional/callback.h"
+#include "base/memory/raw_ptr.h"
 #include "base/memory/weak_ptr.h"
 #include "base/sequence_checker.h"
-#include "components/services/storage/public/cpp/buckets/bucket_id.h"
-#include "components/services/storage/public/cpp/buckets/constants.h"
+#include "base/uuid.h"
 #include "content/common/content_export.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/content_browser_client.h"
+#include "content/public/common/content_client.h"
+#include "ipc/ipc_message.h"
+#include "mojo/public/cpp/bindings/associated_remote.h"
 #include "mojo/public/cpp/bindings/receiver_set.h"
+#include "mojo/public/cpp/bindings/self_owned_associated_receiver.h"
+#include "third_party/abseil-cpp/absl/utility/utility.h"
 #include "third_party/blink/public/mojom/locks/lock_manager.mojom.h"
 #include "url/origin.h"
 
@@ -25,6 +40,7 @@
 // One instance of this exists per StoragePartition, and services multiple
 // child processes/origins. An instance must only be used on the sequence
 // it was created on.
+template <typename LockGroupIdType>
 class CONTENT_EXPORT LockManager : public blink::mojom::LockManager {
  public:
   LockManager();
@@ -34,8 +50,8 @@
   LockManager& operator=(const LockManager&) = delete;
 
   // Binds |receiver| to this LockManager. |receiver| belongs to a frame or
-  // worker at |bucket_id|.
-  void BindReceiver(storage::BucketId bucket_id,
+  // worker at |lock_group_id|.
+  void BindReceiver(LockGroupIdType lock_group_id,
                     mojo::PendingReceiver<blink::mojom::LockManager> receiver);
 
   // Request a lock. When the lock is acquired, |callback| will be invoked with
@@ -47,29 +63,29 @@
                        request) override;
 
   // Called by a LockHandle's implementation when destructed.
-  void ReleaseLock(storage::BucketId bucket_id, int64_t lock_id);
+  void ReleaseLock(LockGroupIdType lock_group_id, int64_t lock_id);
 
-  // Called to request a snapshot of the current lock state for a bucket.
+  // Called to request a snapshot of the current lock state for a lock group.
   void QueryState(QueryStateCallback callback) override;
 
  private:
   // Internal representation of a lock request or held lock.
   class Lock;
 
-  // State for a particular bucket.
-  class BucketState;
+  // State for a particular group of lock requests.
+  class LockGroupState;
 
   // State for each client held in |receivers_|.
   struct ReceiverState {
-    ReceiverState(std::string client_id, storage::BucketId bucket_id);
+    ReceiverState(std::string client_id, LockGroupIdType lock_group_id);
     ReceiverState();
     ReceiverState(const ReceiverState& other);
     ~ReceiverState();
 
     std::string client_id;
 
-    // BucketId of the frame or worker owning this receiver.
-    storage::BucketId bucket_id;
+    // The ID of the lock group owning this receiver.
+    LockGroupIdType lock_group_id;
   };
 
   // Mints a monotonically increasing identifier. Used both for lock requests
@@ -79,12 +95,431 @@
   mojo::ReceiverSet<blink::mojom::LockManager, ReceiverState> receivers_;
 
   int64_t next_lock_id_ = 0;
-  std::map<storage::BucketId, BucketState> buckets_;
+  std::map<LockGroupIdType, LockGroupState> lock_groups_;
 
   SEQUENCE_CHECKER(sequence_checker_);
   base::WeakPtrFactory<LockManager> weak_ptr_factory_{this};
 };
 
+namespace {
+
+using blink::mojom::LockMode;
+
+// Guaranteed to be smaller than any result of
+// LockManager<LockGroupIdType>::NextLockId().
+constexpr int64_t kPreemptiveLockId = 0;
+
+// A LockHandle is passed to the client when a lock is granted. As long as the
+// handle is held, the lock is held. Dropping the handle - either explicitly
+// by script or by process termination - causes the lock to be released. The
+// connection can also be closed here when a lock is stolen.
+template <typename LockGroupIdType>
+class LockHandleImpl final : public blink::mojom::LockHandle {
+ public:
+  static mojo::SelfOwnedAssociatedReceiverRef<blink::mojom::LockHandle> Create(
+      base::WeakPtr<LockManager<LockGroupIdType>> context,
+      LockGroupIdType lock_group_id,
+      int64_t lock_id,
+      mojo::PendingAssociatedRemote<blink::mojom::LockHandle>* remote) {
+    return mojo::MakeSelfOwnedAssociatedReceiver(
+        std::make_unique<LockHandleImpl<LockGroupIdType>>(
+            std::move(context), lock_group_id, lock_id),
+        remote->InitWithNewEndpointAndPassReceiver());
+  }
+
+  LockHandleImpl(base::WeakPtr<LockManager<LockGroupIdType>> context,
+                 LockGroupIdType lock_group_id,
+                 int64_t lock_id)
+      : context_(context), lock_group_id_(lock_group_id), lock_id_(lock_id) {}
+
+  LockHandleImpl(const LockHandleImpl&) = delete;
+  LockHandleImpl& operator=(const LockHandleImpl&) = delete;
+
+  ~LockHandleImpl() override {
+    if (context_) {
+      context_->ReleaseLock(lock_group_id_, lock_id_);
+    }
+  }
+
+  // Called when the handle will be released from this end of the pipe. It
+  // nulls out the context so that the lock will not be double-released.
+  void Close() { context_.reset(); }
+
+ private:
+  base::WeakPtr<LockManager<LockGroupIdType>> context_;
+  const LockGroupIdType lock_group_id_;
+  const int64_t lock_id_;
+};
+
+}  // namespace
+
+// A requested or held lock. When granted, a LockHandle will be minted
+// and passed to the held callback. Eventually the client will drop the
+// handle, which will notify the context and remove this.
+template <typename LockGroupIdType>
+class LockManager<LockGroupIdType>::Lock {
+ public:
+  Lock(const std::string& name,
+       LockMode mode,
+       int64_t lock_id,
+       const ReceiverState& receiver_state,
+       mojo::AssociatedRemote<blink::mojom::LockRequest> request)
+      : name_(name),
+        mode_(mode),
+        lock_id_(lock_id),
+        client_id_(receiver_state.client_id),
+        request_(std::move(request)) {}
+
+  // Grant a lock request. This mints a LockHandle and returns it over the
+  // request pipe.
+  void Grant(LockManager<LockGroupIdType>* lock_manager,
+             LockGroupIdType lock_group_id) {
+    DCHECK(lock_manager);
+    DCHECK(!lock_manager_);
+    DCHECK(request_);
+    DCHECK(!handle_);
+
+    lock_manager_ = lock_manager->weak_ptr_factory_.GetWeakPtr();
+
+    mojo::PendingAssociatedRemote<blink::mojom::LockHandle> remote;
+    handle_ = LockHandleImpl<LockGroupIdType>::Create(
+        lock_manager_, lock_group_id, lock_id_, &remote);
+    request_->Granted(std::move(remote));
+    request_.reset();
+  }
+
+  // Break a granted lock. This terminates the connection, signaling an error
+  // on the other end of the pipe.
+  void Break() {
+    DCHECK(!request_);
+    DCHECK(handle_);
+    DCHECK(lock_manager_);
+
+    LockHandleImpl<LockGroupIdType>* impl =
+        static_cast<LockHandleImpl<LockGroupIdType>*>(handle_->impl());
+    // Explicitly close the LockHandle first; this ensures that when the
+    // connection is subsequently closed it will not re-entrantly try to drop
+    // the lock.
+    impl->Close();
+    handle_->Close();
+  }
+
+  const std::string& name() const { return name_; }
+  LockMode mode() const { return mode_; }
+  int64_t lock_id() const { return lock_id_; }
+  const std::string& client_id() const { return client_id_; }
+  bool is_granted() const { return !!handle_; }
+
+ private:
+  const std::string name_;
+  const LockMode mode_;
+  const int64_t lock_id_;
+  const std::string client_id_;
+  // Set only once the lock is granted.
+  base::WeakPtr<LockManager<LockGroupIdType>> lock_manager_;
+
+  // Exactly one of the following is non-null at any given time.
+
+  // |request_| is valid until the lock is granted (or failure).
+  mojo::AssociatedRemote<blink::mojom::LockRequest> request_;
+
+  // Once granted, |handle_| holds this end of the pipe that lets us monitor
+  // for the other end going away.
+  mojo::SelfOwnedAssociatedReceiverRef<blink::mojom::LockHandle> handle_;
+};
+
+template <typename LockGroupIdType>
+LockManager<LockGroupIdType>::LockManager() = default;
+
+template <typename LockGroupIdType>
+LockManager<LockGroupIdType>::~LockManager() = default;
+
+// The LockGroupState class manages and exposes the state of lock requests
+// for a given LockGroupIdType.
+template <typename LockGroupIdType>
+class LockManager<LockGroupIdType>::LockGroupState {
+ public:
+  explicit LockGroupState(LockManager<LockGroupIdType>* lock_manager)
+      : lock_manager_(lock_manager) {}
+  ~LockGroupState() = default;
+
+  // Helper function for breaking the lock at the front of a given request
+  // queue.
+  void BreakFront(std::list<Lock>& request_queue) {
+    Lock& broken_lock = request_queue.front();
+    lock_id_to_iterator_.erase(broken_lock.lock_id());
+    broken_lock.Break();
+    request_queue.pop_front();
+  }
+
+  // Steals a lock for a given resource.
+  //
+  // Breaks any currently held locks and inserts a new lock at the front of the
+  // request queue and grants it.
+  void PreemptLock(int64_t lock_id,
+                   const std::string& name,
+                   LockMode mode,
+                   mojo::AssociatedRemote<blink::mojom::LockRequest> request,
+                   const ReceiverState& receiver_state) {
+    // Preempting shared locks is not supported.
+    DCHECK_EQ(mode, LockMode::EXCLUSIVE);
+    std::list<Lock>& request_queue = resource_names_to_requests_[name];
+    while (!request_queue.empty() && request_queue.front().is_granted()) {
+      BreakFront(request_queue);
+    }
+    request_queue.emplace_front(name, mode, lock_id, receiver_state,
+                                std::move(request));
+    auto it = request_queue.begin();
+    lock_id_to_iterator_.emplace(it->lock_id(), it);
+    it->Grant(lock_manager_, receiver_state.lock_group_id);
+  }
+
+  void AddRequest(int64_t lock_id,
+                  const std::string& name,
+                  LockMode mode,
+                  mojo::AssociatedRemote<blink::mojom::LockRequest> request,
+                  WaitMode wait,
+                  const ReceiverState& receiver_state) {
+    DCHECK(wait != WaitMode::PREEMPT);
+    std::list<Lock>& request_queue = resource_names_to_requests_[name];
+    bool can_grant = request_queue.empty() ||
+                     (request_queue.back().is_granted() &&
+                      request_queue.back().mode() == LockMode::SHARED &&
+                      mode == LockMode::SHARED);
+
+    if (!can_grant && wait == WaitMode::NO_WAIT) {
+      request->Failed();
+      return;
+    }
+
+    request_queue.emplace_back(name, mode, lock_id, receiver_state,
+                               std::move(request));
+    auto it = --(request_queue.end());
+    lock_id_to_iterator_.emplace(it->lock_id(), it);
+    if (can_grant) {
+      it->Grant(lock_manager_, receiver_state.lock_group_id);
+    }
+  }
+
+  void EraseLock(int64_t lock_id, LockGroupIdType lock_group_id) {
+    // Note - the two lookups here could be replaced with one if the
+    // lock_id_to_iterator_ map also stored a reference to the request queue.
+    auto iterator_it = lock_id_to_iterator_.find(lock_id);
+    if (iterator_it == lock_id_to_iterator_.end()) {
+      return;
+    }
+
+    auto lock_it = iterator_it->second;
+    lock_id_to_iterator_.erase(iterator_it);
+
+    auto request_it = resource_names_to_requests_.find(lock_it->name());
+    if (request_it == resource_names_to_requests_.end()) {
+      return;
+    }
+
+    std::list<Lock>& request_queue = request_it->second;
+#if DCHECK_IS_ON()
+    auto check_it = request_queue.begin();
+    bool found = false;
+    for (; check_it != request_queue.end(); ++check_it) {
+      found = check_it == lock_it;
+      if (found) {
+        break;
+      }
+    }
+    DCHECK(found);
+#endif
+
+    request_queue.erase(lock_it);
+    if (request_queue.empty()) {
+      resource_names_to_requests_.erase(request_it);
+      return;
+    }
+
+    // If, after erasing the lock from the request queue, the front of the
+    // queue is ungranted, then we have just erased the only granted lock. In
+    // this situation it will be necessary then to grant the next lock or locks
+    // (locks in the case that there is more than one SHARED lock at the front
+    // of the request queue now).
+    if (request_queue.front().is_granted()) {
+      return;
+    }
+
+    if (request_queue.front().mode() == LockMode::EXCLUSIVE) {
+      request_queue.front().Grant(lock_manager_, lock_group_id);
+    } else {
+      DCHECK(request_queue.front().mode() == LockMode::SHARED);
+      for (auto grantee = request_queue.begin();
+           grantee != request_queue.end() &&
+           grantee->mode() == LockMode::SHARED;
+           ++grantee) {
+        DCHECK(!grantee->is_granted());
+        grantee->Grant(lock_manager_, lock_group_id);
+      }
+    }
+  }
+
+  bool IsEmpty() const { return lock_id_to_iterator_.empty(); }
+
+  std::pair<std::vector<blink::mojom::LockInfoPtr>,
+            std::vector<blink::mojom::LockInfoPtr>>
+  Snapshot() const {
+    std::vector<blink::mojom::LockInfoPtr> requests;
+    std::vector<blink::mojom::LockInfoPtr> held;
+    for (const auto& name_queue_pair : resource_names_to_requests_) {
+      auto& request_queue = name_queue_pair.second;
+      if (request_queue.empty()) {
+        continue;
+      }
+      for (const auto& lock : request_queue) {
+        std::vector<blink::mojom::LockInfoPtr>& target =
+            lock.is_granted() ? held : requests;
+        target.emplace_back(std::in_place, lock.name(), lock.mode(),
+                            lock.client_id());
+      }
+    }
+    return std::make_pair(std::move(requests), std::move(held));
+  }
+
+ private:
+  // LockGroupState::resource_names_to_requests_ maps a resource name to
+  // that resource's associated request queue for a given lock group.
+  //
+  // A resource's request queue is a list of Lock objects representing lock
+  // requests against that resource. All the granted locks for a resource reside
+  // at the front of the resource's
+  // request queue.
+  base::flat_map<std::string, std::list<Lock>> resource_names_to_requests_;
+
+  // LockGroupState::lock_id_to_iterator_ maps a lock's id to the
+  // iterator pointing to its location in its associated request queue.
+  base::flat_map<int64_t, typename std::list<Lock>::iterator>
+      lock_id_to_iterator_;
+
+  // Any OriginState is owned by a LockManager so a raw pointer back to an
+  // OriginState's owning LockManager is safe.
+  const raw_ptr<LockManager<LockGroupIdType>> lock_manager_;
+};
+
+template <typename LockGroupIdType>
+LockManager<LockGroupIdType>::ReceiverState::ReceiverState(
+    std::string client_id,
+    LockGroupIdType lock_group_id)
+    : client_id(std::move(client_id)), lock_group_id(lock_group_id) {}
+
+template <typename LockGroupIdType>
+LockManager<LockGroupIdType>::ReceiverState::ReceiverState() = default;
+
+template <typename LockGroupIdType>
+LockManager<LockGroupIdType>::ReceiverState::ReceiverState(
+    const ReceiverState& other) = default;
+
+template <typename LockGroupIdType>
+LockManager<LockGroupIdType>::ReceiverState::~ReceiverState() = default;
+
+template <typename LockGroupIdType>
+void LockManager<LockGroupIdType>::BindReceiver(
+    LockGroupIdType lock_group_id,
+    mojo::PendingReceiver<blink::mojom::LockManager> receiver) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  // TODO(jsbell): This should reflect the 'environment id' from HTML,
+  // and be the same opaque string seen in Service Worker client ids.
+  const std::string client_id =
+      base::Uuid::GenerateRandomV4().AsLowercaseString();
+
+  receivers_.Add(this, std::move(receiver), {client_id, lock_group_id});
+}
+
+template <typename LockGroupIdType>
+void LockManager<LockGroupIdType>::RequestLock(
+    const std::string& name,
+    LockMode mode,
+    WaitMode wait,
+    mojo::PendingAssociatedRemote<blink::mojom::LockRequest> request_remote) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  if (wait == WaitMode::PREEMPT && mode != LockMode::EXCLUSIVE) {
+    mojo::ReportBadMessage("Invalid option combination");
+    return;
+  }
+
+  if (name.length() > 0 && name[0] == '-') {
+    mojo::ReportBadMessage("Reserved name");
+    return;
+  }
+
+  mojo::AssociatedRemote<blink::mojom::LockRequest> request(
+      std::move(request_remote));
+  const auto& context = receivers_.current_context();
+  if (context.lock_group_id.is_null()) {
+    request->Failed();
+    return;
+  }
+
+  if (!base::Contains(lock_groups_, context.lock_group_id)) {
+    lock_groups_.emplace(context.lock_group_id, this);
+  }
+
+  int64_t lock_id = NextLockId();
+  request.set_disconnect_handler(
+      base::BindOnce(&LockManager<LockGroupIdType>::ReleaseLock,
+                     base::Unretained(this), context.lock_group_id, lock_id));
+
+  LockGroupState& lock_group_state =
+      lock_groups_.find(context.lock_group_id)->second;
+  if (wait == WaitMode::PREEMPT) {
+    lock_group_state.PreemptLock(lock_id, name, mode, std::move(request),
+                                 context);
+  } else {
+    lock_group_state.AddRequest(lock_id, name, mode, std::move(request), wait,
+                                context);
+  }
+}
+
+template <typename LockGroupIdType>
+void LockManager<LockGroupIdType>::ReleaseLock(LockGroupIdType lock_group_id,
+                                               int64_t lock_id) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  auto lock_group_id_it = lock_groups_.find(lock_group_id);
+  if (lock_group_id_it == lock_groups_.end()) {
+    return;
+  }
+
+  LockGroupState& state = lock_group_id_it->second;
+  state.EraseLock(lock_id, lock_group_id);
+  if (state.IsEmpty()) {
+    lock_groups_.erase(lock_group_id);
+  }
+}
+
+template <typename LockGroupIdType>
+void LockManager<LockGroupIdType>::QueryState(QueryStateCallback callback) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  LockGroupIdType lock_group_id = receivers_.current_context().lock_group_id;
+
+  auto lock_group_id_it = lock_groups_.find(lock_group_id);
+  if (lock_group_id_it == lock_groups_.end()) {
+    std::move(callback).Run(std::vector<blink::mojom::LockInfoPtr>(),
+                            std::vector<blink::mojom::LockInfoPtr>());
+    return;
+  }
+  DCHECK(!lock_group_id.is_null());
+  LockGroupState& state = lock_group_id_it->second;
+  auto requested_held_pair = state.Snapshot();
+  std::move(callback).Run(std::move(requested_held_pair.first),
+                          std::move(requested_held_pair.second));
+}
+
+template <typename LockGroupIdType>
+int64_t LockManager<LockGroupIdType>::NextLockId() {
+  int64_t lock_id = ++next_lock_id_;
+  DCHECK_GT(lock_id, kPreemptiveLockId);
+  return lock_id;
+}
+
 }  // namespace content
 
 #endif  // CONTENT_BROWSER_LOCKS_LOCK_MANAGER_H_
diff --git a/content/browser/locks/lock_manager_unittest.cc b/content/browser/locks/lock_manager_unittest.cc
index bd8b5e2..3e565c6 100644
--- a/content/browser/locks/lock_manager_unittest.cc
+++ b/content/browser/locks/lock_manager_unittest.cc
@@ -33,7 +33,7 @@
 
  private:
   base::test::SingleThreadTaskEnvironment task_environment_;
-  LockManager lock_manager_;
+  LockManager<storage::BucketId> lock_manager_;
   mojo::PendingRemote<blink::mojom::LockManager> pending_remote_;
   mojo::PendingReceiver<blink::mojom::LockManager> pending_receiver_;
   mojo::Remote<blink::mojom::LockManager> remote_;
@@ -78,7 +78,7 @@
   TestLockRequest request(std::move(pending_receiver));
 
   GetRemote()->RequestLock("lock", blink::mojom::LockMode::EXCLUSIVE,
-                           LockManager::WaitMode::WAIT,
+                           blink::mojom::LockManager::WaitMode::WAIT,
                            std::move(pending_remote));
 
   request.WaitForCallback();
diff --git a/content/browser/private_aggregation/private_aggregation_budget_storage.h b/content/browser/private_aggregation/private_aggregation_budget_storage.h
index 2c89712..9019d0a9 100644
--- a/content/browser/private_aggregation/private_aggregation_budget_storage.h
+++ b/content/browser/private_aggregation/private_aggregation_budget_storage.h
@@ -79,10 +79,8 @@
   ~PrivateAggregationBudgetStorage();
 
   // The maximum time writes will be buffered before being committed to disk.
+  // Note that `Shutdown()` will flush pending writes to disk without delay.
   // This value was chosen to match TrustTokenDatabaseOwner.
-  // TODO(crbug.com/40226452): Consider adding a method to flush on destruction
-  // to ensure no writes are lost on shutdown. Note this would also require
-  // postponing `budgets_table_`'s destructor.
   static constexpr base::TimeDelta kFlushDelay = base::Seconds(2);
 
   // TODO(crbug.com/40226450): Support data deletion.
diff --git a/content/browser/site_instance_impl.cc b/content/browser/site_instance_impl.cc
index 0581464..ebe21d8 100644
--- a/content/browser/site_instance_impl.cc
+++ b/content/browser/site_instance_impl.cc
@@ -1112,15 +1112,6 @@
   if (should_check_for_wrong_process && !IsSuitableForUrlInfo(dest_url_info))
     return false;
 
-  // If we don't have a last successful URL, we can't trust the origin or URL
-  // stored on the frame, so we fall back to the SiteInstance URL.  This case
-  // matters for newly created frames which haven't committed a navigation yet,
-  // as well as for net errors. Note that we use the SiteInstance's
-  // original_url() and not the site URL, so that we can do this comparison
-  // without the effective URL resolution if needed.
-  if (last_successful_url.is_empty())
-    return IsOriginalUrlSameSite(dest_url_info, should_compare_effective_urls);
-
   // In the common case, we use the last successful URL. Thus, we compare
   // against the last successful commit when deciding whether to swap this time.
   // We convert |last_successful_url| to UrlInfo with |origin_isolation_request|
diff --git a/content/browser/site_per_process_browsertest.cc b/content/browser/site_per_process_browsertest.cc
index ae3268d..40ed281a 100644
--- a/content/browser/site_per_process_browsertest.cc
+++ b/content/browser/site_per_process_browsertest.cc
@@ -13355,15 +13355,16 @@
       NavigateToURL(shell(), embedded_test_server()->GetURL("/empty.html")));
 
   // Now do a browser-initiated navigation to about:blank in a new tab created
-  // in the previous SiteInstance. This happens to stay in the same process,
-  // though there may not be a requirement for that.
+  // in the previous SiteInstance. The current behavior is for the navigation
+  // to switch to a new SiteInstance, though there is no real requirement for
+  // that. In the past the existing SiteInstance was used.
   WebContents::CreateParams new_contents_params(
       web_contents()->GetBrowserContext(), web_contents()->GetSiteInstance());
   std::unique_ptr<WebContents> new_web_contents(
       WebContents::Create(new_contents_params));
 
   EXPECT_TRUE(NavigateToURL(new_web_contents.get(), GURL(url::kAboutBlankURL)));
-  EXPECT_EQ(web_contents()->GetPrimaryMainFrame()->GetProcess(),
+  EXPECT_NE(web_contents()->GetPrimaryMainFrame()->GetProcess(),
             new_web_contents->GetPrimaryMainFrame()->GetProcess());
 }
 
diff --git a/content/browser/storage_partition_impl.cc b/content/browser/storage_partition_impl.cc
index edbdcac..934ff1d9 100644
--- a/content/browser/storage_partition_impl.cc
+++ b/content/browser/storage_partition_impl.cc
@@ -1329,7 +1329,7 @@
   dom_storage_context_ = DOMStorageContextWrapper::Create(
       this, browser_context_->GetSpecialStoragePolicy());
 
-  lock_manager_ = std::make_unique<LockManager>();
+  lock_manager_ = std::make_unique<LockManager<storage::BucketId>>();
 
   shared_storage_worklet_host_manager_ =
       std::make_unique<SharedStorageWorkletHostManager>();
@@ -1690,7 +1690,7 @@
   return GetDOMStorageContext()->GetLocalStorageControl();
 }
 
-LockManager* StoragePartitionImpl::GetLockManager() {
+LockManager<storage::BucketId>* StoragePartitionImpl::GetLockManager() {
   DCHECK(initialized_);
   return lock_manager_.get();
 }
diff --git a/content/browser/storage_partition_impl.h b/content/browser/storage_partition_impl.h
index fa722af..2e00b90 100644
--- a/content/browser/storage_partition_impl.h
+++ b/content/browser/storage_partition_impl.h
@@ -6,6 +6,7 @@
 #define CONTENT_BROWSER_STORAGE_PARTITION_IMPL_H_
 
 #include <stdint.h>
+
 #include <map>
 #include <memory>
 #include <set>
@@ -27,6 +28,7 @@
 #include "content/browser/child_process_security_policy_impl.h"
 #include "content/browser/content_index/content_index_context_impl.h"
 #include "content/browser/dom_storage/dom_storage_context_wrapper.h"
+#include "content/browser/locks/lock_manager.h"
 #include "content/browser/notifications/platform_notification_context_impl.h"
 #include "content/browser/service_worker/service_worker_context_wrapper.h"
 #include "content/browser/worker_host/dedicated_worker_service_impl.h"
@@ -102,7 +104,6 @@
 class GeneratedCodeCacheContext;
 class HostZoomLevelContext;
 class InterestGroupManagerImpl;
-class LockManager;
 class NavigationStateKeepAlive;
 class PaymentAppContextImpl;
 class PrivateAggregationDataModel;
@@ -188,7 +189,8 @@
   storage::DatabaseTracker* GetDatabaseTracker() override;
   DOMStorageContextWrapper* GetDOMStorageContext() override;
   storage::mojom::LocalStorageControl* GetLocalStorageControl() override;
-  LockManager* GetLockManager();  // override; TODO: Add to interface
+  LockManager<storage::BucketId>*
+  GetLockManager();  // override; TODO: Add to interface
   // TODO(crbug.com/40185706): Add this method to the StoragePartition
   // interface, which would also require making SharedStorageWorkletHostManager
   // an interface accessible in //content/public/.
@@ -741,7 +743,7 @@
   scoped_refptr<storage::FileSystemContext> filesystem_context_;
   scoped_refptr<storage::DatabaseTracker> database_tracker_;
   scoped_refptr<DOMStorageContextWrapper> dom_storage_context_;
-  std::unique_ptr<LockManager> lock_manager_;
+  std::unique_ptr<LockManager<storage::BucketId>> lock_manager_;
   std::unique_ptr<indexed_db::IndexedDBControlWrapper>
       indexed_db_control_wrapper_;
   std::unique_ptr<CacheStorageControlWrapper> cache_storage_control_wrapper_;
diff --git a/content/services/auction_worklet/public/mojom/private_aggregation_request.mojom b/content/services/auction_worklet/public/mojom/private_aggregation_request.mojom
index de094689..fe8b40a 100644
--- a/content/services/auction_worklet/public/mojom/private_aggregation_request.mojom
+++ b/content/services/auction_worklet/public/mojom/private_aggregation_request.mojom
@@ -10,6 +10,9 @@
 
 // Indicates which value the browser should use to calculate the resulting
 // bucket or value.
+// Warning: this is used for UMA histograms, so values should not be
+// renumbered.
+// LINT.IfChange(BaseValue)
 enum BaseValue {
   kWinningBid = 0,
   kHighestScoringOtherBid = 1,
@@ -30,6 +33,7 @@
   kInterestGroupStorageUsed = 14,
   kPercentInterestGroupStorageQuotaUsed = 15,
 };
+// LINT.ThenChange(//tools/metrics/histograms/enums.xml:AdAuctionPABaseValue)
 
 // Bucket's offset to add/subtract to the auction result value. Offset's `value`
 // should be Uint128. We need to support negative offset value, so adding a
diff --git a/content/services/auction_worklet/trusted_signals_kvv2_helper.cc b/content/services/auction_worklet/trusted_signals_kvv2_helper.cc
index e8f01d2..1948b54 100644
--- a/content/services/auction_worklet/trusted_signals_kvv2_helper.cc
+++ b/content/services/auction_worklet/trusted_signals_kvv2_helper.cc
@@ -37,6 +37,7 @@
 #include "content/services/auction_worklet/public/mojom/auction_worklet_service.mojom.h"
 #include "third_party/zlib/google/compression_utils.h"
 #include "url/origin.h"
+#include "v8/include/v8-context.h"
 
 namespace auction_worklet {
 
@@ -52,6 +53,9 @@
 constexpr char kTagRenderUrls[] = "renderUrls";
 constexpr char kTagAdComponentRenderUrls[] = "adComponentRenderUrls";
 
+using ResultOrError =
+    base::expected<scoped_refptr<TrustedSignals::Result>, std::string>;
+
 // Wrapper around cbor::Reader::Read that enables reading floating point. While
 // as of this writing, floating point isn't used, failing on floats means adding
 // floats to the format would be a breaking change. This is also more
@@ -381,13 +385,14 @@
 // `key_group_output_map` with specified tag, and serialize it to
 // `AuctionV8Helper::SerializedValue` as the value. Insert this into a
 // map with the `key` as the key. Return `ErrorInfo` in case of any failure.
+// If `keys` is nullopt, then all keys will be parsed.
 base::expected<std::map<std::string, AuctionV8Helper::SerializedValue>,
                TrustedSignalsKVv2ResponseParser::ErrorInfo>
 SerializeKeyGroupOutputsMap(
     AuctionV8Helper* v8_helper,
     const std::map<std::string, const cbor::Value::MapValue*>&
         key_group_outputs_map,
-    const std::set<std::string>& keys,
+    base::optional_ref<const std::set<std::string>> keys,
     const char* tag) {
   std::map<std::string, AuctionV8Helper::SerializedValue> serialized_value_map;
 
@@ -399,17 +404,19 @@
     return serialized_value_map;
   }
 
-  for (const auto& key : keys) {
-    const auto& key_values_map = tag_it->second;
-    auto cbor_it = key_values_map->find(cbor::Value(key));
+  for (const auto& cbor_it : *tag_it->second) {
+    if (!cbor_it.first.is_string()) {
+      continue;
+    }
 
-    if (cbor_it == key_values_map->end()) {
+    const std::string& key = cbor_it.first.GetString();
+    if (keys && !keys->contains(key)) {
       continue;
     }
 
     base::expected<std::string_view,
                    TrustedSignalsKVv2ResponseParser::ErrorInfo>
-        data_string = GetKeyValueDataString(*cbor_it);
+        data_string = GetKeyValueDataString(cbor_it);
 
     if (!data_string.has_value()) {
       return base::unexpected(std::move(data_string).error());
@@ -442,18 +449,20 @@
 // Return a CBOR value with array type, or `ErrorInfo` in case of any parsing
 // failure.
 base::expected<cbor::Value, TrustedSignalsKVv2ResponseParser::ErrorInfo>
-GetContentFromCompressionGroup(const CompressionGroupResult& group_result) {
+GetContentFromCompressionGroup(
+    mojom::TrustedSignalsCompressionScheme compression_scheme,
+    base::span<const uint8_t> content) {
   base::span<const uint8_t> content_bytes;
   // Buffer for holding the data if we need to decompress.
   std::string decompressed_string;
 
-  if (group_result.compression_scheme ==
+  if (compression_scheme ==
       auction_worklet::mojom::TrustedSignalsCompressionScheme::kNone) {
-    content_bytes = base::as_byte_span(group_result.content);
-  } else if (group_result.compression_scheme ==
+    content_bytes = content;
+  } else if (compression_scheme ==
              auction_worklet::mojom::TrustedSignalsCompressionScheme::kGzip) {
     bool is_decompressed =
-        compression::GzipUncompress(group_result.content, &decompressed_string);
+        compression::GzipUncompress(content, &decompressed_string);
     if (!is_decompressed) {
       return base::unexpected(TrustedSignalsKVv2ResponseParser::ErrorInfo(
           "Failed to decompress content string with Gzip."));
@@ -541,6 +550,118 @@
   return &key_group_outputs_value.GetArray();
 }
 
+// Attempts to create a TrustedSignals::Result for all fields in a bidding
+// partition, given the result of calling ParseKeyGroupOutputsToMap() on the
+// partition and the partition's data version.
+ResultOrError ParseBiddingPartition(
+    AuctionV8Helper* v8_helper,
+    const std::map<std::string, const cbor::Value::MapValue*>&
+        key_group_outputs_map,
+    std::optional<uint32_t> data_version) {
+  // Try to find `kTagInterestGroupName` tag and parse its map.
+  TrustedSignals::Result::PerInterestGroupDataMap per_interest_group_data_map;
+  auto tag_interest_group_name_it =
+      key_group_outputs_map.find(kTagInterestGroupName);
+  if (tag_interest_group_name_it != key_group_outputs_map.end()) {
+    for (auto& interest_group_data : *tag_interest_group_name_it->second) {
+      if (!interest_group_data.first.is_string()) {
+        return base::unexpected("Interest group names must be strings");
+      }
+      const std::string* name = &interest_group_data.first.GetString();
+      base::expected<std::string_view,
+                     TrustedSignalsKVv2ResponseParser::ErrorInfo>
+          data_string = GetKeyValueDataString(interest_group_data);
+
+      if (!data_string.has_value()) {
+        return base::unexpected(std::move(data_string.error().error_msg));
+      }
+
+      // Each interest group name should be associated with a JSON string with
+      // fields for that interest group.
+      v8::Local<v8::Value> per_interest_group_data_v8_value;
+      if (!v8_helper
+               ->CreateValueFromJson(v8_helper->scratch_context(),
+                                     std::move(data_string).value())
+               .ToLocal(&per_interest_group_data_v8_value) ||
+          !per_interest_group_data_v8_value->IsObject() ||
+          // V8 considers arrays a subtype of object, but the
+          // response body must be a JSON object, not a JSON
+          // array,
+          // so need to explicitly check if it's an array.
+          per_interest_group_data_v8_value->IsArray()) {
+        return base::unexpected(
+            "Failed to create V8 value from key group output "
+            "data.");
+      }
+
+      v8::Local<v8::Object> per_interest_group_data_v8_object =
+          per_interest_group_data_v8_value.As<v8::Object>();
+      std::optional<TrustedSignals::Result::PriorityVector> priority_vector =
+          TrustedSignals::ParsePriorityVector(
+              v8_helper, per_interest_group_data_v8_object);
+
+      std::optional<base::TimeDelta> update_if_older_than;
+      if (base::FeatureList::IsEnabled(
+              features::kInterestGroupUpdateIfOlderThan)) {
+        update_if_older_than = TrustedSignals::ParseUpdateIfOlderThan(
+            v8_helper, per_interest_group_data_v8_object);
+      }
+
+      if (priority_vector || update_if_older_than) {
+        per_interest_group_data_map.emplace(
+            *name,
+            TrustedSignals::Result::PerGroupData(
+                std::move(priority_vector), std::move(update_if_older_than)));
+      }
+    }
+  }
+
+  // Try to find `kTagKey` tag and parse the map.
+  auto maybe_key_data_map = SerializeKeyGroupOutputsMap(
+      v8_helper, key_group_outputs_map, /*keys=*/std::nullopt, kTagKey);
+  if (!maybe_key_data_map.has_value()) {
+    return base::unexpected(std::move(maybe_key_data_map).error().error_msg);
+  }
+
+  return base::MakeRefCounted<TrustedSignals::Result>(
+      std::move(per_interest_group_data_map),
+      std::move(maybe_key_data_map).value(), data_version);
+}
+
+// Takes a cbor::Value corresponding to a partition of type `signals_type` and
+// attempts to parse it to a TrustedSignals::Result.
+ResultOrError ParsePartition(
+    AuctionV8Helper* v8_helper,
+    TrustedSignalsKVv2ResponseParser::SignalsType signals_type,
+    const cbor::Value& partition_value,
+    int& partition_id) {
+  std::optional<uint32_t> data_version;
+
+  auto maybe_key_group_outputs = GetKeyGroupOutputsFromPartition(
+      partition_value, partition_id, data_version);
+  if (!maybe_key_group_outputs.has_value()) {
+    return base::unexpected(
+        std::move(maybe_key_group_outputs.error().error_msg));
+  }
+
+  base::expected<std::map<std::string, const cbor::Value::MapValue*>,
+                 TrustedSignalsKVv2ResponseParser::ErrorInfo>
+      key_group_outputs_map =
+          ParseKeyGroupOutputsToMap(*maybe_key_group_outputs.value());
+
+  if (!key_group_outputs_map.has_value()) {
+    return base::unexpected(std::move(key_group_outputs_map.error().error_msg));
+  }
+
+  if (signals_type == TrustedSignalsKVv2ResponseParser::SignalsType::kBidding) {
+    return ParseBiddingPartition(v8_helper, *key_group_outputs_map,
+                                 data_version);
+  } else {
+    // Scoring signals not yet supported.
+    NOTREACHED();
+  }
+}
+
 }  // namespace
 
 TrustedSignalsKVv2RequestHelper::TrustedSignalsKVv2RequestHelper(
@@ -963,7 +1084,9 @@
   TrustedSignalsResultMap result_map;
 
   for (const auto& group : compression_group_result_map) {
-    auto maybe_content = GetContentFromCompressionGroup(group.second);
+    auto maybe_content = GetContentFromCompressionGroup(
+        group.second.compression_scheme,
+        base::as_byte_span(group.second.content));
     if (!maybe_content.has_value()) {
       return base::unexpected(TrustedSignalsKVv2ResponseParser::ErrorInfo(
           std::move(maybe_content).error().error_msg));
@@ -1087,7 +1210,9 @@
   TrustedSignalsResultMap result_map;
 
   for (const auto& group : compression_group_result_map) {
-    auto maybe_content_value = GetContentFromCompressionGroup(group.second);
+    auto maybe_content_value = GetContentFromCompressionGroup(
+        group.second.compression_scheme,
+        base::as_byte_span(group.second.content));
     if (!maybe_content_value.has_value()) {
       return base::unexpected(std::move(maybe_content_value).error());
     }
@@ -1147,4 +1272,36 @@
   return result_map;
 }
 
+TrustedSignalsKVv2ResponseParser::PartitionMapOrError
+TrustedSignalsKVv2ResponseParser::ParseEntireCompressionGroup(
+    AuctionV8Helper* v8_helper,
+    SignalsType signals_type,
+    mojom::TrustedSignalsCompressionScheme compression_scheme,
+    base::span<const uint8_t> compression_group_bytes) {
+  AuctionV8Helper::FullIsolateScope isolate_scope(v8_helper);
+  v8::Context::Scope context_scope(v8_helper->scratch_context());
+
+  auto maybe_cbor = GetContentFromCompressionGroup(compression_scheme,
+                                                   compression_group_bytes);
+  if (!maybe_cbor.has_value()) {
+    return base::unexpected(std::move(maybe_cbor.error().error_msg));
+  }
+
+  PartitionMap partitions;
+  for (const auto& partition_value : maybe_cbor.value().GetArray()) {
+    int partition_id;
+    ResultOrError result =
+        ParsePartition(v8_helper, signals_type, partition_value, partition_id);
+    if (!result.has_value()) {
+      return base::unexpected(std::move(result).error());
+    }
+    if (!partitions.try_emplace(partition_id, std::move(result).value())
+             .second) {
+      return base::unexpected(
+          base::StringPrintf("Duplicated partition id \"%d\".", partition_id));
+    }
+  }
+  return partitions;
+}
+
 }  // namespace auction_worklet
diff --git a/content/services/auction_worklet/trusted_signals_kvv2_helper.h b/content/services/auction_worklet/trusted_signals_kvv2_helper.h
index d472ad51..8c50312d 100644
--- a/content/services/auction_worklet/trusted_signals_kvv2_helper.h
+++ b/content/services/auction_worklet/trusted_signals_kvv2_helper.h
@@ -16,6 +16,7 @@
 #include <string_view>
 #include <vector>
 
+#include "base/containers/span.h"
 #include "base/time/time.h"
 #include "base/types/expected.h"
 #include "base/types/optional_ref.h"
@@ -363,6 +364,38 @@
       const std::set<std::string>& render_urls,
       const std::set<std::string>& ad_component_render_urls,
       const CompressionGroupResultMap& compression_group_result_map);
+
+  // Code below this point is for use by the TrustedSignalsKVv2Manager, which
+  // gets KVv2 responses on a per-compression group basis through the
+  // mojom::TrustedSignalsCache API, instead of making network requests itself.
+
+  enum class SignalsType {
+    kBidding,
+    kScoring,
+  };
+
+  // A map of partition IDs to the result of parsing each partition.
+  using PartitionMap = std::map<int, scoped_refptr<TrustedSignals::Result>>;
+
+  // The result of trying to parse a compression group. Failures are global, so
+  // either there's a PartitionMap or a single global error string.
+  using PartitionMapOrError = base::expected<PartitionMap, std::string>;
+
+  // Parses a compression group. Unlike
+  // ParseBiddingSignalsFetchResultToResultMap(), parses all fields of all
+  // partitions, so if requests for the compression group arrive after it's
+  // already been parsed, there's no need to parse the data again.
+  //
+  // TODO(https://crbug.com/368241694): Consider only parsing the portion of the
+  // compression group that's needed, while keeping the rest around in case it's
+  // needed down the line. Parsing on a per-partition basis may be the best
+  // balance of practicality and compatibility in the case of multiple V8
+  // threads.
+  static PartitionMapOrError ParseEntireCompressionGroup(
+      AuctionV8Helper* v8_helper,
+      SignalsType signals_type,
+      mojom::TrustedSignalsCompressionScheme compression_scheme,
+      base::span<const uint8_t> compression_group_bytes);
 };
 
 }  // namespace auction_worklet
diff --git a/content/services/auction_worklet/trusted_signals_kvv2_helper_unittest.cc b/content/services/auction_worklet/trusted_signals_kvv2_helper_unittest.cc
index bcf8a7a7..a99615c 100644
--- a/content/services/auction_worklet/trusted_signals_kvv2_helper_unittest.cc
+++ b/content/services/auction_worklet/trusted_signals_kvv2_helper_unittest.cc
@@ -23,13 +23,17 @@
 #include "base/containers/span.h"
 #include "base/containers/span_writer.h"
 #include "base/strings/string_number_conversions.h"
+#include "base/test/bind.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/test/task_environment.h"
 #include "base/time/time.h"
 #include "components/cbor/values.h"
 #include "components/cbor/writer.h"
+#include "content/common/features.h"
+#include "content/services/auction_worklet/public/cpp/cbor_test_util.h"
 #include "content/services/auction_worklet/public/mojom/auction_worklet_service.mojom.h"
 #include "net/third_party/quiche/src/quiche/oblivious_http/oblivious_http_gateway.h"
+#include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/common/features.h"
 #include "third_party/zlib/google/compression_utils.h"
@@ -111,7 +115,7 @@
 // GzipCompress() doesn't support writing to a vector, only a std::string. This
 // wrapper provides that capability, at the cost of an extra copy.
 std::vector<std::uint8_t> GzipCompressHelper(
-    const std::vector<std::uint8_t>& in) {
+    base::span<const std::uint8_t> in) {
   std::string compressed_string;
   EXPECT_TRUE(compression::GzipCompress(in, &compressed_string));
   return std::vector<std::uint8_t>(compressed_string.begin(),
@@ -135,6 +139,32 @@
   }
 }
 
+// Returns the results of calling TrustedSignals::Result::GetBiddingSignals()
+// with `trusted_bidding_signals_keys`. Returns value as a JSON std::string,
+// for easy testing.
+std::string ExtractBiddingSignals(
+    AuctionV8Helper* v8_helper,
+    TrustedSignals::Result* signals,
+    std::vector<std::string> trusted_bidding_signals_keys) {
+  AuctionV8Helper::FullIsolateScope isolate_scope(v8_helper);
+  v8::Isolate* isolate = v8_helper->isolate();
+  // Could use the scratch context, but using a separate one more
+  // closely resembles actual use.
+  v8::Local<v8::Context> context = v8::Context::New(isolate);
+  v8::Context::Scope context_scope(context);
+
+  v8::Local<v8::Value> value = signals->GetBiddingSignals(
+      v8_helper, context, trusted_bidding_signals_keys);
+
+  std::string result;
+  if (v8_helper->ExtractJson(context, value,
+                             /*script_timeout=*/nullptr,
+                             &result) != AuctionV8Helper::Result::kSuccess) {
+    result = "JSON extraction failed.";
+  }
+  return result;
+}
+
 // Check trusted bidding signals' priority vector and bidding signals in json
 // format with given interest group names and bidding keys.
 void CheckBiddingResult(
@@ -157,20 +187,8 @@
     EXPECT_EQ(priority_vector_map.at(name), *maybe_priority_vector);
   }
 
-  AuctionV8Helper::FullIsolateScope isolate_scope(v8_helper);
-  v8::Isolate* isolate = v8_helper->isolate();
-  v8::Local<v8::Context> context = v8::Context::New(isolate);
-  v8::Context::Scope context_scope(context);
-  v8::Local<v8::Value> value =
-      result->GetBiddingSignals(v8_helper, context, keys);
-  std::string bidding_signals_json;
-
-  if (v8_helper->ExtractJson(context, value, /*script_timeout=*/nullptr,
-                             &bidding_signals_json) !=
-      AuctionV8Helper::Result::kSuccess) {
-    bidding_signals_json = "JSON extraction failed.";
-  }
-
+  std::string bidding_signals_json =
+      ExtractBiddingSignals(v8_helper, result, keys);
   EXPECT_EQ(bidding_signals, bidding_signals_json);
   EXPECT_EQ(data_version, result->GetDataVersion());
 }
@@ -330,6 +348,39 @@
   return std::move(result.error().error_msg);
 }
 
+// Checks that a PartitionMapOrError is not an error and contains exactly the
+// listed partitions.
+MATCHER_P(PartitionsAre, expected_values, "") {
+  if (!arg.has_value()) {
+    *result_listener << "is unexpectedly an error: \"" << arg.error() << "\"";
+    return false;
+  }
+
+  std::vector<int> keys;
+  for (const auto& pair : *arg) {
+    keys.push_back(pair.first);
+  }
+
+  return testing::ExplainMatchResult(
+      testing::UnorderedElementsAreArray(expected_values), keys,
+      result_listener);
+}
+
+// Checks that a PartitionMapOrError is an error with the specified value.
+MATCHER_P(IsError, expected_error, "") {
+  if (arg.has_value()) {
+    *result_listener << "is unexpectedly not an error.";
+    return false;
+  }
+
+  bool match = testing::ExplainMatchResult(testing::Eq(expected_error),
+                                           arg.error(), result_listener);
+  if (!match) {
+    *result_listener << "Actual error: \"" << arg.error() << "\"";
+  }
+  return match;
+}
+
 }  // namespace
 
 class TrustedSignalsKVv2RequestHelperTest : public testing::Test {
@@ -2383,4 +2434,879 @@
           helper_, kRenderUrls, kAdComponentRenderUrls, result_map));
 }
 
+////////////////////////////////////////////////////////////////////////////////
+// ParseEntireCompressionGroup tests
+////////////////////////////////////////////////////////////////////////////////
+
+TEST_F(TrustedSignalsKVv2ResponseParserTest, BiddingSignalsEmptyData) {
+  auto result_or_error =
+      TrustedSignalsKVv2ResponseParser::ParseEntireCompressionGroup(
+          helper_.get(),
+          TrustedSignalsKVv2ResponseParser::SignalsType::kBidding,
+          mojom::TrustedSignalsCompressionScheme::kNone, base::span<uint8_t>());
+  EXPECT_THAT(result_or_error, IsError("Failed to parse content as CBOR."));
+}
+
+TEST_F(TrustedSignalsKVv2ResponseParserTest, BiddingSignalsNonCborData) {
+  auto result_or_error =
+      TrustedSignalsKVv2ResponseParser::ParseEntireCompressionGroup(
+          helper_.get(),
+          TrustedSignalsKVv2ResponseParser::SignalsType::kBidding,
+          mojom::TrustedSignalsCompressionScheme::kNone,
+          base::as_bytes(base::make_span("Not CBOR")));
+  EXPECT_THAT(result_or_error, IsError("Failed to parse content as CBOR."));
+}
+
+TEST_F(TrustedSignalsKVv2ResponseParserTest, BiddingSignalsNotCborArray) {
+  auto result_or_error =
+      TrustedSignalsKVv2ResponseParser::ParseEntireCompressionGroup(
+          helper_.get(),
+          TrustedSignalsKVv2ResponseParser::SignalsType::kBidding,
+          mojom::TrustedSignalsCompressionScheme::kNone,
+          test::ToCborVector(R"({"this": "is a map."})"));
+  EXPECT_THAT(result_or_error, IsError("Content is not type of array."));
+}
+
+TEST_F(TrustedSignalsKVv2ResponseParserTest, BiddingSignalsNoPartitions) {
+  auto result_or_error =
+      TrustedSignalsKVv2ResponseParser::ParseEntireCompressionGroup(
+          helper_.get(),
+          TrustedSignalsKVv2ResponseParser::SignalsType::kBidding,
+          mojom::TrustedSignalsCompressionScheme::kNone,
+          test::ToCborVector("[]"));
+  ASSERT_THAT(result_or_error, PartitionsAre(std::vector<int>{}));
+}
+
+TEST_F(TrustedSignalsKVv2ResponseParserTest, BiddingSignalsInvalidPartitions) {
+  const struct {
+    const char* reponse_as_json;
+    const char* expected_error;
+  } kTestCases[] = {{"[[]]", "Partition is not type of map."},
+                    {"[1]", "Partition is not type of map."},
+
+                    {R"([{ "id": 0 }])",
+                     R"(Key "keyGroupOutputs" is missing in partition map.)"},
+
+                    {R"([{ "id": [], "keyGroupOutputs": [] }])",
+                     "Partition id is not type of integer."},
+                    {R"([{ "id": "Rosebud", "keyGroupOutputs": [] }])",
+                     "Partition id is not type of integer."},
+                    {R"([{ "id": 0.5, "keyGroupOutputs": [] }])",
+                     "Partition id is not type of integer."},
+
+                    {R"([{ "id": 37, "keyGroupOutputs": [] },
+                         { "id": 37, "keyGroupOutputs": [] }])",
+                     R"(Duplicated partition id "37".)"}};
+
+  for (const auto& test_case : kTestCases) {
+    SCOPED_TRACE(test_case.reponse_as_json);
+    auto result_or_error =
+        TrustedSignalsKVv2ResponseParser::ParseEntireCompressionGroup(
+            helper_.get(),
+            TrustedSignalsKVv2ResponseParser::SignalsType::kBidding,
+            mojom::TrustedSignalsCompressionScheme::kNone,
+            test::ToCborVector(test_case.reponse_as_json));
+    EXPECT_THAT(result_or_error, IsError(test_case.expected_error));
+  }
+}
+
+// Empty partitions are allowed, as long as they have a "keyGroupOutputs" array.
+TEST_F(TrustedSignalsKVv2ResponseParserTest, BiddingSignalsEmptyPartition) {
+  auto result_or_error =
+      TrustedSignalsKVv2ResponseParser::ParseEntireCompressionGroup(
+          helper_.get(),
+          TrustedSignalsKVv2ResponseParser::SignalsType::kBidding,
+          mojom::TrustedSignalsCompressionScheme::kNone,
+          test::ToCborVector(R"([{ "id": 0, "keyGroupOutputs": [] }])"));
+  ASSERT_THAT(result_or_error, PartitionsAre(std::vector<int>{0}));
+  EXPECT_FALSE((*result_or_error)[0]->GetDataVersion());
+  EXPECT_FALSE((*result_or_error)[0]->GetPerGroupData("group1"));
+  EXPECT_EQ(ExtractBiddingSignals(helper_.get(), (*result_or_error)[0].get(),
+                                  {"key1"}),
+            R"({"key1":null})");
+}
+
+TEST_F(TrustedSignalsKVv2ResponseParserTest, BiddingSignalsInvalidDataVersion) {
+  const struct {
+    const char* data_version;
+    const char* expected_error;
+  } kTestCases[] = {
+      {"1.0", "DataVersion is not type of integer."},
+      {"\"1\"", "DataVersion is not type of integer."},
+      {"[1]", "DataVersion is not type of integer."},
+
+      {"-1", "DataVersion field is out of range for uint32."},
+      // 4294967296 is the minimum invalid integer, but can't be covered in a
+      // test that uses ToCborVector(), as it goes through base::Value(), which
+      // only supports floats and ints. As a result, it's covered in another
+      // test.
+  };
+  for (const auto& test_case : kTestCases) {
+    SCOPED_TRACE(test_case.data_version);
+    auto result_or_error =
+        TrustedSignalsKVv2ResponseParser::ParseEntireCompressionGroup(
+            helper_.get(),
+            TrustedSignalsKVv2ResponseParser::SignalsType::kBidding,
+            mojom::TrustedSignalsCompressionScheme::kNone,
+            test::ToCborVector(base::StringPrintf(
+                R"([{"id": 0,
+                     "dataVersion": %s,
+                     "keyGroupOutputs": [] }])",
+                test_case.data_version)));
+    EXPECT_THAT(result_or_error, IsError(test_case.expected_error));
+  }
+}
+
+TEST_F(TrustedSignalsKVv2ResponseParserTest, BiddingSignalsDataVersion) {
+  const uint32_t kTestCases[] = {0, 1};
+  for (uint32_t test_case : kTestCases) {
+    SCOPED_TRACE(test_case);
+    auto result_or_error =
+        TrustedSignalsKVv2ResponseParser::ParseEntireCompressionGroup(
+            helper_.get(),
+            TrustedSignalsKVv2ResponseParser::SignalsType::kBidding,
+            mojom::TrustedSignalsCompressionScheme::kNone,
+            test::ToCborVector(base::StringPrintf(
+                R"([{"id": 0,
+                     "dataVersion": %u,
+                     "keyGroupOutputs": [] }])",
+                test_case)));
+    ASSERT_THAT(result_or_error, PartitionsAre(std::vector<int>{0}));
+    EXPECT_EQ((*result_or_error)[0]->GetDataVersion(), test_case);
+  }
+}
+
+// ToCborVector() uses base::Value, which only supports ints and doubles. The
+// maximum DataVersion value is the max uint32, so to test the max value, have
+// to construct the cbor::Value directly.
+TEST_F(TrustedSignalsKVv2ResponseParserTest,
+       BiddingSignalsDataVersionMaxValue) {
+  const int64_t kDataVersionMax = std::numeric_limits<uint32_t>::max();
+
+  // Max value should succeed.
+  cbor::Value::MapValue max_data_version_compression_group;
+  max_data_version_compression_group.emplace(cbor::Value("id"), cbor::Value(0));
+  max_data_version_compression_group.emplace(cbor::Value("dataVersion"),
+                                             cbor::Value(kDataVersionMax));
+  max_data_version_compression_group.emplace(
+      cbor::Value("keyGroupOutputs"), cbor::Value(cbor::Value::ArrayValue()));
+  cbor::Value::ArrayValue max_data_version_partitions;
+  max_data_version_partitions.emplace_back(
+      std::move(max_data_version_compression_group));
+  cbor::Value max_data_version(std::move(max_data_version_partitions));
+  auto result_or_error =
+      TrustedSignalsKVv2ResponseParser::ParseEntireCompressionGroup(
+          helper_.get(),
+          TrustedSignalsKVv2ResponseParser::SignalsType::kBidding,
+          mojom::TrustedSignalsCompressionScheme::kNone,
+          *cbor::Writer::Write(max_data_version));
+  ASSERT_THAT(result_or_error, PartitionsAre(std::vector<int>{0}));
+  EXPECT_EQ((*result_or_error)[0]->GetDataVersion(), kDataVersionMax);
+
+  // Max value + 1 should fail.
+  cbor::Value::MapValue max_data_version_exceeded_compression_group;
+  max_data_version_exceeded_compression_group.emplace(cbor::Value("id"),
+                                                      cbor::Value(0));
+  max_data_version_exceeded_compression_group.emplace(
+      cbor::Value("dataVersion"), cbor::Value(kDataVersionMax + 1));
+  max_data_version_exceeded_compression_group.emplace(
+      cbor::Value("keyGroupOutputs"), cbor::Value(cbor::Value::ArrayValue()));
+  cbor::Value::ArrayValue max_data_version_exceeded_partitions;
+  max_data_version_exceeded_partitions.emplace_back(
+      std::move(max_data_version_exceeded_compression_group));
+  cbor::Value max_data_version_exceeded(
+      std::move(max_data_version_exceeded_partitions));
+  result_or_error =
+      TrustedSignalsKVv2ResponseParser::ParseEntireCompressionGroup(
+          helper_.get(),
+          TrustedSignalsKVv2ResponseParser::SignalsType::kBidding,
+          mojom::TrustedSignalsCompressionScheme::kNone,
+          *cbor::Writer::Write(std::move(max_data_version_exceeded)));
+  EXPECT_THAT(result_or_error,
+              IsError("DataVersion field is out of range for uint32."));
+}
+
+// This test covers cases where the structure of the "keyGroupOutputs" or the
+// maps the tags or KeyGroupOutputs values it contains are invalid.
+TEST_F(TrustedSignalsKVv2ResponseParserTest,
+       BiddingSignalsInvalidKeyGroupOutputs) {
+  const struct {
+    const char* key_group_outputs;
+    const char* expected_error;
+  } kTestCases[] = {
+      // Value not array.
+      {"{}", R"(Partition key group outputs is not type of array.)"},
+      {"1", R"(Partition key group outputs is not type of array.)"},
+
+      // Array has value of wrong type.
+      {"[[]]", R"(KeyGroupOutput value is not type of map.)"},
+      {"[1]", R"(KeyGroupOutput value is not type of map.)"},
+      // Next two tests have one valid and one invalid entry in the array.
+      {R"([{"tags": ["tag1"], "keyValues": {}}, []])",
+       R"(KeyGroupOutput value is not type of map.)"},
+      {R"([[], {"tags": ["tag1"], "keyValues": {}}])",
+       R"(KeyGroupOutput value is not type of map.)"},
+
+      // Missing / invalid tags array test cases.
+      {R"([{"keyValues": {}}])",
+       R"(Key "tags" is missing in keyGroupOutputs map.)"},
+      {R"([{"tags": {"1":"2"}, "keyValues": {}}])",
+       R"(Tags value in keyGroupOutputs map is not type of array.)"},
+      {R"([{"tags": 1, "keyValues": {}}])",
+       R"(Tags value in keyGroupOutputs map is not type of array.)"},
+      {R"([{"tags": [1], "keyValues": {}}])",
+       "Tag value in tags array of keyGroupOutputs map is not type of string."},
+      {R"([{"tags": ["tag1", "tag2"], "keyValues": {}}])",
+       R"(Tags array must only have one tag.)"},
+      {R"([{"tags": ["tag1", 2], "keyValues": {}}])",
+       R"(Tags array must only have one tag.)"},
+
+      // Missing / invalid `keyValues` map test cases.
+      {R"([{"tags": ["tag1"]}])",
+       R"(Key "keyValues" is missing in keyGroupOutputs map.)"},
+      {R"([{"tags": ["tag1"], "keyValues": 1}])",
+       R"(KeyValue value in keyGroupOutputs map is not type of map.)"},
+      {R"([{"tags": ["tag1"], "keyValues": []}])",
+       R"(KeyValue value in keyGroupOutputs map is not type of map.)"},
+  };
+
+  for (const auto& test_case : kTestCases) {
+    SCOPED_TRACE(test_case.key_group_outputs);
+    auto result_or_error =
+        TrustedSignalsKVv2ResponseParser::ParseEntireCompressionGroup(
+            helper_.get(),
+            TrustedSignalsKVv2ResponseParser::SignalsType::kBidding,
+            mojom::TrustedSignalsCompressionScheme::kNone,
+            test::ToCborVector(
+                base::StringPrintf(R"([{ "id": 0, "keyGroupOutputs": %s }])",
+                                   test_case.key_group_outputs)));
+    EXPECT_THAT(result_or_error, IsError(test_case.expected_error));
+  }
+}
+
+// Unknown tags are ignored.
+TEST_F(TrustedSignalsKVv2ResponseParserTest, BiddingSignalsUnknownTags) {
+  auto result_or_error =
+      TrustedSignalsKVv2ResponseParser::ParseEntireCompressionGroup(
+          helper_.get(),
+          TrustedSignalsKVv2ResponseParser::SignalsType::kBidding,
+          mojom::TrustedSignalsCompressionScheme::kNone,
+          test::ToCborVector(
+              R"([{
+                "id": 0,
+                "keyGroupOutputs": [
+                  {"tags": ["foo"], "keyValues": {}},
+                  {"tags": ["bar"], "keyValues": {"foo":"bar"}}
+                ]
+              }])"));
+  ASSERT_THAT(result_or_error, PartitionsAre(std::vector<int>{0}));
+  EXPECT_FALSE((*result_or_error)[0]->GetDataVersion());
+  EXPECT_FALSE((*result_or_error)[0]->GetPerGroupData("group1"));
+  EXPECT_EQ(ExtractBiddingSignals(helper_.get(), (*result_or_error)[0].get(),
+                                  {"key1"}),
+            R"({"key1":null})");
+}
+
+// Tests errors related to the "value" entry in the interestGroupNames
+// dictionary. In particular, test when it's not present, not JSON, or the wrong
+// JSON type.
+TEST_F(TrustedSignalsKVv2ResponseParserTest,
+       BiddingSignalsInterestGroupNamesValueError) {
+  const struct {
+    const char* value;
+    const char* expected_error;
+  } kTestCases[] = {
+      {"", R"(Failed to find key "value" in the map.)"},
+      {R"("not-value": "{}")", R"(Failed to find key "value" in the map.)"},
+      {R"("value": null)",
+       R"(Failed to read value of key "value" as type String.)"},
+      {R"("value": [42])",
+       R"(Failed to read value of key "value" as type String.)"},
+      {R"("value": "")",
+       "Failed to create V8 value from key group output data."},
+      {R"("value": "Not Json")",
+       "Failed to create V8 value from key group output data."},
+      {R"("value": "\"Not a dictionary\"")",
+       "Failed to create V8 value from key group output data."},
+      {R"("value": "[\"Also not a dictionary\"]")",
+       "Failed to create V8 value from key group output data."},
+  };
+
+  for (const auto& test_case : kTestCases) {
+    SCOPED_TRACE(test_case.value);
+    auto result_or_error =
+        TrustedSignalsKVv2ResponseParser::ParseEntireCompressionGroup(
+            helper_.get(),
+            TrustedSignalsKVv2ResponseParser::SignalsType::kBidding,
+            mojom::TrustedSignalsCompressionScheme::kNone,
+            test::ToCborVector(base::StringPrintf(
+                R"([{
+                  "id": 0,
+                  "keyGroupOutputs": [{
+                    "tags": ["interestGroupNames"],
+                    "keyValues": {
+                      "group1": { %s }
+                    }
+                  }]
+                }])",
+                test_case.value)));
+    EXPECT_THAT(result_or_error, IsError(test_case.expected_error));
+  }
+}
+
+// Test the case where `interestGroupNames` is valid JSON dictionary but has no
+// known keys. This case is not an error, but there should be no PerGroupData.
+TEST_F(TrustedSignalsKVv2ResponseParserTest,
+       BiddingSignalsInterestGroupNamesNoKnownKeys) {
+  const char* kTestCases[] = {
+      R"("{}")",
+      R"("{\"unknown1\":42}")",
+      R"("{\"unknown2\":{\"signal1\":1}}")",
+  };
+
+  for (const auto* test_case : kTestCases) {
+    SCOPED_TRACE(test_case);
+    auto result_or_error =
+        TrustedSignalsKVv2ResponseParser::ParseEntireCompressionGroup(
+            helper_.get(),
+            TrustedSignalsKVv2ResponseParser::SignalsType::kBidding,
+            mojom::TrustedSignalsCompressionScheme::kNone,
+            test::ToCborVector(base::StringPrintf(
+                R"([{
+                  "id": 0,
+                  "keyGroupOutputs": [{
+                    "tags": ["interestGroupNames"],
+                    "keyValues": {
+                      "group1": { "value": %s }
+                    }
+                  }]
+                }])",
+                test_case)));
+    ASSERT_THAT(result_or_error, PartitionsAre(std::vector<int>{0}));
+    EXPECT_FALSE((*result_or_error)[0]->GetPerGroupData("group1"));
+  }
+}
+
+TEST_F(TrustedSignalsKVv2ResponseParserTest,
+       BiddingSignalsPriorityVectorWrongType) {
+  const char* kTestCases[] = {
+      R"("{\"priorityVector\":null}")",
+      R"("{\"priorityVector\":[]}")",
+      R"("{\"priorityVector\":42}")",
+  };
+
+  for (const auto* test_case : kTestCases) {
+    SCOPED_TRACE(test_case);
+    auto result_or_error =
+        TrustedSignalsKVv2ResponseParser::ParseEntireCompressionGroup(
+            helper_.get(),
+            TrustedSignalsKVv2ResponseParser::SignalsType::kBidding,
+            mojom::TrustedSignalsCompressionScheme::kNone,
+            test::ToCborVector(base::StringPrintf(
+                R"([{
+                  "id": 0,
+                  "keyGroupOutputs": [{
+                    "tags": ["interestGroupNames"],
+                    "keyValues": {
+                      "group1": { "value": %s }
+                    }
+                  }]
+                }])",
+                test_case)));
+
+    // These are currently not considered fatal errors.
+    ASSERT_THAT(result_or_error, PartitionsAre(std::vector<int>{0}));
+    EXPECT_FALSE((*result_or_error)[0]->GetPerGroupData("group1"));
+  }
+}
+
+TEST_F(TrustedSignalsKVv2ResponseParserTest, BiddingSignalsPriorityVector) {
+  const struct {
+    const char* priority_vector_json;
+    TrustedSignals::Result::PriorityVector expected_value;
+  } kTestCases[] = {
+      {R"("{\"priorityVector\":{}}")", {}},
+      {R"("{\"priorityVector\":{\"signal1\":1}}")", {{"signal1", 1}}},
+      {R"("{\"priorityVector\":{\"signal1\":-3, \"signal2\":2.5}}")",
+       {{"signal1", -3}, {"signal2", 2.5}}},
+
+      // Invalid values are currently silently ignored, though they do result in
+      // a non-null PerGroupData, with a populated `priority_vector`.
+      {R"("{\"priorityVector\":{\"signal1\":null}}")", {}},
+      {R"("{\"priorityVector\":{\"signal1\":null,\"signal2\":3}}")",
+       {{"signal2", 3}}},
+      {R"("{\"priorityVector\":{\"signal1\":[2]}}")", {}},
+      {R"("{\"priorityVector\":{\"signal1\":[2],\"signal2\":3}}")",
+       {{"signal2", 3}}},
+      {R"("{\"priorityVector\":{\"signal1\":\"2\"}}")", {}},
+  };
+
+  for (const auto& test_case : kTestCases) {
+    SCOPED_TRACE(test_case.priority_vector_json);
+    auto result_or_error =
+        TrustedSignalsKVv2ResponseParser::ParseEntireCompressionGroup(
+            helper_.get(),
+            TrustedSignalsKVv2ResponseParser::SignalsType::kBidding,
+            mojom::TrustedSignalsCompressionScheme::kNone,
+            test::ToCborVector(base::StringPrintf(
+                R"([{
+                  "id": 0,
+                  "keyGroupOutputs": [{
+                    "tags": ["interestGroupNames"],
+                    "keyValues": {
+                      "group1": { "value": %s }
+                    }
+                  }]
+                }])",
+                test_case.priority_vector_json)));
+    ASSERT_THAT(result_or_error, PartitionsAre(std::vector<int>{0}));
+
+    auto* per_group_data = (*result_or_error)[0]->GetPerGroupData("group1");
+    ASSERT_TRUE(per_group_data);
+    EXPECT_EQ(per_group_data->priority_vector, test_case.expected_value);
+    EXPECT_FALSE(per_group_data->update_if_older_than);
+  }
+}
+
+TEST_F(TrustedSignalsKVv2ResponseParserTest,
+       BiddingSignalsUpdateIfOlderThanMs) {
+  const struct {
+    const char* parse_update_if_older_than_json;
+    std::optional<base::TimeDelta> expected_value;
+  } kTestCases[] = {
+      // Invalid values are currently silently ignored.
+      {R"("{\"updateIfOlderThanMs\":null}")", std::nullopt},
+      {R"("{\"updateIfOlderThanMs\":\"2\"}")", std::nullopt},
+      {R"("{\"updateIfOlderThanMs\":[2]}")", std::nullopt},
+      {R"("{\"updateIfOlderThanMs\":{}}")", std::nullopt},
+
+      {R"("{\"updateIfOlderThanMs\":2}")", base::Milliseconds(2)},
+      {R"("{\"updateIfOlderThanMs\":-3.5}")", base::Milliseconds(-3.5)},
+  };
+
+  for (bool enable_feature : {false, true}) {
+    SCOPED_TRACE(enable_feature);
+    base::test::ScopedFeatureList feature_list;
+    feature_list.InitWithFeatureState(features::kInterestGroupUpdateIfOlderThan,
+                                      enable_feature);
+    for (const auto& test_case : kTestCases) {
+      SCOPED_TRACE(test_case.parse_update_if_older_than_json);
+      auto result_or_error =
+          TrustedSignalsKVv2ResponseParser::ParseEntireCompressionGroup(
+              helper_.get(),
+              TrustedSignalsKVv2ResponseParser::SignalsType::kBidding,
+              mojom::TrustedSignalsCompressionScheme::kNone,
+              test::ToCborVector(base::StringPrintf(
+                  R"([{
+                    "id": 0,
+                    "keyGroupOutputs": [{
+                      "tags": ["interestGroupNames"],
+                      "keyValues": {
+                        "group1": { "value": %s }
+                      }
+                    }]
+                  }])",
+                  test_case.parse_update_if_older_than_json)));
+      ASSERT_THAT(result_or_error, PartitionsAre(std::vector<int>{0}));
+
+      auto* per_group_data = (*result_or_error)[0]->GetPerGroupData("group1");
+      if (!enable_feature || !test_case.expected_value) {
+        // When no value is expected and there is no priority vector,
+        // `per_group_data` is nullopt.
+        EXPECT_FALSE(per_group_data);
+      } else {
+        ASSERT_TRUE(per_group_data);
+        EXPECT_FALSE(per_group_data->priority_vector);
+        EXPECT_EQ(per_group_data->update_if_older_than,
+                  test_case.expected_value);
+      }
+    }
+  }
+}
+
+// Test that when part of an interest group's JSON is invalid, the rest is
+// successfully parsed. e.g., a bad `priorityVectors` doesn't invalidate
+// `updateIfOlderThanMs`, and vice versa.
+TEST_F(TrustedSignalsKVv2ResponseParserTest,
+       BiddingSignalsPerGroupDataHalfBad) {
+  // Invalid `priorityVector`, valid `updateIfOlderThanMs`.
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeature(features::kInterestGroupUpdateIfOlderThan);
+  auto result_or_error =
+      TrustedSignalsKVv2ResponseParser::ParseEntireCompressionGroup(
+          helper_.get(),
+          TrustedSignalsKVv2ResponseParser::SignalsType::kBidding,
+          mojom::TrustedSignalsCompressionScheme::kNone,
+          test::ToCborVector(
+              R"([{
+                "id": 0,
+                "keyGroupOutputs": [{
+                  "tags": ["interestGroupNames"],
+                  "keyValues": {
+                    "group1": {
+                      "value": "{
+                        \"priorityVector\": \"Not valid\",
+                        \"updateIfOlderThanMs\": 2
+                      }"
+                    }
+                  }
+                }]
+              }])"));
+  ASSERT_THAT(result_or_error, PartitionsAre(std::vector<int>{0}));
+  auto* per_group_data = (*result_or_error)[0]->GetPerGroupData("group1");
+  ASSERT_TRUE(per_group_data);
+  EXPECT_FALSE(per_group_data->priority_vector);
+  EXPECT_EQ(per_group_data->update_if_older_than, base::Milliseconds(2));
+
+  // Valid `priorityVector`, invalid `updateIfOlderThanMs`.
+  result_or_error =
+      TrustedSignalsKVv2ResponseParser::ParseEntireCompressionGroup(
+          helper_.get(),
+          TrustedSignalsKVv2ResponseParser::SignalsType::kBidding,
+          mojom::TrustedSignalsCompressionScheme::kNone,
+          test::ToCborVector(
+              R"([{
+                "id": 0,
+                "keyGroupOutputs": [{
+                  "tags": ["interestGroupNames"],
+                  "keyValues": {
+                    "group1": {
+                      "value": "{
+                        \"priorityVector\": {\"signal1\":2},
+                        \"updateIfOlderThanMs\": \"Not valid\"
+                      }"
+                    }
+                  }
+                }]
+              }])"));
+  ASSERT_THAT(result_or_error, PartitionsAre(std::vector<int>{0}));
+  per_group_data = (*result_or_error)[0]->GetPerGroupData("group1");
+  ASSERT_TRUE(per_group_data);
+  const TrustedSignals::Result::PriorityVector kExpectedPriorityVector{
+      {"signal1", 2}};
+  EXPECT_EQ(per_group_data->priority_vector, kExpectedPriorityVector);
+  EXPECT_FALSE(per_group_data->update_if_older_than);
+}
+
+TEST_F(TrustedSignalsKVv2ResponseParserTest,
+       BiddingSignalsPerGroupDataMultipleGroups) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeature(features::kInterestGroupUpdateIfOlderThan);
+  auto result_or_error =
+      TrustedSignalsKVv2ResponseParser::ParseEntireCompressionGroup(
+          helper_.get(),
+          TrustedSignalsKVv2ResponseParser::SignalsType::kBidding,
+          mojom::TrustedSignalsCompressionScheme::kNone,
+          test::ToCborVector(
+              R"([{
+                "id": 0,
+                "keyGroupOutputs": [{
+                  "tags": ["interestGroupNames"],
+                  "keyValues": {
+                    "group1": {
+                      "value": "{
+                        \"priorityVector\": {\"signal1\":1, \"signal3\":3},
+                        \"updateIfOlderThanMs\":2
+                      }"
+                    },
+                    "group2": {
+                      "value": "{
+                        \"priorityVector\": {\"signal1\":2, \"signal2\":4},
+                        \"updateIfOlderThanMs\":3
+                      }"
+                    }
+                  }
+                }]
+              }])"));
+  ASSERT_THAT(result_or_error, PartitionsAre(std::vector<int>{0}));
+
+  auto* per_group1_data = (*result_or_error)[0]->GetPerGroupData("group1");
+  ASSERT_TRUE(per_group1_data);
+  const TrustedSignals::Result::PriorityVector kExpectedPriorityVector1{
+      {"signal1", 1}, {"signal3", 3}};
+  EXPECT_EQ(per_group1_data->priority_vector, kExpectedPriorityVector1);
+  EXPECT_EQ(per_group1_data->update_if_older_than, base::Milliseconds(2));
+
+  auto* per_group2_data = (*result_or_error)[0]->GetPerGroupData("group2");
+  ASSERT_TRUE(per_group2_data);
+  const TrustedSignals::Result::PriorityVector kExpectedPriorityVector2{
+      {"signal1", 2}, {"signal2", 4}};
+  EXPECT_EQ(per_group2_data->priority_vector, kExpectedPriorityVector2);
+  EXPECT_EQ(per_group2_data->update_if_older_than, base::Milliseconds(3));
+
+  EXPECT_FALSE((*result_or_error)[0]->GetPerGroupData("group3"));
+}
+
+// Test cases where a `keys` entry of `keyGroupOutputs` has an invalid value.
+// Test is named "InvalidKeys" rather than "InvalidKeyValue" because there's a
+// field named KeyValue, and general invalid KeyValues are covered by another
+// test.
+TEST_F(TrustedSignalsKVv2ResponseParserTest, BiddingSignalsInvalidKeys) {
+  const struct {
+    const char* key_values_json;
+    const char* expected_error;
+  } kTestCases[] = {
+      {R"()", R"(Failed to find key "value" in the map.)"},
+      {R"("not-value":1)", R"(Failed to find key "value" in the map.)"},
+      {R"("value":"Not JSON")",
+       R"(Failed to parse key-value string to JSON for key "key1".)"},
+  };
+
+  for (const auto& test_case : kTestCases) {
+    SCOPED_TRACE(test_case.key_values_json);
+    auto result_or_error =
+        TrustedSignalsKVv2ResponseParser::ParseEntireCompressionGroup(
+            helper_.get(),
+            TrustedSignalsKVv2ResponseParser::SignalsType::kBidding,
+            mojom::TrustedSignalsCompressionScheme::kNone,
+            test::ToCborVector(base::StringPrintf(
+                R"([{
+                  "id": 0,
+                  "keyGroupOutputs": [{
+                    "tags": ["keys"],
+                    "keyValues": {
+                      "key1": { %s }
+                    }
+                  }]
+                }])",
+                test_case.key_values_json)));
+    EXPECT_THAT(result_or_error, IsError(test_case.expected_error));
+  }
+}
+
+TEST_F(TrustedSignalsKVv2ResponseParserTest, BiddingSignalsKeys) {
+  const struct {
+    const char* key_values_json;
+    std::vector<std::string> keys_to_request;
+    const char* expected_value;
+  } kTestCases[] = {
+      {R"({"key1":{"value":"null"}})", {"key1"}, R"({"key1":null})"},
+      {R"({"key1":{"value":"1"}})", {"key1"}, R"({"key1":1})"},
+      {R"({"key1":{"value":"-1.5"}})", {"key1"}, R"({"key1":-1.5})"},
+      {R"({"key1":{"value":"[]"}})", {"key1"}, R"({"key1":[]})"},
+      {R"({"key1":{"value":"[1,\"b\"]"}})", {"key1"}, R"({"key1":[1,"b"]})"},
+      {R"({"key1":{"value":"{}"}})", {"key1"}, R"({"key1":{}})"},
+      {R"({"key1":{"value":"{\"a\":\"b\",\"c\":1}"}})",
+       {"key1"},
+       R"({"key1":{"a":"b","c":1}})"},
+      {R"({"key1":{"value":"1"}})", {"key2"}, R"({"key2":null})"},
+      {R"({"key1":{"value":"1"},"key2":{"value":"3"}})",
+       {"key1", "key2"},
+       R"({"key1":1,"key2":3})"},
+
+      // Unexpected values are ignored.
+      {R"({"key1":{"value":"1","foo":"bar"}})", {"key1"}, R"({"key1":1})"},
+  };
+
+  for (const auto& test_case : kTestCases) {
+    SCOPED_TRACE(test_case.key_values_json);
+    auto result_or_error =
+        TrustedSignalsKVv2ResponseParser::ParseEntireCompressionGroup(
+            helper_.get(),
+            TrustedSignalsKVv2ResponseParser::SignalsType::kBidding,
+            mojom::TrustedSignalsCompressionScheme::kNone,
+            test::ToCborVector(base::StringPrintf(
+                R"([{
+                  "id": 0,
+                  "keyGroupOutputs": [{
+                    "tags": ["keys"],
+                    "keyValues": %s
+                  }]
+                }])",
+                test_case.key_values_json)));
+    ASSERT_THAT(result_or_error, PartitionsAre(std::vector<int>{0}));
+    EXPECT_FALSE((*result_or_error)[0]->GetPerGroupData("group1"));
+    EXPECT_EQ(ExtractBiddingSignals(helper_.get(), (*result_or_error)[0].get(),
+                                    test_case.keys_to_request),
+              test_case.expected_value);
+  }
+}
+
+// Test all fields together, for a single partition. Main purpose of this test
+// to test keys and interestGroupNames in a single response.
+TEST_F(TrustedSignalsKVv2ResponseParserTest, BiddingSignalsFullyPopulated) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeature(features::kInterestGroupUpdateIfOlderThan);
+  auto result_or_error =
+      TrustedSignalsKVv2ResponseParser::ParseEntireCompressionGroup(
+          helper_.get(),
+          TrustedSignalsKVv2ResponseParser::SignalsType::kBidding,
+          mojom::TrustedSignalsCompressionScheme::kNone,
+          test::ToCborVector(
+              R"([{
+                "id": 0,
+                "dataVersion": 1,
+                "keyGroupOutputs": [
+                  {
+                    "tags": ["interestGroupNames"],
+                    "keyValues": {
+                      "group1": {
+                        "value": "{
+                          \"priorityVector\": {\"signal1\":2},
+                          \"updateIfOlderThanMs\":3
+                        }"
+                      }
+                    }
+                  },
+                  {
+                    "tags": ["keys"],
+                    "keyValues": {
+                      "key1": {"value":"\"4\""}
+                    }
+                  }
+                ]
+              }])"));
+  ASSERT_THAT(result_or_error, PartitionsAre(std::vector<int>{0}));
+  EXPECT_EQ((*result_or_error)[0]->GetDataVersion(), 1);
+
+  auto* per_group1_data = (*result_or_error)[0]->GetPerGroupData("group1");
+  ASSERT_TRUE(per_group1_data);
+  const TrustedSignals::Result::PriorityVector kExpectedPriorityVector{
+      {"signal1", 2}};
+  EXPECT_EQ(per_group1_data->priority_vector, kExpectedPriorityVector);
+  EXPECT_EQ(per_group1_data->update_if_older_than, base::Milliseconds(3));
+
+  EXPECT_EQ(ExtractBiddingSignals(helper_.get(), (*result_or_error)[0].get(),
+                                  {"key1"}),
+            R"({"key1":"4"})");
+}
+
+TEST_F(TrustedSignalsKVv2ResponseParserTest,
+       BiddingSignalsCompressionSchemeNoneButGzipped) {
+  auto result_or_error =
+      TrustedSignalsKVv2ResponseParser::ParseEntireCompressionGroup(
+          helper_.get(),
+          TrustedSignalsKVv2ResponseParser::SignalsType::kBidding,
+          mojom::TrustedSignalsCompressionScheme::kNone,
+          GzipCompressHelper(
+              test::ToCborVector(R"([{ "id": 0, "keyGroupOutputs": [] }])")));
+  EXPECT_THAT(result_or_error, IsError("Failed to parse content as CBOR."));
+}
+
+TEST_F(TrustedSignalsKVv2ResponseParserTest,
+       BiddingSignalsCompressionSchemeGzipButNotGzipped) {
+  auto result_or_error =
+      TrustedSignalsKVv2ResponseParser::ParseEntireCompressionGroup(
+          helper_.get(),
+          TrustedSignalsKVv2ResponseParser::SignalsType::kBidding,
+          mojom::TrustedSignalsCompressionScheme::kGzip,
+          // Ideally this would be a valid CBOR compression group, but the gzip
+          // code unconditionally allocates memory based on the last 4 bytes of
+          // the response, which can be quite large. End this string with 4
+          // character 01's to avoid allocating too much memory.
+          base::as_bytes(base::make_span("Not gzip.\x1\x1\x1\x1")));
+  ASSERT_THAT(result_or_error,
+              IsError("Failed to decompress content string with Gzip."));
+}
+
+TEST_F(TrustedSignalsKVv2ResponseParserTest,
+       BiddingSignalsCompressionSchemeGzip) {
+  auto result_or_error =
+      TrustedSignalsKVv2ResponseParser::ParseEntireCompressionGroup(
+          helper_.get(),
+          TrustedSignalsKVv2ResponseParser::SignalsType::kBidding,
+          mojom::TrustedSignalsCompressionScheme::kGzip,
+          GzipCompressHelper(test::ToCborVector(
+              R"([{ "id": 37, "dataVersion": 5, "keyGroupOutputs": [] }])")));
+  ASSERT_THAT(result_or_error, PartitionsAre(std::vector<int>{37}));
+  EXPECT_EQ((*result_or_error)[37]->GetDataVersion(), 5);
+}
+
+TEST_F(TrustedSignalsKVv2ResponseParserTest, BiddingSignalsMultiplePartitions) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeature(features::kInterestGroupUpdateIfOlderThan);
+  auto result_or_error =
+      TrustedSignalsKVv2ResponseParser::ParseEntireCompressionGroup(
+          helper_.get(),
+          TrustedSignalsKVv2ResponseParser::SignalsType::kBidding,
+          mojom::TrustedSignalsCompressionScheme::kNone,
+          test::ToCborVector(
+              R"([
+                {
+                  "id": 0,
+                  "dataVersion": 1,
+                  "keyGroupOutputs": [
+                    {
+                      "tags": ["interestGroupNames"],
+                      "keyValues": {
+                        "group1": {
+                          "value": "{
+                            \"priorityVector\": {\"signal1\":2, \"signal2\":3},
+                            \"updateIfOlderThanMs\":4
+                          }"
+                        }
+                      }
+                    },
+                    {
+                      "tags": ["keys"],
+                      "keyValues": {
+                        "key1": {"value":"5"},
+                        "key2": {"value":"\"6\""}
+                      }
+                    }
+                  ]
+                },
+                {
+                  "id": 7,
+                  "dataVersion": 8,
+                  "keyGroupOutputs": [
+                    {
+                      "tags": ["interestGroupNames"],
+                      "keyValues": {
+                        "group2": {
+                          "value": "{
+                            \"priorityVector\": {\"signal1\":9, \"signal3\":10},
+                            \"updateIfOlderThanMs\":11
+                          }"
+                        }
+                      }
+                    },
+                    {
+                      "tags": ["keys"],
+                      "keyValues": {
+                        "key1": {"value":"12"},
+                        "key3": {"value":"[13]"},
+                      }
+                    }
+                  ]
+                },
+                {
+                  "id": 14,
+                  "keyGroupOutputs": []
+                }
+              ])"));
+  ASSERT_THAT(result_or_error, PartitionsAre(std::vector<int>{0, 7, 14}));
+
+  EXPECT_EQ((*result_or_error)[0]->GetDataVersion(), 1);
+  auto* per_group_data = (*result_or_error)[0]->GetPerGroupData("group1");
+  ASSERT_TRUE(per_group_data);
+  const TrustedSignals::Result::PriorityVector kExpectedPriorityVector1{
+      {"signal1", 2}, {"signal2", 3}};
+  EXPECT_EQ(per_group_data->priority_vector, kExpectedPriorityVector1);
+  EXPECT_EQ(per_group_data->update_if_older_than, base::Milliseconds(4));
+  EXPECT_FALSE((*result_or_error)[0]->GetPerGroupData("group2"));
+  EXPECT_EQ(ExtractBiddingSignals(helper_.get(), (*result_or_error)[0].get(),
+                                  {"key1", "key2", "key3"}),
+            R"({"key1":5,"key2":"6","key3":null})");
+
+  EXPECT_EQ((*result_or_error)[7]->GetDataVersion(), 8);
+  EXPECT_FALSE((*result_or_error)[7]->GetPerGroupData("group1"));
+  per_group_data = (*result_or_error)[7]->GetPerGroupData("group2");
+  ASSERT_TRUE(per_group_data);
+  const TrustedSignals::Result::PriorityVector kExpectedPriorityVector2{
+      {"signal1", 9}, {"signal3", 10}};
+  EXPECT_EQ(per_group_data->priority_vector, kExpectedPriorityVector2);
+  EXPECT_EQ(per_group_data->update_if_older_than, base::Milliseconds(11));
+  EXPECT_EQ(ExtractBiddingSignals(helper_.get(), (*result_or_error)[7].get(),
+                                  {"key1", "key2", "key3"}),
+            R"({"key1":12,"key2":null,"key3":[13]})");
+
+  EXPECT_FALSE((*result_or_error)[14]->GetDataVersion());
+  EXPECT_FALSE((*result_or_error)[14]->GetPerGroupData("group1"));
+  EXPECT_FALSE((*result_or_error)[14]->GetPerGroupData("group2"));
+  EXPECT_EQ(ExtractBiddingSignals(helper_.get(), (*result_or_error)[14].get(),
+                                  {"key1", "key2", "key3"}),
+            R"({"key1":null,"key2":null,"key3":null})");
+}
+
 }  // namespace auction_worklet
diff --git a/content/shell/app/shell_main_delegate.cc b/content/shell/app/shell_main_delegate.cc
index a51d1daf..f8bb18b 100644
--- a/content/shell/app/shell_main_delegate.cc
+++ b/content/shell/app/shell_main_delegate.cc
@@ -256,13 +256,6 @@
 }
 
 void ShellMainDelegate::PreSandboxStartup() {
-#if defined(ARCH_CPU_ARM_FAMILY) && \
-    (BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS))
-  // Create an instance of the CPU class to parse /proc/cpuinfo and cache
-  // cpu_brand info.
-  base::CPU cpu_info;
-#endif
-
 // Disable platform crash handling and initialize the crash reporter, if
 // requested.
 // TODO(crbug.com/40188745): Implement crash reporter integration for Fuchsia.
diff --git a/device/fido/enclave/enclave_websocket_client.cc b/device/fido/enclave/enclave_websocket_client.cc
index b3d42475..d9dd03f 100644
--- a/device/fido/enclave/enclave_websocket_client.cc
+++ b/device/fido/enclave/enclave_websocket_client.cc
@@ -5,6 +5,7 @@
 #include "device/fido/enclave/enclave_websocket_client.h"
 
 #include <limits>
+#include <utility>
 
 #include "components/device_event_log/device_event_log.h"
 #include "device/fido/fido_constants.h"
@@ -97,6 +98,7 @@
       data.size() > std::numeric_limits<uint32_t>::max()) {
     FIDO_LOG(ERROR) << "Invalid WebSocket write.";
     ClosePipe(SocketStatus::kError);
+    // `this` may have been deleted at this point.
     return;
   }
 
@@ -110,6 +112,7 @@
   }
 
   InternalWrite(data);
+  // `this` may have been deleted at this point.
 }
 
 void EnclaveWebSocketClient::Connect() {
@@ -154,6 +157,7 @@
   if (result != MOJO_RESULT_OK) {
     FIDO_LOG(ERROR) << "Failed to write to WebSocket.";
     ClosePipe(SocketStatus::kError);
+    // `this` may have been deleted at this point.
   }
 }
 
@@ -170,6 +174,7 @@
                   << net_error << ", " << response_code;
 
   ClosePipe(SocketStatus::kError);
+  // `this` may have been deleted at this point.
 }
 
 void EnclaveWebSocketClient::OnConnectionEstablished(
@@ -204,8 +209,9 @@
   state_ = State::kOpen;
 
   if (pending_write_data_) {
-    InternalWrite(*pending_write_data_);
-    pending_write_data_ = std::nullopt;
+    auto write_data = std::exchange(pending_write_data_, std::nullopt).value();
+    InternalWrite(write_data);
+    // `this` may have been deleted at this point.
   }
 }
 
@@ -221,6 +227,7 @@
   if (data_len == 0) {
     if (finish) {
       ProcessCompletedResponse();
+      // `this` may have been deleted at this point.
     }
     return;
   }
@@ -234,6 +241,7 @@
     FIDO_LOG(ERROR) << "Invalid WebSocket frame (type: "
                     << static_cast<int>(type) << ", len: " << data_len << ")";
     ClosePipe(SocketStatus::kError);
+    // `this` may have been deleted at this point.
     return;
   }
 
@@ -241,6 +249,7 @@
   pending_read_finished_ = finish;
   client_receiver_.Pause();
   ReadFromDataPipe(MOJO_RESULT_OK, mojo::HandleSignalsState());
+  // `this` may have been deleted at this point.
 }
 
 void EnclaveWebSocketClient::OnDropChannel(bool was_clean,
@@ -250,6 +259,7 @@
   CHECK(state_ == State::kOpen || state_ == State::kConnecting);
 
   ClosePipe(SocketStatus::kSocketClosed);
+  // `this` may have been deleted at this point.
 }
 
 void EnclaveWebSocketClient::OnClosingHandshake() {}
@@ -273,6 +283,7 @@
       client_receiver_.Resume();
       if (pending_read_finished_) {
         ProcessCompletedResponse();
+        // `this` may have been deleted at this point.
       }
     }
   } else if (result == MOJO_RESULT_SHOULD_WAIT) {
@@ -281,6 +292,7 @@
     FIDO_LOG(ERROR) << "Reading WebSocket frame failed: "
                     << static_cast<int>(result);
     ClosePipe(SocketStatus::kError);
+    // `this` may have been deleted at this point.
   }
 }
 
@@ -310,6 +322,7 @@
 
 void EnclaveWebSocketClient::OnMojoPipeDisconnect() {
   ClosePipe(SocketStatus::kSocketClosed);
+  // `this` may have been deleted at this point.
 }
 
 }  // namespace device::enclave
diff --git a/device/fido/enclave/enclave_websocket_client.h b/device/fido/enclave/enclave_websocket_client.h
index 8f2c8c24..535f73b 100644
--- a/device/fido/enclave/enclave_websocket_client.h
+++ b/device/fido/enclave/enclave_websocket_client.h
@@ -82,6 +82,10 @@
   };
 
   void Connect();
+
+  // All of the methods below have the potential to invoke the response
+  // callback, which can destroy this object. No data members should be
+  // accessed after calling one.
   void InternalWrite(base::span<const uint8_t> data);
   void ReadFromDataPipe(MojoResult, const mojo::HandleSignalsState&);
   void ProcessCompletedResponse();
diff --git a/extensions/common/BUILD.gn b/extensions/common/BUILD.gn
index 00092ad..f1ee40d 100644
--- a/extensions/common/BUILD.gn
+++ b/extensions/common/BUILD.gn
@@ -512,6 +512,7 @@
     "//components/crash/core/common",
     "//components/crx_file",
     "//components/nacl/common:buildflags",
+    "//components/pdf/common:util",
     "//components/safe_browsing/core/common",
     "//components/safe_browsing/core/common/hashprefix_realtime:hash_realtime_utils",
     "//components/url_formatter",
diff --git a/extensions/common/DEPS b/extensions/common/DEPS
index 5636aa17..cff3228 100644
--- a/extensions/common/DEPS
+++ b/extensions/common/DEPS
@@ -4,6 +4,7 @@
   "+components/url_formatter",
   "+tools/json_schema_compiler",
   "+components/nacl/common/buildflags.h",
+  "+components/pdf/common/pdf_util.h",
   "+components/safe_browsing/core/common",
   "+components/safe_browsing/core/common/hashprefix_realtime",
   "+components/version_info/channel.h",
diff --git a/extensions/common/manifest_handlers/mime_types_handler.cc b/extensions/common/manifest_handlers/mime_types_handler.cc
index 017ff21..53e67d4 100644
--- a/extensions/common/manifest_handlers/mime_types_handler.cc
+++ b/extensions/common/manifest_handlers/mime_types_handler.cc
@@ -13,6 +13,7 @@
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/values.h"
+#include "components/pdf/common/pdf_util.h"
 #include "content/public/common/webplugininfo.h"
 #include "extensions/common/constants.h"
 #include "extensions/common/error_utils.h"
@@ -52,7 +53,6 @@
         static_cast<size_t>(MimeHandlerType::kMaxValue) + 1,
     "MimeHandlerType enum is not in sync with kMIMETypeHandlersAllowlist.");
 
-constexpr SkColor kPdfExtensionBackgroundColor = SkColorSetRGB(82, 86, 89);
 constexpr SkColor kQuickOfficeExtensionBackgroundColor =
     SkColorSetRGB(241, 241, 241);
 
@@ -94,7 +94,7 @@
 
 SkColor MimeTypesHandler::GetBackgroundColor() const {
   if (extension_id_ == extension_misc::kPdfExtensionId) {
-    return kPdfExtensionBackgroundColor;
+    return GetPdfBackgroundColor();
   }
   if (extension_misc::IsQuickOfficeExtension(extension_id_)) {
     return kQuickOfficeExtensionBackgroundColor;
diff --git a/infra/config/generated/luci/cr-buildbucket.cfg b/infra/config/generated/luci/cr-buildbucket.cfg
index 66418b7..52ba49a 100644
--- a/infra/config/generated/luci/cr-buildbucket.cfg
+++ b/infra/config/generated/luci/cr-buildbucket.cfg
@@ -23250,8 +23250,9 @@
     builders {
       name: "Mac deterministic"
       swarming_host: "chromium-swarm.appspot.com"
-      dimensions: "builder:Mac deterministic"
+      dimensions: "builderless:1"
       dimensions: "cpu:x86-64"
+      dimensions: "free_space:standard"
       dimensions: "os:Mac-14"
       dimensions: "pool:luci.chromium.ci"
       exe {
@@ -23351,16 +23352,16 @@
           '    "remote_jobs": 250'
           '  }'
           '}'
-        dimensions: "builder:"
-        dimensions: "builderless:1"
+        dimensions: "free_space:"
         dimensions: "pool:luci.chromium.try"
       }
     }
     builders {
       name: "Mac deterministic (dbg)"
       swarming_host: "chromium-swarm.appspot.com"
-      dimensions: "builder:Mac deterministic (dbg)"
+      dimensions: "builderless:1"
       dimensions: "cpu:x86-64"
+      dimensions: "free_space:standard"
       dimensions: "os:Mac-14"
       dimensions: "pool:luci.chromium.ci"
       exe {
@@ -23460,8 +23461,7 @@
           '    "remote_jobs": 250'
           '  }'
           '}'
-        dimensions: "builder:"
-        dimensions: "builderless:1"
+        dimensions: "free_space:"
         dimensions: "pool:luci.chromium.try"
       }
     }
diff --git a/infra/config/generated/testing/mixins.pyl b/infra/config/generated/testing/mixins.pyl
index c65f18ed..0bc8277 100644
--- a/infra/config/generated/testing/mixins.pyl
+++ b/infra/config/generated/testing/mixins.pyl
@@ -106,6 +106,16 @@
       },
     },
   },
+  'chrome-flame-fleet-pool': {
+    'swarming': {
+      'dimensions': {
+        'device_type': 'flame',
+        'device_os': 'R',
+        'pool': 'chrome.tests',
+        'os': 'Android',
+      },
+    },
+  },
   'chrome-intelligence-swarming-pool': {
     'swarming': {
       'dimensions': {
diff --git a/infra/config/migration/README.md b/infra/config/migration/README.md
new file mode 100644
index 0000000..a7534378c
--- /dev/null
+++ b/infra/config/migration/README.md
@@ -0,0 +1,113 @@
+# Tools for migrating to starlark
+
+This directory contains tools for migrating configuration to starlark from other
+locations.
+
+## Migrating tests to starlark
+
+Make sure to [install
+buildozer](https://github.com/bazelbuild/buildtools/blob/main/buildozer/README.md#installation)
+and that it is on your path.
+
+Commands assume the working directory is //infra/config.
+
+1. Execute //infra/config/migration/migrate-targets.py.
+
+    ```sh
+    ./migration/migrate-targets.py $BUILDER_GROUP
+    ```
+
+    This will modify the starlark files so that it will generate per-builder
+    targets spec files for all builders in the builder group that have their tests
+    set in //testing/buildbot/waterfalls.pyl.
+
+    To migrate a subset of builders from a builder group, instead run
+
+    ```sh
+    ./migration/migrate-targets.py $BUILDER_GROUP $BUILDER1 $BUILDER2 ... $BUILDER_N
+    ```
+
+    When migrating an entire builder group, it might be necessary to do this to
+    migrate some builders in another builder group since all builders that are
+    related via parent-child triggering or try-builder mirroring must be migrated
+    in one CL.
+
+1. Regenerate the config files from starlark.
+
+    ```sh
+    ./main.star
+    ```
+
+    This should run without error and produce .json files for the migrated
+    builders.
+
+1. Some manual modifications may be necessary to the starlark files:
+
+    * buildozer reformats the entire file, some of this is undesirable.
+    * Top-level function calls added by the script should be reformatted so that
+      each argument is on its own line (it's not possible to force this with
+      buildozer).
+    * Comments associated with the entries in test_suite_exceptions.pyl and
+      waterfalls.pyl should be transferred to the newly added starlark
+      declarations
+    * Tests that are removed require a reason specified. The script puts in a
+      reason string that will prevent the change from being submitted. This
+      allows for confirming the generated files, without risking an undocumented
+      removal. This may overlap with the previous bullet point since comments on
+      the removal may serve as an adequate value for the reason field.
+
+1. Remove the entries for all migrated builders from
+  //testing/buildbot/waterfalls.pyl and
+  //testing/buildbot/test_suite_exceptions.pyl. In waterfalls.pyl, make sure to
+  add breadcrumb comments to indicate the builders that are migrated at the top
+  of the builder group. If the entire builder group is migrated, add the
+  breadcrumb comment for the entire builder group near the top of the file.
+
+1. If any builder group was fully migrated, manually remove the corresponding
+  .json file from //testing/buildbot.
+
+1. Regenerate .json files in //testing/buildbot that have had builders removed
+  by running
+
+    ```sh
+    ../../testing/buildbot/generate_buildbot_json.py
+    ```
+
+1. Test suites and mixins that are no longer referenced by
+  //testing/buildbot/waterfalls.pyl require additional updates to the starlark.
+  Suites need to be converted to bundles and mixins need to set
+  `generate_pyl_entry=False`.
+
+    To find the necessary updates, run
+
+    ```sh
+    ../../testing/buildbot/generate_buildbot_json.py --check
+    ```
+
+    Unused mixins will only be reported when there are no unused test suites, so
+    you should run it until it produces no output.
+
+1. Binaries that are no longer referenced by any builder in
+  //testing/waterfalls.pyl should be added to the exclude list in
+  //testing/buildbot/check.py. These binaries can be found by running
+
+    ```sh
+    ../../testing/buildbot/check.py
+    ```
+
+## buildozer
+
+The migration scripts make use of buildozer, which is a tool for making edits to
+starlark files. It's geared towards modifying BUILD files but can be used for
+other types of starlark files with some care. See installation
+[instructions](https://github.com/bazelbuild/buildtools/blob/main/buildozer/README.md#installation)
+to get buildozer.
+
+Ideally all edits of a script could be done in a single invocation of buildozer
+by using the -f flag to pass commands in a file, but command files don't
+currently support values with escaped newlines which are necessary in order to
+force values onto multiple lines when buildozer would normally put them on a
+single line: calls with one argument, dicts with one item or lists with one
+element. There is a [pull
+request](https://github.com/bazelbuild/buildtools/pull/1296) to enable this
+functionality waiting for review.
diff --git a/infra/config/migration/migrate-targets.py b/infra/config/migration/migrate-targets.py
new file mode 100755
index 0000000..e7d6b131
--- /dev/null
+++ b/infra/config/migration/migrate-targets.py
@@ -0,0 +1,336 @@
+#!/usr/bin/env python3
+# Copyright 2024 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+"""Migrate tests for a builder from //testing/buildbot to starlark.
+
+buildozer must be installed on the system. To get the desired output from
+buildozer, we must use a custom fork (belonging to gbeaty@google.com) that
+allows for newlines to be specified in the values when using a command file. Run
+the following commands:
+
+git clone https://github.com/kleerwater/buildtools.git
+cd buildtools/buildozer
+git checkout origin/command-file-newlines
+go install
+
+There is a pull request to get the capability into the upstream repository:
+https://github.com/bazelbuild/buildtools/pull/1296
+"""
+
+import argparse
+import ast
+import pathlib
+import subprocess
+import sys
+import typing
+
+import value_builders
+
+_THIS_DIR = pathlib.Path(__file__).parent
+_INFRA_CONFIG_DIR = _THIS_DIR.parent
+_TESTING_BUILDBOT_DIR = (_INFRA_CONFIG_DIR / '../../testing/buildbot').resolve()
+
+_BROWSER_CONFIG_MAPPING = {
+    'android-chromium': 'ANDROID_CHROMIUM',
+    'android-chromium-monochrome': 'ANDROID_CHROMIUM_MONOCHROME',
+    'android-webview': 'ANDROID_WEBVIEW',
+    'cros-chrome': 'CROS_CHROME',
+    'debug': 'DEBUG',
+    'debug_x64': 'DEBUG_X64',
+    'lacros-chrome': 'LACROS_CHROME',
+    'release': 'RELEASE',
+    'release_x64': 'RELEASE_X64',
+    'web-engine-shell': 'WEB_ENGINE_SHELL',
+}
+
+_OS_TYPE_MAPPING = {
+    'android': 'ANDROID',
+    'chromeos': 'CROS',
+    'fuchsia': 'FUCHSIA',
+    'lacros': 'LACROS',
+    'linux': 'LINUX',
+    'mac': 'MAC',
+    'win': 'WINDOWS',
+}
+
+
+def _swarming(swarming: dict[str, typing.Any]) -> value_builders.ValueBuilder:
+  value_builder = value_builders.CallValueBuilder('targets.swarming')
+
+  for key, value in swarming.items():
+    match key:
+      case 'dimensions':
+        dimensions_builder = value_builders.DictValueBuilder()
+        for k, v in value.items():
+          dimensions_builder[k] = f'"{v}"'
+        value_builder['dimensions'] = dimensions_builder
+
+      case 'shards':
+        value_builder['shards'] = value
+
+      case _:
+        raise Exception(f'unhandled key in swarming: "{key}"')
+
+  return value_builder
+
+
+def _per_test_modifications(
+    builder: str,
+    test_suite_exceptions: dict[str, typing.Any],
+) -> value_builders.ValueBuilder:
+  value_builder = value_builders.DictValueBuilder()
+
+  for test_name, exceptions in test_suite_exceptions.items():
+    if builder in exceptions.get('remove_from', []):
+      value_builder[test_name] = value_builders.CallValueBuilder(
+          'targets.remove',
+          {
+              # Break up the string so that it doesn't get flagged by the
+              # presubmit check but the generated code will if not updated
+              'reason': f'"DO{""} NOT SUBMIT provide an actual reason"',
+          })
+
+    elif modifications := exceptions.get('modifications', {}).get(builder):
+      mixin_builder = value_builders.CallValueBuilder('targets.mixin')
+      value_builder[test_name] = mixin_builder
+
+      for key, value in modifications.items():
+        match key:
+          case 'args':
+            mixin_builder['args'] = value_builders.ListValueBuilder(
+                [f'"{arg}"' for arg in value])
+
+          case 'ci_only' | 'experiment_percentage':
+            mixin_builder[key] = value
+
+          case 'swarming':
+            mixin_builder['swarming'] = _swarming(value)
+
+          case _:
+            raise Exception(f'unhandled key in modifications: "{key}"')
+
+  return value_builder
+
+
+def _escape_spaces(s: str) -> str:
+  return s.replace(' ', '\\ ').replace('\n', '\\\n')
+
+
+# Override the buildozer tables with empty tanles to avoid buildozer making
+# unintended changes such as sorting most lists, including in existing portions
+# of the file
+_TABLE_JSON_FILE = _THIS_DIR / 'tables.json'
+_CMD_PREFIX = ['buildozer', '-tables', _TABLE_JSON_FILE]
+
+
+def _update_starlark(
+    builder_group: str,
+    star_file: pathlib.Path,
+    targets_builder_defaults: dict[str, str],
+    edits_by_builder: dict[str, dict[str, str]],
+):
+
+  # Ideally all modifications would be done with a single buildozer invocation
+  # using a commands file, but escaped newlines aren't supported with commands
+  # files.
+  #
+  # Pull request to fix: https://github.com/bazelbuild/buildtools/pull/1296
+  def buildoze(*args):
+    # output is always captured to avoid having each edit trigger a repetitive
+    # line of output
+    ret = subprocess.run([*_CMD_PREFIX, *args],
+                         capture_output=True,
+                         encoding='utf-8')
+    # buildozer returns exit code of 3 when it makes no changes, the edits
+    # should be idempotent, so we have to manually check the return code
+    if ret.returncode not in (0, 3):
+      sys.stderr.write(ret.stderr)
+      ret.check_returncode()
+    return ret.stdout
+
+  # Buildozer is geared towards manipulating build targets, actions that operate
+  # on the file level, such as adding a new rule, use __pkg__ as the target name
+  file_target = f'{star_file}:__pkg__'
+
+  buildoze('new_load //lib/targets.star targets', file_target)
+
+  defaults_rule_kind = 'targets.builder_defaults.set'
+  # %{kind} as the pattern tells it to operate on all rules of that kind. There
+  # shouldn't be more than one targets.builder_defaults.set "rule".
+  defaults_target = f'{star_file}:%{defaults_rule_kind}'
+  # Check if a targets.builder_defaults.set declaration already exists, any
+  # print operation will result in output if there is already a rule
+  if not subprocess.check_output([*_CMD_PREFIX, 'print kind', defaults_target]):
+    # It's not possible to add an arbitrary function call, only new rules, which
+    # require a name, so create a rule with a temporary name and then remove the
+    # name attribute, then we can just use the kind filter for modifying it
+    temp_name = 'NO_DECLARATION_SHOULD_EXIST_WITH_THIS_NAME'
+    buildoze(f'new {defaults_rule_kind} {temp_name} before {builder_group}',
+             file_target)
+    buildoze('remove name', f'{star_file}:{temp_name}')
+
+  for attr, value in targets_builder_defaults.items():
+    buildoze(f'set {attr} {_escape_spaces(value)}', defaults_target)
+
+  for builder, edits in edits_by_builder.items():
+    for attr, value in edits.items():
+      buildoze(f'set {attr} {_escape_spaces(value)}', f'{star_file}:{builder}')
+
+
+class SkylabSuite(Exception):
+
+  def __init__(self, suite_type: str, suite: str):
+    return super().__init__(f'skylab suite "{suite}" with'
+                            f' suite_type "{suite_type}" is not supported')
+
+
+def _compute_edits(
+    builder: str,
+    builder_config: dict[str, typing.Any],
+    test_suite_exceptions: dict[str, typing.Any],
+) -> dict[str, str] | None:
+  anonymous_mixin_builder = value_builders.CallValueBuilder('targets.mixin')
+  mixins_builder = value_builders.ListValueBuilder([anonymous_mixin_builder])
+  bundle_builder = value_builders.CallValueBuilder('targets.bundle',
+                                                   {'mixins': mixins_builder},
+                                                   output_empty=True)
+  settings_builder = value_builders.CallValueBuilder('targets.settings')
+
+  for key, value in builder_config.items():
+    match key:
+      case 'test_suites':
+        targets_builder = value_builders.ListValueBuilder()
+        bundle_builder['targets'] = targets_builder
+
+        for suite_type, suite in value.items():
+          match suite_type:
+            case ('android_webview_gpu_telemetry_tests'
+                  | 'cast_streaming_tests' | 'gpu_telemetry_tests'
+                  | 'gtest_tests' | 'isolated_scripts' | 'scripts'):
+              targets_builder.append(f'"{suite}"')
+
+            case 'skylab_tests' | 'skylab_gpu_telemetry_tests':
+              raise SkylabSuite(suite_type, suite)
+
+            case 'junit_tests' | _:
+              raise Exception(f'unhandled suite type: "{suite}"')
+
+      case 'additional_compile_targets':
+        bundle_builder[key] = value_builders.ListValueBuilder(
+            [f'"{element}"' for element in value])
+
+      case 'args':
+        anonymous_mixin_builder['args'] = value_builders.ListValueBuilder(
+            [f'"{arg}"' for arg in value])
+
+      case 'mixins':
+        for element in value:
+          mixins_builder.append(f'"{element}"')
+
+      case 'browser_config':
+        browser_config = _BROWSER_CONFIG_MAPPING[value]
+        settings_builder[key] = f'targets.browser_config.{browser_config}'
+
+      case 'os_type':
+        settings_builder[key] = f'targets.os_type.{_OS_TYPE_MAPPING[value]}'
+
+      case 'skip_merge_script':
+        if value:
+          settings_builder['use_android_merge_script_by_default'] = False
+
+      case _:
+        raise Exception(f'unhandled key in builder config: "{key}"')
+
+  bundle_builder['per_test_modifications'] = (_per_test_modifications(
+      builder, test_suite_exceptions))
+
+  edits = {'targets': ''.join(bundle_builder.output())}
+  if (settings_output := settings_builder.output()) is not None:
+    edits['targets_settings'] = ''.join(settings_output)
+
+  return edits
+
+
+def main(argv: list[str]):
+  parser = argparse.ArgumentParser()
+  parser.add_argument('builder_group')
+  parser.add_argument('builder', nargs='*', default=None)
+  args = parser.parse_args(argv)
+
+  builders = set(args.builder) or None
+
+  with open(_TESTING_BUILDBOT_DIR / 'waterfalls.pyl', encoding='utf-8') as f:
+    waterfalls = ast.literal_eval(f.read())
+  for waterfall in waterfalls:
+    if waterfall['name'] == args.builder_group:
+      break
+  else:
+    print(f'builder_group "{args.builder_group}" not found', file=sys.stderr)
+    sys.exit(1)
+
+  with open(
+      _TESTING_BUILDBOT_DIR / 'test_suite_exceptions.pyl',
+      encoding='utf-8',
+  ) as f:
+    test_suite_exceptions = ast.literal_eval(f.read())
+
+  targets_builder_defaults = {}
+  edits_by_builder = {}
+  for key, value in waterfall.items():
+    match key:
+      case 'project' | 'bucket' | 'name':
+        pass
+
+      case 'mixins':
+        mixins_default_builder = value_builders.ListValueBuilder(
+            [f'"{m}"' for m in value])
+        targets_builder_defaults['mixins'] = mixins_default_builder.output()
+
+      case 'machines':
+        for builder_name, builder_config in value.items():
+          if builders is not None and builder_name not in builders:
+            continue
+
+          edits_by_builder[builder_name] = _compute_edits(
+              builder_name,
+              builder_config,
+              test_suite_exceptions,
+          )
+
+          if builders is not None:
+            builders.remove(builder_name)
+            if not builders:
+              break
+
+      case 'forbid_script_tests':
+        # TODO: crbug.com/40258588: Implement support for forbid_script_test in
+        # starlark and handle this
+        pass
+
+      case _:
+        raise Exception(f'unhandled key in waterfall: "{key}"')
+
+  if builders:
+    builder_message = ', '.join(f'"{b}"' for b in sorted(builders))
+    print(("the following builders don't exist in builder group "
+           f'"{args.builder_group}": {builder_message}'),
+          file=sys.stderr)
+    sys.exit(1)
+
+  bucket = 'try' if args.builder_group.startswith('tryserver.') else 'ci'
+  star_file = (_INFRA_CONFIG_DIR /
+               f'subprojects/chromium/{bucket}/{args.builder_group}.star')
+
+  _update_starlark(
+      args.builder_group,
+      star_file,
+      targets_builder_defaults,
+      edits_by_builder,
+  )
+
+  subprocess.check_call(['lucicfg', 'fmt'], cwd=_INFRA_CONFIG_DIR)
+
+
+if __name__ == '__main__':
+  main(sys.argv[1:])
diff --git a/infra/config/migration/tables.json b/infra/config/migration/tables.json
new file mode 100644
index 0000000..9e26dfe
--- /dev/null
+++ b/infra/config/migration/tables.json
@@ -0,0 +1 @@
+{}
\ No newline at end of file
diff --git a/infra/config/subprojects/chromium/ci/chromium.fyi.star b/infra/config/subprojects/chromium/ci/chromium.fyi.star
index baacc6f33..32aaf0a0 100644
--- a/infra/config/subprojects/chromium/ci/chromium.fyi.star
+++ b/infra/config/subprojects/chromium/ci/chromium.fyi.star
@@ -1967,6 +1967,7 @@
             "x64",
         ],
     ),
+    builderless = True,
     cores = None,
     console_view_entry = consoles.console_view_entry(
         category = "deterministic|mac",
@@ -1986,6 +1987,7 @@
             "x64",
         ],
     ),
+    builderless = True,
     cores = None,
     os = os.MAC_DEFAULT,
     console_view_entry = consoles.console_view_entry(
diff --git a/infra/config/targets/mixins.star b/infra/config/targets/mixins.star
index 68b32bf..50796c1 100644
--- a/infra/config/targets/mixins.star
+++ b/infra/config/targets/mixins.star
@@ -371,6 +371,18 @@
 )
 
 targets.mixin(
+    name = "chrome-flame-fleet-pool",
+    swarming = targets.swarming(
+        dimensions = {
+            "device_type": "flame",
+            "device_os": "R",
+            "pool": "chrome.tests",
+            "os": "Android",
+        },
+    ),
+)
+
+targets.mixin(
     name = "chrome-intelligence-swarming-pool",
     swarming = targets.swarming(
         dimensions = {
diff --git a/internal b/internal
index 81fbe9c..ecf5106 160000
--- a/internal
+++ b/internal
@@ -1 +1 @@
-Subproject commit 81fbe9c25bdaf47289eb8fdd6a0405ccd372b82e
+Subproject commit ecf5106d74f447df12fc97a4e374e05d005942ca
diff --git a/ios/chrome/browser/ui/settings/search_engine_table_view_controller_non_eea_unittest.mm b/ios/chrome/browser/ui/settings/search_engine_table_view_controller_non_eea_unittest.mm
index 2934c8c..c5f76bf 100644
--- a/ios/chrome/browser/ui/settings/search_engine_table_view_controller_non_eea_unittest.mm
+++ b/ios/chrome/browser/ui/settings/search_engine_table_view_controller_non_eea_unittest.mm
@@ -148,8 +148,7 @@
   // literal symbol(e.g. "google" or "AOL") from
   // "components/search_engines/prepopulated_engines.h" since it's a generated
   // file.
-  std::vector<const PrepopulatedEngine*> prepopulated_engines =
-      GetAllPrepopulatedEngines();
+  const auto prepopulated_engines = GetAllPrepopulatedEngines();
   ASSERT_LE(2UL, prepopulated_engines.size());
 
   TemplateURL* url_p1 =
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_egtest.mm b/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_egtest.mm
index ef8c3e3..57b1221 100644
--- a/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_egtest.mm
+++ b/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_egtest.mm
@@ -728,7 +728,8 @@
 }
 
 // Tests that the user interface style is respected after a drag and drop.
-- (void)testTraitCollection {
+// TODO(crbug.com/368385383): Test flaky on iOS.
+- (void)DISABLED_testTraitCollection {
   [ChromeEarlGrey loadURL:_URL1];
   [ChromeEarlGrey waitForWebStateContainingText:kResponse1];
   [ChromeEarlGrey openNewTab];
@@ -755,10 +756,8 @@
       [[GREYElementMatcherBlock alloc] initWithMatchesBlock:match
                                            descriptionBlock:describe];
 
-  [[EarlGrey
-      selectElementWithMatcher:grey_allOf(grey_accessibilityID(
-                                              IdentifierForCellAtIndex(0)),
-                                          grey_sufficientlyVisible(), nil)]
+  [[EarlGrey selectElementWithMatcher:grey_accessibilityID(
+                                          IdentifierForCellAtIndex(0))]
       assertWithMatcher:matcher];
 }
 
diff --git a/ios/components/security_interstitials/safe_browsing/safe_browsing_query_manager_unittest.mm b/ios/components/security_interstitials/safe_browsing/safe_browsing_query_manager_unittest.mm
index 47252a9..e55c49b 100644
--- a/ios/components/security_interstitials/safe_browsing/safe_browsing_query_manager_unittest.mm
+++ b/ios/components/security_interstitials/safe_browsing/safe_browsing_query_manager_unittest.mm
@@ -370,7 +370,24 @@
       const SafeBrowsingQueryManager::Result& result,
       safe_browsing::SafeBrowsingUrlCheckerImpl::PerformedCheck performed_check)
       override {
+    ResetWebState();
+  }
+
+  void SafeBrowsingSyncQueryFinished(
+      const SafeBrowsingQueryManager::QueryData& query_data) override {
+    if (!reset_with_async_check_) {
+      ResetWebState();
+    }
+  }
+
+  void SafeBrowsingAsyncQueryFinished(
+      const SafeBrowsingQueryManager::QueryData& query_data) override {
+    ResetWebState();
+  }
+
+  void ResetWebState() {
     web_state_.reset();
+    was_observer_removed_ = true;
   }
 
   void SafeBrowsingQueryManagerDestroyed(
@@ -378,19 +395,30 @@
     manager->RemoveObserver(this);
   }
 
+  void EnsureWebStateResetsWithAsyncCheck() { reset_with_async_check_ = true; }
+
   web::WebState* web_state() { return web_state_.get(); }
 
+  bool was_observer_removed() { return was_observer_removed_; }
+
  private:
   std::unique_ptr<web::FakeBrowserState> browser_state_;
   std::unique_ptr<web::FakeWebState> web_state_;
+  bool was_observer_removed_ = false;
+  bool reset_with_async_check_ = false;
 };
 }  // namespace
 
 // Test fixture for testing WebState destruction during a
 // SafeBrowsingQueryManager::Observer callback.
-class SafeBrowsingQueryManagerWebStateDestructionTest : public PlatformTest {
+class SafeBrowsingQueryManagerWebStateDestructionTest
+    : public testing::TestWithParam<bool> {
  protected:
-  SafeBrowsingQueryManagerWebStateDestructionTest() : http_method_("GET") {
+  SafeBrowsingQueryManagerWebStateDestructionTest()
+      : http_method_("GET"), use_async_safe_browsing_(GetParam()) {
+    scoped_feature_list_.InitWithFeatureState(
+        safe_browsing::kSafeBrowsingAsyncRealTimeCheck,
+        use_async_safe_browsing_);
     SafeBrowsingQueryManager::CreateForWebState(observer_.web_state(),
                                                 &client_);
     SafeBrowsingUrlAllowList::CreateForWebState(observer_.web_state());
@@ -401,24 +429,49 @@
     return SafeBrowsingQueryManager::FromWebState(observer_.web_state());
   }
 
+  // Helper function to run all sync callbacks first then async callbacks.
+  void RunSyncCallbacksThenAsyncCallbacks() {
+    if (base::FeatureList::IsEnabled(
+            safe_browsing::kSafeBrowsingAsyncRealTimeCheck)) {
+      base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
+          FROM_HERE, base::BindOnce(^() {
+            client_.run_sync_callbacks();
+          }));
+      base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
+          FROM_HERE, base::BindOnce(^() {
+            client_.run_async_callbacks();
+          }));
+    }
+
+    // TODO(crbug.com/359420122): Remove when clean up is complete.
+    base::RunLoop().RunUntilIdle();
+  }
+
   web::WebTaskEnvironment task_environment_{
       web::WebTaskEnvironment::MainThreadType::IO};
   WebStateDestroyingQueryManagerObserver observer_;
   std::string http_method_;
   FakeSafeBrowsingClient client_;
+  bool use_async_safe_browsing_;
+  base::test::ScopedFeatureList scoped_feature_list_;
 };
 
+INSTANTIATE_TEST_SUITE_P(/* No Instantiation Name */,
+                         SafeBrowsingQueryManagerWebStateDestructionTest,
+                         testing::Bool());
+
 // Tests that a query for a safe URL doesn't cause a crash.
-TEST_F(SafeBrowsingQueryManagerWebStateDestructionTest, SafeURLQuery) {
+TEST_P(SafeBrowsingQueryManagerWebStateDestructionTest, SafeURLQuery) {
   GURL url("http://chromium.test");
   // Start a URL check query for the safe URL and run the runloop until the
   // result is received.
   manager()->StartQuery(SafeBrowsingQueryManager::Query(url, http_method_));
-  base::RunLoop().RunUntilIdle();
+  RunSyncCallbacksThenAsyncCallbacks();
+  EXPECT_TRUE(observer_.was_observer_removed());
 }
 
 // Tests that a query for an unsafe URL doesn't cause a crash.
-TEST_F(SafeBrowsingQueryManagerWebStateDestructionTest, UnsafeURLQuery) {
+TEST_P(SafeBrowsingQueryManagerWebStateDestructionTest, UnsafeURLQuery) {
   GURL url("http://" + FakeSafeBrowsingService::kUnsafeHost);
 
   // Start a URL check query for the unsafe URL and run the runloop until the
diff --git a/ios_internal b/ios_internal
index 2859786..f17fd6e 160000
--- a/ios_internal
+++ b/ios_internal
@@ -1 +1 @@
-Subproject commit 2859786c197bdbcd36e02c749007b8b55801e48a
+Subproject commit f17fd6e8cd1a625d259c96d0dfcfdd41b0687ef1
diff --git a/media/formats/mp2t/descriptors.cc b/media/formats/mp2t/descriptors.cc
index 6c2eb9c..0a06fc93 100644
--- a/media/formats/mp2t/descriptors.cc
+++ b/media/formats/mp2t/descriptors.cc
@@ -112,6 +112,9 @@
   RCHECK(reader.SkipBits(3));
   RCHECK(reader.ReadBits(13, pid));
   size_t extra_bits = reader.bits_available();
+  if (extra_bits == 0) {
+    return true;
+  }
   RCHECK(extra_bits % 8 == 0);
   RCHECK(reader.ReadString(extra_bits, private_data));
   return true;
diff --git a/media/muxers/OWNERS b/media/muxers/OWNERS
index ded4b7e..18c8a02 100644
--- a/media/muxers/OWNERS
+++ b/media/muxers/OWNERS
@@ -1,2 +1,4 @@
+handellm@google.com
+
 # Original (legacy) owner.
 mcasas@chromium.org
diff --git a/media/muxers/README.md b/media/muxers/README.md
new file mode 100644
index 0000000..eff00a0
--- /dev/null
+++ b/media/muxers/README.md
@@ -0,0 +1,22 @@
+# //media/muxers
+
+This directory contains code for muxing mkv (webm) and mp4 files.
+ * https://en.wikipedia.org/wiki/Matroska
+ * https://en.wikipedia.org/wiki/WebM
+ * https://en.wikipedia.org/wiki/MP4_file_format
+
+Muxers are based off the `media::Muxer` interface. These muxers are
+primarily used by MediaRecorder and ChromeOS video recording.
+
+Since audio and video streams often come from sources with different
+clocks, timestamp alignment can be handled by `MuxerTimestampAdapter`.
+More details on synchronization can be found in the
+[Media Capture and Streams](https://www.w3.org/TR/mediacapture-streams/#introduction)
+spec.
+
+Currently the following codecs are supported in each muxer:
+ * mkv / webm: VP8, VP9, AV1, H.264 for video plus Opus for audio.
+ * mp4: VP9, AV1, H.264 for video and Opus, AAC for audio.
+
+Note: webm is a subset of mkv that technically only supports vp8, vp9,
+and av1 codecs.
diff --git a/remoting/base/cloud_service_client.cc b/remoting/base/cloud_service_client.cc
index 30310cf..a5d9e8d 100644
--- a/remoting/base/cloud_service_client.cc
+++ b/remoting/base/cloud_service_client.cc
@@ -345,12 +345,15 @@
     host->set_version(*host_version);
   }
   if (signaling_id.has_value()) {
-    auto parts = base::SplitStringOnce(*signaling_id, kFtlResourceSeparator);
-    if (parts) {
-      host->mutable_tachyon_account_info()->set_account_id(
-          std::string(parts->first));
+    auto parts = base::SplitStringUsingSubstr(
+        *signaling_id, kFtlResourceSeparator, base::TRIM_WHITESPACE,
+        base::SPLIT_WANT_ALL);
+    if (parts.size() == 2) {
+      host->mutable_tachyon_account_info()->set_account_id(std::move(parts[0]));
       host->mutable_tachyon_account_info()->set_registration_id(
-          std::string(parts->second));
+          std::move(parts[1]));
+    } else {
+      LOG(WARNING) << "Invalid signaling_id provided: " << *signaling_id;
     }
   }
   if (offline_reason.has_value()) {
diff --git a/remoting/host/heartbeat_sender.cc b/remoting/host/heartbeat_sender.cc
index 6402f74..383eebd 100644
--- a/remoting/host/heartbeat_sender.cc
+++ b/remoting/host/heartbeat_sender.cc
@@ -208,13 +208,14 @@
     return;
   }
 
-  VLOG(1) << "About to send full heartbeat.";
+  HOST_LOG << "Sending full heartbeat.";
 
   ClearHeartbeatTimer();
 
   std::optional<std::string> offline_reason;
   if (!host_offline_reason_.empty()) {
     offline_reason = host_offline_reason_;
+    HOST_LOG << "Sending offline reason: " << host_offline_reason_;
   }
   std::optional<std::string> signaling_id;
   auto signaling_id_str = signal_strategy_->GetLocalAddress().id();
@@ -239,7 +240,7 @@
     return;
   }
 
-  VLOG(1) << "About to send lite heartbeat.";
+  HOST_LOG << "Sending heartbeat.";
 
   ClearHeartbeatTimer();
 
diff --git a/remoting/host/me2me_heartbeat_service_client.cc b/remoting/host/me2me_heartbeat_service_client.cc
index fb668f4..4869661 100644
--- a/remoting/host/me2me_heartbeat_service_client.cc
+++ b/remoting/host/me2me_heartbeat_service_client.cc
@@ -33,10 +33,6 @@
     HeartbeatResponseCallback callback) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
-  std::string host_offline_reason =
-      offline_reason ? (" host_offline_reason: " + *offline_reason) : "";
-  HOST_LOG << "Sending outgoing legacy heartbeat." << host_offline_reason;
-
   std::string os_name = GetHostOperatingSystemName();
   std::string os_version = GetHostOperatingSystemVersion();
 
diff --git a/sandbox/policy/features.cc b/sandbox/policy/features.cc
index fabda3d..0a6c9b2 100644
--- a/sandbox/policy/features.cc
+++ b/sandbox/policy/features.cc
@@ -70,13 +70,6 @@
              "RendererAppContainer",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
-// Enables very high job memory limits for sandboxed renderer processes. This
-// sets a limit of 1Tb, effectively removing the Job memory limits, except in
-// egregious cases.
-BASE_FEATURE(kWinSboxHighRendererJobMemoryLimits,
-             "WinSboxHighRendererJobMemoryLimits",
-             base::FEATURE_ENABLED_BY_DEFAULT);
-
 // If enabled, launch the network service within an LPAC sandbox. If disabled,
 // the network service will run inside an App Container.
 BASE_FEATURE(kWinSboxNetworkServiceSandboxIsLPAC,
diff --git a/sandbox/policy/features.h b/sandbox/policy/features.h
index 1d801ca..4896604 100644
--- a/sandbox/policy/features.h
+++ b/sandbox/policy/features.h
@@ -31,7 +31,6 @@
 SANDBOX_POLICY_EXPORT BASE_DECLARE_FEATURE(kGpuLPAC);
 SANDBOX_POLICY_EXPORT BASE_DECLARE_FEATURE(kPrintCompositorLPAC);
 SANDBOX_POLICY_EXPORT BASE_DECLARE_FEATURE(kRendererAppContainer);
-SANDBOX_POLICY_EXPORT BASE_DECLARE_FEATURE(kWinSboxHighRendererJobMemoryLimits);
 SANDBOX_POLICY_EXPORT BASE_DECLARE_FEATURE(kWinSboxNetworkServiceSandboxIsLPAC);
 SANDBOX_POLICY_EXPORT BASE_DECLARE_FEATURE(kWinSboxForceRendererCodeIntegrity);
 SANDBOX_POLICY_EXPORT BASE_DECLARE_FEATURE(kWinSboxZeroAppShim);
diff --git a/sandbox/policy/win/sandbox_win.cc b/sandbox/policy/win/sandbox_win.cc
index 30d2877..00525a5 100644
--- a/sandbox/policy/win/sandbox_win.cc
+++ b/sandbox/policy/win/sandbox_win.cc
@@ -1081,12 +1081,6 @@
 
 // static
 std::optional<size_t> SandboxWin::GetJobMemoryLimit(Sandbox sandbox_type) {
-  // Trigger feature list initialization here to ensure no population bias in
-  // the experimental and control groups.
-  [[maybe_unused]] const bool high_renderer_limits =
-      base::FeatureList::IsEnabled(
-          sandbox::policy::features::kWinSboxHighRendererJobMemoryLimits);
-
 #if defined(ARCH_CPU_64_BITS)
   size_t memory_limit = static_cast<size_t>(kDataSizeLimit);
 
@@ -1109,7 +1103,7 @@
       memory_limit = 8 * GB;
     }
 
-    if (sandbox_type == Sandbox::kRenderer && high_renderer_limits) {
+    if (sandbox_type == Sandbox::kRenderer) {
       // Set limit to 1Tb.
       memory_limit = 1024 * GB;
     }
diff --git a/sandbox/policy/win/sandbox_win_unittest.cc b/sandbox/policy/win/sandbox_win_unittest.cc
index c469e57..aa496fa 100644
--- a/sandbox/policy/win/sandbox_win_unittest.cc
+++ b/sandbox/policy/win/sandbox_win_unittest.cc
@@ -547,36 +547,9 @@
     EXPECT_EQ(memory_limit, 8 * kGB);
   }
 
-  // Test Renderer with physical memory > 16GB
-  {
-    base::test::ScopedAmountOfPhysicalMemoryOverride memory_override(k17GB);
-    base::test::ScopedFeatureList scoped_feature_list;
-    scoped_feature_list.InitAndDisableFeature(
-        sandbox::policy::features::kWinSboxHighRendererJobMemoryLimits);
-    std::optional<size_t> memory_limit =
-        SandboxWin::GetJobMemoryLimit(sandbox::mojom::Sandbox::kRenderer);
-    EXPECT_TRUE(memory_limit.has_value());
-    EXPECT_EQ(memory_limit, 16 * kGB);
-  }
-
-  // Test Renderer with physical memory < 16GB
+  // Test that Renderer has high (1TB) memory limit.
   {
     base::test::ScopedAmountOfPhysicalMemoryOverride memory_override(k8GB);
-    base::test::ScopedFeatureList scoped_feature_list;
-    scoped_feature_list.InitAndDisableFeature(
-        sandbox::policy::features::kWinSboxHighRendererJobMemoryLimits);
-    std::optional<size_t> memory_limit =
-        SandboxWin::GetJobMemoryLimit(sandbox::mojom::Sandbox::kRenderer);
-    EXPECT_TRUE(memory_limit.has_value());
-    EXPECT_EQ(memory_limit, 8 * kGB);
-  }
-
-  // Test Renderer with high renderer limits enabled.
-  {
-    base::test::ScopedAmountOfPhysicalMemoryOverride memory_override(k8GB);
-    base::test::ScopedFeatureList scoped_feature_list;
-    scoped_feature_list.InitAndEnableFeature(
-        sandbox::policy::features::kWinSboxHighRendererJobMemoryLimits);
     std::optional<size_t> memory_limit =
         SandboxWin::GetJobMemoryLimit(sandbox::mojom::Sandbox::kRenderer);
     EXPECT_TRUE(memory_limit.has_value());
diff --git a/testing/buildbot/generate_buildbot_json.py b/testing/buildbot/generate_buildbot_json.py
index 9f8a09a..71eedee 100755
--- a/testing/buildbot/generate_buildbot_json.py
+++ b/testing/buildbot/generate_buildbot_json.py
@@ -152,17 +152,6 @@
     return scripts
 
 
-class JUnitGenerator(BaseGenerator):
-  def generate(self, waterfall, tester_name, tester_config, input_tests):
-    scripts = []
-    for test_name, test_config in sorted(input_tests.items()):
-      test = self.bb_gen.generate_junit_test(
-        waterfall, tester_name, tester_config, test_name, test_config)
-      if test:
-        scripts.append(test)
-    return scripts
-
-
 class SkylabGenerator(BaseGenerator):
   def generate(self, waterfall, tester_name, tester_config, input_tests):
     scripts = []
@@ -850,22 +839,6 @@
     result = {k: result[k] for k in self._SCRIPT_FIELDS if k in result}
     return result
 
-  def generate_junit_test(self, waterfall, tester_name, tester_config,
-                          test_name, test_config):
-    if not self.should_run_on_tester(waterfall, tester_name, test_config):
-      return None
-    result = copy.deepcopy(test_config)
-    # Use test_name here instead of test['name'] because test['name'] will be
-    # modified with the variant identifier in a matrix compound suite
-    result.setdefault('test', test_name)
-    result = self.apply_common_transformations(waterfall,
-                                               tester_name,
-                                               tester_config,
-                                               result,
-                                               test_name,
-                                               swarmable=False)
-    return result
-
   def generate_skylab_test(self, waterfall, tester_name, tester_config,
                            test_name, test_config):
     if not self.should_run_on_tester(waterfall, tester_name, test_config):
@@ -1038,8 +1011,6 @@
         GTestGenerator(self),
         'isolated_scripts':
         IsolatedScriptTestGenerator(self),
-        'junit_tests':
-        JUnitGenerator(self),
         'scripts':
         ScriptGenerator(self),
         'skylab_tests':
diff --git a/testing/buildbot/generate_buildbot_json_unittest.py b/testing/buildbot/generate_buildbot_json_unittest.py
index 44f2218..2304167 100755
--- a/testing/buildbot/generate_buildbot_json_unittest.py
+++ b/testing/buildbot/generate_buildbot_json_unittest.py
@@ -2025,15 +2025,6 @@
         'Attempted to generate a script test on tester.*'):
       fbb.check_output_file_consistency(verbose=True)
 
-  def test_junit_tests(self):
-    fbb = FakeBBGen(self.args,
-                    FOO_JUNIT_WATERFALL,
-                    GOOD_COMPOSITION_TEST_SUITES,
-                    LUCI_MILO_CFG,
-                    exceptions=NO_BAR_TEST_EXCEPTIONS)
-    fbb.check_output_file_consistency(verbose=True)
-    self.assertFalse(fbb.printed_lines)
-
   def test_gpu_telemetry_test_with_invalid_name(self):
     fbb = FakeBBGen(self.args,
                     FOO_GPU_TELEMETRY_TEST_WATERFALL,
diff --git a/testing/buildbot/internal.optimization_guide.json b/testing/buildbot/internal.optimization_guide.json
index ec0971f..d7e9b1d 100644
--- a/testing/buildbot/internal.optimization_guide.json
+++ b/testing/buildbot/internal.optimization_guide.json
@@ -22,9 +22,11 @@
         "swarming": {
           "dimensions": {
             "cpu": null,
+            "device_os": "R",
             "device_os_type": "userdebug",
+            "device_type": "flame",
             "os": "Android",
-            "pool": "chrome.tests.intelligence"
+            "pool": "chrome.tests"
           },
           "service_account": "chrome-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
diff --git a/testing/buildbot/mixins.pyl b/testing/buildbot/mixins.pyl
index c65f18ed..0bc8277 100644
--- a/testing/buildbot/mixins.pyl
+++ b/testing/buildbot/mixins.pyl
@@ -106,6 +106,16 @@
       },
     },
   },
+  'chrome-flame-fleet-pool': {
+    'swarming': {
+      'dimensions': {
+        'device_type': 'flame',
+        'device_os': 'R',
+        'pool': 'chrome.tests',
+        'os': 'Android',
+      },
+    },
+  },
   'chrome-intelligence-swarming-pool': {
     'swarming': {
       'dimensions': {
diff --git a/testing/buildbot/unittest_expectations/test_junit_tests/chromium.test.json b/testing/buildbot/unittest_expectations/test_junit_tests/chromium.test.json
deleted file mode 100644
index c147d52..0000000
--- a/testing/buildbot/unittest_expectations/test_junit_tests/chromium.test.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
-  "AAAAA1 AUTOGENERATED FILE DO NOT EDIT": {},
-  "AAAAA2 See generate_buildbot_json.py to make changes": {},
-  "Fake Tester": {
-    "junit_tests": [
-      {
-        "name": "foo_test",
-        "test": "foo_test"
-      }
-    ]
-  }
-}
diff --git a/testing/buildbot/waterfalls.pyl b/testing/buildbot/waterfalls.pyl
index 26456d2..83a2cdc 100644
--- a/testing/buildbot/waterfalls.pyl
+++ b/testing/buildbot/waterfalls.pyl
@@ -5487,7 +5487,7 @@
       'optimization_guide-android-arm64': {
         'mixins': [
           'android',
-          'chrome-intelligence-swarming-pool',
+          'chrome-flame-fleet-pool',
         ],
         'test_suites': {
            'gtest_tests': 'optimization_guide_android_gtests',
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 788ef47..98a4663 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -12613,26 +12613,6 @@
             ]
         }
     ],
-    "IncrementLocalSurfaceIdForMainframeSameDocNavigation": [
-        {
-            "platforms": [
-                "windows",
-                "mac",
-                "chromeos",
-                "chromeos_lacros",
-                "linux",
-                "android_webview"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "IncrementLocalSurfaceIdForMainframeSameDocNavigation"
-                    ]
-                }
-            ]
-        }
-    ],
     "InfoBarIconMonochrome": [
         {
             "platforms": [
@@ -12778,6 +12758,21 @@
             ]
         }
     ],
+    "JumpStartOmnibox": [
+        {
+            "platforms": [
+                "android"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "JumpStartOmnibox"
+                    ]
+                }
+            ]
+        }
+    ],
     "KeepAliveInBrowserMigration": [
         {
             "platforms": [
diff --git a/third_party/android_deps/build.gradle b/third_party/android_deps/build.gradle
index b01883c..7ccd6ef9 100644
--- a/third_party/android_deps/build.gradle
+++ b/third_party/android_deps/build.gradle
@@ -121,6 +121,8 @@
 
     // Needed by androidx.datastore
     compile "com.squareup.okio:okio-jvm:3.9.0"
+    // Needed by androidx.appcompat -> androidx.cursoradaptor
+    compile "org.jspecify:jspecify:1.0.0"
     // Needed by androidx.macrobenchmarks
     androidTestCompile "com.squareup.wire:wire-runtime-jvm:4.4.3"
     // Needed by androidx.benchmark
diff --git a/third_party/android_deps/fetch_all.py b/third_party/android_deps/fetch_all.py
index 073d7b1..82556a14 100755
--- a/third_party/android_deps/fetch_all.py
+++ b/third_party/android_deps/fetch_all.py
@@ -497,6 +497,7 @@
         logging.debug('mv [%s -> %s]', src_path, dst_path)
         if os.path.exists(dst_path):
             os.unlink(dst_path)
+        os.makedirs(os.path.dirname(dst_path), exist_ok=True)
         shutil.move(src_path, dst_path)
 
 
diff --git a/third_party/android_deps/libs/org_jspecify_jspecify/3pp/3pp.pb b/third_party/android_deps/libs/org_jspecify_jspecify/3pp/3pp.pb
new file mode 100644
index 0000000..d743b51b
--- /dev/null
+++ b/third_party/android_deps/libs/org_jspecify_jspecify/3pp/3pp.pb
@@ -0,0 +1,16 @@
+# Copyright 2021 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# This is generated, do not edit. Update BuildConfigGenerator.groovy instead.
+
+create {
+  source {
+    script { name: "fetch.py" }
+  }
+}
+
+upload {
+  pkg_prefix: "chromium/third_party/android_deps/libs"
+  universal: true
+}
diff --git a/third_party/android_deps/libs/org_jspecify_jspecify/3pp/fetch.py b/third_party/android_deps/libs/org_jspecify_jspecify/3pp/fetch.py
new file mode 100755
index 0000000..a2a9471a
--- /dev/null
+++ b/third_party/android_deps/libs/org_jspecify_jspecify/3pp/fetch.py
@@ -0,0 +1,26 @@
+#!/usr/bin/env python3
+# Copyright 2021 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# This is generated, do not edit. Update BuildConfigGenerator.groovy and
+# 3ppFetch.template instead.
+
+import pathlib
+import sys
+
+_3PP_DIR = pathlib.Path(__file__).resolve().parent
+sys.path.insert(0, str(_3PP_DIR.parents[2]))
+import fetch_common
+
+_REPO_URL = 'https://repo.maven.apache.org/maven2'
+SPEC = fetch_common.Spec(repo_url=_REPO_URL,
+                         group_name='org/jspecify',
+                         module_name='jspecify',
+                         file_ext='jar',
+                         patch_version='cr1',
+                         version_override=None,
+                         version_filter=None)
+
+if __name__ == '__main__':
+    fetch_common.main(SPEC)
diff --git a/third_party/android_deps/libs/org_jspecify_jspecify/OWNERS b/third_party/android_deps/libs/org_jspecify_jspecify/OWNERS
new file mode 100644
index 0000000..aea47a05
--- /dev/null
+++ b/third_party/android_deps/libs/org_jspecify_jspecify/OWNERS
@@ -0,0 +1 @@
+file://third_party/android_deps/OWNERS
diff --git a/third_party/blink/public/mojom/use_counter/metrics/webdx_feature.mojom b/third_party/blink/public/mojom/use_counter/metrics/webdx_feature.mojom
index acfbdbf..af4a2a2 100644
--- a/third_party/blink/public/mojom/use_counter/metrics/webdx_feature.mojom
+++ b/third_party/blink/public/mojom/use_counter/metrics/webdx_feature.mojom
@@ -175,12 +175,13 @@
   kHiddenUntilFoundAttribute = 117,
   kScheduler = 118,
   kKeyboardLock = 119,
-  kHTMLSearchElement = 120,
+  kSearch = 120,
   kAsyncClipboard = 121,
-  kHTMLUnsafeMethods = 122,
+  kParseHtmlUnsafe = 122,
   kClipboardUnsanitizedFormats = 123,
   kAborting = 124,
   kEditContext = 125,
+  kPaintOrder = 126,
 
   // Add new features immediately above this line. Don't change assigned
   // numbers of any item, and don't reuse removed slots.
diff --git a/third_party/blink/public/platform/web_vector.h b/third_party/blink/public/platform/web_vector.h
index 5ab5090..e246629 100644
--- a/third_party/blink/public/platform/web_vector.h
+++ b/third_party/blink/public/platform/web_vector.h
@@ -28,11 +28,6 @@
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-#ifdef UNSAFE_BUFFERS_BUILD
-// TODO(crbug.com/351564777): Remove this and convert code to safer constructs.
-#pragma allow_unsafe_buffers
-#endif
-
 #ifndef THIRD_PARTY_BLINK_PUBLIC_PLATFORM_WEB_VECTOR_H_
 #define THIRD_PARTY_BLINK_PUBLIC_PLATFORM_WEB_VECTOR_H_
 
@@ -145,11 +140,6 @@
     data_.assign(other.begin(), other.end());
   }
 
-  template <typename U>
-  void Assign(const U* values, size_t size) {
-    data_.assign(values, values + size);
-  }
-
   size_t size() const { return data_.size(); }
   void resize(size_t new_size) {
     DCHECK_LE(new_size, data_.capacity());
diff --git a/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_serializer_for_modules_test.cc b/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_serializer_for_modules_test.cc
index 1bdc20f..3b13606 100644
--- a/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_serializer_for_modules_test.cc
+++ b/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_serializer_for_modules_test.cc
@@ -309,8 +309,7 @@
   DummyExceptionStateForTesting exception_state;
   if (DOMArrayBuffer* buffer = NativeValueTraits<DOMArrayBuffer>::NativeValue(
           isolate, value.V8Value(), exception_state)) {
-    vector.Assign(reinterpret_cast<const unsigned char*>(buffer->Data()),
-                  buffer->ByteLength());
+    vector.Assign(buffer->ByteSpan());
   }
   return vector;
 }
diff --git a/third_party/blink/renderer/controller/highest_pmf_reporter.cc b/third_party/blink/renderer/controller/highest_pmf_reporter.cc
index f7cc8a0..4e87384 100644
--- a/third_party/blink/renderer/controller/highest_pmf_reporter.cc
+++ b/third_party/blink/renderer/controller/highest_pmf_reporter.cc
@@ -2,11 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifdef UNSAFE_BUFFERS_BUILD
-// TODO(crbug.com/351564777): Remove this and convert code to safer constructs.
-#pragma allow_unsafe_buffers
-#endif
-
 #include "third_party/blink/renderer/controller/highest_pmf_reporter.h"
 
 #include <limits>
@@ -20,15 +15,21 @@
 
 namespace blink {
 
-const char* HighestPmfReporter::highest_pmf_metric_names[] = {
+namespace {
+
+constexpr size_t kMaxReportCount = 4;
+
+constexpr std::array<const char*, kMaxReportCount> kHighestPmfMetricNames = {
     "Memory.Experimental.Renderer.HighestPrivateMemoryFootprint.0to2min",
     "Memory.Experimental.Renderer.HighestPrivateMemoryFootprint.2to4min",
     "Memory.Experimental.Renderer.HighestPrivateMemoryFootprint.4to8min",
     "Memory.Experimental.Renderer.HighestPrivateMemoryFootprint.8to16min"};
 
-constexpr base::TimeDelta HighestPmfReporter::time_to_report[] = {
+constexpr std::array<base::TimeDelta, kMaxReportCount> kTimeToReport = {
     base::Minutes(2), base::Minutes(4), base::Minutes(8), base::Minutes(16)};
 
+}  // namespace
+
 void HighestPmfReporter::Initialize(
     scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
   DEFINE_STATIC_LOCAL(HighestPmfReporter, reporter, (std::move(task_runner)));
@@ -79,7 +80,7 @@
         FROM_HERE,
         WTF::BindOnce(&HighestPmfReporter::OnReportMetrics,
                       WTF::Unretained(this)),
-        time_to_report[0]);
+        kTimeToReport[0]);
   }
 
   if (current_highest_pmf_ > usage.private_footprint_bytes)
@@ -105,7 +106,7 @@
   peak_resident_bytes_at_current_highest_pmf_ = 0.0;
   webpage_counts_at_current_highest_pmf_ = 0;
   report_count_++;
-  if (report_count_ >= std::size(time_to_report)) {
+  if (report_count_ >= kMaxReportCount) {
     // Stop observing the MemoryUsageMonitor once there's no more histogram to
     // report.
     MemoryUsageMonitor::Instance().RemoveObserver(this);
@@ -113,7 +114,7 @@
   }
 
   base::TimeDelta delay =
-      time_to_report[report_count_] - time_to_report[report_count_ - 1];
+      kTimeToReport[report_count_] - kTimeToReport[report_count_ - 1];
   task_runner_->PostDelayedTask(
       FROM_HERE,
       WTF::BindOnce(&HighestPmfReporter::OnReportMetrics,
@@ -122,7 +123,7 @@
 }
 
 void HighestPmfReporter::ReportMetrics() {
-  base::UmaHistogramMemoryMB(highest_pmf_metric_names[report_count_],
+  base::UmaHistogramMemoryMB(kHighestPmfMetricNames[report_count_],
                              base::saturated_cast<base::Histogram::Sample>(
                                  current_highest_pmf_ / 1024 / 1024));
 }
diff --git a/third_party/blink/renderer/controller/highest_pmf_reporter.h b/third_party/blink/renderer/controller/highest_pmf_reporter.h
index 6afda34..b3a86309 100644
--- a/third_party/blink/renderer/controller/highest_pmf_reporter.h
+++ b/third_party/blink/renderer/controller/highest_pmf_reporter.h
@@ -57,11 +57,6 @@
   double peak_resident_bytes_at_current_highest_pmf_ = 0.0;
   unsigned webpage_counts_at_current_highest_pmf_ = 0;
   unsigned report_count_ = 0;
-
-  static const char* highest_pmf_metric_names[];
-  static const char* peak_resident_bytes_metric_names[];
-  static const char* webpage_counts_metric_names[];
-  static const base::TimeDelta time_to_report[];
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/controller/memory_usage_monitor_posix.cc b/third_party/blink/renderer/controller/memory_usage_monitor_posix.cc
index 60fff06..5f2584a 100644
--- a/third_party/blink/renderer/controller/memory_usage_monitor_posix.cc
+++ b/third_party/blink/renderer/controller/memory_usage_monitor_posix.cc
@@ -2,11 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifdef UNSAFE_BUFFERS_BUILD
-// TODO(crbug.com/351564777): Remove this and convert code to safer constructs.
-#pragma allow_unsafe_buffers
-#endif
-
 #include "third_party/blink/renderer/controller/memory_usage_monitor_posix.h"
 
 #include <fcntl.h>
@@ -27,7 +22,7 @@
   ssize_t res = read(fd, contents.data(), contents.size() - 1);
   if (res <= 0)
     return false;
-  contents.data()[res] = '\0';
+  contents[res] = '\0';
   return true;
 }
 
diff --git a/third_party/blink/renderer/core/display_lock/display_lock_document_state.cc b/third_party/blink/renderer/core/display_lock/display_lock_document_state.cc
index b7fe205..a96721ee 100644
--- a/third_party/blink/renderer/core/display_lock/display_lock_document_state.cc
+++ b/third_party/blink/renderer/core/display_lock/display_lock_document_state.cc
@@ -396,7 +396,9 @@
       if (context->HasElement()) {
         context->DidForceActivatableDisplayLocks();
       } else {
-        DUMP_WILL_BE_NOTREACHED()
+        // This used to be a DUMP_WILL_BE_NOTREACHED(), but the crash volume was
+        // too high. See crbug.com/41494130
+        DCHECK(false)
             << "The DisplayLockContext's element has been garbage collected or"
             << " otherwise deleted, but the DisplayLockContext is still alive!"
             << " This shouldn't happen and could cause a crash. See"
diff --git a/third_party/blink/renderer/core/html/canvas/canvas_context_creation_attributes_core.h b/third_party/blink/renderer/core/html/canvas/canvas_context_creation_attributes_core.h
index 8652e81..badabb7 100644
--- a/third_party/blink/renderer/core/html/canvas/canvas_context_creation_attributes_core.h
+++ b/third_party/blink/renderer/core/html/canvas/canvas_context_creation_attributes_core.h
@@ -22,6 +22,7 @@
   virtual ~CanvasContextCreationAttributesCore();
 
   enum class WillReadFrequently { kTrue, kFalse, kUndefined };
+  enum class PowerPreference { kDefault, kLowPower, kHighPerformance };
 
   bool alpha = true;
   bool antialias = true;
@@ -32,7 +33,7 @@
   CanvasPixelFormat pixel_format = CanvasPixelFormat::kUint8;
   bool premultiplied_alpha = true;
   bool preserve_drawing_buffer = false;
-  String power_preference = "default";
+  PowerPreference power_preference = PowerPreference::kDefault;
   bool stencil = false;
   // Help to determine whether to use GPU or CPU for the canvas.
   WillReadFrequently will_read_frequently = WillReadFrequently::kUndefined;
diff --git a/third_party/blink/renderer/core/html/canvas/html_canvas_element.cc b/third_party/blink/renderer/core/html/canvas/html_canvas_element.cc
index 33fab03..2e89ecba 100644
--- a/third_party/blink/renderer/core/html/canvas/html_canvas_element.cc
+++ b/third_party/blink/renderer/core/html/canvas/html_canvas_element.cc
@@ -467,8 +467,10 @@
   // If this context is cross-origin, it should prefer to use the low-power GPU
   LocalFrame* frame = GetDocument().GetFrame();
   CanvasContextCreationAttributesCore recomputed_attributes = attributes;
-  if (frame && frame->IsCrossOriginToOutermostMainFrame())
-    recomputed_attributes.power_preference = "low-power";
+  if (frame && frame->IsCrossOriginToOutermostMainFrame()) {
+    recomputed_attributes.power_preference =
+        CanvasContextCreationAttributesCore::PowerPreference::kLowPower;
+  }
 
   context_ = factory->Create(this, recomputed_attributes);
   if (!context_)
diff --git a/third_party/blink/renderer/core/inspector/inspector_session_state.cc b/third_party/blink/renderer/core/inspector/inspector_session_state.cc
index d6e1bc0..4ccd397 100644
--- a/third_party/blink/renderer/core/inspector/inspector_session_state.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_session_state.cc
@@ -148,7 +148,7 @@
   // We could CBOR encode this, but since we never look at the contents
   // anyway (except for decoding just below), we just cheat and use the
   // blob directly.
-  out->Assign(v.data(), v.size());
+  out->Assign(v);
 }
 
 /*static*/
diff --git a/third_party/blink/renderer/core/offscreencanvas/offscreen_canvas.cc b/third_party/blink/renderer/core/offscreencanvas/offscreen_canvas.cc
index ed4e649..553d6a0 100644
--- a/third_party/blink/renderer/core/offscreencanvas/offscreen_canvas.cc
+++ b/third_party/blink/renderer/core/offscreencanvas/offscreen_canvas.cc
@@ -420,8 +420,10 @@
     probe::DidCreateOffscreenCanvasContext(this);
 
     CanvasContextCreationAttributesCore recomputed_attributes = attributes;
-    if (!allow_high_performance_power_preference_)
-      recomputed_attributes.power_preference = "low-power";
+    if (!allow_high_performance_power_preference_) {
+      recomputed_attributes.power_preference =
+          CanvasContextCreationAttributesCore::PowerPreference::kLowPower;
+    }
 
     context_ = factory->Create(this, recomputed_attributes);
     if (context_) {
diff --git a/third_party/blink/renderer/modules/accessibility/ax_image_map_link.cc b/third_party/blink/renderer/modules/accessibility/ax_image_map_link.cc
index 744b2da..ffbef70 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_image_map_link.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_image_map_link.cc
@@ -31,6 +31,7 @@
 #include "third_party/blink/renderer/core/aom/accessible_node.h"
 #include "third_party/blink/renderer/core/dom/element_traversal.h"
 #include "third_party/blink/renderer/core/html/html_image_element.h"
+#include "third_party/blink/renderer/core/html/html_map_element.h"
 #include "third_party/blink/renderer/modules/accessibility/ax_node_object.h"
 #include "third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.h"
 #include "third_party/blink/renderer/platform/graphics/path.h"
diff --git a/third_party/blink/renderer/modules/accessibility/ax_image_map_link.h b/third_party/blink/renderer/modules/accessibility/ax_image_map_link.h
index eb9b5a75..e947c66 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_image_map_link.h
+++ b/third_party/blink/renderer/modules/accessibility/ax_image_map_link.h
@@ -30,12 +30,12 @@
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_ACCESSIBILITY_AX_IMAGE_MAP_LINK_H_
 
 #include "third_party/blink/renderer/core/html/html_area_element.h"
-#include "third_party/blink/renderer/core/html/html_map_element.h"
 #include "third_party/blink/renderer/modules/accessibility/ax_node_object.h"
 
 namespace blink {
 
 class AXObjectCacheImpl;
+class HTMLMapElement;
 
 class AXImageMapLink final : public AXNodeObject {
  public:
diff --git a/third_party/blink/renderer/modules/accessibility/ax_media_element.h b/third_party/blink/renderer/modules/accessibility/ax_media_element.h
index 2838785..20a28af 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_media_element.h
+++ b/third_party/blink/renderer/modules/accessibility/ax_media_element.h
@@ -5,7 +5,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_ACCESSIBILITY_AX_MEDIA_ELEMENT_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_ACCESSIBILITY_AX_MEDIA_ELEMENT_H_
 
-#include "third_party/blink/renderer/core/html/media/html_media_element.h"
 #include "third_party/blink/renderer/modules/accessibility/ax_node_object.h"
 
 namespace blink {
diff --git a/third_party/blink/renderer/modules/accessibility/ax_node_object.h b/third_party/blink/renderer/modules/accessibility/ax_node_object.h
index 6c429f2..191c4fd9 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_node_object.h
+++ b/third_party/blink/renderer/modules/accessibility/ax_node_object.h
@@ -30,7 +30,6 @@
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_ACCESSIBILITY_AX_NODE_OBJECT_H_
 
 #include "base/dcheck_is_on.h"
-#include "third_party/blink/renderer/core/editing/markers/document_marker.h"
 #include "third_party/blink/renderer/core/html/forms/html_form_control_element.h"
 #include "third_party/blink/renderer/modules/accessibility/ax_object.h"
 #include "third_party/blink/renderer/modules/modules_export.h"
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object.h b/third_party/blink/renderer/modules/accessibility/ax_object.h
index 19ecf787..3ecba71 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_object.h
+++ b/third_party/blink/renderer/modules/accessibility/ax_object.h
@@ -35,7 +35,6 @@
 #include <utility>
 
 #include "base/dcheck_is_on.h"
-#include "base/gtest_prod_util.h"
 #include "base/memory/raw_ref.h"
 #include "base/memory/stack_allocated.h"
 #include "third_party/blink/public/common/input/web_input_event.h"
@@ -51,7 +50,6 @@
 #include "third_party/blink/renderer/platform/graphics/color.h"
 #include "third_party/blink/renderer/platform/heap/collection_support/heap_hash_set.h"
 #include "third_party/blink/renderer/platform/heap/garbage_collected.h"
-#include "third_party/blink/renderer/platform/heap/persistent.h"
 #include "third_party/blink/renderer/platform/weborigin/kurl.h"
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
 #include "third_party/blink/renderer/platform/wtf/vector.h"
@@ -60,8 +58,6 @@
 #include "ui/accessibility/ax_mode.h"
 #include "ui/accessibility/ax_node_id_forward.h"
 #include "ui/accessibility/ax_tree_id.h"
-#include "ui/accessibility/ax_tree_serializer.h"
-#include "ui/gfx/geometry/quad_f.h"
 
 namespace gfx {
 class Transform;
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc b/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc
index 1a249cfb0..a5d08e4 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc
@@ -75,6 +75,7 @@
 #include "third_party/blink/renderer/core/html/html_frame_owner_element.h"
 #include "third_party/blink/renderer/core/html/html_head_element.h"
 #include "third_party/blink/renderer/core/html/html_image_element.h"
+#include "third_party/blink/renderer/core/html/html_map_element.h"
 #include "third_party/blink/renderer/core/html/html_menu_element.h"
 #include "third_party/blink/renderer/core/html/html_object_element.h"
 #include "third_party/blink/renderer/core/html/html_olist_element.h"
diff --git a/third_party/blink/renderer/modules/accessibility/ax_position.h b/third_party/blink/renderer/modules/accessibility/ax_position.h
index 6817bfd..2e1f4339 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_position.h
+++ b/third_party/blink/renderer/modules/accessibility/ax_position.h
@@ -10,10 +10,8 @@
 #include <ostream>
 
 #include "base/dcheck_is_on.h"
-#include "base/logging.h"
 #include "third_party/blink/renderer/core/editing/forward.h"
 #include "third_party/blink/renderer/core/editing/text_affinity.h"
-#include "third_party/blink/renderer/core/layout/inline/offset_mapping.h"
 #include "third_party/blink/renderer/modules/modules_export.h"
 #include "third_party/blink/renderer/platform/heap/persistent.h"
 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
@@ -25,6 +23,7 @@
 class ContainerNode;
 class Document;
 class Node;
+class OffsetMapping;
 
 // When converting to a DOM position from an |AXPosition| or vice versa, and the
 // corresponding position is invalid, doesn't exist, or is inside an ignored
diff --git a/third_party/blink/renderer/modules/accessibility/ax_range.h b/third_party/blink/renderer/modules/accessibility/ax_range.h
index f5779d9..4929779 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_range.h
+++ b/third_party/blink/renderer/modules/accessibility/ax_range.h
@@ -10,11 +10,9 @@
 #include <ostream>
 
 #include "base/dcheck_is_on.h"
-#include "base/logging.h"
 #include "third_party/blink/renderer/modules/accessibility/ax_position.h"
 #include "third_party/blink/renderer/modules/modules_export.h"
 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
-#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
 
 namespace blink {
 
diff --git a/third_party/blink/renderer/modules/accessibility/ax_relation_cache.h b/third_party/blink/renderer/modules/accessibility/ax_relation_cache.h
index 3cac154..7aa3185 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_relation_cache.h
+++ b/third_party/blink/renderer/modules/accessibility/ax_relation_cache.h
@@ -8,7 +8,6 @@
 #include "third_party/blink/renderer/core/dom/dom_node_ids.h"
 #include "third_party/blink/renderer/core/html/forms/html_label_element.h"
 #include "third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.h"
-#include "third_party/blink/renderer/modules/modules_export.h"
 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
 #include "third_party/blink/renderer/platform/wtf/hash_map.h"
 #include "third_party/blink/renderer/platform/wtf/hash_set.h"
diff --git a/third_party/blink/renderer/modules/accessibility/ax_selection.h b/third_party/blink/renderer/modules/accessibility/ax_selection.h
index 1302bf6..7aff726 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_selection.h
+++ b/third_party/blink/renderer/modules/accessibility/ax_selection.h
@@ -11,7 +11,6 @@
 #include <ostream>
 
 #include "base/dcheck_is_on.h"
-#include "base/logging.h"
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/editing/forward.h"
 #include "third_party/blink/renderer/core/html/forms/text_control_element.h"
diff --git a/third_party/blink/renderer/modules/accessibility/blink_ax_tree_source.cc b/third_party/blink/renderer/modules/accessibility/blink_ax_tree_source.cc
index 242bc59..11c4caa 100644
--- a/third_party/blink/renderer/modules/accessibility/blink_ax_tree_source.cc
+++ b/third_party/blink/renderer/modules/accessibility/blink_ax_tree_source.cc
@@ -6,13 +6,8 @@
 
 #include <stddef.h>
 
-#include <algorithm>
-#include <set>
-
 #include "base/containers/contains.h"
 #include "base/memory/ptr_util.h"
-#include "build/build_config.h"
-#include "build/chromeos_buildflags.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
 #include "third_party/blink/renderer/core/html/html_head_element.h"
 #include "third_party/blink/renderer/core/html/html_link_element.h"
@@ -22,13 +17,8 @@
 #include "third_party/blink/renderer/modules/accessibility/ax_object.h"
 #include "third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.h"
 #include "third_party/blink/renderer/modules/accessibility/ax_selection.h"
-#include "third_party/blink/renderer/platform/heap/collection_support/heap_deque.h"
-#include "ui/accessibility/ax_common.h"
-#include "ui/accessibility/ax_enum_util.h"
 #include "ui/accessibility/ax_role_properties.h"
 #include "ui/accessibility/ax_tree_id.h"
-#include "ui/gfx/geometry/transform.h"
-#include "ui/gfx/geometry/vector2d_f.h"
 
 namespace blink {
 
diff --git a/third_party/blink/renderer/modules/accessibility/blink_ax_tree_source.h b/third_party/blink/renderer/modules/accessibility/blink_ax_tree_source.h
index 4fca0b3..0b51738 100644
--- a/third_party/blink/renderer/modules/accessibility/blink_ax_tree_source.h
+++ b/third_party/blink/renderer/modules/accessibility/blink_ax_tree_source.h
@@ -8,21 +8,21 @@
 #include <stdint.h>
 
 #include <optional>
-#include <set>
 #include <string>
 
 #include "third_party/blink/public/web/web_ax_object.h"
 #include "third_party/blink/public/web/web_document.h"
-#include "third_party/blink/renderer/core/frame/local_frame_view.h"
 #include "third_party/blink/renderer/modules/accessibility/ax_object.h"
 #include "third_party/blink/renderer/modules/modules_export.h"
-#include "third_party/blink/renderer/platform/wtf/hash_set.h"
 #include "ui/accessibility/ax_common.h"
 #include "ui/accessibility/ax_mode.h"
 #include "ui/accessibility/ax_node_data.h"
-#include "ui/accessibility/ax_tree_data.h"
 #include "ui/accessibility/ax_tree_source.h"
 
+namespace ui {
+struct AXTreeData;
+}
+
 namespace blink {
 
 class AXObjectCacheImpl;
diff --git a/third_party/blink/renderer/modules/canvas/htmlcanvas/canvas_context_creation_attributes_helpers.cc b/third_party/blink/renderer/modules/canvas/htmlcanvas/canvas_context_creation_attributes_helpers.cc
index 8938e79..fdb9976 100644
--- a/third_party/blink/renderer/modules/canvas/htmlcanvas/canvas_context_creation_attributes_helpers.cc
+++ b/third_party/blink/renderer/modules/canvas/htmlcanvas/canvas_context_creation_attributes_helpers.cc
@@ -40,7 +40,20 @@
   }
   result.premultiplied_alpha = attrs->premultipliedAlpha();
   result.preserve_drawing_buffer = attrs->preserveDrawingBuffer();
-  result.power_preference = attrs->powerPreference();
+  switch (attrs->powerPreference().AsEnum()) {
+    case V8CanvasPowerPreference::Enum::kDefault:
+      result.power_preference =
+          CanvasContextCreationAttributesCore::PowerPreference::kDefault;
+      break;
+    case V8CanvasPowerPreference::Enum::kLowPower:
+      result.power_preference =
+          CanvasContextCreationAttributesCore::PowerPreference::kLowPower;
+      break;
+    case V8CanvasPowerPreference::Enum::kHighPerformance:
+      result.power_preference = CanvasContextCreationAttributesCore::
+          PowerPreference::kHighPerformance;
+      break;
+  }
   result.stencil = attrs->stencil();
   switch (attrs->willReadFrequently().AsEnum()) {
     case V8CanvasWillReadFrequently::Enum::kTrue:
diff --git a/third_party/blink/renderer/modules/csspaint/nativepaint/clip_path_paint_definition.cc b/third_party/blink/renderer/modules/csspaint/nativepaint/clip_path_paint_definition.cc
index ecf2ae43..1739f82d 100644
--- a/third_party/blink/renderer/modules/csspaint/nativepaint/clip_path_paint_definition.cc
+++ b/third_party/blink/renderer/modules/csspaint/nativepaint/clip_path_paint_definition.cc
@@ -309,7 +309,6 @@
       const auto& entry = animated_property_values.begin();
       progress = entry->second.float_value.value();
     } else {
-      CHECK(input->MainThreadProgress().has_value());
       progress = input->MainThreadProgress().value();
     }
 
@@ -393,8 +392,12 @@
       static_path = path.GetSkPath();
       break;
     }
-    default:
-      break;
+    case Timing::FillMode::BOTH:
+    case Timing::FillMode::BACKWARDS: {
+      Path path;
+      animated_shapes[0].get()->GetPath(path, reference_box, zoom);
+      static_path = path.GetSkPath();
+    }
   }
 
   node.GetLayoutObject()->GetMutableForPainting().EnsureId();
diff --git a/third_party/blink/renderer/modules/indexeddb/idb_cursor.cc b/third_party/blink/renderer/modules/indexeddb/idb_cursor.cc
index 8e452bd..2a538fc 100644
--- a/third_party/blink/renderer/modules/indexeddb/idb_cursor.cc
+++ b/third_party/blink/renderer/modules/indexeddb/idb_cursor.cc
@@ -460,19 +460,18 @@
 }
 
 // static
-mojom::IDBCursorDirection IDBCursor::StringToDirection(
-    const String& direction_string) {
-  if (direction_string == indexed_db_names::kNext)
-    return mojom::IDBCursorDirection::Next;
-  if (direction_string == indexed_db_names::kNextunique)
-    return mojom::IDBCursorDirection::NextNoDuplicate;
-  if (direction_string == indexed_db_names::kPrev)
-    return mojom::IDBCursorDirection::Prev;
-  if (direction_string == indexed_db_names::kPrevunique)
-    return mojom::IDBCursorDirection::PrevNoDuplicate;
-
-  NOTREACHED_IN_MIGRATION();
-  return mojom::IDBCursorDirection::Next;
+mojom::blink::IDBCursorDirection IDBCursor::V8EnumToDirection(
+    V8IDBCursorDirection::Enum mode) {
+  switch (mode) {
+    case V8IDBCursorDirection::Enum::kNext:
+      return mojom::blink::IDBCursorDirection::Next;
+    case V8IDBCursorDirection::Enum::kNextunique:
+      return mojom::blink::IDBCursorDirection::NextNoDuplicate;
+    case V8IDBCursorDirection::Enum::kPrev:
+      return mojom::blink::IDBCursorDirection::Prev;
+    case V8IDBCursorDirection::Enum::kPrevunique:
+      return mojom::blink::IDBCursorDirection::PrevNoDuplicate;
+  }
 }
 
 // static
diff --git a/third_party/blink/renderer/modules/indexeddb/idb_cursor.h b/third_party/blink/renderer/modules/indexeddb/idb_cursor.h
index 3d186457..e045e69 100644
--- a/third_party/blink/renderer/modules/indexeddb/idb_cursor.h
+++ b/third_party/blink/renderer/modules/indexeddb/idb_cursor.h
@@ -36,6 +36,7 @@
 #include "mojo/public/cpp/bindings/associated_remote.h"
 #include "third_party/blink/public/mojom/indexeddb/indexeddb.mojom-blink.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_value.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_idb_cursor_direction.h"
 #include "third_party/blink/renderer/modules/indexeddb/idb_factory_client.h"
 #include "third_party/blink/renderer/modules/indexeddb/idb_key.h"
 #include "third_party/blink/renderer/modules/indexeddb/idb_request.h"
@@ -59,7 +60,8 @@
  public:
   using Source = V8UnionIDBIndexOrIDBObjectStore;
 
-  static mojom::IDBCursorDirection StringToDirection(const String& mode_string);
+  static mojom::blink::IDBCursorDirection V8EnumToDirection(
+      V8IDBCursorDirection::Enum mode);
 
   // Reset cursor prefetch caches for all cursors except `except_cursor`.
   // In most callers, `except_cursor` is passed as nullptr, causing all cursors
diff --git a/third_party/blink/renderer/modules/indexeddb/idb_database.cc b/third_party/blink/renderer/modules/indexeddb/idb_database.cc
index 31a9fb9..505b90b 100644
--- a/third_party/blink/renderer/modules/indexeddb/idb_database.cc
+++ b/third_party/blink/renderer/modules/indexeddb/idb_database.cc
@@ -300,7 +300,7 @@
 IDBTransaction* IDBDatabase::transaction(
     ScriptState* script_state,
     const V8UnionStringOrStringSequence* store_names,
-    const String& mode_string,
+    const V8IDBTransactionMode& v8_mode,
     const IDBTransactionOptions* options,
     ExceptionState& exception_state) {
   TRACE_EVENT0("IndexedDB", "IDBDatabase::transaction");
@@ -355,11 +355,12 @@
     object_store_ids.push_back(object_store_id);
   }
 
-  mojom::IDBTransactionMode mode = IDBTransaction::StringToMode(mode_string);
-  if (mode != mojom::IDBTransactionMode::ReadOnly &&
-      mode != mojom::IDBTransactionMode::ReadWrite) {
+  mojom::blink::IDBTransactionMode mode =
+      IDBTransaction::EnumToMode(v8_mode.AsEnum());
+  if (mode != mojom::blink::IDBTransactionMode::ReadOnly &&
+      mode != mojom::blink::IDBTransactionMode::ReadWrite) {
     exception_state.ThrowTypeError(
-        "The mode provided ('" + mode_string +
+        "The mode provided ('" + v8_mode.AsString() +
         "') is not one of 'readonly' or 'readwrite'.");
     return nullptr;
   }
diff --git a/third_party/blink/renderer/modules/indexeddb/idb_database.h b/third_party/blink/renderer/modules/indexeddb/idb_database.h
index a7b6f1d..599fe366 100644
--- a/third_party/blink/renderer/modules/indexeddb/idb_database.h
+++ b/third_party/blink/renderer/modules/indexeddb/idb_database.h
@@ -103,7 +103,7 @@
   }
   IDBTransaction* transaction(ScriptState* script_state,
                               const V8UnionStringOrStringSequence* store_names,
-                              const String& mode,
+                              const V8IDBTransactionMode& mode,
                               const IDBTransactionOptions* options,
                               ExceptionState& exception_state);
   void deleteObjectStore(const String& name, ExceptionState&);
diff --git a/third_party/blink/renderer/modules/indexeddb/idb_index.cc b/third_party/blink/renderer/modules/indexeddb/idb_index.cc
index 92d2885..24cf8a9 100644
--- a/third_party/blink/renderer/modules/indexeddb/idb_index.cc
+++ b/third_party/blink/renderer/modules/indexeddb/idb_index.cc
@@ -115,7 +115,7 @@
 
 IDBRequest* IDBIndex::openCursor(ScriptState* script_state,
                                  const ScriptValue& range,
-                                 const String& direction_string,
+                                 const V8IDBCursorDirection& v8_direction,
                                  ExceptionState& exception_state) {
   TRACE_EVENT1("IndexedDB", "IDBIndex::openCursorRequestSetup", "index_name",
                metadata_->name.Utf8());
@@ -133,7 +133,7 @@
     return nullptr;
   }
   mojom::blink::IDBCursorDirection direction =
-      IDBCursor::StringToDirection(direction_string);
+      IDBCursor::V8EnumToDirection(v8_direction.AsEnum());
   IDBKeyRange* key_range = IDBKeyRange::FromScriptValue(
       ExecutionContext::From(script_state), range, exception_state);
   if (exception_state.HadException())
@@ -198,7 +198,7 @@
 
 IDBRequest* IDBIndex::openKeyCursor(ScriptState* script_state,
                                     const ScriptValue& range,
-                                    const String& direction_string,
+                                    const V8IDBCursorDirection& v8_direction,
                                     ExceptionState& exception_state) {
   TRACE_EVENT1("IndexedDB", "IDBIndex::openKeyCursorRequestSetup", "index_name",
                metadata_->name.Utf8());
@@ -216,7 +216,7 @@
     return nullptr;
   }
   mojom::blink::IDBCursorDirection direction =
-      IDBCursor::StringToDirection(direction_string);
+      IDBCursor::V8EnumToDirection(v8_direction.AsEnum());
   IDBKeyRange* key_range = IDBKeyRange::FromScriptValue(
       ExecutionContext::From(script_state), range, exception_state);
   if (exception_state.HadException())
diff --git a/third_party/blink/renderer/modules/indexeddb/idb_index.h b/third_party/blink/renderer/modules/indexeddb/idb_index.h
index a530e2b6..9de1153 100644
--- a/third_party/blink/renderer/modules/indexeddb/idb_index.h
+++ b/third_party/blink/renderer/modules/indexeddb/idb_index.h
@@ -67,11 +67,11 @@
 
   IDBRequest* openCursor(ScriptState*,
                          const ScriptValue& key,
-                         const String& direction,
+                         const V8IDBCursorDirection& direction,
                          ExceptionState&);
   IDBRequest* openKeyCursor(ScriptState*,
                             const ScriptValue& range,
-                            const String& direction,
+                            const V8IDBCursorDirection& direction,
                             ExceptionState&);
   IDBRequest* count(ScriptState*, const ScriptValue& range, ExceptionState&);
   IDBRequest* get(ScriptState*, const ScriptValue& key, ExceptionState&);
diff --git a/third_party/blink/renderer/modules/indexeddb/idb_object_store.cc b/third_party/blink/renderer/modules/indexeddb/idb_object_store.cc
index e3f76da..de9e142 100644
--- a/third_party/blink/renderer/modules/indexeddb/idb_object_store.cc
+++ b/third_party/blink/renderer/modules/indexeddb/idb_object_store.cc
@@ -974,7 +974,7 @@
 
 IDBRequest* IDBObjectStore::openCursor(ScriptState* script_state,
                                        const ScriptValue& range,
-                                       const String& direction_string,
+                                       const V8IDBCursorDirection& v8_direction,
                                        ExceptionState& exception_state) {
   TRACE_EVENT1("IndexedDB", "IDBObjectStore::openCursorRequestSetup",
                "store_name", metadata_->name.Utf8());
@@ -994,7 +994,7 @@
   }
 
   mojom::IDBCursorDirection direction =
-      IDBCursor::StringToDirection(direction_string);
+      IDBCursor::V8EnumToDirection(v8_direction.AsEnum());
   IDBKeyRange* key_range = IDBKeyRange::FromScriptValue(
       ExecutionContext::From(script_state), range, exception_state);
   if (exception_state.HadException())
@@ -1024,10 +1024,11 @@
   return request;
 }
 
-IDBRequest* IDBObjectStore::openKeyCursor(ScriptState* script_state,
-                                          const ScriptValue& range,
-                                          const String& direction_string,
-                                          ExceptionState& exception_state) {
+IDBRequest* IDBObjectStore::openKeyCursor(
+    ScriptState* script_state,
+    const ScriptValue& range,
+    const V8IDBCursorDirection& v8_direction,
+    ExceptionState& exception_state) {
   TRACE_EVENT1("IndexedDB", "IDBObjectStore::openKeyCursorRequestSetup",
                "store_name", metadata_->name.Utf8());
   IDBRequest::AsyncTraceState metrics(
@@ -1046,7 +1047,7 @@
   }
 
   mojom::IDBCursorDirection direction =
-      IDBCursor::StringToDirection(direction_string);
+      IDBCursor::V8EnumToDirection(v8_direction.AsEnum());
   IDBKeyRange* key_range = IDBKeyRange::FromScriptValue(
       ExecutionContext::From(script_state), range, exception_state);
   if (exception_state.HadException())
diff --git a/third_party/blink/renderer/modules/indexeddb/idb_object_store.h b/third_party/blink/renderer/modules/indexeddb/idb_object_store.h
index 60ca307..5875892 100644
--- a/third_party/blink/renderer/modules/indexeddb/idb_object_store.h
+++ b/third_party/blink/renderer/modules/indexeddb/idb_object_store.h
@@ -74,11 +74,11 @@
 
   IDBRequest* openCursor(ScriptState*,
                          const ScriptValue& range,
-                         const String& direction,
+                         const V8IDBCursorDirection& direction,
                          ExceptionState&);
   IDBRequest* openKeyCursor(ScriptState*,
                             const ScriptValue& range,
-                            const String& direction,
+                            const V8IDBCursorDirection& direction,
                             ExceptionState&);
   IDBRequest* get(ScriptState*, const ScriptValue& key, ExceptionState&);
   IDBRequest* getKey(ScriptState*, const ScriptValue& key, ExceptionState&);
diff --git a/third_party/blink/renderer/modules/indexeddb/idb_transaction.cc b/third_party/blink/renderer/modules/indexeddb/idb_transaction.cc
index fe57ccb6..49f8356 100644
--- a/third_party/blink/renderer/modules/indexeddb/idb_transaction.cc
+++ b/third_party/blink/renderer/modules/indexeddb/idb_transaction.cc
@@ -573,32 +573,29 @@
   return has_pending_activity_ && GetExecutionContext();
 }
 
-mojom::blink::IDBTransactionMode IDBTransaction::StringToMode(
-    const String& mode_string) {
-  if (mode_string == indexed_db_names::kReadonly)
-    return mojom::blink::IDBTransactionMode::ReadOnly;
-  if (mode_string == indexed_db_names::kReadwrite)
-    return mojom::blink::IDBTransactionMode::ReadWrite;
-  if (mode_string == indexed_db_names::kVersionchange)
-    return mojom::blink::IDBTransactionMode::VersionChange;
-  NOTREACHED_IN_MIGRATION();
-  return mojom::blink::IDBTransactionMode::ReadOnly;
+mojom::blink::IDBTransactionMode IDBTransaction::EnumToMode(
+    V8IDBTransactionMode::Enum mode) {
+  switch (mode) {
+    case V8IDBTransactionMode::Enum::kReadonly:
+      return mojom::blink::IDBTransactionMode::ReadOnly;
+    case V8IDBTransactionMode::Enum::kReadwrite:
+      return mojom::blink::IDBTransactionMode::ReadWrite;
+    case V8IDBTransactionMode::Enum::kVersionchange:
+      return mojom::blink::IDBTransactionMode::VersionChange;
+  }
 }
 
-const String& IDBTransaction::mode() const {
+V8IDBTransactionMode IDBTransaction::mode() const {
   switch (mode_) {
     case mojom::blink::IDBTransactionMode::ReadOnly:
-      return indexed_db_names::kReadonly;
+      return V8IDBTransactionMode(V8IDBTransactionMode::Enum::kReadonly);
 
     case mojom::blink::IDBTransactionMode::ReadWrite:
-      return indexed_db_names::kReadwrite;
+      return V8IDBTransactionMode(V8IDBTransactionMode::Enum::kReadwrite);
 
     case mojom::blink::IDBTransactionMode::VersionChange:
-      return indexed_db_names::kVersionchange;
+      return V8IDBTransactionMode(V8IDBTransactionMode::Enum::kVersionchange);
   }
-
-  NOTREACHED_IN_MIGRATION();
-  return indexed_db_names::kReadonly;
 }
 
 const String& IDBTransaction::durability() const {
diff --git a/third_party/blink/renderer/modules/indexeddb/idb_transaction.h b/third_party/blink/renderer/modules/indexeddb/idb_transaction.h
index 1543739..8857e2f 100644
--- a/third_party/blink/renderer/modules/indexeddb/idb_transaction.h
+++ b/third_party/blink/renderer/modules/indexeddb/idb_transaction.h
@@ -32,6 +32,7 @@
 #include "third_party/blink/public/mojom/indexeddb/indexeddb.mojom-blink-forward.h"
 #include "third_party/blink/public/mojom/indexeddb/indexeddb.mojom-blink.h"
 #include "third_party/blink/renderer/bindings/core/v8/active_script_wrappable.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_idb_transaction_mode.h"
 #include "third_party/blink/renderer/core/dom/dom_string_list.h"
 #include "third_party/blink/renderer/core/dom/events/event_listener.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context_lifecycle_observer.h"
@@ -110,7 +111,8 @@
 
   void Trace(Visitor*) const override;
 
-  static mojom::blink::IDBTransactionMode StringToMode(const String&);
+  static mojom::blink::IDBTransactionMode EnumToMode(
+      V8IDBTransactionMode::Enum);
 
   int64_t Id() const { return id_; }
   bool IsActive() const { return state_ == kActive; }
@@ -128,7 +130,7 @@
   void IncrementNumErrorsHandled() { ++num_errors_handled_; }
 
   // Implement the IDBTransaction IDL
-  const String& mode() const;
+  V8IDBTransactionMode mode() const;
   const String& durability() const;
   DOMStringList* objectStoreNames() const;
   IDBDatabase& db() { return *database_; }
diff --git a/third_party/blink/renderer/modules/indexeddb/inspector_indexed_db_agent.cc b/third_party/blink/renderer/modules/indexeddb/inspector_indexed_db_agent.cc
index 061876d6..bf0b6fe 100644
--- a/third_party/blink/renderer/modules/indexeddb/inspector_indexed_db_agent.cc
+++ b/third_party/blink/renderer/modules/indexeddb/inspector_indexed_db_agent.cc
@@ -417,8 +417,12 @@
   IDBTransactionOptions* options =
       MakeGarbageCollected<IDBTransactionOptions>();
   options->setDurability("relaxed");
+  auto v8_mode = V8IDBTransactionMode::Create(mode);
+  if (!v8_mode) {
+    return nullptr;
+  }
   IDBTransaction* idb_transaction = idb_database->transaction(
-      script_state, scope, mode, options, exception_state);
+      script_state, scope, v8_mode.value(), options, exception_state);
   if (exception_state.HadException()) {
     return nullptr;
   }
diff --git a/third_party/blink/renderer/modules/media_controls/media_controls_impl_test.cc b/third_party/blink/renderer/modules/media_controls/media_controls_impl_test.cc
index a49270bd..d8331ae7 100644
--- a/third_party/blink/renderer/modules/media_controls/media_controls_impl_test.cc
+++ b/third_party/blink/renderer/modules/media_controls/media_controls_impl_test.cc
@@ -285,7 +285,7 @@
         AtomicString("https://example.com/foo.mp4"));
     test::RunPendingTasks();
     WebTimeRange time_range(0.0, duration);
-    WebMediaPlayer()->seekable_.Assign(&time_range, 1);
+    WebMediaPlayer()->seekable_.Assign(base::span_from_ref(time_range));
     MediaControls().MediaElement().DurationChanged(duration,
                                                    false /* requestSeek */);
     SimulateLoadedMetadata();
diff --git a/third_party/blink/renderer/modules/ml/ml_context.cc b/third_party/blink/renderer/modules/ml/ml_context.cc
index 3b3c5e59..ce6b0ce 100644
--- a/third_party/blink/renderer/modules/ml/ml_context.cc
+++ b/third_party/blink/renderer/modules/ml/ml_context.cc
@@ -176,6 +176,11 @@
     return EmptyPromise();
   }
 
+  LogConsoleWarning(script_state,
+                    "WARNING: MLContext.compute() is deprecated. Use "
+                    "MLContext.dispatch() instead.",
+                    mojom::blink::ConsoleMessageSource::kDeprecation);
+
   return graph->Compute(std::move(scoped_trace), inputs, outputs, script_state,
                         exception_state);
 }
diff --git a/third_party/blink/renderer/modules/ml/webnn/ml_graph_utils.cc b/third_party/blink/renderer/modules/ml/webnn/ml_graph_utils.cc
index 369d558..4c778bdd 100644
--- a/third_party/blink/renderer/modules/ml/webnn/ml_graph_utils.cc
+++ b/third_party/blink/renderer/modules/ml/webnn/ml_graph_utils.cc
@@ -6,6 +6,7 @@
 
 #include <numeric>
 
+#include "third_party/blink/public/mojom/devtools/console_message.mojom-blink-forward.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_ml_gemm_options.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
 #include "third_party/blink/renderer/core/inspector/console_message.h"
@@ -409,21 +410,23 @@
       LogConsoleWarning(
           script_state,
           "WARNING: MLOperandDescriptor.dimensions is deprecated. "
-          "Use MLOperandDescriptor.shape instead.");
+          "Use MLOperandDescriptor.shape instead.",
+          mojom::blink::ConsoleMessageSource::kDeprecation);
     }
   }
 
   return desc.dimensions();
 }
 
-void LogConsoleWarning(ScriptState* script_state, const String& message) {
+void LogConsoleWarning(ScriptState* script_state,
+                       const String& message,
+                       mojom::blink::ConsoleMessageSource message_source) {
   ExecutionContext* execution_context = ExecutionContext::From(script_state);
   if (!execution_context) {
     return;
   }
   execution_context->AddConsoleMessage(MakeGarbageCollected<ConsoleMessage>(
-      mojom::blink::ConsoleMessageSource::kJavaScript,
-      mojom::blink::ConsoleMessageLevel::kWarning, message));
+      message_source, mojom::blink::ConsoleMessageLevel::kWarning, message));
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/ml/webnn/ml_graph_utils.h b/third_party/blink/renderer/modules/ml/webnn/ml_graph_utils.h
index b0ef099..45b228b 100644
--- a/third_party/blink/renderer/modules/ml/webnn/ml_graph_utils.h
+++ b/third_party/blink/renderer/modules/ml/webnn/ml_graph_utils.h
@@ -12,6 +12,7 @@
 #include "services/webnn/public/cpp/graph_validation_utils.h"
 #include "services/webnn/public/cpp/operand_descriptor.h"
 #include "services/webnn/public/mojom/webnn_graph.mojom-blink.h"
+#include "third_party/blink/public/mojom/devtools/console_message.mojom-blink.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_ml_conv_2d_filter_operand_layout.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_ml_conv_transpose_2d_options.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_ml_input_operand_layout.h"
@@ -116,8 +117,11 @@
 GetShapeFromDescriptor(ScriptState* script_state,
                        const MLOperandDescriptor& desc);
 
-MODULES_EXPORT void LogConsoleWarning(ScriptState* script_state,
-                                      const String& message);
+MODULES_EXPORT void LogConsoleWarning(
+    ScriptState* script_state,
+    const String& message,
+    mojom::blink::ConsoleMessageSource message_source =
+        mojom::blink::ConsoleMessageSource::kJavaScript);
 
 }  // namespace blink
 
diff --git a/third_party/blink/renderer/modules/webgl/webgl_context_attribute_helpers.cc b/third_party/blink/renderer/modules/webgl/webgl_context_attribute_helpers.cc
index b2e92fb5..51ee8ec 100644
--- a/third_party/blink/renderer/modules/webgl/webgl_context_attribute_helpers.cc
+++ b/third_party/blink/renderer/modules/webgl/webgl_context_attribute_helpers.cc
@@ -9,6 +9,18 @@
 
 namespace blink {
 
+V8WebGLPowerPreference::Enum ToGLPowerPreference(
+    CanvasContextCreationAttributesCore::PowerPreference power_preference) {
+  switch (power_preference) {
+    case CanvasContextCreationAttributesCore::PowerPreference::kDefault:
+      return V8WebGLPowerPreference::Enum::kDefault;
+    case CanvasContextCreationAttributesCore::PowerPreference::kLowPower:
+      return V8WebGLPowerPreference::Enum::kLowPower;
+    case CanvasContextCreationAttributesCore::PowerPreference::kHighPerformance:
+      return V8WebGLPowerPreference::Enum::kHighPerformance;
+  }
+}
+
 WebGLContextAttributes* ToWebGLContextAttributes(
     const CanvasContextCreationAttributesCore& attrs) {
   WebGLContextAttributes* result = WebGLContextAttributes::Create();
@@ -18,7 +30,7 @@
   result->setAntialias(attrs.antialias);
   result->setPremultipliedAlpha(attrs.premultiplied_alpha);
   result->setPreserveDrawingBuffer(attrs.preserve_drawing_buffer);
-  result->setPowerPreference(attrs.power_preference);
+  result->setPowerPreference(ToGLPowerPreference(attrs.power_preference));
   result->setFailIfMajorPerformanceCaveat(
       attrs.fail_if_major_performance_caveat);
   result->setXrCompatible(attrs.xr_compatible);
@@ -39,10 +51,13 @@
   return result;
 }
 
-gl::GpuPreference PowerPreferenceToGpuPreference(String power_preference) {
+gl::GpuPreference PowerPreferenceToGpuPreference(
+    CanvasContextCreationAttributesCore::PowerPreference power_preference) {
   // This code determines the handling of the "default" power preference.
-  if (power_preference == "high-performance")
+  if (power_preference ==
+      CanvasContextCreationAttributesCore::PowerPreference::kHighPerformance) {
     return gl::GpuPreference::kHighPerformance;
+  }
   return gl::GpuPreference::kLowPower;
 }
 
diff --git a/third_party/blink/renderer/modules/webgl/webgl_context_attribute_helpers.h b/third_party/blink/renderer/modules/webgl/webgl_context_attribute_helpers.h
index 1a02fe7..d8e21b9c5 100644
--- a/third_party/blink/renderer/modules/webgl/webgl_context_attribute_helpers.h
+++ b/third_party/blink/renderer/modules/webgl/webgl_context_attribute_helpers.h
@@ -29,7 +29,8 @@
 // gl::GpuPreference enum which is sent along with GPU switching
 // notifications. This must not return the kDefault constant, but
 // choose either the low-power or high-performance GPU.
-gl::GpuPreference PowerPreferenceToGpuPreference(String power_preference);
+gl::GpuPreference PowerPreferenceToGpuPreference(
+    CanvasContextCreationAttributesCore::PowerPreference power_preference);
 
 }  // namespace blink
 
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_request_adapter_options.idl b/third_party/blink/renderer/modules/webgpu/gpu_request_adapter_options.idl
index a0d090a..f194147 100644
--- a/third_party/blink/renderer/modules/webgpu/gpu_request_adapter_options.idl
+++ b/third_party/blink/renderer/modules/webgpu/gpu_request_adapter_options.idl
@@ -10,7 +10,7 @@
 };
 
 dictionary GPURequestAdapterOptions {
-    any featureLevel;
+    [RuntimeEnabled=WebGPUExperimentalFeatures] DOMString featureLevel;
     GPUPowerPreference powerPreference;
     boolean forceFallbackAdapter = false;
     [RuntimeEnabled=WebGPUExperimentalFeatures] boolean compatibilityMode = false;
diff --git a/third_party/blink/renderer/modules/webusb/usb_device.cc b/third_party/blink/renderer/modules/webusb/usb_device.cc
index a620da73..61c95901 100644
--- a/third_party/blink/renderer/modules/webusb/usb_device.cc
+++ b/third_party/blink/renderer/modules/webusb/usb_device.cc
@@ -17,6 +17,7 @@
 #include "third_party/blink/renderer/bindings/core/v8/to_v8_traits.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_union_arraybuffer_arraybufferview.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_usb_control_transfer_parameters.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_usb_direction.h"
 #include "third_party/blink/renderer/core/dom/dom_exception.h"
 #include "third_party/blink/renderer/core/inspector/console_message.h"
 #include "third_party/blink/renderer/core/typed_arrays/dom_array_buffer.h"
@@ -100,18 +101,21 @@
   }
 }
 
-String ConvertTransferStatus(const UsbTransferStatus& status) {
+V8USBTransferStatus ConvertTransferStatus(const UsbTransferStatus& status) {
   switch (status) {
     case UsbTransferStatus::COMPLETED:
     case UsbTransferStatus::SHORT_PACKET:
-      return "ok";
+      return V8USBTransferStatus(V8USBTransferStatus::Enum::kOk);
     case UsbTransferStatus::STALLED:
-      return "stall";
+      return V8USBTransferStatus(V8USBTransferStatus::Enum::kStall);
     case UsbTransferStatus::BABBLE:
-      return "babble";
-    default:
-      NOTREACHED_IN_MIGRATION();
-      return "";
+      return V8USBTransferStatus(V8USBTransferStatus::Enum::kBabble);
+    case UsbTransferStatus::TRANSFER_ERROR:
+    case UsbTransferStatus::PERMISSION_DENIED:
+    case UsbTransferStatus::TIMEOUT:
+    case UsbTransferStatus::CANCELLED:
+    case UsbTransferStatus::DISCONNECT:
+      NOTREACHED();
   }
 }
 
@@ -513,13 +517,15 @@
 
 ScriptPromise<IDLUndefined> USBDevice::clearHalt(
     ScriptState* script_state,
-    String direction,
+    const V8USBDirection& direction,
     uint8_t endpoint_number,
     ExceptionState& exception_state) {
-  UsbTransferDirection mojo_direction = direction == "in"
-                                            ? UsbTransferDirection::INBOUND
-                                            : UsbTransferDirection::OUTBOUND;
-  EnsureEndpointAvailable(direction == "in", endpoint_number, exception_state);
+  UsbTransferDirection mojo_direction =
+      direction.AsEnum() == V8USBDirection::Enum::kIn
+          ? UsbTransferDirection::INBOUND
+          : UsbTransferDirection::OUTBOUND;
+  EnsureEndpointAvailable(mojo_direction == UsbTransferDirection::INBOUND,
+                          endpoint_number, exception_state);
   if (exception_state.HadException())
     return EmptyPromise();
 
diff --git a/third_party/blink/renderer/modules/webusb/usb_device.h b/third_party/blink/renderer/modules/webusb/usb_device.h
index 08d52ee..d1d7d59 100644
--- a/third_party/blink/renderer/modules/webusb/usb_device.h
+++ b/third_party/blink/renderer/modules/webusb/usb_device.h
@@ -30,6 +30,7 @@
 class USBIsochronousInTransferResult;
 class USBIsochronousOutTransferResult;
 class USBOutTransferResult;
+class V8USBDirection;
 
 class USBDevice : public ScriptWrappable,
                   public ExecutionContextLifecycleObserver {
@@ -102,7 +103,7 @@
       const DOMArrayPiece& optional_data,
       ExceptionState&);
   ScriptPromise<IDLUndefined> clearHalt(ScriptState*,
-                                        String direction,
+                                        const V8USBDirection& direction,
                                         uint8_t endpoint_number,
                                         ExceptionState&);
   ScriptPromise<USBInTransferResult> transferIn(ScriptState*,
diff --git a/third_party/blink/renderer/modules/webusb/usb_in_transfer_result.h b/third_party/blink/renderer/modules/webusb/usb_in_transfer_result.h
index c0047f93..9609945a 100644
--- a/third_party/blink/renderer/modules/webusb/usb_in_transfer_result.h
+++ b/third_party/blink/renderer/modules/webusb/usb_in_transfer_result.h
@@ -5,6 +5,7 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_WEBUSB_USB_IN_TRANSFER_RESULT_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_WEBUSB_USB_IN_TRANSFER_RESULT_H_
 
+#include "third_party/blink/renderer/bindings/modules/v8/v8_usb_transfer_status.h"
 #include "third_party/blink/renderer/core/typed_arrays/array_buffer_view_helpers.h"
 #include "third_party/blink/renderer/core/typed_arrays/dom_array_buffer.h"
 #include "third_party/blink/renderer/core/typed_arrays/dom_data_view.h"
@@ -19,28 +20,28 @@
   DEFINE_WRAPPERTYPEINFO();
 
  public:
-  static USBInTransferResult* Create(const String& status,
+  static USBInTransferResult* Create(const V8USBTransferStatus& status,
                                      base::span<const uint8_t> data) {
     DOMDataView* data_view =
         DOMDataView::Create(DOMArrayBuffer::Create(data), 0, data.size());
     return MakeGarbageCollected<USBInTransferResult>(status, data_view);
   }
 
-  static USBInTransferResult* Create(const String& status) {
+  static USBInTransferResult* Create(const V8USBTransferStatus& status) {
     return MakeGarbageCollected<USBInTransferResult>(status, nullptr);
   }
 
-  static USBInTransferResult* Create(const String& status,
+  static USBInTransferResult* Create(const V8USBTransferStatus& status,
                                      NotShared<DOMDataView> data) {
     return MakeGarbageCollected<USBInTransferResult>(status, data.Get());
   }
 
-  USBInTransferResult(const String& status, DOMDataView* data)
+  USBInTransferResult(const V8USBTransferStatus& status, DOMDataView* data)
       : status_(status), data_(data) {}
 
   ~USBInTransferResult() override = default;
 
-  String status() const { return status_; }
+  V8USBTransferStatus status() const { return status_; }
   DOMDataView* data() const { return data_.Get(); }
 
   void Trace(Visitor* visitor) const override {
@@ -49,7 +50,7 @@
   }
 
  private:
-  const String status_;
+  const V8USBTransferStatus status_;
   const NotShared<DOMDataView> data_;
 };
 
diff --git a/third_party/blink/renderer/modules/webusb/usb_isochronous_in_transfer_packet.h b/third_party/blink/renderer/modules/webusb/usb_isochronous_in_transfer_packet.h
index 25191efd..d0bb6ed7 100644
--- a/third_party/blink/renderer/modules/webusb/usb_isochronous_in_transfer_packet.h
+++ b/third_party/blink/renderer/modules/webusb/usb_isochronous_in_transfer_packet.h
@@ -5,6 +5,7 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_WEBUSB_USB_ISOCHRONOUS_IN_TRANSFER_PACKET_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_WEBUSB_USB_ISOCHRONOUS_IN_TRANSFER_PACKET_H_
 
+#include "third_party/blink/renderer/bindings/modules/v8/v8_usb_transfer_status.h"
 #include "third_party/blink/renderer/core/typed_arrays/array_buffer_view_helpers.h"
 #include "third_party/blink/renderer/core/typed_arrays/dom_data_view.h"
 #include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
@@ -17,22 +18,25 @@
   DEFINE_WRAPPERTYPEINFO();
 
  public:
-  static USBIsochronousInTransferPacket* Create(const String& status) {
+  static USBIsochronousInTransferPacket* Create(
+      const V8USBTransferStatus& status) {
     return MakeGarbageCollected<USBIsochronousInTransferPacket>(status,
                                                                 nullptr);
   }
 
-  static USBIsochronousInTransferPacket* Create(const String& status,
-                                                NotShared<DOMDataView> data) {
+  static USBIsochronousInTransferPacket* Create(
+      const V8USBTransferStatus& status,
+      NotShared<DOMDataView> data) {
     return MakeGarbageCollected<USBIsochronousInTransferPacket>(status,
                                                                 data.Get());
   }
 
-  USBIsochronousInTransferPacket(const String& status, DOMDataView* data)
+  USBIsochronousInTransferPacket(const V8USBTransferStatus& status,
+                                 DOMDataView* data)
       : status_(status), data_(data) {}
   ~USBIsochronousInTransferPacket() override = default;
 
-  String status() const { return status_; }
+  V8USBTransferStatus status() const { return status_; }
   DOMDataView* data() const { return data_.Get(); }
 
   void Trace(Visitor* visitor) const override {
@@ -41,7 +45,7 @@
   }
 
  private:
-  const String status_;
+  const V8USBTransferStatus status_;
   const Member<DOMDataView> data_;
 };
 
diff --git a/third_party/blink/renderer/modules/webusb/usb_isochronous_out_transfer_packet.h b/third_party/blink/renderer/modules/webusb/usb_isochronous_out_transfer_packet.h
index 4782f2b..46f95248 100644
--- a/third_party/blink/renderer/modules/webusb/usb_isochronous_out_transfer_packet.h
+++ b/third_party/blink/renderer/modules/webusb/usb_isochronous_out_transfer_packet.h
@@ -5,9 +5,9 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_WEBUSB_USB_ISOCHRONOUS_OUT_TRANSFER_PACKET_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_WEBUSB_USB_ISOCHRONOUS_OUT_TRANSFER_PACKET_H_
 
+#include "third_party/blink/renderer/bindings/modules/v8/v8_usb_transfer_status.h"
 #include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
 #include "third_party/blink/renderer/platform/heap/garbage_collected.h"
-#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
 
 namespace blink {
 
@@ -15,26 +15,29 @@
   DEFINE_WRAPPERTYPEINFO();
 
  public:
-  static USBIsochronousOutTransferPacket* Create(const String& status) {
+  static USBIsochronousOutTransferPacket* Create(
+      const V8USBTransferStatus& status) {
     return MakeGarbageCollected<USBIsochronousOutTransferPacket>(status, 0);
   }
 
-  static USBIsochronousOutTransferPacket* Create(const String& status,
-                                                 unsigned bytes_written) {
+  static USBIsochronousOutTransferPacket* Create(
+      const V8USBTransferStatus& status,
+      unsigned bytes_written) {
     return MakeGarbageCollected<USBIsochronousOutTransferPacket>(status,
                                                                  bytes_written);
   }
 
-  USBIsochronousOutTransferPacket(const String& status, unsigned bytes_written)
+  USBIsochronousOutTransferPacket(const V8USBTransferStatus& status,
+                                  unsigned bytes_written)
       : status_(status), bytes_written_(bytes_written) {}
 
   ~USBIsochronousOutTransferPacket() override = default;
 
-  String status() const { return status_; }
+  V8USBTransferStatus status() const { return status_; }
   unsigned bytesWritten() const { return bytes_written_; }
 
  private:
-  const String status_;
+  const V8USBTransferStatus status_;
   const unsigned bytes_written_;
 };
 
diff --git a/third_party/blink/renderer/modules/webusb/usb_out_transfer_result.h b/third_party/blink/renderer/modules/webusb/usb_out_transfer_result.h
index be1c618..843ea139 100644
--- a/third_party/blink/renderer/modules/webusb/usb_out_transfer_result.h
+++ b/third_party/blink/renderer/modules/webusb/usb_out_transfer_result.h
@@ -15,25 +15,26 @@
   DEFINE_WRAPPERTYPEINFO();
 
  public:
-  static USBOutTransferResult* Create(const String& status) {
+  static USBOutTransferResult* Create(const V8USBTransferStatus& status) {
     return MakeGarbageCollected<USBOutTransferResult>(status, 0);
   }
 
-  static USBOutTransferResult* Create(const String& status,
+  static USBOutTransferResult* Create(const V8USBTransferStatus& status,
                                       uint32_t bytes_written) {
     return MakeGarbageCollected<USBOutTransferResult>(status, bytes_written);
   }
 
-  USBOutTransferResult(const String& status, uint32_t bytes_written)
+  USBOutTransferResult(const V8USBTransferStatus& status,
+                       uint32_t bytes_written)
       : status_(status), bytes_written_(bytes_written) {}
 
   ~USBOutTransferResult() override = default;
 
-  String status() const { return status_; }
+  V8USBTransferStatus status() const { return status_; }
   uint32_t bytesWritten() const { return bytes_written_; }
 
  private:
-  const String status_;
+  const V8USBTransferStatus status_;
   const uint32_t bytes_written_;
 };
 
diff --git a/third_party/blink/renderer/modules/xr/xr_reference_space.cc b/third_party/blink/renderer/modules/xr/xr_reference_space.cc
index e85ce24..51c745b 100644
--- a/third_party/blink/renderer/modules/xr/xr_reference_space.cc
+++ b/third_party/blink/renderer/modules/xr/xr_reference_space.cc
@@ -21,21 +21,20 @@
 // Rough estimate of avg human eye height in meters.
 const double kDefaultEmulationHeightMeters = -1.6;
 
-ReferenceSpaceType XRReferenceSpace::StringToReferenceSpaceType(
-    const String& reference_space_type) {
-  if (reference_space_type == "viewer") {
-    return ReferenceSpaceType::kViewer;
-  } else if (reference_space_type == "local") {
-    return ReferenceSpaceType::kLocal;
-  } else if (reference_space_type == "local-floor") {
-    return ReferenceSpaceType::kLocalFloor;
-  } else if (reference_space_type == "bounded-floor") {
-    return ReferenceSpaceType::kBoundedFloor;
-  } else if (reference_space_type == "unbounded") {
-    return ReferenceSpaceType::kUnbounded;
+ReferenceSpaceType XRReferenceSpace::V8EnumToReferenceSpaceType(
+    V8XRReferenceSpaceType::Enum reference_space_type) {
+  switch (reference_space_type) {
+    case V8XRReferenceSpaceType::Enum::kViewer:
+      return ReferenceSpaceType::kViewer;
+    case V8XRReferenceSpaceType::Enum::kLocal:
+      return ReferenceSpaceType::kLocal;
+    case V8XRReferenceSpaceType::Enum::kLocalFloor:
+      return ReferenceSpaceType::kLocalFloor;
+    case V8XRReferenceSpaceType::Enum::kBoundedFloor:
+      return ReferenceSpaceType::kBoundedFloor;
+    case V8XRReferenceSpaceType::Enum::kUnbounded:
+      return ReferenceSpaceType::kUnbounded;
   }
-  NOTREACHED_IN_MIGRATION();
-  return ReferenceSpaceType::kViewer;
 }
 
 // origin offset starts as identity transform
diff --git a/third_party/blink/renderer/modules/xr/xr_reference_space.h b/third_party/blink/renderer/modules/xr/xr_reference_space.h
index 28f797c..f3fd18a9 100644
--- a/third_party/blink/renderer/modules/xr/xr_reference_space.h
+++ b/third_party/blink/renderer/modules/xr/xr_reference_space.h
@@ -9,6 +9,7 @@
 #include <string>
 
 #include "device/vr/public/mojom/vr_service.mojom-blink.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_xr_reference_space_type.h"
 #include "third_party/blink/renderer/modules/xr/xr_space.h"
 #include "ui/gfx/geometry/transform.h"
 
@@ -20,8 +21,8 @@
   DEFINE_WRAPPERTYPEINFO();
 
  public:
-  static device::mojom::blink::XRReferenceSpaceType StringToReferenceSpaceType(
-      const String& reference_space_type);
+  static device::mojom::blink::XRReferenceSpaceType V8EnumToReferenceSpaceType(
+      V8XRReferenceSpaceType::Enum reference_space_type);
 
   XRReferenceSpace(XRSession* session,
                    device::mojom::blink::XRReferenceSpaceType type);
diff --git a/third_party/blink/renderer/modules/xr/xr_session.cc b/third_party/blink/renderer/modules/xr/xr_session.cc
index f6fa59c..01b246a 100644
--- a/third_party/blink/renderer/modules/xr/xr_session.cc
+++ b/third_party/blink/renderer/modules/xr/xr_session.cc
@@ -651,9 +651,9 @@
 
 ScriptPromise<XRReferenceSpace> XRSession::requestReferenceSpace(
     ScriptState* script_state,
-    const String& type,
+    const V8XRReferenceSpaceType& type,
     ExceptionState& exception_state) {
-  DVLOG(2) << __func__ << ": type=" << type;
+  DVLOG(2) << __func__ << ": type=" << type.AsCStr();
 
   if (ended_) {
     exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
@@ -662,7 +662,7 @@
   }
 
   device::mojom::blink::XRReferenceSpaceType requested_type =
-      XRReferenceSpace::StringToReferenceSpaceType(type);
+      XRReferenceSpace::V8EnumToReferenceSpaceType(type.AsEnum());
 
   if (sensorless_session_ &&
       requested_type != device::mojom::blink::XRReferenceSpaceType::kViewer) {
@@ -686,7 +686,7 @@
   }
 
   if (!IsFeatureEnabled(type_as_feature.value())) {
-    DVLOG(2) << __func__ << ": feature not enabled, type=" << type;
+    DVLOG(2) << __func__ << ": feature not enabled, type=" << type.AsCStr();
     exception_state.ThrowDOMException(DOMExceptionCode::kNotSupportedError,
                                       kReferenceSpaceNotSupported);
     return EmptyPromise();
diff --git a/third_party/blink/renderer/modules/xr/xr_session.h b/third_party/blink/renderer/modules/xr/xr_session.h
index e01a1c09..e4dd689 100644
--- a/third_party/blink/renderer/modules/xr/xr_session.h
+++ b/third_party/blink/renderer/modules/xr/xr_session.h
@@ -44,6 +44,7 @@
 class HTMLCanvasElement;
 class ResizeObserver;
 class V8XRFrameRequestCallback;
+class V8XRReferenceSpaceType;
 class XRAnchor;
 class XRAnchorSet;
 class XRCanvasInputProvider;
@@ -182,7 +183,7 @@
 
   ScriptPromise<XRReferenceSpace> requestReferenceSpace(
       ScriptState* script_state,
-      const String& type,
+      const V8XRReferenceSpaceType& type,
       ExceptionState&);
 
   // Helper, not IDL-exposed
diff --git a/third_party/blink/renderer/modules/xr/xr_system.cc b/third_party/blink/renderer/modules/xr/xr_system.cc
index c030a8af..d879748f 100644
--- a/third_party/blink/renderer/modules/xr/xr_system.cc
+++ b/third_party/blink/renderer/modules/xr/xr_system.cc
@@ -18,6 +18,8 @@
 #include "third_party/blink/public/platform/browser_interface_broker_proxy.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_xr_depth_state_init.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_xr_reference_space_type.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_xr_session_mode.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_xr_tracked_image_init.h"
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/dom/dom_exception.h"
@@ -98,20 +100,16 @@
     device::mojom::XRSessionFeature::REF_SPACE_VIEWER,
 };
 
-device::mojom::blink::XRSessionMode stringToSessionMode(
-    const String& mode_string) {
-  if (mode_string == "inline") {
-    return device::mojom::blink::XRSessionMode::kInline;
+device::mojom::blink::XRSessionMode V8EnumToSessionMode(
+    V8XRSessionMode::Enum mode) {
+  switch (mode) {
+    case V8XRSessionMode::Enum::kInline:
+      return device::mojom::blink::XRSessionMode::kInline;
+    case V8XRSessionMode::Enum::kImmersiveVr:
+      return device::mojom::blink::XRSessionMode::kImmersiveVr;
+    case V8XRSessionMode::Enum::kImmersiveAr:
+      return device::mojom::blink::XRSessionMode::kImmersiveAr;
   }
-  if (mode_string == "immersive-vr") {
-    return device::mojom::blink::XRSessionMode::kImmersiveVr;
-  }
-  if (mode_string == "immersive-ar") {
-    return device::mojom::blink::XRSessionMode::kImmersiveAr;
-  }
-
-  NOTREACHED_IN_MIGRATION();  // Only strings in the enum are allowed by IDL.
-  return device::mojom::blink::XRSessionMode::kInline;
 }
 
 const char* SessionModeToString(device::mojom::blink::XRSessionMode mode) {
@@ -123,9 +121,6 @@
     case device::mojom::blink::XRSessionMode::kImmersiveAr:
       return "immersive-ar";
   }
-
-  NOTREACHED_IN_MIGRATION();
-  return "";
 }
 
 device::mojom::XRDepthUsage ParseDepthUsage(const V8XRDepthUsage& usage) {
@@ -905,7 +900,7 @@
 
 ScriptPromise<IDLUndefined> XRSystem::supportsSession(
     ScriptState* script_state,
-    const String& mode,
+    const V8XRSessionMode& mode,
     ExceptionState& exception_state) {
   auto* resolver = MakeGarbageCollected<ScriptPromiseResolver<IDLUndefined>>(
       script_state, exception_state.GetContext());
@@ -916,7 +911,7 @@
 
 ScriptPromise<IDLBoolean> XRSystem::isSessionSupported(
     ScriptState* script_state,
-    const String& mode,
+    const V8XRSessionMode& mode,
     ExceptionState& exception_state) {
   auto* resolver = MakeGarbageCollected<ScriptPromiseResolver<IDLBoolean>>(
       script_state, exception_state.GetContext());
@@ -954,7 +949,7 @@
 }
 
 void XRSystem::InternalIsSessionSupported(ScriptPromiseResolverBase* resolver,
-                                          const String& mode,
+                                          const V8XRSessionMode& mode,
                                           ExceptionState& exception_state,
                                           bool throw_on_unsupported) {
   if (!GetExecutionContext()) {
@@ -964,7 +959,8 @@
     return;  // Promise will be rejected by generated bindings
   }
 
-  device::mojom::blink::XRSessionMode session_mode = stringToSessionMode(mode);
+  device::mojom::blink::XRSessionMode session_mode =
+      V8EnumToSessionMode(mode.AsEnum());
   PendingSupportsSessionQuery* query =
       MakeGarbageCollected<PendingSupportsSessionQuery>(resolver, session_mode,
                                                         throw_on_unsupported);
@@ -1230,7 +1226,7 @@
 
 ScriptPromise<XRSession> XRSystem::requestSession(
     ScriptState* script_state,
-    const String& mode,
+    const V8XRSessionMode& mode,
     XRSessionInit* session_init,
     ExceptionState& exception_state) {
   DVLOG(2) << __func__;
@@ -1248,7 +1244,8 @@
                             // bindings
   }
 
-  device::mojom::blink::XRSessionMode session_mode = stringToSessionMode(mode);
+  device::mojom::blink::XRSessionMode session_mode =
+      V8EnumToSessionMode(mode.AsEnum());
 
   // If the request is for immersive-ar, ensure that feature is enabled.
   if (session_mode == device::mojom::blink::XRSessionMode::kImmersiveAr &&
diff --git a/third_party/blink/renderer/modules/xr/xr_system.h b/third_party/blink/renderer/modules/xr/xr_system.h
index 029da5a..3d6796f 100644
--- a/third_party/blink/renderer/modules/xr/xr_system.h
+++ b/third_party/blink/renderer/modules/xr/xr_system.h
@@ -37,6 +37,7 @@
 namespace blink {
 
 class Navigator;
+class V8XRSessionMode;
 class XRFrameProvider;
 class XRSession;
 class XRSessionInit;
@@ -88,13 +89,13 @@
   DEFINE_ATTRIBUTE_EVENT_LISTENER(devicechange, kDevicechange)
 
   ScriptPromise<IDLUndefined> supportsSession(ScriptState*,
-                                              const String&,
+                                              const V8XRSessionMode&,
                                               ExceptionState& exception_state);
   ScriptPromise<IDLBoolean> isSessionSupported(ScriptState*,
-                                               const String&,
+                                               const V8XRSessionMode&,
                                                ExceptionState& exception_state);
   ScriptPromise<XRSession> requestSession(ScriptState*,
-                                          const String&,
+                                          const V8XRSessionMode&,
                                           XRSessionInit*,
                                           ExceptionState& exception_state);
 
@@ -373,7 +374,7 @@
                          const String& message);
 
   void InternalIsSessionSupported(ScriptPromiseResolverBase*,
-                                  const String&,
+                                  const V8XRSessionMode&,
                                   ExceptionState& exception_state,
                                   bool throw_on_unsupported);
 
diff --git a/third_party/blink/renderer/platform/exported/web_vector_test.cc b/third_party/blink/renderer/platform/exported/web_vector_test.cc
index 20601dc..2ee4afac 100644
--- a/third_party/blink/renderer/platform/exported/web_vector_test.cc
+++ b/third_party/blink/renderer/platform/exported/web_vector_test.cc
@@ -57,7 +57,7 @@
   WebVector<int> vector;
   ASSERT_TRUE(vector.empty());
   int value = 1;
-  vector.Assign(&value, 1);
+  vector.Assign(base::span_from_ref(value));
   ASSERT_EQ(1u, vector.size());
   ASSERT_FALSE(vector.empty());
 }
diff --git a/third_party/blink/renderer/platform/image-decoders/segment_stream.cc b/third_party/blink/renderer/platform/image-decoders/segment_stream.cc
index 528568e..e829d78 100644
--- a/third_party/blink/renderer/platform/image-decoders/segment_stream.cc
+++ b/third_party/blink/renderer/platform/image-decoders/segment_stream.cc
@@ -15,14 +15,18 @@
 
 namespace blink {
 
-SegmentStream::SegmentStream() = default;
+SegmentStream::SegmentStream(size_t reading_offset)
+    : position_(reading_offset), reading_offset_(reading_offset) {}
 
 SegmentStream::SegmentStream(SegmentStream&& rhs)
-    : reader_(std::move(rhs.reader_)), position_(rhs.position_) {}
+    : reader_(std::move(rhs.reader_)),
+      position_(rhs.position_),
+      reading_offset_(rhs.reading_offset_) {}
 
 SegmentStream& SegmentStream::operator=(SegmentStream&& rhs) {
   reader_ = std::move(rhs.reader_);
   position_ = rhs.position_;
+  reading_offset_ = rhs.reading_offset_;
 
   return *this;
 }
@@ -90,7 +94,7 @@
 }
 
 bool SegmentStream::rewind() {
-  position_ = 0;
+  position_ = reading_offset_;
   return true;
 }
 
@@ -99,11 +103,11 @@
 }
 
 size_t SegmentStream::getPosition() const {
-  return position_;
+  return position_ - reading_offset_;
 }
 
 bool SegmentStream::seek(size_t position) {
-  position_ = position;
+  position_ = reading_offset_ + position;
   return true;
 }
 
diff --git a/third_party/blink/renderer/platform/image-decoders/segment_stream.h b/third_party/blink/renderer/platform/image-decoders/segment_stream.h
index db2d7bd..fff1e216 100644
--- a/third_party/blink/renderer/platform/image-decoders/segment_stream.h
+++ b/third_party/blink/renderer/platform/image-decoders/segment_stream.h
@@ -15,7 +15,7 @@
 
 class PLATFORM_EXPORT SegmentStream : public SkStream {
  public:
-  SegmentStream();
+  explicit SegmentStream(size_t reading_offset = 0);
   SegmentStream(const SegmentStream&) = delete;
   SegmentStream& operator=(const SegmentStream&) = delete;
   SegmentStream(SegmentStream&&);
@@ -43,6 +43,7 @@
  private:
   scoped_refptr<SegmentReader> reader_;
   size_t position_ = 0;
+  size_t reading_offset_ = 0;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/image-decoders/segment_stream_test.cc b/third_party/blink/renderer/platform/image-decoders/segment_stream_test.cc
index 3fe5af26..3eb2d97 100644
--- a/third_party/blink/renderer/platform/image-decoders/segment_stream_test.cc
+++ b/third_party/blink/renderer/platform/image-decoders/segment_stream_test.cc
@@ -606,6 +606,62 @@
   ASSERT_TRUE(segment_stream.hasLength());
 }
 
+TEST(SegmentStreamTest, BasicReadingTest) {
+  scoped_refptr<SharedBuffer> buffer = SharedBuffer::Create();
+  buffer->Append(std::string_view("0123456789"));
+  scoped_refptr<SegmentReader> reader =
+      SegmentReader::CreateFromSharedBuffer(buffer);
+
+  SegmentStream stream;
+  stream.SetReader(reader);
+  EXPECT_EQ(0u, stream.getPosition());
+
+  std::vector<uint8_t> output(1024, 0x00);
+  size_t read_bytes = stream.read(output.data(), output.size());
+  std::string_view result =
+      base::as_string_view(base::span(output).first(read_bytes));
+  EXPECT_EQ("0123456789", result);
+  EXPECT_EQ(10u, stream.getPosition());
+}
+
+TEST(SegmentStreamTest, OffsetReadingTest) {
+  scoped_refptr<SharedBuffer> buffer = SharedBuffer::Create();
+  buffer->Append(std::string_view("0123456789"));
+  scoped_refptr<SegmentReader> reader =
+      SegmentReader::CreateFromSharedBuffer(buffer);
+
+  constexpr size_t kOffset = 5;
+  SegmentStream stream(kOffset);
+  stream.SetReader(reader);
+  EXPECT_EQ(0u, stream.getPosition());
+
+  // Test basic reading.
+  std::vector<uint8_t> output(1024, 0x00);
+  size_t read_bytes = stream.read(output.data(), output.size());
+  std::string_view result =
+      base::as_string_view(base::span(output).first(read_bytes));
+  EXPECT_EQ("56789", result);
+  EXPECT_EQ(5u, stream.getPosition());
+
+  // Test that rewinding takes reading offset into account.
+  ASSERT_TRUE(stream.rewind());
+  EXPECT_EQ(0u, stream.getPosition());
+
+  read_bytes = stream.read(output.data(), output.size());
+  result = base::as_string_view(base::span(output).first(read_bytes));
+  EXPECT_EQ("56789", result);
+  EXPECT_EQ(5u, stream.getPosition());
+
+  // Test that seeking takes reading offset into account.
+  ASSERT_TRUE(stream.seek(2));
+  EXPECT_EQ(2u, stream.getPosition());
+
+  read_bytes = stream.read(output.data(), output.size());
+  result = base::as_string_view(base::span(output).first(read_bytes));
+  EXPECT_EQ("789", result);
+  EXPECT_EQ(5u, stream.getPosition());
+}
+
 namespace {
 
 ::testing::AssertionResult IsCleared(const SegmentStream& segment_stream) {
diff --git a/third_party/blink/renderer/platform/image-decoders/skia/skia_image_decoder_base.cc b/third_party/blink/renderer/platform/image-decoders/skia/skia_image_decoder_base.cc
index 84fd71c..4cd27962 100644
--- a/third_party/blink/renderer/platform/image-decoders/skia/skia_image_decoder_base.cc
+++ b/third_party/blink/renderer/platform/image-decoders/skia/skia_image_decoder_base.cc
@@ -7,6 +7,7 @@
 #include <limits>
 #include <stack>
 
+#include "base/numerics/checked_math.h"
 #include "third_party/blink/renderer/platform/image-decoders/segment_stream.h"
 #include "third_party/skia/include/codec/SkCodec.h"
 #include "third_party/skia/include/codec/SkCodecAnimation.h"
@@ -37,12 +38,14 @@
 
 SkiaImageDecoderBase::SkiaImageDecoderBase(AlphaOption alpha_option,
                                            ColorBehavior color_behavior,
-                                           wtf_size_t max_decoded_bytes)
+                                           wtf_size_t max_decoded_bytes,
+                                           wtf_size_t reading_offset)
     : ImageDecoder(alpha_option,
                    ImageDecoder::kDefaultBitDepth,
                    color_behavior,
                    cc::AuxImage::kDefault,
-                   max_decoded_bytes) {}
+                   max_decoded_bytes),
+      reading_offset_(reading_offset) {}
 
 SkiaImageDecoderBase::~SkiaImageDecoderBase() = default;
 
@@ -60,7 +63,8 @@
   } else {
     DCHECK(!codec_);
 
-    auto segment_stream = std::make_unique<SegmentStream>();
+    auto segment_stream = std::make_unique<SegmentStream>(
+        base::checked_cast<size_t>(reading_offset_));
     SegmentStream* segment_stream_ptr = segment_stream.get();
     segment_stream->SetReader(std::move(data));
 
diff --git a/third_party/blink/renderer/platform/image-decoders/skia/skia_image_decoder_base.h b/third_party/blink/renderer/platform/image-decoders/skia/skia_image_decoder_base.h
index 3d79e46..057e494 100644
--- a/third_party/blink/renderer/platform/image-decoders/skia/skia_image_decoder_base.h
+++ b/third_party/blink/renderer/platform/image-decoders/skia/skia_image_decoder_base.h
@@ -18,9 +18,12 @@
 // Base class for implementing a `blink::ImageDecoder` on top of an `SkCodec`.
 class PLATFORM_EXPORT SkiaImageDecoderBase : public ImageDecoder {
  public:
+  static constexpr wtf_size_t kNoReadingOffset = 0;
+
   SkiaImageDecoderBase(AlphaOption,
                        ColorBehavior,
-                       wtf_size_t max_decoded_bytes);
+                       wtf_size_t max_decoded_bytes,
+                       wtf_size_t reading_offset = kNoReadingOffset);
   SkiaImageDecoderBase(const SkiaImageDecoderBase&) = delete;
   SkiaImageDecoderBase& operator=(const SkiaImageDecoderBase&) = delete;
   ~SkiaImageDecoderBase() override;
@@ -77,6 +80,8 @@
   mutable int repetition_count_ = kAnimationLoopOnce;
   int prior_frame_ = SkCodec::kNoFrame;
   base::flat_set<wtf_size_t> decode_failed_frames_;
+
+  const wtf_size_t reading_offset_ = 0;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index 708d4d4..f91396d 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -1667,20 +1667,12 @@
     {
       // TODO(crbug.fom/41492947) This is the "old" version of getInnerHTML()
       // used for declarative shadow DOM, and this version is deprecated and
-      // currently being removed via Finch. The removal rollout plan is:
-      //   1. (July 29) Disable at 50% of Canary/Dev in M129.
-      //   2. (August 19) Disable at 50% of Canary/Dev/Beta, M129.
-      //   3. (September 16) Disable at 1% of Stable M129.
-      //   4. (September 23) Disable at 2% of Stable M129.
-      //   5. (September 30) Disable at 5% of Stable M129.
-      //   5.5. (September 30) Disable in code, M131, assuming no issues so far.
-      //   6. (October 7) Disable at 10% of Stable M129.
-      //   7. (October 14) Disable at 50% of Stable M129-M130 (Oct 15 stable release).
-      //   8. (October 21) Full disable via Finch, M129-130.
-      // In the meantime, replacement API is called `getHTML()` and that
-      // shipped in M125.
+      // currently being removed via Finch. It has been removed via Finch at
+      // 5+ percent of Stable users, and that ramp will continue. So this
+      // feature is now disabled by default in code, starting M131. This flag
+      // can be removed in M133, assuming no issues. In the meantime,
+      // replacement API is called `getHTML()` and that shipped in M125.
       name: "ElementGetInnerHTML",
-      status: "stable",
     },
     {
       name: "EnforceAnonymityExposure",
diff --git a/third_party/blink/renderer/platform/wtf/text/string_concatenate.cc b/third_party/blink/renderer/platform/wtf/text/string_concatenate.cc
index ea770259..1eca476 100644
--- a/third_party/blink/renderer/platform/wtf/text/string_concatenate.cc
+++ b/third_party/blink/renderer/platform/wtf/text/string_concatenate.cc
@@ -12,31 +12,15 @@
 #include "base/numerics/safe_conversions.h"
 #include "third_party/blink/renderer/platform/wtf/text/string_impl.h"
 
-WTF::StringTypeAdapter<char*>::StringTypeAdapter(char* buffer, size_t length)
+WTF::StringTypeAdapter<const char*>::StringTypeAdapter(const LChar* buffer,
+                                                       size_t length)
     : buffer_(buffer), length_(base::checked_cast<unsigned>(length)) {}
 
-void WTF::StringTypeAdapter<char*>::WriteTo(LChar* destination) const {
-  for (unsigned i = 0; i < length_; ++i)
-    destination[i] = static_cast<LChar>(buffer_[i]);
+void WTF::StringTypeAdapter<const char*>::WriteTo(LChar* destination) const {
+  memcpy(destination, buffer_, length_);
 }
 
-void WTF::StringTypeAdapter<char*>::WriteTo(UChar* destination) const {
-  for (unsigned i = 0; i < length_; ++i) {
-    unsigned char c = buffer_[i];
-    destination[i] = c;
-  }
-}
-
-WTF::StringTypeAdapter<LChar*>::StringTypeAdapter(LChar* buffer)
-    : buffer_(buffer),
-      length_(base::checked_cast<wtf_size_t>(
-          strlen(reinterpret_cast<char*>(buffer)))) {}
-
-void WTF::StringTypeAdapter<LChar*>::WriteTo(LChar* destination) const {
-  memcpy(destination, buffer_, length_ * sizeof(LChar));
-}
-
-void WTF::StringTypeAdapter<LChar*>::WriteTo(UChar* destination) const {
+void WTF::StringTypeAdapter<const char*>::WriteTo(UChar* destination) const {
   StringImpl::CopyChars(destination, buffer_, length_);
 }
 
@@ -47,34 +31,6 @@
   memcpy(destination, buffer_, length_ * sizeof(UChar));
 }
 
-WTF::StringTypeAdapter<const char*>::StringTypeAdapter(const char* buffer)
-    : buffer_(buffer),
-      length_(base::checked_cast<wtf_size_t>(strlen(buffer))) {}
-
-void WTF::StringTypeAdapter<const char*>::WriteTo(LChar* destination) const {
-  memcpy(destination, buffer_, static_cast<size_t>(length_) * sizeof(LChar));
-}
-
-void WTF::StringTypeAdapter<const char*>::WriteTo(UChar* destination) const {
-  for (unsigned i = 0; i < length_; ++i) {
-    unsigned char c = buffer_[i];
-    destination[i] = c;
-  }
-}
-
-WTF::StringTypeAdapter<const LChar*>::StringTypeAdapter(const LChar* buffer)
-    : buffer_(buffer),
-      length_(base::checked_cast<wtf_size_t>(
-          strlen(reinterpret_cast<const char*>(buffer)))) {}
-
-void WTF::StringTypeAdapter<const LChar*>::WriteTo(LChar* destination) const {
-  memcpy(destination, buffer_, static_cast<size_t>(length_) * sizeof(LChar));
-}
-
-void WTF::StringTypeAdapter<const LChar*>::WriteTo(UChar* destination) const {
-  StringImpl::CopyChars(destination, buffer_, length_);
-}
-
 void WTF::StringTypeAdapter<StringView>::WriteTo(LChar* destination) const {
   DCHECK(Is8Bit());
   StringImpl::CopyChars(destination, view_.Characters8(), view_.length());
diff --git a/third_party/blink/renderer/platform/wtf/text/string_concatenate.h b/third_party/blink/renderer/platform/wtf/text/string_concatenate.h
index 3351385..daa4367 100644
--- a/third_party/blink/renderer/platform/wtf/text/string_concatenate.h
+++ b/third_party/blink/renderer/platform/wtf/text/string_concatenate.h
@@ -27,6 +27,8 @@
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_STRING_CONCATENATE_H_
 
 #include <string.h>
+
+#include "base/notreached.h"
 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
 
 #ifndef WTFString_h
@@ -54,24 +56,14 @@
   void WriteTo(UChar* destination) const { *destination = buffer_; }
 
  private:
-  const unsigned char buffer_;
+  const LChar buffer_;
 };
 
 template <>
-class StringTypeAdapter<LChar> {
-  DISALLOW_NEW();
-
+class StringTypeAdapter<LChar> : public StringTypeAdapter<char> {
  public:
-  explicit StringTypeAdapter<LChar>(LChar buffer) : buffer_(buffer) {}
-
-  unsigned length() const { return 1; }
-  bool Is8Bit() const { return true; }
-
-  void WriteTo(LChar* destination) const { *destination = buffer_; }
-  void WriteTo(UChar* destination) const { *destination = buffer_; }
-
- private:
-  const LChar buffer_;
+  explicit StringTypeAdapter<LChar>(LChar buffer)
+      : StringTypeAdapter<char>(buffer) {}
 };
 
 template <>
@@ -96,12 +88,13 @@
 };
 
 template <>
-class WTF_EXPORT StringTypeAdapter<char*> {
+class WTF_EXPORT StringTypeAdapter<const char*> {
   DISALLOW_NEW();
 
  public:
-  explicit StringTypeAdapter<char*>(char* buffer)
-      : StringTypeAdapter(buffer, strlen(buffer)) {}
+  explicit StringTypeAdapter<const char*>(const char* buffer)
+      : StringTypeAdapter(reinterpret_cast<const LChar*>(buffer),
+                          strlen(buffer)) {}
 
   unsigned length() const { return length_; }
   bool Is8Bit() const { return true; }
@@ -110,28 +103,34 @@
   void WriteTo(UChar* destination) const;
 
  private:
-  StringTypeAdapter(char* buffer, size_t length);
+  StringTypeAdapter(const LChar* buffer, size_t length);
 
-  const char* buffer_;
+  const LChar* buffer_;
   unsigned length_;
 };
 
 template <>
-class WTF_EXPORT StringTypeAdapter<LChar*> {
-  DISALLOW_NEW();
-
+class WTF_EXPORT StringTypeAdapter<const LChar*>
+    : StringTypeAdapter<const char*> {
  public:
-  explicit StringTypeAdapter<LChar*>(LChar* buffer);
+  explicit StringTypeAdapter<const LChar*>(const LChar* buffer)
+      : StringTypeAdapter<const char*>(reinterpret_cast<const char*>(buffer)) {}
+};
 
-  unsigned length() const { return length_; }
-  bool Is8Bit() const { return true; }
+template <>
+class WTF_EXPORT StringTypeAdapter<char*>
+    : public StringTypeAdapter<const char*> {
+ public:
+  explicit StringTypeAdapter<char*>(char* buffer)
+      : StringTypeAdapter<const char*>(buffer) {}
+};
 
-  void WriteTo(LChar* destination) const;
-  void WriteTo(UChar* destination) const;
-
- private:
-  const LChar* buffer_;
-  const unsigned length_;
+template <>
+class WTF_EXPORT StringTypeAdapter<LChar*>
+    : public StringTypeAdapter<const LChar*> {
+ public:
+  explicit StringTypeAdapter<LChar*>(LChar* buffer)
+      : StringTypeAdapter<const LChar*>(buffer) {}
 };
 
 template <>
@@ -144,7 +143,7 @@
   unsigned length() const { return length_; }
   bool Is8Bit() const { return false; }
 
-  void WriteTo(LChar*) const { CHECK(false); }
+  void WriteTo(LChar* destination) const { NOTREACHED(); }
   void WriteTo(UChar* destination) const;
 
  private:
@@ -153,42 +152,6 @@
 };
 
 template <>
-class WTF_EXPORT StringTypeAdapter<const char*> {
-  DISALLOW_NEW();
-
- public:
-  explicit StringTypeAdapter<const char*>(const char* buffer);
-
-  unsigned length() const { return length_; }
-  bool Is8Bit() const { return true; }
-
-  void WriteTo(LChar* destination) const;
-  void WriteTo(UChar* destination) const;
-
- private:
-  const char* buffer_;
-  const unsigned length_;
-};
-
-template <>
-class WTF_EXPORT StringTypeAdapter<const LChar*> {
-  DISALLOW_NEW();
-
- public:
-  explicit StringTypeAdapter<const LChar*>(const LChar* buffer);
-
-  unsigned length() const { return length_; }
-  bool Is8Bit() const { return true; }
-
-  void WriteTo(LChar* destination) const;
-  void WriteTo(UChar* destination) const;
-
- private:
-  const LChar* buffer_;
-  const unsigned length_;
-};
-
-template <>
 class WTF_EXPORT StringTypeAdapter<StringView> {
   DISALLOW_NEW();
 
diff --git a/third_party/blink/tools/blinkpy/w3c/test_importer.py b/third_party/blink/tools/blinkpy/w3c/test_importer.py
index 4921002..a692516 100644
--- a/third_party/blink/tools/blinkpy/w3c/test_importer.py
+++ b/third_party/blink/tools/blinkpy/w3c/test_importer.py
@@ -634,7 +634,6 @@
         # Prevent FindIt from auto-reverting import CLs.
         description += 'NOAUTOREVERT=true\n'
         description += 'No-Export: true\n'
-        description += 'Validate-Test-Flakiness: skip\n'
 
         # If this starts blocking the importer unnecessarily, revert
         # https://chromium-review.googlesource.com/c/chromium/src/+/2451504
diff --git a/third_party/blink/tools/blinkpy/w3c/test_importer_unittest.py b/third_party/blink/tools/blinkpy/w3c/test_importer_unittest.py
index 9543db1..157d888 100644
--- a/third_party/blink/tools/blinkpy/w3c/test_importer_unittest.py
+++ b/third_party/blink/tools/blinkpy/w3c/test_importer_unittest.py
@@ -564,7 +564,6 @@
 
                 NOAUTOREVERT=true
                 No-Export: true
-                Validate-Test-Flakiness: skip
                 Cq-Include-Trybots: luci.chromium.try:linux-blink-rel
                 """))
         self.assertEqual(host.executive.calls, [MANIFEST_INSTALL_CMD] +
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 90ae505..2eca43d 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -4129,9 +4129,6 @@
 # Recently became flaky on multiple platforms (Linux and Windows primarily)
 crbug.com/927769 fast/webgl/OffscreenCanvas-webgl-preserveDrawingBuffer.html [ Crash Failure Pass Timeout ]
 
-# Sheriff 2019-02-12
-crbug.com/1072768 media/video-played-ranges-1.html [ Failure Pass Timeout ]
-
 # These started failing when network service was enabled by default.
 crbug.com/933880 external/wpt/service-workers/service-worker/request-end-to-end.https.html [ Failure ]
 crbug.com/933880 http/tests/inspector-protocol/network/xhr-interception-auth-fail.js [ Failure ]
@@ -5979,6 +5976,7 @@
 crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/video-display-toggle.html [ Failure Skip ]
 crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/video-object-fit-change.html [ Failure Skip ]
 crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/video-object-fit.html [ Failure Skip ]
+crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/video-played-ranges-1.html [ Failure Skip ]
 crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/video-replaces-poster.html [ Failure Skip ]
 crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/video-zoom-controls.html [ Failure Skip ]
 crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/controls/video-overlay-cast-dark-rendering.html [ Failure Skip ]
@@ -8097,7 +8095,7 @@
 crbug.com/365432212 virtual/prefetch-reusable/http/tests/inspector-protocol/prefetch/request-will-be-sent-redirect-cross-site.https.js [ Failure Pass ]
 crbug.com/365432212 virtual/prefetch-new-wait-loop/http/tests/inspector-protocol/prefetch/request-will-be-sent-redirect-cross-site.https.js [ Failure Pass ]
 crbug.com/365444428 [ Linux ] fast/events/middleClickAutoscroll-event-fired.html [ Failure Pass ]
-crbug.com/365487889 [ Mac13 ] shadow-dom/focus-navigation/focus-scroller-activeElement-on-event.html [ Failure Pass ]
+crbug.com/363342597 [ Mac13 ] shadow-dom/focus-navigation/focus-scroller-activeElement-on-event.html [ Failure Timeout Pass ]
 
 # Gardener 2024-09-10
 crbug.com/364306254 [ Linux ] fast/events/middleClickAutoscroll-drag.html [ Failure Pass ]
diff --git a/third_party/blink/web_tests/virtual/stable/webexposed/element-instance-property-listing-expected.txt b/third_party/blink/web_tests/virtual/stable/webexposed/element-instance-property-listing-expected.txt
index f422676..9e55e63 100644
--- a/third_party/blink/web_tests/virtual/stable/webexposed/element-instance-property-listing-expected.txt
+++ b/third_party/blink/web_tests/virtual/stable/webexposed/element-instance-property-listing-expected.txt
@@ -122,7 +122,6 @@
     property getElementsByTagName
     property getElementsByTagNameNS
     property getHTML
-    property getInnerHTML
     property getRootNode
     property hasAttribute
     property hasAttributeNS
@@ -1339,7 +1338,6 @@
     property getElementsByTagName
     property getElementsByTagNameNS
     property getHTML
-    property getInnerHTML
     property getRootNode
     property hasAttribute
     property hasAttributeNS
diff --git a/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt b/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt
index 18c3cd5..d0bccf81 100644
--- a/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt
+++ b/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt
@@ -2185,7 +2185,6 @@
     method getElementsByTagName
     method getElementsByTagNameNS
     method getHTML
-    method getInnerHTML
     method hasAttribute
     method hasAttributeNS
     method hasAttributes
@@ -8687,7 +8686,6 @@
     method elementsFromPoint
     method getAnimations
     method getHTML
-    method getInnerHTML
     method getSelection
     method setHTMLUnsafe
     setter adoptedStyleSheets
diff --git a/third_party/boringssl/src b/third_party/boringssl/src
index 72a6050..f8bb652 160000
--- a/third_party/boringssl/src
+++ b/third_party/boringssl/src
@@ -1 +1 @@
-Subproject commit 72a60506ded3407454d6ddc1d848c266020c0c82
+Subproject commit f8bb652b01d3b34a20ddbaaa35def260783ee734
diff --git a/third_party/dav1d/libdav1d b/third_party/dav1d/libdav1d
index a7a40a3..ed004fe 160000
--- a/third_party/dav1d/libdav1d
+++ b/third_party/dav1d/libdav1d
@@ -1 +1 @@
-Subproject commit a7a40a3fde967f227d1ab00370141c491bc8e638
+Subproject commit ed004fe95d47e30e8248764fddbb08b77ba13187
diff --git a/third_party/dav1d/version/vcs_version.h b/third_party/dav1d/version/vcs_version.h
index 3da758b..1cb1111 100644
--- a/third_party/dav1d/version/vcs_version.h
+++ b/third_party/dav1d/version/vcs_version.h
@@ -1,2 +1,2 @@
 /* auto-generated, do not edit */
-#define DAV1D_VERSION "1.4.2-65-ga7a40a3"
+#define DAV1D_VERSION "1.4.2-101-ged004fe"
diff --git a/third_party/depot_tools b/third_party/depot_tools
index b5210fd..6dec852 160000
--- a/third_party/depot_tools
+++ b/third_party/depot_tools
@@ -1 +1 @@
-Subproject commit b5210fdb7491d082965270b650c8080930b91c26
+Subproject commit 6dec85272d23ae587984cdd78eae428ce3b2ad9b
diff --git a/third_party/devtools-frontend-internal b/third_party/devtools-frontend-internal
index 3ed9eea..5ab078e 160000
--- a/third_party/devtools-frontend-internal
+++ b/third_party/devtools-frontend-internal
@@ -1 +1 @@
-Subproject commit 3ed9eeaf4c42ea7a10232f1df5dcd94ea68452b5
+Subproject commit 5ab078e56cd2ba95a134785b8c2ad8d43db28f19
diff --git a/third_party/devtools-frontend/src b/third_party/devtools-frontend/src
index 986c1da7..01c5df5 160000
--- a/third_party/devtools-frontend/src
+++ b/third_party/devtools-frontend/src
@@ -1 +1 @@
-Subproject commit 986c1da746d466f1079e62047f7a176e244b51f4
+Subproject commit 01c5df59684df8989c4926853e9a26f897f38c72
diff --git a/third_party/libaom/README.chromium b/third_party/libaom/README.chromium
index a8e6142..a116fe7 100644
--- a/third_party/libaom/README.chromium
+++ b/third_party/libaom/README.chromium
@@ -2,7 +2,7 @@
 Short Name: libaom
 URL: https://aomedia.googlesource.com/aom/
 Version: N/A
-Revision: 3177866cdd22b0d1392e80681e907b68850903ea
+Revision: 3817481261f8675ef24f327b3dbcdcebbfa389e1
 CPEPrefix: cpe:/a:aomedia:aomedia:3.10.0
 License: BSD
 License File: source/libaom/LICENSE
diff --git a/third_party/libaom/source/config/config/aom_version.h b/third_party/libaom/source/config/config/aom_version.h
index 91ed581..f3bb268 100644
--- a/third_party/libaom/source/config/config/aom_version.h
+++ b/third_party/libaom/source/config/config/aom_version.h
@@ -14,9 +14,9 @@
 #define VERSION_MAJOR 3
 #define VERSION_MINOR 10
 #define VERSION_PATCH 0
-#define VERSION_EXTRA "106-g3177866cdd"
+#define VERSION_EXTRA "110-g3817481261"
 #define VERSION_PACKED \
   ((VERSION_MAJOR << 16) | (VERSION_MINOR << 8) | (VERSION_PATCH))
-#define VERSION_STRING_NOSP "3.10.0-106-g3177866cdd"
-#define VERSION_STRING " 3.10.0-106-g3177866cdd"
+#define VERSION_STRING_NOSP "3.10.0-110-g3817481261"
+#define VERSION_STRING " 3.10.0-110-g3817481261"
 #endif  // AOM_VERSION_H_
diff --git a/third_party/libaom/source/libaom b/third_party/libaom/source/libaom
index 3177866..3817481 160000
--- a/third_party/libaom/source/libaom
+++ b/third_party/libaom/source/libaom
@@ -1 +1 @@
-Subproject commit 3177866cdd22b0d1392e80681e907b68850903ea
+Subproject commit 3817481261f8675ef24f327b3dbcdcebbfa389e1
diff --git a/third_party/libavif/BUILD.gn b/third_party/libavif/BUILD.gn
index 7e55141c10..204be29 100644
--- a/third_party/libavif/BUILD.gn
+++ b/third_party/libavif/BUILD.gn
@@ -79,7 +79,11 @@
   public = [ "src/include/avif/avif.h" ]
   public_configs = [ ":avif_public_config" ]
 
-  sources = libavif_decoder_sources + [ "src/src/write.c" ]
+  sources = libavif_decoder_sources + [
+              "src/src/colrconvert.c",
+              "src/src/gainmap.c",
+              "src/src/write.c",
+            ]
   testonly = true
 
   configs += [ ":avif_config" ]
diff --git a/third_party/libavif/src b/third_party/libavif/src
index 3d4c39c..9100176 160000
--- a/third_party/libavif/src
+++ b/third_party/libavif/src
@@ -1 +1 @@
-Subproject commit 3d4c39cd8f6cb32264ad22616c3125942bd70768
+Subproject commit 9100176307d34dd843f76923a03c0a8b7b1769d7
diff --git a/third_party/perfetto b/third_party/perfetto
index ba40a4d..71aaede 160000
--- a/third_party/perfetto
+++ b/third_party/perfetto
@@ -1 +1 @@
-Subproject commit ba40a4dd8c1e0ccd0f6d5b567802a00b717562b4
+Subproject commit 71aaedeaea2b288f27d67d853fb7c4da9762796e
diff --git a/third_party/webgpu-cts/src b/third_party/webgpu-cts/src
index 1746bcb..9d029d3 160000
--- a/third_party/webgpu-cts/src
+++ b/third_party/webgpu-cts/src
@@ -1 +1 @@
-Subproject commit 1746bcbc10a809cbadb3b131675b885ed08d9da5
+Subproject commit 9d029d337ae1832b5a7b9bf049a87076c12f749d
diff --git a/third_party/webrtc b/third_party/webrtc
index 59ba4ef4..8c09a11 160000
--- a/third_party/webrtc
+++ b/third_party/webrtc
@@ -1 +1 @@
-Subproject commit 59ba4ef4b5502e177d3608a5ad62bcd343eb2a93
+Subproject commit 8c09a115e576e1a82ae7715fbc18ddc45609171e
diff --git a/tools/bisect-builds.py b/tools/bisect-builds.py
index 7eb2fee..baa483b0 100755
--- a/tools/bisect-builds.py
+++ b/tools/bisect-builds.py
@@ -2067,11 +2067,18 @@
 
   build_type_group = parser.add_mutually_exclusive_group()
   build_type_group.add_argument(
+      '-s',
+      dest='build_type',
+      action='store_const',
+      const='snapshot',
+      default='snapshot',
+      help='Bisect across Chromium snapshot archives (default).',
+  )
+  build_type_group.add_argument(
       '-r',
       dest='build_type',
       action='store_const',
       const='release',
-      default='snapshot',
       help='Bisect across release Chrome builds (internal only) instead of '
       'Chromium archives.',
   )
@@ -2269,9 +2276,15 @@
       parser.error('Error: Missing required parameter: --archive')
 
   if opts.archive not in PATH_CONTEXT[opts.build_type]:
-    parser.error(
-        f'Bisecting on {opts.build_type} are only supported on these platforms '
-        f'(-a/--archive): {{{",".join(PATH_CONTEXT[opts.build_type].keys())}}}')
+    supported_build_types = [
+        "%s(%s)" % (b, BuildTypeToCommandLineArgument(b, omit_default=False))
+        for b, context in PATH_CONTEXT.items() if opts.archive in context
+    ]
+    parser.error(f'Bisecting on {opts.build_type} is only supported on these '
+                 'platforms (-a/--archive): '
+                 f'{{{",".join(PATH_CONTEXT[opts.build_type].keys())}}}\n'
+                 f'To bisect for {opts.archive}, please choose from '
+                 f'{", ".join(supported_build_types)}')
 
   if opts.signed and not (opts.archive.startswith('android-')
                           or opts.archive.startswith('ios')):
@@ -2312,6 +2325,23 @@
   return opts
 
 
+def BuildTypeToCommandLineArgument(build_type, omit_default=True):
+  """Convert the build_type back to command line argument."""
+  if build_type == 'release':
+    return '-r'
+  elif build_type == 'official':
+    return '-o'
+  elif build_type == 'snapshot':
+    if not omit_default:
+      return '-s'
+    else:
+      return ''
+  elif build_type == 'asan':
+    return '--asan'
+  else:
+    raise ValueError(f'Unknown build type: {build_type}')
+
+
 def GenerateCommandLine(opts):
   """Generate a command line for bisect options.
 
@@ -2334,10 +2364,7 @@
                                               action='store_true')
   _, remaining_args = parser_to_remove_known_options.parse_known_args()
   args = []
-  if opts.build_type == 'release':
-    args.append('-r')
-  elif opts.build_type == 'official':
-    args.append('-o')
+  args.append(BuildTypeToCommandLineArgument(opts.build_type))
   if opts.archive:
     args.extend(['-a', opts.archive])
   if opts.signed:
diff --git a/tools/bisect_test.py b/tools/bisect_test.py
index 057e2cbe..1d26911 100644
--- a/tools/bisect_test.py
+++ b/tools/bisect_test.py
@@ -1352,7 +1352,8 @@
 
 class MethodTest(BisectTestCase):
 
-  def test_ParseCommandLine(self):
+  @patch('sys.stderr', new_callable=io.StringIO)
+  def test_ParseCommandLine(self, mock_stderr):
     opts = bisect_builds.ParseCommandLine(
         ['-a', 'linux64', '-g', '1', 'args1', 'args2 3', '-b', '2'])
     self.assertEqual(opts.build_type, 'snapshot')
@@ -1366,6 +1367,12 @@
         ['-a', 'linux64', '-g', '1', '--', 'args1', 'args2 3', '-b', '2'])
     self.assertEqual(opts.args, ['args1', 'args2 3', '-b', '2'])
 
+    with self.assertRaises(SystemExit):
+      bisect_builds.ParseCommandLine(['-a', 'mac64', '-o', '-g', '1'])
+      self.assertRegexpMatches(
+          mock_stderr.getvalue(), r'To bisect for mac64, please choose from '
+          r'release(-r), snapshot(-s)')
+
   @patch("urllib.request.urlopen",
          side_effect=[
              urllib.request.HTTPError('url', 404, 'Not Found', None, None),
diff --git a/tools/json_schema_compiler/test/web_idl/basics.idl b/tools/json_schema_compiler/test/web_idl/basics.idl
index 03c91ea..c344bc43 100644
--- a/tools/json_schema_compiler/test/web_idl/basics.idl
+++ b/tools/json_schema_compiler/test/web_idl/basics.idl
@@ -8,6 +8,7 @@
 dictionary ExampleType {
   DOMString someString;
   double someNumber;
+  boolean? optionalBoolean;
 };
 
 interface TestWebIdl {
diff --git a/tools/json_schema_compiler/web_idl_schema.py b/tools/json_schema_compiler/web_idl_schema.py
index 666b1c79..33e254d5 100755
--- a/tools/json_schema_compiler/web_idl_schema.py
+++ b/tools/json_schema_compiler/web_idl_schema.py
@@ -113,11 +113,13 @@
 
   def process(self) -> dict:
     properties = OrderedDict()
-    # TODO(crbug.com/340297705): Add support for optional/nullable types.
+    # TODO(crbug.com/340297705): Add support for optional types.
     # TODO(crbug.com/340297705): Add support for extended attributes on types.
     # TODO(crbug.com/340297705): Add processing of comments to descriptions on
     #                            types.
     properties['name'] = self.name
+    if self.node.GetProperty('NULLABLE'):
+      properties['optional'] = True
     # TODO(crbug.com/340297705): Add support for more types, including TypeRefs.
     basic_type = self.node.GetOneOf('PrimitiveType', 'StringType')
     if basic_type:
diff --git a/tools/json_schema_compiler/web_idl_schema_test.py b/tools/json_schema_compiler/web_idl_schema_test.py
index 6934b39..a6a03196 100755
--- a/tools/json_schema_compiler/web_idl_schema_test.py
+++ b/tools/json_schema_compiler/web_idl_schema_test.py
@@ -86,6 +86,11 @@
                 'someNumber': {
                     'name': 'someNumber',
                     'type': 'number'
+                },
+                'optionalBoolean': {
+                    'name': 'optionalBoolean',
+                    'type': 'boolean',
+                    'optional': True
                 }
             },
             'type': 'object'
diff --git a/tools/json_to_struct/json_to_struct.py b/tools/json_to_struct/json_to_struct.py
index 9ee7ae1..37464da 100755
--- a/tools/json_to_struct/json_to_struct.py
+++ b/tools/json_to_struct/json_to_struct.py
@@ -151,10 +151,8 @@
     if 'generate_array' in description:
       f.write(u'\n')
       f.write(
-          u'extern const %s* const %s[];\n' %
+          u'extern const base::span<const %s* const> %s;\n' %
           (schema['type_name'], description['generate_array']['array_name']))
-      f.write(u'extern const size_t %s;\n' %
-              (description['generate_array']['array_name'] + u'Length'))
 
     if namespace:
       f.write(u'\n')
@@ -196,14 +194,14 @@
     if 'generate_array' in description:
       f.write(u'\n')
       f.write(
-          u'const %s* const %s[] = {\n' %
+          u'const %s* const array_%s[] = {\n' %
           (schema['type_name'], description['generate_array']['array_name']))
       for element_name, _ in description['elements'].items():
         f.write(u'\t&%s,\n' % element_name)
       f.write(u'};\n')
-      f.write(u'const size_t %s = %d;\n' %
-              (description['generate_array']['array_name'] + u'Length',
-               len(description['elements'])))
+      f.write(u'const base::span<const %s* const> %s{array_%s};\n' %
+              (schema['type_name'], description['generate_array']['array_name'],
+               description['generate_array']['array_name']))
 
     if namespace:
       f.write(u'\n')
diff --git a/tools/metrics/actions/actions.xml b/tools/metrics/actions/actions.xml
index 48243f7..7200fda 100644
--- a/tools/metrics/actions/actions.xml
+++ b/tools/metrics/actions/actions.xml
@@ -30357,6 +30357,37 @@
   <description>Please enter the description of this user action.</description>
 </action>
 
+<action name="OsDefaultsChoiceDialogClosed">
+  <owner>dgn@google.com</owner>
+  <owner>triploblastic@google.com</owner>
+  <owner>chrome-waffle-eng@google.com</owner>
+  <description>
+    Recorded when a choice dialog to invite users to finish choosing their
+    default browser app and search engine choice from OS is closed. In rare
+    occassions this may be closed by the backend if the dialog is not required.
+  </description>
+</action>
+
+<action name="OsDefaultsChoiceDialogShown" not_user_triggered="true">
+  <owner>dgn@google.com</owner>
+  <owner>triploblastic@google.com</owner>
+  <owner>chrome-waffle-eng@google.com</owner>
+  <description>
+    Recorded when a choice dialog to invite users to finish choosing their
+    default browser app and search engine choice from OS is shown.
+  </description>
+</action>
+
+<action name="OsDefaultsChoiceDialogUnblocked" not_user_triggered="true">
+  <owner>dgn@google.com</owner>
+  <owner>triploblastic@google.com</owner>
+  <owner>chrome-waffle-eng@google.com</owner>
+  <description>
+    Recorded when a choice dialog to invite users to finish choosing their
+    default browser app and search engine choice from OS is unblocked.
+  </description>
+</action>
+
 <action name="OutdatedPluginInfobar.AllowThisTime">
   <obsolete>Removed in M107</obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 8d406c0..3c6d7cc 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -293,6 +293,29 @@
   <int value="12" label="Voice to share"/>
 </enum>
 
+<!-- LINT.IfChange(AdAuctionPABaseValue) -->
+
+<enum name="AdAuctionPABaseValue">
+  <int value="0" label="kWinningBid"/>
+  <int value="1" label="kHighestScoringOtherBid"/>
+  <int value="2" label="kScriptRunTime"/>
+  <int value="3" label="kSignalsFetchTime"/>
+  <int value="4" label="kBidRejectReason"/>
+  <int value="5" label="kParticipatingInterestGroupCount"/>
+  <int value="6" label="kAverageCodeFetchTime"/>
+  <int value="7" label="kPercentScriptsTimeout"/>
+  <int value="8" label="kPercentInterestGroupsCumulativeTimeout"/>
+  <int value="9" label="kCumulativeBuyerTime"/>
+  <int value="10" label="kRegularInterestGroupsUsed"/>
+  <int value="11" label="kPercentRegularInterestGroupQuotaUsed"/>
+  <int value="12" label="kNegativeInterestGroupsUsed"/>
+  <int value="13" label="kPercentNegativeInterestGroupQuotaUsed"/>
+  <int value="14" label="kInterestGroupStorageUsed"/>
+  <int value="15" label="kPercentInterestGroupStorageQuotaUsed"/>
+</enum>
+
+<!-- LINT.ThenChange(//content/services/auction_worklet/public/mojom/private_aggregation_request.mojom:BaseValue) -->
+
 <enum name="AdditionalBidResult">
   <int value="0" label="Sent for Scoring"/>
   <int value="1" label="Negative Targeted"/>
@@ -15672,6 +15695,7 @@
   <int value="-1910427915" label="HttpsOnlyMode:enabled"/>
   <int value="-1910305102" label="EnhancedDeskAnimations:enabled"/>
   <int value="-1910061571" label="NtpOutlookCalendarModule:enabled"/>
+  <int value="-1910009033" label="JumpStartOmnibox:enabled"/>
   <int value="-1909983714" label="ExperimentalTabController:disabled"/>
   <int value="-1909702940" label="WebXRARModule:disabled"/>
   <int value="-1909356390"
@@ -19691,6 +19715,7 @@
   <int value="-249636068" label="ChromeRootStoreUsed:enabled"/>
   <int value="-249443346" label="PhoneHubCallNotification:disabled"/>
   <int value="-249415830" label="FilteringScrollPrediction:disabled"/>
+  <int value="-248905552" label="JumpStartOmnibox:disabled"/>
   <int value="-248488262" label="MemorySaverModeAggressiveness:enabled"/>
   <int value="-248223420" label="AutofillKeyboardAccessory:disabled"/>
   <int value="-247542772" label="CheckOfflineCapability:enabled"/>
@@ -27897,6 +27922,17 @@
   <int value="6" label="Invalid Key Length"/>
 </enum>
 
+<!-- LINT.IfChange(OsDefaultsChoiceDialogStatus) -->
+
+<enum name="OsDefaultsChoiceDialogStatus">
+  <int value="0" label="Unknown: Dialog dismissed or abandoned before showing"/>
+  <int value="1" label="Pending"/>
+  <int value="2" label="ChoiceLaunch"/>
+  <int value="3" label="ChoiceConfirmation"/>
+</enum>
+
+<!-- LINT.ThenChange() -->
+
 <enum name="OSXNSException">
   <int value="0" label="NSGenericException"/>
   <int value="1" label="NSRangeException"/>
@@ -33984,12 +34020,13 @@
   <int value="117" label="HiddenUntilFoundAttribute"/>
   <int value="118" label="Scheduler"/>
   <int value="119" label="KeyboardLock"/>
-  <int value="120" label="HTMLSearchElement"/>
+  <int value="120" label="Search"/>
   <int value="121" label="AsyncClipboard"/>
-  <int value="122" label="HTMLUnsafeMethods"/>
+  <int value="122" label="ParseHtmlUnsafe"/>
   <int value="123" label="ClipboardUnsanitizedFormats"/>
   <int value="124" label="Aborting"/>
   <int value="125" label="EditContext"/>
+  <int value="126" label="PaintOrder"/>
 </enum>
 
 <!-- LINT.ThenChange(//third_party/blink/public/mojom/use_counter/metrics/webdx_feature.mojom:WebDXFeature) -->
diff --git a/tools/metrics/histograms/metadata/ash/enums.xml b/tools/metrics/histograms/metadata/ash/enums.xml
index 4e94582..737790c 100644
--- a/tools/metrics/histograms/metadata/ash/enums.xml
+++ b/tools/metrics/histograms/metadata/ash/enums.xml
@@ -1975,6 +1975,30 @@
   <int value="1" label="kSamplePrompts"/>
 </enum>
 
+<enum name="SeaPenSamplePromptId">
+  <int value="0" label="chrome spheres"/>
+  <int value="1" label="galaxy with spaceship"/>
+  <int value="2" label="cat riding unicorn"/>
+  <int value="3" label="animated flowers"/>
+  <int value="4" label="lily in rain"/>
+  <int value="5" label="colorful treehouse"/>
+  <int value="6" label="dalmation"/>
+  <int value="7" label="delorean"/>
+  <int value="8" label="black motorcycle"/>
+  <int value="9" label="spaceship over city"/>
+  <int value="10" label="cat on windowsill"/>
+  <int value="11" label="bioluminescent beach"/>
+  <int value="12" label="black sand dunes"/>
+  <int value="13" label="tree made of stars"/>
+  <int value="14" label="moon over lake"/>
+  <int value="15" label="marble arch"/>
+  <int value="16" label="steampunk spaceship"/>
+  <int value="17" label="white tiger"/>
+  <int value="18" label="anime path overlooking ocean"/>
+  <int value="19" label="papaver rhea stems"/>
+  <int value="20" label="meteor shower"/>
+</enum>
+
 <enum name="SeaPenTemplateId">
 <!-- Defined in ash/webui/common/mojom/sea_pen_generated.mojom -->
 
diff --git a/tools/metrics/histograms/metadata/ash/histograms.xml b/tools/metrics/histograms/metadata/ash/histograms.xml
index 8eee41e9..7ce07d16 100644
--- a/tools/metrics/histograms/metadata/ash/histograms.xml
+++ b/tools/metrics/histograms/metadata/ash/histograms.xml
@@ -7602,7 +7602,7 @@
 </histogram>
 
 <histogram name="Ash.Projector.CreationFlow.{TabletOrClamshell}"
-    enum="ProjectorCreationFlow" expires_after="2024-10-01">
+    enum="ProjectorCreationFlow" expires_after="2025-04-01">
   <owner>dorianbrandon@google.com</owner>
   <owner>bzielinski@google.com</owner>
   <owner>cros-projector@google.com</owner>
@@ -7615,7 +7615,7 @@
 </histogram>
 
 <histogram name="Ash.Projector.CreationFlowError.{TabletOrClamshell}"
-    enum="ProjectorCreationFlowError" expires_after="2024-10-01">
+    enum="ProjectorCreationFlowError" expires_after="2025-04-01">
   <owner>dorianbrandon@google.com</owner>
   <owner>bzielinski@google.com</owner>
   <owner>cros-projector@google.com</owner>
@@ -7653,7 +7653,7 @@
 </histogram>
 
 <histogram name="Ash.Projector.PendingScreencastBatchIOTaskDuration" units="ms"
-    expires_after="2024-12-08">
+    expires_after="2025-04-01">
   <owner>dorianbrandon@google.com</owner>
   <owner>bzielinski@google.com</owner>
   <owner>cros-projector@google.com</owner>
@@ -7664,7 +7664,7 @@
 </histogram>
 
 <histogram name="Ash.Projector.PendingScreencastChangeInterval" units="ms"
-    expires_after="2024-10-01">
+    expires_after="2025-04-01">
   <owner>dorianbrandon@google.com</owner>
   <owner>bzielinski@google.com</owner>
   <owner>cros-projector@google.com</owner>
@@ -7676,7 +7676,7 @@
 </histogram>
 
 <histogram name="Ash.Projector.SpeechRecognitionEndState.{Location}"
-    enum="SpeechRecognitionEndState" expires_after="2024-12-08">
+    enum="SpeechRecognitionEndState" expires_after="2025-04-01">
   <owner>dorianbrandon@google.com</owner>
   <owner>bzielinski@google.com</owner>
   <owner>cros-projector@google.com</owner>
@@ -7700,7 +7700,7 @@
 </histogram>
 
 <histogram name="Ash.Projector.TranscriptsCount.{TabletOrClamshell}"
-    units="Number of transcripts" expires_after="2024-10-01">
+    units="Number of transcripts" expires_after="2025-04-01">
   <owner>dorianbrandon@google.com</owner>
   <owner>bzielinski@google.com</owner>
   <owner>cros-projector@google.com</owner>
@@ -8134,8 +8134,8 @@
   <token key="Type" variants="SeaPenApiType"/>
 </histogram>
 
-<histogram name="Ash.SeaPen.Freeform.SamplePrompt.Clicked"
-    enum="BooleanClicked" expires_after="2025-07-31">
+<histogram name="Ash.SeaPen.Freeform.SamplePrompt.SampleClicked"
+    enum="SeaPenSamplePromptId" expires_after="2025-07-31">
   <owner>ericamlee@google.com</owner>
   <owner>assistive-eng@google.com</owner>
   <summary>
@@ -8451,7 +8451,7 @@
 
 <histogram
     name="Ash.ServerBasedSpeechRecognition.EndOfRecognitionSignalLatency"
-    units="ms" expires_after="2024-10-01">
+    units="ms" expires_after="2025-04-01">
   <owner>dorianbrandon@google.com</owner>
   <owner>bzielinski@google.com</owner>
   <owner>cros-projector@google.com</owner>
@@ -8462,7 +8462,7 @@
 </histogram>
 
 <histogram name="Ash.ServerBasedSpeechRecognition.ErrorCode"
-    enum="CombinedHttpResponseAndNetErrorCode" expires_after="2024-10-01">
+    enum="CombinedHttpResponseAndNetErrorCode" expires_after="2025-04-01">
   <owner>dorianbrandon@google.com</owner>
   <owner>bzielinski@google.com</owner>
   <owner>cros-projector@google.com</owner>
diff --git a/tools/metrics/histograms/metadata/others/histograms.xml b/tools/metrics/histograms/metadata/others/histograms.xml
index 7317f66..c4efff7 100644
--- a/tools/metrics/histograms/metadata/others/histograms.xml
+++ b/tools/metrics/histograms/metadata/others/histograms.xml
@@ -963,6 +963,24 @@
   </summary>
 </histogram>
 
+<histogram name="Ads.InterestGroup.Auction.NumOwnerOriginsCachedForPreconnect"
+    units="owners" expires_after="2025-03-02">
+  <owner>abigailkatcoff@chromium.org</owner>
+  <owner>privacy-sandbox-dev@chromium.org</owner>
+  <summary>
+    The number of owner origins in a FLEDGE auction config (including in
+    componenent auctions) whose origins are cached in-memory from previous
+    auctions and joins. If the FLEDGE auction config has duplicate owners (e.g.
+    because an owner is participating in more than one component auction),
+    they're included in this number multiple times. Recorded when trying to
+    preconnect to these owners at the auction start (right before the
+    AuctionRunner is created).
+
+    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.NumOwnersWithInterestGroups"
     units="owners" expires_after="2025-03-02">
   <owner>mmenke@chromium.org</owner>
@@ -1019,6 +1037,21 @@
   </summary>
 </histogram>
 
+<histogram name="Ads.InterestGroup.Auction.PABaseValueUsed"
+    enum="AdAuctionPABaseValue" expires_after="2025-03-26">
+  <owner>morlovich@google.com</owner>
+  <owner>privacy-sandbox-dev@chromium.org</owner>
+  <summary>
+    Counts instances each base value enum was used for Private Aggregation
+    contribution computations. Recorded every time the contribution's event has
+    been determined to have occurred and the base value occurred in either
+    bucket or value computation.
+
+    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.ParseBaServerResponseDuration"
     units="ms" expires_after="2025-02-10">
   <owner>behamilton@google.com</owner>
diff --git a/tools/metrics/histograms/metadata/printing/histograms.xml b/tools/metrics/histograms/metadata/printing/histograms.xml
index e9a8d72..137e1cd2 100644
--- a/tools/metrics/histograms/metadata/printing/histograms.xml
+++ b/tools/metrics/histograms/metadata/printing/histograms.xml
@@ -383,17 +383,6 @@
   </summary>
 </histogram>
 
-<histogram name="Printing.CUPS.PrinterStatusRetrySuccess" enum="Boolean"
-    expires_after="2022-12-15">
-  <owner>gavinwill@chromium.org</owner>
-  <owner>cros-device-enablement@google.com</owner>
-  <summary>
-    Records the success outcome for a retried printer status query to an
-    unreachable local printer. Only recorded for the retry attempt. The result
-    for the first attempt is recorded by Printing.CUPS.PrinterStatusQueryResult.
-  </summary>
-</histogram>
-
 <histogram name="Printing.CUPS.ProtocolUsed" enum="PrinterProtocol"
     expires_after="2025-03-23">
   <owner>bmgordon@chromium.org</owner>
@@ -464,13 +453,14 @@
 </histogram>
 
 <histogram name="Printing.CUPS.UsbSetupSource" enum="UsbPrinterSetupSource"
-    expires_after="2022-10-04">
+    expires_after="2025-09-27">
   <owner>gavinwill@chromium.org</owner>
   <owner>cros-device-enablement@google.com</owner>
   <summary>
     Records the source from which a USB printer was configured in Chrome OS.
     Used to track relative usage of the various printer setup routes. Only emits
-    after a successful printer setup.
+    after a successful printer setup. Warning: this histogram was expired from
+    2022-10-04 to M130; data may be missing.
   </summary>
 </histogram>
 
diff --git a/tools/metrics/histograms/metadata/scanning/histograms.xml b/tools/metrics/histograms/metadata/scanning/histograms.xml
index 48060e6..3d094e4b8 100644
--- a/tools/metrics/histograms/metadata/scanning/histograms.xml
+++ b/tools/metrics/histograms/metadata/scanning/histograms.xml
@@ -23,12 +23,13 @@
 <histograms>
 
 <histogram name="Scanning.CombinedImageSizeInKbBeforePdf" units="KB"
-    expires_after="2024-01-04">
+    expires_after="2025-09-27">
   <owner>gavinwill@chromium.org</owner>
   <owner>cros-device-enablement@google.com</owner>
   <summary>
     Records the total size of the scanned images stored in memory right before
-    they are converted into the final PDF.
+    they are converted into the final PDF. Warning: this histogram was expired
+    from 2024-01-04 to M130; data may be missing.
   </summary>
 </histogram>
 
@@ -62,22 +63,25 @@
 </histogram>
 
 <histogram name="Scanning.MultiPageScan.CombinedImageSizeInKbBeforePdf"
-    units="KB" expires_after="2024-01-04">
+    units="KB" expires_after="2025-09-27">
   <owner>gavinwill@chromium.org</owner>
   <owner>cros-device-enablement@google.com</owner>
   <summary>
     During a multi-page scan session, records the total size of the scanned
     images stored in memory at the moment the user clicks save to convert them
-    into the final PDF.
+    into the final PDF. Warning: this histogram was expired from 2024-01-04 to
+    M130; data may be missing.
   </summary>
 </histogram>
 
 <histogram name="Scanning.MultiPageScan.NumPagesScanned" units="pages"
-    expires_after="2024-01-04">
+    expires_after="2025-09-27">
   <owner>gavinwill@chromium.org</owner>
   <owner>cros-device-enablement@google.com</owner>
   <summary>
     Records the number of pages scanned in a saved multi-page scan session.
+    Warning: this histogram was expired from 2024-01-04 to M130; data may be
+    missing.
   </summary>
 </histogram>
 
@@ -91,34 +95,37 @@
 </histogram>
 
 <histogram name="Scanning.MultiPageScan.PDFGenerationTime" units="ms"
-    expires_after="2024-01-04">
+    expires_after="2025-09-27">
   <owner>gavinwill@chromium.org</owner>
   <owner>cros-device-enablement@google.com</owner>
   <summary>
     Records the amount of time it takes from the user clicking save on their
-    multi-page scan session to the PDF file being generated and saved.
+    multi-page scan session to the PDF file being generated and saved. Warning:
+    this histogram was expired from 2024-01-04 to M130; data may be missing.
   </summary>
 </histogram>
 
 <histogram name="Scanning.MultiPageScan.SessionDuration" units="ms"
-    expires_after="2024-01-04">
+    expires_after="2025-09-27">
   <owner>gavinwill@chromium.org</owner>
   <owner>cros-device-enablement@google.com</owner>
   <summary>
     Records the time between a user starting the first scan in a multi-page scan
     session to the time the user clicks save to end the session. This does not
     record when a multi-page scan session ends prematurely without the PDF being
-    saved.
+    saved. Warning: this histogram was expired from 2024-01-04 to M130; data may
+    be missing.
   </summary>
 </histogram>
 
 <histogram name="Scanning.MultiPageScan.ToolbarAction"
-    enum="ScanMultiPageToolbarAction" expires_after="2024-01-04">
+    enum="ScanMultiPageToolbarAction" expires_after="2025-09-27">
   <owner>gavinwill@chromium.org</owner>
   <owner>cros-device-enablement@google.com</owner>
   <summary>
     Records when a user clicks an action on the action toolbar in a multi-page
-    scan session.
+    scan session. Warning: this histogram was expired from 2024-01-04 to M130;
+    data may be missing.
   </summary>
 </histogram>
 
@@ -155,35 +162,44 @@
 </histogram>
 
 <histogram name="Scanning.NumFilesCreated" units="files"
-    expires_after="2024-01-04">
+    expires_after="2025-09-27">
   <owner>gavinwill@chromium.org</owner>
   <owner>cros-device-enablement@google.com</owner>
-  <summary>Records the number of files created in a successful scan.</summary>
+  <summary>
+    Records the number of files created in a successful scan. Warning: this
+    histogram was expired from 2024-01-04 to M130; data may be missing.
+  </summary>
 </histogram>
 
 <histogram name="Scanning.NumPagesScanned" units="pages"
-    expires_after="2024-01-04">
+    expires_after="2025-09-27">
   <owner>gavinwill@chromium.org</owner>
   <owner>cros-device-enablement@google.com</owner>
-  <summary>Records the number of pages scanned in a successful scan.</summary>
+  <summary>
+    Records the number of pages scanned in a successful scan. Warning: this
+    histogram was expired from 2024-01-04 to M130; data may be missing.
+  </summary>
 </histogram>
 
 <histogram name="Scanning.NumScanSettingChanges" units="changes"
-    expires_after="2024-01-04">
+    expires_after="2025-09-27">
   <owner>gavinwill@chromium.org</owner>
   <owner>cros-device-enablement@google.com</owner>
   <summary>
     Records the number of scan setting changes before a scan is initiated.
+    Warning: this histogram was expired from 2024-01-04 to M130; data may be
+    missing.
   </summary>
 </histogram>
 
 <histogram name="Scanning.PDFGenerationTime" units="ms"
-    expires_after="2024-01-04">
+    expires_after="2025-09-27">
   <owner>gavinwill@chromium.org</owner>
   <owner>cros-device-enablement@google.com</owner>
   <summary>
     Records the amount of time it takes from the final document in a scan job
-    being scanned to the PDF file being generated and saved.
+    being scanned to the PDF file being generated and saved. Warning: this
+    histogram was expired from 2024-01-04 to M130; data may be missing.
   </summary>
 </histogram>
 
@@ -199,11 +215,13 @@
 </histogram>
 
 <histogram name="Scanning.ScanCompleteAction" enum="ScanCompleteAction"
-    expires_after="2024-01-04">
+    expires_after="2025-09-27">
   <owner>gavinwill@chromium.org</owner>
   <owner>cros-device-enablement@google.com</owner>
   <summary>
     Records the action taken by a user after a successful scan job on Chrome OS.
+    Warning: this histogram was expired from 2024-01-04 to M130; data may be
+    missing.
   </summary>
 </histogram>
 
@@ -215,47 +233,52 @@
 </histogram>
 
 <histogram name="Scanning.ScanJobSettings.ColorMode"
-    enum="ScanJobSettingsColorMode" expires_after="2024-01-04">
+    enum="ScanJobSettingsColorMode" expires_after="2025-09-27">
   <owner>gavinwill@chromium.org</owner>
   <owner>cros-device-enablement@google.com</owner>
   <summary>
-    Records the selected Color Mode setting for a submitted scan job.
+    Records the selected Color Mode setting for a submitted scan job. Warning:
+    this histogram was expired from 2024-01-04 to M130; data may be missing.
   </summary>
 </histogram>
 
 <histogram name="Scanning.ScanJobSettings.FileType"
-    enum="ScanJobSettingsFileType" expires_after="2024-01-04">
+    enum="ScanJobSettingsFileType" expires_after="2025-09-27">
   <owner>gavinwill@chromium.org</owner>
   <owner>cros-device-enablement@google.com</owner>
   <summary>
-    Records the selected File Type setting for a submitted scan job.
+    Records the selected File Type setting for a submitted scan job. Warning:
+    this histogram was expired from 2024-01-04 to M130; data may be missing.
   </summary>
 </histogram>
 
 <histogram name="Scanning.ScanJobSettings.PageSize"
-    enum="ScanJobSettingsPageSize" expires_after="2024-01-04">
+    enum="ScanJobSettingsPageSize" expires_after="2025-09-27">
   <owner>gavinwill@chromium.org</owner>
   <owner>cros-device-enablement@google.com</owner>
   <summary>
-    Records the selected Page Size setting for a submitted scan job.
+    Records the selected Page Size setting for a submitted scan job. Warning:
+    this histogram was expired from 2024-01-04 to M130; data may be missing.
   </summary>
 </histogram>
 
 <histogram name="Scanning.ScanJobSettings.Resolution"
-    enum="ScanJobSettingsResolution" expires_after="2024-01-04">
+    enum="ScanJobSettingsResolution" expires_after="2025-09-27">
   <owner>gavinwill@chromium.org</owner>
   <owner>cros-device-enablement@google.com</owner>
   <summary>
-    Records the selected Resolution setting for a submitted scan job.
+    Records the selected Resolution setting for a submitted scan job. Warning:
+    this histogram was expired from 2024-01-04 to M130; data may be missing.
   </summary>
 </histogram>
 
 <histogram name="Scanning.ScanJobSettings.Source" enum="ScanJobSettingsSource"
-    expires_after="2024-01-04">
+    expires_after="2025-09-27">
   <owner>gavinwill@chromium.org</owner>
   <owner>cros-device-enablement@google.com</owner>
   <summary>
-    Records the selected Source setting for a submitted scan job.
+    Records the selected Source setting for a submitted scan job. Warning: this
+    histogram was expired from 2024-01-04 to M130; data may be missing.
   </summary>
 </histogram>
 
diff --git a/tools/metrics/histograms/metadata/search/histograms.xml b/tools/metrics/histograms/metadata/search/histograms.xml
index 1925f7b..c8ddcd1 100644
--- a/tools/metrics/histograms/metadata/search/histograms.xml
+++ b/tools/metrics/histograms/metadata/search/histograms.xml
@@ -822,6 +822,72 @@
   </summary>
 </histogram>
 
+<histogram name="Search.OsDefaultsChoice.DelayFromDialogShownToFirstStatus"
+    units="ms" expires_after="2025-03-01">
+  <owner>dgn@google.com</owner>
+  <owner>triploblastic@google.com</owner>
+  <owner>chrome-waffle-eng@google.com</owner>
+  <summary>
+    Measures latency for the dialog that blocks Chrome usage to require the
+    Android OS default apps choice to be completed. [Android only]
+
+    This value represents the time between when we showed the dialog and when we
+    received a response from the backend. The backend has the most accurate
+    information about whether the dialog needs to be shown, but we may show the
+    dialog based on less accurate signals to avoid surprising the user after
+    some potentially long delay into the task they started with Chrome. Recorded
+    for each browser session eligible to show the dialog, on startup.
+
+    If the status is received before the dialog is shown then 0 is recorded.
+  </summary>
+</histogram>
+
+<histogram name="Search.OsDefaultsChoice.DelayFromObservationToFirstStatus"
+    units="ms" expires_after="2025-03-01">
+  <owner>dgn@google.com</owner>
+  <owner>triploblastic@google.com</owner>
+  <owner>chrome-waffle-eng@google.com</owner>
+  <summary>
+    This value represents the time between when Chrome starts waiting on the
+    backend for a response and when the UI actually gets a response from the
+    backend. [Android only]
+
+    Recorded for each browser session that attempted to show the dialog and got
+    a backend response, on startup.
+
+    If the status is received before Chrome starts observing then 0 is recorded.
+  </summary>
+</histogram>
+
+<histogram name="Search.OsDefaultsChoice.DialogStatusChange"
+    enum="OsDefaultsChoiceDialogStatus" expires_after="2025-03-01">
+  <owner>dgn@google.com</owner>
+  <owner>triploblastic@google.com</owner>
+  <owner>chrome-waffle-eng@google.com</owner>
+  <summary>
+    State of the dialog that blocks Chrome usage to require the Android OS
+    default apps choice to be completed, logged every time it changes. [Android
+    only]
+
+    The dialog itself is triggered shortly after startup when it is determined
+    that blocking the user is needed. Some status changes might be happening
+    while the dialog is not visible, for example when the feature is in the
+    &quot;dark launch&quot; mode. The &quot;Unknown&quot; value is logged when
+    the dialog is dismissed.
+  </summary>
+</histogram>
+
+<histogram name="Search.OsDefaultsChoice.DialogStatusOnAppOpen"
+    enum="OsDefaultsChoiceDialogStatus" expires_after="2025-03-01">
+  <owner>dgn@google.com</owner>
+  <owner>triploblastic@google.com</owner>
+  <owner>chrome-waffle-eng@google.com</owner>
+  <summary>
+    Logs the status of the choice dialog (if it's shown) when Chrome is brought
+    to the foreground. [Android only].
+  </summary>
+</histogram>
+
 <histogram name="Search.PartnershipSearchPerformed" enum="SearchEntryPoint"
     expires_after="M85">
   <owner>yusufo@chromium.org</owner>
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json
index 4db8d09..ad374190 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": "066fdeeadeb913dc99cd6cb19916237ccf804d49",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/46ba5136cbf8f0465969e287872943161bffb3a9/trace_processor_shell.exe"
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/71aaedeaea2b288f27d67d853fb7c4da9762796e/trace_processor_shell.exe"
         },
         "linux_arm": {
             "hash": "bb963e5488d9a76861165256126830c7ae523733",
@@ -22,7 +22,7 @@
         },
         "linux": {
             "hash": "a970025e2d93c368de68982b257f43c28cd3d4c5",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/ba40a4dd8c1e0ccd0f6d5b567802a00b717562b4/trace_processor_shell"
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/71aaedeaea2b288f27d67d853fb7c4da9762796e/trace_processor_shell"
         }
     },
     "power_profile.sql": {
diff --git a/ui/accessibility/ax_tree_data.h b/ui/accessibility/ax_tree_data.h
index e6bf8f1..010b352 100644
--- a/ui/accessibility/ax_tree_data.h
+++ b/ui/accessibility/ax_tree_data.h
@@ -7,7 +7,6 @@
 
 #include <stdint.h>
 
-#include <map>
 #include <string>
 #include <vector>
 
diff --git a/ui/accessibility/platform/ax_platform_node_win.h b/ui/accessibility/platform/ax_platform_node_win.h
index b6ed0aa..0e4caf3 100644
--- a/ui/accessibility/platform/ax_platform_node_win.h
+++ b/ui/accessibility/platform/ax_platform_node_win.h
@@ -12,18 +12,15 @@
 #include <wrl/client.h>
 
 #include <array>
-#include <map>
 #include <string>
 #include <vector>
 
 #include "base/component_export.h"
-#include "base/gtest_prod_util.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/observer_list.h"
 #include "base/win/atl.h"
 #include "third_party/iaccessible2/ia2_api_all.h"
 #include "ui/accessibility/ax_enums.mojom-forward.h"
-#include "ui/accessibility/ax_text_utils.h"
 #include "ui/accessibility/platform/ax_platform_node_base.h"
 #include "ui/accessibility/platform/ax_platform_text_boundary.h"
 #include "ui/accessibility/platform/ichromeaccessible.h"
diff --git a/ui/accessibility/platform/ax_platform_relation_win.cc b/ui/accessibility/platform/ax_platform_relation_win.cc
index 881ec21..37b9eff1 100644
--- a/ui/accessibility/platform/ax_platform_relation_win.cc
+++ b/ui/accessibility/platform/ax_platform_relation_win.cc
@@ -11,23 +11,10 @@
 
 #include <wrl/client.h>
 
-#include <algorithm>
 #include <vector>
 
-#include "base/lazy_instance.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
-#include "base/win/enum_variant.h"
-#include "base/win/scoped_variant.h"
 #include "third_party/iaccessible2/ia2_api_all.h"
-#include "third_party/skia/include/core/SkColor.h"
-#include "ui/accessibility/ax_action_data.h"
-#include "ui/accessibility/ax_mode_observer.h"
-#include "ui/accessibility/ax_node_data.h"
-#include "ui/accessibility/ax_role_properties.h"
-#include "ui/accessibility/ax_text_utils.h"
-#include "ui/accessibility/ax_tree_data.h"
 #include "ui/accessibility/platform/ax_platform_node_base.h"
 #include "ui/accessibility/platform/ax_platform_node_delegate.h"
 #include "ui/base/win/atl_module.h"
diff --git a/ui/accessibility/platform/ax_platform_relation_win.h b/ui/accessibility/platform/ax_platform_relation_win.h
index d4074ef4..fda9fc72 100644
--- a/ui/accessibility/platform/ax_platform_relation_win.h
+++ b/ui/accessibility/platform/ax_platform_relation_win.h
@@ -8,10 +8,8 @@
 #include <oleacc.h>
 #include <wrl/client.h>
 
-#include <set>
 #include <vector>
 
-#include "base/component_export.h"
 #include "base/win/atl.h"
 #include "third_party/iaccessible2/ia2_api_all.h"
 #include "ui/accessibility/ax_text_utils.h"
diff --git a/ui/accessibility/platform/ax_platform_tree_manager.h b/ui/accessibility/platform/ax_platform_tree_manager.h
index b43629c..16eac67 100644
--- a/ui/accessibility/platform/ax_platform_tree_manager.h
+++ b/ui/accessibility/platform/ax_platform_tree_manager.h
@@ -8,7 +8,6 @@
 #include "base/component_export.h"
 #include "base/memory/weak_ptr.h"
 #include "ui/accessibility/ax_node.h"
-#include "ui/accessibility/ax_tree_id.h"
 #include "ui/accessibility/ax_tree_manager.h"
 
 namespace ui {
diff --git a/ui/accessibility/platform/browser_accessibility.cc b/ui/accessibility/platform/browser_accessibility.cc
index 777a31ad..18a39ce 100644
--- a/ui/accessibility/platform/browser_accessibility.cc
+++ b/ui/accessibility/platform/browser_accessibility.cc
@@ -10,7 +10,6 @@
 
 #include "base/check.h"
 #include "base/containers/contains.h"
-#include "base/debug/dump_without_crashing.h"
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
 #include "base/ranges/algorithm.h"
diff --git a/ui/accessibility/platform/browser_accessibility.h b/ui/accessibility/platform/browser_accessibility.h
index 2ea2664..ee424b16 100644
--- a/ui/accessibility/platform/browser_accessibility.h
+++ b/ui/accessibility/platform/browser_accessibility.h
@@ -7,26 +7,18 @@
 
 #include <stdint.h>
 
-#include <map>
+#include <cstddef>
+#include <iterator>
 #include <memory>
 #include <optional>
-#include <ostream>
-#include <set>
 #include <string>
-#include <utility>
 #include <vector>
 
-#include "base/memory/raw_ptr.h"
-#include "base/strings/string_split.h"
-#include "build/build_config.h"
 #include "base/component_export.h"
+#include "base/memory/raw_ptr.h"
+#include "build/build_config.h"
 #include "ui/accessibility/ax_enums.mojom-forward.h"
 #include "ui/accessibility/ax_node.h"
-#include "ui/accessibility/ax_node_data.h"
-#include "ui/accessibility/ax_node_position.h"
-#include "ui/accessibility/ax_range.h"
-#include "ui/accessibility/ax_text_attributes.h"
-#include "ui/accessibility/platform/ax_platform_node.h"
 #include "ui/accessibility/platform/ax_platform_node_id.h"
 #include "ui/accessibility/platform/child_iterator.h"
 #include "ui/base/buildflags.h"
@@ -38,6 +30,7 @@
 class DumpAccessibilityTestBase;
 }
 namespace ui {
+class AXPlatformNode;
 class BrowserAccessibilityManager;
 // A `BrowserAccessibility` object represents one node in the accessibility tree
 // on the browser side. It wraps an `AXNode` and assists in exposing
diff --git a/ui/accessibility/platform/browser_accessibility_com_win.h b/ui/accessibility/platform/browser_accessibility_com_win.h
index 5fd12b9..97f1079 100644
--- a/ui/accessibility/platform/browser_accessibility_com_win.h
+++ b/ui/accessibility/platform/browser_accessibility_com_win.h
@@ -23,7 +23,6 @@
 #include "third_party/isimpledom/ISimpleDOMNode.h"
 #include "third_party/isimpledom/ISimpleDOMText.h"
 #include "ui/accessibility/ax_enums.mojom-forward.h"
-#include "ui/accessibility/platform/ax_platform_node_delegate.h"
 #include "ui/accessibility/platform/ax_platform_node_win.h"
 
 // This nonstandard GUID is taken directly from the Mozilla sources
diff --git a/ui/events/cocoa/cocoa_event_utils.h b/ui/events/cocoa/cocoa_event_utils.h
index fc780c9..a52ea4b5 100644
--- a/ui/events/cocoa/cocoa_event_utils.h
+++ b/ui/events/cocoa/cocoa_event_utils.h
@@ -9,6 +9,7 @@
 
 #include <vector>
 
+#include "base/containers/span.h"
 #include "ui/events/events_export.h"
 
 namespace ui {
@@ -36,7 +37,7 @@
 
 // Create an NSEvent from an opaque serialization using CGEventCreateFromData.
 // The result is autoreleased.
-EVENTS_EXPORT NSEvent* EventFromData(const std::vector<uint8_t>& data);
+EVENTS_EXPORT NSEvent* EventFromData(base::span<const uint8_t> data);
 
 }  // namespace ui
 
diff --git a/ui/events/cocoa/cocoa_event_utils.mm b/ui/events/cocoa/cocoa_event_utils.mm
index ab18e15..bd8521a 100644
--- a/ui/events/cocoa/cocoa_event_utils.mm
+++ b/ui/events/cocoa/cocoa_event_utils.mm
@@ -2,16 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifdef UNSAFE_BUFFERS_BUILD
-// TODO(crbug.com/351564777): Remove this and convert code to safer constructs.
-#pragma allow_unsafe_buffers
-#endif
-
 #import "ui/events/cocoa/cocoa_event_utils.h"
 
-#include <Carbon/Carbon.h>  // for <HIToolbox/Events.h>
+#include <Carbon/Carbon.h>              // for <HIToolbox/Events.h>
 #include <IOKit/hidsystem/IOLLEvent.h>  // for NX_ constants
 
+#include "base/apple/foundation_util.h"
 #include "base/apple/scoped_cftyperef.h"
 #include "ui/events/event_constants.h"
 #include "ui/events/event_utils.h"
@@ -143,12 +139,11 @@
 std::vector<uint8_t> EventToData(NSEvent* event) {
   base::apple::ScopedCFTypeRef<CFDataRef> cf_data(
       CGEventCreateData(nullptr, event.CGEvent));
-  const uint8_t* cf_data_ptr = CFDataGetBytePtr(cf_data.get());
-  size_t cf_data_size = CFDataGetLength(cf_data.get());
-  return std::vector<uint8_t>(cf_data_ptr, cf_data_ptr + cf_data_size);
+  base::span<const uint8_t> span = base::apple::CFDataToSpan(cf_data.get());
+  return {span.begin(), span.end()};
 }
 
-NSEvent* EventFromData(const std::vector<uint8_t>& data) {
+NSEvent* EventFromData(base::span<const uint8_t> data) {
   base::apple::ScopedCFTypeRef<CFDataRef> cf_data(
       CFDataCreate(nullptr, data.data(), data.size()));
   base::apple::ScopedCFTypeRef<CGEventRef> cg_event(
diff --git a/ui/webui/resources/cr_components/certificate_manager/local_certs_section_v2.html b/ui/webui/resources/cr_components/certificate_manager/local_certs_section_v2.html
index 74b7da9..c3474b29 100644
--- a/ui/webui/resources/cr_components/certificate_manager/local_certs_section_v2.html
+++ b/ui/webui/resources/cr_components/certificate_manager/local_certs_section_v2.html
@@ -26,9 +26,11 @@
             [[numSystemCertsString_]]
           </div>
         </div>
+        <!-- TODO(crbug.com/40928765): Have this toggle control whether the OS
+          imported certs are used.  -->
         <cr-toggle id="importOsCerts"
             aria-label="Toggle operating system certificate imports"
-            checked="[[importOsCertsEnabled_]]" disabled>
+            checked="[[importOsCertsEnabled_]]" disabled$="[[importOsCertsEnabledManaged_]]">
         </cr-toggle>
 
         <cr-icon id="importOsCertsManagedIcon" icon="cr:domain"
diff --git a/v8 b/v8
index ac7f43c..c42072b 160000
--- a/v8
+++ b/v8
@@ -1 +1 @@
-Subproject commit ac7f43cd4bad11b77aaa46d0d6b5ec5d06bf716c
+Subproject commit c42072b224c98f718f5ad17d3d54e10e2ba2f2c1